Merge branch 'master' into funnel

Conflicts:
	java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/DatapathId.java
diff --git a/c_gen/c_match.py b/c_gen/c_match.py
index e15688e..3dff5a5 100644
--- a/c_gen/c_match.py
+++ b/c_gen/c_match.py
@@ -602,8 +602,8 @@
 
     /* For each active member, add an OXM entry to the list */
 """)
-    # @fixme Would like to generate the list in some reasonable order
-    for key, entry in match.of_match_members.items():
+    for key in match.match_keys_sorted:
+        entry = match.of_match_members[key]
         out.write("""\
     if (OF_MATCH_MASK_%(ku)s_ACTIVE_TEST(src)) {
         if (!OF_MATCH_MASK_%(ku)s_EXACT_TEST(src)) {
diff --git a/java_gen/codegen.py b/java_gen/codegen.py
index 776b8c5..4369b30 100644
--- a/java_gen/codegen.py
+++ b/java_gen/codegen.py
@@ -44,6 +44,8 @@
 import java_gen.java_model as java_model
 
 def gen_all_java(out, name):
+    # close the virtual file - we don't need it
+    out.close()
     basedir= '%s/openflowj' % of_g.options.install_dir
     print "Outputting to %s" % basedir
     if os.path.exists(basedir):
@@ -56,7 +58,6 @@
     gen.create_of_const_enums()
     gen.create_of_factories()
 
-    out.close()
 
 
 class JavaGenerator(object):
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/DatapathId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/DatapathId.java
index 4d64f83..42f5c1c 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/DatapathId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/DatapathId.java
@@ -3,6 +3,7 @@
 import org.projectfloodlight.openflow.util.HexString;
 
 import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.UnsignedLongs;
 
 /**
  * Abstraction of a datapath ID that can be set and/or accessed as either a
@@ -10,7 +11,7 @@
  *
  * @author Rob Vaterlaus <rob.vaterlaus@bigswitch.com>
  */
-public class DatapathId implements PrimitiveSinkable {
+public class DatapathId implements PrimitiveSinkable, Comparable<DatapathId> {
 
     public static final DatapathId NONE = new DatapathId(0);
 
@@ -67,4 +68,8 @@
     public void putTo(PrimitiveSink sink) {
         sink.putLong(rawValue);
     }
+
+    public int compareTo(DatapathId o) {
+        return UnsignedLongs.compare(rawValue, o.rawValue);
+    }
 }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
index 8b2bc53..aaa31eb 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
@@ -20,6 +20,9 @@
     private final static long NONE_VAL = 0x0L;
     public static final MacAddress NONE = new MacAddress(NONE_VAL);
 
+    private final static long BROADCAST_VAL = 0x0000FFFFFFFFFFFFL;
+    public static final MacAddress BROADCAST = new MacAddress(BROADCAST_VAL);
+
     public static final MacAddress NO_MASK = MacAddress.of(0xFFFFFFFFFFFFFFFFl);
     public static final MacAddress FULL_MASK = MacAddress.of(0x0);
 
@@ -28,6 +31,9 @@
     }
 
     public static MacAddress of(final byte[] address) {
+        if (address.length != MacAddrLen)
+            throw new IllegalArgumentException(
+                    "Mac address byte array must be exactly 6 bytes long; length = " + address.length);
         long raw =
                 (address[0] & 0xFFL) << 40 | (address[1] & 0xFFL) << 32
                         | (address[2] & 0xFFL) << 24 | (address[3] & 0xFFL) << 16
@@ -35,30 +41,36 @@
         return MacAddress.of(raw);
     }
 
-    public static MacAddress of(final long raw) {
+    public static MacAddress of(long raw) {
+        raw &= BROADCAST_VAL;
         if(raw == NONE_VAL)
             return NONE;
-
+        if (raw == BROADCAST_VAL)
+            return BROADCAST;
         return new MacAddress(raw);
     }
 
     public static MacAddress of(final String string) {
         int index = 0;
         int shift = 40;
+        final String FORMAT_ERROR = "Mac address is not well-formed. " +
+                "It must consist of 6 hex digit pairs separated by colons: ";
 
         long raw = 0;
         if (string.length() != 6 * 2 + 5)
-            throw new IllegalArgumentException("Mac address not well formed: " + string);
+            throw new IllegalArgumentException(FORMAT_ERROR + string);
 
         while (shift >= 0) {
-            raw |=
-                    ((long) (Character.digit(string.charAt(index++), 16) << 4 | Character
-                            .digit(string.charAt(index++), 16))) << shift;
+            int digit1 = Character.digit(string.charAt(index++), 16);
+            int digit2 = Character.digit(string.charAt(index++), 16);
+            if ((digit1 < 0) || (digit2 < 0))
+                throw new IllegalArgumentException(FORMAT_ERROR + string);
+            raw |= ((long) (digit1 << 4 | digit2)) << shift;
 
             if (shift == 0)
                 break;
             if (string.charAt(index++) != ':')
-                throw new IllegalArgumentException("Mac address not well formed: " + string);
+                throw new IllegalArgumentException(FORMAT_ERROR + string);
             shift -= 8;
         }
         return MacAddress.of(raw);
@@ -83,6 +95,25 @@
         return bytesCache;
     }
 
+    /**
+     * Returns {@code true} if the MAC address is the broadcast address.
+     * @return {@code true} if the MAC address is the broadcast address.
+     */
+    public boolean isBroadcast() {
+        return this == BROADCAST;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is a multicast address.
+     * @return {@code true} if the MAC address is a multicast address.
+     */
+    public boolean isMulticast() {
+        if (isBroadcast()) {
+            return false;
+        }
+        return (rawValue & (0x01L << 40)) != 0;
+    }
+
     @Override
     public int getLength() {
         return MacAddrLen;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
index f255e7b..7f76b4d 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
@@ -1,8 +1,6 @@
 package org.projectfloodlight.openflow.types;
 
-import org.jboss.netty.buffer.ChannelBuffer;
 import org.projectfloodlight.openflow.annotations.Immutable;
-import org.projectfloodlight.openflow.exceptions.OFParseError;
 
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.UnsignedInts;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
index ea2c4a9..13cfdc7 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
@@ -21,7 +21,12 @@
     public static String readFixedLengthString(ChannelBuffer bb, int length) {
         byte[] dst = new byte[length];
         bb.readBytes(dst, 0, length);
-        return new String(dst, Charsets.US_ASCII);
+        int validLength = 0;
+        for (validLength = 0; validLength < length; validLength++) {
+            if (dst[validLength] == 0)
+                break;
+        }
+        return new String(dst, 0, validLength, Charsets.US_ASCII);
     }
 
     public static void writeFixedLengthString(ChannelBuffer bb, String string,
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
index dc25b12..ed380ee 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
@@ -2,6 +2,8 @@
 
 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;
@@ -29,9 +31,11 @@
             0x00ffffffffffffL
     };
 
-    String[] invalidMacs = {
+    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",
@@ -39,6 +43,10 @@
             "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() {
@@ -72,8 +80,8 @@
 
 
     @Test
-    public void testInvalidMacss() throws OFParseError {
-        for(String invalid : invalidMacs) {
+    public void testInvalidMacStrings() throws OFParseError {
+        for(String invalid : invalidMacStrings) {
             try {
                 MacAddress.of(invalid);
                 fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
@@ -82,4 +90,54 @@
             }
         }
     }
+
+    @Test
+    public void testInvalidMacBytes() throws OFParseError {
+        for(byte[] invalid : invalidMacBytes) {
+            try {
+                MacAddress.of(invalid);
+                fail("Invalid IP "+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());
+    }
 }
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
index 38cc127..239828e 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
@@ -25,6 +25,15 @@
                 case VLAN_PCP:
                     result = vlanPcp;
                     break;
+                case ARP_OP:
+                    result = ArpOpcode.of(ipProto.getIpProtocolNumber());
+                    break;
+                case ARP_SPA:
+                    result = ipv4Src;
+                    break;
+                case ARP_TPA:
+                    result = ipv4Dst;
+                    break;
                 case IP_DSCP:
                     result = ipDscp;
                     break;
@@ -32,13 +41,13 @@
                     result = ipProto;
                     break;
                 case IPV4_SRC:
-                    result = ipv4Dst;
+                    result = ipv4Src;
                     break;
                 case IPV4_DST:
                     result = ipv4Dst;
                     break;
                 case TCP_SRC:
-                    result = ipv4Src;
+                    result = tcpSrc;
                     break;
                 case TCP_DST:
                     result = tcpDst;
@@ -77,10 +86,12 @@
             Object result;
             switch (field.id) {
                 case IPV4_SRC:
+                case ARP_SPA:
                     int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                     result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                     break;
                 case IPV4_DST:
+                case ARP_TPA:
                     int dstMaskedBits = Math.min(32, (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT);
                     int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());
 
@@ -101,6 +112,9 @@
                 case ETH_TYPE:
                 case VLAN_VID:
                 case VLAN_PCP:
+                case ARP_OP:
+                case ARP_SPA:
+                case ARP_TPA:
                 case IP_DSCP:
                 case IP_PROTO:
                 case IPV4_SRC:
@@ -122,6 +136,8 @@
         @Override
         public boolean supportsMasked(MatchField<?> field) {
             switch (field.id) {
+                case ARP_SPA:
+                case ARP_TPA:
                 case IPV4_SRC:
                 case IPV4_DST:
                     return true;
@@ -145,6 +161,12 @@
                     return (this.wildcards & OFPFW_DL_VLAN) == 0;
                 case VLAN_PCP:
                     return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
+                case ARP_OP:
+                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
+                case ARP_SPA:
+                    return this.getIpv4SrcCidrMaskLen() >= 32;
+                case ARP_TPA:
+                    return this.getIpv4DstCidrMaskLen() >= 32;
                 case IP_DSCP:
                     return (this.wildcards & OFPFW_NW_TOS) == 0;
                 case IP_PROTO:
@@ -216,6 +238,12 @@
                     return (this.wildcards & OFPFW_DL_VLAN) != 0;
                 case VLAN_PCP:
                     return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
+                case ARP_OP:
+                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
+                case ARP_SPA:
+                    return this.getIpv4SrcCidrMaskLen() <= 0;
+                case ARP_TPA:
+                    return this.getIpv4DstCidrMaskLen() <= 0;
                 case IP_DSCP:
                     return (this.wildcards & OFPFW_NW_TOS) != 0;
                 case IP_PROTO:
@@ -248,11 +276,13 @@
         @Override
         public boolean isPartiallyMasked(MatchField<?> field) {
             switch (field.id) {
+                case ARP_SPA:
                 case IPV4_SRC:
                     int srcCidrLen = getIpv4SrcCidrMaskLen();
                     return srcCidrLen > 0 && srcCidrLen < 32;
+                case ARP_TPA:
                 case IPV4_DST:
-                    int dstCidrLen = getIpv4SrcCidrMaskLen();
+                    int dstCidrLen = getIpv4DstCidrMaskLen();
                     return dstCidrLen > 0 && dstCidrLen < 32;
                 default:
                     throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
@@ -300,10 +330,16 @@
                     setInPort((OFPort) value);
                     wildcards &= ~OFPFW_IN_PORT;
                     break;
+                case ARP_OP:
+                    setIpProto(IpProtocol.of((short)((ArpOpcode)value).getOpcode()));
+                    wildcards &= ~OFPFW_NW_PROTO;
+                    break;
+                case ARP_TPA:
                 case IPV4_DST:
                     setIpv4Dst((IPv4Address) value);
                     wildcards &= ~OFPFW_NW_DST_MASK;
                     break;
+                case ARP_SPA:
                 case IPV4_SRC:
                     setIpv4Src((IPv4Address) value);
                     wildcards &= ~OFPFW_NW_SRC_MASK;
@@ -347,6 +383,7 @@
                 case VLAN_VID:
                     setVlanVid((VlanVid) value);
                     wildcards &= ~OFPFW_DL_VLAN;
+                    break;
                 default:
                     throw new UnsupportedOperationException(
                             "OFMatch does not support matching on field " + field.getName());
@@ -359,6 +396,8 @@
                 F value, F mask) {
             initWildcards();
             switch (field.id) {
+                case ARP_SPA:
+                case ARP_TPA:
                 case IPV4_DST:
                 case IPV4_SRC:
                     Object valObj = value;
@@ -369,10 +408,12 @@
                         throw new UnsupportedOperationException("OFMatch only supports CIDR masks for IPv4");
                     int maskLen = 32 - Integer.bitCount(maskval);
                     switch(field.id) {
+                        case ARP_TPA:
                         case IPV4_DST:
                             setIpv4Dst(ip);
                             wildcards = (wildcards &~OFPFW_NW_DST_MASK) | (maskLen << OFPFW_NW_DST_SHIFT);
                             break;
+                        case ARP_SPA:
                         case IPV4_SRC:
                             setIpv4Src(ip);
                             wildcards = (wildcards &~OFPFW_NW_SRC_MASK) | (maskLen << OFPFW_NW_SRC_SHIFT);
@@ -428,10 +469,12 @@
                     setInPort(OFPort.of(0)); // NOTE: not 'NONE' -- that is 0xFF for ports
                     wildcards |= OFPFW_IN_PORT;
                     break;
+                case ARP_TPA:
                 case IPV4_DST:
                     setIpv4Dst(IPv4Address.NONE);
                     wildcards |= OFPFW_NW_DST_MASK;
                     break;
+                case ARP_SPA:
                 case IPV4_SRC:
                     setIpv4Src(IPv4Address.NONE);
                     wildcards |= OFPFW_NW_SRC_MASK;
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.java b/java_gen/templates/custom/OFMatchV1Ver10.java
index eaaacb0..82ff10f 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.java
@@ -61,6 +61,15 @@
             case VLAN_PCP:
                 result = vlanPcp;
                 break;
+            case ARP_OP:
+                result = ArpOpcode.of(ipProto.getIpProtocolNumber());
+                break;
+            case ARP_SPA:
+                result = ipv4Src;
+                break;
+            case ARP_TPA:
+                result = ipv4Dst;
+                break;
             case IP_DSCP:
                 result = ipDscp;
                 break;
@@ -68,7 +77,7 @@
                 result = ipProto;
                 break;
             case IPV4_SRC:
-                result = ipv4Dst;
+                result = ipv4Src;
                 break;
             case IPV4_DST:
                 result = ipv4Dst;
@@ -114,10 +123,12 @@
             return null;
         Object result;
         switch (field.id) {
+            case ARP_SPA:
             case IPV4_SRC:
                 int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                 result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                 break;
+            case ARP_TPA:
             case IPV4_DST:
                 int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());
 
@@ -138,6 +149,9 @@
             case ETH_TYPE:
             case VLAN_VID:
             case VLAN_PCP:
+            case ARP_OP:
+            case ARP_SPA:
+            case ARP_TPA:
             case IP_DSCP:
             case IP_PROTO:
             case IPV4_SRC:
@@ -159,6 +173,8 @@
     @Override
     public boolean supportsMasked(MatchField<?> field) {
         switch (field.id) {
+            case ARP_SPA:
+            case ARP_TPA:
             case IPV4_SRC:
             case IPV4_DST:
                 return true;
@@ -185,6 +201,12 @@
                 return (this.wildcards & OFPFW_DL_VLAN) == 0;
             case VLAN_PCP:
                 return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
+            case ARP_OP:
+                return (this.wildcards & OFPFW_NW_PROTO) == 0;
+            case ARP_SPA:
+                return this.getIpv4SrcCidrMaskLen() >= 32;
+            case ARP_TPA:
+                return this.getIpv4DstCidrMaskLen() >= 32;
             case IP_DSCP:
                 return (this.wildcards & OFPFW_NW_TOS) == 0;
             case IP_PROTO:
@@ -259,6 +281,12 @@
                 return (this.wildcards & OFPFW_DL_VLAN) != 0;
             case VLAN_PCP:
                 return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
+            case ARP_OP:
+                return (this.wildcards & OFPFW_NW_PROTO) != 0;
+            case ARP_SPA:
+                return this.getIpv4SrcCidrMaskLen() <= 0;
+            case ARP_TPA:
+                return this.getIpv4DstCidrMaskLen() <= 0;
             case IP_DSCP:
                 return (this.wildcards & OFPFW_NW_TOS) != 0;
             case IP_PROTO:
@@ -294,9 +322,11 @@
             return false;
 
         switch (field.id) {
+            case ARP_SPA:
             case IPV4_SRC:
                 int srcCidrLen = getIpv4SrcCidrMaskLen();
                 return srcCidrLen > 0 && srcCidrLen < 32;
+            case ARP_TPA:
             case IPV4_DST:
                 int dstCidrLen = getIpv4SrcCidrMaskLen();
                 return dstCidrLen > 0 && dstCidrLen < 32;
diff --git a/java_gen/templates/of_factories.java b/java_gen/templates/of_factories.java
index 0044335..0a27c59 100644
--- a/java_gen/templates/of_factories.java
+++ b/java_gen/templates/of_factories.java
@@ -36,6 +36,9 @@
 //:: include("_imports.java")
 
 public final class OFFactories {
+
+    private final static GenericReader GENERIC_READER = new GenericReader();
+
     public static OFFactory getFactory(OFVersion version) {
         switch(version) {
             //:: for v in versions:
@@ -46,4 +49,25 @@
                 throw new IllegalArgumentException("Unknown version: "+version);
             }
     }
+    
+    private static class GenericReader implements OFMessageReader<OFMessage> {
+        public OFMessage readFrom(ChannelBuffer bb) throws OFParseError {
+            short wireVersion = U8.f(bb.getByte(0));
+            OFFactory factory;
+            switch (wireVersion) {
+            //:: for v in versions:
+            case ${v.int_version}:
+                factory = org.projectfloodlight.openflow.protocol.ver${v.of_version}.OFFactoryVer${v.of_version}.INSTANCE;
+                break;
+            //:: #endfor
+            default:
+                throw new IllegalArgumentException("Unknown wire version: " + wireVersion);
+            }
+            return factory.getReader().readFrom(bb);
+        }
+    }
+
+    public static OFMessageReader<OFMessage> getGenericReader() {
+        return GENERIC_READER;
+    }
 }
diff --git a/loxi_front_end/match.py b/loxi_front_end/match.py
index c510477..f580354 100644
--- a/loxi_front_end/match.py
+++ b/loxi_front_end/match.py
@@ -145,24 +145,7 @@
         takes_mask_in_spec=False,
         order=211,
         ),
-    ipv4_src = dict(
-        name="ipv4_src",
-        m_type="of_ipv4_t",
-        v1_wc_shift=8,
-        print_type="PRIx32",
-        conditions="is_ipv4(match)",
-        takes_mask_in_spec=True,
-        order=300,
-        ),
-    ipv4_dst = dict(
-        name="ipv4_dst",
-        m_type="of_ipv4_t",
-        v1_wc_shift=14,
-        print_type="PRIx32",
-        conditions="is_ipv4(match)",
-        takes_mask_in_spec=True,
-        order=301,
-        ),
+
     ip_dscp = dict(
         name="ip_dscp",
         m_type="uint8_t",
@@ -191,6 +174,24 @@
         takes_mask_in_spec=False,
         order=320,
         ),
+    ipv4_src = dict(
+        name="ipv4_src",
+        m_type="of_ipv4_t",
+        v1_wc_shift=8,
+        print_type="PRIx32",
+        conditions="is_ipv4(match)",
+        takes_mask_in_spec=True,
+        order=330,
+        ),
+    ipv4_dst = dict(
+        name="ipv4_dst",
+        m_type="of_ipv4_t",
+        v1_wc_shift=14,
+        print_type="PRIx32",
+        conditions="is_ipv4(match)",
+        takes_mask_in_spec=True,
+        order=331,
+        ),
 
     tcp_dst = dict(
         name="tcp_dst",
@@ -270,7 +271,7 @@
         print_type="PRIx16",
         conditions="is_arp(match)",
         takes_mask_in_spec=False,
-        order=250,
+        order=450,
         ),
 
     arp_spa = dict(
@@ -279,7 +280,7 @@
         print_type="PRIx32",
         conditions="is_arp(match)",
         takes_mask_in_spec=True,
-        order=251,
+        order=451,
         ),
     arp_tpa = dict(
         name="arp_tpa",
@@ -287,7 +288,7 @@
         print_type="PRIx32",
         conditions="is_arp(match)",
         takes_mask_in_spec=True,
-        order=252,
+        order=452,
         ),
 
     arp_sha = dict(
@@ -296,7 +297,7 @@
         print_type="\"p\"",
         conditions="is_arp(match)",
         takes_mask_in_spec=False,
-        order=253,
+        order=453,
         ),
     arp_tha = dict(
         name="arp_tha",
@@ -304,7 +305,7 @@
         print_type="\"p\"",
         conditions="is_arp(match)",
         takes_mask_in_spec=False,
-        order=254,
+        order=454,
         ),
 
     ipv6_src = dict(
@@ -313,7 +314,7 @@
         print_type="\"p\"",
         conditions="is_ipv6(match)",
         takes_mask_in_spec=True,
-        order=350,
+        order=500,
         ),
     ipv6_dst = dict(
         name="ipv6_dst",
@@ -321,7 +322,7 @@
         print_type="\"p\"",
         conditions="is_ipv6(match)",
         takes_mask_in_spec=True,
-        order=351,
+        order=501,
         ),
 
     ipv6_flabel = dict(
@@ -330,7 +331,7 @@
         print_type="PRIx32",
         conditions="is_ipv6(match)",
         takes_mask_in_spec=False, # Comment in openflow.h says True
-        order=360,
+        order=502,
         ),
 
     icmpv6_type = dict(
@@ -339,7 +340,7 @@
         print_type="PRIx8",
         conditions="is_icmp_v6(match)",
         takes_mask_in_spec=False,
-        order=440,
+        order=510,
         ),
     icmpv6_code = dict(
         name="icmpv6_code",
@@ -347,7 +348,7 @@
         print_type="PRIx8",
         conditions="is_icmp_v6(match)",
         takes_mask_in_spec=False,
-        order=441,
+        order=511,
         ),
 
     ipv6_nd_target = dict(
@@ -356,7 +357,7 @@
         print_type="\"p\"",
         conditions="", # fixme
         takes_mask_in_spec=False,
-        order=442,
+        order=512,
         ),
 
     ipv6_nd_sll = dict(
@@ -365,7 +366,7 @@
         print_type="\"p\"",
         conditions="", # fixme
         takes_mask_in_spec=False,
-        order=443,
+        order=520,
         ),
     ipv6_nd_tll = dict(
         name="ipv6_nd_tll",
@@ -373,7 +374,7 @@
         print_type="\"p\"",
         conditions="", # fixme
         takes_mask_in_spec=False,
-        order=444,
+        order=521,
         ),
 
     mpls_label = dict(
@@ -383,7 +384,7 @@
         print_type="PRIx32",
         conditions="",
         takes_mask_in_spec=False,
-        order=500,
+        order=600,
         ),
     mpls_tc = dict(
         name="mpls_tc",
@@ -392,7 +393,7 @@
         print_type="PRIx8",
         conditions="",
         takes_mask_in_spec=False,
-        order=501,
+        order=601,
         ),
 
     bsn_in_ports_128 = dict(
diff --git a/openflow_input/bsn_flow_idle b/openflow_input/bsn_flow_idle
new file mode 100644
index 0000000..40a95d8
--- /dev/null
+++ b/openflow_input/bsn_flow_idle
@@ -0,0 +1,99 @@
+// Copyright 2013, Big Switch Networks, Inc.
+//
+// LoxiGen is licensed under the Eclipse Public License,
+// version 1.0 (EPL), with the following special exception:
+//
+// LOXI Exception
+//
+// As a special exception to the terms of the EPL, you may
+// distribute libraries generated by LoxiGen (LoxiGen Libraries)
+// under the terms of your choice, provided that copyright and
+// licensing notices generated by LoxiGen are not altered or removed
+// from the LoxiGen Libraries and the notice provided below is (i)
+// included in the LoxiGen Libraries, if distributed in source code
+// form and (ii) included in any documentation for the LoxiGen
+// Libraries, if distributed in binary form.
+//
+// Notice: "Copyright 2013, Big Switch Networks, Inc.
+// This library was generated by the LoxiGen Compiler."
+//
+// You may not use this file except in compliance with the EPL or
+// LOXI Exception. You may obtain a copy of the EPL at:
+//
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// 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 EPL for the specific language
+// governing permissions and limitations under the EPL.
+
+#version 4
+
+/*
+ * Notification of idle flows
+ *
+ * This extension allows the controller to request to be notified periodically
+ * about idle flows. It is very similar to the flow_removed message in standard
+ * OpenFlow, but does not delete the idle flows.
+ *
+ * If the extension is enabled using of_bsn_flow_idle_enable_set_request and
+ * the OFPFF_BSN_SEND_IDLE bit is set in the flow-mod, then the idle_timeout
+ * field in the flow-mod is not used for standard flow expiration. Instead,
+ * the switch will send an of_bsn_flow_idle message every idle_timeout seconds
+ * if the flow was not used during that period.
+ */
+
+struct of_bsn_flow_idle_enable_set_request : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 36;
+    uint32_t enable;        // 0 to disable the extension, 1 to enable it
+};
+
+struct of_bsn_flow_idle_enable_set_reply : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 37;
+    uint32_t enable;        // Resulting state, 0 disabled, 1 enabled
+    uint32_t status;        // Result code: 0 success
+};
+
+struct of_bsn_flow_idle_enable_get_request : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 38;
+};
+
+struct of_bsn_flow_idle_enable_get_reply : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 39;
+    uint32_t enabled;       // 0 if feature is disabled; 1 if feature enabled
+};
+
+struct of_bsn_flow_idle : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 40;
+    uint64_t cookie;
+    uint16_t priority;
+    uint8_t table_id;
+    pad(5); // align to 8 bytes
+    of_match_t match;
+};
diff --git a/openflow_input/standard-1.0 b/openflow_input/standard-1.0
index 3493c6b..79d5bad 100644
--- a/openflow_input/standard-1.0
+++ b/openflow_input/standard-1.0
@@ -94,6 +94,7 @@
     OFPPC_NO_FLOOD = 0x10,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
diff --git a/openflow_input/standard-1.1 b/openflow_input/standard-1.1
index 85f696f..9937c6d 100644
--- a/openflow_input/standard-1.1
+++ b/openflow_input/standard-1.1
@@ -135,6 +135,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
@@ -1202,7 +1203,7 @@
 
 struct of_group_desc_stats_entry {
     uint16_t length;
-    uint8_t type;
+    uint8_t group_type;
     pad(1);
     uint32_t group_id;
     list(of_bucket_t) buckets;
diff --git a/openflow_input/standard-1.2 b/openflow_input/standard-1.2
index 72568fb..182794e 100644
--- a/openflow_input/standard-1.2
+++ b/openflow_input/standard-1.2
@@ -138,6 +138,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
@@ -1171,7 +1172,7 @@
 
 struct of_group_desc_stats_entry {
     uint16_t length;
-    uint8_t type;
+    uint8_t group_type;
     pad(1);
     uint32_t group_id;
     list(of_bucket_t) buckets;
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index 767594a..9d53f11 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -137,6 +137,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
@@ -250,6 +251,10 @@
     OFPFF_RESET_COUNTS = 0x4,
     OFPFF_NO_PKT_COUNTS = 0x8,
     OFPFF_NO_BYT_COUNTS = 0x10,
+
+    /* Non-standard, enabled by an experimenter message */
+    /* See the bsn_flow_idle input file */
+    OFPFF_BSN_SEND_IDLE = 0x80,
 };
 
 enum ofp_group(wire_type=uint32_t, complete=False) {
@@ -1382,7 +1387,7 @@
 
 struct of_group_desc_stats_entry {
     uint16_t length;
-    uint8_t type;
+    uint8_t group_type;
     pad(1);
     uint32_t group_id;
     list(of_bucket_t) buckets;
diff --git a/test_data/of13/bsn_flow_idle.data b/test_data/of13/bsn_flow_idle.data
new file mode 100644
index 0000000..61b656f
--- /dev/null
+++ b/test_data/of13/bsn_flow_idle.data
@@ -0,0 +1,54 @@
+-- binary
+04 04 # version, type
+00 38 # length
+12 34 56 78 # xid
+00 5c 16 c7 # experimenter
+00 00 00 28 # subtype
+fe dc ba 98 76 54 32 10 # cookie
+42 68 # priority
+14 # table_id
+00 # pad
+00 00 00 00 # pad
+00 01 # match.type
+00 16 # match.length
+80 00 01 08 # match.oxm_list[0].type_len - IN_PORT
+00 00 00 04 # match.oxm_list[0].value
+00 00 00 05 # match.oxm_list[0].mask
+80 00 2A 02 # match.oxm_list[1].type_len - ARP_OP
+00 01 # match.oxm_list[1].value
+00 00 # match.pad
+-- python
+ofp.message.bsn_flow_idle(
+    xid=0x12345678,
+    cookie=0xFEDCBA9876543210,
+    priority=17000,
+    table_id=20,
+    match=ofp.match([
+        ofp.oxm.in_port_masked(value=4, value_mask=5),
+        ofp.oxm.arp_op(value=1),
+    ]))
+-- c
+obj = of_bsn_flow_idle_new(OF_VERSION_1_3);
+of_bsn_flow_idle_xid_set(obj, 0x12345678);
+of_bsn_flow_idle_cookie_set(obj, 0xFEDCBA9876543210);
+of_bsn_flow_idle_priority_set(obj, 17000);
+of_bsn_flow_idle_table_id_set(obj, 20);
+{
+    of_match_t match = { OF_VERSION_1_3 };
+    match.fields.in_port = 4;
+    match.masks.in_port = 5;
+    match.fields.arp_op = 1;
+    OF_MATCH_MASK_ARP_OP_EXACT_SET(&match);
+    of_bsn_flow_idle_match_set(obj, &match);
+}
+-- java
+builder.setXid(0x12345678)
+    .setCookie(U64.parseHex("FEDCBA9876543210"))
+    .setPriority(17000)
+    .setTableId(TableId.of(20))
+    .setMatch(
+        factory.buildMatch()
+            .setMasked(MatchField.IN_PORT, OFPort.of(4), OFPort.of(5))
+            .setExact(MatchField.ARP_OP, ArpOpcode.of(1))
+                .build()
+    );
diff --git a/test_data/of13/group_desc_stats_reply.data b/test_data/of13/group_desc_stats_reply.data
index 34c0d50..5e95a67 100644
--- a/test_data/of13/group_desc_stats_reply.data
+++ b/test_data/of13/group_desc_stats_reply.data
@@ -49,7 +49,7 @@
     flags=0,
     entries=[
         ofp.group_desc_stats_entry(
-            type=ofp.OFPGT_FF,
+            group_type=ofp.OFPGT_FF,
             group_id=1,
             buckets=[
                 ofp.bucket(
@@ -66,4 +66,4 @@
                     actions=[
                         ofp.action.output(port=5, max_len=0),
                         ofp.action.output(port=6, max_len=0)])]),
-        ofp.group_desc_stats_entry(type=ofp.OFPGT_FF, group_id=2, buckets=[])])
+        ofp.group_desc_stats_entry(group_type=ofp.OFPGT_FF, group_id=2, buckets=[])])