package org.batfish.datamodel; import java.io.Serializable; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.BitSet; import java.util.HashSet; import java.util.Set; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonValue; public class Prefix6Space implements Serializable { private static class BitTrie implements Serializable { /** * */ private static final long serialVersionUID = 1L; private BitTrieNode _root; public BitTrie() { _root = new BitTrieNode(); } public void addPrefix6Range(Prefix6Range prefix6Range) { Prefix6 prefix6 = prefix6Range.getPrefix6(); int prefixLength = prefix6.getPrefixLength(); BitSet bits = getAddressBits(prefix6.getAddress()); _root.addPrefix6Range(prefix6Range, bits, prefixLength, 0); } public void addTrieNodeSpace(BitTrieNode node) { if (node._left != null) { addTrieNodeSpace(node._left); } if (node._right != null) { addTrieNodeSpace(node._right); } for (Prefix6Range prefix6Range : node._prefix6Ranges) { addPrefix6Range(prefix6Range); } } public boolean containsPrefix6Range(Prefix6Range prefix6Range) { Prefix6 prefix6 = prefix6Range.getPrefix6(); int prefixLength = prefix6.getPrefixLength(); BitSet bits = getAddressBits(prefix6.getAddress()); return _root.containsPrefix6Range(prefix6Range, bits, prefixLength, 0); } public Set<Prefix6Range> getPrefix6Ranges() { Set<Prefix6Range> prefix6Ranges = new HashSet<>(); _root.collectPrefix6Ranges(prefix6Ranges); return prefix6Ranges; } } private static class BitTrieNode implements Serializable { /** * */ private static final long serialVersionUID = 1L; private BitTrieNode _left; private Set<Prefix6Range> _prefix6Ranges; private BitTrieNode _right; public BitTrieNode() { _prefix6Ranges = new HashSet<>(); } public void addPrefix6Range(Prefix6Range prefix6Range, BitSet bits, int prefixLength, int depth) { for (Prefix6Range nodeRange : _prefix6Ranges) { if (nodeRange.includesPrefix6Range(prefix6Range)) { return; } } if (prefixLength == depth) { _prefix6Ranges.add(prefix6Range); prune(prefix6Range); } else { boolean currentBit = bits.get(depth); if (currentBit) { if (_right == null) { _right = new BitTrieNode(); } _right.addPrefix6Range(prefix6Range, bits, prefixLength, depth + 1); } else { if (_left == null) { _left = new BitTrieNode(); } _left.addPrefix6Range(prefix6Range, bits, prefixLength, depth + 1); } } } public void collectPrefix6Ranges(Set<Prefix6Range> prefix6Ranges) { prefix6Ranges.addAll(_prefix6Ranges); if (_left != null) { _left.collectPrefix6Ranges(prefix6Ranges); } if (_right != null) { _right.collectPrefix6Ranges(prefix6Ranges); } } public boolean containsPrefix6Range(Prefix6Range prefix6Range, BitSet bits, int prefixLength, int depth) { for (Prefix6Range nodeRange : _prefix6Ranges) { if (nodeRange.includesPrefix6Range(prefix6Range)) { return true; } } if (prefixLength == depth) { return false; } else { boolean currentBit = bits.get(depth); if (currentBit) { if (_right == null) { return false; } else { return _right.containsPrefix6Range(prefix6Range, bits, prefixLength, depth + 1); } } else { if (_left == null) { return false; } else { return _left.containsPrefix6Range(prefix6Range, bits, prefixLength, depth + 1); } } } } private boolean isEmpty() { return _left == null && _right == null && _prefix6Ranges.isEmpty(); } private void prune(Prefix6Range prefix6Range) { if (_left != null) { _left.prune(prefix6Range); if (_left.isEmpty()) { _left = null; } } if (_right != null) { _right.prune(prefix6Range); if (_right.isEmpty()) { _right = null; } } Set<Prefix6Range> oldPrefix6Ranges = new HashSet<>(); oldPrefix6Ranges.addAll(_prefix6Ranges); for (Prefix6Range oldPrefix6Range : oldPrefix6Ranges) { if (!prefix6Range.equals(oldPrefix6Range) && prefix6Range.includesPrefix6Range(oldPrefix6Range)) { _prefix6Ranges.remove(oldPrefix6Range); } } } } private static final int NUM_BITS = 128; /** * */ private static final long serialVersionUID = 1L; // TODO: verify that this has been correctly modified for Ip6 / BigInteger private static BitSet getAddressBits(Ip6 address) { int numBytes = NUM_BITS / 8; BigInteger addressAsBigInteger = address.asBigInteger(); byte[] addressAsByteArray = addressAsBigInteger.toByteArray(); byte[] addressAs128BitByteArray = Arrays.copyOfRange(addressAsByteArray, 0, numBytes); ByteBuffer b = ByteBuffer.allocate(numBytes); b.order(ByteOrder.LITTLE_ENDIAN); b.put(addressAs128BitByteArray); BitSet bitsWithHighestMostSignificant = BitSet.valueOf(b.array()); BitSet bits = new BitSet(NUM_BITS); for (int i = NUM_BITS - 1, j = 0; i >= 0; i--, j++) { bits.set(j, bitsWithHighestMostSignificant.get(i)); } return bits; } private BitTrie _trie; public Prefix6Space() { _trie = new BitTrie(); } @JsonCreator public Prefix6Space(Set<Prefix6Range> prefix6Ranges) { _trie = new BitTrie(); for (Prefix6Range prefix6Range : prefix6Ranges) { _trie.addPrefix6Range(prefix6Range); } } public void addPrefix6(Prefix6 prefix6) { addPrefix6Range(Prefix6Range.fromPrefix6(prefix6)); } public void addPrefix6Range(Prefix6Range prefix6Range) { _trie.addPrefix6Range(prefix6Range); } public void addSpace(Prefix6Space prefix6Space) { _trie.addTrieNodeSpace(prefix6Space._trie._root); } public boolean containsPrefix6(Prefix6 prefix6) { return containsPrefix6Range(Prefix6Range.fromPrefix6(prefix6)); } public boolean containsPrefix6Range(Prefix6Range prefix6Range) { return _trie.containsPrefix6Range(prefix6Range); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return getPrefix6Ranges().equals(((Prefix6Space) obj).getPrefix6Ranges()); } @JsonValue public Set<Prefix6Range> getPrefix6Ranges() { return _trie.getPrefix6Ranges(); } @Override public int hashCode() { return getPrefix6Ranges().hashCode(); } public Prefix6Space intersection(Prefix6Space intersectSpace) { Prefix6Space newSpace = new Prefix6Space(); Set<Prefix6Range> intersectRanges = intersectSpace.getPrefix6Ranges(); for (Prefix6Range intersectRange : intersectRanges) { if (containsPrefix6Range(intersectRange)) { newSpace.addPrefix6Range(intersectRange); } } return newSpace; } @JsonIgnore public boolean isEmpty() { return _trie._root.isEmpty(); } public boolean overlaps(Prefix6Space intersectSpace) { Prefix6Space intersection = intersection(intersectSpace); return !intersection.isEmpty(); } @Override public String toString() { return getPrefix6Ranges().toString(); } }