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