package org.batfish.bdp;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.batfish.datamodel.AbstractRoute;
import org.batfish.datamodel.IRib;
import org.batfish.datamodel.Ip;
import org.batfish.datamodel.Prefix;
import org.batfish.datamodel.collections.MultiSet;
import org.batfish.datamodel.collections.TreeMultiSet;
public abstract class AbstractRib<R extends AbstractRoute> implements IRib<R> {
private class ByteTrie implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _root;
public ByteTrie() {
_root = new ByteTrieNode(Prefix.ZERO);
}
public void addRoute(R route) {
Prefix prefix = route.getNetwork();
int prefixLength = prefix.getPrefixLength();
BitSet bits = prefix.getAddress().getAddressBits();
_root.addRoute(route, bits, prefixLength, 0);
}
public Set<R> getLongestPrefixMatch(Ip address) {
BitSet addressBits = address.getAddressBits();
return _root.getLongestPrefixMatch(address, addressBits, 0);
}
public Set<R> getRoutes() {
Set<R> routes = new LinkedHashSet<>();
_root.collectRoutes(routes);
return routes;
}
public boolean mergeRoute(R route) {
Prefix prefix = route.getNetwork();
int prefixLength = prefix.getPrefixLength();
BitSet bits = prefix.getAddress().getAddressBits();
return _root.mergeRoute(route, bits, prefixLength, 0);
}
}
private class ByteTrieNode implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private ByteTrieNode _left;
private Prefix _prefix;
private ByteTrieNode _right;
private final Set<R> _routes;
public ByteTrieNode(Prefix prefix) {
_routes = new HashSet<>();
_prefix = prefix;
}
public void addRoute(R route, BitSet bits, int prefixLength,
int firstUnmatchedBitIndex) {
if (prefixLength == _prefix.getPrefixLength()) {
_routes.add(route);
}
else {
boolean currentBit = bits.get(firstUnmatchedBitIndex);
if (currentBit) {
if (_right == null) {
_right = new ByteTrieNode(route.getNetwork());
_right._routes.add(route);
}
else {
Prefix rightPrefix = _right._prefix;
int rightPrefixLength = rightPrefix.getPrefixLength();
Ip rightAddress = rightPrefix.getAddress();
BitSet rightAddressBits = rightAddress.getAddressBits();
int nextUnmatchedBit;
boolean currentAddressBit = false;
boolean currentRightAddressBit;
for (nextUnmatchedBit = firstUnmatchedBitIndex
+ 1; nextUnmatchedBit < rightPrefixLength
&& nextUnmatchedBit < prefixLength; nextUnmatchedBit++) {
currentAddressBit = bits.get(nextUnmatchedBit);
currentRightAddressBit = rightAddressBits
.get(nextUnmatchedBit);
if (currentRightAddressBit != currentAddressBit) {
break;
}
}
if (nextUnmatchedBit == rightPrefixLength) {
_right.addRoute(route, bits, prefixLength,
nextUnmatchedBit);
}
else if (nextUnmatchedBit == prefixLength) {
currentRightAddressBit = rightAddressBits
.get(nextUnmatchedBit);
ByteTrieNode oldRight = _right;
_right = new ByteTrieNode(route.getNetwork());
_right._routes.add(route);
if (currentRightAddressBit) {
_right._right = oldRight;
}
else {
_right._left = oldRight;
}
}
else {
ByteTrieNode oldRight = _right;
Prefix newNetwork = new Prefix(
route.getNetwork().getAddress(), nextUnmatchedBit)
.getNetworkPrefix();
_right = new ByteTrieNode(newNetwork);
if (currentAddressBit) {
_right._left = oldRight;
_right._right = new ByteTrieNode(route.getNetwork());
_right._right._routes.add(route);
}
else {
_right._right = oldRight;
_right._left = new ByteTrieNode(route.getNetwork());
_right._left._routes.add(route);
}
}
}
}
else {
if (_left == null) {
_left = new ByteTrieNode(route.getNetwork());
_left._routes.add(route);
}
else {
Prefix leftPrefix = _left._prefix;
int leftPrefixLength = leftPrefix.getPrefixLength();
Ip leftAddress = leftPrefix.getAddress();
BitSet leftAddressBits = leftAddress.getAddressBits();
int nextUnmatchedBit;
boolean currentAddressBit = false;
boolean currentLeftAddressBit;
for (nextUnmatchedBit = firstUnmatchedBitIndex
+ 1; nextUnmatchedBit < leftPrefixLength
&& nextUnmatchedBit < prefixLength; nextUnmatchedBit++) {
currentAddressBit = bits.get(nextUnmatchedBit);
currentLeftAddressBit = leftAddressBits
.get(nextUnmatchedBit);
if (currentLeftAddressBit != currentAddressBit) {
break;
}
}
if (nextUnmatchedBit == leftPrefixLength) {
_left.addRoute(route, bits, prefixLength,
nextUnmatchedBit);
}
else if (nextUnmatchedBit == prefixLength) {
currentLeftAddressBit = leftAddressBits
.get(nextUnmatchedBit);
ByteTrieNode oldLeft = _left;
_left = new ByteTrieNode(route.getNetwork());
_left._routes.add(route);
if (currentLeftAddressBit) {
_left._right = oldLeft;
}
else {
_left._left = oldLeft;
}
}
else {
ByteTrieNode oldLeft = _left;
Prefix newNetwork = new Prefix(
route.getNetwork().getAddress(), nextUnmatchedBit)
.getNetworkPrefix();
_left = new ByteTrieNode(newNetwork);
if (currentAddressBit) {
_left._left = oldLeft;
_left._right = new ByteTrieNode(route.getNetwork());
_left._right._routes.add(route);
}
else {
_left._right = oldLeft;
_left._left = new ByteTrieNode(route.getNetwork());
_left._left._routes.add(route);
}
}
}
}
}
}
public void collectRoutes(Set<R> routes) {
if (_left != null) {
_left.collectRoutes(routes);
}
if (_right != null) {
_right.collectRoutes(routes);
}
routes.addAll(_routes);
}
private Set<R> getLongestPrefixMatch(Ip address) {
Set<R> longestPrefixMatches = new HashSet<>();
for (R route : _routes) {
Prefix prefix = route.getNetwork();
if (prefix.contains(address)) {
longestPrefixMatches.add(route);
}
}
return longestPrefixMatches;
}
public Set<R> getLongestPrefixMatch(Ip address, BitSet bits, int index) {
Set<R> longestPrefixMatches = getLongestPrefixMatch(address);
if (index == Prefix.MAX_PREFIX_LENGTH) {
return longestPrefixMatches;
}
boolean currentBit = bits.get(index);
Set<R> longerMatches = null;
if (currentBit) {
if (_right != null) {
longerMatches = _right.getLongestPrefixMatch(address, bits,
_right._prefix.getPrefixLength());
}
}
else {
if (_left != null) {
longerMatches = _left.getLongestPrefixMatch(address, bits,
_left._prefix.getPrefixLength());
}
}
if (longerMatches == null || longerMatches.isEmpty()) {
return longestPrefixMatches;
}
else {
return longerMatches;
}
}
public boolean mergeRoute(R route, BitSet bits, int prefixLength,
int firstUnmatchedBitIndex) {
if (prefixLength == _prefix.getPrefixLength()) {
// no routes with this prefix, so just add it
if (_routes.isEmpty()) {
_routes.add(route);
return true;
}
else {
// suitability check
R rhs = _routes.iterator().next();
int preferenceComparison = comparePreference(route, rhs);
if (preferenceComparison < 0) {
// less preferable, so it doesn't get added
return false;
}
else if (preferenceComparison == 0) {
// equal preference, so add for multipath routing
if (!_routes.contains(route)) {
_routes.add(route);
return true;
}
else {
// route is already here, so nothing to do
return false;
}
}
else {
// better than all pre-existing routes for this prefix, so
// replace them with this one
_routes.clear();
_routes.add(route);
return true;
}
}
}
else {
boolean currentBit = bits.get(firstUnmatchedBitIndex);
if (currentBit) {
if (_right == null) {
_right = new ByteTrieNode(route.getNetwork());
_right._routes.add(route);
return true;
}
else {
Prefix rightPrefix = _right._prefix;
int rightPrefixLength = rightPrefix.getPrefixLength();
Ip rightAddress = rightPrefix.getAddress();
BitSet rightAddressBits = rightAddress.getAddressBits();
int nextUnmatchedBit;
boolean currentAddressBit = false;
boolean currentRightAddressBit;
for (nextUnmatchedBit = firstUnmatchedBitIndex
+ 1; nextUnmatchedBit < rightPrefixLength
&& nextUnmatchedBit < prefixLength; nextUnmatchedBit++) {
currentAddressBit = bits.get(nextUnmatchedBit);
currentRightAddressBit = rightAddressBits
.get(nextUnmatchedBit);
if (currentRightAddressBit != currentAddressBit) {
break;
}
}
if (nextUnmatchedBit == rightPrefixLength) {
return _right.mergeRoute(route, bits, prefixLength,
nextUnmatchedBit);
}
else if (nextUnmatchedBit == prefixLength) {
currentRightAddressBit = rightAddressBits
.get(nextUnmatchedBit);
ByteTrieNode oldRight = _right;
_right = new ByteTrieNode(route.getNetwork());
_right._routes.add(route);
if (currentRightAddressBit) {
_right._right = oldRight;
}
else {
_right._left = oldRight;
}
return true;
}
else {
ByteTrieNode oldRight = _right;
Prefix newNetwork = new Prefix(
route.getNetwork().getAddress(), nextUnmatchedBit)
.getNetworkPrefix();
_right = new ByteTrieNode(newNetwork);
if (currentAddressBit) {
_right._left = oldRight;
_right._right = new ByteTrieNode(route.getNetwork());
_right._right._routes.add(route);
}
else {
_right._right = oldRight;
_right._left = new ByteTrieNode(route.getNetwork());
_right._left._routes.add(route);
}
return true;
}
}
}
else {
if (_left == null) {
_left = new ByteTrieNode(route.getNetwork());
_left._routes.add(route);
return true;
}
else {
Prefix leftPrefix = _left._prefix;
int leftPrefixLength = leftPrefix.getPrefixLength();
Ip leftAddress = leftPrefix.getAddress();
BitSet leftAddressBits = leftAddress.getAddressBits();
int nextUnmatchedBit;
boolean currentAddressBit = false;
boolean currentLeftAddressBit;
for (nextUnmatchedBit = firstUnmatchedBitIndex
+ 1; nextUnmatchedBit < leftPrefixLength
&& nextUnmatchedBit < prefixLength; nextUnmatchedBit++) {
currentAddressBit = bits.get(nextUnmatchedBit);
currentLeftAddressBit = leftAddressBits
.get(nextUnmatchedBit);
if (currentLeftAddressBit != currentAddressBit) {
break;
}
}
if (nextUnmatchedBit == leftPrefixLength) {
return _left.mergeRoute(route, bits, prefixLength,
nextUnmatchedBit);
}
else if (nextUnmatchedBit == prefixLength) {
currentLeftAddressBit = leftAddressBits
.get(nextUnmatchedBit);
ByteTrieNode oldLeft = _left;
_left = new ByteTrieNode(route.getNetwork());
_left._routes.add(route);
if (currentLeftAddressBit) {
_left._right = oldLeft;
}
else {
_left._left = oldLeft;
}
return true;
}
else {
ByteTrieNode oldLeft = _left;
Prefix newPrefix = new Prefix(
route.getNetwork().getAddress(), nextUnmatchedBit)
.getNetworkPrefix();
_left = new ByteTrieNode(newPrefix);
if (currentAddressBit) {
_left._left = oldLeft;
_left._right = new ByteTrieNode(route.getNetwork());
_left._right._routes.add(route);
}
else {
_left._right = oldLeft;
_left._left = new ByteTrieNode(route.getNetwork());
_left._left._routes.add(route);
}
return true;
}
}
}
}
}
@Override
public String toString() {
return _prefix.toString();
}
}
/**
*
*/
private static final long serialVersionUID = 1L;
protected VirtualRouter _owner;
private ByteTrie _trie;
public AbstractRib(VirtualRouter owner) {
_trie = new ByteTrie();
_owner = owner;
}
@Override
public void addRoute(R route) {
_trie.addRoute(route);
}
@Override
public final MultiSet<Prefix> getPrefixCount() {
MultiSet<Prefix> prefixCount = new TreeMultiSet<>();
for (R route : getRoutes()) {
Prefix prefix = route.getNetwork();
prefixCount.add(prefix);
}
return prefixCount;
}
@Override
public final SortedSet<Prefix> getPrefixes() {
SortedSet<Prefix> prefixes = new TreeSet<>();
Set<R> routes = getRoutes();
for (R route : routes) {
prefixes.add(route.getNetwork());
}
return prefixes;
}
@Override
public Set<R> getRoutes() {
return _trie.getRoutes();
}
@Override
public final Map<Integer, Map<Ip, List<AbstractRoute>>> getRoutesByPrefixPopularity() {
Map<Integer, Map<Ip, List<AbstractRoute>>> map = new TreeMap<>();
MultiSet<Prefix> prefixCountSet = getPrefixCount();
for (AbstractRoute route : getRoutes()) {
Prefix prefix = route.getNetwork();
int prefixCount = prefixCountSet.count(prefix);
Map<Ip, List<AbstractRoute>> byIp = map.get(prefixCount);
if (byIp == null) {
byIp = new TreeMap<>();
map.put(prefixCount, byIp);
}
Ip nextHopIp = route.getNextHopIp();
List<AbstractRoute> routesByPopularity = byIp.get(nextHopIp);
if (routesByPopularity == null) {
routesByPopularity = new ArrayList<>();
byIp.put(nextHopIp, routesByPopularity);
}
routesByPopularity.add(route);
}
return map;
}
@Override
public Set<R> longestPrefixMatch(Ip address) {
return _trie.getLongestPrefixMatch(address);
}
@Override
public boolean mergeRoute(R route) {
return _trie.mergeRoute(route);
}
@Override
public final Map<Prefix, Set<Ip>> nextHopIpsByPrefix() {
Map<Prefix, Set<Ip>> map = new TreeMap<>();
for (AbstractRoute route : getRoutes()) {
Prefix prefix = route.getNetwork();
Ip nextHopIp = route.getNextHopIp();
Set<Ip> nextHopIps = map.get(prefix);
if (nextHopIps == null) {
nextHopIps = new TreeSet<>();
map.put(prefix, nextHopIps);
}
nextHopIps.add(nextHopIp);
}
return map;
}
}