Added support for IPv6 addresses to class IpAddress:

 - For some of the methods, the IP Version needs to be specified by the
   caller. This applies to the IpPrefix API as well.
 - For now, everywhere IpAddress is used and the IP version has to be
   explicitly specified by the caller, we assume/specify IPv4.
 - Added unit test for class IpAddress: for both IPv4 and IPv6 addresses.
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
index af67f1a..901ac90 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
@@ -246,11 +246,13 @@
         InetAddress inetAddr;
         if (localAddress instanceof InetSocketAddress) {
             inetAddr = ((InetSocketAddress) localAddress).getAddress();
-            localIp4Address = IpAddress.valueOf(inetAddr.getAddress());
+            localIp4Address = IpAddress.valueOf(IpAddress.Version.INET,
+                                                inetAddr.getAddress());
         }
         if (remoteAddress instanceof InetSocketAddress) {
             inetAddr = ((InetSocketAddress) remoteAddress).getAddress();
-            remoteIp4Address = IpAddress.valueOf(inetAddr.getAddress());
+            remoteIp4Address = IpAddress.valueOf(IpAddress.Version.INET,
+                                                 inetAddr.getAddress());
         }
 
         log.debug("BGP Session Connected from {} on {}",
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java
index 1b5dfb5..247c95f 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java
@@ -105,7 +105,8 @@
         if (bgpSession.getLocalAddress() instanceof InetSocketAddress) {
             InetAddress inetAddr =
                 ((InetSocketAddress) bgpSession.getLocalAddress()).getAddress();
-            IpAddress ip4Address = IpAddress.valueOf(inetAddr.getAddress());
+            IpAddress ip4Address = IpAddress.valueOf(IpAddress.Version.INET,
+                                                     inetAddr.getAddress());
             updateMyBgpId(ip4Address);
         }
         return true;
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 953e88a..16e3798 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -132,7 +132,8 @@
         // for one of our external addresses.
         if (isOutsidePort(inPort)) {
             IpAddress target =
-                IpAddress.valueOf(arp.getTargetProtocolAddress());
+                IpAddress.valueOf(IpAddress.Version.INET,
+                                  arp.getTargetProtocolAddress());
             PortAddresses addresses =
                 hostService.getAddressBindingsForPort(inPort);
 
@@ -149,7 +150,8 @@
             // it could be a request from an internal host to an external
             // address. Forward it over to the correct port.
             IpAddress source =
-                IpAddress.valueOf(arp.getSenderProtocolAddress());
+                IpAddress.valueOf(IpAddress.Version.INET,
+                                  arp.getSenderProtocolAddress());
             PortAddresses sourceAddresses = findPortInSubnet(source);
             if (sourceAddresses != null) {
                 for (InterfaceIpAddress ia : sourceAddresses.ipAddresses()) {
@@ -164,8 +166,9 @@
         // Continue with normal proxy ARP case
 
         VlanId vlan = VlanId.vlanId(eth.getVlanID());
-        Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(arp
-                .getTargetProtocolAddress()));
+        Set<Host> hosts =
+            hostService.getHostsByIp(IpAddress.valueOf(IpAddress.Version.INET,
+                                        arp.getTargetProtocolAddress()));
 
         Host dst = null;
         Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
index e3822ad..fd85f65 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
@@ -148,7 +148,8 @@
 
     private IpAddress memberAddress(Member member) {
         byte[] address = member.getSocketAddress().getAddress().getAddress();
-        return IpAddress.valueOf(address);
+        // TODO: Add support for IPv6
+        return IpAddress.valueOf(IpAddress.Version.INET, address);
     }
 
     // Interceptor for membership events.
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
index 686adb2..ae60793 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
@@ -46,7 +46,8 @@
         final int octLen = input.readInt();
         byte[] octs = new byte[octLen];
         input.readBytes(octs);
-        return IpAddress.valueOf(octs);
+        // TODO: Add support for reading/writing the IP version
+        return IpAddress.valueOf(IpAddress.Version.INET, octs);
     }
 
 }
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
index 004f75f..5d895d7 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
@@ -15,6 +15,7 @@
  */
 package org.onlab.onos.store.serializers;
 
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 
 import com.esotericsoftware.kryo.Kryo;
@@ -51,6 +52,7 @@
         byte[] octs = new byte[octLen];
         input.readBytes(octs);
         int prefLen = input.readInt();
-        return IpPrefix.valueOf(octs, prefLen);
+        // TODO: Add support for reading/writing the IP version
+        return IpPrefix.valueOf(IpAddress.Version.INET, octs, prefLen);
     }
 }
diff --git a/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
index 36d67b2..48762bb 100644
--- a/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
@@ -120,7 +120,8 @@
             if (eth.getEtherType() == Ethernet.TYPE_ARP) {
                 ARP arp = (ARP) eth.getPayload();
                 IpAddress ip =
-                    IpAddress.valueOf(arp.getSenderProtocolAddress());
+                    IpAddress.valueOf(IpAddress.Version.INET,
+                                      arp.getSenderProtocolAddress());
                 HostDescription hdescr =
                         new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
                 providerService.hostDetected(hid, hdescr);
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
index 749b4a9..b191784 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
@@ -145,7 +145,8 @@
                 ip = (IPCriterion) c;
                 if (ip.ip().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH) {
                     IpAddress maskAddr =
-                        IpAddress.makeMaskPrefix(ip.ip().prefixLength());
+                        IpAddress.makeMaskPrefix(ip.ip().address().version(),
+                                                 ip.ip().prefixLength());
                     Masked<IPv4Address> maskedIp =
                         Masked.of(IPv4Address.of(ip.ip().address().toInt()),
                                   IPv4Address.of(maskAddr.toInt()));
@@ -159,7 +160,8 @@
                 ip = (IPCriterion) c;
                 if (ip.ip().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH) {
                     IpAddress maskAddr =
-                        IpAddress.makeMaskPrefix(ip.ip().prefixLength());
+                        IpAddress.makeMaskPrefix(ip.ip().address().version(),
+                                                 ip.ip().prefixLength());
                     Masked<IPv4Address> maskedIp =
                         Masked.of(IPv4Address.of(ip.ip().address().toInt()),
                                   IPv4Address.of(maskAddr.toInt()));
diff --git a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
index e563c34..3c08a2f 100644
--- a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
+++ b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
@@ -126,7 +126,8 @@
             if (eth.getEtherType() == Ethernet.TYPE_ARP) {
                 ARP arp = (ARP) eth.getPayload();
                 IpAddress ip =
-                    IpAddress.valueOf(arp.getSenderProtocolAddress());
+                    IpAddress.valueOf(IpAddress.Version.INET,
+                                      arp.getSenderProtocolAddress());
                 HostDescription hdescr =
                         new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
                 providerService.hostDetected(hid, hdescr);
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index 33be226..3f5931f 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -15,10 +15,18 @@
  */
 package org.onlab.packet;
 
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Objects;
-import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedBytes;
+
+import static com.google.common.base.Preconditions.checkState;
 
 /**
  * A class representing an IP address.
@@ -40,87 +48,26 @@
     /**
      * Constructor for given IP address version and address octets.
      *
+     * @param version the IP address version
      * @param value the IP address value stored in network byte order
      * (i.e., the most significant byte first)
-     * @param value the IP address value
+     * @throws IllegalArgumentException if the arguments are invalid
      */
     private IpAddress(Version version, byte[] value) {
+        checkArguments(version, value, 0);
         this.version = version;
-        this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
-    }
-
-    /**
-     * Converts an integer into an IPv4 address.
-     *
-     * @param value an integer representing an IPv4 address value
-     * @return an IP address
-     */
-    public static IpAddress valueOf(int value) {
-        byte[] bytes =
-            ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
-        return new IpAddress(Version.INET, bytes);
-    }
-
-    /**
-     * Converts a byte array into an IP address.
-     *
-     * @param value the IP address value stored in network byte order
-     * (i.e., the most significant byte first)
-     * @return an IP address
-     */
-    public static IpAddress valueOf(byte[] value) {
-        checkNotNull(value);
-        return new IpAddress(Version.INET, value);
-    }
-
-    /**
-     * Converts a byte array and a given offset from the beginning of the
-     * array into an IP address.
-     * <p>
-     * The IP address is stored in network byte order (i.e., the most
-     * significant byte first).
-     * </p>
-     * @param value the value to use
-     * @param offset the offset in bytes from the beginning of the byte array
-     * @return an IP address
-     */
-    public static IpAddress valueOf(byte[] value, int offset) {
-        // Verify the arguments
-        if ((offset < 0) || (offset + INET_BYTE_LENGTH > value.length)) {
-            String msg;
-            if (value.length < INET_BYTE_LENGTH) {
-                msg = "Invalid IPv4 address array: array length: " +
-                    value.length + ". Must be at least " + INET_BYTE_LENGTH;
-            } else {
-                msg = "Invalid IPv4 address array: array offset: " +
-                    offset + ". Must be in the interval [0, " +
-                    (value.length - INET_BYTE_LENGTH) + "]";
-            }
-            throw new IllegalArgumentException(msg);
+        switch (version) {
+        case INET:
+            this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
+            break;
+        case INET6:
+            this.octets = Arrays.copyOf(value, INET6_BYTE_LENGTH);
+            break;
+        default:
+            // Should not be reached
+            this.octets = null;
+            break;
         }
-
-        byte[] bc = Arrays.copyOfRange(value, offset, value.length);
-        return IpAddress.valueOf(bc);
-    }
-
-    /**
-     * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address.
-     *
-     * @param address an IP address in string form, e.g. "10.0.0.1"
-     * @return an IP address
-     */
-    public static IpAddress valueOf(String address) {
-        final String[] net = address.split("\\.");
-        if (net.length != INET_BYTE_LENGTH) {
-            String msg = "Malformed IPv4 address string: " + address + "." +
-                "Address must have four decimal values separated by dots (.)";
-            throw new IllegalArgumentException(msg);
-        }
-        final byte[] bytes = new byte[INET_BYTE_LENGTH];
-        for (int i = 0; i < INET_BYTE_LENGTH; i++) {
-            bytes[i] = (byte) Short.parseShort(net[i], 10);
-        }
-        return new IpAddress(Version.INET, bytes);
     }
 
     /**
@@ -138,11 +85,12 @@
      * @return a byte array
      */
     public byte[] toOctets() {
-        return Arrays.copyOf(this.octets, INET_BYTE_LENGTH);
+        return Arrays.copyOf(octets, octets.length);
     }
 
     /**
      * Returns the integral value of this IP address.
+     * TODO: This method should be moved to Ip4Address.
      *
      * @return the IP address's value as an integer
      */
@@ -152,24 +100,133 @@
     }
 
     /**
+     * Computes the IP address byte length for a given IP version.
+     *
+     * @param version the IP version
+     * @return the IP address byte length for the IP version
+     * @throws IllegalArgumentException if the IP version is invalid
+     */
+    public static int byteLength(Version version) {
+        switch (version) {
+        case INET:
+            return INET_BYTE_LENGTH;
+        case INET6:
+            return INET6_BYTE_LENGTH;
+        default:
+            String msg = "Invalid IP version " + version;
+            throw new IllegalArgumentException(msg);
+        }
+    }
+
+    /**
+     * Converts an integer into an IPv4 address.
+     *
+     * @param value an integer representing an IPv4 address value
+     * @return an IP address
+     */
+    public static IpAddress valueOf(int value) {
+        byte[] bytes =
+            ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+        return new IpAddress(Version.INET, bytes);
+    }
+
+    /**
+     * Converts a byte array into an IP address.
+     *
+     * @param version the IP address version
+     * @param value the IP address value stored in network byte order
+     * (i.e., the most significant byte first)
+     * @return an IP address
+     * @throws IllegalArgumentException if the arguments are invalid
+     */
+    public static IpAddress valueOf(Version version, byte[] value) {
+        return new IpAddress(version, value);
+    }
+
+    /**
+     * Converts a byte array and a given offset from the beginning of the
+     * array into an IP address.
+     * <p>
+     * The IP address is stored in network byte order (i.e., the most
+     * significant byte first).
+     * </p>
+     * @param version the IP address version
+     * @param value the value to use
+     * @param offset the offset in bytes from the beginning of the byte array
+     * @return an IP address
+     * @throws IllegalArgumentException if the arguments are invalid
+     */
+    public static IpAddress valueOf(Version version, byte[] value,
+                                    int offset) {
+        checkArguments(version, value, offset);
+        byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+        return IpAddress.valueOf(version, bc);
+    }
+
+    /**
+     * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
+     * "1111:2222::8888") into an IP address.
+     *
+     * @param value an IP address value in string form
+     * @return an IP address
+     * @throws IllegalArgumentException if the argument is invalid
+     */
+    public static IpAddress valueOf(String value) {
+        InetAddress addr = null;
+        try {
+            addr = InetAddresses.forString(value);
+        } catch (IllegalArgumentException e) {
+            final String msg = "Invalid IP address string: " + value;
+            throw new IllegalArgumentException(msg);
+        }
+        byte[] bytes = addr.getAddress();
+        if (addr instanceof Inet4Address) {
+            return new IpAddress(Version.INET, bytes);
+        }
+        if (addr instanceof Inet6Address) {
+            return new IpAddress(Version.INET6, bytes);
+        }
+        final String msg = "Unrecognized IP version address string: " + value;
+        throw new IllegalArgumentException(msg);
+    }
+
+    /**
      * Creates an IP network mask prefix.
      *
+     * @param version the IP address version
      * @param prefixLength the length of the mask prefix. Must be in the
-     * interval [0, 32] for IPv4
+     * interval [0, 32] for IPv4, or [0, 128] for IPv6
      * @return a new IP address that contains a mask prefix of the
      * specified length
+     * @throws IllegalArgumentException if the arguments are invalid
      */
-    public static IpAddress makeMaskPrefix(int prefixLength) {
+    public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
+        int addrByteLength = byteLength(version);
+        int addrBitLength = addrByteLength * Byte.SIZE;
+
         // Verify the prefix length
-        if ((prefixLength < 0) || (prefixLength > INET_BIT_LENGTH)) {
-            final String msg = "Invalid IPv4 prefix length: " + prefixLength +
-                ". Must be in the interval [0, 32].";
+        if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
+            final String msg = "Invalid IP prefix length: " + prefixLength +
+                ". Must be in the interval [0, " + addrBitLength + "].";
             throw new IllegalArgumentException(msg);
         }
 
-        long v =
-            (0xffffffffL << (INET_BIT_LENGTH - prefixLength)) & 0xffffffffL;
-        return IpAddress.valueOf((int) v);
+        // Number of bytes and extra bits that should be all 1s
+        int maskBytes = prefixLength / Byte.SIZE;
+        int maskBits = prefixLength % Byte.SIZE;
+        byte[] mask = new byte[addrByteLength];
+
+        // Set the bytes and extra bits to 1s
+        for (int i = 0; i < maskBytes; i++) {
+            mask[i] = (byte) 0xff;              // Set mask bytes to 1s
+        }
+        for (int i = maskBytes; i < addrByteLength; i++) {
+            mask[i] = 0;                        // Set remaining bytes to 0s
+        }
+        if (maskBits > 0) {
+            mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
+        }
+        return new IpAddress(version, mask);
     }
 
     /**
@@ -178,27 +235,38 @@
      *
      * @param addr the address to mask
      * @param prefixLength the length of the mask prefix. Must be in the
-     * interval [0, 32] for IPv4
+     * interval [0, 32] for IPv4, or [0, 128] for IPv6
      * @return a new IP address that is masked with a mask prefix of the
      * specified length
+     * @throws IllegalArgumentException if the prefix length is invalid
      */
     public static IpAddress makeMaskedAddress(final IpAddress addr,
                                               int prefixLength) {
-        IpAddress mask = IpAddress.makeMaskPrefix(prefixLength);
-        byte[] net = new byte[INET_BYTE_LENGTH];
+        IpAddress mask = IpAddress.makeMaskPrefix(addr.version(),
+                                                  prefixLength);
+        byte[] net = new byte[mask.octets.length];
 
         // Mask each byte
-        for (int i = 0; i < INET_BYTE_LENGTH; i++) {
+        for (int i = 0; i < net.length; i++) {
             net[i] = (byte) (addr.octets[i] & mask.octets[i]);
         }
-        return IpAddress.valueOf(net);
+        return IpAddress.valueOf(addr.version(), net);
     }
 
     @Override
     public int compareTo(IpAddress o) {
-        Long lv = ((long) this.toInt()) & 0xffffffffL;
-        Long rv = ((long) o.toInt()) & 0xffffffffL;
-        return lv.compareTo(rv);
+        // Compare first the version
+        if (this.version != o.version) {
+            return this.version.compareTo(o.version);
+        }
+
+        // Compare the bytes, one-by-one
+        for (int i = 0; i < this.octets.length; i++) {
+            if (this.octets[i] != o.octets[i]) {
+                return UnsignedBytes.compare(this.octets[i], o.octets[i]);
+            }
+        }
+        return 0;       // Equal
     }
 
     @Override
@@ -222,18 +290,67 @@
     @Override
     /*
      * (non-Javadoc)
-     * The format is "x.x.x.x" for IPv4 addresses.
+     * The string representation of the IP address: "x.x.x.x" for IPv4
+     * addresses, or ':' separated string for IPv6 addresses.
      *
      * @see java.lang.Object#toString()
      */
     public String toString() {
-        final StringBuilder builder = new StringBuilder();
-        for (final byte b : this.octets) {
-            if (builder.length() > 0) {
-                builder.append(".");
-            }
-            builder.append(String.format("%d", b & 0xff));
+        InetAddress inetAddr = null;
+        try {
+            inetAddr = InetAddress.getByAddress(octets);
+        } catch (UnknownHostException e) {
+            // Should never happen
+            checkState(false, "Internal error: Ip6Address.toString()");
+            return "[Invalid IP Address]";
         }
-        return builder.toString();
+        return InetAddresses.toAddrString(inetAddr);
+    }
+
+    /**
+     * Gets the IP address name for the IP address version.
+     *
+     * @param version the IP address version
+     * @return the IP address name for the IP address version
+     */
+    private static String addressName(Version version) {
+        switch (version) {
+        case INET:
+            return "IPv4";
+        case INET6:
+            return "IPv6";
+        default:
+            break;
+        }
+        return "UnknownIP(" + version + ")";
+    }
+
+    /**
+     * Checks whether the arguments are valid.
+     *
+     * @param version the IP address version
+     * @param value the IP address value stored in a byte array
+     * @param offset the offset in bytes from the beginning of the byte
+     * array with the address
+     * @throws IllegalArgumentException if any of the arguments is invalid
+     */
+    private static void checkArguments(Version version, byte[] value,
+                                       int offset) {
+        // Check the offset and byte array length
+        int addrByteLength = byteLength(version);
+        if ((offset < 0) || (offset + addrByteLength > value.length)) {
+            String msg;
+            if (value.length < addrByteLength) {
+                msg = "Invalid " + addressName(version) +
+                    " address array: array length: " + value.length +
+                    ". Must be at least " + addrByteLength;
+            } else {
+                msg = "Invalid " + addressName(version) +
+                    " address array: array offset: " + offset +
+                    ". Must be in the interval [0, " +
+                    (value.length - addrByteLength) + "]";
+            }
+            throw new IllegalArgumentException(msg);
+        }
     }
 }
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
index 180b771..3cde79d 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -76,12 +76,15 @@
     /**
      * Converts a byte array and a prefix length into an IP prefix.
      *
+     * @param version the IP address version
      * @param address the IP address value stored in network byte order
      * @param prefixLength the prefix length
      * @return an IP prefix
      */
-    public static IpPrefix valueOf(byte[] address, int prefixLength) {
-        return new IpPrefix(IpAddress.valueOf(address), prefixLength);
+    public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
+                                   int prefixLength) {
+        return new IpPrefix(IpAddress.valueOf(version, address),
+                            prefixLength);
     }
 
     /**
diff --git a/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java b/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java
new file mode 100644
index 0000000..5e01aa1
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/IpAddressTest.java
@@ -0,0 +1,795 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Tests for class {@link IpAddress}.
+ */
+public class IpAddressTest {
+    /**
+     * Tests the immutability of {@link IpAddress}.
+     */
+    @Test
+    public void testImmutable() {
+        assertThatClassIsImmutable(IpAddress.class);
+    }
+
+    /**
+     * Tests the length of the address in bytes (octets).
+     */
+    @Test
+    public void testAddrByteLength() {
+        assertThat(IpAddress.INET_BYTE_LENGTH, is(4));
+        assertThat(IpAddress.INET6_BYTE_LENGTH, is(16));
+        assertThat(IpAddress.byteLength(IpAddress.Version.INET), is(4));
+        assertThat(IpAddress.byteLength(IpAddress.Version.INET6), is(16));
+    }
+
+    /**
+     * Tests the length of the address in bits.
+     */
+    @Test
+    public void testAddrBitLength() {
+        assertThat(IpAddress.INET_BIT_LENGTH, is(32));
+        assertThat(IpAddress.INET6_BIT_LENGTH, is(128));
+    }
+
+    /**
+     * Tests returning the IP address version.
+     */
+    @Test
+    public void testVersion() {
+        IpAddress ipAddress;
+
+        // IPv4
+        ipAddress = IpAddress.valueOf("0.0.0.0");
+        assertThat(ipAddress.version(), is(IpAddress.Version.INET));
+
+        // IPv6
+        ipAddress = IpAddress.valueOf("::");
+        assertThat(ipAddress.version(), is(IpAddress.Version.INET6));
+    }
+
+    /**
+     * Tests returning an IPv4 address as a byte array.
+     */
+    @Test
+    public void testAddressToOctetsIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {1, 2, 3, 4};
+        ipAddress = IpAddress.valueOf("1.2.3.4");
+        assertThat(ipAddress.toOctets(), is(value1));
+
+        final byte[] value2 = new byte[] {0, 0, 0, 0};
+        ipAddress = IpAddress.valueOf("0.0.0.0");
+        assertThat(ipAddress.toOctets(), is(value2));
+
+        final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff};
+        ipAddress = IpAddress.valueOf("255.255.255.255");
+        assertThat(ipAddress.toOctets(), is(value3));
+    }
+
+    /**
+     * Tests returning an IPv6 address as a byte array.
+     */
+    @Test
+    public void testAddressToOctetsIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
+                                          0x33, 0x33, 0x44, 0x44,
+                                          0x55, 0x55, 0x66, 0x66,
+                                          0x77, 0x77,
+                                          (byte) 0x88, (byte) 0x88};
+        ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        assertThat(ipAddress.toOctets(), is(value1));
+
+        final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00};
+        ipAddress = IpAddress.valueOf("::");
+        assertThat(ipAddress.toOctets(), is(value2));
+
+        final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff};
+        ipAddress =
+            IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        assertThat(ipAddress.toOctets(), is(value3));
+    }
+
+    /**
+     * Tests returning an IPv4 address asn an integer.
+     */
+    @Test
+    public void testToint() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.valueOf("1.2.3.4");
+        assertThat(ipAddress.toInt(), is(0x01020304));
+
+        ipAddress = IpAddress.valueOf("0.0.0.0");
+        assertThat(ipAddress.toInt(), is(0));
+
+        ipAddress = IpAddress.valueOf("255.255.255.255");
+        assertThat(ipAddress.toInt(), is(-1));
+    }
+
+    /**
+     * Tests valueOf() converter for an integer value.
+     */
+    @Test
+    public void testValueOfForInteger() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.valueOf(0x01020304);
+        assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+        ipAddress = IpAddress.valueOf(0);
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        ipAddress = IpAddress.valueOf(0xffffffff);
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests valueOf() converter for IPv4 byte array.
+     */
+    @Test
+    public void testValueOfByteArrayIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {1, 2, 3, 4};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1);
+        assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+        final byte[] value2 = new byte[] {0, 0, 0, 0};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value2);
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value3);
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests valueOf() converter for IPv6 byte array.
+     */
+    @Test
+    public void testValueOfByteArrayIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {0x11, 0x11, 0x22, 0x22,
+                                          0x33, 0x33, 0x44, 0x44,
+                                          0x55, 0x55, 0x66, 0x66,
+                                          0x77, 0x77,
+                                          (byte) 0x88, (byte) 0x88};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1);
+        assertThat(ipAddress.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+        final byte[] value2 = new byte[] {0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value2);
+        assertThat(ipAddress.toString(), is("::"));
+
+        final byte[] value3 = new byte[] {(byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value3);
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+    }
+
+    /**
+     * Tests invalid valueOf() converter for a null array for IPv4.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testInvalidValueOfNullArrayIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] fromArray = null;
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, fromArray);
+    }
+
+    /**
+     * Tests invalid valueOf() converter for a null array for IPv6.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testInvalidValueOfNullArrayIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] fromArray = null;
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, fromArray);
+    }
+
+    /**
+     * Tests invalid valueOf() converger for an array that is too short for
+     * IPv4.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfShortArrayIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] fromArray = new byte[] {1, 2, 3};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, fromArray);
+    }
+
+    /**
+     * Tests invalid valueOf() converger for an array that is too short for
+     * IPv6.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfShortArrayIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] fromArray = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, fromArray);
+    }
+
+    /**
+     * Tests valueOf() converter for IPv4 byte array and an offset.
+     */
+    @Test
+    public void testValueOfByteArrayOffsetIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {11, 22, 33,   // Preamble
+                                          1, 2, 3, 4,
+                                          44, 55};      // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1, 3);
+        assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+        final byte[] value2 = new byte[] {11, 22,       // Preamble
+                                          0, 0, 0, 0,
+                                          33};          // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value2, 2);
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        final byte[] value3 = new byte[] {11, 22,       // Preamble
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          33};          // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value3, 2);
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests valueOf() converter for IPv6 byte array and an offset.
+     */
+    @Test
+    public void testValueOfByteArrayOffsetIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {11, 22, 33,           // Preamble
+                                          0x11, 0x11, 0x22, 0x22,
+                                          0x33, 0x33, 0x44, 0x44,
+                                          0x55, 0x55, 0x66, 0x66,
+                                          0x77, 0x77,
+                                          (byte) 0x88, (byte) 0x88,
+                                          44, 55};              // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1, 3);
+        assertThat(ipAddress.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+        final byte[] value2 = new byte[] {11, 22,               // Preamble
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          0x00, 0x00, 0x00, 0x00,
+                                          33};                  // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value2, 2);
+        assertThat(ipAddress.toString(), is("::"));
+
+        final byte[] value3 = new byte[] {11, 22,               // Preamble
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          (byte) 0xff, (byte) 0xff,
+                                          33};                  // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value3, 2);
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+    }
+
+    /**
+     * Tests invalid valueOf() converger for an array and an invalid offset
+     * for IPv4.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfArrayInvalidOffsetIPv4() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {11, 22, 33,   // Preamble
+                                          1, 2, 3, 4,
+                                          44, 55};      // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET, value1, 6);
+    }
+
+    /**
+     * Tests invalid valueOf() converger for an array and an invalid offset
+     * for IPv6.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfArrayInvalidOffsetIPv6() {
+        IpAddress ipAddress;
+
+        final byte[] value1 = new byte[] {11, 22, 33,           // Preamble
+                                          0x11, 0x11, 0x22, 0x22,
+                                          0x33, 0x33, 0x44, 0x44,
+                                          0x55, 0x55, 0x66, 0x66,
+                                          0x77, 0x77,
+                                          (byte) 0x88, (byte) 0x88,
+                                          44, 55};              // Extra bytes
+        ipAddress = IpAddress.valueOf(IpAddress.Version.INET6, value1, 6);
+    }
+
+    /**
+     * Tests valueOf() converter for IPv4 string.
+     */
+    @Test
+    public void testValueOfStringIPv4() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.valueOf("1.2.3.4");
+        assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+        ipAddress = IpAddress.valueOf("0.0.0.0");
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        ipAddress = IpAddress.valueOf("255.255.255.255");
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests valueOf() converter for IPv6 string.
+     */
+    @Test
+    public void testValueOfStringIPv6() {
+        IpAddress ipAddress;
+
+        ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        assertThat(ipAddress.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+        ipAddress = IpAddress.valueOf("::");
+        assertThat(ipAddress.toString(), is("::"));
+
+        ipAddress =
+            IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+    }
+
+    /**
+     * Tests invalid valueOf() converter for a null string.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testInvalidValueOfNullString() {
+        IpAddress ipAddress;
+
+        String fromString = null;
+        ipAddress = IpAddress.valueOf(fromString);
+    }
+
+    /**
+     * Tests invalid valueOf() converter for an empty string.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfEmptyString() {
+        IpAddress ipAddress;
+
+        String fromString = "";
+        ipAddress = IpAddress.valueOf(fromString);
+    }
+
+    /**
+     * Tests invalid valueOf() converter for an incorrect string.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidValueOfIncorrectString() {
+        IpAddress ipAddress;
+
+        String fromString = "NoSuchIpAddress";
+        ipAddress = IpAddress.valueOf(fromString);
+    }
+
+    /**
+     * Tests making a mask prefix for a given prefix length for IPv4.
+     */
+    @Test
+    public void testMakeMaskPrefixIPv4() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET, 25);
+        assertThat(ipAddress.toString(), is("255.255.255.128"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET, 0);
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET, 32);
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests making a mask prefix for a given prefix length for IPv6.
+     */
+    @Test
+    public void testMakeMaskPrefixIPv6() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 8);
+        assertThat(ipAddress.toString(), is("ff00::"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 120);
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 0);
+        assertThat(ipAddress.toString(), is("::"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 128);
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 64);
+        assertThat(ipAddress.toString(), is("ffff:ffff:ffff:ffff::"));
+    }
+
+    /**
+     * Tests making a mask prefix for an invalid prefix length for IPv4:
+     * negative prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeNegativeMaskPrefixIPv4() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET, -1);
+    }
+
+    /**
+     * Tests making a mask prefix for an invalid prefix length for IPv6:
+     * negative prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeNegativeMaskPrefixIPv6() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, -1);
+    }
+
+    /**
+     * Tests making a mask prefix for an invalid prefix length for IPv4:
+     * too long prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeTooLongMaskPrefixIPv4() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET, 33);
+    }
+
+    /**
+     * Tests making a mask prefix for an invalid prefix length for IPv6:
+     * too long prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeTooLongMaskPrefixIPv6() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.makeMaskPrefix(IpAddress.Version.INET6, 129);
+    }
+
+    /**
+     * Tests making of a masked address for IPv4.
+     */
+    @Test
+    public void testMakeMaskedAddressIPv4() {
+        IpAddress ipAddress = IpAddress.valueOf("1.2.3.5");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 24);
+        assertThat(ipAddressMasked.toString(), is("1.2.3.0"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 0);
+        assertThat(ipAddressMasked.toString(), is("0.0.0.0"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 32);
+        assertThat(ipAddressMasked.toString(), is("1.2.3.5"));
+    }
+
+    /**
+     * Tests making of a masked address for IPv6.
+     */
+    @Test
+    public void testMakeMaskedAddressIPv6() {
+        IpAddress ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 8);
+        assertThat(ipAddressMasked.toString(), is("1100::"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 120);
+        assertThat(ipAddressMasked.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8800"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 0);
+        assertThat(ipAddressMasked.toString(), is("::"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 128);
+        assertThat(ipAddressMasked.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8885"));
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 64);
+        assertThat(ipAddressMasked.toString(), is("1111:2222:3333:4444::"));
+    }
+
+    /**
+     * Tests making of a masked address for invalid prefix length for IPv4:
+     * negative prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeNegativeMaskedAddressIPv4() {
+        IpAddress ipAddress = IpAddress.valueOf("1.2.3.5");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, -1);
+    }
+
+    /**
+     * Tests making of a masked address for invalid prefix length for IPv6:
+     * negative prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeNegativeMaskedAddressIPv6() {
+        IpAddress ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, -1);
+    }
+
+    /**
+     * Tests making of a masked address for an invalid prefix length for IPv4:
+     * too long prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeTooLongMaskedAddressIPv4() {
+        IpAddress ipAddress = IpAddress.valueOf("1.2.3.5");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 33);
+    }
+
+    /**
+     * Tests making of a masked address for an invalid prefix length for IPv6:
+     * too long prefix length.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidMakeTooLongMaskedAddressIPv6() {
+        IpAddress ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8885");
+        IpAddress ipAddressMasked;
+
+        ipAddressMasked = IpAddress.makeMaskedAddress(ipAddress, 129);
+    }
+
+    /**
+     * Tests comparison of {@link IpAddress} for IPv4.
+     */
+    @Test
+    public void testComparisonIPv4() {
+        IpAddress addr1, addr2, addr3, addr4;
+
+        addr1 = IpAddress.valueOf("1.2.3.4");
+        addr2 = IpAddress.valueOf("1.2.3.4");
+        addr3 = IpAddress.valueOf("1.2.3.3");
+        addr4 = IpAddress.valueOf("1.2.3.5");
+        assertTrue(addr1.compareTo(addr2) == 0);
+        assertTrue(addr1.compareTo(addr3) > 0);
+        assertTrue(addr1.compareTo(addr4) < 0);
+
+        addr1 = IpAddress.valueOf("255.2.3.4");
+        addr2 = IpAddress.valueOf("255.2.3.4");
+        addr3 = IpAddress.valueOf("255.2.3.3");
+        addr4 = IpAddress.valueOf("255.2.3.5");
+        assertTrue(addr1.compareTo(addr2) == 0);
+        assertTrue(addr1.compareTo(addr3) > 0);
+        assertTrue(addr1.compareTo(addr4) < 0);
+    }
+
+    /**
+     * Tests comparison of {@link IpAddress} for IPv6.
+     */
+    @Test
+    public void testComparisonIPv6() {
+        IpAddress addr1, addr2, addr3, addr4;
+
+        addr1 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        addr2 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        addr3 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8887");
+        addr4 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8889");
+        assertTrue(addr1.compareTo(addr2) == 0);
+        assertTrue(addr1.compareTo(addr3) > 0);
+        assertTrue(addr1.compareTo(addr4) < 0);
+
+        addr1 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+        addr2 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+        addr3 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8887");
+        addr4 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8889");
+        assertTrue(addr1.compareTo(addr2) == 0);
+        assertTrue(addr1.compareTo(addr3) > 0);
+        assertTrue(addr1.compareTo(addr4) < 0);
+
+        addr1 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+        addr2 = IpAddress.valueOf("ffff:2222:3333:4444:5555:6666:7777:8888");
+        addr3 = IpAddress.valueOf("ffff:2222:3333:4443:5555:6666:7777:8888");
+        addr4 = IpAddress.valueOf("ffff:2222:3333:4445:5555:6666:7777:8888");
+        assertTrue(addr1.compareTo(addr2) == 0);
+        assertTrue(addr1.compareTo(addr3) > 0);
+        assertTrue(addr1.compareTo(addr4) < 0);
+    }
+
+    /**
+     * Tests equality of {@link IpAddress} for IPv4.
+     */
+    @Test
+    public void testEqualityIPv4() {
+        IpAddress addr1, addr2;
+
+        addr1 = IpAddress.valueOf("1.2.3.4");
+        addr2 = IpAddress.valueOf("1.2.3.4");
+        assertThat(addr1, is(addr2));
+
+        addr1 = IpAddress.valueOf("0.0.0.0");
+        addr2 = IpAddress.valueOf("0.0.0.0");
+        assertThat(addr1, is(addr2));
+
+        addr1 = IpAddress.valueOf("255.255.255.255");
+        addr2 = IpAddress.valueOf("255.255.255.255");
+        assertThat(addr1, is(addr2));
+    }
+
+    /**
+     * Tests equality of {@link IpAddress} for IPv6.
+     */
+    @Test
+    public void testEqualityIPv6() {
+        IpAddress addr1, addr2;
+
+        addr1 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        addr2 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        assertThat(addr1, is(addr2));
+
+        addr1 = IpAddress.valueOf("::");
+        addr2 = IpAddress.valueOf("::");
+        assertThat(addr1, is(addr2));
+
+        addr1 = IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        addr2 = IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        assertThat(addr1, is(addr2));
+    }
+
+    /**
+     * Tests non-equality of {@link IpAddress} for IPv4.
+     */
+    @Test
+    public void testNonEqualityIPv4() {
+        IpAddress addr1, addr2, addr3, addr4;
+
+        addr1 = IpAddress.valueOf("1.2.3.4");
+        addr2 = IpAddress.valueOf("1.2.3.5");
+        addr3 = IpAddress.valueOf("0.0.0.0");
+        addr4 = IpAddress.valueOf("255.255.255.255");
+        assertThat(addr1, is(not(addr2)));
+        assertThat(addr3, is(not(addr2)));
+        assertThat(addr4, is(not(addr2)));
+    }
+
+    /**
+     * Tests non-equality of {@link IpAddress} for IPv6.
+     */
+    @Test
+    public void testNonEqualityIPv6() {
+        IpAddress addr1, addr2, addr3, addr4;
+
+        addr1 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        addr2 = IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:888A");
+        addr3 = IpAddress.valueOf("::");
+        addr4 = IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        assertThat(addr1, is(not(addr2)));
+        assertThat(addr3, is(not(addr2)));
+        assertThat(addr4, is(not(addr2)));
+    }
+
+    /**
+     * Tests object string representation for IPv4.
+     */
+    @Test
+    public void testToStringIPv4() {
+        IpAddress ipAddress;
+
+        ipAddress = IpAddress.valueOf("1.2.3.4");
+        assertThat(ipAddress.toString(), is("1.2.3.4"));
+
+        ipAddress = IpAddress.valueOf("0.0.0.0");
+        assertThat(ipAddress.toString(), is("0.0.0.0"));
+
+        ipAddress = IpAddress.valueOf("255.255.255.255");
+        assertThat(ipAddress.toString(), is("255.255.255.255"));
+    }
+
+    /**
+     * Tests object string representation for IPv6.
+     */
+    @Test
+    public void testToStringIPv6() {
+        IpAddress ipAddress;
+
+        ipAddress =
+            IpAddress.valueOf("1111:2222:3333:4444:5555:6666:7777:8888");
+        assertThat(ipAddress.toString(),
+                   is("1111:2222:3333:4444:5555:6666:7777:8888"));
+
+        ipAddress = IpAddress.valueOf("1111::8888");
+        assertThat(ipAddress.toString(), is("1111::8888"));
+
+        ipAddress = IpAddress.valueOf("1111::");
+        assertThat(ipAddress.toString(), is("1111::"));
+
+        ipAddress = IpAddress.valueOf("::8888");
+        assertThat(ipAddress.toString(), is("::8888"));
+
+        ipAddress = IpAddress.valueOf("::");
+        assertThat(ipAddress.toString(), is("::"));
+
+        ipAddress =
+            IpAddress.valueOf("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+        assertThat(ipAddress.toString(),
+                   is("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+    }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
index ce6a1e3..f5ad88b 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
@@ -38,9 +38,11 @@
 
     @Test
     public void testEquality() {
-        IpPrefix ip1 = IpPrefix.valueOf(BYTES1, IpPrefix.MAX_INET_MASK_LENGTH);
+        IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        BYTES1, IpPrefix.MAX_INET_MASK_LENGTH);
         IpPrefix ip2 = IpPrefix.valueOf(INTVAL1, IpPrefix.MAX_INET_MASK_LENGTH);
-        IpPrefix ip3 = IpPrefix.valueOf(BYTES2, IpPrefix.MAX_INET_MASK_LENGTH);
+        IpPrefix ip3 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        BYTES2, IpPrefix.MAX_INET_MASK_LENGTH);
         IpPrefix ip4 = IpPrefix.valueOf(INTVAL2, IpPrefix.MAX_INET_MASK_LENGTH);
         IpPrefix ip5 = IpPrefix.valueOf(STRVAL);
 
@@ -50,16 +52,19 @@
         .testEquals();
 
         // string conversions
-        IpPrefix ip6 = IpPrefix.valueOf(BYTES1, MASK_LENGTH);
+        IpPrefix ip6 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        BYTES1, MASK_LENGTH);
         IpPrefix ip7 = IpPrefix.valueOf("10.0.0.10/16");
-        IpPrefix ip8 = IpPrefix.valueOf(new byte [] {0xa, 0x0, 0x0, 0xc}, 16);
+        IpPrefix ip8 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        new byte [] {0xa, 0x0, 0x0, 0xc}, 16);
         assertEquals("incorrect address conversion", ip6, ip7);
         assertEquals("incorrect address conversion", ip5, ip8);
     }
 
     @Test
     public void basics() {
-        IpPrefix ip1 = IpPrefix.valueOf(BYTES1, MASK_LENGTH);
+        IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        BYTES1, MASK_LENGTH);
         final byte [] bytes = new byte [] {0xa, 0x0, 0x0, 0x0};
 
         // check fields
@@ -74,7 +79,8 @@
     @Test
     public void netmasks() {
         // masked
-        IpPrefix ip1 = IpPrefix.valueOf(BYTES1, MASK_LENGTH);
+        IpPrefix ip1 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                        BYTES1, MASK_LENGTH);
         IpPrefix ip2 = IpPrefix.valueOf("10.0.0.10/16");
         IpPrefix ip3 = IpPrefix.valueOf("10.0.0.0/16");
         assertEquals("incorrect binary masked address",
@@ -87,9 +93,12 @@
 
     @Test
     public void testContainsIpPrefix() {
-        IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
-        IpPrefix slash32 = IpPrefix.valueOf(BYTES1, 32);
-        IpPrefix differentSlash32 = IpPrefix.valueOf(BYTES2, 32);
+        IpPrefix slash31 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                            BYTES1, 31);
+        IpPrefix slash32 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                            BYTES1, 32);
+        IpPrefix differentSlash32 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                                     BYTES2, 32);
 
         assertTrue(slash31.contains(differentSlash32));
         assertFalse(differentSlash32.contains(slash31));
@@ -109,8 +118,9 @@
 
     @Test
     public void testContainsIpAddress() {
-        IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
-        IpAddress addr32 = IpAddress.valueOf(BYTES1);
+        IpPrefix slash31 = IpPrefix.valueOf(IpAddress.Version.INET,
+                                            BYTES1, 31);
+        IpAddress addr32 = IpAddress.valueOf(IpAddress.Version.INET, BYTES1);
 
         assertTrue(slash31.contains(addr32));