import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
testPositiveMappingRemoval();
// https://bugs.opendaylight.org/show_bug.cgi?id=9037
- testPositivePrefixOverlappingNegativePrefix();
+ testPositivePrefixOverlappingNegativePrefix_moreSpecific();
+
+ // https://bugs.opendaylight.org/show_bug.cgi?id=9116
+ testPositivePrefixOverlappingNegativePrefix_lessSpecific();
+ }
+
+ @Test
+ public void testMappingChangeCases() {
+ testSubtree();
}
private void testRepeatedSmr() throws SocketTimeoutException, UnknownHostException {
printMapCacheState();
}
- private void testPositivePrefixOverlappingNegativePrefix() {
+ private void testPositivePrefixOverlappingNegativePrefix_moreSpecific() {
cleanUP();
insertNBMappings(1L, "192.167.0.0/16", "192.169.0.0/16");
assertTrue(MappingRecordUtil.isNegativeMapping(mr));
}
+ private void testPositivePrefixOverlappingNegativePrefix_lessSpecific() {
+ cleanUP();
+
+ insertNBMappings(1L, "192.167.0.0/16", "192.169.0.0/16");
+
+ MapReply mapReply = lms.handleMapRequest(newMapRequest(1L, "192.168.0.1/32"));
+ Eid expectedNegativePrefix = LispAddressUtil.asIpv4PrefixBinaryEid(1L, "192.168.0.0/16");
+ MappingRecord mr = mapReply.getMappingRecordItem().get(0).getMappingRecord();
+ assertEquals(expectedNegativePrefix, mr.getEid());
+ assertTrue(MappingRecordUtil.isNegativeMapping(mr));
+
+ insertNBMappings(1L, "192.0.0.0/8");
+
+ mapReply = lms.handleMapRequest(newMapRequest(1L, "192.168.0.1/32"));
+ Eid expectedPositivePrefix = LispAddressUtil.asIpv4PrefixBinaryEid(1L, "192.0.0.0/8");
+ mr = mapReply.getMappingRecordItem().get(0).getMappingRecord();
+ assertEquals(expectedPositivePrefix, mr.getEid());
+ assertTrue(MappingRecordUtil.isPositiveMapping(mr));
+ }
+
+ private void testSubtree() {
+ cleanUP();
+
+ insertSBMappings(1L, "10.0.0.0/8",
+ "10.0.0.0/16", "10.2.0.0/16", "10.255.0.0/16");
+ Eid queryPrefix = LispAddressUtil.asIpv4PrefixBinaryEid(1L, "10.0.0.0/9");
+ Set<Eid> subtreePrefixes = mapService.getSubtree(MappingOrigin.Southbound, queryPrefix);
+ LOG.debug("Subtree prefix set for EID {}: {}", LispAddressStringifier.getString(queryPrefix),
+ LispAddressStringifier.getString(subtreePrefixes));
+ Set<Eid> expectedSubtreePrefixes = new HashSet<>();
+ expectedSubtreePrefixes.add(LispAddressUtil.asIpv4PrefixBinaryEid(1L, "10.0.0.0/16"));
+ expectedSubtreePrefixes.add(LispAddressUtil.asIpv4PrefixBinaryEid(1L, "10.2.0.0/16"));
+ assertEquals(expectedSubtreePrefixes, subtreePrefixes);
+ }
+
private void insertMappings() {
cleanUP();
mapService.setLookupPolicy(IMappingService.LookupPolicy.NB_AND_SB);
*/
SimpleImmutableEntry<Eid, Map<String, ?>> getBestPair(Object key);
+ /**
+ * Look up the covering prefix for the argument, but exclude the argument itself, so the result is always less
+ * specific than the lookup key.
+ *
+ * @param key
+ * The eid prefix, IPv4 or IPv6, to be looked up. Key must be normalized.
+ * @return The covering prefix.
+ */
+ Eid getCoveringLessSpecific(Eid key);
+
/**
* Get parent prefix.
*
*/
void removeXtrIdMappings(Eid key, List<XtrId> xtrIds);
+ /**
+ * Look up the covering prefix for the argument, but exclude the argument itself, so the result is always less
+ * specific than the lookup key.
+ *
+ * @param key
+ * The eid prefix, IPv4 or IPv6, to be looked up. Key must be normalized.
+ * @return The covering prefix.
+ */
+ Eid getCoveringLessSpecific(Eid key);
+
/**
* Returns the parent prefix for given key.
*
*/
MappingData addNegativeMapping(Eid key);
+ /**
+ * Update mapping.
+ *
+ * @param origin
+ * Table where mapping should be added
+ * @param key
+ * Key of the mapping
+ * @param mapping
+ * Mapping to be stored
+ */
+ void updateMapping(MappingOrigin origin, Eid key, MappingData mapping);
+
/**
* Retrieves mapping for the provided src and dst key.
*
*/
Eid getWidestNegativePrefix(Eid key);
+ /**
+ * Retrieves the subtree of a maskable prefix from the given map-cache.
+ *
+ * @param origin
+ * Table where the key should be looked up
+ * @param key
+ * Key to be looked up
+ * @return The child prefixes of the prefix, including the prefix itself if present
+ */
+ Set<Eid> getSubtree(MappingOrigin origin, Eid key);
+
/**
* Refresh southbound mapping registration timestamp.
*
*/
Eid getWidestNegativePrefix(Eid key);
+ /**
+ * Retrieves the subtree of a maskable prefix from the given map-cache.
+ *
+ * @param origin
+ * Table where the key should be looked up
+ * @param key
+ * Key to be looked up
+ * @return The child prefixes of the prefix, including the prefix itself if present
+ */
+ Set<Eid> getSubtree(MappingOrigin origin, Eid key);
+
/**
* Refresh southbound mapping registration timestamp.
*
type mapping-change;
}
uses lisp-proto:mapping-record-container;
+ uses lisp-proto:eid-container;
list subscriber-item {
description "The list of subscribers to be notified of this change.";
uses lisp-proto:subscriber-data-grouping;
return mappingSystem.getWidestNegativePrefix(key);
}
+ @Override
+ public Set<Eid> getSubtree(MappingOrigin origin, Eid key) {
+ return mappingSystem.getSubtree(origin, key);
+ }
+
@Override
public void addAuthenticationKey(Eid key, MappingAuthkey authKey) {
dsbe.addAuthenticationKey(DSBEInputUtil.toAuthenticationKey(key, authKey));
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
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.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChange;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChanged;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.AuthenticationKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
tableMap.put(MappingOrigin.Southbound, smc);
}
+ @Override
+ public void updateMapping(MappingOrigin origin, Eid key, MappingData mappingData) {
+ addMapping(origin, key, mappingData, MappingChange.Updated);
+ }
+
+ @Override
public void addMapping(MappingOrigin origin, Eid key, MappingData mappingData) {
+ addMapping(origin, key, mappingData, MappingChange.Created);
+ }
+
+ private void addMapping(MappingOrigin origin, Eid key, MappingData mappingData, MappingChange changeType) {
sbMappingTimeoutService.removeExpiredMappings();
tableMap.get(origin).addMapping(key, mappingData);
- // We store explicit negative mappings in SB. That may cause issues, see bug 9037 for details
if (origin != MappingOrigin.Southbound) {
+ // If a NB mapping is added, we need to check if it's covering negative mappings in SB, and remove those
handleSbNegativeMappings(key);
+ // Notifications for SB changes are sent in the SB code itself, so this is called for non-SB additions only
+ notifyChange(key, mappingData.getRecord(), changeType);
}
}
}
private void handleSbNegativeMappings(Eid key) {
+ Set<Eid> childPrefixes = getSubtree(MappingOrigin.Southbound, key);
+
+ // We don't want to remove a newly added negative mapping, so remove it from the child set
+ childPrefixes.remove(key);
+
+ LOG.trace("handleSbNegativeMappings(): subtree prefix set for EID {} (excluding the EID itself): {}",
+ LispAddressStringifier.getString(key),
+ LispAddressStringifier.getString(childPrefixes));
+ if (childPrefixes == null || childPrefixes.isEmpty()) {
+ // The assumption here is that negative prefixes are well maintained and never overlapping.
+ // If we have children for the EID, no parent lookup should thus be necessary.
+ Eid parentPrefix = smc.getCoveringLessSpecific(key);
+ LOG.trace("handleSbNegativeMappings(): parent prefix for EID {}: {}",
+ LispAddressStringifier.getString(key),
+ LispAddressStringifier.getString(parentPrefix));
+ handleSbNegativeMapping(parentPrefix);
+ return;
+ }
+
+ for (Eid prefix : childPrefixes) {
+ handleSbNegativeMapping(prefix);
+ }
+ }
+
+ private void handleSbNegativeMapping(Eid key) {
MappingData mappingData = getSbMappingWithExpiration(null, key, null);
if (mappingData != null && mappingData.isNegative().or(false)) {
removeSbMapping(mappingData.getRecord().getEid(), mappingData);
public MappingData addNegativeMapping(Eid key) {
MappingRecord mapping = buildNegativeMapping(key);
MappingData mappingData = new MappingData(mapping);
- LOG.debug("Adding negative mapping for EID {}", LispAddressStringifier.getString(key));
+ LOG.debug("Adding negative mapping for EID {}", LispAddressStringifier.getString(mapping.getEid()));
LOG.trace(mappingData.getString());
smc.addMapping(mapping.getEid(), mappingData);
dsbe.addMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, mapping.getEid(), null, mappingData));
Set<Subscriber> subscribers = getSubscribers(key);
smc.removeMapping(key);
dsbe.removeMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mappingData));
- notifyChange(mappingData, subscribers, null, MappingChange.Removed);
- removeSubscribers(key);
+ publishNotification(mappingData.getRecord(), null, subscribers, null, MappingChange.Removed);
+ removeSubscribersConditionally(MappingOrigin.Southbound, key);
}
private void removeFromSbTimeoutService(Eid key) {
}
}
+ @Override
+ public Set<Eid> getSubtree(MappingOrigin origin, Eid key) {
+ if (!MaskUtil.isMaskable(key.getAddress())) {
+ LOG.warn("Child prefixes only make sense for maskable addresses!");
+ return Collections.emptySet();
+ }
+
+ return tableMap.get(origin).getSubtree(key);
+ }
+
@Override
public void removeMapping(MappingOrigin origin, Eid key) {
+ Eid dstAddr = null;
Set<Subscriber> subscribers = null;
Set<Subscriber> dstSubscribers = null;
MappingData mapping = (MappingData) tableMap.get(origin).getMapping(null, key);
LOG.trace(mapping.getString());
}
- MappingData notificationMapping = mapping;
+ MappingRecord notificationMapping = null;
if (mapping != null) {
+ notificationMapping = mapping.getRecord();
subscribers = getSubscribers(key);
// For SrcDst LCAF also send SMRs to Dst prefix
if (key.getAddress() instanceof SourceDestKey) {
- Eid dstAddr = SourceDestKeyHelper.getDstBinary(key);
+ dstAddr = SourceDestKeyHelper.getDstBinary(key);
dstSubscribers = getSubscribers(dstAddr);
- if (!(mapping.getRecord().getEid().getAddress() instanceof SourceDestKey)) {
- notificationMapping = new MappingData(new MappingRecordBuilder().setEid(key).build());
- }
}
}
- removeSubscribers(key);
+ removeSubscribersConditionally(origin, key);
if (origin == MappingOrigin.Southbound) {
removeFromSbTimeoutService(key);
}
if (notificationMapping != null) {
- notifyChange(notificationMapping, subscribers, dstSubscribers, MappingChange.Removed);
+ publishNotification(notificationMapping, key, subscribers, dstSubscribers, MappingChange.Removed);
+ notifyChildren(key, notificationMapping, MappingChange.Removed);
+ if (dstAddr != null) {
+ notifyChildren(dstAddr, notificationMapping, MappingChange.Removed);
+ }
+ }
+ }
+
+ public void notifyChange(Eid eid, MappingRecord mapping, MappingChange mappingChange) {
+ Set<Subscriber> subscribers = getSubscribers(eid);
+
+ Set<Subscriber> dstSubscribers = null;
+ // For SrcDst LCAF also send SMRs to Dst prefix
+ if (eid.getAddress() instanceof SourceDestKey) {
+ Eid dstAddr = SourceDestKeyHelper.getDstBinary(eid);
+ dstSubscribers = getSubscribers(dstAddr);
+ notifyChildren(dstAddr, mapping, mappingChange);
+ }
+ publishNotification(mapping, eid, subscribers, dstSubscribers, mappingChange);
+
+ notifyChildren(eid, mapping, mappingChange);
+ }
+
+ private void notifyChildren(Eid eid, MappingRecord mapping, MappingChange mappingChange) {
+ // Update/created only happens for NB mappings. We assume no overlapping prefix support for NB mappings - so
+ // this NB mapping should not have any children. Both for NB first and NB&SB cases all subscribers for SB
+ // prefixes that are equal or more specific to this NB prefix have to be notified of the potential change.
+ // Each subscriber is notified for the prefix that it is currently subscribed to, and MS should return to them
+ // a Map-Reply with the same prefix and update mapping in cases of EID_INTERSECTION_RLOC_NB_FIRST which is a
+ // new option we are creating TODO
+ Set<Eid> childPrefixes = getSubtree(MappingOrigin.Southbound, eid);
+ if (childPrefixes == null || childPrefixes.isEmpty()) {
+ return;
+ }
+
+ childPrefixes.remove(eid);
+ for (Eid prefix : childPrefixes) {
+ Set<Subscriber> subscribers = getSubscribers(prefix);
+ publishNotification(mapping, prefix, subscribers, null, mappingChange);
}
}
- private void notifyChange(MappingData mapping, Set<Subscriber> subscribers, Set<Subscriber> dstSubscribers,
- MappingChange mappingChange) {
- MappingChanged notification = MSNotificationInputUtil.toMappingChanged(mapping, subscribers, dstSubscribers,
- mappingChange);
+ private void publishNotification(MappingRecord mapping, Eid eid, Set<Subscriber> subscribers,
+ Set<Subscriber> dstSubscribers, MappingChange mappingChange) {
try {
- notificationPublishService.putNotification(notification);
+ // The notifications are used for sending SMR.
+ notificationPublishService.putNotification(MSNotificationInputUtil.toMappingChanged(
+ mapping, eid, subscribers, dstSubscribers, mappingChange));
} catch (InterruptedException e) {
LOG.warn("Notification publication interrupted!");
}
}
-
/*
* Merges adjacent negative prefixes and notifies their subscribers.
*/
return subscribers;
}
+ /*
+ * Only remove subscribers if there is no exact match mapping in the map-cache other than the one specified by
+ * origin. Right now we only have two origins, but in case we will have more, we only need to update this method,
+ * not the callers. We use getData() instead of getMapping to do exact match instead of longest prefix match.
+ */
+ private void removeSubscribersConditionally(MappingOrigin origin, Eid address) {
+ if (origin == MappingOrigin.Southbound) {
+ if (pmc.getData(address, SubKeys.RECORD) == null) {
+ removeSubscribers(address);
+ }
+ } else if (origin == MappingOrigin.Northbound) {
+ if (smc.getData(address, SubKeys.RECORD) == null) {
+ removeSubscribers(address);
+ }
+ }
+ }
+
private void removeSubscribers(Eid address) {
if (LOG.isDebugEnabled()) {
LOG.debug("Removing subscribers for EID {}", LispAddressStringifier.getString(address));
public void onMappingChanged(MappingChanged notification) {
LOG.trace("MappingChanged event of type: `{}'", notification.getChangeType());
if (subscriptionService) {
- Eid eid = notification.getMappingRecord().getEid();
+ Eid eid = notification.getEid();
+ if (eid == null) {
+ eid = notification.getMappingRecord().getEid();
+ }
Set<Subscriber> subscribers = MSNotificationInputUtil.toSubscriberSet(notification.getSubscriberItem());
LoggingUtil.logSubscribers(LOG, eid, subscribers);
if (mapService.isMaster()) {
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
-import org.opendaylight.lispflowmapping.implementation.util.MSNotificationInputUtil;
-import org.opendaylight.lispflowmapping.interfaces.dao.Subscriber;
import org.opendaylight.lispflowmapping.interfaces.mapcache.IMappingSystem;
import org.opendaylight.lispflowmapping.lisp.type.MappingData;
import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
-import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
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.mappingservice.rev150906.MappingChange;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingDatabase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
continue;
}
- MappingChange mappingChange;
+ final Mapping convertedMapping = convertToBinaryIfNecessary(mapping);
+ Eid convertedEid = convertedMapping.getMappingRecord().getEid();
if (ModificationType.SUBTREE_MODIFIED == mod.getModificationType()) {
LOG.trace("Received update data");
- mappingChange = MappingChange.Updated;
+ LOG.trace("Key: {}", change.getRootPath().getRootIdentifier());
+ LOG.trace("Value: {}", mapping);
+ mapSystem.updateMapping(convertedMapping.getOrigin(), convertedEid,
+ new MappingData(convertedMapping.getMappingRecord()));
} else {
LOG.trace("Received write data");
- mappingChange = MappingChange.Created;
+ LOG.trace("Key: {}", change.getRootPath().getRootIdentifier());
+ LOG.trace("Value: {}", mapping);
+ mapSystem.addMapping(convertedMapping.getOrigin(), convertedEid,
+ new MappingData(convertedMapping.getMappingRecord()));
}
- LOG.trace("Key: {}", change.getRootPath().getRootIdentifier());
- LOG.trace("Value: {}", mapping);
-
- final Mapping convertedMapping = convertToBinaryIfNecessary(mapping);
- Eid convertedEid = convertedMapping.getMappingRecord().getEid();
-
- mapSystem.addMapping(convertedMapping.getOrigin(), convertedEid,
- new MappingData(convertedMapping.getMappingRecord()));
- Set<Subscriber> subscribers = mapSystem.getSubscribers(convertedEid);
-
- Set<Subscriber> dstSubscribers = null;
- // For SrcDst LCAF also send SMRs to Dst prefix
- if (convertedEid.getAddress() instanceof SourceDestKey) {
- Eid dstAddr = SourceDestKeyHelper.getDstBinary(convertedEid);
- dstSubscribers = mapSystem.getSubscribers(dstAddr);
- }
-
- try {
- // The notifications are used for sending SMR.
- notificationPublishService.putNotification(MSNotificationInputUtil.toMappingChanged(
- convertedMapping, subscribers, dstSubscribers, mappingChange));
- } catch (InterruptedException e) {
- LOG.warn("Notification publication interrupted!");
- }
-
} else {
LOG.warn("Ignoring unhandled modification type {}", mod.getModificationType());
}
import java.util.List;
import java.util.Set;
import org.opendaylight.lispflowmapping.interfaces.dao.Subscriber;
-import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+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.mappingservice.rev150906.MappingChange;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChanged;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChangedBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.changed.DstSubscriberItem;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.changed.DstSubscriberItemBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.changed.SubscriberItem;
private MSNotificationInputUtil() {
}
- public static MappingChanged toMappingChanged(Mapping input, Set<Subscriber> subscribers,
- Set<Subscriber> dstSubscribers, MappingChange change) {
+ public static MappingChanged toMappingChanged(MappingRecord mapping, Eid eid, Set<Subscriber> subscribers,
+ Set<Subscriber> dstSubscribers, MappingChange change) {
return new MappingChangedBuilder()
- .setMappingRecord(input.getMappingRecord())
- .setSubscriberItem(toSubscriberList(subscribers))
- .setDstSubscriberItem(toDstSubscriberList(dstSubscribers))
- .setChangeType(change).build();
- }
-
- public static MappingChanged toMappingChanged(MappingData mapping, Set<Subscriber> subscribers,
- Set<Subscriber> dstSubscribers, MappingChange change) {
- return new MappingChangedBuilder()
- .setMappingRecord(mapping.getRecord())
+ .setMappingRecord(mapping)
+ .setEid(eid)
.setSubscriberItem(toSubscriberList(subscribers))
.setDstSubscriberItem(toDstSubscriberList(dstSubscribers))
.setChangeType(change).build();
@SuppressWarnings("unchecked")
public void onDataTreeChangedTest_delete_NB() throws InterruptedException {
final List<DataTreeModification<Mapping>> changes = Lists.newArrayList(change_del);
- final MappingChanged mapChanged = MSNotificationInputUtil
- .toMappingChanged(MAPPING_EID_1_NB, null, null, MappingChange.Removed);
Mockito.when(mod_del.getDataBefore()).thenReturn(MAPPING_EID_1_NB);
mappingDataListener.onDataTreeChanged(changes);
@SuppressWarnings("unchecked")
public void onDataTreeChangedTest_subtreeModified_NB() throws InterruptedException {
final List<DataTreeModification<Mapping>> changes = Lists.newArrayList(change_subtreeModified);
- final MappingChanged mapChanged = MSNotificationInputUtil
- .toMappingChanged(MAPPING_EID_2_NB, null, null, MappingChange.Updated);
+ final MappingChanged mapChanged = MSNotificationInputUtil.toMappingChanged(
+ MAPPING_EID_2_NB.getMappingRecord(), null, null, null, MappingChange.Updated);
Mockito.when(mod_subtreeModified.getDataAfter()).thenReturn(MAPPING_EID_2_NB);
mappingDataListener.onDataTreeChanged(changes);
@SuppressWarnings("unchecked")
public void onDataTreeChangedTest_write_NB() throws InterruptedException {
final List<DataTreeModification<Mapping>> changes = Lists.newArrayList(change_write);
- final MappingChanged mapChanged = MSNotificationInputUtil
- .toMappingChanged(MAPPING_EID_3_NB, null, null, MappingChange.Created);
+ final MappingChanged mapChanged = MSNotificationInputUtil.toMappingChanged(
+ MAPPING_EID_3_NB.getMappingRecord(), null, null, null, MappingChange.Created);
Mockito.when(mod_write.getDataAfter()).thenReturn(MAPPING_EID_3_NB);
mappingDataListener.onDataTreeChanged(changes);
public void onDataTreeChangedTest_multipleChanges() throws InterruptedException {
final List<DataTreeModification<Mapping>> changes =
Lists.newArrayList(change_del, change_subtreeModified, change_write);
- final MappingChanged mapChangedDel = MSNotificationInputUtil
- .toMappingChanged(MAPPING_EID_1_NB, null, null, MappingChange.Removed);
- final MappingChanged mapChangedSubtreeMod = MSNotificationInputUtil
- .toMappingChanged(MAPPING_EID_2_NB, null, null, MappingChange.Updated);
+ final MappingChanged mapChangedSubtreeMod = MSNotificationInputUtil.toMappingChanged(
+ MAPPING_EID_2_NB.getMappingRecord(), null, null, null, MappingChange.Updated);
Mockito.when(mod_del.getDataBefore()).thenReturn(MAPPING_EID_1_NB);
Mockito.when(mod_subtreeModified.getDataAfter()).thenReturn(MAPPING_EID_2_NB);
PARENT,
SIBLING,
VIRTUAL_PARENT_SIBLING,
- WIDEST_NEGATIVE
+ WIDEST_NEGATIVE,
+ COVERING
}
public void tryAddToIpTrie(Object key) {
if (key.getAddress() instanceof Ipv4PrefixBinary) {
Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) key.getAddress();
switch (method) {
+ case COVERING:
+ node = ip4Trie.lookupCoveringLessSpecific(prefix.getIpv4AddressBinary().getValue(),
+ prefix.getIpv4MaskLength());
+ break;
case PARENT:
node = ip4Trie.lookupParent(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength());
break;
node = null;
break;
}
- if (node != null) {
+ if (node != null && node.prefix() != null) {
return LispAddressUtil.asIpv4PrefixBinaryEid(
key.getVirtualNetworkId(), node.prefix(), (short) node.prefixLength());
}
} else if (key.getAddress() instanceof Ipv6PrefixBinary) {
Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) key.getAddress();
switch (method) {
+ case COVERING:
+ node = ip6Trie.lookupCoveringLessSpecific(prefix.getIpv6AddressBinary().getValue(),
+ prefix.getIpv6MaskLength());
+ break;
case PARENT:
node = ip6Trie.lookupParent(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength());
break;
node = null;
break;
}
- if (node != null) {
+ if (node != null && node.prefix() != null) {
return LispAddressUtil.asIpv6PrefixBinaryEid(
key.getVirtualNetworkId(), node.prefix(), (short) node.prefixLength());
}
return null;
}
+ @Override
+ public Eid getCoveringLessSpecific(Eid key) {
+ return getPrefix(key, GetPrefixMethods.COVERING);
+ }
+
@Override
public Eid getParentPrefix(Eid key) {
return getPrefix(key, GetPrefixMethods.PARENT);
}
// find closest prefix starting at ROOT
- TrieNode closest = root.findClosest(prefix, preflen);
+ TrieNode closest = root.findClosest(prefix, preflen, false);
// find first different bit <= min(closestNode.preflen, preflen)
int diffbit = closest.firstDifferentBit(prefix, preflen);
return null;
}
+ /**
+ * Look up the covering prefix for the argument, but exclude the argument itself, so the result is always less
+ * specific than the lookup key.
+ *
+ * @param prefix Big endian byte array representation of the prefix argument
+ * @param preflen Prefix length
+ * @return Covering node
+ */
+ public TrieNode lookupCoveringLessSpecific(byte[] prefix, int preflen) {
+ if (root == null || preflen > maxBits) {
+ return null;
+ }
+
+ TrieNode node = root.findClosest(prefix, preflen, true);
+
+ if (node == null) {
+ return null;
+ } else if (node.bit < preflen && node.prefix != null) {
+ // If the current node is not virtual and is less specific than the query, we can return it directly
+ return node;
+ }
+
+ // Else, we need to find a non-virtual parent
+ node = node.up;
+
+ while (node != null && node.prefix == null) {
+ node = node.up;
+ }
+
+ return node;
+ }
+
/**
* Given an EID, lookup the longest prefix match, then return its parent node.
*
if (node == null) {
return null;
- } else {
- node = node.up;
}
+ node = node.up;
+
while (node != null && node.prefix == null) {
node = node.up;
}
return null;
}
- TrieNode node = root.findClosest(prefix, preflen);
+ TrieNode node = root.findClosest(prefix, preflen, false);
// not a negative match
if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
return null;
}
- TrieNode node = root.findClosest(prefix, preflen);
+ TrieNode node = root.findClosest(prefix, preflen, false);
// if no node is found or if node not a prefix or if mask is not the same
if (node == null || node.prefix == null || node.bit != preflen) {
return Collections.emptySet();
}
- TrieNode node = root.findClosest(prefix, preflen);
-
- // if no node is found or if node not a prefix
- if (node == null || node.prefix == null) {
- return Collections.emptySet();
- }
+ TrieNode node = root.findClosest(prefix, preflen, true);
Set<TrieNode> children = new HashSet<>();
- children.add(node);
+ if (node.prefix != null && node.bit >= preflen) {
+ children.add(node);
+ }
Iterator<TrieNode> it = node.iterator();
while (it.hasNext()) {
node = it.next();
- if (node.prefix != null) {
+ if (node.prefix != null && node.bit >= preflen) {
children.add(node);
}
}
}
}
- /**
- * Remove node's subtree.
- *
- * @param node Node who's subtree is to be removed
- */
- public void removeSubtree(TrieNode node) {
- TrieNode itNode;
- Iterator<TrieNode> it = node.iterator();
-
- while (it.hasNext()) {
- itNode = it.next();
- itNode.erase();
- }
- }
-
- /**
- * Remove subtree for longest match.
- *
- * @param prefix Prefix to be looked up
- * @param preflen Prefix length
- */
- public void removeSubtree(byte[] prefix, int preflen) {
- TrieNode itNode = lookupBest(prefix, preflen);
-
- if (itNode == null) {
- return;
- }
-
- Iterator<TrieNode> it = itNode.iterator();
-
- while (it.hasNext()) {
- itNode = it.next();
- itNode.erase();
- }
- }
-
/**
* Remove all entries in the trie.
*/
*
* @param pref Searched prefix
* @param preflen Searched prefix length
+ * @param virtual Include virtual nodes in search
* @return The node found
*/
- public TrieNode findClosest(byte[] pref, int preflen) {
+ public TrieNode findClosest(byte[] pref, int preflen, boolean virtual) {
TrieNode node = this;
- while (node.prefix == null || node.bit < preflen) {
+ while ((!virtual && node.prefix == null) || node.bit < preflen) {
if (testBitInPrefixByte(pref, node.bit)) {
if (node.right == null) {
break;
return new TriePostOrderIterator(this);
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(printPrefixAndMask());
+ if (up != null) {
+ sb.append(", parent: ");
+ sb.append(up.printPrefixAndMask());
+ }
+ if (left != null) {
+ sb.append(", left child: ");
+ sb.append(left.printPrefixAndMask());
+ }
+ if (right != null) {
+ sb.append(", right child: ");
+ sb.append(right.printPrefixAndMask());
+ }
+ if (data != null) {
+ sb.append(", data: ");
+ sb.append(data);
+ }
+ return sb.toString();
+ }
+
+ private String printPrefixAndMask() {
+ if (prefix == null) {
+ return "Virtual @ bit " + bit;
+ }
+ StringBuilder sb = new StringBuilder();
+ try {
+ sb.append(InetAddress.getByAddress(prefix));
+ sb.append("/");
+ sb.append(bit);
+ } catch (UnknownHostException e) {
+ return "NaP";
+ }
+ return sb.toString();
+ }
+
/**
* Post order iterator implementation for prefix trie. It's safe to use it to remove nodes.
*/
*/
package org.opendaylight.lispflowmapping.lisp.util;
-import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import com.google.common.net.InetAddresses;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.List;
+import java.util.Set;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.LispAddress;
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.opendaylight.lfm.lisp.binary.address.types.rev160504.augmented.lisp.address.address.Ipv6Binary;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.augmented.lisp.address.address.Ipv6PrefixBinary;
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.eid.container.Eid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* @author Lorand Jakab
*
*/
-public class LispAddressStringifier {
+public final class LispAddressStringifier {
protected static final Logger LOG = LoggerFactory.getLogger(LispAddressStringifier.class);
private static final String PREFIX_SEPARATOR = ":";
URL;
}
+ // Utility class, should not be instantiated
+ private LispAddressStringifier() {
+ }
+
public static String getString(LispAddress lispAddress) {
return getAddrString(Destination.USER, lispAddress);
}
+ public static String getString(Set<Eid> eids) {
+ StringBuilder sb = new StringBuilder("{");
+ boolean first = true;
+ for (Eid eid : eids) {
+ if (!first) {
+ sb.append(", ");
+ }
+ sb.append(getString(eid));
+ first = false;
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
public static String getString(Address address) {
return getAddrString(Destination.USER, address, null);
}
}
private static String getAddrString(Destination dst, LispAddress lispAddress) {
- Preconditions.checkNotNull(lispAddress, "lispAddress should not be null");
+ if (lispAddress == null) {
+ return "null";
+ }
+
Address addr = lispAddress.getAddress();
Long vni = null;
return table.getWidestNegativePrefix(MaskUtil.normalize(eid));
}
+ @Override
+ public Eid getCoveringLessSpecific(Eid eid) {
+ ILispDAO table = getVniTable(eid);
+ if (table == null) {
+ return null;
+ }
+ return table.getCoveringLessSpecific(MaskUtil.normalize(eid));
+ }
+
@Override
public Eid getParentPrefix(Eid eid) {
ILispDAO table = getVniTable(eid);