Refactor the IpPrefix API and implementation:
 * Now IpPrefix uses IpAddress to represent the subnet address
 * The IpPrefix subnet address is masked-out by the prefix length.
   E.g., IpPrefix("1.2.3.4/24") is now stored as IpPrefix("1.2.3.0/24")
 * Removed IpPrefix methods that are not used or don't apply anymore
 * Replaced usage of IpPrefix with IpAddress where appropriate
diff --git a/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java b/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
index b7ce160..f015e36 100644
--- a/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
+++ b/apps/config/src/main/java/org/onlab/onos/config/NetworkConfigReader.java
@@ -36,7 +36,6 @@
 import org.onlab.onos.net.host.PortAddresses;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 import org.slf4j.Logger;
 
@@ -81,12 +80,8 @@
                         if (splits.length != 2) {
                             throw new IllegalArgumentException("Invalid IP address and prefix length format");
                         }
-                        //
-                        // TODO: For now we need Ip4Prefix to mask-out the
-                        // subnet address.
-                        //
-                        Ip4Prefix subnet4 = new Ip4Prefix(strIp);
-                        IpPrefix subnet = IpPrefix.valueOf(subnet4.toString());
+                        // NOTE: IpPrefix will mask-out the bits after the prefix length.
+                        IpPrefix subnet = IpPrefix.valueOf(strIp);
                         IpAddress addr = IpAddress.valueOf(splits[0]);
                         InterfaceIpAddress ia =
                             new InterfaceIpAddress(addr, subnet);
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/RouteEntry.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/RouteEntry.java
index 1b45ac5..95169bb 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/RouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/RouteEntry.java
@@ -73,7 +73,7 @@
         }
 
         StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
-        long value = ip4Prefix.toInt();
+        long value = ip4Prefix.address().toInt() & 0xffffffffL;
         for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
             long mask = 1 << (IpPrefix.MAX_INET_MASK_LENGTH - 1 - i);
             result.append(((value & mask) == 0) ? "0" : "1");
diff --git a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/TestBgpPeerChannelHandler.java b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/TestBgpPeerChannelHandler.java
index ca4ccb7..4d94e1d 100644
--- a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/TestBgpPeerChannelHandler.java
+++ b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/TestBgpPeerChannelHandler.java
@@ -188,7 +188,7 @@
             int prefixBytelen = (prefixBitlen + 7) / 8;         // Round-up
             message.writeByte(prefixBitlen);
 
-            IpAddress address = prefix.toIpAddress();
+            IpAddress address = prefix.address();
             long value = address.toInt() & 0xffffffffL;
             for (int i = 0; i < IpAddress.INET_BYTE_LENGTH; i++) {
                 if (prefixBytelen-- == 0) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
index a584e17..ab7c230 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
@@ -22,7 +22,7 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.flow.instructions.Instruction;
 import org.onlab.onos.net.flow.instructions.Instructions;
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -181,12 +181,12 @@
         }
 
         @Override
-        public Builder setIpSrc(IpPrefix addr) {
+        public Builder setIpSrc(IpAddress addr) {
             return add(Instructions.modL3Src(addr));
         }
 
         @Override
-        public Builder setIpDst(IpPrefix addr) {
+        public Builder setIpDst(IpAddress addr) {
             return add(Instructions.modL3Dst(addr));
         }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
index 85c8bc0..20447ed 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
@@ -19,7 +19,7 @@
 
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.flow.instructions.Instruction;
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -92,14 +92,14 @@
          * @param addr an ip
          * @return a treatment builder
          */
-        public Builder setIpSrc(IpPrefix addr);
+        public Builder setIpSrc(IpAddress addr);
 
         /**
          * Sets the dst l3 address.
          * @param addr an ip
          * @return a treatment builder
          */
-        public Builder setIpDst(IpPrefix addr);
+        public Builder setIpDst(IpAddress addr);
 
         /**
          * Sets the optical channel ID or lambda.
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
index 3b1d433..0bdff77 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
@@ -27,7 +27,7 @@
 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 import org.onlab.onos.net.flow.instructions.L3ModificationInstruction.L3SubType;
 import org.onlab.onos.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -115,7 +115,7 @@
      * @param addr the ip address to modify to.
      * @return a L3 modification
      */
-    public static L3ModificationInstruction modL3Src(IpPrefix addr) {
+    public static L3ModificationInstruction modL3Src(IpAddress addr) {
         checkNotNull(addr, "Src l3 address cannot be null");
         return new ModIPInstruction(L3SubType.IP_SRC, addr);
     }
@@ -125,7 +125,7 @@
      * @param addr the ip address to modify to.
      * @return a L3 modification
      */
-    public static L3ModificationInstruction modL3Dst(IpPrefix addr) {
+    public static L3ModificationInstruction modL3Dst(IpAddress addr) {
         checkNotNull(addr, "Dst l3 address cannot be null");
         return new ModIPInstruction(L3SubType.IP_DST, addr);
     }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
index 609d245..89a8cda 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
@@ -19,7 +19,7 @@
 
 import java.util.Objects;
 
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
 
 /**
  * Abstraction of a single traffic treatment step.
@@ -60,9 +60,9 @@
     public static final class ModIPInstruction extends L3ModificationInstruction {
 
         private final L3SubType subtype;
-        private final IpPrefix ip;
+        private final IpAddress ip;
 
-        public ModIPInstruction(L3SubType subType, IpPrefix addr) {
+        public ModIPInstruction(L3SubType subType, IpAddress addr) {
 
             this.subtype = subType;
             this.ip = addr;
@@ -73,7 +73,7 @@
             return this.subtype;
         }
 
-        public IpPrefix ip() {
+        public IpAddress ip() {
             return this.ip;
         }
 
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 a9d3e4f..004f75f 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
@@ -38,7 +38,7 @@
     @Override
     public void write(Kryo kryo, Output output,
             IpPrefix object) {
-        byte[] octs = object.toOctets();
+        byte[] octs = object.address().toOctets();
         output.writeInt(octs.length);
         output.writeBytes(octs);
         output.writeInt(object.prefixLength());
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
index 2ccb9a6..02ec827 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
@@ -31,6 +31,7 @@
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.openflow.controller.Dpid;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -167,24 +168,12 @@
             case SET_NW_DST:
                 OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
                 IPv4Address di = nwdst.getNwAddr();
-                if (di.isCidrMask()) {
-                    builder.setIpDst(IpPrefix.valueOf(di.getInt(),
-                            di.asCidrMaskLength()));
-                } else {
-                    builder.setIpDst(IpPrefix.valueOf(di.getInt(),
-                                        IpPrefix.MAX_INET_MASK_LENGTH));
-                }
+                builder.setIpDst(IpAddress.valueOf(di.getInt()));
                 break;
             case SET_NW_SRC:
                 OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
                 IPv4Address si = nwsrc.getNwAddr();
-                if (si.isCidrMask()) {
-                    builder.setIpSrc(IpPrefix.valueOf(si.getInt(),
-                            si.asCidrMaskLength()));
-                } else {
-                    builder.setIpSrc(IpPrefix.valueOf(si.getInt(),
-                                        IpPrefix.MAX_INET_MASK_LENGTH));
-                }
+                builder.setIpSrc(IpAddress.valueOf(si.getInt()));
                 break;
             case EXPERIMENTER:
                 OFActionExperimenter exp = (OFActionExperimenter) act;
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 85c900b..749b4a9 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
@@ -29,6 +29,8 @@
 import org.onlab.onos.net.flow.criteria.Criteria.VlanIdCriterion;
 import org.onlab.onos.net.flow.criteria.Criteria.VlanPcpCriterion;
 import org.onlab.onos.net.flow.criteria.Criterion;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -141,22 +143,30 @@
                 break;
             case IPV4_DST:
                 ip = (IPCriterion) c;
-                if (ip.ip().isMasked()) {
-                    Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
-                            IPv4Address.of(ip.ip().netmask().toInt()));
+                if (ip.ip().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH) {
+                    IpAddress maskAddr =
+                        IpAddress.makeMaskPrefix(ip.ip().prefixLength());
+                    Masked<IPv4Address> maskedIp =
+                        Masked.of(IPv4Address.of(ip.ip().address().toInt()),
+                                  IPv4Address.of(maskAddr.toInt()));
                     mBuilder.setMasked(MatchField.IPV4_DST, maskedIp);
                 } else {
-                    mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toInt()));
+                    mBuilder.setExact(MatchField.IPV4_DST,
+                                IPv4Address.of(ip.ip().address().toInt()));
                 }
                 break;
             case IPV4_SRC:
                 ip = (IPCriterion) c;
-                if (ip.ip().isMasked()) {
-                    Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
-                            IPv4Address.of(ip.ip().netmask().toInt()));
+                if (ip.ip().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH) {
+                    IpAddress maskAddr =
+                        IpAddress.makeMaskPrefix(ip.ip().prefixLength());
+                    Masked<IPv4Address> maskedIp =
+                        Masked.of(IPv4Address.of(ip.ip().address().toInt()),
+                                  IPv4Address.of(maskAddr.toInt()));
                     mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp);
                 } else {
-                    mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toInt()));
+                    mBuilder.setExact(MatchField.IPV4_SRC,
+                                IPv4Address.of(ip.ip().address().toInt()));
                 }
                 break;
             case IP_PROTO:
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 6af87a7..487fb5b 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -21,7 +21,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * A class representing an IPv4 address.
+ * A class representing an IP address.
+ * TODO: Add support for IPv6 as well.
  */
 public final class IpAddress implements Comparable<IpAddress> {
     // IP Versions
@@ -44,8 +45,6 @@
      * @param value the IP address value
      */
     private IpAddress(Version version, byte[] value) {
-        checkNotNull(value);
-
         this.version = version;
         this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
     }
@@ -53,7 +52,7 @@
     /**
      * Converts an integer into an IPv4 address.
      *
-     * @param value an integer representing an IPv4 value
+     * @param value an integer representing an IPv4 address value
      * @return an IP address
      */
     public static IpAddress valueOf(int value) {
@@ -70,6 +69,7 @@
      * @return an IP address
      */
     public static IpAddress valueOf(byte[] value) {
+        checkNotNull(value);
         return new IpAddress(Version.INET, value);
     }
 
@@ -106,13 +106,13 @@
     /**
      * 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".
+     * @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; " +
+            String msg = "Malformed IPv4 address string: " + address + "." +
                 "Address must have four decimal values separated by dots (.)";
             throw new IllegalArgumentException(msg);
         }
@@ -154,20 +154,21 @@
     /**
      * Creates an IP network mask prefix.
      *
-     * @param prefixLen the length of the mask prefix. Must be in the interval
-     * [0, 32] for IPv4
+     * @param prefixLength 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) {
+    public static IpAddress makeMaskPrefix(int prefixLength) {
         // Verify the prefix length
-        if ((prefixLen < 0) || (prefixLen > INET_BIT_LENGTH)) {
-            final String msg = "Invalid IPv4 prefix length: " + prefixLen +
+        if ((prefixLength < 0) || (prefixLength > INET_BIT_LENGTH)) {
+            final String msg = "Invalid IPv4 prefix length: " + prefixLength +
                 ". Must be in the interval [0, 32].";
             throw new IllegalArgumentException(msg);
         }
 
-        long v = (0xffffffffL << (INET_BIT_LENGTH - prefixLen)) & 0xffffffffL;
+        long v =
+            (0xffffffffL << (INET_BIT_LENGTH - prefixLength)) & 0xffffffffL;
         return IpAddress.valueOf((int) v);
     }
 
@@ -176,14 +177,14 @@
      * 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
+     * @param prefixLength 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);
+                                              int prefixLength) {
+        IpAddress mask = IpAddress.makeMaskPrefix(prefixLength);
         byte[] net = new byte[INET_BYTE_LENGTH];
 
         // Mask each byte
@@ -207,7 +208,7 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj == this) {
+        if (this == obj) {
             return true;
         }
         if ((obj == null) || (getClass() != obj.getClass())) {
@@ -221,7 +222,7 @@
     @Override
     /*
      * (non-Javadoc)
-     * format is "x.x.x.x" for IPv4 addresses.
+     * The format is "x.x.x.x" for IPv4 addresses.
      *
      * @see java.lang.Object#toString()
      */
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 e864125..f15946a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -15,285 +15,174 @@
  */
 package org.onlab.packet;
 
-import java.util.Arrays;
+import java.util.Objects;
 
 /**
- * A class representing an IPv4 prefix.
+ * A class representing an IP prefix.
+ * TODO: Add support for IPv6 as well.
  * <p/>
  * A prefix consists of an IP address and a subnet mask.
+ * NOTE: The stored IP address in the result IP prefix is masked to
+ * contain zeroes in all bits after the prefix length.
  */
 public final class IpPrefix {
-
-    // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
-
-    // IP Versions: IPv4 and IPv6
-    public enum Version { INET, INET6 };
-
     // Maximum network mask length
     public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
     public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
 
-    //no mask (no network), e.g. a simple address
-    private static final int DEFAULT_MASK = 0;
+    private final IpAddress address;
+    private final short prefixLength;
 
     /**
-     * Default value indicating an unspecified address.
-     */
-    private static final byte[] ANY = new byte[] {0, 0, 0, 0};
-
-    private final Version version;
-    private final byte[] octets;
-    private final int netmask;
-
-    /**
-     * Constructor for given IP address version, prefix address octets,
-     * and network mask length.
+     * Constructor for given IP address, and a prefix length.
      *
-     * @param ver the IP address version
-     * @param octets the IP prefix address octets
-     * @param netmask the network mask length
+     * @param address the IP address
+     * @param prefixLength the prefix length
      */
-    private IpPrefix(Version ver, byte[] octets, int netmask) {
-        this.version = ver;
-        this.octets = Arrays.copyOf(octets, IpAddress.INET_BYTE_LENGTH);
-        this.netmask = netmask;
+    private IpPrefix(IpAddress address, int prefixLength) {
+        checkPrefixLength(prefixLength);
+        this.address = IpAddress.makeMaskedAddress(address, prefixLength);
+        this.prefixLength = (short) prefixLength;
     }
 
     /**
-     * Converts a byte array into an IP address.
+     * Checks whether the prefix length is valid.
      *
-     * @param address a byte array
-     * @param netmask the CIDR value subnet mask
-     * @return an IP address
+     * @param prefixLength the prefix length value to check
+     * @throws IllegalArgumentException if the prefix length value is invalid
      */
-    public static IpPrefix valueOf(byte[] address, int netmask) {
-        return new IpPrefix(Version.INET, address, netmask);
-    }
-
-    /**
-     * Helper to convert an integer into a byte array.
-     *
-     * @param address the integer to convert
-     * @return a byte array
-     */
-    private static byte[] bytes(int address) {
-        byte[] bytes = new byte [IpAddress.INET_BYTE_LENGTH];
-        for (int i = 0; i < IpAddress.INET_BYTE_LENGTH; i++) {
-            bytes[i] = (byte) ((address >> (IpAddress.INET_BYTE_LENGTH
-                                            - (i + 1)) * 8) & 0xff);
+    private static void checkPrefixLength(int prefixLength) {
+        if ((prefixLength < 0) || (prefixLength > MAX_INET_MASK_LENGTH)) {
+            String msg = "Invalid prefix length " + prefixLength + ". " +
+                "The value must be in the interval [0, " +
+                MAX_INET_MASK_LENGTH + "]";
+            throw new IllegalArgumentException(msg);
         }
-
-        return bytes;
     }
 
     /**
-     * Converts an integer into an IPv4 address.
+     * Converts an integer and a prefix length into an IPv4 prefix.
      *
-     * @param address an integer representing an IP value
-     * @param netmask the CIDR value subnet mask
-     * @return an IP address
+     * @param address an integer representing the IPv4 address
+     * @param prefixLength the prefix length
+     * @return an IP prefix
      */
-    public static IpPrefix valueOf(int address, int netmask) {
-        return new IpPrefix(Version.INET, bytes(address), netmask);
+    public static IpPrefix valueOf(int address, int prefixLength) {
+        return new IpPrefix(IpAddress.valueOf(address), prefixLength);
     }
 
     /**
-     * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
-     * string can also be in CIDR (slash) notation. If the netmask is omitted,
-     * it will be set to DEFAULT_MASK (0).
+     * Converts a byte array and a prefix length into an IP prefix.
      *
-     * @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24"
-     * @return an IP address
+     * @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);
+    }
+
+    /**
+     * Converts an IP address and a prefix length into IP prefix.
+     *
+     * @param address the IP address
+     * @param prefixLength the prefix length
+     * @return an IP prefix
+     */
+    public static IpPrefix valueOf(IpAddress address, int prefixLength) {
+        return new IpPrefix(address, prefixLength);
+    }
+
+    /**
+     * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16") into an
+     * IP prefix.
+     *
+     * @param value an IP prefix in string form, e.g. "10.1.0.0/16"
+     * @return an IP prefix
      */
     public static IpPrefix valueOf(String address) {
+        final String[] parts = address.split("/");
+        if (parts.length != 2) {
+            String msg = "Malformed IP prefix string: " + address + "." +
+                "Address must take form \"x.x.x.x/y\"";
+            throw new IllegalArgumentException(msg);
+        }
+        IpAddress ipAddress = IpAddress.valueOf(parts[0]);
+        int prefixLength = Integer.parseInt(parts[1]);
 
-        final String[] parts = address.split("\\/");
-        if (parts.length > 2) {
-            throw new IllegalArgumentException("Malformed IP address string; "
-                    + "Address must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
-        }
-
-        int mask = DEFAULT_MASK;
-        if (parts.length == 2) {
-            mask = Integer.parseInt(parts[1]);
-            if (mask > MAX_INET_MASK_LENGTH) {
-                throw new IllegalArgumentException(
-                        "Value of subnet mask cannot exceed "
-                                + MAX_INET_MASK_LENGTH);
-            }
-        }
-
-        final String[] net = parts[0].split("\\.");
-        if (net.length != IpAddress.INET_BYTE_LENGTH) {
-            throw new IllegalArgumentException("Malformed IP address string; "
-                    + "Address must have four decimal values separated by dots (.)");
-        }
-        final byte[] bytes = new byte[IpAddress.INET_BYTE_LENGTH];
-        for (int i = 0; i < IpAddress.INET_BYTE_LENGTH; i++) {
-            bytes[i] = (byte) Short.parseShort(net[i], 10);
-        }
-        return new IpPrefix(Version.INET, bytes, mask);
+        return new IpPrefix(ipAddress, prefixLength);
     }
 
     /**
-     * Returns the IP version of this address.
+     * Returns the IP version of the prefix.
      *
-     * @return the version
+     * @return the IP version of the prefix
      */
-    public Version version() {
-        return this.version;
+    public IpAddress.Version version() {
+        return address.version();
     }
 
     /**
-     * Returns the IP address as a byte array.
+     * Returns the IP address value of the prefix.
      *
-     * @return a byte array
+     * @return the IP address value of the prefix
      */
-    public byte[] toOctets() {
-        return Arrays.copyOf(this.octets, IpAddress.INET_BYTE_LENGTH);
+    public IpAddress address() {
+        return address;
     }
 
     /**
      * Returns the IP address prefix length.
      *
-     * @return prefix length
+     * @return the IP address prefix length
      */
     public int prefixLength() {
-        return netmask;
+        return prefixLength;
     }
 
     /**
-     * Returns the integral value of this IP address.
+     * Determines whether a given IP prefix is contained within this prefix.
      *
-     * @return the IP address's value as an integer
-     */
-    public int toInt() {
-        int val = 0;
-        for (int i = 0; i < octets.length; i++) {
-          val <<= 8;
-          val |= octets[i] & 0xff;
-        }
-        return val;
-    }
-
-    /**
-     * Helper for computing the mask value from CIDR.
-     *
-     * @return an integer bitmask
-     */
-    private int mask() {
-        int shift = MAX_INET_MASK_LENGTH - this.netmask;
-        return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
-    }
-
-    /**
-     * Returns the subnet mask in IpAddress form.
-     *
-     * @return the subnet mask as an IpAddress
-     */
-    public IpAddress netmask() {
-        return IpAddress.valueOf(mask());
-    }
-
-    /**
-     * Returns the network portion of this address as an IpAddress.
-     * The netmask of the returned IpAddress is the current mask. If this
-     * address doesn't have a mask, this returns an all-0 IpAddress.
-     *
-     * @return the network address or null
-     */
-    public IpPrefix network() {
-        if (netmask == DEFAULT_MASK) {
-            return new IpPrefix(version, ANY, DEFAULT_MASK);
-        }
-
-        byte[] net = new byte [4];
-        byte[] mask = bytes(mask());
-        for (int i = 0; i < IpAddress.INET_BYTE_LENGTH; i++) {
-            net[i] = (byte) (octets[i] & mask[i]);
-        }
-        return new IpPrefix(version, net, netmask);
-    }
-
-    /**
-     * Returns the host portion of the IPAddress, as an IPAddress.
-     * The netmask of the returned IpAddress is the current mask. If this
-     * address doesn't have a mask, this returns a copy of the current
-     * address.
-     *
-     * @return the host address
-     */
-    public IpPrefix host() {
-        if (netmask == DEFAULT_MASK) {
-            new IpPrefix(version, octets, netmask);
-        }
-
-        byte[] host = new byte [IpAddress.INET_BYTE_LENGTH];
-        byte[] mask = bytes(mask());
-        for (int i = 0; i < IpAddress.INET_BYTE_LENGTH; i++) {
-            host[i] = (byte) (octets[i] & ~mask[i]);
-        }
-        return new IpPrefix(version, host, netmask);
-    }
-
-    /**
-     * Returns an IpAddress of the bytes contained in this prefix.
-     * FIXME this is a hack for now and only works because IpPrefix doesn't
-     * mask the input bytes on creation.
-     *
-     * @return the IpAddress
-     */
-    public IpAddress toIpAddress() {
-        return IpAddress.valueOf(octets);
-    }
-
-    public boolean isMasked() {
-        return mask() != 0;
-    }
-
-    /**
-     * Determines whether a given address is contained within this IpAddress'
-     * network.
-     *
-     * @param other another IP address that could be contained in this network
-     * @return true if the other IP address is contained in this address'
-     * network, otherwise false
+     * @param other the IP prefix to test
+     * @return true if the other IP prefix is contained in this prefix,
+     * otherwise false
      */
     public boolean contains(IpPrefix other) {
-        if (this.netmask <= other.netmask) {
-            // Special case where they're both /32 addresses
-            if (this.netmask == MAX_INET_MASK_LENGTH) {
-                return Arrays.equals(octets, other.octets);
-            }
-
-            // Mask the other address with our network mask
-            IpPrefix otherMasked =
-                    IpPrefix.valueOf(other.octets, netmask).network();
-
-            return network().equals(otherMasked);
+        if (this.prefixLength > other.prefixLength) {
+            return false;               // This prefix has smaller prefix size
         }
-        return false;
+
+        //
+        // Mask the other address with my prefix length.
+        // If the other prefix is within this prefix, the masked address must
+        // be same as the address of this prefix.
+        //
+        IpAddress maskedAddr =
+            IpAddress.makeMaskedAddress(other.address, this.prefixLength);
+        return this.address.equals(maskedAddr);
     }
 
-    public boolean contains(IpAddress address) {
-        // Need to get the network address because prefixes aren't automatically
-        // masked on creation
-        IpPrefix meMasked = network();
-
-        IpPrefix otherMasked =
-            IpPrefix.valueOf(address.toOctets(), netmask).network();
-
-        return Arrays.equals(meMasked.octets, otherMasked.octets);
+    /**
+     * Determines whether a given IP address is contained within this prefix.
+     *
+     * @param other the IP address to test
+     * @return true if the IP address is contained in this prefix, otherwise
+     * false
+     */
+    public boolean contains(IpAddress other) {
+        //
+        // Mask the other address with my prefix length.
+        // If the other prefix is within this prefix, the masked address must
+        // be same as the address of this prefix.
+        //
+        IpAddress maskedAddr =
+            IpAddress.makeMaskedAddress(other, this.prefixLength);
+        return this.address.equals(maskedAddr);
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + netmask;
-        result = prime * result + Arrays.hashCode(octets);
-        result = prime * result + ((version == null) ? 0 : version.hashCode());
-        return result;
+        return Objects.hash(address, prefixLength);
     }
 
     @Override
@@ -301,46 +190,26 @@
         if (this == obj) {
             return true;
         }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
+        if ((obj == null) || (getClass() != obj.getClass())) {
             return false;
         }
         IpPrefix other = (IpPrefix) obj;
-        if (netmask != other.netmask) {
-            return false;
-        }
-        // TODO not quite right until we mask the input
-        if (!Arrays.equals(octets, other.octets)) {
-            return false;
-        }
-        if (version != other.version) {
-            return false;
-        }
-        return true;
+        return ((prefixLength == other.prefixLength) &&
+                address.equals(other.address));
     }
 
     @Override
     /*
      * (non-Javadoc)
-     * format is "x.x.x.x" for non-masked (netmask 0) addresses,
-     * and "x.x.x.x/y" for masked addresses.
+     * The format is "x.x.x.x/y" for IPv4 prefixes.
      *
      * @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));
-        }
-        if (netmask != DEFAULT_MASK) {
-            builder.append("/");
-            builder.append(String.format("%d", netmask));
-        }
+        builder.append(address.toString());
+        builder.append("/");
+        builder.append(String.format("%d", prefixLength));
         return builder.toString();
     }
 }
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 90ebafd..ce6a1e3 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
@@ -22,7 +22,7 @@
 import java.util.Arrays;
 
 import org.junit.Test;
-import org.onlab.packet.IpPrefix.Version;
+import org.onlab.packet.IpAddress.Version;
 
 import com.google.common.testing.EqualsTester;
 
@@ -30,8 +30,9 @@
 
     private static final byte [] BYTES1 = new byte [] {0xa, 0x0, 0x0, 0xa};
     private static final byte [] BYTES2 = new byte [] {0xa, 0x0, 0x0, 0xb};
-    private static final int INTVAL1 = 167772170;
-    private static final int INTVAL2 = 167772171;
+    private static final int INTVAL0 = 0x0a000000;
+    private static final int INTVAL1 = 0x0a00000a;
+    private static final int INTVAL2 = 0x0a00000b;
     private static final String STRVAL = "10.0.0.12/16";
     private static final int MASK_LENGTH = 16;
 
@@ -59,27 +60,29 @@
     @Test
     public void basics() {
         IpPrefix ip1 = IpPrefix.valueOf(BYTES1, MASK_LENGTH);
-        final byte [] bytes = new byte [] {0xa, 0x0, 0x0, 0xa};
+        final byte [] bytes = new byte [] {0xa, 0x0, 0x0, 0x0};
 
-        //check fields
+        // check fields
         assertEquals("incorrect IP Version", Version.INET, ip1.version());
         assertEquals("incorrect netmask", 16, ip1.prefixLength());
-        assertTrue("faulty toOctets()", Arrays.equals(bytes, ip1.toOctets()));
-        assertEquals("faulty toInt()", INTVAL1, ip1.toInt());
-        assertEquals("faulty toString()", "10.0.0.10/16", ip1.toString());
+        assertTrue("faulty toOctets()",
+                   Arrays.equals(bytes, ip1.address().toOctets()));
+        assertEquals("faulty toInt()", INTVAL0, ip1.address().toInt());
+        assertEquals("faulty toString()", "10.0.0.0/16", ip1.toString());
     }
 
     @Test
     public void netmasks() {
         // masked
         IpPrefix ip1 = IpPrefix.valueOf(BYTES1, MASK_LENGTH);
-
-        IpPrefix host = IpPrefix.valueOf("0.0.0.10/16");
-        IpPrefix network = IpPrefix.valueOf("10.0.0.0/16");
-        assertEquals("incorrect host address", host, ip1.host());
-        assertEquals("incorrect network address", network, ip1.network());
-        assertEquals("incorrect netmask", "255.255.0.0", ip1.netmask().toString());
-
+        IpPrefix ip2 = IpPrefix.valueOf("10.0.0.10/16");
+        IpPrefix ip3 = IpPrefix.valueOf("10.0.0.0/16");
+        assertEquals("incorrect binary masked address",
+                     ip1.toString(), "10.0.0.0/16");
+        assertEquals("incorrect string masked address",
+                     ip2.toString(), "10.0.0.0/16");
+        assertEquals("incorrect network address",
+                     ip2.toString(), "10.0.0.0/16");
     }
 
     @Test