2 * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
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
8 package org.opendaylight.lispflowmapping.implementation.util;
10 import static org.opendaylight.yangtools.yang.common.UintConversions.fromJava;
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.Date;
15 import java.util.HashMap;
16 import java.util.LinkedHashMap;
17 import java.util.List;
20 import org.opendaylight.lispflowmapping.config.ConfigIni;
21 import org.opendaylight.lispflowmapping.lisp.type.MappingData;
22 import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
23 import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
24 import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.SimpleAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.Address;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.source.dest.key.SourceDestKey;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.source.dest.key.SourceDestKeyBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecordBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * Utility class to implement merging of locator sets.
45 * @author Lorand Jakab
48 public final class MappingMergeUtil {
49 private static final Logger LOG = LoggerFactory.getLogger(MappingMergeUtil.class);
51 // Utility class, should not be instantiated
52 private MappingMergeUtil() {
55 private static void mergeCommonMappingRecordFields(MappingRecordBuilder mrb, MappingRecord record) {
56 // Set xTR-ID and site-ID from the current mapping, it help with determining the timestamp
57 mrb.setXtrId(record.getXtrId());
58 mrb.setSiteId(record.getSiteId());
59 // For the TTL value we take the minimum of all records
60 mrb.setRecordTtl(Math.min(mrb.getRecordTtl(), record.getRecordTtl()));
61 if (!mrb.getAction().equals(record.getAction())) {
62 LOG.warn("Mapping merge operation: actions are different, which one is used is undefined");
64 if (mrb.isAuthoritative() != record.isAuthoritative()) {
65 LOG.warn("Mapping merge operation: authoritative status is different, which one is used is undefined");
67 if (!mrb.getEid().equals(record.getEid())) {
68 LOG.warn("Mapping merge operation: EID records are different, which one is used is undefined");
72 private static LocatorRecord mergeLocators(LocatorRecord existingLocator, LocatorRecord newLocator) {
73 if (existingLocator.isLocalLocator()) {
74 return existingLocator;
79 private static int compareLocators(LocatorRecord one, LocatorRecord two) {
80 byte[] oneIp = LispAddressUtil.ipAddressToByteArray(one.getRloc().getAddress());
81 byte[] twoIp = LispAddressUtil.ipAddressToByteArray(two.getRloc().getAddress());
82 return LispAddressUtil.compareIpAddressByteArrays(oneIp, twoIp);
85 private static void mergeLocatorRecords(MappingRecordBuilder mrb, MappingRecord newRecord) {
86 List<LocatorRecord> locators = mrb.getLocatorRecord();
88 // We assume locators are unique and sorted and don't show up several times (with different or identical
89 // p/w/mp/mw), so we create a LinkedHashMap (which preserves order) of the locators from the existing merged
90 // record, keyed by the Rloc
91 Map<Rloc, LocatorRecord> locatorMap = new LinkedHashMap<Rloc, LocatorRecord>();
93 // All locators to be added to the merge set are first stored in this list
94 List<LocatorRecord> newLocatorList = new ArrayList<LocatorRecord>();
96 for (LocatorRecord locator : locators) {
97 locatorMap.put(locator.getRloc(), locator);
99 for (LocatorRecord newLocator : newRecord.getLocatorRecord()) {
100 Rloc newRloc = newLocator.getRloc();
101 if (locatorMap.containsKey(newRloc)) {
102 // overlapping locator
103 if (!locatorMap.get(newRloc).equals(newLocator)) {
104 LocatorRecord mergedLocator = mergeLocators(locatorMap.get(newRloc), newLocator);
105 newLocatorList.add(mergedLocator);
109 newLocatorList.add(newLocator);
113 // Build new merged and sorted locator set if need be
114 if (!newLocatorList.isEmpty()) {
115 List<LocatorRecord> mergedLocators = new ArrayList<LocatorRecord>();
119 while (mlocIt < newLocatorList.size() && locIt < locators.size()) {
120 int cmp = compareLocators(locators.get(locIt), newLocatorList.get(mlocIt));
122 mergedLocators.add(locators.get(locIt));
124 } else if (cmp > 0) {
125 mergedLocators.add(newLocatorList.get(mlocIt));
128 // when a locator appears in both lists, keep the new (merged) one and skip the old
129 mergedLocators.add(newLocatorList.get(mlocIt));
134 while (locIt < locators.size()) {
135 mergedLocators.add(locators.get(locIt));
138 while (mlocIt < newLocatorList.size()) {
139 mergedLocators.add(newLocatorList.get(mlocIt));
142 mrb.setLocatorRecord(mergedLocators);
146 public static MappingRecord mergeMappings(MappingRecord currentMergedMapping, MappingRecord newMapping,
147 XtrId xtrId, Date regdate) {
148 if (currentMergedMapping == null) {
152 MappingRecordBuilder mrb = new MappingRecordBuilder(currentMergedMapping);
153 mergeCommonMappingRecordFields(mrb, newMapping);
154 mergeLocatorRecords(mrb, newMapping);
158 mrb.setTimestamp(regdate.getTime());
164 public static MappingData mergeXtrIdMappings(List<Object> mappingDataList, List<MappingData> expiredMappingDataList,
165 Set<IpAddressBinary> sourceRlocs) {
166 MappingRecordBuilder mrb = null;
168 Long timestamp = Long.MAX_VALUE;
170 for (int i = 0; i < mappingDataList.size(); i++) {
171 MappingData mappingData = (MappingData) mappingDataList.get(i);
172 MappingRecord record = mappingData.getRecord();
174 // Skip expired mappings and add them to a list to be returned to the caller
175 if (timestampIsExpired(mappingData.getTimestamp())) {
176 expiredMappingDataList.add(mappingData);
181 mrb = new MappingRecordBuilder(record);
184 // Save the oldest valid timestamp
185 if (mappingData.getTimestamp().getTime() < timestamp) {
186 timestamp = mappingData.getTimestamp().getTime();
187 xtrId = mappingData.getXtrId();
190 // Merge record fields and locators
191 mergeCommonMappingRecordFields(mrb, record);
192 mergeLocatorRecords(mrb, record);
194 // Save source locator for use in Map-Notify
195 sourceRlocs.add(record.getSourceRloc());
199 LOG.warn("All mappings expired when merging! Unexpected!");
204 return new MappingData(mrb.build(), new Date(timestamp));
208 * The following three methods intentionally throw an exception when their argument is null, because they can't
209 * decide based on that to return true or false, so the calling function should do that and only call these with
212 public static boolean mappingIsExpired(MappingData mappingData) {
213 Preconditions.checkNotNull(mappingData, "mapping should not be null!");
214 if (mappingData.getTimestamp() != null) {
215 return timestampIsExpired(mappingData.getTimestamp());
220 public static boolean timestampIsExpired(Date timestamp) {
221 Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
222 return timestampIsExpired(timestamp.getTime());
225 private static boolean timestampIsExpired(Long timestamp) {
226 Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
227 if ((System.currentTimeMillis() - timestamp) > ConfigIni.getInstance().getRegistrationValiditySb()) {
233 public static MappingData computeNbSbIntersection(MappingData nbMappingData,
234 MappingData sbMappingData) {
235 return new MappingData(computeNbSbIntersection(nbMappingData.getRecord(), sbMappingData.getRecord()));
238 private static MappingRecord computeNbSbIntersection(MappingRecord nbMapping, MappingRecord sbMapping) {
239 // returns a MappingRecord which has the more specific EID, and intersection of locator records.
240 // If locators intersection is empty, original NB mapping is returned.
241 // The intersection is only computed for mappings with maskable EIDs.
242 // Supports both maskable and non-maskable EIDs
244 MappingRecordBuilder mrb = new MappingRecordBuilder(nbMapping);
246 if (MaskUtil.isMaskable(sbMapping.getEid().getAddress())
247 && MaskUtil.isMaskable(nbMapping.getEid().getAddress())) {
249 short sbMask = MaskUtil.getMaskForAddress(sbMapping.getEid().getAddress());
250 short nbMask = MaskUtil.getMaskForAddress(nbMapping.getEid().getAddress());
252 if (nbMapping.getEid().getAddress() instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
253 .ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey) {
254 nbMask = SourceDestKeyHelper.getDstMask(nbMapping.getEid());
255 if (nbMask < sbMask) {
256 // We have to create a new SourceDest EID, where the source is same as the
257 // one in NB record, and dest EID is the more specific from SB mapping record.
259 SourceDestKey srcDstKey = ((org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp
260 .address.types.rev151105.lisp.address.address.SourceDestKey) nbMapping.getEid()
261 .getAddress()).getSourceDestKey();
262 SourceDestKeyBuilder sdb = new SourceDestKeyBuilder(srcDstKey);
263 sdb.setDest(new SimpleAddress(getIpPrefix(sbMapping.getEid().getAddress())));
264 mrb.setEid(LispAddressUtil.asSrcDstEid(sdb.build(), nbMapping.getEid().getVirtualNetworkId()));
266 } else if (nbMask < sbMask) {
267 // Both EIDs are IP prefixes. SB mapping is a subprefix so we have to update EID intersection
268 mrb.setEid(sbMapping.getEid());
271 // find and update locators intersection if not empty
272 List<LocatorRecord> commonLocators = getCommonLocatorRecords(nbMapping, sbMapping);
273 if (commonLocators != null && !commonLocators.isEmpty()) {
274 mrb.setLocatorRecord(commonLocators);
280 private static List<LocatorRecord> getCommonLocatorRecords(MappingRecord nbMapping, MappingRecord sbMapping) {
281 // This method updates the MappingRecord builder with the intersection of the locator records
282 // from the two mappings. NB mapping records fields have precedence, only Priority is updated
283 // from SB mapping if p is 255.
285 // Return null when NB is a negative mapping
286 if (nbMapping.getLocatorRecord() == null || nbMapping.getLocatorRecord().isEmpty()) {
290 List<LocatorRecord> sbLocators = sbMapping.getLocatorRecord();
292 // We assume locators are unique and don't show up several times (with different or identical p/w/mp/mw),
293 // so we create a HashMap of the locators from the SB mapping record, keyed by the Rloc
294 Map<Rloc, LocatorRecord> sbLocatorMap = new HashMap<Rloc, LocatorRecord>();
295 for (LocatorRecord locator : sbLocators) {
296 sbLocatorMap.put(locator.getRloc(), locator);
299 // Gradually building final list of common locators, in order that they appear in NB Mapping
300 List<LocatorRecord> commonLocators = new ArrayList<LocatorRecord>();
302 for (LocatorRecord nbLocator : nbMapping.getLocatorRecord()) {
303 Rloc nbRloc = nbLocator.getRloc();
304 if (sbLocatorMap.containsKey(nbRloc)) {
305 // common locator found. use the NB record as the common locator.
307 if (sbLocatorMap.get(nbRloc).getPriority().toJava() == (short) 255) {
308 // if SB locator has p == 255 then common locator takes all NB fields except for p
309 // which must be set to 255
310 LocatorRecordBuilder lrb = new LocatorRecordBuilder(nbLocator);
311 lrb.setPriority(fromJava((short) 255));
312 commonLocators.add(lrb.build());
314 commonLocators.add(nbLocator);
318 return commonLocators;
321 private static IpPrefix getIpPrefix(Address address) {
322 IpPrefix ipPrefix = null;
324 if (address instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns
325 .yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4Prefix) {
326 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp
327 .address.address.Ipv4Prefix lispPrefix = (org.opendaylight.yang.gen.v1.urn.ietf.params
328 .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4Prefix) address;
330 Ipv4Prefix inetPrefix = new Ipv4Prefix(lispPrefix.getIpv4Prefix());
331 ipPrefix = new IpPrefix(inetPrefix);
332 } else if (address instanceof org.opendaylight.yang.gen.v1.urn.ietf.params
333 .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6Prefix) {
334 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp
335 .address.address.Ipv6Prefix lispPrefix = (org.opendaylight.yang.gen.v1.urn.ietf.params
336 .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6Prefix) address;
338 Ipv6Prefix inetPrefix = new Ipv6Prefix(lispPrefix.getIpv6Prefix());
339 ipPrefix = new IpPrefix(inetPrefix);
341 LOG.warn("Southbound mapping address is not an IpPrefix");