Updated the implementation of IpAddress:
 * Added new static methods IpAddress.makeMaskPrefix and
   IpAddress.makeMaskedAddress
 * Cleanup

Also, removed obsoleted Javadoc comments in Ip4Prefix and Ip6Prefix classes
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
index b32cbba..e3b5246 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
@@ -101,20 +101,6 @@
         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) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
index 9603b68..5422ae1 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
@@ -101,20 +101,6 @@
         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) {
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 9dd97c3..eead723 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -17,6 +17,8 @@
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * A class representing an IPv4 address.
@@ -37,38 +39,71 @@
     /**
      * Constructor for given IP address version and address octets.
      *
-     * @param ver the IP address version
-     * @param octets the IP address octets
-     */
-    private IpAddress(Version ver, byte[] octets) {
-        this.version = ver;
-        this.octets = Arrays.copyOf(octets, INET_BYTE_LENGTH);
-    }
-
-    /**
-     * Converts a byte array into an IP address.
-     *
-     * @param address the IP address value stored in network byte order
+     * @param value the IP address value stored in network byte order
      * (i.e., the most significant byte first)
-     * @return an IP address
+     * @param value the IP address value
      */
-    public static IpAddress valueOf(byte[] address) {
-        return new IpAddress(Version.INET, address);
+    private IpAddress(Version version, byte[] value) {
+        checkNotNull(value);
+
+        this.version = version;
+        this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
     }
 
     /**
      * Converts an integer into an IPv4 address.
      *
-     * @param address an integer representing an IPv4 value
+     * @param value an integer representing an IPv4 value
      * @return an IP address
      */
-    public static IpAddress valueOf(int address) {
+    public static IpAddress valueOf(int value) {
         byte[] bytes =
-            ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(address).array();
+            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) {
+        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).
+     *
+     * @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);
+        }
+
+        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 a IP address in string form, e.g. "10.0.0.1".
@@ -77,8 +112,9 @@
     public static IpAddress valueOf(String address) {
         final String[] net = address.split("\\.");
         if (net.length != INET_BYTE_LENGTH) {
-            throw new IllegalArgumentException("Malformed IP address string; "
-                    + "Address must have four decimal values separated by dots (.)");
+            String msg = "Malformed IPv4 address string; " +
+                "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++) {
@@ -115,6 +151,48 @@
         return bb.getInt();
     }
 
+    /**
+     * Creates an IP network mask prefix.
+     *
+     * @param prefixLen the length of the mask prefix. Must be in the interval
+     * [0, 32] for IPv4
+     * @return a new IP address that contains a mask prefix of the
+     * specified length
+     */
+    public static IpAddress makeMaskPrefix(int prefixLen) {
+        // Verify the prefix length
+        if ((prefixLen < 0) || (prefixLen > INET_BIT_LENGTH)) {
+            final String msg = "Invalid IPv4 prefix length: " + prefixLen +
+                ". Must be in the interval [0, 32].";
+            throw new IllegalArgumentException(msg);
+        }
+
+        long v = (0xffffffffL << (INET_BIT_LENGTH - prefixLen)) & 0xffffffffL;
+        return IpAddress.valueOf((int) v);
+    }
+
+    /**
+     * Creates an IP 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] for IPv4
+     * @return a new IP address that is masked with a mask prefix of the
+     * specified length
+     */
+    public static IpAddress makeMaskedAddress(final IpAddress addr,
+                                              int prefixLen) {
+        IpAddress mask = IpAddress.makeMaskPrefix(prefixLen);
+        byte[] net = new byte[INET_BYTE_LENGTH];
+
+        // Mask each byte
+        for (int i = 0; i < INET_BYTE_LENGTH; i++) {
+            net[i] = (byte) (addr.octets[i] & mask.octets[i]);
+        }
+        return IpAddress.valueOf(net);
+    }
+
     @Override
     public int compareTo(IpAddress o) {
         Long lv = ((long) this.toInt()) & 0xffffffffL;
@@ -124,32 +202,20 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + Arrays.hashCode(octets);
-        result = prime * result + ((version == null) ? 0 : version.hashCode());
-        return result;
+        return Objects.hash(version, octets);
     }
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj) {
+        if (obj == this) {
             return true;
         }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
+        if ((obj == null) || (getClass() != obj.getClass())) {
             return false;
         }
         IpAddress other = (IpAddress) obj;
-        if (!Arrays.equals(octets, other.octets)) {
-            return false;
-        }
-        if (version != other.version) {
-            return false;
-        }
-        return true;
+        return (version == other.version) &&
+            Arrays.equals(octets, other.octets);
     }
 
     @Override