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