package org.opendaylight.lispflowmapping.interfaces.dao;
import java.util.Map;
+import java.util.AbstractMap.SimpleImmutableEntry;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
public interface ILispDAO {
* Get the entries from the DAO
*
* @param key
- * The key.
+ * The key to be looked up as exact match.
* @return The value from the DAO.
*/
Map<String, Object> get(Object key);
+ /**
+ * Get value for longest prefix match from the DAO
+ *
+ * @param key
+ * The eid prefix, IPv4 or IPv6, to be looked up. Key must be normalized.
+ * @return The value from the DAO.
+ */
+ Map<String, Object> getBest(Object key);
+
+ /**
+ * Get longest prefix match and value from the DAO
+ *
+ * @param key
+ * The eid prefix, IPv4 or IPv6, to be looked up. Key must be normalized
+ * @return The best match and value pair from the DAO.
+ */
+ SimpleImmutableEntry<Eid, Map<String, ?>> getBestPair(Object key);
+
+ /**
+ * Get widest negative prefix
+ *
+ * @param key
+ * The eid prefix, IPv4 or IPv6, to be looked up. Key must be normalized.
+ * @return The widest negative prefix found.
+ */
+ Eid getWidestNegativePrefix(Eid key);
+
/**
* Enumerate all the entries from the DAO
*
*/
Object getMapping(Eid srcKey, Eid dstKey);
+ /**
+ * Retrieves widest negative prefix
+ *
+ * @param key
+ * Source Key to be looked up
+ * @return Returns the widest negative prefix or null if nothing is found.
+ */
+ Eid getWidestNegativeMapping(Eid key);
+
/**
* Remove mapping
*
*/
Object getMapping(MappingOrigin origin, Eid key);
+ /**
+ * Retrieves widest negative prefix from table for provided key
+ *
+ * @param key
+ * Key to be looked up
+ * @return Returns the prefix found in the cache or null if nothing is found.
+ */
+ Object getWidestNegativePrefix(Eid key);
+
/**
* Update mapping registration
*
*/
Object getMapping(Eid srcKey, Eid dstKey);
+ /**
+ * Retrieves widest negative prefix found in the Mapping System for given key
+ *
+ * @param key
+ * Key being looked up
+ * @return Returns the widest negative prefix or null if nothing is found.
+ */
+ Eid getWidestNegativePrefix(Eid key);
+
/**
* Update mapping registration
*
return mappingSystem.getMapping(srcKey, dstKey);
}
+ @Override
+ public Eid getWidestNegativePrefix(Eid key) {
+ return mappingSystem.getWidestNegativePrefix(key);
+ }
+
@Override
public void removeMapping(MappingOrigin origin, Eid key) {
dsbe.removeMapping(DSBEInputUtil.toMapping(origin, key));
return tableMap.get(origin).getMapping(null, key);
}
+ @Override
+ public Eid getWidestNegativePrefix(Eid key) {
+ Eid nbPrefix, sbPrefix;
+ nbPrefix = pmc.getWidestNegativeMapping(key);
+ if (nbPrefix == null) {
+ return null;
+ }
+
+ sbPrefix = smc.getWidestNegativeMapping(key);
+ if (sbPrefix == null) {
+ return null;
+ }
+
+ // since prefixes overlap, just return the more specific (larger mask)
+ if (LispAddressUtil.getIpPrefixMask(nbPrefix) < LispAddressUtil.getIpPrefixMask(sbPrefix)) {
+ return sbPrefix;
+ } else{
+ return nbPrefix;
+ }
+ }
+
@Override
public void removeMapping(MappingOrigin origin, Eid key) {
tableMap.get(origin).removeMapping(key, origin == MappingOrigin.Southbound ? overwrite : true);
MappingRecordBuilder recordBuilder = new MappingRecordBuilder();
recordBuilder.setAuthoritative(false);
recordBuilder.setMapVersion((short) 0);
- recordBuilder.setEid(eid);
+ recordBuilder.setEid(mapService.getWidestNegativePrefix(eid));
recordBuilder.setAction(Action.NativelyForward);
if (authenticate && mapService.getAuthenticationKey(eid) != null) {
recordBuilder.setRecordTtl(TTL_RLOC_TIMED_OUT);
.thenReturn(null);
Mockito.when(mapServiceMock.getAuthenticationKey(IPV4_PREFIX_EID_1))
.thenReturn(new MappingAuthkeyBuilder().build());
+ Mockito.when(mapServiceMock.getWidestNegativePrefix(IPV4_PREFIX_EID_1)).thenReturn(IPV4_PREFIX_EID_1);
// result
final MapReplyBuilder mapReplyBuilder = getDefaultMapReplyBuilder();
import java.util.Date;
import java.util.Map;
+import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
+import org.opendaylight.lispflowmapping.inmemorydb.radixtrie.RadixTrie;
import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
+import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.binary.address.types.rev160504.augmented.lisp.address.address.Ipv4PrefixBinary;
+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.eid.container.Eid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private TimeUnit timeUnit = TimeUnit.SECONDS;
private int recordTimeOut = DEFAULT_RECORD_TIMEOUT;
+ // IPv4 and IPv6 radix tries used for longest prefix matching
+ private RadixTrie<Object> ip4Trie = new RadixTrie<Object>(32, true);
+ private RadixTrie<Object> ip6Trie = new RadixTrie<Object>(128, true);
+
+ public void tryAddToIpTrie(Object key) {
+ if (key instanceof Eid) {
+ Eid eid = (Eid) key;
+ if (eid.getAddress() instanceof Ipv4PrefixBinary) {
+ Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) eid.getAddress();
+ ip4Trie.insert(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength(), key);
+ } else if (eid.getAddress() instanceof Ipv6PrefixBinary) {
+ Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) eid.getAddress();
+ ip6Trie.insert(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength(), key);
+ }
+ }
+ }
+
@Override
public void put(Object key, MappingEntry<?>... values) {
if (!data.containsKey(key)) {
data.put(key, new ConcurrentHashMap<String, Object>());
}
for (MappingEntry<?> entry : values) {
+ tryAddToIpTrie(key);
data.get(key).put(entry.getKey(), entry.getValue());
}
}
return data.get(key);
}
+ @Override
+ public Map<String, Object> getBest(Object key) {
+ if (key instanceof Eid) {
+ Eid eid = (Eid) key;
+ RadixTrie<Object>.TrieNode node = null;
+
+ if (eid.getAddress() instanceof Ipv4PrefixBinary) {
+ Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) eid.getAddress();
+ node = ip4Trie.lookupBest(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength());
+ } else if (eid.getAddress() instanceof Ipv6PrefixBinary) {
+ Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) eid.getAddress();
+ node = ip6Trie.lookupBest(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength());
+ }
+ if (node == null) {
+ return get(key);
+ }
+ return get(node.data());
+ }
+ return null;
+ }
+
+ @Override
+ public SimpleImmutableEntry<Eid, Map<String, ?>> getBestPair(Object key) {
+ Map<String, ?> data = null;
+
+ if (key instanceof Eid) {
+ Eid eid = (Eid) key;
+ RadixTrie<Object>.TrieNode node = null;
+
+ if (eid.getAddress() instanceof Ipv4PrefixBinary) {
+ Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) eid.getAddress();
+ node = ip4Trie.lookupBest(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength());
+ } else if (eid.getAddress() instanceof Ipv6PrefixBinary) {
+ Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) eid.getAddress();
+ node = ip6Trie.lookupBest(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength());
+ }
+ if (node == null) {
+ data = get(key);
+ return (data == null) ? null : new SimpleImmutableEntry<Eid, Map<String, ?>>((Eid)key, data);
+ }
+ data = get(node.data());
+ return (data == null) ? null : new SimpleImmutableEntry<Eid, Map<String, ?>>((Eid)node.data(), data);
+ }
+ return null;
+ }
+
@Override
public void getAll(IRowVisitor visitor) {
for (ConcurrentMap.Entry<Object, ConcurrentMap<String, Object>> keyEntry : data.entrySet()) {
}
}
}
+ @Override
+ public Eid getWidestNegativePrefix(Eid key) {
+ RadixTrie<Object>.TrieNode node = null;
+
+ if (key.getAddress() instanceof Ipv4PrefixBinary) {
+ Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) key.getAddress();
+ node = ip4Trie.lookupWidestNegative(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength());
+ if (node != null) {
+ return LispAddressUtil.asIpv4PrefixBinaryEid(key, node.prefix(), (short) node.prefixLength());
+ }
+ } else if (key.getAddress() instanceof Ipv6PrefixBinary) {
+ Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) key.getAddress();
+ node = ip6Trie.lookupWidestNegative(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength());
+ if (node != null) {
+ return LispAddressUtil.asIpv6PrefixBinaryEid(key, node.prefix(), (short) node.prefixLength());
+ }
+ }
+ return null;
+ }
+
+ private void tryRemoveFromTrie(Object key) {
+ if (key instanceof Eid) {
+ Eid eid = (Eid) key;
+ if (eid.getAddress() instanceof Ipv4PrefixBinary) {
+ Ipv4PrefixBinary prefix = (Ipv4PrefixBinary) eid.getAddress();
+ ip4Trie.remove(prefix.getIpv4AddressBinary().getValue(), prefix.getIpv4MaskLength());
+ } else if (eid.getAddress() instanceof Ipv6PrefixBinary) {
+ Ipv6PrefixBinary prefix = (Ipv6PrefixBinary) eid.getAddress();
+ ip6Trie.remove(prefix.getIpv6AddressBinary().getValue(), prefix.getIpv6MaskLength());
+ }
+ }
+ }
@Override
public void remove(Object key) {
+ tryRemoveFromTrie(key);
data.remove(key);
}
@Override
public void removeAll() {
+ ip4Trie.removeAll();
+ ip6Trie.removeAll();
data.clear();
}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.inmemorydb.radixtrie;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.ListIterator;
+
+/**
+ * Radix trie/tree (also known as Patricia tree) implementation. Supports CRD operations for
+ * generic, big endian (network byte order) prefixes. It can do exact and longest prefix matchin,
+ * post order iteration over the entries in the tree and can lookup widest negative prefixes (i.e.,
+ * shortest overlapping prefix not registered in the tree).
+ *
+ * @author Florin Coras
+ *
+ * @param <T> Data type stored in each tree node
+ */
+public class RadixTrie<T> {
+ private int MAXBITS;
+ private TrieNode ROOT;
+ private long nbActiveNodes;
+ private boolean rootZero;
+
+ /**
+ * RadixTrie constructor
+ *
+ * @param bits Maximum prefix length supported.
+ */
+ public RadixTrie(int bits) {
+ MAXBITS = bits;
+ ROOT = null;
+ nbActiveNodes = 0;
+ }
+
+ /**
+ * Radix trie constructors
+ * @param bits Maximum prefix length supported
+ * @param rootZero Flag that decides if 0/0 should be inserted as a non-prefix root node or not
+ */
+ public RadixTrie(int bits, boolean rootZero) {
+ MAXBITS = bits;
+ ROOT = rootZero ? new TrieNode(null, 0, null) : null;
+ nbActiveNodes = 0;
+ this.rootZero = rootZero;
+ }
+
+ public TrieNode getRoot() {
+ return ROOT;
+ }
+
+ private void setRoot(TrieNode root) {
+ ROOT = root;
+ }
+
+ public int getMaxbits() {
+ return MAXBITS;
+ }
+
+ public long getSize() {
+ return nbActiveNodes;
+ }
+
+ /**
+ * Test bit in byte. Assumes bits are numbered from 0 to 7
+ * @param b byte
+ * @param bitPosition the position to be tested
+ * @return 1 if bit at position is 1, 0 otherwise.
+ */
+ private boolean testBitInByte(byte b, int bitPosition) {
+ return (b & (0x80 >> bitPosition)) != 0;
+ }
+
+ private boolean testBitInPrefixByte(byte[] prefix, int bitPosition) {
+ return testBitInByte(prefix[bitPosition / 8], bitPosition & 0x07);
+ }
+
+ /**
+ * Mask prefix
+ * @param prefix Prefix to be masked
+ * @param prefLen Prefix length
+ * @return Prefix after masking
+ */
+ private byte [] maskPrefix(byte[] prefix, int prefLen) {
+ int i;
+ byte[] res = prefix.clone();
+ res[prefLen/8] = (byte) (res[prefLen/8] & (0xFF << (7 - (prefLen & 0x07))));
+ for (i = prefLen/8 + 1; i < MAXBITS/8; i++) {
+ res[i] = 0;
+ }
+ return res;
+ }
+
+ /**
+ * Insert prefix-data tuple into radix trie
+ * @param prefix Big endian (network order) byte array representation of the prefix
+ * @param preflen Prefix length
+ * @param data Data to be stored in the tree
+ * @return Newly inserted TrieNode
+ */
+ public TrieNode insert(byte[] prefix, int preflen, T data) {
+ TrieNode node;
+ int diffbit;
+
+ if (preflen > MAXBITS) {
+ return null;
+ }
+
+ // trie is empty
+ if (ROOT == null) {
+ ROOT = new TrieNode(prefix, preflen, data);
+ return ROOT;
+ }
+
+ // find closest prefix starting at ROOT
+ node = ROOT.findClosest(prefix, preflen);
+
+ // find first different bit
+ diffbit = node.firstDifferentBit(prefix, preflen);
+
+ // find the first node with bit less than diffbit
+ node = node.parentWithBitLessThan(diffbit);
+
+ // insert new prefix
+ return node.insert(prefix, preflen, diffbit, data);
+ }
+
+ /**
+ * Longest prefix match of prefix/preflen
+ * @param prefix Big endian byte array representation of the prefix to be looked up.
+ * @param preflen Prefix length
+ * @return Node with longest prefix match or null if nothing is found.
+ */
+ public TrieNode lookupBest(byte[] prefix, int preflen) {
+ TrieNode node;
+ ArrayList<TrieNode> candidates;
+ ListIterator<TrieNode> it;
+
+ if (ROOT == null || preflen > MAXBITS) {
+ return null;
+ }
+
+ node = ROOT;
+ candidates = new ArrayList<TrieNode>();
+
+ while (node != null && node.bit < preflen) {
+ if (node.prefix != null) {
+ candidates.add(node);
+ }
+
+ if (testBitInPrefixByte(prefix, node.bit)) {
+ node = node.right;
+ } else {
+ node = node.left;
+ }
+ }
+
+ if (node != null && node.prefix != null) {
+ candidates.add(node);
+ }
+
+ it = candidates.listIterator(candidates.size());
+ while (it.hasPrevious()) {
+ node = it.previous();
+ if (node.comparePrefix(prefix)) {
+ return node;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Lookup widest negative (i.e., overlapping but not present in trie) prefix for given prefix and prefix length
+ * @param prefix Prefix looked up.
+ * @param preflen Prefix length.
+ * @return Node containing the widest negative prefix.
+ */
+ public TrieNode lookupWidestNegative(byte [] prefix, int preflen) {
+ TrieNode node;
+ int diffbit;
+
+ if (ROOT == null || preflen > MAXBITS) {
+ return null;
+ }
+
+ node = ROOT.findClosest(prefix, preflen);
+
+ // not a negative match
+ if (node.prefix != null && node.prefixLength() <= preflen && node.comparePrefix(prefix)) {
+ return null;
+ }
+ diffbit = node.firstDifferentBit(prefix, preflen);
+ return new TrieNode(maskPrefix(prefix, diffbit), diffbit + 1, null);
+ }
+
+ /**
+ * Exact prefix match of prefix/preflen
+ * @param prefix Big endian byte array representation of the prefix to be looked up.
+ * @param preflen Prefix length
+ * @return Node with exact prefix match or null
+ */
+ public TrieNode lookupExact(byte[] prefix, int preflen) {
+ TrieNode node;
+
+ if (ROOT == null || preflen > MAXBITS) {
+ return null;
+ }
+
+ node = ROOT.findClosest(prefix, preflen);
+
+ // if no node is found or if node not a prefix or if mask is too long
+ if (node == null || node.prefix == null || node.bit > preflen) {
+ return null;
+ }
+
+ // check that we have exact match
+ if (node.comparePrefix(prefix)) {
+ return node;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Remove prefix from radix trie
+ * @param prefix Big endian byte array representation of the prefix to be removed.
+ * @param preflen Prefix length.
+ */
+ public void remove(byte[] prefix, int preflen) {
+ TrieNode node;
+ node = lookupExact(prefix, preflen);
+ if (node != null) {
+ node.erase();
+ }
+ }
+
+ /**
+ * Remove node's subtree
+ * @param node Node who's subtree is to be removed
+ */
+ public void removeSubtree(TrieNode node) {
+ TrieNode itNode;
+ Iterator<TrieNode> it;
+ 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;
+ Iterator<TrieNode> it;
+
+ itNode = lookupBest(prefix, preflen);
+
+ if (itNode == null) {
+ return;
+ }
+
+ it = itNode.iterator();
+
+ while (it.hasNext()) {
+ itNode = it.next();
+ itNode.erase();
+ }
+ }
+
+ /**
+ * Remove all entries in the trie.
+ */
+ public void removeAll() {
+ TrieNode node;
+ Iterator<TrieNode> it;
+
+ it = ROOT.iterator();
+ while (it.hasNext()) {
+ node = it.next();
+ node.erase();
+ }
+ }
+
+ /**
+ * Trie node definition.
+ *
+ */
+ public class TrieNode implements Iterable<TrieNode>{
+ // since bits are counted from 0, bit and prefix length are equal
+ int bit;
+ byte[] prefix;
+ TrieNode left, right;
+ TrieNode up;
+ T data;
+
+ TrieNode(byte[] prefix, int prefixlen, T data) {
+ this.bit = prefixlen;
+ this.prefix = prefix;
+ this.data = data;
+ left = right = null;
+ }
+
+ public byte[] prefix() {
+ return prefix;
+ }
+
+ public int prefixLength() {
+ return bit;
+ }
+
+ public T data() {
+ return data;
+ }
+
+ /**
+ * Finds closest prefix NOT the longest prefix match
+ * @param prefix Searched prefix
+ * @param preflen Searched prefix length
+ * @return The node found
+ */
+ public TrieNode findClosest(byte[] prefix, int preflen) {
+ TrieNode node = this;
+
+ while (node.prefix == null || node.bit < preflen) {
+ if (testBitInPrefixByte(prefix, node.bit)) {
+ if (node.right == null) {
+ break;
+ }
+ node = node.right;
+ } else {
+ if (node.left == null) {
+ break;
+ }
+ node = node.left;
+ }
+ }
+ return node;
+ }
+
+ /**
+ * Compares prefix to node's prefix and returns position of first different bit
+ * @param prefix Prefix to be compared.
+ * @param preflen Prefix length.
+ * @return Position of first different bit.
+ */
+ public int firstDifferentBit(byte[] prefix, int preflen) {
+ int maxbit, diffbit, i;
+ byte bitxor;
+
+ maxbit = (bit < preflen) ? bit : preflen;
+ diffbit = 0;
+ for (i = 0; i * 8 < maxbit; i++) {
+ // if match, move to next byte
+ if ((bitxor = (byte) (this.prefix[i] ^ prefix[i])) == 0) {
+ diffbit = (i+1) * 8;
+ continue;
+ }
+ // if not matched, find first diff bit (0 to 7)
+ diffbit = i * 8 + Integer.numberOfLeadingZeros(bitxor) - 24;
+ diffbit = (diffbit > maxbit) ? maxbit : diffbit;
+ break;
+ }
+
+ return diffbit;
+ }
+
+ /**
+ * Find parent with bit less than given value
+ * @param bitlen Bit value
+ * @return Parent with bit less than given value
+ */
+ public TrieNode parentWithBitLessThan(int bitlen) {
+ TrieNode node, parent;
+ node = this;
+ parent = node.up;
+ while (parent != null && parent.bit >= bitlen) {
+ node = parent;
+ parent = node.up;
+ }
+ return node;
+ }
+
+ /**
+ * Inserts node in trie near this node with prefix that has the first bit difference at diffbit
+ * @param pref Prefix to be inserted.
+ * @param preflen Prefix length of the prefix to be inserted.
+ * @param diffbit Bit index of the first different bit between prefix and current node
+ * @param data Data to be stored together with the prefix
+ * @return The trie node created or current node if it's an overwrite.
+ */
+ public TrieNode insert(byte[] pref, int preflen, int diffbit, T data) {
+ TrieNode parent, newNode;
+
+ // same node, check if prefix needs saving
+ if (diffbit == preflen && bit == preflen) {
+ if (prefix != null) {
+ return this;
+ }
+ this.prefix = pref;
+ this.data = data;
+ nbActiveNodes++;
+ return this;
+ }
+
+ newNode = new TrieNode(pref, preflen, data);
+
+ // node is more specific, add new prefix as parent
+ if (preflen == diffbit) {
+ if (testBitInPrefixByte(pref, preflen)) {
+ newNode.right = this;
+ } else {
+ newNode.left = this;
+ }
+ newNode.up = up;
+
+ if (up == null) {
+ setRoot(newNode);
+ } else if (this.equals(up.right)) {
+ up.right = newNode;
+ } else {
+ up.left = newNode;
+ }
+
+ up = newNode;
+ // new prefix is more specific than node, add as child
+ } else if (bit == diffbit) {
+ newNode.up = this;
+ if (testBitInPrefixByte(pref, bit)) {
+ right = newNode;
+ } else {
+ left = newNode;
+ }
+ // new prefix is a sibling of/on a different branch from node, add common parent
+ } else {
+ parent = new TrieNode(null, diffbit, null);
+ parent.up = up;
+ if (testBitInPrefixByte(pref, diffbit)) {
+ parent.right = newNode;
+ parent.left = this;
+ } else {
+ parent.right = this;
+ parent.left = newNode;
+ }
+ newNode.up = parent;
+ if (up == null) {
+ setRoot(parent);
+ } else if (this.equals(up.right)) {
+ up.right = parent;
+ } else {
+ up.left = parent;
+ }
+ up = parent;
+ }
+ nbActiveNodes++;
+ return newNode;
+ }
+
+ /**
+ * Erase node
+ */
+ public void erase () {
+ TrieNode cur = this, child, parent;
+ boolean isRoot = false;
+
+ if (this.equals(ROOT)) {
+ isRoot = true;
+ }
+
+ if (prefix != null) {
+ resetData();
+ }
+
+ // as long as one of the children is null and this is an intermediary node
+ // link parent to child. If parent null, child becomes the root.
+ while (cur != null && cur.prefix == null && (cur.left == null || cur.right == null)) {
+ parent = cur.up;
+ child = cur.left != null ? cur.left : cur.right;
+
+ if (parent == null) {
+ setRoot(child);
+ } else {
+ if (parent.left != null && parent.left.equals(cur)) {
+ parent.left = child;
+ } else {
+ parent.right = child;
+ }
+ if (child != null) {
+ child.up = parent;
+ }
+ }
+
+ cur.resetData();
+ cur = parent;
+ }
+
+ if (isRoot) {
+ setRoot((rootZero ? new TrieNode(null, 0, null) : null));
+ }
+
+ nbActiveNodes--;
+ }
+
+ /**
+ * Clear node data
+ */
+ public void resetData () {
+ prefix = null;
+ data = null;
+ }
+
+ /**
+ * Compare node prefix with prefix
+ * @param pref Prefix to be compared
+ * @return True if prefixes are equal, false otherwise
+ */
+ public boolean comparePrefix(byte[] pref) {
+ int i, mask, r;
+
+ for (i = 0; i < bit/8; i++) {
+ if (prefix[i] != pref[i]) {
+ return false;
+ }
+ }
+ if ((r = bit % 8) != 0) {
+ mask = (0xFF << r) & 0xFF;
+ return ((prefix[i] & mask) == (pref[i] & mask));
+ }
+ return true;
+ }
+
+ /**
+ * Helper method that converts prefix and prefix length to dotted decimal, string, representation.
+ * @return String representation of prefix.
+ */
+ public String asIpPrefix() {
+ try {
+ return InetAddress.getByAddress(prefix).getHostAddress() + "/" + bit;
+ } catch (UnknownHostException e) {
+ return "NaA/"+bit;
+ }
+ }
+
+ /**
+ * Retrieve iterator.
+ */
+ @Override
+ public Iterator<RadixTrie<T>.TrieNode> iterator() {
+ return new TriePostOrderIterator(this);
+ }
+
+ /**
+ * Post order iterator implementation for prefix trie. It's safe to use it to remove nodes
+ *
+ */
+ private class TriePostOrderIterator implements Iterator<RadixTrie<T>.TrieNode> {
+ private TrieNode current, lastNodeVisited;
+ private Deque<RadixTrie<T>.TrieNode> stack;
+
+ TriePostOrderIterator(TrieNode node) {
+ stack = new ArrayDeque<RadixTrie<T>.TrieNode>();
+ current = node;
+ lastNodeVisited = null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ TrieNode peekNode, last = lastNodeVisited;
+ if (current != null && (current.left != null || current.right != null)) {
+ return true;
+ } else {
+ Iterator<TrieNode> it = stack.iterator();
+ while (it.hasNext()) {
+ peekNode = it.next();
+ if (peekNode.right != null && !peekNode.right.equals(last)) {
+ return true;
+ } else {
+ last = peekNode;
+ if (peekNode.prefix != null) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public RadixTrie<T>.TrieNode next() {
+ TrieNode peekNode;
+ TrieNode next = current;
+ while (!stack.isEmpty() || next != null) {
+ if (next != null) {
+ stack.push(next);
+ next = next.left;
+ } else {
+ peekNode = stack.peek();
+ if (peekNode.right != null && !peekNode.right.equals(lastNodeVisited)) {
+ next = peekNode.right;
+ } else {
+ lastNodeVisited = stack.pop();
+ if (peekNode.prefix != null) {
+ current = null;
+ return peekNode;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Before;
import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
+import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
/**
* Test for {@link HashMapDb} class
Assert.assertEquals(mapValue1, map.getSpecific(dbEntryKey, mapKey1));
}
+ /**
+ * Test {@link HashMapDb#getBest} with IP prefix
+ */
+ @Test
+ public void testGetBest_withIpPrefix() throws Exception {
+ final Eid ipv4PrefixEid1 = LispAddressUtil.asIpv4PrefixBinaryEid("192.168.0.0" + "/16");
+ final Eid ipv4PrefixEid2 = LispAddressUtil.asIpv4PrefixBinaryEid("192.169.0.0" + "/16");
+ final Eid ipv4PrefixEid3 = LispAddressUtil.asIpv4PrefixBinaryEid("192.168.1.1" + "/32");
+ final String mapSubKey1 = "mapSubKey1";
+ final String mapSubKey2 = "mapSubKey2";
+ final String mapValue1 = "mapValue1";
+ final String mapValue2 = "mapValue2";
+
+ final MappingEntry<Object> mapEntry1 = new MappingEntry<Object>(mapSubKey1, mapValue1);
+ final MappingEntry<Object> mapEntry2 = new MappingEntry<Object>(mapSubKey2, mapValue2);
+
+ map.put(ipv4PrefixEid1, mapEntry1);
+ map.put(ipv4PrefixEid2, mapEntry2);
+
+ Map<String, ?> res = map.getBest(ipv4PrefixEid3);
+ Assert.assertEquals(Collections.<String, Object>singletonMap(mapSubKey1, mapValue1), res);
+ }
+
+ /**
+ * Test {@link HashMapDb#getBest} with non-IP prefix
+ */
+ @Test
+ public void testGetBest_withNonIpPrefix() throws Exception {
+ final Eid mac1 = LispAddressUtil.asMacEid("01:02:03:04:05:06");
+ final Eid mac2 = LispAddressUtil.asMacEid("01:02:03:04:05:07");
+ final String mapSubKey1 = "mapSubKey1";
+ final String mapSubKey2 = "mapSubKey2";
+ final String mapValue1 = "mapValue1";
+ final String mapValue2 = "mapValue2";
+ final MappingEntry<Object> mapEntry1 = new MappingEntry<Object>(mapSubKey1, mapValue1);
+ final MappingEntry<Object> mapEntry2 = new MappingEntry<Object>(mapSubKey2, mapValue2);
+
+ map.put(mac1, mapEntry1);
+ map.put(mac2, mapEntry2);
+ Assert.assertEquals(Collections.<String, Object>singletonMap(mapSubKey1, mapValue1), map.getBest(mac1));
+ }
+
+ /**
+ * Test {@link HashMapDb#getBestPair} with IP prefix
+ */
+ @Test
+ public void testGetBestPair_withIpPrefix() throws Exception {
+ final Eid ipv4PrefixEid1 = LispAddressUtil.asIpv4PrefixBinaryEid("192.168.0.0" + "/16");
+ final Eid ipv4PrefixEid2 = LispAddressUtil.asIpv4PrefixBinaryEid("192.169.0.0" + "/16");
+ final Eid ipv4PrefixEid3 = LispAddressUtil.asIpv4PrefixBinaryEid("192.168.1.1" + "/32");
+ final String mapSubKey1 = "mapSubKey1";
+ final String mapSubKey2 = "mapSubKey2";
+ final String mapValue1 = "mapValue1";
+ final String mapValue2 = "mapValue2";
+ final MappingEntry<Object> mapEntry1 = new MappingEntry<Object>(mapSubKey1, mapValue1);
+ final MappingEntry<Object> mapEntry2 = new MappingEntry<Object>(mapSubKey2, mapValue2);
+
+ map.put(ipv4PrefixEid1, mapEntry1);
+ map.put(ipv4PrefixEid2, mapEntry2);
+
+ SimpleImmutableEntry<Eid, Map<String, ?>> res = map.getBestPair(ipv4PrefixEid3);
+ Assert.assertEquals(ipv4PrefixEid1, res.getKey());
+ Assert.assertEquals(Collections.<String, Object>singletonMap(mapSubKey1, mapValue1), res.getValue());
+ }
+
+ /**
+ * Test {@link HashMapDb#getBestPair} with non-IP prefix
+ */
+ @Test
+ public void testGetBestPair_withNonIpPrefix() throws Exception {
+ final Eid mac1 = LispAddressUtil.asMacEid("01:02:03:04:05:06");
+ final Eid mac2 = LispAddressUtil.asMacEid("01:02:03:04:05:07");
+ final String mapSubKey1 = "mapSubKey1";
+ final String mapSubKey2 = "mapSubKey2";
+ final String mapValue1 = "mapValue1";
+ final String mapValue2 = "mapValue2";
+ final MappingEntry<Object> mapEntry1 = new MappingEntry<Object>(mapSubKey1, mapValue1);
+ final MappingEntry<Object> mapEntry2 = new MappingEntry<Object>(mapSubKey2, mapValue2);
+
+
+ map.put(mac1, mapEntry1);
+ map.put(mac2, mapEntry2);
+ SimpleImmutableEntry<Eid, Map<String, ?>> res = map.getBestPair(mac1);
+ Assert.assertEquals(mac1, res.getKey());
+ Assert.assertEquals(Collections.<String, Object>singletonMap(mapSubKey1, mapValue1), res.getValue());
+ }
+
@Test
public void testGetAll() throws Exception {
Object dbEntryKey = "dbEntryKey";
Assert.assertNull("DB should be empty after entry removal", map.get(dbEntryKey));
}
+ /**
+ * Test {@link HashMapDb#remove} with IP-prefix
+ */
+ @Test
+ public void testRemove_withIpPrefix() throws Exception {
+ final Eid ipv4PrefixEid1 = LispAddressUtil.asIpv4PrefixBinaryEid("192.168.0.0" + "/16");
+ final String mapSubKey1 = "mapSubKey1";
+ final String mapValue1 = "mapValue1";
+
+ final MappingEntry<Object> mapEntry1 = new MappingEntry<Object>(mapSubKey1, mapValue1);
+
+ map.put(ipv4PrefixEid1, mapEntry1);
+ map.remove(ipv4PrefixEid1);
+
+ Assert.assertNull(map.getBest(ipv4PrefixEid1));
+ Assert.assertNull(map.getBestPair(ipv4PrefixEid1));
+ }
+
@Test
public void testRemoveSpecific() throws Exception {
Object dbEntryKey = "dbEntryKey";
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.inmemorydb.radixtrie;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class RadixTrieTest {
+ private static RadixTrie<Integer> radixTrie4;
+ private static RadixTrie<Integer> radixTrie6;
+
+ private static byte[] IP4_DEFAULT;
+ private static byte[] IP4_BYTES1;
+ private static byte[] IP4_BYTES2;
+ private static byte[] IP4_BYTES3;
+ private static byte[] IP4_BYTES4;
+ private static byte[] IP4_BYTES5;
+ private static byte[] IP4_BYTES6;
+ private static byte[] IP4_BYTES7;
+ private static byte[] IP4_BYTES8;
+ private static byte[] IP4_BYTES9;
+ private static byte[] IP4_BYTES10;
+ private static byte[] IP4_BYTES11;
+ private static byte[] IP4_BYTES12;
+
+ ArrayList<byte []> itPrefixList4;
+ ArrayList<Integer> itPreflenList4;
+
+ private static byte[] IP6_BYTES1;
+ private static byte[] IP6_BYTES2;
+ private static byte[] IP6_BYTES3;
+ private static byte[] IP6_BYTES4;
+ private static byte[] IP6_BYTES5;
+ private static byte[] IP6_BYTES6;
+ private static byte[] IP6_BYTES7;
+
+ @Before
+ public void init() {
+ try {
+ IP4_DEFAULT = InetAddress.getByName("0.0.0.0").getAddress();
+ IP4_BYTES1 = InetAddress.getByName("192.168.0.0").getAddress();
+ IP4_BYTES2 = InetAddress.getByName("192.167.0.0").getAddress();
+ IP4_BYTES3 = InetAddress.getByName("192.169.0.0").getAddress();
+ IP4_BYTES4 = InetAddress.getByName("192.168.1.0").getAddress();
+ IP4_BYTES5 = InetAddress.getByName("192.168.2.0").getAddress();
+ IP4_BYTES6 = InetAddress.getByName("192.168.1.0").getAddress();
+ IP4_BYTES7 = InetAddress.getByName("192.168.1.1").getAddress();
+ IP4_BYTES8 = InetAddress.getByName("193.168.1.1").getAddress();
+ IP4_BYTES9 = InetAddress.getByName("192.168.3.0").getAddress();
+ IP4_BYTES10 = InetAddress.getByName("129.214.0.0").getAddress();
+ IP4_BYTES11 = InetAddress.getByName("192.168.2.0").getAddress();
+ IP4_BYTES12 = InetAddress.getByName("128.0.0.0").getAddress();
+
+ IP6_BYTES1 = InetAddress.getByName("192:168::0:0").getAddress();
+ IP6_BYTES2 = InetAddress.getByName("192:167::0:0").getAddress();
+ IP6_BYTES3 = InetAddress.getByName("192:169::0:0").getAddress();
+ IP6_BYTES4 = InetAddress.getByName("192:168::1:0").getAddress();
+ IP6_BYTES5 = InetAddress.getByName("192:168::2:0").getAddress();
+ IP6_BYTES6 = InetAddress.getByName("192:168::1:0").getAddress();
+ IP6_BYTES7 = InetAddress.getByName("192:168::1:1").getAddress();
+
+ itPrefixList4 = new ArrayList<byte []>();
+ itPrefixList4.add(IP4_BYTES2);
+ itPrefixList4.add(IP4_BYTES7);
+ itPrefixList4.add(IP4_BYTES4);
+ itPrefixList4.add(IP4_BYTES4);
+ itPrefixList4.add(IP4_BYTES5);
+ itPrefixList4.add(IP4_BYTES1);
+ itPrefixList4.add(IP4_BYTES3);
+
+ itPreflenList4 = new ArrayList<Integer>();
+ itPreflenList4.add(16);
+ itPreflenList4.add(32);
+ itPreflenList4.add(25);
+ itPreflenList4.add(24);
+ itPreflenList4.add(24);
+ itPreflenList4.add(16);
+ itPreflenList4.add(16);
+
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Tests v4 CRD operations
+ */
+ @Test
+ public void testIp4() {
+ RadixTrie<Integer>.TrieNode res;
+ radixTrie4 = new RadixTrie<Integer>(32);
+ addIp4Addresses(radixTrie4);
+
+ res = radixTrie4.lookupBest(IP4_BYTES7, 32);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES7));
+ assertTrue(res.prefixLength() == 32);
+ assertTrue(res.data() == 7);
+
+ res = radixTrie4.lookupBest(IP4_BYTES5, 24);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES5));
+ assertTrue(res.prefixLength() == 24);
+ assertTrue(res.data() == 5);
+
+ radixTrie4.remove(IP4_BYTES5, 24);
+ res = radixTrie4.lookupBest(IP4_BYTES5, 24);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES1));
+ assertTrue(res.prefixLength() == 16);
+ assertTrue(res.data() == 1);
+
+ radixTrie4.remove(IP4_BYTES4, 24);
+ radixTrie4.remove(IP4_BYTES7, 32);
+ res = radixTrie4.lookupBest(IP4_BYTES7, 32);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES6));
+ assertTrue(res.prefixLength() == 25);
+ assertTrue(res.data() == 6);
+
+ radixTrie4.removeAll();
+ assertTrue(radixTrie4.getRoot() == null);
+ }
+
+ /**
+ * Tests iterator
+ */
+ @Test
+ public void testIterator() {
+ RadixTrie<Integer>.TrieNode res;
+ int i = 0;
+
+ addIp4Addresses(radixTrie4);
+
+ radixTrie4.remove(IP4_BYTES5, 24);
+ radixTrie4.remove(IP4_BYTES4, 24);
+ radixTrie4.insert(IP4_BYTES5, 24, 1);
+ radixTrie4.insert(IP4_BYTES6, 24, 1);
+
+ Iterator<RadixTrie<Integer>.TrieNode> it = radixTrie4.getRoot().iterator();
+
+ while (it.hasNext()) {
+ res = it.next();
+ assertTrue(Arrays.equals(res.prefix(), itPrefixList4.get(i)));
+ assertTrue(res.prefixLength() == itPreflenList4.get(i));
+ i++;
+ }
+ }
+
+ /**
+ * Tests v4 CRD operations with 0/0 as root
+ */
+ @Test
+ public void testIp4ZeroRoot() {
+ radixTrie4 = new RadixTrie<Integer>(32, true);
+
+ RadixTrie<Integer>.TrieNode res;
+
+ addIp4Addresses(radixTrie4);
+
+ res = radixTrie4.lookupBest(IP4_BYTES7, 32);
+ System.out.println(res.asIpPrefix());
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES7));
+ assertTrue(res.prefixLength() == 32);
+
+ res = radixTrie4.lookupBest(IP4_BYTES5, 24);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES5));
+ assertTrue(res.prefixLength() == 24);
+
+ radixTrie4.remove(IP4_BYTES5, 24);
+ res = radixTrie4.lookupBest(IP4_BYTES5, 24);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES1));
+ assertTrue(res.prefixLength() == 16);
+
+ radixTrie4.remove(IP4_BYTES4, 24);
+ radixTrie4.remove(IP4_BYTES7, 32);
+ res = radixTrie4.lookupBest(IP4_BYTES7, 32);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES6));
+ assertTrue(res.prefixLength() == 25);
+
+ res = radixTrie4.lookupBest(IP4_BYTES8, 32);
+ assertTrue(res == null);
+
+ radixTrie4.insert(IP4_DEFAULT, 0, 0);
+ res = radixTrie4.lookupBest(IP4_BYTES8, 32);
+ assertTrue(Arrays.equals(res.prefix(), IP4_DEFAULT));
+
+ radixTrie4.removeAll();
+ assertTrue(!Arrays.equals(radixTrie4.getRoot().prefix(), IP4_DEFAULT));
+ assertTrue(radixTrie4.getRoot().bit == 0);
+ }
+
+ /**
+ * Tests v4 widest negative prefix
+ */
+ @Test
+ public void testIp4WidestNegativePrefix() {
+ radixTrie4 = new RadixTrie<Integer>(32, true);
+
+ RadixTrie<Integer>.TrieNode res;
+
+ addIp4Addresses(radixTrie4);
+
+ radixTrie4.remove(IP4_BYTES5, 24);
+ radixTrie4.remove(IP4_BYTES4, 24);
+
+ res = radixTrie4.lookupWidestNegative(IP4_BYTES9, 24);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES11));
+ assertTrue(res.prefixLength() == 23);
+ res = radixTrie4.lookupWidestNegative(IP4_BYTES10, 16);
+ assertTrue(Arrays.equals(res.prefix(), IP4_BYTES12));
+ assertTrue(res.prefixLength() == 2);
+ }
+
+ /**
+ * Tests v6 CRD operations
+ * It just makes sure v6 prefix lengths don't generate problems. Therefore it's not as thorough as v4.
+ */
+ @Test
+ public void testIp6() {
+ RadixTrie<Integer>.TrieNode res;
+ radixTrie6 = new RadixTrie<Integer>(128);
+
+ addIp6Addresses(radixTrie6);
+
+ res = radixTrie6.lookupBest(IP6_BYTES7, 128);
+ assertTrue(Arrays.equals(res.prefix(), IP6_BYTES4));
+ assertTrue(res.prefixLength() == 113);
+
+ res = radixTrie6.lookupBest(IP6_BYTES5, 112);
+ assertTrue(Arrays.equals(res.prefix(), IP6_BYTES5));
+ assertTrue(res.prefixLength() == 112);
+
+ radixTrie6.remove(IP6_BYTES5, 112);
+ res = radixTrie6.lookupBest(IP6_BYTES5, 112);
+ assertTrue(Arrays.equals(res.prefix(), IP6_BYTES1));
+ assertTrue(res.prefixLength() == 32);
+
+ radixTrie6.remove(IP6_BYTES4, 112);
+ res = radixTrie6.lookupBest(IP6_BYTES7, 128);
+ assertTrue(Arrays.equals(res.prefix(), IP6_BYTES6));
+ assertTrue(res.prefixLength() == 113);
+
+ radixTrie6.removeAll();
+ assertTrue(radixTrie6.getRoot() == null);
+ }
+
+ /**
+ * Test {@link RadixTrie#removeAll}
+ */
+ @Test
+ public void testRemoveAll() {
+ radixTrie4 = new RadixTrie<Integer>(32, true);
+ addIp4Addresses(radixTrie4);
+
+ radixTrie4.removeAll();
+ Assert.assertEquals(0, radixTrie4.getSize());
+ }
+
+ private void addIp4Addresses(RadixTrie<Integer> trie) {
+ trie.insert(IP4_BYTES7, 32, 7);
+ trie.insert(IP4_BYTES6, 25, 6);
+ trie.insert(IP4_BYTES5, 24, 5);
+ trie.insert(IP4_BYTES4, 24, 4);
+ trie.insert(IP4_BYTES3, 16, 3);
+ trie.insert(IP4_BYTES2, 16, 2);
+ trie.insert(IP4_BYTES1, 16, 1);
+ }
+
+ private void addIp6Addresses(RadixTrie<Integer> trie) {
+ trie.insert(IP6_BYTES1, 32, 1);
+ trie.insert(IP6_BYTES2, 32, 1);
+ trie.insert(IP6_BYTES3, 8, 1);
+ trie.insert(IP6_BYTES4, 112, 1);
+ trie.insert(IP6_BYTES5, 112, 1);
+ trie.insert(IP6_BYTES6, 113, 1);
+ }
+}
}
return 0;
}
+
+ public static short getIpPrefixMask(Eid eid) {
+ Address addr = eid.getAddress();
+ if (addr instanceof Ipv4PrefixBinary) {
+ return ((Ipv4PrefixBinary) addr).getIpv4MaskLength();
+ } else if (addr instanceof Ipv6PrefixBinary) {
+ return ((Ipv6PrefixBinary) addr).getIpv6MaskLength();
+ }
+ return 0;
+ }
}
return dao.getSpecific(key, SubKeys.RECORD);
}
+ @Override
+ public Eid getWidestNegativeMapping(Eid key) {
+ return null;
+ }
+
@Override
public void removeMapping(Eid eid, boolean overwrite) {
Eid key = MaskUtil.normalize(eid);
}
}
- // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
- // or the exact match otherwise. eid must be a 'simple' address
- private Map<String, ?> getDaoEntryBest(Eid eid, ILispDAO dao) {
- Eid key;
- if (MaskUtil.isMaskable(eid.getAddress())) {
- short mask = MaskUtil.getMaskForAddress(eid.getAddress());
- while (mask > 0) {
- key = MaskUtil.normalize(eid, mask);
- mask--;
- Map<String, ?> entry = dao.get(key);
- if (entry != null) {
- return entry;
- }
- }
- return null;
- } else {
- key = MaskUtil.normalize(eid);
- Map<String, ?> entry = dao.get(key);
- return entry;
- }
- }
-
private Object getMappingExactSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
Map<String, ?> daoEntry = dao.get(dstEid);
if (daoEntry != null) {
if (eid == null) {
return null;
}
- Eid key = MaskUtil.normalize(eid);
- Map<String, ?> daoEntry = getDaoEntryBest(key, dao);
+ Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(eid));
if (daoEntry != null) {
return daoEntry.get(SubKeys.RECORD);
} else {
// Returns a mapping corresponding to either the longest prefix match for both dstEid and srcEid,
// if a SourceDest mapping exists, or to dstEid
private Object getMappingLpmSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
- Map<String, ?> daoEntry = getDaoEntryBest(dstEid, dao);
+ Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(dstEid));
if (daoEntry != null) {
// try SrcDst eid lookup
ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
return getMappingLpmSD(srcEid, dstEid, table);
}
+ @Override
+ public Eid getWidestNegativeMapping(Eid key) {
+ return dao.getWidestNegativePrefix(key);
+ }
+
public void removeMapping(Eid eid, boolean overwrite) {
Eid key = MaskUtil.normalize(eid);
ILispDAO table = getVniTable(key);
}
}
- // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
- // or the exact match otherwise. eid must be a 'simple' address
- private Map<String, Object> getDaoEntryBest(Eid eid, ILispDAO dao) {
- Eid key;
- if (MaskUtil.isMaskable(eid.getAddress())) {
- short mask = MaskUtil.getMaskForAddress(eid.getAddress());
- while (mask > 0) {
- key = MaskUtil.normalize(eid, mask);
- mask--;
- Map<String, Object> entry = dao.get(key);
- if (entry != null) {
- return entry;
- }
- }
- return null;
- } else {
- key = MaskUtil.normalize(eid);
- return dao.get(key);
- }
- }
-
- // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
- // or the exact match otherwise. eid must be a 'simple' address
- private SimpleImmutableEntry<Eid, Map<String, ?>> getDaoPairEntryBest(Eid eid, ILispDAO dao) {
- Eid key;
- if (MaskUtil.isMaskable(eid.getAddress())) {
- short mask = MaskUtil.getMaskForAddress(eid.getAddress());
- while (mask > 0) {
- key = MaskUtil.normalize(eid, mask);
- mask--;
- Map<String, ?> entry = dao.get(key);
- if (entry != null) {
- return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
- }
- }
- return null;
- } else {
- key = MaskUtil.normalize(eid);
- Map<String, ?> entry = dao.get(key);
- if (entry != null) {
- return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
- } else {
- return null;
- }
- }
- }
-
// Returns the list of mappings stored in an xTR-ID DAO
private List<Object> getXtrIdMappingList(ILispDAO dao) {
if (dao != null) {
// Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not)
// address
private Object getMappingLpmEid(Eid eid, byte[] xtrId, ILispDAO dao) {
- SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = getDaoPairEntryBest(eid, dao);
+ SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = dao.getBestPair(MaskUtil.normalize(eid));
if (daoEntry != null) {
if (xtrId != null) {
ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.getValue().get(SubKeys.XTRID_RECORDS));
}
public List<Object> getAllXtrIdMappings(Eid eid) {
- Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
+ Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(eid));
if (daoEntry != null) {
ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.get(SubKeys.XTRID_RECORDS));
if (xtrIdTable != null) {
return null;
}
+ public Eid getWidestNegativeMapping(Eid key) {
+ return dao.getWidestNegativePrefix(MaskUtil.normalize(key));
+ }
+
public void removeMapping(Eid eid, boolean overwrite) {
Eid key = MaskUtil.normalize(eid);
ILispDAO table = getVniTable(key);
if (timestamp == null) {
timestamp = System.currentTimeMillis();
}
- Map<String, Object> daoEntry = getDaoEntryBest(eid, table);
+ Map<String, Object> daoEntry = table.getBest(MaskUtil.normalize(eid));
if (daoEntry != null) {
daoEntry.put(SubKeys.REGDATE, new Date(timestamp));
}
final Map<String, Object> entry = getEntry1();
final Map<String, Object> entry2 = getEntry2();
- when(tableDaoMock.get(normalizedDstAddr)).thenReturn(entry);
- when(srcDstDaoMock.get(normalizedSrcAddr)).thenReturn(entry2);
+ when(tableDaoMock.getBest(normalizedDstAddr)).thenReturn(entry);
+ when(srcDstDaoMock.getBest(normalizedSrcAddr)).thenReturn(entry2);
assertEquals(DUMMY_OBJECT_2, multiTableMapCache.getMapping(null, EID_SOURCE_DEST_KEY_TYPE));
assertNull(multiTableMapCache.getMapping(null, null));
final Map<String, Object> entry2 = getEntry2();
when(daoMock.getSpecific(VNI, SubKeys.VNI)).thenReturn(tableDaoMock);
- when(tableDaoMock.get(key)).thenReturn(entry);
- when(srcDstDaoMock.get(key2)).thenReturn(entry2);
+ when(tableDaoMock.getBest(key)).thenReturn(entry);
+ when(srcDstDaoMock.getBest(key2)).thenReturn(entry2);
assertEquals(DUMMY_OBJECT_2, multiTableMapCache.getMapping(EID_IPV4_PREFIX_SRC, EID_IPV4_PREFIX_DST));
assertEquals(DUMMY_OBJECT, multiTableMapCache.getMapping(null, EID_IPV4_PREFIX_DST));
import java.util.Date;
import java.util.Map;
+import java.util.AbstractMap.SimpleImmutableEntry;
import org.junit.Before;
import org.junit.Test;
}
/**
- * Tests {@link SimpleMapCache#getDaoEntryBest} method with maskable address.
+ * Tests {@link SimpleMapCache#getAllXtrIdMappings} method with maskable address.
*/
@Test
@SuppressWarnings("unchecked")
- public void getDaoEntryBestTest_withMaskableAddress() {
+ public void getAllXtrIdMappings_withMaskableAddress() {
final Eid normalizedKey = MaskUtil.normalize(EID_IPV4_PREFIX_1_VNI, MASK);
final Map<String, Object> entryMock = Mockito.mock(Map.class);
final ILispDAO xtrIdRecordsMock = Mockito.mock(ILispDAO.class);
- Mockito.when(daoMock.get(normalizedKey)).thenReturn(entryMock);
+ Mockito.when(daoMock.getBest(normalizedKey)).thenReturn(entryMock);
Mockito.when(entryMock.get(SubKeys.XTRID_RECORDS)).thenReturn(xtrIdRecordsMock);
Mockito.when(xtrIdRecordsMock.getSpecific(EID_IPV4_PREFIX_1_VNI, SubKeys.XTRID_RECORDS))
.thenReturn(xtrIdDaoMock);
simpleMapCache.getAllXtrIdMappings(EID_IPV4_PREFIX_1_VNI);
- Mockito.verify(daoMock).get(Mockito.any(Eid.class));
+ Mockito.verify(daoMock).getBest(Mockito.any(Eid.class));
}
/**
- * Tests {@link SimpleMapCache#getDaoEntryBest} method with non maskable address.
+ * Tests {@link SimpleMapCache#getAllXtrIdMappings} method with non maskable address.
*/
@Test
@SuppressWarnings("unchecked")
- public void getDaoEntryBestTest_withNonMaskableAddress() {
+ public void getAllXtrIdMappings_withNonMaskableAddress() {
final Map<String, Object> entryMock = Mockito.mock(Map.class);
final ILispDAO xtrIdRecordsMock = Mockito.mock(ILispDAO.class);
- Mockito.when(daoMock.get(NORMALIZED_EID_IPV4)).thenReturn(entryMock);
+ Mockito.when(daoMock.getBest(NORMALIZED_EID_IPV4)).thenReturn(entryMock);
Mockito.when(entryMock.get(SubKeys.XTRID_RECORDS)).thenReturn(xtrIdRecordsMock);
Mockito.when(xtrIdRecordsMock.getSpecific(EID_IPV4, SubKeys.XTRID_RECORDS))
.thenReturn(xtrIdDaoMock);
simpleMapCache.getAllXtrIdMappings(EID_IPV4);
- Mockito.verify(daoMock).get(Mockito.any(Eid.class));
+ Mockito.verify(daoMock).getBest(Mockito.any(Eid.class));
}
/**
- * Tests {@link SimpleMapCache#getDaoEntryBest} method with null daoEntry.
+ * Tests {@link SimpleMapCache#getAllXtrIdMappings} method with null daoEntry.
*/
@Test
- public void getDaoEntryBestTest_withNullEntry() {
- Mockito.when(daoMock.get(Mockito.any(Eid.class))).thenReturn(null);
+ public void getAllXtrIdMappings_withNullEntry() {
+ Mockito.when(daoMock.getBest(Mockito.any(Eid.class))).thenReturn(null);
assertNull(simpleMapCache.getAllXtrIdMappings(EID_IPV4_PREFIX_1_VNI));
- Mockito.verify(daoMock, Mockito.times(24)).get(Mockito.any(Eid.class));
+ Mockito.verify(daoMock, Mockito.times(1)).getBest(Mockito.any(Eid.class));
}
/**
@SuppressWarnings("unchecked")
public void getMappingLpmEidTest() throws Exception {
final Map<String, Object> mapMock = Mockito.mock(Map.class);
+ final SimpleImmutableEntry<Eid, Map<String, ?>> mapPair = new SimpleImmutableEntry<>(
+ NORMALIZED_EID_IPV4_PREFIX_DST, mapMock);
final ILispDAO xtrIdRecordsMock = Mockito.mock(ILispDAO.class);
final MappingRecord expiredMappingRecord = getDefaultMappingRecordBuilder().setTimestamp(1L).build(); // expired
final MappingRecord mappingRecord = getDefaultMappingRecordBuilder().build(); // not expired
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
- Mockito.when(tableMock.get(NORMALIZED_EID_IPV4_PREFIX_DST)).thenReturn(mapMock);
+ Mockito.when(tableMock.getBestPair(NORMALIZED_EID_IPV4_PREFIX_DST)).thenReturn(mapPair);
Mockito.when(mapMock.get(SubKeys.XTRID_RECORDS)).thenReturn(xtrIdRecordsMock);
Mockito.when(xtrIdRecordsMock.getSpecific(EID_IPV4_PREFIX_DST, SubKeys.XTRID_RECORDS)).thenReturn(xtrIdDaoMock);
Mockito.when(xtrIdDaoMock.getSpecific(XTR_ID, SubKeys.RECORD))
@SuppressWarnings("unchecked")
public void getMappingLpmEidTest_withNullXtrId() throws Exception {
final Map<String, Object> mapMock = Mockito.mock(Map.class);
+ final SimpleImmutableEntry<Eid, Map<String, ?>> mapPair = new SimpleImmutableEntry<>(
+ NORMALIZED_EID_IPV4_PREFIX_DST, mapMock);
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
- Mockito.when(tableMock.get(MaskUtil.normalize(EID_IPV4_PREFIX_DST, (short) 24))).thenReturn(mapMock);
+ Mockito.when(tableMock.getBestPair(MaskUtil.normalize(EID_IPV4_PREFIX_DST, (short) 24))).thenReturn(mapPair);
Mockito.when(mapMock.get(SubKeys.REGDATE)).thenReturn(EXPIRED_DATE);
simpleMapCache.getMapping(null, EID_IPV4_PREFIX_DST, null);
Mockito.verify(tableMock).removeSpecific(NORMALIZED_EID_IPV4_PREFIX_DST, SubKeys.REGDATE);
Mockito.verify(tableMock).removeSpecific(NORMALIZED_EID_IPV4_PREFIX_DST, SubKeys.RECORD);
Mockito.verify(mapMock).get(SubKeys.RECORD);
-
}
/**
- * Tests {@link SimpleMapCache#getDaoPairEntryBest} method with maskable eid.
+ * Tests {@link SimpleMapCache#getMapping} method with maskable eid.
*/
@Test
@SuppressWarnings("unchecked")
- public void getDaoPairEntryBestTest_withMaskableEid() {
+ public void getMappingTest_withMaskableEid() {
final Eid ipv4PrefixEid = LispAddressUtil.asIpv4PrefixEid("192.168.0.225" + "/32");
- final Eid normalizedKey = MaskUtil.normalize(EID_IPV4_PREFIX_DST, MASK);
final Map<String, Object> entryMock = Mockito.mock(Map.class);
+ final SimpleImmutableEntry<Eid, Map<String, ?>> mapPair = new SimpleImmutableEntry<>(
+ NORMALIZED_EID_IPV4_PREFIX_DST, entryMock);
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
- Mockito.when(tableMock.get(Mockito.any(Eid.class))).thenReturn(null);
- Mockito.when(tableMock.get(normalizedKey)).thenReturn(entryMock);
+ Mockito.when(tableMock.getBestPair(ipv4PrefixEid)).thenReturn(mapPair);
Mockito.when(entryMock.get(SubKeys.XTRID_RECORDS)).thenReturn(xtrIdDaoMock);
+ Mockito.when(xtrIdDaoMock.getSpecific(NORMALIZED_EID_IPV4_PREFIX_DST, SubKeys.XTRID_RECORDS)).thenReturn(null);
simpleMapCache.getMapping(null, ipv4PrefixEid, XTR_ID);
Mockito.verify(entryMock).get(SubKeys.XTRID_RECORDS);
}
/**
- * Tests {@link SimpleMapCache#getDaoPairEntryBest} method with maskable eid and entry not found.
+ * Tests {@link SimpleMapCache#getMapping} method with maskable eid and entry not found.
*/
@Test
- public void getDaoPairEntryBestTest_withMaskableEid_noEntry() {
+ public void getMappingTest_withMaskableEid_noEntry() {
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
Mockito.when(tableMock.get(Mockito.any(Eid.class))).thenReturn(null);
}
/**
- * Tests {@link SimpleMapCache#getDaoPairEntryBest} method with non-maskable eid.
+ * Tests {@link SimpleMapCache#getMapping} method with non-maskable eid.
*/
@Test
@SuppressWarnings("unchecked")
- public void getDaoPairEntryBestTest_withNonMaskableEid() {
+ public void getMappingTest_withNonMaskableEid() {
final Map<String, Object> entryMock = Mockito.mock(Map.class);
+ final SimpleImmutableEntry<Eid, Map<String, ?>> mapPair = new SimpleImmutableEntry<>(
+ NORMALIZED_EID_IPV4_PREFIX_DST, entryMock);
+
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
- Mockito.when(tableMock.get(NORMALIZED_EID_IPV4)).thenReturn(entryMock);
+ Mockito.when(tableMock.getBestPair(NORMALIZED_EID_IPV4)).thenReturn(mapPair);
Mockito.when(entryMock.get(SubKeys.XTRID_RECORDS)).thenReturn(xtrIdDaoMock);
simpleMapCache.getMapping(null, EID_IPV4, XTR_ID);
}
/**
- * Tests {@link SimpleMapCache#getDaoPairEntryBest} method with non-maskable eid and entry not found.
+ * Tests {@link SimpleMapCache#getMapping} method with non-maskable eid and entry not found.
*/
@Test
- public void getDaoPairEntryBestTest_withNonMaskableEid_noEntry() {
+ public void getMappingTest_withNonMaskableEid_noEntry() {
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
Mockito.when(tableMock.get(NORMALIZED_EID_IPV4)).thenReturn(null);
public void updateMappingRegistrationTest() {
final Map<String, Object> entryMock = Mockito.mock(Map.class);
Mockito.when(daoMock.getSpecific(VNI_0, SubKeys.VNI)).thenReturn(tableMock);
- Mockito.when(tableMock.get(NORMALIZED_EID_IPV4)).thenReturn(entryMock);
+ Mockito.when(tableMock.getBest(NORMALIZED_EID_IPV4)).thenReturn(entryMock);
simpleMapCache.updateMappingRegistration(EID_IPV4, null);
Mockito.verify(entryMock).put(Mockito.eq(SubKeys.REGDATE), Mockito.any(Date.class));