Add radix trie to HashMapDb for fast IP LPM 29/40129/5
authorFlorin Coras <fcoras@cisco.com>
Fri, 3 Jun 2016 07:37:31 +0000 (10:37 +0300)
committerFlorin Coras <fcoras@cisco.com>
Fri, 17 Jun 2016 21:47:54 +0000 (23:47 +0200)
This adds a radix trie implementation for fast IP longest prefix
matching leveraged for now in HashMapDb to speed up mappings lookup. The
change is not fully transparent as it introduces a number of ILispDAO
API and map-cache implementation updates.

An important caveat to consider is that authkey lookup logic in map-caches
cannot be changed to use the tries because keys are not always found
under the longest prefix match. This is a consequence of the fact that
we now accept generic auth keys for aggregate prefixes, i.e., we don't
have one-to-one matching between mapping and auth entries, and we store
both auth keys and mappings in the same map-cache.

Change-Id: I4e7d2badce3b3cd31030fbea87f7366ea6b41035
Signed-off-by: Florin Coras <fcoras@cisco.com>
18 files changed:
mappingservice/api/src/main/java/org/opendaylight/lispflowmapping/interfaces/dao/ILispDAO.java
mappingservice/api/src/main/java/org/opendaylight/lispflowmapping/interfaces/mapcache/IMapCache.java
mappingservice/api/src/main/java/org/opendaylight/lispflowmapping/interfaces/mapcache/IMappingSystem.java
mappingservice/api/src/main/java/org/opendaylight/lispflowmapping/interfaces/mappingservice/IMappingService.java
mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/MappingService.java
mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/MappingSystem.java
mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/lisp/MapResolver.java
mappingservice/implementation/src/test/java/org/opendaylight/lispflowmapping/implementation/lisp/MapResolverTest.java
mappingservice/inmemorydb/src/main/java/org/opendaylight/lispflowmapping/inmemorydb/HashMapDb.java
mappingservice/inmemorydb/src/main/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrie.java [new file with mode: 0644]
mappingservice/inmemorydb/src/test/java/org/opendaylight/lispflowmapping/inmemorydb/HashMapDbTest.java
mappingservice/inmemorydb/src/test/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrieTest.java [new file with mode: 0644]
mappingservice/lisp-proto/src/main/java/org/opendaylight/lispflowmapping/lisp/util/LispAddressUtil.java
mappingservice/mapcache/src/main/java/org/opendaylight/lispflowmapping/mapcache/FlatMapCache.java
mappingservice/mapcache/src/main/java/org/opendaylight/lispflowmapping/mapcache/MultiTableMapCache.java
mappingservice/mapcache/src/main/java/org/opendaylight/lispflowmapping/mapcache/SimpleMapCache.java
mappingservice/mapcache/src/test/java/org/opendaylight/lispflowmapping/mapcache/MultiTableMapCacheTest.java
mappingservice/mapcache/src/test/java/org/opendaylight/lispflowmapping/mapcache/SimpleMapCacheTest.java

index f9ff43ea59843b17d0c707779d3d17d0cac12306..8474f94e933a569fa279de4a94adba3597413dd3 100644 (file)
@@ -9,6 +9,9 @@
 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 {
 
@@ -37,11 +40,38 @@ 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
      *
index 3870c21ef1fdbece1e6bdf19d690034542e8b99e..ba78288c7067fe3414d7227cfbe70ac9f767d4c4 100644 (file)
@@ -44,6 +44,15 @@ public interface IMapCache {
      */
     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
      *
index 3dad3ba1b909f3ecafc8c6e0b52144c40fa69dfb..8c767245de0a0a05fbaab25ef91349d1120e8697 100644 (file)
@@ -65,6 +65,15 @@ public interface IMappingSystem {
      */
     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
      *
index afc6cc0dc6a12218e464cc596da246616124bd59..6bf0d995a0bc7b4aea117a0bdc03878cafcfbd4a 100644 (file)
@@ -75,6 +75,15 @@ public interface IMappingService {
      */
     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
      *
index 643c70c1bf25aabb377b2926957492de15413c54..f5bedf75cd2232e10109ef6f26f082a4a9d0182f 100644 (file)
@@ -403,6 +403,11 @@ public class MappingService implements OdlMappingserviceService, IMappingService
         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));
index 55082865c40bbfdbc62d0beeb043d982c2d12fd8..1cf24233544bbbb9fe288cbe225ae96602ac20c4 100644 (file)
@@ -229,6 +229,27 @@ public class MappingSystem implements IMappingSystem {
         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);
index 4c534fe7462e242aea99ffd51eab1ea11a029285..7f425983c648a3882351c9a859e0f6faba5dcb79 100644 (file)
@@ -103,7 +103,7 @@ public class MapResolver implements IMapResolverAsync {
         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);
index 12d7633bca408a658cf0281b0bfbeafc3daa28d3..fb5e7899ced815bdfa0073117a90661d59812aa3 100644 (file)
@@ -125,6 +125,7 @@ public class MapResolverTest {
                 .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();
index 102162a8f832cbd4be539ba32059e5fc9ecd3d37..1990dbd172727fc1dc1dde0acbac8ee9f9bf4552 100644 (file)
@@ -10,14 +10,20 @@ package org.opendaylight.lispflowmapping.inmemorydb;
 
 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;
 
@@ -31,12 +37,30 @@ public class HashMapDb implements ILispDAO, AutoCloseable {
     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());
         }
     }
@@ -55,6 +79,52 @@ public class HashMapDb implements ILispDAO, AutoCloseable {
         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()) {
@@ -63,9 +133,42 @@ public class HashMapDb implements ILispDAO, AutoCloseable {
             }
         }
     }
+    @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);
     }
 
@@ -78,6 +181,8 @@ public class HashMapDb implements ILispDAO, AutoCloseable {
 
     @Override
     public void removeAll() {
+        ip4Trie.removeAll();
+        ip6Trie.removeAll();
         data.clear();
     }
 
diff --git a/mappingservice/inmemorydb/src/main/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrie.java b/mappingservice/inmemorydb/src/main/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrie.java
new file mode 100644 (file)
index 0000000..4d2467b
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * 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
index b2e3998e13df7b27cf7d50457dab5c08a9cfa912..717c67a5e49eb1f96a1a8b6bd829874f68a6563d 100644 (file)
@@ -12,6 +12,7 @@ import java.util.Collections;
 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;
@@ -19,6 +20,8 @@ import org.junit.Test;
 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
@@ -87,6 +90,93 @@ public class HashMapDbTest {
         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";
@@ -124,6 +214,24 @@ public class HashMapDbTest {
         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";
diff --git a/mappingservice/inmemorydb/src/test/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrieTest.java b/mappingservice/inmemorydb/src/test/java/org/opendaylight/lispflowmapping/inmemorydb/radixtrie/RadixTrieTest.java
new file mode 100644 (file)
index 0000000..8bda3c0
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * 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);
+    }
+}
index 4e4f4a78029df0169c3d85babf05a4d01ad806d9..2a230e53417fabfe86815f2ae8ea5b4d301f2975 100644 (file)
@@ -917,4 +917,14 @@ public final class LispAddressUtil {
         }
         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;
+    }
 }
index 48d9f98eaf5ea3f55caaad2682b6ebaef1c9120f..f71845fd89c207e2131aebc211d26a4acd73ac55 100644 (file)
@@ -50,6 +50,11 @@ public class FlatMapCache implements IMapCache {
         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);
index 3663c36f9c26ab4d9aad941bc1625f3226dfcbbd..13001a56ec47bdbb10d52b69cc06a089cfa90721 100644 (file)
@@ -79,28 +79,6 @@ public class MultiTableMapCache implements IMapCache {
         }
     }
 
-    // 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) {
@@ -121,8 +99,7 @@ public class MultiTableMapCache implements IMapCache {
         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 {
@@ -133,7 +110,7 @@ public class MultiTableMapCache implements IMapCache {
     // 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);
@@ -171,6 +148,11 @@ public class MultiTableMapCache implements IMapCache {
         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);
index 4cb8ad763ad1ca4c7a3e30c99c6b2b10efab37b8..69aec2927a7ec8f0bc24e8de64e7a8dc02fd0a98 100644 (file)
@@ -135,53 +135,6 @@ public class SimpleMapCache implements IMapCache {
         }
     }
 
-    // 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) {
@@ -236,7 +189,7 @@ public class SimpleMapCache implements IMapCache {
     // 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));
@@ -282,7 +235,7 @@ public class SimpleMapCache implements IMapCache {
     }
 
     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) {
@@ -292,6 +245,10 @@ public class SimpleMapCache implements IMapCache {
         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);
@@ -404,7 +361,7 @@ public class SimpleMapCache implements IMapCache {
         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));
         }
index 1a4d22957bc8c1728c06404af033bdc2cb89aaa2..325f5ba9746b53daabf67a24dac2d79c7d004f01 100644 (file)
@@ -144,8 +144,8 @@ public class MultiTableMapCacheTest {
         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));
@@ -184,8 +184,8 @@ public class MultiTableMapCacheTest {
         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));
index 8dd932173c164fea9a472804ea544dcd2215e041..03fdcfb91db0efdaf20fc33b7e5292720f9777d7 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.collect.Lists;
 
 import java.util.Date;
 import java.util.Map;
+import java.util.AbstractMap.SimpleImmutableEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -221,51 +222,51 @@ public class SimpleMapCacheTest {
     }
 
     /**
-     * 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));
     }
 
     /**
@@ -275,12 +276,14 @@ public class SimpleMapCacheTest {
     @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))
@@ -302,41 +305,43 @@ public class SimpleMapCacheTest {
     @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);
 
@@ -344,14 +349,17 @@ public class SimpleMapCacheTest {
     }
 
     /**
-     * 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);
@@ -359,10 +367,10 @@ public class SimpleMapCacheTest {
     }
 
     /**
-     * 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);
 
@@ -399,7 +407,7 @@ public class SimpleMapCacheTest {
     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));