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