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