import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.lispflowmapping.config.ConfigIni;
import org.opendaylight.lispflowmapping.dsbackend.DataStoreBackEnd;
import org.opendaylight.lispflowmapping.implementation.timebucket.implementation.TimeBucketMappingTimeoutService;
import org.opendaylight.lispflowmapping.lisp.type.MappingData;
import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
+import org.opendaylight.lispflowmapping.lisp.util.MappingRecordUtil;
import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
import org.opendaylight.lispflowmapping.mapcache.AuthKeyDb;
import org.opendaylight.lispflowmapping.mapcache.MultiTableMapCache;
import org.opendaylight.lispflowmapping.mapcache.SimpleMapCache;
import org.opendaylight.lispflowmapping.mapcache.lisp.LispMapCacheStringifier;
+import org.opendaylight.mdsal.binding.api.NotificationPublishService;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
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.ExplicitLocatorPath;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4;
private static final String AUTH_KEY_TABLE = "authentication";
//private static final int TTL_RLOC_TIMED_OUT = 1;
private static final int TTL_NO_RLOC_KNOWN = ConfigIni.getInstance().getNegativeMappingTTL();
- private NotificationPublishService notificationPublishService;
+ private final NotificationPublishService notificationPublishService;
private boolean mappingMerge;
- private ILispDAO dao;
+ private final ILispDAO dao;
private ILispDAO sdao;
private ILispMapCache smc;
private IMapCache pmc;
- private ConcurrentHashMap<Eid, Set<Subscriber>> subscriberdb = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<Eid, Set<Subscriber>> subscriberdb = new ConcurrentHashMap<>();
private IAuthKeyDb akdb;
private final EnumMap<MappingOrigin, IMapCache> tableMap = new EnumMap<>(MappingOrigin.class);
private DataStoreBackEnd dsbe;
private boolean isMaster = false;
- private ISouthBoundMappingTimeoutService sbMappingTimeoutService;
+ private final ISouthBoundMappingTimeoutService sbMappingTimeoutService;
public MappingSystem(ILispDAO dao, boolean iterateMask, NotificationPublishService nps, boolean mappingMerge) {
this.dao = dao;
return;
}
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("DAO: Adding {} mapping for EID {}", origin, LispAddressStringifier.getString(key));
+ }
+
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("mappingData = {}", mappingData.getString());
+ }
+
+ // Save the old mapping for the key before we modify anything, so that we can detect changes later
+ final MappingRecord oldMapping = getMappingRecord(getMapping(key));
+
if (origin == MappingOrigin.Southbound) {
XtrId xtrId = mappingData.getXtrId();
if (xtrId == null && mappingMerge && mappingData.isMergeEnabled()) {
tableMap.get(origin).addMapping(key, mappingData);
- 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
+ // We need to check if the newly added mapping is covering negatives in SB, and remove those (with notification)
+ if (mappingData.isPositive().or(true)) {
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);
}
+
+ MappingRecord newMapping = getMappingRecord(getMapping(key));
+
+ handleAddMappingNotifications(origin, key, mappingData, oldMapping, newMapping, changeType);
+ }
+
+ private static MappingRecord getMappingRecord(MappingData mappingData) {
+ return mappingData != null ? mappingData.getRecord() : null;
}
@SuppressWarnings("unchecked")
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): {}",
+ LOG.trace("handleSbNegativeMappings(): subtree prefix set for EID {}: {}",
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);
}
+
+ Eid parentPrefix = smc.getCoveringLessSpecific(key);
+ LOG.trace("handleSbNegativeMappings(): parent prefix for EID {}: {}",
+ LispAddressStringifier.getString(key),
+ LispAddressStringifier.getString(parentPrefix));
+ handleSbNegativeMapping(parentPrefix);
}
private void handleSbNegativeMapping(Eid key) {
}
}
+ private void handleAddMappingNotifications(MappingOrigin origin, Eid key, MappingData mappingData,
+ MappingRecord oldMapping, MappingRecord newMapping,
+ MappingChange changeType) {
+ // Non-southbound origins are MD-SAL first, so they only get to call addMapping() if there is a change
+ // Southbound is different, so we need to check if there is a change in the mapping. This check takes into
+ // account policy as well
+ if (origin != MappingOrigin.Southbound || MappingRecordUtil.mappingChanged(oldMapping, newMapping)) {
+ notifyChange(key, mappingData.getRecord(), changeType);
+
+ Eid dstKey = key;
+ // Since the above notifyChange() already notifies the dest part of source/dest addresses, we save the dest
+ // for the checks that we do afterwards
+ if (key.getAddress() instanceof SourceDestKey) {
+ dstKey = SourceDestKeyHelper.getDstBinary(key);
+ }
+ // If the old mapping had a different EID than what was just added, notify those subscribers too
+ if (oldMapping != null && !oldMapping.getEid().equals(key) && !oldMapping.getEid().equals(dstKey)) {
+ notifyChange(oldMapping.getEid(), oldMapping, changeType);
+ }
+ // If the new mapping has a different EID than what was just added (e.g., due to NB_AND_SB), notify those
+ // subscribers too
+ if (newMapping != null && !newMapping.getEid().equals(key) && !newMapping.getEid().equals(dstKey)) {
+ notifyChange(newMapping.getEid(), newMapping, changeType);
+ }
+ }
+
+ }
+
@Override
public MappingData addNegativeMapping(Eid key) {
MappingRecord mapping = buildNegativeMapping(key);
* Since this method is only called when there is a hit in the southbound Map-Register cache, and that cache is
* not used when merge is on, it's OK to ignore the effects of timestamp changes on merging for now.
*/
+ @Override
public void refreshMappingRegistration(Eid key, XtrId xtrId, Long timestamp) {
sbMappingTimeoutService.removeExpiredMappings();
}
LocatorRecord locatorRecord = mappingData.getRecord().getLocatorRecord().get(0);
- long serviceIndex = ((ServicePath) eid.getAddress()).getServicePath().getServiceIndex();
+ long serviceIndex = ((ServicePath) eid.getAddress()).getServicePath().getServiceIndex().toJava();
int index = LispAddressUtil.STARTING_SERVICE_INDEX - (int) serviceIndex;
Rloc rloc = locatorRecord.getRloc();
if (rloc.getAddress() instanceof Ipv4 || rloc.getAddress() instanceof Ipv6) {
}
private MappingData handleMergedMapping(Eid key) {
+ LOG.trace("Merging mappings for EID {}", LispAddressStringifier.getString(key));
List<MappingData> expiredMappingDataList = new ArrayList<>();
Set<IpAddressBinary> sourceRlocs = new HashSet<>();
@Override
public MappingData getMapping(Eid src, Eid dst) {
// NOTE: Currently we have two lookup algorithms implemented, which are configurable
+ IMappingService.LookupPolicy policy = ConfigIni.getInstance().getLookupPolicy();
+ LOG.debug("DAO: Looking up mapping for {}, source EID {} with policy {}",
+ LispAddressStringifier.getString(dst),
+ LispAddressStringifier.getString(src),
+ policy);
- if (ConfigIni.getInstance().getLookupPolicy() == IMappingService.LookupPolicy.NB_AND_SB) {
+ if (policy == IMappingService.LookupPolicy.NB_AND_SB) {
return getMappingNbSbIntersection(src, dst);
} else {
return getMappingNbFirst(src, dst);
private MappingData getSbMappingWithExpiration(Eid src, Eid dst, XtrId xtrId) {
MappingData mappingData = (MappingData) smc.getMapping(dst, xtrId);
- if (mappingData != null && MappingMergeUtil.mappingIsExpired(mappingData)) {
- return handleSbExpiredMapping(dst, xtrId, mappingData);
- } else {
- return mappingData;
+ while (mappingData != null && MappingMergeUtil.mappingIsExpired(mappingData)) {
+ // If the mappingData is expired, handleSbExpiredMapping() will run merge for it if merge is enabled,
+ // otherwise it will remove the expired mapping, returning null.
+ MappingData mergedMappingData = handleSbExpiredMapping(dst, xtrId, mappingData);
+ if (mergedMappingData != null) {
+ return mergedMappingData;
+ }
+ // If the expired mapping was removed, we look up the original query again
+ mappingData = (MappingData) smc.getMapping(dst, xtrId);
}
+ return mappingData;
}
public MappingData handleSbExpiredMapping(Eid key, XtrId xtrId, MappingData mappingData) {
}
private void removeSbXtrIdSpecificMapping(Eid key, XtrId xtrId, MappingData mappingData) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("DAO: Removing southbound mapping for EID {}, xTR-ID {}",
+ LispAddressStringifier.getString(key),
+ LispAddressStringifier.getString(xtrId));
+ }
smc.removeMapping(key, xtrId);
dsbe.removeXtrIdMapping(DSBEInputUtil.toXtrIdMapping(mappingData));
}
}
removeFromSbTimeoutService(key);
- Set<Subscriber> subscribers = getSubscribers(key);
+ final Set<Subscriber> subscribers = getSubscribers(key);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("DAO: Removing southbound mapping for EID {}", LispAddressStringifier.getString(key));
+ }
smc.removeMapping(key);
dsbe.removeMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mappingData));
publishNotification(mappingData.getRecord(), null, subscribers, null, MappingChange.Removed);
dstSubscribers = getSubscribers(dstAddr);
notifyChildren(dstAddr, mapping, mappingChange);
}
- publishNotification(mapping, eid, subscribers, dstSubscribers, mappingChange);
+
+ // No reason to send a notification when no subscribers exist
+ if (subscribers != null || dstSubscribers != null) {
+ publishNotification(mapping, eid, subscribers, dstSubscribers, mappingChange);
+ }
notifyChildren(eid, mapping, mappingChange);
}
childPrefixes.remove(eid);
for (Eid prefix : childPrefixes) {
Set<Subscriber> subscribers = getSubscribers(prefix);
- publishNotification(mapping, prefix, subscribers, null, mappingChange);
+ // No reason to send a notification when no subscribers exist
+ if (subscribers != null) {
+ publishNotification(mapping, prefix, subscribers, null, mappingChange);
+ }
}
}
private static Eid getVirtualParent(Eid eid) {
if (eid.getAddress() instanceof Ipv4PrefixBinary) {
Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) eid.getAddress();
- short parentPrefixLength = (short) (prefix.getIpv4MaskLength() - 1);
+ short parentPrefixLength = (short) (prefix.getIpv4MaskLength().toJava() - 1);
byte[] parentPrefix = MaskUtil.normalizeByteArray(prefix.getIpv4AddressBinary().getValue(),
parentPrefixLength);
return LispAddressUtil.asIpv4PrefixBinaryEid(eid, parentPrefix, parentPrefixLength);
} else if (eid.getAddress() instanceof Ipv6PrefixBinary) {
Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) eid.getAddress();
- short parentPrefixLength = (short) (prefix.getIpv6MaskLength() - 1);
+ short parentPrefixLength = (short) (prefix.getIpv6MaskLength().toJava() - 1);
byte[] parentPrefix = MaskUtil.normalizeByteArray(prefix.getIpv6AddressBinary().getValue(),
parentPrefixLength);
return LispAddressUtil.asIpv6PrefixBinaryEid(eid, parentPrefix, parentPrefixLength);