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; } }