More detailed subscriber event logging
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / lisp / MapResolver.java
1 /*
2  * Copyright (c) 2014, 2017 Contextream, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.lispflowmapping.implementation.lisp;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.Sets;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Set;
16 import org.apache.commons.lang3.exception.ExceptionUtils;
17 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
18 import org.opendaylight.lispflowmapping.interfaces.dao.Subscriber;
19 import org.opendaylight.lispflowmapping.interfaces.lisp.IMapRequestResultHandler;
20 import org.opendaylight.lispflowmapping.interfaces.lisp.IMapResolverAsync;
21 import org.opendaylight.lispflowmapping.interfaces.lisp.ISmrNotificationListener;
22 import org.opendaylight.lispflowmapping.interfaces.lisp.SmrEvent;
23 import org.opendaylight.lispflowmapping.interfaces.mappingservice.IMappingService;
24 import org.opendaylight.lispflowmapping.lisp.type.MappingData;
25 import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
26 import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
27 import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.Ipv4Afi;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.Ipv4PrefixAfi;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.Ipv6Afi;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.Ipv6PrefixAfi;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.SimpleAddress;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.SourceDestKeyLcaf;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.Address;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.ExplicitLocatorPath;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.explicit.locator.path.explicit.locator.path.Hop;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.Ipv4BinaryAfi;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.Ipv4PrefixBinaryAfi;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.Ipv6BinaryAfi;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.Ipv6PrefixBinaryAfi;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.MapRequest;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.list.EidItem;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecordBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.list.MappingRecordItemBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapreplymessage.MapReplyBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.maprequest.ItrRloc;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 public class MapResolver implements IMapResolverAsync {
59     protected static final Logger LOG = LoggerFactory.getLogger(MapResolver.class);
60
61     private IMappingService mapService;
62     private boolean subscriptionService;
63     private String elpPolicy;
64     private IMapRequestResultHandler requestHandler;
65     private boolean authenticate = true;
66     private ISmrNotificationListener smrNotificationListener;
67
68     public MapResolver(IMappingService mapService, boolean smr, String elpPolicy,
69                        IMapRequestResultHandler requestHandler) {
70         Preconditions.checkNotNull(mapService);
71         this.subscriptionService = smr;
72         this.mapService = mapService;
73         this.elpPolicy = elpPolicy;
74         this.requestHandler = requestHandler;
75     }
76
77     public void handleMapRequest(MapRequest request) {
78         // SMRs and RLOC probes are directed towards xTRs and we're a Map-Resolver here, so ignore them
79         if (request.isSmr() != null && request.isSmr()) {
80             LOG.debug("Map-Resolver ignoring incoming SMR control message.");
81             return;
82         }
83         if (request.isProbe() != null && request.isProbe()) {
84             LOG.debug("Map-Resolver ignoring incoming RLOC probe control message.");
85             return;
86         }
87         if (request.isSmrInvoked()) {
88             LOG.debug("SMR-invoked request received.");
89             for (EidItem eidItem : request.getEidItem()) {
90                 final SmrEvent event = new SmrEvent(LispAddressUtil.addressBinariesFromItrRlocs(request.getItrRloc()),
91                         eidItem.getEid(), request.getNonce());
92                 smrNotificationListener.onSmrInvokedReceived(event);
93             }
94         }
95         Eid srcEid = null;
96         if (request.getSourceEid() != null) {
97             srcEid = request.getSourceEid().getEid();
98         }
99         MapReplyBuilder replyBuilder = new MapReplyBuilder();
100         replyBuilder.setEchoNonceEnabled(false);
101         replyBuilder.setProbe(false);
102         replyBuilder.setSecurityEnabled(false);
103         replyBuilder.setNonce(request.getNonce());
104         replyBuilder.setMappingRecordItem(new ArrayList<>());
105         List<ItrRloc> itrRlocs = request.getItrRloc();
106         final IpAddressBinary sourceRloc = request.getSourceRloc();
107
108         for (EidItem eidRecord : request.getEidItem()) {
109             MappingData mappingData = mapService.getMapping(srcEid, eidRecord.getEid());
110             MappingRecord mapping;
111             if (mappingData == null) {
112                 mapping = mapService.addNegativeMapping(eidRecord.getEid()).getRecord();
113             } else {
114                 mapping = mappingData.getRecord();
115             }
116
117             if (itrRlocs != null && itrRlocs.size() != 0) {
118                 if (subscriptionService) {
119                     final Rloc resolvedRloc = resolveRloc(itrRlocs, sourceRloc);
120                     updateSubscribers(resolvedRloc, eidRecord.getEid(), mapping.getEid(),
121                             srcEid, mapping.getRecordTtl());
122                 }
123                 mapping = updateLocators(mapping, itrRlocs);
124             }
125             mapping = fixIfNotSDRequest(mapping, eidRecord.getEid());
126             replyBuilder.getMappingRecordItem().add(new MappingRecordItemBuilder().setMappingRecord(mapping).build());
127         }
128         requestHandler.handleMapReply(replyBuilder.build());
129     }
130
131     private static boolean isEqualIpVersion(IpAddressBinary srcRloc, Rloc rloc) {
132         if (srcRloc.getIpv4AddressBinary() != null) {
133             if (rloc.getAddressType() == Ipv4Afi.class
134                     || rloc.getAddressType() == Ipv4BinaryAfi.class
135                     || rloc.getAddressType() == Ipv4PrefixAfi.class
136                     || rloc.getAddressType() == Ipv4PrefixBinaryAfi.class) {
137                 return true;
138             }
139         } else if (rloc.getAddressType() == Ipv6Afi.class
140                 || rloc.getAddressType() == Ipv6BinaryAfi.class
141                 || rloc.getAddressType() == Ipv6PrefixAfi.class
142                 || rloc.getAddressType() == Ipv6PrefixBinaryAfi.class) {
143             return true;
144         }
145         return false;
146     }
147
148     private Rloc resolveRloc(List<ItrRloc> itrRlocList, IpAddressBinary srcRloc) {
149         if (srcRloc == null) {
150             return itrRlocList.get(0).getRloc();
151         }
152         byte[] srcRlocByte;
153         if (srcRloc.getIpv4AddressBinary() != null) {
154             srcRlocByte = srcRloc.getIpv4AddressBinary().getValue();
155         } else {
156             srcRlocByte = srcRloc.getIpv6AddressBinary().getValue();
157         }
158
159         Rloc equalIpvRloc = null;
160         for (ItrRloc itrRloc : itrRlocList) {
161             final Rloc rloc = itrRloc.getRloc();
162             final byte[] itrRlocByte = LispAddressUtil.ipAddressToByteArray(rloc.getAddress());
163
164             // return an Rloc equal to the source Rloc
165             if (itrRlocByte != null && LispAddressUtil.compareIpAddressByteArrays(srcRlocByte, itrRlocByte) == 0) {
166                 return rloc;
167             }
168             // else lookup the first Rloc with identical Ip version
169             if (equalIpvRloc == null && isEqualIpVersion(srcRloc, rloc)) {
170                 equalIpvRloc = rloc;
171             }
172         }
173         if (equalIpvRloc != null) {
174             return equalIpvRloc;
175         } else {
176             // if none of the above, return the first Rloc
177             return itrRlocList.get(0).getRloc();
178         }
179     }
180
181     private void updateSubscribers(Rloc itrRloc, Eid reqEid, Eid mapEid, Eid srcEid, Integer recordTtl) {
182         Subscriber subscriber = new Subscriber(itrRloc, srcEid, Subscriber.recordTtlToSubscriberTime(recordTtl));
183         Eid subscribedEid = mapEid;
184
185         // If the eid in the matched mapping is SourceDest and the requested eid IS NOT then we subscribe itrRloc only
186         // to dst from the src/dst since that what's been requested. Note though that any updates to to the src/dst
187         // mapping will be pushed to dst as well (see sendSMRs in MapServer)
188         if (mapEid.getAddressType().equals(SourceDestKeyLcaf.class)
189                 && !reqEid.getAddressType().equals(SourceDestKeyLcaf.class)) {
190             subscribedEid = SourceDestKeyHelper.getDstBinary(mapEid);
191         }
192
193         Set<Subscriber> subscribers = getSubscribers(subscribedEid);
194         if (subscribers == null) {
195             subscribers = Sets.newConcurrentHashSet();
196         } else if (subscribers.contains(subscriber)) {
197             // If there is an entry already for this subscriber, remove it, so that it gets the new timestamp
198             subscribers.remove(subscriber);
199         }
200         if (LOG.isTraceEnabled()) {
201             LOG.trace("Adding new subscriber {} for EID {}, source EID {}",
202                     LispAddressStringifier.getString(subscriber.getSrcRloc()),
203                     LispAddressStringifier.getString(subscribedEid),
204                     LispAddressStringifier.getString(subscriber.getSrcEid()));
205         }
206         subscribers.add(subscriber);
207         addSubscribers(subscribedEid, subscribers);
208     }
209
210     // Fixes mapping if request was for simple dst EID but the matched mapping is a SourceDest
211     private MappingRecord fixIfNotSDRequest(MappingRecord mapping, Eid dstEid) {
212         if (mapping.getEid().getAddress() instanceof SourceDestKey
213                 && !(dstEid.getAddress() instanceof SourceDestKey)) {
214             return new MappingRecordBuilder(mapping).setEid(
215                     SourceDestKeyHelper.getDstBinary(mapping.getEid())).build();
216         }
217         return mapping;
218     }
219
220     private boolean locatorsNeedFixing(List<LocatorRecord> locatorRecords) {
221         // no locators - no fixing needed ;)
222         if (locatorRecords == null) {
223             return false;
224         }
225
226         for (LocatorRecord record : locatorRecords) {
227             if (record.getRloc().getAddress() instanceof ExplicitLocatorPath) {
228                 return true;
229             }
230         }
231         return false;
232     }
233
234     // Process locators according to configured policy
235     private MappingRecord updateLocators(MappingRecord mapping, List<ItrRloc> itrRlocs) {
236         // no fixing if elpPolicy is default
237         if (elpPolicy.equalsIgnoreCase("default")) {
238             return mapping;
239         }
240
241         List<LocatorRecord> locatorRecords = mapping.getLocatorRecord();
242
243         // if no updated is needed, just return the mapping
244         if (!locatorsNeedFixing(locatorRecords)) {
245             return mapping;
246         }
247
248         MappingRecordBuilder recordBuilder = new MappingRecordBuilder(mapping);
249         recordBuilder.setLocatorRecord(new ArrayList<LocatorRecord>());
250         try {
251             for (LocatorRecord record : locatorRecords) {
252                 Rloc container = record.getRloc();
253
254                 // For non-ELP RLOCs, or when ELP policy is default, or itrRlocs is null, just add the locator and be
255                 // done
256                 if ((!(container.getAddress() instanceof ExplicitLocatorPath))
257                         || elpPolicy.equalsIgnoreCase("default") || itrRlocs == null) {
258                     recordBuilder.getLocatorRecord().add(
259                             new LocatorRecordBuilder().setLocalLocator(record.isLocalLocator())
260                                     .setRlocProbed(record.isRlocProbed()).setWeight(record.getWeight())
261                                     .setPriority(record.getPriority()).setMulticastWeight(record.getMulticastWeight())
262                                     .setMulticastPriority(record.getMulticastPriority()).setRouted(record.isRouted())
263                                     .setRloc(container).setLocatorId(record.getLocatorId()).build());
264                     continue;
265                 }
266
267                 ExplicitLocatorPath teAddress = ((ExplicitLocatorPath) container.getAddress());
268                 SimpleAddress nextHop = getNextELPHop(teAddress, itrRlocs);
269                 if (nextHop != null) {
270                     java.lang.Short priority = record.getPriority();
271                     if (elpPolicy.equalsIgnoreCase("both")) {
272                         recordBuilder.getLocatorRecord().add(
273                                 new LocatorRecordBuilder().setLocalLocator(record.isLocalLocator())
274                                         .setRlocProbed(record.isRlocProbed()).setWeight(record.getWeight())
275                                         .setPriority(record.getPriority())
276                                         .setMulticastWeight(record.getMulticastWeight())
277                                         .setMulticastPriority(record.getMulticastPriority())
278                                         .setRouted(record.isRouted()).setRloc(container)
279                                         .setLocatorId(record.getLocatorId()).build());
280                         // Make the priority of the added simple locator lower so that ELP is used by default if
281                         // the xTR understands ELP. Exclude 255, since that means don't use for unicast forwarding
282                         // XXX Complex cases like several ELPs with different priorities are not handled
283                         if (priority != 254 || priority != 255) {
284                             priority++;
285                         }
286                     }
287                     // Build and add the simple RLOC
288                     recordBuilder.getLocatorRecord().add(
289                             new LocatorRecordBuilder().setLocalLocator(record.isLocalLocator())
290                                     .setRlocProbed(record.isRlocProbed()).setWeight(record.getWeight())
291                                     .setPriority(priority).setMulticastWeight(record.getMulticastWeight())
292                                     .setMulticastPriority(record.getMulticastPriority()).setRouted(record.isRouted())
293                                     .setRloc(LispAddressUtil.toRloc(nextHop))
294                                     .setLocatorId(record.getLocatorId()).build());
295                 }
296             }
297         } catch (ClassCastException cce) {
298             LOG.error("Class Cast Exception while building EidToLocatorRecord: {}", ExceptionUtils.getStackTrace(cce));
299         }
300
301         return recordBuilder.build();
302     }
303
304     private SimpleAddress getNextELPHop(ExplicitLocatorPath elp, List<ItrRloc> itrRlocs) {
305         SimpleAddress nextHop = null;
306         List<Hop> hops = elp.getExplicitLocatorPath().getHop();
307
308         if (hops != null && hops.size() > 0) {
309             // By default we return the first hop
310             nextHop = hops.get(0).getAddress();
311             for (Hop hop : hops) {
312                 Address hopAddress = LispAddressUtil.addressFromSimpleAddress(hop.getAddress());
313                 for (ItrRloc itrRloc : itrRlocs) {
314                     if (itrRloc.getRloc().getAddress().equals(hopAddress)) {
315                         int iterator = hops.indexOf(hop);
316                         if (iterator < hops.size() - 1) {
317                             nextHop = hops.get(iterator + 1).getAddress();
318                             return nextHop;
319                         }
320                     }
321                 }
322             }
323         }
324
325         return nextHop;
326     }
327
328     @SuppressWarnings("unchecked")
329     private Set<Subscriber> getSubscribers(Eid address) {
330         return (Set<Subscriber>) mapService.getData(MappingOrigin.Southbound, address, SubKeys.SUBSCRIBERS);
331     }
332
333     private void addSubscribers(Eid address, Set<Subscriber> subscribers) {
334         mapService.addData(MappingOrigin.Southbound, address, SubKeys.SUBSCRIBERS, subscribers);
335     }
336
337     @Override
338     public void setSubscriptionService(boolean smr) {
339         subscriptionService = smr;
340     }
341
342     @Override
343     public void setElpPolicy(String elpPolicy) {
344         this.elpPolicy = elpPolicy;
345     }
346
347     @Override
348     public void setShouldAuthenticate(boolean shouldAuthenticate) {
349         this.authenticate = shouldAuthenticate;
350     }
351
352     @Override
353     public void setSmrNotificationListener(ISmrNotificationListener smrNotificationListener) {
354         this.smrNotificationListener = smrNotificationListener;
355     }
356 }