package org.batfish.datamodel;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.BitSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.batfish.common.BatfishException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
public class Ip implements Comparable<Ip>, Serializable {
private static Map<Ip, BitSet> _addressBitsCache = new ConcurrentHashMap<>();
public static final Ip AUTO = new Ip(-1l);
public static final Ip MAX = new Ip(0xFFFFFFFFl);
private static final int NUM_BYTES = 4;
private static final long serialVersionUID = 1L;
public static final Ip ZERO = new Ip(0l);
private static long ipStrToLong(String addr) {
String[] addrArray = addr.split("\\.");
if (addrArray.length != 4) {
if (addr.startsWith("INVALID_IP") || addr.startsWith("AUTO/NONE")) {
String[] tail = addr.split("\\(");
if (tail.length == 2) {
String[] longStrParts = tail[1].split("l");
if (longStrParts.length == 2) {
String longStr = longStrParts[0];
return Long.parseLong(longStr);
}
}
}
throw new BatfishException("Invalid ip string: \"" + addr + "\"");
}
long num = 0;
for (int i = 0; i < addrArray.length; i++) {
int power = 3 - i;
String segmentStr = addrArray[i];
try {
int segment = Integer.parseInt(segmentStr);
num += ((segment % 256 * Math.pow(256, power)));
}
catch (NumberFormatException e) {
throw new BatfishException("Invalid ip segment: \"" + segmentStr
+ "\" in ip string: \"" + addr + "\"", e);
}
}
return num;
}
private static long numSubnetBitsToSubnetLong(int numBits) {
long val = 0;
for (int i = 31; i > 31 - numBits; i--) {
val |= ((long) 1 << i);
}
return val;
}
public static Ip numSubnetBitsToSubnetMask(int numBits) {
long mask = numSubnetBitsToSubnetLong(numBits);
return new Ip(mask);
}
private final long _ip;
public Ip(long ipAsLong) {
_ip = ipAsLong;
}
@JsonCreator
public Ip(String ipAsString) {
_ip = ipStrToLong(ipAsString);
}
public long asLong() {
return _ip;
}
@Override
public int compareTo(Ip rhs) {
return Long.compare(_ip, rhs._ip);
}
@Override
public boolean equals(Object o) {
Ip rhs = (Ip) o;
return _ip == rhs._ip;
}
public BitSet getAddressBits() {
BitSet bits = _addressBitsCache.get(this);
if (bits == null) {
int addressAsInt = (int) (_ip);
ByteBuffer b = ByteBuffer.allocate(NUM_BYTES);
b.order(ByteOrder.LITTLE_ENDIAN); // optional, the initial order of a
// byte
// buffer is always BIG_ENDIAN.
b.putInt(addressAsInt);
BitSet bitsWithHighestMostSignificant = BitSet.valueOf(b.array());
bits = new BitSet(Prefix.MAX_PREFIX_LENGTH);
for (int i = Prefix.MAX_PREFIX_LENGTH - 1, j = 0; i >= 0; i--, j++) {
bits.set(j, bitsWithHighestMostSignificant.get(i));
}
_addressBitsCache.put(this, bits);
}
return bits;
}
public Ip getClassMask() {
long firstOctet = _ip >> 24;
if (firstOctet <= 126) {
return new Ip(0xFF000000l);
}
else if (firstOctet >= 128 && firstOctet <= 191) {
return new Ip(0XFFFF0000l);
}
else if (firstOctet >= 192 && firstOctet <= 223) {
return new Ip(0xFFFFFF00l);
}
else {
throw new BatfishException("Cannot compute classmask");
}
}
public Ip getNetworkAddress(int subnetBits) {
long mask = numSubnetBitsToSubnetLong(subnetBits);
return new Ip(_ip & mask);
}
public Ip getNetworkAddress(Ip mask) {
return new Ip(_ip & mask.asLong());
}
public Ip getSubnetEnd(Ip mask) {
return new Ip(_ip | mask.inverted().asLong());
}
public Ip getWildcardEndIp(Ip wildcard) {
return new Ip(_ip | wildcard.asLong());
}
@Override
public int hashCode() {
return Long.hashCode(_ip);
}
public Ip inverted() {
long invertedLong = (~_ip) & 0xFFFFFFFFl;
return new Ip(invertedLong);
}
public String networkString(int prefixLength) {
return toString() + "/" + prefixLength;
}
public String networkString(Ip mask) {
return toString() + "/" + mask.numSubnetBits();
}
public int numSubnetBits() {
int numTrailingZeros = Long.numberOfTrailingZeros(_ip);
if (numTrailingZeros > Prefix.MAX_PREFIX_LENGTH) {
return 0;
}
else {
return Prefix.MAX_PREFIX_LENGTH - numTrailingZeros;
}
}
@Override
@JsonValue
public String toString() {
if (!valid()) {
if (_ip == -1l) {
return "AUTO/NONE(-1l)";
}
else {
return "INVALID_IP(" + _ip + "l)";
}
}
else {
return ((_ip >> 24) & 0xFF) + "." + ((_ip >> 16) & 0xFF) + "."
+ ((_ip >> 8) & 0xFF) + "." + (_ip & 0xFF);
}
}
public boolean valid() {
return 0l <= _ip && _ip <= 0xFFFFFFFFl;
}
}