*/
package org.opendaylight.lispflowmapping.implementation.util;
+import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-
+import java.util.Set;
+import org.opendaylight.lispflowmapping.config.ConfigIni;
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
+import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
+import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.SimpleAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.source.dest.key.SourceDestKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.source.dest.key.SourceDestKeyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecordBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
import org.slf4j.LoggerFactory;
/**
- * Utility class to implement merging of locator sets
+ * Utility class to implement merging of locator sets.
*
* @author Lorand Jakab
*
mrb.setSiteId(record.getSiteId());
// For the TTL value we take the minimum of all records
mrb.setRecordTtl(Math.min(mrb.getRecordTtl(), record.getRecordTtl()));
- if (!mrb.getAction().equals(record.getAction())) {
+ if (!mrb.getAction().equals(record.getAction())) {
LOG.warn("Mapping merge operation: actions are different, which one is used is undefined");
}
- if (mrb.isAuthoritative() != record.isAuthoritative()) {
+ if (mrb.isAuthoritative() != record.isAuthoritative()) {
LOG.warn("Mapping merge operation: authoritative status is different, which one is used is undefined");
}
- if (!mrb.getEid().equals(record.getEid())) {
+ if (!mrb.getEid().equals(record.getEid())) {
LOG.warn("Mapping merge operation: EID records are different, which one is used is undefined");
}
}
return newLocator;
}
+ private static int compareLocators(LocatorRecord one, LocatorRecord two) {
+ byte[] oneIp = LispAddressUtil.ipAddressToByteArray(one.getRloc().getAddress());
+ byte[] twoIp = LispAddressUtil.ipAddressToByteArray(two.getRloc().getAddress());
+ return LispAddressUtil.compareIpAddressByteArrays(oneIp, twoIp);
+ }
+
private static void mergeLocatorRecords(MappingRecordBuilder mrb, MappingRecord newRecord) {
List<LocatorRecord> locators = mrb.getLocatorRecord();
- // Optimization: unless we had to merge any of the locators due to differences in weights, etc, we return
- // the original 'locators' List with any new locators added
- boolean mergeHappened = false;
-
- // We assume locators are unique and don't show up several times (with different or identical p/w/mp/mw),
- // so we create a LinkedHashMap (which preserves order) of the locators from the existing merged record,
- // keyed by the Rloc
+ // We assume locators are unique and sorted and don't show up several times (with different or identical
+ // p/w/mp/mw), so we create a LinkedHashMap (which preserves order) of the locators from the existing merged
+ // record, keyed by the Rloc
Map<Rloc, LocatorRecord> locatorMap = new LinkedHashMap<Rloc, LocatorRecord>();
+
+ // All locators to be added to the merge set are first stored in this list
+ List<LocatorRecord> newLocatorList = new ArrayList<LocatorRecord>();
+
for (LocatorRecord locator : locators) {
locatorMap.put(locator.getRloc(), locator);
}
for (LocatorRecord newLocator : newRecord.getLocatorRecord()) {
Rloc newRloc = newLocator.getRloc();
if (locatorMap.containsKey(newRloc)) {
- // XXX LocatorRecord YANG generated class doesn't override equals() so I'm not sure of the behavior
- // here, need to verify if it works as expected
+ // overlapping locator
if (locatorMap.get(newRloc).equals(newLocator)) {
continue;
} else {
LocatorRecord mergedLocator = mergeLocators(locatorMap.get(newRloc), newLocator);
- locatorMap.put(newRloc, mergedLocator);
- mergeHappened = true;
+ newLocatorList.add(mergedLocator);
}
} else {
- // We add both the LinkedHanshMap and the List, in case we can return the original list plus new
- // elements and need not generate a new list with merged locators (which should be the most common
- // scenario).
- locatorMap.put(newRloc, newLocator);
- locators.add(newLocator);
+ // new locator
+ newLocatorList.add(newLocator);
}
}
- if (mergeHappened) {
+ // Build new merged and sorted locator set if need be
+ if (newLocatorList.size() != 0) {
List<LocatorRecord> mergedLocators = new ArrayList<LocatorRecord>();
- for (Map.Entry<Rloc, LocatorRecord> entry : locatorMap.entrySet()) {
- mergedLocators.add(entry.getValue());
+
+ int mlocIt = 0;
+ int locIt = 0;
+ while (mlocIt < newLocatorList.size() && locIt < locators.size()) {
+ int cmp = compareLocators(locators.get(locIt), newLocatorList.get(mlocIt));
+ if (cmp < 0) {
+ mergedLocators.add(locators.get(locIt));
+ locIt++;
+ } else if (cmp > 0) {
+ mergedLocators.add(newLocatorList.get(mlocIt));
+ mlocIt++;
+ } else {
+ // when a locator appears in both lists, keep the new (merged) one and skip the old
+ mergedLocators.add(newLocatorList.get(mlocIt));
+ mlocIt++;
+ locIt++;
+ }
+ }
+ while (locIt < locators.size()) {
+ mergedLocators.add(locators.get(locIt));
+ locIt++;
+ }
+ while (mlocIt < newLocatorList.size()) {
+ mergedLocators.add(newLocatorList.get(mlocIt));
+ mlocIt++;
}
mrb.setLocatorRecord(mergedLocators);
- } else {
- // TODO Check if this is necessary after and .add() was called on locators
- mrb.setLocatorRecord(locators);
}
}
public static MappingRecord mergeMappings(MappingRecord currentMergedMapping, MappingRecord newMapping,
- byte[] xtrId, Date regdate) {
+ XtrId xtrId, Date regdate) {
if (currentMergedMapping == null) {
return newMapping;
}
return mrb.build();
}
- public static MappingRecord mergeXtrIdMappings(List<Object> records) {
- MappingRecordBuilder mrb = new MappingRecordBuilder((MappingRecord) records.get(0));
- for (int i = 1; i < records.size(); i++) {
- MappingRecord record = (MappingRecord) records.get(i);
+ public static MappingData mergeXtrIdMappings(List<Object> mappingDataList, List<XtrId> expiredMappings,
+ Set<IpAddressBinary> sourceRlocs) {
+ MappingRecordBuilder mrb = null;
+ XtrId xtrId = null;
+ Long timestamp = Long.MAX_VALUE;
+
+ for (int i = 0; i < mappingDataList.size(); i++) {
+ MappingData mappingData = (MappingData) mappingDataList.get(i);
+ MappingRecord record = mappingData.getRecord();
+
+ // Skip expired mappings and add them to a list to be returned to the caller
+ if (timestampIsExpired(mappingData.getTimestamp())) {
+ expiredMappings.add(mappingData.getXtrId());
+ continue;
+ }
+
+ if (mrb == null) {
+ mrb = new MappingRecordBuilder(record);
+ }
+
+ // Save the oldest valid timestamp
+ if (mappingData.getTimestamp().getTime() < timestamp) {
+ timestamp = mappingData.getTimestamp().getTime();
+ xtrId = mappingData.getXtrId();
+ }
+
+ // Merge record fields and locators
mergeCommonMappingRecordFields(mrb, record);
mergeLocatorRecords(mrb, record);
+
+ // Save source locator for use in Map-Notify
+ sourceRlocs.add(record.getSourceRloc());
}
+
+ if (mrb == null) {
+ LOG.warn("All mappings expired when merging! Unexpected!");
+ return null;
+ }
+ mrb.setXtrId(xtrId);
+
+ return new MappingData(mrb.build(), new Date(timestamp));
+ }
+
+ /*
+ * The following three methods intentionally throw an exception when their argument is null, because they can't
+ * decide based on that to return true or false, so the calling function should do that and only call these with
+ * non-null arguments
+ */
+ public static boolean mappingIsExpired(MappingData mappingData) {
+ Preconditions.checkNotNull(mappingData, "mapping should not be null!");
+ if (mappingData.getTimestamp() != null) {
+ return timestampIsExpired(mappingData.getTimestamp());
+ }
+ return false;
+ }
+
+ public static boolean timestampIsExpired(Date timestamp) {
+ Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
+ return timestampIsExpired(timestamp.getTime());
+ }
+
+ public static boolean timestampIsExpired(Long timestamp) {
+ Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
+ if ((System.currentTimeMillis() - timestamp) > ConfigIni.getInstance().getRegistrationValiditySb()) {
+ return true;
+ }
+ return false;
+ }
+
+ public static MappingData computeNbSbIntersection(MappingData nbMappingData,
+ MappingData sbMappingData) {
+ return new MappingData(computeNbSbIntersection(nbMappingData.getRecord(), sbMappingData.getRecord()));
+ }
+
+ private static MappingRecord computeNbSbIntersection(MappingRecord nbMapping, MappingRecord sbMapping) {
+ // returns a MappingRecord which has the more specific EID, and intersection of locator records.
+ // If locators intersection is empty, original NB mapping is returned.
+ // The intersection is only computed for mappings with maskable EIDs.
+ // Supports both maskable and non-maskable EIDs
+
+ MappingRecordBuilder mrb = new MappingRecordBuilder(nbMapping);
+
+ if (MaskUtil.isMaskable(sbMapping.getEid().getAddress())
+ && MaskUtil.isMaskable(nbMapping.getEid().getAddress())) {
+
+ short sbMask = MaskUtil.getMaskForAddress(sbMapping.getEid().getAddress());
+ short nbMask = MaskUtil.getMaskForAddress(nbMapping.getEid().getAddress());
+
+ if (nbMapping.getEid().getAddress() instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
+ .ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey) {
+ nbMask = SourceDestKeyHelper.getDstMask(nbMapping.getEid());
+ if (nbMask < sbMask) {
+ // We have to create a new SourceDest EID, where the source is same as the
+ // one in NB record, and dest EID is the more specific from SB mapping record.
+
+ SourceDestKey srcDstKey = ((org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp
+ .address.types.rev151105.lisp.address.address.SourceDestKey) nbMapping.getEid()
+ .getAddress()).getSourceDestKey();
+ SourceDestKeyBuilder sdb = new SourceDestKeyBuilder(srcDstKey);
+ sdb.setDest(new SimpleAddress(getIpPrefix(sbMapping.getEid().getAddress())));
+ mrb.setEid(LispAddressUtil.asSrcDstEid(sdb.build(), nbMapping.getEid().getVirtualNetworkId()));
+ }
+ } else if (nbMask < sbMask) {
+ // Both EIDs are IP prefixes. SB mapping is a subprefix so we have to update EID intersection
+ mrb.setEid(sbMapping.getEid());
+ }
+ }
+ // find and update locators intersection if not empty
+ List<LocatorRecord> commonLocators = getCommonLocatorRecords(nbMapping, sbMapping);
+ if (commonLocators != null && !commonLocators.isEmpty()) {
+ mrb.setLocatorRecord(commonLocators);
+ }
+
return mrb.build();
}
+
+ private static List<LocatorRecord> getCommonLocatorRecords(MappingRecord nbMapping, MappingRecord sbMapping) {
+ // This method updates the MappingRecord builder with the intersection of the locator records
+ // from the two mappings. NB mapping records fields have precedence, only Priority is updated
+ // from SB mapping if p is 255.
+
+ // Return null when NB is a negative mapping
+ if (nbMapping.getLocatorRecord() == null || nbMapping.getLocatorRecord().isEmpty()) {
+ return null;
+ }
+
+ List<LocatorRecord> sbLocators = sbMapping.getLocatorRecord();
+
+ // We assume locators are unique and don't show up several times (with different or identical p/w/mp/mw),
+ // so we create a HashMap of the locators from the SB mapping record, keyed by the Rloc
+ Map<Rloc, LocatorRecord> sbLocatorMap = new HashMap<Rloc, LocatorRecord>();
+ for (LocatorRecord locator : sbLocators) {
+ sbLocatorMap.put(locator.getRloc(), locator);
+ }
+
+ // Gradually building final list of common locators, in order that they appear in NB Mapping
+ List<LocatorRecord> commonLocators = new ArrayList<LocatorRecord>();
+
+ for (LocatorRecord nbLocator : nbMapping.getLocatorRecord()) {
+ Rloc nbRloc = nbLocator.getRloc();
+ if (sbLocatorMap.containsKey(nbRloc)) {
+ // common locator found. use the NB record as the common locator.
+
+ if (sbLocatorMap.get(nbRloc).getPriority() == (short) 255) {
+ // if SB locator has p == 255 then common locator takes all NB fields except for p
+ // which must be set to 255
+ LocatorRecordBuilder lrb = new LocatorRecordBuilder(nbLocator);
+ lrb.setPriority((short) 255);
+ commonLocators.add(lrb.build());
+ } else {
+ commonLocators.add(nbLocator);
+ }
+ }
+ }
+ return commonLocators;
+ }
+
+ private static IpPrefix getIpPrefix(Address address) {
+ IpPrefix ipPrefix = null;
+
+ if (address instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns
+ .yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4Prefix) {
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp
+ .address.address.Ipv4Prefix lispPrefix = (org.opendaylight.yang.gen.v1.urn.ietf.params
+ .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4Prefix) address;
+
+ Ipv4Prefix inetPrefix = new Ipv4Prefix(lispPrefix.getIpv4Prefix());
+ ipPrefix = new IpPrefix(inetPrefix);
+ } else if (address instanceof org.opendaylight.yang.gen.v1.urn.ietf.params
+ .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6Prefix) {
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp
+ .address.address.Ipv6Prefix lispPrefix = (org.opendaylight.yang.gen.v1.urn.ietf.params
+ .xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6Prefix) address;
+
+ Ipv6Prefix inetPrefix = new Ipv6Prefix(lispPrefix.getIpv6Prefix());
+ ipPrefix = new IpPrefix(inetPrefix);
+ } else {
+ LOG.warn("Southbound mapping address is not an IpPrefix");
+ }
+ return ipPrefix;
+ }
}