initial import

Change-Id: Ief25aef0066ea96bd2c329ccef974c072b3a5a73
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java
new file mode 100644
index 0000000..1fe36ac
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java
@@ -0,0 +1,23 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class HashValueUtilsTest {
+    @Test
+    public void testBasic() {
+        long key =        0x1234_5678_1234_5678L;
+        long value =      0x8765_4321_8765_4321L;
+        long firstword  = 0xFFFF_FFFF_0000_0000L;
+        long secondword = 0x0000_0000_FFFF_FFFFL;
+        long xor =        key ^ value;
+
+        assertThat(HashValueUtils.combineWithValue(key, value, 0), equalTo(xor));
+        assertThat(HashValueUtils.combineWithValue(key, value, 64), equalTo(key));
+        assertThat(HashValueUtils.combineWithValue(key, value, 32), equalTo(key & firstword | xor & secondword ));
+        assertThat(HashValueUtils.combineWithValue(key, value, 8), equalTo(0x1251_1559_9551_1559L));
+    }
+
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java
new file mode 100644
index 0000000..865df75
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPAddressTest.java
@@ -0,0 +1,67 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+
+/**
+ * Most tests are in IPv4AddressTest and IPv6AddressTest
+ * Just exception testing here
+ * @author gregor
+ *
+ */
+public class IPAddressTest {
+    @Test
+    public void testOfException() {
+        try {
+            IPAddress.of("Foobar");
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPAddressWithMask.of("Foobar");
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPAddress.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddress.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPAddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+
+    @Test
+    public void testFromInetAddressException() throws UnknownHostException {
+        try {
+            IPAddress.fromInetAddress(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
new file mode 100644
index 0000000..a57b42a
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv4AddressTest.java
@@ -0,0 +1,371 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.hamcrest.CoreMatchers;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+public class IPv4AddressTest {
+    byte[][] testAddresses = new byte[][] {
+            {0x01, 0x02, 0x03, 0x04 },
+            {127, 0, 0, 1},
+            {(byte) 192, (byte) 168, 0, 100 },
+            {(byte) 255, (byte) 255, (byte) 255, (byte) 255 }
+    };
+
+    String[] testStrings = {
+            "1.2.3.4",
+            "127.0.0.1",
+            "192.168.0.100",
+            "255.255.255.255"
+    };
+
+    int[] testInts = {
+            0x01020304,
+            0x7f000001,
+            (192 << 24) | (168 << 16) | 100,
+            0xffffffff
+    };
+
+    String[] invalidIPs = {
+            "",
+            ".",
+            "1.2..3.4",
+            "1.2.3.4.",
+            "257.11.225.1",
+            "256.11.225.1",
+            "-1.2.3.4",
+            "1.2.3.4.5",
+            "1.x.3.4",
+            "1.2x.3.4"
+    };
+
+    String[] ipsWithMask = {
+                            "1.2.3.4/24",
+                            "192.168.130.140/255.255.192.0",
+                            "127.0.0.1/8",
+                            "8.8.8.8",
+                            "8.8.8.8/32",
+                            "0.0.0.0/0",
+                            "192.168.130.140/255.0.255.0",
+                            "1.2.3.4/0.127.0.255"
+    };
+
+    boolean[] hasMask = {
+                         true,
+                         true,
+                         true,
+                         false,
+                         false,
+                         true,
+                         true,
+                         true
+    };
+
+    byte[][][] ipsWithMaskValues = {
+                             new byte[][] { new byte[] { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0xC0, (byte)0xA8, (byte)0x82, (byte)0x8C }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xC0, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0x7F, (byte)0x00, (byte)0x00, (byte)0x01 }, new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF } },
+                             new byte[][] { new byte[] { (byte)0x08, (byte)0x08, (byte)0x08, (byte)0x08 }, new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF } },
+                             new byte[][] { new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0xC0, (byte)0xA8, (byte)0x82, (byte)0x8C }, new byte[] { (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00 } },
+                             new byte[][] { new byte[] { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 }, new byte[] { (byte)0x00, (byte)0x7F, (byte)0x00, (byte)0xFF } }
+    };
+
+    int[] ipsWithMaskLengths = {
+                                24,
+                                18,
+                                8,
+                                32,
+                                32,
+                                0,
+                                -1,
+                                -1
+    };
+
+    String[] invalidIpsWithMask = {
+                                   "asdf",
+                                   "1.2.3.4/33",
+                                   "1.2.3.4/34",
+                                   "1.2.3.4/-1",
+                                   "1.2.3.4/256.0.0.0",
+                                   "1.256.3.4/255.255.0.0",
+                                   "1.2.3.4/255.255.0.0.0",
+    };
+
+    @Test
+    public void testLogicalOperatorsBroadcast() {
+        assertTrue(IPv4Address.NO_MASK.not().equals(IPv4Address.FULL_MASK));
+        assertTrue(IPv4Address.NO_MASK.or(IPv4Address.FULL_MASK).
+                   equals(IPv4Address.NO_MASK));
+        assertTrue(IPv4Address.NO_MASK.and(IPv4Address.FULL_MASK).
+                   equals(IPv4Address.FULL_MASK));
+
+        assertTrue(IPv4Address.NO_MASK.isBroadcast());
+        assertTrue(!IPv4Address.FULL_MASK.isBroadcast());
+    }
+
+    @Test
+    public void testMaskedSubnetBroadcast() {
+        assertTrue(IPv4AddressWithMask.of("10.10.10.1/24")
+                   .getSubnetBroadcastAddress()
+                   .equals(IPv4Address.of("10.10.10.255")));
+        assertTrue(IPv4AddressWithMask.of("10.10.10.1/24")
+                   .isSubnetBroadcastAddress(IPv4Address.of("10.10.10.255")));
+        assertTrue(!IPv4AddressWithMask.of("10.10.10.1/24")
+                   .isSubnetBroadcastAddress(IPv4Address.of("10.10.10.254")));
+    }
+
+    @Test
+    public void testMaskedMatchesCidr() {
+        IPv4AddressWithMask slash28 = IPv4AddressWithMask.of("10.0.42.16/28");
+
+        String[] notContained = {"0.0.0.0", "11.0.42.16", "10.0.41.1", "10.0.42.0", "10.0.42.15",
+                                 "10.0.42.32", "255.255.255.255" };
+
+        for(String n: notContained) {
+            assertThat(String.format("slash 28 %s should not contain address %s",
+                                     slash28, n),
+                    slash28.matches(IPv4Address.of(n)), equalTo(false));
+        }
+        for(int i=16; i < 32; i++) {
+            IPv4Address c = IPv4Address.of(String.format("10.0.42.%d", i));
+            assertThat(String.format("slash 28 %s should contain address %s",
+                                     slash28, c),
+                       slash28.matches(c), equalTo(true));
+        }
+    }
+
+    @Test
+    public void testMaskedMatchesArbitrary() {
+        // irregular octect on the 3rd bitmask requires '1'bit to be set
+        // 4 bit unset, all others arbitrary
+        IPv4AddressWithMask slash28 = IPv4AddressWithMask.of("1.2.1.4/255.255.5.255");
+
+        String[] notContained = {"0.0.0.0", "1.2.3.5", "1.2.3.3",
+                                 "1.2.0.4", "1.2.2.4", "1.2.4.4", "1.2.5.4", "1.2.6.4", "1.2.7.4",
+                                 "1.2.8.4", "1.2.12.4", "1.2.13.4"
+                                 };
+        String[] contained = {"1.2.1.4", "1.2.3.4", "1.2.9.4", "1.2.11.4", "1.2.251.4",
+                };
+
+        for(String n: notContained) {
+            assertThat(String.format("slash 28 %s should not contain address %s",
+                                     slash28, n),
+                    slash28.matches(IPv4Address.of(n)), equalTo(false));
+        }
+        for(String c: contained) {
+            IPv4Address addr = IPv4Address.of(c);
+            assertThat(String.format("slash 28 %s should contain address %s",
+                                     slash28, addr),
+                       slash28.matches(addr), equalTo(true));
+        }
+
+    }
+
+
+    @Test
+    public void testConstants() {
+        byte[] zeros = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
+        byte[] ones =  { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
+        // Make sure class initializtation and static assignment don't get
+        // messed up. Test everything twice for cached values
+        assertTrue(IPv4Address.NONE.isCidrMask());
+        assertEquals(0, IPv4Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.NONE.getBytes());
+        assertTrue(IPv4Address.NONE.isCidrMask());
+        assertEquals(0, IPv4Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.NONE.getBytes());
+
+        assertTrue(IPv4Address.NO_MASK.isCidrMask());
+        assertEquals(32, IPv4Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv4Address.NO_MASK.getBytes());
+        assertTrue(IPv4Address.NO_MASK.isCidrMask());
+        assertEquals(32, IPv4Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv4Address.NO_MASK.getBytes());
+
+        assertTrue(IPv4Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv4Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.FULL_MASK.getBytes());
+        assertTrue(IPv4Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv4Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv4Address.FULL_MASK.getBytes());
+    }
+
+
+    @Test
+    public void testOfString() {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            IPv4Address ip = IPv4Address.of(testStrings[i]);
+            assertEquals(testInts[i], ip.getInt());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+    @Test
+    public void testOfByteArray() {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            IPv4Address ip = IPv4Address.of(testAddresses[i]);
+            assertEquals(testInts[i], ip.getInt());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+    @Test
+    public void testReadFrom() throws OFParseError {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            IPv4Address ip = IPv4Address.read4Bytes(ChannelBuffers.copiedBuffer(testAddresses[i]));
+            assertEquals(testInts[i], ip.getInt());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+
+    @Test
+    public void testInvalidIPs() throws OFParseError {
+        for(String invalid : invalidIPs) {
+            try {
+                IPv4Address.of(invalid);
+                fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    @Test
+    public void testOfMasked() throws OFParseError {
+        for (int i = 0; i < ipsWithMask.length; i++) {
+            IPv4AddressWithMask value = IPv4AddressWithMask.of(ipsWithMask[i]);
+            if (!hasMask[i]) {
+                IPv4Address ip = value.getValue();
+                assertArrayEquals(ipsWithMaskValues[i][0], ip.getBytes());
+            }
+            IPv4Address mask = value.getMask();
+            if (ipsWithMaskLengths[i] == -1) {
+                assertFalse(mask.isCidrMask());
+                try {
+                    mask.asCidrMaskLength();
+                    fail("Expected IllegalStateException not thrown");
+                } catch(IllegalStateException e) {
+                    //expected
+                }
+            } else {
+                assertTrue(mask.isCidrMask());
+                assertEquals(ipsWithMaskLengths[i], mask.asCidrMaskLength());
+            }
+            assertArrayEquals(ipsWithMaskValues[i][1], mask.getBytes());
+            byte[] ipBytes = new byte[4];
+            System.arraycopy(ipsWithMaskValues[i][0], 0, ipBytes, 0, 4);
+            assertEquals(ipBytes.length, value.getValue().getBytes().length);
+            for (int j = 0; j < ipBytes.length; j++) {
+                ipBytes[j] &= ipsWithMaskValues[i][1][j];
+            }
+
+            assertArrayEquals(ipBytes, value.getValue().getBytes());
+            assertThat(String.format("Byte comparison for mask of %s (%s)", ipsWithMask[i], value),
+                    value.getMask().getBytes(), CoreMatchers.equalTo(ipsWithMaskValues[i][1]));
+        }
+    }
+
+    @Test
+    public void testOfMaskedInvalid() throws Exception {
+        for(String invalid : invalidIpsWithMask) {
+            try {
+                IPv4Address.of(invalid);
+                fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    @Test
+    public void testSuperclass() throws Exception {
+        for(String ipString: testStrings) {
+            IPAddress<?> superIp = IPAddress.of(ipString);
+            assertEquals(IPVersion.IPv4, superIp.getIpVersion());
+            assertEquals(IPv4Address.of(ipString), superIp);
+        }
+
+        for(String ipMaskedString: ipsWithMask) {
+            IPAddressWithMask<?> superIp = IPAddressWithMask.of(ipMaskedString);
+            assertEquals(IPVersion.IPv4, superIp.getIpVersion());
+            assertEquals(IPv4AddressWithMask.of(ipMaskedString), superIp);
+        }
+    }
+
+    @Test
+    public void testOfExceptions() {
+        // We check if the message of a caught NPE is set to a useful message
+        // as a hacky way of verifying that we got an NPE thrown by use rather
+        // than one the JVM created for a null access.
+        try {
+            String s = null;
+            IPv4Address.of(s);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = null;
+            IPv4Address.of(b);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = new byte[3];
+            IPv4Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            byte[] b = new byte[5];
+            IPv4Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPv4AddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv4AddressWithMask.of(IPv4Address.of("1.2.3.4"), null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv4AddressWithMask.of(null, IPv4Address.of("255.0.0.0"));
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv4AddressWithMask.of(IPv4Address.of("10.10.10.0"),
+                                   IPv4Address.of("255.0.255.0"))
+                                   .getSubnetBroadcastAddress();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
new file mode 100644
index 0000000..6963c21
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
@@ -0,0 +1,317 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.junit.Assert.*;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.hamcrest.CoreMatchers;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+import com.google.common.io.BaseEncoding;
+
+public class IPv6AddressTest {
+
+    String[] testStrings = {
+            "::",
+            "::1",
+            "ffe0::",
+            "1:2:3:4:5:6:7:8"
+    };
+
+
+    private final BaseEncoding hex = BaseEncoding.base16().omitPadding().lowerCase();
+
+    private class WithMaskTaskCase {
+        final String input;
+        boolean hasMask;
+        int expectedMaskLength = 128;
+        byte[] expectedMask = hex.decode("ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff".replaceAll(" ", ""));
+
+        public WithMaskTaskCase(String input) {
+            super();
+            this.input = input;
+        }
+
+        public WithMaskTaskCase maskHex(String string) {
+            string = string.replaceAll(" ", "");
+            this.hasMask = true;
+            expectedMask = hex.decode(string);
+            return this;
+        }
+
+        public WithMaskTaskCase expectedMaskLength(int expectedLength) {
+            this.expectedMaskLength = expectedLength;
+            return this;
+        }
+
+    }
+
+    WithMaskTaskCase[] withMasks = new WithMaskTaskCase[] {
+            new WithMaskTaskCase("1::1/80")
+                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00")
+                .expectedMaskLength(80),
+
+            new WithMaskTaskCase("ffff:ffee:1::/ff00:ff00:ff00:ff00::")
+                .maskHex("ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/1::ff00:ff00")
+                .maskHex("00 01 00 00 00 00 00 00 00 00 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/::ff00:ff00")
+                .maskHex("00 00 00 00 00 00 00 00 00 00 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff::ff00:ff00")
+                .maskHex("ff ff ff ff ff ff ff ff ff ff 00 00 ff 00 ff 00")
+                .expectedMaskLength(-1),
+            new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
+            new WithMaskTaskCase("8:8:8:8:8:8:8:8"),
+            new WithMaskTaskCase("1:2:3:4:5:6:7:8/128"),
+            new WithMaskTaskCase("::/0")
+                .maskHex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
+                .expectedMaskLength(0),
+    };
+
+    @Test
+    public void testLogicalOperatorsBroadcast() {
+        assertTrue(IPv6Address.NO_MASK.not().equals(IPv6Address.FULL_MASK));
+        assertTrue(IPv6Address.NO_MASK.or(IPv6Address.FULL_MASK).
+                   equals(IPv6Address.NO_MASK));
+        assertTrue(IPv6Address.NO_MASK.and(IPv6Address.FULL_MASK).
+                   equals(IPv6Address.FULL_MASK));
+
+        assertTrue(IPv6Address.NO_MASK.isBroadcast());
+        assertTrue(!IPv6Address.FULL_MASK.isBroadcast());
+    }
+
+    @Test
+    public void testMaskedSubnetBroadcast() {
+        assertTrue(IPv6AddressWithMask.of("10:10::1/112")
+                   .getSubnetBroadcastAddress()
+                   .equals(IPv6Address.of("10:10::ffff")));
+        assertTrue(IPv6AddressWithMask.of("10:10::1/112")
+                   .isSubnetBroadcastAddress(IPv6Address.of("10:10::ffff")));
+        assertTrue(!IPv6AddressWithMask.of("10:10::1/112")
+                   .isSubnetBroadcastAddress(IPv6Address.of("10:10::fffd")));
+    }
+
+    @Test
+    public void testConstants() {
+        byte[] zeros = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+                         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
+        byte[] ones = { (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 };
+        // Make sure class initializtation and static assignment don't get
+        // messed up. Test everything twice for cached values
+        assertTrue(IPv6Address.NONE.isCidrMask());
+        assertEquals(0, IPv6Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.NONE.getBytes());
+        assertTrue(IPv6Address.NONE.isCidrMask());
+        assertEquals(0, IPv6Address.NONE.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.NONE.getBytes());
+
+        assertTrue(IPv6Address.NO_MASK.isCidrMask());
+        assertEquals(128, IPv6Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv6Address.NO_MASK.getBytes());
+        assertTrue(IPv6Address.NO_MASK.isCidrMask());
+        assertEquals(128, IPv6Address.NO_MASK.asCidrMaskLength());
+        assertArrayEquals(ones, IPv6Address.NO_MASK.getBytes());
+
+        assertTrue(IPv6Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv6Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.FULL_MASK.getBytes());
+        assertTrue(IPv6Address.FULL_MASK.isCidrMask());
+        assertEquals(0, IPv6Address.FULL_MASK.asCidrMaskLength());
+        assertArrayEquals(zeros, IPv6Address.FULL_MASK.getBytes());
+    }
+
+    @Test
+    public void testMasked() throws UnknownHostException {
+        for(WithMaskTaskCase w: withMasks) {
+            IPv6AddressWithMask value = IPv6AddressWithMask.of(w.input);
+            if (!w.hasMask) {
+                IPv6Address ip = value.getValue();
+                InetAddress inetAddress = InetAddress.getByName(w.input.split("/")[0]);
+
+                assertArrayEquals(ip.getBytes(), inetAddress.getAddress());
+                assertEquals(w.input.split("/")[0], ip.toString());
+            }
+            InetAddress inetAddress = InetAddress.getByName(w.input.split("/")[0]);
+
+            if (w.expectedMaskLength == -1) {
+                assertFalse(value.getMask().isCidrMask());
+                try {
+                    value.getMask().asCidrMaskLength();
+                    fail("Expected IllegalStateException not thrown");
+                } catch(IllegalStateException e) {
+                    //expected
+                }
+            } else {
+                assertTrue(value.getMask().isCidrMask());
+                assertEquals("Input " + w.input, w.expectedMaskLength,
+                             value.getMask().asCidrMaskLength());
+            }
+
+            byte[] address = inetAddress.getAddress();
+            assertEquals(address.length, value.getValue().getBytes().length);
+
+            for (int j = 0; j < address.length; j++) {
+                address[j] &= w.expectedMask[j];
+            }
+
+            assertThat("Address bytes for input " + w.input + ", value=" + value, value.getValue().getBytes(), CoreMatchers.equalTo(address));
+            assertThat("mask check for input " + w.input + ", value=" + value, value.getMask().getBytes(), CoreMatchers.equalTo(w.expectedMask));
+        }
+        for (int i = 0; i <= 128; i++) {
+            String ipString = String.format("8001:2::1/%d", i);
+            IPv6AddressWithMask value = IPv6AddressWithMask.of(ipString);
+            assertEquals("Input " + ipString, i, value.getMask().asCidrMaskLength());
+        }
+    }
+
+
+    @Test
+    public void testOfString() throws UnknownHostException {
+        for(int i=0; i < testStrings.length; i++ ) {
+            IPv6Address ip = IPv6Address.of(testStrings[i]);
+            InetAddress inetAddress = InetAddress.getByName(testStrings[i]);
+
+            assertArrayEquals(ip.getBytes(), inetAddress.getAddress());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+    @Test
+    public void testOfByteArray() throws UnknownHostException {
+        for(int i=0; i < testStrings.length; i++ ) {
+            byte[] bytes = Inet6Address.getByName(testStrings[i]).getAddress();
+            IPv6Address ip = IPv6Address.of(bytes);
+            assertEquals(testStrings[i], ip.toString());
+            assertArrayEquals(bytes, ip.getBytes());
+        }
+    }
+
+    @Test
+    public void testReadFrom() throws OFParseError, UnknownHostException {
+        for(int i=0; i < testStrings.length; i++ ) {
+            byte[] bytes = Inet6Address.getByName(testStrings[i]).getAddress();
+            IPv6Address ip = IPv6Address.read16Bytes(ChannelBuffers.copiedBuffer(bytes));
+            assertEquals(testStrings[i], ip.toString());
+            assertArrayEquals(bytes, ip.getBytes());
+        }
+    }
+
+    String[] invalidIPs = {
+            "",
+            ":",
+            "1:2:3:4:5:6:7:8:9",
+            "1:2:3:4:5:6:7:8:",
+            "1:2:3:4:5:6:7:8g",
+            "1:2:3:",
+            "12345::",
+            "1::3::8",
+            "::3::"
+    };
+
+    @Test
+    public void testInvalidIPs() throws OFParseError {
+        for(String invalid : invalidIPs) {
+            try {
+                IPv6Address.of(invalid);
+                fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    @Test
+    public void testZeroCompression() throws OFParseError {
+        assertEquals("::", IPv6Address.of("::").toString(true, false));
+        assertEquals("0:0:0:0:0:0:0:0", IPv6Address.of("::").toString(false, false));
+        assertEquals("0000:0000:0000:0000:0000:0000:0000:0000", IPv6Address.of("::").toString(false, true));
+        assertEquals("1::4:5:6:0:8", IPv6Address.of("1:0:0:4:5:6:0:8").toString(true, false));
+        assertEquals("1:0:0:4::8", IPv6Address.of("1:0:0:4:0:0:0:8").toString(true, false));
+    }
+
+    @Test
+    public void testSuperclass() throws Exception {
+        for(String ipString: testStrings) {
+            IPAddress<?> superIp = IPAddress.of(ipString);
+            assertEquals(IPVersion.IPv6, superIp.getIpVersion());
+            assertEquals(IPv6Address.of(ipString), superIp);
+        }
+
+        for(WithMaskTaskCase w: withMasks) {
+            String ipMaskedString = w.input;
+            IPAddressWithMask<?> superIp = IPAddressWithMask.of(ipMaskedString);
+            assertEquals(IPVersion.IPv6, superIp.getIpVersion());
+            assertEquals(IPv6AddressWithMask.of(ipMaskedString), superIp);
+        }
+    }
+
+    @Test
+    public void testOfExceptions() throws Exception {
+        try {
+            IPv6AddressWithMask.of(null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            String s = null;
+            IPv6Address.of(s);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = null;
+            IPv6Address.of(b);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            byte[] b = new byte[7];
+            IPv6Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            byte[] b = new byte[9];
+            IPv6Address.of(b);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            IPv6AddressWithMask.of(IPv6Address.of("1::"), null);
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv6AddressWithMask.of(null, IPv6Address.of("255::"));
+            fail("Should have thrown NullPointerException");
+        } catch (NullPointerException e) {
+            assertNotNull(e.getMessage());
+        }
+        try {
+            IPv6AddressWithMask.of(IPv6Address.of("10:10::0"),
+                                   IPv6Address.of("ffff:0:ffff::"))
+                                   .getSubnetBroadcastAddress();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
new file mode 100644
index 0000000..a13fdd4
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
@@ -0,0 +1,154 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+public class MacAddressTest {
+    byte[][] testAddresses = new byte[][] {
+            {0x01, 0x02, 0x03, 0x04, 0x05, 0x06 },
+            {(byte) 0x80, 0x0, 0x0, 0x0, 0x0, 0x01},
+            {(byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 }
+    };
+
+    String[] testStrings = {
+            "01:02:03:04:05:06",
+            "80:00:00:00:00:01",
+            "ff:ff:ff:ff:ff:ff"
+    };
+
+    long[] testInts = {
+            0x00010203040506L,
+            0x00800000000001L,
+            0x00ffffffffffffL
+    };
+
+    String[] invalidMacStrings = {
+            "",
+            "1.2.3.4",
+            "0T:00:01:02:03:04",
+            "00:01:02:03:04:05:06",
+            "00:ff:ef:12:12:ff:",
+            "00:fff:ef:12:12:ff",
+            "01:02:03:04:05;06",
+            "0:1:2:3:4:5:6",
+            "01:02:03:04"
+    };
+
+    byte[][] invalidMacBytes = {
+            new byte[]{0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
+            new byte[]{0x01, 0x01, 0x02, 0x03, 0x04}
+    };
+
+    @Test
+    public void testOfString() {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            MacAddress ip = MacAddress.of(testStrings[i]);
+            assertEquals(testInts[i], ip.getLong());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+    @Test
+    public void testOfByteArray() {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            MacAddress ip = MacAddress.of(testAddresses[i]);
+            assertEquals("error checking long representation of "+Arrays.toString(testAddresses[i]) + "(should be "+Long.toHexString(testInts[i]) +")", testInts[i],  ip.getLong());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+    @Test
+    public void testReadFrom() throws OFParseError {
+        for(int i=0; i < testAddresses.length; i++ ) {
+            MacAddress ip = MacAddress.read6Bytes(ChannelBuffers.copiedBuffer(testAddresses[i]));
+            assertEquals(testInts[i], ip.getLong());
+            assertArrayEquals(testAddresses[i], ip.getBytes());
+            assertEquals(testStrings[i], ip.toString());
+        }
+    }
+
+
+    @Test
+    public void testInvalidMacStrings() throws OFParseError {
+        for(String invalid : invalidMacStrings) {
+            try {
+                MacAddress.of(invalid);
+                fail("Invalid MAC address "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    @Test
+    public void testInvalidMacBytes() throws OFParseError {
+        for(byte[] invalid : invalidMacBytes) {
+            try {
+                MacAddress.of(invalid);
+                fail("Invalid MAC address bytes "+ Arrays.toString(invalid) + " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    //  Test data is imported from org.projectfloodlight.packet.EthernetTest
+    @Test
+    public void testToLong() {
+        assertEquals(
+                281474976710655L,
+                MacAddress.of(new byte[]{(byte) 0xff, (byte) 0xff,
+                        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}).getLong());
+
+        assertEquals(
+                1103823438081L,
+                MacAddress.of(new byte[] { (byte) 0x01, (byte) 0x01,
+                        (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 }).getLong());
+
+        assertEquals(
+                141289400074368L,
+                MacAddress.of(new byte[] { (byte) 0x80, (byte) 0x80,
+                        (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80 }).getLong());
+
+    }
+
+    @Test
+    public void testIsBroadcast() {
+        assertTrue(MacAddress.of("FF:FF:FF:FF:FF:FF").isBroadcast());
+        assertTrue(MacAddress.of(-1).isBroadcast());
+        assertTrue(MacAddress.of(0x05FFFFFFFFFFFFL).isBroadcast());
+        assertFalse(MacAddress.of("11:22:33:44:55:66").isBroadcast());
+    }
+
+    @Test
+    public void testIsMulticast() {
+        assertTrue(MacAddress.of("01:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of("00:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of("FE:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of(-1).isMulticast());
+        assertFalse(MacAddress.of(0x05FFFFFFFFFFFFL).isMulticast());
+        assertFalse(MacAddress.of("FF:FF:FF:FF:FF:FF").isMulticast());
+    }
+
+    @Test
+    public void testIsLLDPAddress() {
+        assertTrue(MacAddress.of("01:80:C2:00:00:00").isLLDPAddress());
+        assertTrue(MacAddress.of("01:80:C2:00:00:0f").isLLDPAddress());
+        assertFalse(MacAddress.of("01:80:C2:00:00:50").isLLDPAddress());
+        assertFalse(MacAddress.of("01:80:C2:00:10:00").isLLDPAddress());
+        assertFalse(MacAddress.of("01:80:C2:40:00:01").isLLDPAddress());
+        assertFalse(MacAddress.of("00:80:C2:f0:00:00").isLLDPAddress());
+        assertFalse(MacAddress.of("FE:80:C2:00:00:00").isLLDPAddress());
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFErrorCauseDataTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFErrorCauseDataTest.java
new file mode 100644
index 0000000..9a5a4dc
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFErrorCauseDataTest.java
@@ -0,0 +1,74 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.Matchers;
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class OFErrorCauseDataTest {
+    @Test
+    public void testEmpty() {
+        OFErrorCauseData emptyCause = OFErrorCauseData.of(new byte[] {}, OFVersion.OF_13);
+        assertThat(emptyCause.getData(), equalTo(new byte[] {}));
+        assertThat(emptyCause.getParsedMessage().isPresent(), equalTo(false));
+        assertThat(emptyCause.toString(), Matchers.containsString("unparsed"));
+    }
+
+    @Test
+    public void testTooShort() {
+        OFErrorCauseData emptyCause = OFErrorCauseData.of(new byte[] {0x1, 0x2}, OFVersion.OF_13);
+        assertThat(emptyCause.getData(), equalTo(new byte[] {0x1, 0x2}));
+        assertThat(emptyCause.getParsedMessage().isPresent(), equalTo(false));
+        assertThat(emptyCause.toString(), Matchers.containsString("unparsed"));
+        assertThat(emptyCause.toString(), Matchers.containsString("01 02"));
+    }
+
+    byte[] truncatedFlowAddd = new byte[] {
+            0x04, 0x0e, // version, type
+            0x00, (byte) 0x80, // length
+            0x12, 0x34, 0x56, 0x78, // xid
+            (byte) 0xfe, (byte) 0xdc , (byte) 0xba, (byte) 0x98, 0x76, 0x54, 0x32, 0x10, // cookie
+            (byte) 0xff, 0x00, (byte) 0xff, 0x00, (byte) 0xff, 0x00, (byte) 0xff, 0x00, // cookie_mask
+            0x03 // table_id
+            // rest truncated
+    };
+
+    @Test
+    public void testTruncated() {
+        OFErrorCauseData emptyCause = OFErrorCauseData.of(truncatedFlowAddd, OFVersion.OF_13);
+        assertThat(emptyCause.getData(), equalTo(truncatedFlowAddd));
+        assertThat(emptyCause.getParsedMessage().isPresent(), equalTo(false));
+        assertThat(emptyCause.toString(), Matchers.containsString("unparsed"));
+        assertThat(emptyCause.toString(), Matchers.containsString("04 0e 00 80"));
+    }
+
+    @Test
+    public void testFlowAdd() {
+        OFFlowAdd flowAdd = OFFactories.getFactory(OFVersion.OF_13).buildFlowAdd()
+        .setXid(0x12345678)
+        .setCookie(U64.parseHex("FEDCBA9876543210"))
+        .setCookieMask(U64.parseHex("FF00FF00FF00FF00"))
+        .setTableId(TableId.of(3))
+        .setIdleTimeout(5)
+        .setHardTimeout(10)
+        .setPriority(6000)
+        .build();
+
+        ChannelBuffer bb = ChannelBuffers.dynamicBuffer();
+        flowAdd.writeTo(bb);
+        byte[] flowAddBytes = new byte[bb.readableBytes()];
+        bb.readBytes(flowAddBytes);
+
+        OFErrorCauseData emptyCause = OFErrorCauseData.of(flowAddBytes, OFVersion.OF_13);
+        assertThat(emptyCause.getData(), equalTo(flowAddBytes));
+        assertThat(emptyCause.getParsedMessage().isPresent(), equalTo(true));
+        assertThat(emptyCause.toString(), Matchers.containsString("OFFlowAdd"));
+        assertThat(emptyCause.toString(), Matchers.containsString("idleTimeout=5"));
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFPortBitMapTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFPortBitMapTest.java
new file mode 100644
index 0000000..4db84f1
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFPortBitMapTest.java
@@ -0,0 +1,71 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertThat;
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class OFPortBitMapTest extends TestCase {
+    @Test
+    public void testCreateAndIterate() {
+        OFPortBitMap map = OFPortBitMap.ofPorts(OFPort.of(1), OFPort.of(2), OFPort.of(5));
+
+        assertThat(map.getOnPorts(), contains(OFPort.of(1), OFPort.of(2), OFPort.of(5)));
+    }
+
+    @Test
+    public void testOFBitMap() {
+        OFBitMask128 bitmap = OFBitMask128.of(0xFFFF_FFFF_FFFF_FFFFL, 0xFFFF_FFFF_FFFF_FFD9L);
+
+        OFPortBitMap map = OFPortBitMap.of(bitmap);
+
+        assertThat(map.getOnPorts(), contains(OFPort.of(1), OFPort.of(2), OFPort.of(5)));
+    }
+
+    @Test
+    public void testOFPortBitMap() {
+        Boolean[] on = new Boolean[127];
+        for (int i = 0; i < 127; i++) {
+            on[i] = false;
+        }
+
+        OFPortBitMap.Builder builder = new OFPortBitMap.Builder();
+
+        for (int i = 0; i < 127; i += 3) {
+            OFPort p = OFPort.of(i);
+            builder.set(p);
+            on[p.getPortNumber()] = true;
+        }
+
+        // Test that all ports that were added are actually on, and all other ports are off
+        OFPortBitMap portmap = builder.build();
+        //System.out.println(portmap);
+        Boolean[] actual = new Boolean[127];
+        for (int i = 0; i < 127; i++) {
+            actual[i] = false;
+        }
+        for (int i = 0; i < 127; i++) {
+            actual[i] = portmap.isOn(OFPort.of(i));
+        }
+        assertArrayEquals(on, actual);
+
+        // Turn some ports off
+        for (int i = 0; i < 127; i += 7) {
+            on[i] = false;
+            builder.unset(OFPort.of(i));
+        }
+
+        // Test again
+        portmap = builder.build();
+        actual = new Boolean[127];
+        for (int i = 0; i < 127; i++) {
+            actual[i] = false;
+        }
+        for (int i = 0; i < 127; i++) {
+            actual[i] = portmap.isOn(OFPort.of(i));
+        }
+        assertArrayEquals(on, actual);
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFVlanVidMatchTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFVlanVidMatchTest.java
new file mode 100644
index 0000000..ce6e7a2
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/OFVlanVidMatchTest.java
@@ -0,0 +1,44 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+public class OFVlanVidMatchTest {
+    @Test
+    public void testofVlanVid() {
+        assertThat(
+                (int) OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(1)).getRawVid(),
+                equalTo(0x1001));
+        assertThat(
+                (int) OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(0xFFF)).getRawVid(),
+                equalTo(0x1FFF));
+        assertThat(OFVlanVidMatch.ofVlanVid(null), equalTo(OFVlanVidMatch.UNTAGGED));
+        assertThat(OFVlanVidMatch.ofVlanVid(VlanVid.NO_MASK),
+                equalTo(OFVlanVidMatch.NO_MASK));
+        // a fully masked VlanVid means "PRESENT" in OFVlanVid
+        // (because a VlanVid always specifies a Vlan)
+        assertThat(OFVlanVidMatch.ofVlanVid(VlanVid.FULL_MASK),
+                equalTo(OFVlanVidMatch.PRESENT));
+    }
+    @Test
+    public void testtoVlanVid() {
+        assertThat(
+                OFVlanVidMatch.ofRawVid((short)0x1001).getVlanVid(),
+                                        equalTo(VlanVid.ofVlan(1)));
+        assertThat(
+                OFVlanVidMatch.ofRawVid((short)0x1FFF).getVlanVid(),
+                                        equalTo(VlanVid.ofVlan(0xFFF)));
+        assertThat(OFVlanVidMatch.UNTAGGED.getVlanVid(), CoreMatchers.nullValue());
+        assertThat(
+                OFVlanVidMatch.NO_MASK.getVlanVid(),
+                                        equalTo(VlanVid.NO_MASK));
+        assertThat(
+                OFVlanVidMatch.PRESENT.getVlanVid(),
+                                        equalTo(VlanVid.FULL_MASK));
+    }
+
+
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/U128Test.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/U128Test.java
new file mode 100644
index 0000000..fb5cd23
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/U128Test.java
@@ -0,0 +1,182 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+
+public class U128Test {
+    @Test
+    public void testPositiveRaws() {
+        assertThat(U128.of(0, 0).getMsb(), equalTo(0L));
+        assertThat(U128.of(0, 0).getLsb(), equalTo(0L));
+
+        assertThat(U128.of(1, 2).getMsb(), equalTo(1L));
+        assertThat(U128.of(1, 2).getLsb(), equalTo(2L));
+    }
+
+    @Test
+    public void testPutTo() {
+        U128 h         = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+        U128 hSame     = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+
+        U128 hBothDiff = U128.of(0x1234_5678_90ab_cdefL,0x1234_5678_90ab_cdefL);
+        U128 hMsbDiff  = U128.of(0x0234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+        U128 hLsbDiff  = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeeeL);
+
+        assertThat(hash(h), equalTo(hash(hSame)));
+        assertThat(hash(h), not(hash(hBothDiff)));
+        assertThat(hash(h), not(hash(hMsbDiff)));
+        assertThat(hash(h), not(hash(hLsbDiff)));
+    }
+
+    private HashCode hash(U128 f) {
+        Hasher hash = Hashing.murmur3_128().newHasher();
+        f.putTo(hash);
+        return hash.hash();
+
+    }
+
+    @Test
+    public void testEqualHashCode() {
+        U128 h1 = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h2 = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h3 = U128.of(0xeeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h3_2 = U128.of(0xdeafbeefdeadbeefL, 0xeeafbeefdeadbeefL);
+
+        assertTrue(h1.equals(h1));
+        assertTrue(h1.equals(h2));
+        assertFalse(h1.equals(h3));
+        assertFalse(h1.equals(h3_2));
+        assertTrue(h2.equals(h1));
+
+        assertEquals(h1.hashCode(), h2.hashCode());
+        assertNotEquals(h1.hashCode(), h3.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+        assertNotEquals(h1.hashCode(), h3_2.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+    }
+
+    @Test
+    public void testXor() {
+        U128 hNull = U128.of(0, 0);
+        U128 hDeadBeef = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        assertThat(hNull.xor(hNull), equalTo(hNull));
+        assertThat(hNull.xor(hDeadBeef), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hNull), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hDeadBeef), equalTo(hNull));
+
+
+        U128 h1_0 = U128.of(1L, 0);
+        U128 h8_0 = U128.of(0x8000000000000000L, 0);
+        U128 h81_0 = U128.of(0x8000000000000001L, 0);
+        assertThat(h1_0.xor(h8_0), equalTo(h81_0));
+
+        U128 h0_1 = U128.of(0, 1L);
+        U128 h0_8 = U128.of(0, 0x8000000000000000L);
+        U128 h0_81 = U128.of(0, 0x8000000000000001L);
+        assertThat(h0_1.xor(h0_8), equalTo(h0_81));
+    }
+
+    @Test
+    public void testKeyBits() {
+        U128 zeroU = U128.of(0,0);
+        assertThat(zeroU.prefixBits(0), equalTo(0));
+        assertThat(zeroU.prefixBits(16), equalTo(0));
+        assertThat(zeroU.prefixBits(32), equalTo(0));
+
+        checkInvalidKeyBitSize(zeroU, 33);
+        checkInvalidKeyBitSize(zeroU, 64);
+        assertThat(zeroU.prefixBits(3), equalTo(0));
+
+        U128 positiveU = U128.of(0x1234_5678_1234_5678L, 0x1234_5678_1234_5678L);
+        assertThat(positiveU.prefixBits(0), equalTo(0));
+        assertThat(positiveU.prefixBits(16), equalTo(0x1234));
+        assertThat(positiveU.prefixBits(32), equalTo(0x12345678));
+        checkInvalidKeyBitSize(positiveU, 33);
+        checkInvalidKeyBitSize(positiveU, 64);
+
+        U128 signedBitU = U128.of(0x8765_4321_8765_4321L, 0x1234_5678_1234_5678L);
+        assertThat(signedBitU.prefixBits(0), equalTo(0));
+        assertThat(signedBitU.prefixBits(16), equalTo(0x8765));
+        assertThat(signedBitU.prefixBits(32), equalTo(0x8765_4321));
+        checkInvalidKeyBitSize(signedBitU, 33);
+        checkInvalidKeyBitSize(signedBitU, 64);
+    }
+
+    private void
+    checkInvalidKeyBitSize(U128 u, int prefixBit) {
+        try {
+            u.prefixBits(prefixBit);
+            fail("Expected exception not thrown for "+prefixBit + " bits");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+
+    @Test
+    public void testCompare() {
+        U128 u0_0 = U128.of(0, 0);
+        U128 u0_1 = U128.of(0, 1);
+        U128 u0_8 = U128.of(0, 0x8765_4321_8765_4321L);
+        U128 u1_0 = U128.of(0x1234_5678_1234_5678L, 0);
+        U128 u8_0 = U128.of(0x8765_4321_8765_4321L, 0);
+        U128 uf_0 = U128.of(0xFFFF_FFFF_FFFF_FFFFL, 0);
+
+        U128[] us = new U128[] { u0_0, u0_1, u0_8, u1_0, u8_0, uf_0 };
+
+        for(int i = 0; i< us.length; i++) {
+            U128 u_base = us[i];
+            assertThat(
+                    String.format("%s should be equal to itself (compareTo)", u_base),
+                    u_base.compareTo(u_base), equalTo(0));
+            assertThat(
+                    String.format("%s should be equal to itself (equals)", u_base),
+                    u_base.equals(u_base), equalTo(true));
+            assertThat(
+                    String.format("%s should be equal to itself (equals, by value)", u_base),
+                    u_base.equals(U128.of(u_base.getMsb(), u_base.getLsb())), equalTo(true));
+
+            for(int j = i+1; j< us.length; j++) {
+                U128 u_greater = us[j];
+                assertThat(
+                        String.format("%s should not be equal to %s", u_base, u_greater),
+                        u_base.equals(u_base), equalTo(true));
+                assertThat(
+                        String.format("%s should be smaller than %s", u_base, u_greater),
+                        u_base.compareTo(u_greater), Matchers.lessThan(0));
+                assertThat(
+                        String.format("%s should be greater than %s", u_greater, u_base),
+                        u_greater.compareTo(u_base), Matchers.greaterThan(0));
+            }
+        }
+    }
+
+    @Test
+    public void testCombine() {
+        long key = 0x1234567890abcdefL;
+        long val = 0xdeafbeefdeadbeefL;
+        U128 hkey = U128.of(key, key*2);
+        U128 hVal = U128.of(val, val/2);
+
+        assertThat(hkey.combineWithValue(hVal, 0), equalTo(hkey.xor(hVal)));
+        assertThat(hkey.combineWithValue(hVal, 64), equalTo(U128.of(hkey.getMsb(), hkey.getLsb() ^ hVal.getLsb())));
+        assertThat(hkey.combineWithValue(hVal, 128), equalTo(hkey));
+
+        long mask8 = 0xFF00_0000_0000_0000L;
+
+        assertThat(hkey.combineWithValue(hVal, 8), equalTo(U128.of(hkey.getMsb() & mask8 |  hkey.getMsb() ^ hVal.getMsb() & ~mask8,
+                                                                   hkey.getLsb() ^ hVal.getLsb() )));
+    }
+
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/types/U64Test.java b/of/lib/src/test/java/org/projectfloodlight/openflow/types/U64Test.java
new file mode 100644
index 0000000..b0cca23
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/types/U64Test.java
@@ -0,0 +1,118 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+public class U64Test {
+
+    @Test
+    public void testPositiveRaws() {
+        for(long positive: new long[] { 0, 1, 100, Long.MAX_VALUE }) {
+            assertEquals(positive, U64.ofRaw(positive).getValue());
+            assertEquals(BigInteger.valueOf(positive), U64.ofRaw(positive).getBigInteger());
+        }
+    }
+
+    @Test
+    public void testNegativeRaws() {
+        long minus_1 = 0xFFFF_FFFF_FFFF_FFFFL;
+        assertEquals(minus_1, U64.ofRaw(minus_1).getValue());
+        assertEquals(new BigInteger("FFFF_FFFF_FFFF_FFFF".replace("_", ""), 16),  U64.ofRaw(minus_1).getBigInteger());
+        assertEquals(new BigInteger("18446744073709551615"),  U64.ofRaw(minus_1).getBigInteger());
+    }
+
+    @Test
+    public void testEqualHashCode() {
+        U64 h1 = U64.of(0xdeafbeefdeadbeefL);
+        U64 h2 = U64.of(0xdeafbeefdeadbeefL);
+        U64 h3 = U64.of(0xeeafbeefdeadbeefL);
+
+        assertTrue(h1.equals(h1));
+        assertTrue(h1.equals(h2));
+        assertFalse(h1.equals(h3));
+        assertTrue(h2.equals(h1));
+
+        assertEquals(h1.hashCode(), h2.hashCode());
+        assertNotEquals(h1.hashCode(), h3.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+    }
+
+    @Test
+    public void testXor() {
+        U64 hNull = U64.of(0);
+        U64 hDeadBeef = U64.of(0xdeafbeefdeadbeefL);
+        assertThat(hNull.xor(hNull), equalTo(hNull));
+        assertThat(hNull.xor(hDeadBeef), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hNull), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hDeadBeef), equalTo(hNull));
+
+
+        U64 h1 = U64.of(1L);
+        U64 h8 = U64.of(0x8000000000000000L);
+        U64 h81 = U64.of(0x8000000000000001L);
+        assertThat(h1.xor(h8), equalTo(h81));
+    }
+
+    @Test
+    public void testCombine() {
+        long key = 0x1234567890abcdefL;
+        long val = 0xdeafbeefdeadbeefL;
+        U64 hkey = U64.of(key);
+        U64 hVal = U64.of(val);
+
+        assertThat(hkey.combineWithValue(hVal, 0), equalTo(hkey.xor(hVal)));
+        assertThat(hkey.combineWithValue(hVal, 64), equalTo(hkey));
+        long mask32 = 0x00000000FFFFFFFFL;
+        assertThat(hkey.combineWithValue(hVal, 32),
+                equalTo(U64.of(key & ~mask32| (key ^ val) & mask32)));
+
+        long tenMask = 0x003FFFFFFFFFFFFFL;
+        assertThat(hkey.combineWithValue(hVal, 10),
+                equalTo(U64.of(key & ~tenMask | (key ^ val) & tenMask)));
+    }
+
+    @Test
+    public void testKeyBits() {
+        U64 zeroU = U64.of(0);
+        assertThat(zeroU.prefixBits(0), equalTo(0));
+        assertThat(zeroU.prefixBits(16), equalTo(0));
+        assertThat(zeroU.prefixBits(32), equalTo(0));
+
+        checkInvalidKeyBitSize(zeroU, 33);
+        checkInvalidKeyBitSize(zeroU, 64);
+        assertThat(zeroU.prefixBits(3), equalTo(0));
+
+        U64 positiveU = U64.of(0x1234_5678_1234_5678L);
+        assertThat(positiveU.prefixBits(0), equalTo(0));
+        assertThat(positiveU.prefixBits(16), equalTo(0x1234));
+        assertThat(positiveU.prefixBits(32), equalTo(0x12345678));
+        checkInvalidKeyBitSize(positiveU, 33);
+        checkInvalidKeyBitSize(positiveU, 64);
+
+        U64 signedBitU = U64.of(0x8765_4321_8765_4321L);
+        assertThat(signedBitU.prefixBits(0), equalTo(0));
+        assertThat(signedBitU.prefixBits(16), equalTo(0x8765));
+        assertThat(signedBitU.prefixBits(32), equalTo(0x8765_4321));
+        checkInvalidKeyBitSize(signedBitU, 33);
+        checkInvalidKeyBitSize(signedBitU, 64);
+    }
+
+    private void
+            checkInvalidKeyBitSize(U64 u, int prefixBit) {
+        try {
+            u.prefixBits(prefixBit);
+            fail("Expected exception not thrown for "+prefixBit + " bits");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java
new file mode 100644
index 0000000..360cb5a6
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java
@@ -0,0 +1,103 @@
+/**
+*    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
+*    University
+*
+*    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.projectfloodlight.openflow.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/**
+ * Does hexstring conversion work?
+ *
+ * @author Rob Sherwood (rob.sherwood@stanford.edu)
+ */
+public class HexStringTest {
+
+    @Test
+    public void testMarshalling() throws Exception {
+        String dpidStr = "00:00:00:23:20:2d:16:71";
+        long dpid = HexString.toLong(dpidStr);
+        String testStr = HexString.toHexString(dpid);
+        assertEquals(dpidStr, testStr);
+    }
+
+    @Test
+    public void testToLong() {
+        String dpidStr = "3e:1f:01:fc:72:8c:63:31";
+        long valid = 0x3e1f01fc728c6331L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test
+    public void testToLong2() {
+        String dpidStr = "1f:1:fc:72:3:f:31";
+        long valid = 0x1f01fc72030f31L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test
+    public void testToLongMSB() {
+        String dpidStr = "ca:7c:5e:d1:64:7a:95:9b";
+        long valid = -3856102927509056101L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorTooManyBytes() {
+        HexString.toLong("09:08:07:06:05:04:03:02:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorByteValueTooLong() {
+        HexString.toLong("234:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorEmptyByte() {
+        HexString.toLong("03::01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorInvalidHexDigit() {
+        HexString.toLong("ss:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorEmptyString() {
+        HexString.toLong("");
+    }
+
+
+    @Test
+    public void testToStringBytes() {
+        byte[] dpid = { 0, 0, 0, 0, 0, 0, 0, -1 };
+        String valid = "00:00:00:00:00:00:00:ff";
+        String testString = HexString.toHexString(dpid);
+        assertEquals(valid, testString);
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testFromHexStringError() {
+        String invalidStr = "00:00:00:00:00:00:ffff";
+        HexString.fromHexString(invalidStr);
+    }
+}
+
diff --git a/of/lib/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java b/of/lib/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java
new file mode 100644
index 0000000..f5bf3e4
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java
@@ -0,0 +1,130 @@
+package org.projectfloodlight.openflow.util;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.PrimitiveSinkable;
+
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+
+public class PrimitiveSinkUtilsTest {
+
+    private HashFunction hash;
+
+    @Before
+    public void setup() {
+        hash = Hashing.murmur3_128();
+    }
+
+    @Test
+    public void testPutNullableString() {
+        // test that these different invocations of putNullable
+        // differ pairwise
+        HashCode[] hs = new HashCode[] {
+                calcPutNullableString((String) null),
+                calcPutNullableString(""),
+                calcPutNullableString(null, null),
+                calcPutNullableString(null, ""),
+                calcPutNullableString("", null),
+                calcPutNullableString("a\0a", null),
+                calcPutNullableString(null, "a\0a"),
+        };
+
+        checkPairwiseDifferent(hs);
+    }
+
+    @Test
+    public void testPutNullable() {
+        // test that these different invocations of putNullable
+        // differ pairwise
+        HashCode[] hs = new HashCode[] {
+                calcPutNullables(),
+                calcPutNullables(OFPort.of(1)),
+                calcPutNullables(OFPort.of(1), null),
+                calcPutNullables(OFPort.of(1), null, null),
+                calcPutNullables(null, OFPort.of(1), null),
+                calcPutNullables(null, null, OFPort.of(1))
+        };
+
+        checkPairwiseDifferent(hs);
+    }
+
+    private void checkPairwiseDifferent(HashCode[] hs) {
+        for(int i=0;i<hs.length;i++) {
+            for(int j=i+1; j<hs.length;j++) {
+                assertThat(hs[i], not(hs[j]));
+            }
+        }
+    }
+
+    @Test
+    public void testPutList() {
+        HashCode[] hs = new HashCode[] {
+                calcPutList(),
+                calcPutList(OFPort.of(1)),
+                calcPutList(OFPort.of(2)),
+                calcPutList(OFPort.of(1), OFPort.of(2)),
+                calcPutList(OFPort.of(2), OFPort.of(1)),
+                calcPutList(OFPort.of(1), OFPort.of(3)),
+                calcPutList(OFPort.of(1), OFPort.of(2), OFPort.of(3)),
+        };
+
+        checkPairwiseDifferent(hs);
+    }
+
+    @Test
+    public void testPutSortedSet() {
+        HashCode[] hs = new HashCode[] {
+                calcPutSortedSet(),
+                calcPutSortedSet(OFPort.of(1)),
+                calcPutSortedSet(OFPort.of(2)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(2)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(3)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(2), OFPort.of(3)),
+        };
+
+        checkPairwiseDifferent(hs);
+
+        assertThat(calcPutSortedSet(OFPort.of(1), OFPort.of(2)),
+                equalTo(calcPutSortedSet(OFPort.of(2), OFPort.of(1))));
+    }
+
+    private HashCode calcPutNullableString(String... strings) {
+        Hasher h = hash.newHasher();
+        for(String s: strings) {
+            PrimitiveSinkUtils.putNullableStringTo(h, s);
+        }
+        return h.hash();
+    }
+
+    private HashCode calcPutSortedSet(OFPort... ports) {
+        Hasher h = hash.newHasher();
+        PrimitiveSinkUtils.putSortedSetTo(h, ImmutableSortedSet.copyOf(ports));
+        return h.hash();
+    }
+
+    private HashCode calcPutList(OFPort... ports) {
+        Hasher h = hash.newHasher();
+        PrimitiveSinkUtils.putListTo(h, Arrays.asList(ports));
+        return h.hash();
+    }
+
+
+    private HashCode calcPutNullables(PrimitiveSinkable... ps) {
+        Hasher h = hash.newHasher();
+        for(PrimitiveSinkable p : ps) {
+            PrimitiveSinkUtils.putNullableTo(h, p);
+        }
+        return h.hash();
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java b/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java
new file mode 100644
index 0000000..39e8c0c
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmListTest.java
@@ -0,0 +1,41 @@
+package org.projectfloodlight.protocol;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFOxmList;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6DstMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6SrcMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
+import org.projectfloodlight.openflow.types.IPv6AddressWithMask;
+
+public class OFOxmListTest {
+    private OFOxms oxms;
+
+    @Before
+    public void setup() {
+        oxms = OFFactories.getFactory(OFVersion.OF_13).oxms();
+    }
+
+    @Test
+    public void testCanonicalize() {
+        OFOxmList.Builder builder = new OFOxmList.Builder();
+        IPv6AddressWithMask fullMasked = IPv6AddressWithMask.of("::/0");
+        OFOxmIpv6DstMasked  fullMaskedOxm = oxms.ipv6DstMasked(fullMasked.getValue(), fullMasked.getMask());
+        builder.set(fullMaskedOxm);
+
+        IPv6AddressWithMask address= IPv6AddressWithMask.of("1:2:3:4:5:6::8");
+        OFOxmIpv6SrcMasked  addressSrcOxm = oxms.ipv6SrcMasked(address.getValue(), address.getMask());
+        builder.set(addressSrcOxm);
+
+        OFOxmList list = builder.build();
+        assertThat(list.get(MatchField.IPV6_DST), CoreMatchers.nullValue());
+        assertFalse(list.get(MatchField.IPV6_SRC).isMasked());
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java b/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
new file mode 100644
index 0000000..8482886
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
@@ -0,0 +1,62 @@
+package org.projectfloodlight.protocol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4SrcMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv4AddressWithMask;
+
+public class OFOxmTest {
+    private OFOxms oxms;
+
+    @Before
+    public void setup() {
+        oxms = OFFactories.getFactory(OFVersion.OF_13).oxms();
+    }
+
+    @Test
+    public void testGetCanonicalFullMask() {
+        IPv4AddressWithMask empty = IPv4AddressWithMask.of("0.0.0.0/0");
+        assertEquals(IPv4Address.FULL_MASK, empty.getMask());
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(empty.getValue(), empty.getMask());
+        // canonicalize should remove /0
+        assertNull(ipv4SrcMasked.getCanonical());
+    }
+
+    @Test
+    public void testGetCanonicalNoMask() {
+        IPv4AddressWithMask fullIp = IPv4AddressWithMask.of("1.2.3.4/32");
+        assertEquals(IPv4Address.NO_MASK, fullIp.getMask());
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(fullIp.getValue(), fullIp.getMask());
+        assertTrue(ipv4SrcMasked.isMasked());
+        assertEquals(IPv4Address.NO_MASK, ipv4SrcMasked.getMask());
+
+        // canonicalize should convert the masked oxm to the non-masked one
+        OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+        assertThat(canonical, CoreMatchers.instanceOf(OFOxmIpv4Src.class));
+        assertFalse(canonical.isMasked());
+    }
+
+    @Test
+    public void testGetCanonicalNormalMask() {
+        IPv4AddressWithMask ip = IPv4AddressWithMask.of("1.2.3.0/24");
+        OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(ip.getValue(), ip.getMask());
+        assertTrue(ipv4SrcMasked.isMasked());
+
+        // canonicalize should convert the masked oxm to the non-masked one
+        OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+        assertEquals(ipv4SrcMasked, canonical);
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
new file mode 100644
index 0000000..c6f4471
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration10Test extends MatchFieldIterationBase {
+    public MatchFieldIteration10Test() {
+        super(OFFactories.getFactory(OFVersion.OF_10));
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
new file mode 100644
index 0000000..b654a53
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration13Test extends MatchFieldIterationBase {
+    public MatchFieldIteration13Test() {
+        super(OFFactories.getFactory(OFVersion.OF_13));
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
new file mode 100644
index 0000000..9c72e37
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
@@ -0,0 +1,249 @@
+package org.projectfloodlight.protocol.match;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.types.ArpOpcode;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.Masked;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TransportPort;
+
+import com.google.common.collect.Iterables;
+
+public class MatchFieldIterationBase {
+
+    private OFFactory factory;
+
+    protected MatchFieldIterationBase(OFFactory factory) {
+        this.factory = factory;
+    }
+    
+    @Test
+    public void iterateEmptyMatch() {
+        Match match = factory.buildMatch().build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @Test
+    public void iterateSingleExactMatchField() {
+        OFPort port5 = OFPort.of(5);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(true));
+        MatchField<?> matchField = iter.next();
+        assertThat(matchField.id, is(MatchFields.IN_PORT));
+        assertThat(match.isExact(matchField), is(true));
+        @SuppressWarnings("unchecked")
+        MatchField<OFPort> portMatchField = (MatchField<OFPort>) matchField;
+        OFPort port = match.get(portMatchField);
+        assertThat(port, is(port5));
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateExactMatchFields() {
+        OFPort port5 = OFPort.of(5);
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        TransportPort tcpSrc = TransportPort.of(100);
+        TransportPort tcpDst = TransportPort.of(200);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setExact(MatchField.ETH_SRC, macSrc)
+                .setExact(MatchField.ETH_DST, macDst)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                .setExact(MatchField.IPV4_SRC, ipSrc)
+                .setExact(MatchField.IPV4_DST, ipDst)
+                .setExact(MatchField.TCP_SRC, tcpSrc)
+                .setExact(MatchField.TCP_DST, tcpDst)
+                .build();
+        assertThat(Iterables.size(match.getMatchFields()), is(9));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case IN_PORT:
+                OFPort port = match.get((MatchField<OFPort>) matchField);
+                assertThat(port, is(port5));
+                break;
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ETH_DST:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case IPV4_DST:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            case TCP_SRC:
+                TransportPort tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpSrc));
+                break;
+            case TCP_DST:
+                tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateArpFields() {
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        OFVersion version = factory.getVersion();
+        boolean supportsArpHardwareAddress = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch();
+        builder.setExact(MatchField.ETH_TYPE, EthType.ARP)
+                .setExact(MatchField.ARP_OP, ArpOpcode.REPLY)
+                .setExact(MatchField.ARP_SPA, ipSrc)
+                .setExact(MatchField.ARP_TPA, ipDst);
+        if (supportsArpHardwareAddress) {
+            builder.setExact(MatchField.ARP_SHA, macSrc);
+            builder.setExact(MatchField.ARP_THA, macDst);
+            matchFieldCount += 2;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.ARP));
+                break;
+            case ARP_OP:
+                ArpOpcode opcode = match.get((MatchField<ArpOpcode>) matchField);
+                assertThat(opcode, is(ArpOpcode.REPLY));
+                break;
+            case ARP_SHA:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ARP_THA:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case ARP_SPA:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case ARP_TPA:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateMaskedFields() {
+        MacAddress macSrc = MacAddress.of("01:02:03:04:00:00");
+        MacAddress macSrcMask = MacAddress.of("FF:FF:FF:FF:00:00");
+        MacAddress macDst = MacAddress.of("11:22:33:00:00:00");
+        MacAddress macDstMask = MacAddress.of("FF:FF:FF:00:00:00");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.0");
+        IPv4Address ipSrcMask = IPv4Address.of("255.255.255.0");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.0");
+        IPv4Address ipDstMask = IPv4Address.of("255.255.255.128");
+        TransportPort tcpSrcMask = TransportPort.of(0x01F0);
+        OFVersion version = factory.getVersion();
+        boolean supportsAllMasks = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch()
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setMasked(MatchField.IPV4_SRC, ipSrc, ipSrcMask)
+                .setMasked(MatchField.IPV4_DST, ipDst, ipDstMask)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP);
+        if (supportsAllMasks) {
+            builder.setMasked(MatchField.ETH_SRC, macSrc, macSrcMask);
+            builder.setMasked(MatchField.ETH_DST, macDst, macDstMask);
+            builder.setMasked(MatchField.TCP_SRC, tcpSrcMask, tcpSrcMask);
+            matchFieldCount += 3;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                Masked<MacAddress> mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macSrc));
+                assertThat(mac.getMask(), is(macSrcMask));
+                break;
+            case ETH_DST:
+                mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macDst));
+                assertThat(mac.getMask(), is(macDstMask));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                Masked<IPv4Address> ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipSrc));
+                assertThat(ip.getMask(), is(ipSrcMask));
+                break;
+            case IPV4_DST:
+                ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipDst));
+                assertThat(ip.getMask(), is(ipDstMask));
+                break;
+            case TCP_SRC:
+                Masked<TransportPort> tcp = match.getMasked((MatchField<TransportPort>) matchField);
+                assertThat(tcp.getValue(), is(tcpSrcMask));
+                assertThat(tcp.getMask(), is(tcpSrcMask));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+}
diff --git a/of/lib/src/test/java/org/projectfloodlight/test/TestUtils.java b/of/lib/src/test/java/org/projectfloodlight/test/TestUtils.java
new file mode 100644
index 0000000..7a5b8b0
--- /dev/null
+++ b/of/lib/src/test/java/org/projectfloodlight/test/TestUtils.java
@@ -0,0 +1,62 @@
+package org.projectfloodlight.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Bytes;
+
+public class TestUtils {
+     private TestUtils() {}
+
+     private static final int PER_LINE = 8;
+
+     public static void betterAssertArrayEquals(byte[] expected, byte[] got) {
+         int maxlen = Math.max(expected.length, got.length);
+
+         List<String> expectedList = formatHex(Bytes.asList(expected));
+         List<String> gotList = formatHex(Bytes.asList(got));
+
+         boolean fail = false;
+         for (int i = 0; i < maxlen;i+= PER_LINE) {
+             int maxThisLine = Math.min(maxlen, PER_LINE);
+             boolean print = false;
+
+             ArrayList<String> changeMarkers = new ArrayList<String>();
+
+             for (int j = i; j < maxThisLine; j++) {
+                 if (j >= expected.length || j >= got.length  || expected[j] != got[j]) {
+                     print = true;
+                     fail = true;
+                     changeMarkers.add("==");
+                     break;
+                 } else {
+                     changeMarkers.add("  ");
+                 }
+             }
+             if(print) {
+                System.out.println(String.format("%4x: %s", i, Joiner.on(" ").join(expectedList.subList(i, Math.min(expectedList.size(), i+PER_LINE)))));
+                System.out.println(String.format("%4x: %s", i, Joiner.on(" ").join(gotList.subList(i, Math.min(gotList.size(), i+PER_LINE)))));
+                System.out.println(String.format("%4s  %s", "", Joiner.on(" ").join(changeMarkers)));
+                System.out.println("\n");
+             }
+         }
+         if(fail) {
+             Assert.fail("Array comparison failed");
+         }
+
+     }
+
+     private static List<String> formatHex(List<Byte> b) {
+         return Lists.transform(b, new Function<Byte, String>() {
+             @Override
+             public String apply(Byte input) {
+                 return String.format("%02x", input);
+             }
+         });
+     }
+}
\ No newline at end of file
diff --git a/of/lib/src/test/resources/logback-test.xml b/of/lib/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..e759962
--- /dev/null
+++ b/of/lib/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<configuration scan="true">
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} %level [%logger{20}:%thread] %msg%n</pattern>
+    </encoder>
+  </appender>
+  <root level="INFO">
+    <appender-ref ref="STDOUT" />
+  </root>
+  <logger name="org" level="WARN"/>
+  <logger name="LogService" level="WARN"/> <!-- Restlet access logging -->
+  <logger name="org.projectfloodlight.openflow" level="DEBUG"/>
+</configuration>