package org.batfish.datamodel;
import java.io.Serializable;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.ArrayUtils;
import org.batfish.common.BatfishException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.net.InetAddresses;
public class Ip6 implements Comparable<Ip6>, Serializable {
private static Map<Ip6, BitSet> _addressBitsCache = new ConcurrentHashMap<>();
public static final Ip6 MAX = new Ip6(
new BigInteger("+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16));
private static final int NUM_BYTES = 16;
private static final long serialVersionUID = 1L;
public static final Ip6 ZERO = new Ip6(BigInteger.ZERO);
private static String asIpv6AddressString(
BigInteger ipv6AddressAsBigInteger) {
BigInteger remainder = ipv6AddressAsBigInteger;
String out = "";
BigInteger segmentMask = new BigInteger("+FFFF", 16);
for (int i = 0; i < 8; i++) {
out = remainder.and(segmentMask).toString(16) + ":" + out;
remainder = remainder.shiftRight(16);
}
out = out.substring(0, out.length() - 1);
return out;
}
private static BigInteger numSubnetBitsToSubnetBigInteger(int numBits) {
BigInteger val = BigInteger.ZERO;
for (int i = Prefix6.MAX_PREFIX_LENGTH - 1; i > Prefix6.MAX_PREFIX_LENGTH
- 1 - numBits; i--) {
val = val.or(BigInteger.ONE.shiftLeft(i));
}
return val;
}
public static Ip6 numSubnetBitsToSubnetMask(int numBits) {
BigInteger mask = numSubnetBitsToSubnetBigInteger(numBits);
return new Ip6(mask);
}
private final BigInteger _ip6;
public Ip6(BigInteger ip6AsBigInteger) {
_ip6 = ip6AsBigInteger;
}
@JsonCreator
public Ip6(String ipAsString) {
boolean invalid = false;
byte[] ip6AsByteArray = null;
if (!ipAsString.contains(":")) {
invalid = true;
}
else {
try {
ip6AsByteArray = InetAddresses.forString(ipAsString).getAddress();
}
catch (IllegalArgumentException e) {
invalid = true;
}
}
if (invalid) {
throw new BatfishException(
"Invalid ipv6 address literal: \"" + ipAsString + "\"");
}
_ip6 = new BigInteger(ip6AsByteArray);
}
public BigInteger asBigInteger() {
return _ip6;
}
@Override
public int compareTo(Ip6 rhs) {
return _ip6.compareTo(rhs._ip6);
}
@Override
public boolean equals(Object o) {
Ip6 rhs = (Ip6) o;
return _ip6.equals(rhs._ip6);
}
public BitSet getAddressBits() {
BitSet bits = _addressBitsCache.get(this);
if (bits == null) {
ByteBuffer b = ByteBuffer.allocate(NUM_BYTES);
byte[] ip6Bytes = _ip6.toByteArray();
ArrayUtils.reverse(ip6Bytes);
b.put(ip6Bytes);
BitSet bitsWithHighestMostSignificant = BitSet.valueOf(b.array());
bits = new BitSet(Prefix6.MAX_PREFIX_LENGTH);
for (int i = Prefix6.MAX_PREFIX_LENGTH - 1, j = 0; i >= 0; i--, j++) {
bits.set(j, bitsWithHighestMostSignificant.get(i));
}
_addressBitsCache.put(this, bits);
}
return bits;
}
public Ip6 getNetworkAddress(int subnetBits) {
BigInteger mask = numSubnetBitsToSubnetBigInteger(subnetBits);
return new Ip6(_ip6.and(mask));
}
@Override
public int hashCode() {
return _ip6.hashCode();
}
public Ip6 inverted() {
BigInteger mask = new BigInteger("+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);
BigInteger invertedBigInteger = mask.andNot(_ip6);
return new Ip6(invertedBigInteger);
}
public String networkString(int prefixLength) {
return toString() + "/" + prefixLength;
}
public int numSubnetBits() {
int numTrailingZeros = _ip6.getLowestSetBit();
if (numTrailingZeros == -1) {
return 0;
}
else {
return Prefix6.MAX_PREFIX_LENGTH - numTrailingZeros;
}
}
@Override
@JsonValue
public String toString() {
return asIpv6AddressString(_ip6);
}
public boolean valid() {
return _ip6.compareTo(BigInteger.ZERO) >= 0 && _ip6.compareTo(
new BigInteger("+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)) <= 0;
}
}