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