Work toward common IP address classes.
Ported the following IP address classes from the older codebase:
Ip4Address, Ip6Address, Ip4Prefix, Ip6Prefix (and the corresponding
unit tests).
NOTE: Those classes are not ready to be used yet.
Change-Id: I234875abbc9df8daa2f8ae28706af591dd2c5f2d
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java b/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
new file mode 100644
index 0000000..1571cf6
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
@@ -0,0 +1,202 @@
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * The class representing an IPv4 address.
+ * This class is immutable.
+ */
+public final class Ip4Address implements Comparable<Ip4Address> {
+ private final int value;
+
+ /** The length of the address in bytes (octets). */
+ public static final int BYTE_LENGTH = 4;
+
+ /** The length of the address in bits. */
+ public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
+
+ /**
+ * Default constructor.
+ */
+ public Ip4Address() {
+ this.value = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from
+ */
+ public Ip4Address(Ip4Address other) {
+ this.value = other.value;
+ }
+
+ /**
+ * Constructor from an integer value.
+ *
+ * @param value the value to use
+ */
+ public Ip4Address(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a byte array with the IPv4 address stored in network
+ * byte order (i.e., the most significant byte first).
+ *
+ * @param value the value to use
+ */
+ public Ip4Address(byte[] value) {
+ this(value, 0);
+ }
+
+ /**
+ * Constructor from a byte array with the IPv4 address stored in network
+ * byte order (i.e., the most significant byte first), and a given offset
+ * from the beginning of the byte array.
+ *
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ */
+ public Ip4Address(byte[] value, int offset) {
+ checkNotNull(value);
+
+ // Verify the arguments
+ if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
+ String msg;
+ if (value.length < BYTE_LENGTH) {
+ msg = "Invalid IPv4 address array: array length: " +
+ value.length + ". Must be at least " + BYTE_LENGTH;
+ } else {
+ msg = "Invalid IPv4 address array: array offset: " +
+ offset + ". Must be in the interval [0, " +
+ (value.length - BYTE_LENGTH) + "]";
+ }
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Read the address
+ ByteBuffer bb = ByteBuffer.wrap(value);
+ this.value = bb.getInt(offset);
+ }
+
+ /**
+ * Constructs an IPv4 address from a string representation of the address.
+ *<p>
+ * Example: "1.2.3.4"
+ *
+ * @param value the value to use
+ */
+ public Ip4Address(String value) {
+ checkNotNull(value);
+
+ String[] splits = value.split("\\.");
+ if (splits.length != 4) {
+ final String msg = "Invalid IPv4 address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+
+ int result = 0;
+ for (int i = 0; i < BYTE_LENGTH; i++) {
+ result |= Integer.parseInt(splits[i]) <<
+ ((BYTE_LENGTH - (i + 1)) * Byte.SIZE);
+ }
+ this.value = result;
+ }
+
+ /**
+ * Gets the IPv4 address as a byte array.
+ *
+ * @return a byte array with the IPv4 address stored in network byte order
+ * (i.e., the most significant byte first).
+ */
+ public byte[] toOctets() {
+ return ByteBuffer.allocate(BYTE_LENGTH).putInt(value).array();
+ }
+
+ /**
+ * Creates an IPv4 network mask prefix.
+ *
+ * @param prefixLen the length of the mask prefix. Must be in the interval
+ * [0, 32].
+ * @return a new IPv4 address that contains a mask prefix of the
+ * specified length
+ */
+ public static Ip4Address makeMaskPrefix(int prefixLen) {
+ // Verify the prefix length
+ if ((prefixLen < 0) || (prefixLen > Ip4Address.BIT_LENGTH)) {
+ final String msg = "Invalid IPv4 prefix length: " + prefixLen +
+ ". Must be in the interval [0, 32].";
+ throw new IllegalArgumentException(msg);
+ }
+
+ long v =
+ (0xffffffffL << (Ip4Address.BIT_LENGTH - prefixLen)) & 0xffffffffL;
+ return new Ip4Address((int) v);
+ }
+
+ /**
+ * Creates an IPv4 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param addr the address to mask
+ * @param prefixLen the length of the mask prefix. Must be in the interval
+ * [0, 32].
+ * @return a new IPv4 address that is masked with a mask prefix of the
+ * specified length
+ */
+ public static Ip4Address makeMaskedAddress(final Ip4Address addr,
+ int prefixLen) {
+ Ip4Address mask = Ip4Address.makeMaskPrefix(prefixLen);
+ long v = addr.value & mask.value;
+
+ return new Ip4Address((int) v);
+ }
+
+ /**
+ * Gets the value of the IPv4 address.
+ *
+ * @return the value of the IPv4 address
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Converts the IPv4 value to a '.' separated string.
+ *
+ * @return the IPv4 value as a '.' separated string
+ */
+ @Override
+ public String toString() {
+ return ((this.value >> 24) & 0xff) + "." +
+ ((this.value >> 16) & 0xff) + "." +
+ ((this.value >> 8) & 0xff) + "." +
+ (this.value & 0xff);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Ip4Address)) {
+ return false;
+ }
+ Ip4Address other = (Ip4Address) o;
+ if (this.value != other.value) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.value;
+ }
+
+ @Override
+ public int compareTo(Ip4Address o) {
+ Long lv = ((long) this.value) & 0xffffffffL;
+ Long rv = ((long) o.value) & 0xffffffffL;
+ return lv.compareTo(rv);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
new file mode 100644
index 0000000..4d0c755
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
@@ -0,0 +1,123 @@
+package org.onlab.packet;
+
+import java.util.Objects;
+
+/**
+ * The class representing an IPv4 network address.
+ * This class is immutable.
+ */
+public final class Ip4Prefix {
+ private final Ip4Address address; // The IPv4 address
+ private final short prefixLen; // The prefix length
+
+ /**
+ * Default constructor.
+ */
+ public Ip4Prefix() {
+ this.address = new Ip4Address();
+ this.prefixLen = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from
+ */
+ public Ip4Prefix(Ip4Prefix other) {
+ this.address = new Ip4Address(other.address);
+ this.prefixLen = other.prefixLen;
+ }
+
+ /**
+ * Constructor for a given address and prefix length.
+ *
+ * @param address the address to use
+ * @param prefixLen the prefix length to use
+ */
+ public Ip4Prefix(Ip4Address address, short prefixLen) {
+ this.address = Ip4Address.makeMaskedAddress(address, prefixLen);
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Constructs an IPv4 prefix from a string representation of the
+ * prefix.
+ *<p>
+ * Example: "1.2.0.0/16"
+ *
+ * @param value the value to use
+ */
+ public Ip4Prefix(String value) {
+ String[] splits = value.split("/");
+ if (splits.length != 2) {
+ throw new IllegalArgumentException("Specified IPv4 prefix must contain an IPv4 " +
+ "address and a prefix length separated by '/'");
+ }
+ this.prefixLen = Short.decode(splits[1]);
+ this.address = Ip4Address.makeMaskedAddress(new Ip4Address(splits[0]),
+ this.prefixLen);
+ }
+
+ /**
+ * Gets the address value of the IPv4 prefix.
+ *
+ * @return the address value of the IPv4 prefix
+ */
+ public Ip4Address getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the prefix length value of the IPv4 prefix.
+ *
+ * @return the prefix length value of the IPv4 prefix
+ */
+ public short getPrefixLen() {
+ return prefixLen;
+ }
+
+ /**
+ * Converts the IPv4 prefix value to an "address/prefixLen" string.
+ *
+ * @return the IPv4 prefix value as an "address/prefixLen" string
+ */
+ @Override
+ public String toString() {
+ return this.address.toString() + "/" + this.prefixLen;
+ }
+
+ /**
+ * Compares the value of two Ip4Prefix objects.
+ * <p/>
+ * Note the value of the IPv4 address is compared directly between the
+ * objects, and must match exactly for the objects to be considered equal.
+ * This may result in objects which represent the same IP prefix being
+ * classified as unequal, because the unsignificant bits of the address
+ * field don't match (the bits to the right of the prefix length).
+ * <p/>
+ * TODO Change this behavior so that objects that represent the same prefix
+ * are classified as equal according to this equals method.
+ *
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Ip4Prefix)) {
+ return false;
+ }
+
+ Ip4Prefix otherIp4Prefix = (Ip4Prefix) other;
+
+ return Objects.equals(this.address, otherIp4Prefix.address)
+ && this.prefixLen == otherIp4Prefix.prefixLen;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLen);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
new file mode 100644
index 0000000..57e893f
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
@@ -0,0 +1,260 @@
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedLongs;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * The class representing an IPv6 address.
+ * This class is immutable.
+ */
+public final class Ip6Address implements Comparable<Ip6Address> {
+ private final long valueHigh; // The higher (more significant) 64 bits
+ private final long valueLow; // The lower (less significant) 64 bits
+
+ /** The length of the address in bytes (octets). */
+ public static final int BYTE_LENGTH = 16;
+
+ /** The length of the address in bits. */
+ public static final int BIT_LENGTH = BYTE_LENGTH * Byte.SIZE;
+
+ /**
+ * Default constructor.
+ */
+ public Ip6Address() {
+ this.valueHigh = 0;
+ this.valueLow = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from
+ */
+ public Ip6Address(Ip6Address other) {
+ this.valueHigh = other.valueHigh;
+ this.valueLow = other.valueLow;
+ }
+
+ /**
+ * Constructor from integer values.
+ *
+ * @param valueHigh the higher (more significant) 64 bits of the address
+ * @param valueLow the lower (less significant) 64 bits of the address
+ */
+ public Ip6Address(long valueHigh, long valueLow) {
+ this.valueHigh = valueHigh;
+ this.valueLow = valueLow;
+ }
+
+ /**
+ * Constructor from a byte array with the IPv6 address stored in network
+ * byte order (i.e., the most significant byte first).
+ *
+ * @param value the value to use
+ */
+ public Ip6Address(byte[] value) {
+ this(value, 0);
+ }
+
+ /**
+ * Constructor from a byte array with the IPv6 address stored in network
+ * byte order (i.e., the most significant byte first), and a given offset
+ * from the beginning of the byte array.
+ *
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ */
+ public Ip6Address(byte[] value, int offset) {
+ checkNotNull(value);
+
+ // Verify the arguments
+ if ((offset < 0) || (offset + BYTE_LENGTH > value.length)) {
+ String msg;
+ if (value.length < BYTE_LENGTH) {
+ msg = "Invalid IPv6 address array: array length: " +
+ value.length + ". Must be at least " + BYTE_LENGTH;
+ } else {
+ msg = "Invalid IPv6 address array: array offset: " +
+ offset + ". Must be in the interval [0, " +
+ (value.length - BYTE_LENGTH) + "]";
+ }
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Read the address
+ ByteBuffer bb = ByteBuffer.wrap(value);
+ bb.position(offset);
+ this.valueHigh = bb.getLong();
+ this.valueLow = bb.getLong();
+ }
+
+ /**
+ * Constructs an IPv6 address from a string representation of the address.
+ *<p>
+ * Example: "1111:2222::8888"
+ *
+ * @param value the value to use
+ */
+ public Ip6Address(String value) {
+ checkNotNull(value);
+
+ if (value.isEmpty()) {
+ final String msg = "Specified IPv6 cannot be an empty string";
+ throw new IllegalArgumentException(msg);
+ }
+ InetAddress addr = null;
+ try {
+ addr = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IPv6 address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ byte[] bytes = addr.getAddress();
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ this.valueHigh = bb.getLong();
+ this.valueLow = bb.getLong();
+ }
+
+ /**
+ * Gets the IPv6 address as a byte array.
+ *
+ * @return a byte array with the IPv6 address stored in network byte order
+ * (i.e., the most significant byte first).
+ */
+ public byte[] toOctets() {
+ return ByteBuffer.allocate(BYTE_LENGTH)
+ .putLong(valueHigh).putLong(valueLow).array();
+ }
+
+ /**
+ * Creates an IPv6 network mask prefix.
+ *
+ * @param prefixLen the length of the mask prefix. Must be in the interval
+ * [0, 128].
+ * @return a new IPv6 address that contains a mask prefix of the
+ * specified length
+ */
+ public static Ip6Address makeMaskPrefix(int prefixLen) {
+ long vh, vl;
+
+ // Verify the prefix length
+ if ((prefixLen < 0) || (prefixLen > Ip6Address.BIT_LENGTH)) {
+ final String msg = "Invalid IPv6 prefix length: " + prefixLen +
+ ". Must be in the interval [0, 128].";
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (prefixLen == 0) {
+ //
+ // NOTE: Apparently, the result of "<< 64" shifting to the left
+ // results in all 1s instead of all 0s, hence we handle it as
+ // a special case.
+ //
+ vh = 0;
+ vl = 0;
+ } else if (prefixLen <= 64) {
+ vh = (0xffffffffffffffffL << (64 - prefixLen)) & 0xffffffffffffffffL;
+ vl = 0;
+ } else {
+ vh = -1L; // All 1s
+ vl = (0xffffffffffffffffL << (128 - prefixLen)) & 0xffffffffffffffffL;
+ }
+ return new Ip6Address(vh, vl);
+ }
+
+ /**
+ * Creates an IPv6 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param addr the address to mask
+ * @param prefixLen the length of the mask prefix. Must be in the interval
+ * [0, 128].
+ * @return a new IPv6 address that is masked with a mask prefix of the
+ * specified length
+ */
+ public static Ip6Address makeMaskedAddress(final Ip6Address addr,
+ int prefixLen) {
+ Ip6Address mask = Ip6Address.makeMaskPrefix(prefixLen);
+ long vh = addr.valueHigh & mask.valueHigh;
+ long vl = addr.valueLow & mask.valueLow;
+
+ return new Ip6Address(vh, vl);
+ }
+
+ /**
+ * Gets the value of the higher (more significant) 64 bits of the address.
+ *
+ * @return the value of the higher (more significant) 64 bits of the
+ * address
+ */
+ public long getValueHigh() {
+ return valueHigh;
+ }
+
+ /**
+ * Gets the value of the lower (less significant) 64 bits of the address.
+ *
+ * @return the value of the lower (less significant) 64 bits of the
+ * address
+ */
+ public long getValueLow() {
+ return valueLow;
+ }
+
+ /**
+ * Converts the IPv6 value to a ':' separated string.
+ *
+ * @return the IPv6 value as a ':' separated string
+ */
+ @Override
+ public String toString() {
+ ByteBuffer bb = ByteBuffer.allocate(Ip6Address.BYTE_LENGTH);
+ bb.putLong(valueHigh);
+ bb.putLong(valueLow);
+ InetAddress inetAddr = null;
+ try {
+ inetAddr = InetAddress.getByAddress(bb.array());
+ } catch (UnknownHostException e) {
+ // Should never happen
+ checkState(false, "Internal error: Ip6Address.toString()");
+ return "::";
+ }
+ return InetAddresses.toAddrString(inetAddr);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Ip6Address)) {
+ return false;
+ }
+ Ip6Address other = (Ip6Address) o;
+ return this.valueHigh == other.valueHigh
+ && this.valueLow == other.valueLow;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(valueHigh, valueLow);
+ }
+
+ @Override
+ public int compareTo(Ip6Address o) {
+ // Compare the high-order 64-bit value
+ if (this.valueHigh != o.valueHigh) {
+ return UnsignedLongs.compare(this.valueHigh, o.valueHigh);
+ }
+ // Compare the low-order 64-bit value
+ if (this.valueLow != o.valueLow) {
+ return UnsignedLongs.compare(this.valueLow, o.valueLow);
+ }
+ return 0;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
new file mode 100644
index 0000000..d38f505
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
@@ -0,0 +1,123 @@
+package org.onlab.packet;
+
+import java.util.Objects;
+
+/**
+ * The class representing an IPv6 network address.
+ * This class is immutable.
+ */
+public final class Ip6Prefix {
+ private final Ip6Address address; // The IPv6 address
+ private final short prefixLen; // The prefix length
+
+ /**
+ * Default constructor.
+ */
+ public Ip6Prefix() {
+ this.address = new Ip6Address();
+ this.prefixLen = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from
+ */
+ public Ip6Prefix(Ip6Prefix other) {
+ this.address = new Ip6Address(other.address);
+ this.prefixLen = other.prefixLen;
+ }
+
+ /**
+ * Constructor for a given address and prefix length.
+ *
+ * @param address the address to use
+ * @param prefixLen the prefix length to use
+ */
+ public Ip6Prefix(Ip6Address address, short prefixLen) {
+ this.address = Ip6Address.makeMaskedAddress(address, prefixLen);
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Constructs an IPv6 prefix from a string representation of the
+ * prefix.
+ *<p>
+ * Example: "1111:2222::/32"
+ *
+ * @param value the value to use
+ */
+ public Ip6Prefix(String value) {
+ String[] splits = value.split("/");
+ if (splits.length != 2) {
+ throw new IllegalArgumentException("Specified IPv6 prefix must contain an IPv6 " +
+ "address and a prefix length separated by '/'");
+ }
+ this.prefixLen = Short.decode(splits[1]);
+ this.address = Ip6Address.makeMaskedAddress(new Ip6Address(splits[0]),
+ this.prefixLen);
+ }
+
+ /**
+ * Gets the address value of the IPv6 prefix.
+ *
+ * @return the address value of the IPv6 prefix
+ */
+ public Ip6Address getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the prefix length value of the IPv6 prefix.
+ *
+ * @return the prefix length value of the IPv6 prefix
+ */
+ public short getPrefixLen() {
+ return prefixLen;
+ }
+
+ /**
+ * Converts the IPv6 prefix value to an "address/prefixLen" string.
+ *
+ * @return the IPv6 prefix value as an "address/prefixLen" string
+ */
+ @Override
+ public String toString() {
+ return this.address.toString() + "/" + this.prefixLen;
+ }
+
+ /**
+ * Compares the value of two Ip6Prefix objects.
+ * <p/>
+ * Note the value of the IPv6 address is compared directly between the
+ * objects, and must match exactly for the objects to be considered equal.
+ * This may result in objects which represent the same IP prefix being
+ * classified as unequal, because the unsignificant bits of the address
+ * field don't match (the bits to the right of the prefix length).
+ * <p/>
+ * TODO Change this behavior so that objects that represent the same prefix
+ * are classified as equal according to this equals method.
+ *
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Ip6Prefix)) {
+ return false;
+ }
+
+ Ip6Prefix otherIp6Prefix = (Ip6Prefix) other;
+
+ return Objects.equals(this.address, otherIp6Prefix.address)
+ && this.prefixLen == otherIp6Prefix.prefixLen;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLen);
+ }
+}