Merge into master from pull request #188:
Error-proofing dissector and packet_out recursive (https://github.com/floodlight/loxigen/pull/188)
diff --git a/Makefile b/Makefile
index 80b81eb..3767fba 100644
--- a/Makefile
+++ b/Makefile
@@ -84,6 +84,7 @@
 	ln -sf ../java_gen/pre-written/pom.xml ${OPENFLOWJ_ECLIPSE_WORKSPACE}/pom.xml
 	ln -sf ../java_gen/pre-written/LICENSE.txt ${OPENFLOWJ_ECLIPSE_WORKSPACE}/LICENSE.txt
 	ln -sf ../java_gen/pre-written/src ${OPENFLOWJ_ECLIPSE_WORKSPACE}
+	rsync --checksum --delete -rv ${LOXI_OUTPUT_DIR}/openflowj/gen-src/ ${OPENFLOWJ_ECLIPSE_WORKSPACE}/gen-src
 	cd ${OPENFLOWJ_ECLIPSE_WORKSPACE} && mvn eclipse:eclipse
 	# Unfortunately, mvn eclipse:eclipse resolves the symlink, which doesn't work with eclipse
 	cd ${OPENFLOWJ_ECLIPSE_WORKSPACE} && perl -pi -e 's{<classpathentry kind="src" path="[^"]*/java_gen/pre-written/src/}{<classpathentry kind="src" path="src/}' .classpath
diff --git a/c_gen/templates/_push_wire_types.c b/c_gen/templates/_push_wire_types.c
index b7b6288..5b16e24 100644
--- a/c_gen/templates/_push_wire_types.c
+++ b/c_gen/templates/_push_wire_types.c
@@ -41,6 +41,8 @@
         *(uint16_t *)(buf + ${m.offset}) = htobe16(${m.value}); /* ${m.name} */
 :: elif m.length == 4:
         *(uint32_t *)(buf + ${m.offset}) = htobe32(${m.value}); /* ${m.name} */
+:: elif m.length == 8:
+        *(uint64_t *)(buf + ${m.offset}) = htobe64(${m.value}); /* ${m.name} */
 :: else:
 :: raise("unsupported push_wire_types length %d" % m.length)
 :: #endif
diff --git a/c_gen/templates/loci_show.h b/c_gen/templates/loci_show.h
index 1856ae5..f5b80f2 100644
--- a/c_gen/templates/loci_show.h
+++ b/c_gen/templates/loci_show.h
@@ -382,5 +382,6 @@
 #define LOCI_SHOW_checksum_128_checksum_mask(writer, cookie, val) LOCI_SHOW_checksum_128(writer, cookie, val)
 #define LOCI_SHOW_u32_buckets_size(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
 #define LOCI_SHOW_u32_entry_count(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
+#define LOCI_SHOW_u32_num_aux(writer, cookie, val) LOCI_SHOW_u32(writer, cookie, val)
 
 #endif /* _LOCI_SHOW_H_ */
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 351765b..e57000f 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -308,8 +308,9 @@
         .op(read='U8.of(bb.readByte())', write='bb.writeByte($name.getRaw())', default="U8.ZERO")
 u32obj = JType('U32', 'U32') \
         .op(read='U32.of(bb.readInt())', write='bb.writeInt($name.getRaw())', default="U32.ZERO")
-u64 = JType('U64', 'U64') \
-        .op(read='U64.ofRaw(bb.readLong())', write='bb.writeLong($name.getValue())', default="U64.ZERO")
+u64 = JType('U64', 'long') \
+        .op(read='U64.ofRaw(bb.readLong())', write='bb.writeLong($name.getValue())', default="U64.ZERO", pub_type=True) \
+        .op(read='bb.readLong()', write='bb.writeLong($name)', pub_type=False)
 of_port = JType("OFPort") \
          .op(version=1, read="OFPort.read2Bytes(bb)", write="$name.write2Bytes(bb)", default="OFPort.ANY") \
          .op(version=ANY, read="OFPort.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="OFPort.ANY")
@@ -440,6 +441,10 @@
         .op(read='TableId.readByte(bb)',
             write='$name.writeByte(bb)',
             default='TableId.ZERO')
+of_aux_id = JType("OFAuxId") \
+        .op(read='OFAuxId.readByte(bb)',
+            write='$name.writeByte(bb)',
+            default='OFAuxId.MAIN')
 of_version = JType("OFVersion", 'byte') \
             .op(read='bb.readByte()', write='bb.writeByte($name)')
 
@@ -637,6 +642,8 @@
 
         'of_bsn_tlv_vlan_vid' : { 'value' : vlan_vid },
         'of_bsn_gentable_entry_add' : { 'table_id' : gen_table_id },
+
+        'of_features_reply' : { 'auxiliary_id' : of_aux_id},
 }
 
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFAuxId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFAuxId.java
new file mode 100644
index 0000000..7b124f2
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFAuxId.java
@@ -0,0 +1,83 @@
+package org.projectfloodlight.openflow.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.Shorts;
+
+public class OFAuxId implements Comparable<OFAuxId>, PrimitiveSinkable {
+    
+    private static final short VALIDATION_MASK = 0xFF;
+    
+    private static final short MAIN_VAL = 0x0000;
+    
+    public static final OFAuxId MAIN = new OFAuxId(MAIN_VAL);
+            
+    private final short id;
+
+    private OFAuxId(short id) {
+        this.id = id;
+    }
+
+    public static OFAuxId of(short id) {
+        switch(id) {
+            case MAIN_VAL:
+                return MAIN;
+            default:
+                if ((id & VALIDATION_MASK) != id)
+                    throw new IllegalArgumentException("Illegal Aux id value: " + id);
+                return new OFAuxId(id);
+        }
+    }
+
+    public static OFAuxId of(int id) {
+        if((id & VALIDATION_MASK) != id)
+            throw new IllegalArgumentException("Illegal Aux id value: "+id);
+        return of((short) id);
+    }
+
+    @Override
+    public String toString() {
+        return "0x" + Integer.toHexString(id);
+    }
+
+    public short getValue() {
+        return id;
+    }
+
+    public void writeByte(ChannelBuffer c) {
+        c.writeByte(this.id);
+    }
+
+    public static OFAuxId readByte(ChannelBuffer c) throws OFParseError {
+        return OFAuxId.of(c.readUnsignedByte());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof TableId))
+            return false;
+        OFAuxId other = (OFAuxId)obj;
+        if (other.id != this.id)
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int prime = 13873;
+        return this.id * prime;
+    }
+
+    @Override
+    public int compareTo(OFAuxId other) {
+        return Shorts.compare(this.id, other.id);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putByte((byte) id);
+    }
+
+}
diff --git a/openflow_input/bsn_aux_cxns b/openflow_input/bsn_aux_cxns
new file mode 100644
index 0000000..247f81b
--- /dev/null
+++ b/openflow_input/bsn_aux_cxns
@@ -0,0 +1,54 @@
+// 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.
+
+// Request that the switch spawn and configure auxiliary OF connections.
+//
+#version 4
+// Set the number of desired aux connections num_aux=(0-16) accompanying this main connection
+// This message is only allowed on the main connection. 
+struct of_bsn_set_aux_cxns_request : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 58;
+    uint32_t num_aux;
+};
+
+// Synchronous reply. Confirms that the aux_cxn_set_request has been received and that the
+// the requested num_aux value is supported by the switch.
+struct of_bsn_set_aux_cxns_reply : of_bsn_header {
+    uint8_t version;
+    uint8_t type == 4;
+    uint16_t length;
+    uint32_t xid;
+    uint32_t experimenter == 0x5c16c7;
+    uint32_t subtype == 59;
+    uint32_t num_aux;
+    uint32_t status; //0 = Success, !0 = Failure
+};
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 7e28a0c..3b56308 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -150,12 +150,6 @@
         pack='%s.pack()',
         unpack='%s.unpack(%%s)' % pyclass)
 
-# Special case for lists of hello_elem, which must ignore unknown types
-type_data_map['list(of_hello_elem_t)'] = OFTypeData(
-    init='[]',
-    pack='loxi.generic_util.pack_list(%s)',
-    unpack='util.unpack_list_hello_elem(%s)')
-
 ## Public interface
 
 def lookup_type_data(oftype, version):
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index f902ae8..dfcd1fc 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,9 +1,19 @@
 :: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
 :: from loxi_ir import *
 :: import py_gen.oftype
+:: import py_gen.util as util
 :: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
-:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember or
+::                                                 type(m) == OFDiscriminatorMember]
+:: if ofclass.virtual:
+:: discriminator_fmts = { 1: "B", 2: "!H", 4: "!L" }
+:: discriminator_fmt = discriminator_fmts[ofclass.discriminator.length]
+:: #endif
 class ${ofclass.pyname}(${superclass_pyname}):
+:: if ofclass.virtual:
+    subtypes = {}
+
+:: #endif
 :: for m in type_members:
     ${m.name} = ${m.value}
 :: #endfor
@@ -29,6 +39,13 @@
 
     @staticmethod
     def unpack(reader):
+:: if ofclass.virtual:
+        subtype, = reader.peek(${repr(discriminator_fmt)}, ${ofclass.discriminator.offset})
+        subclass = ${ofclass.pyname}.subtypes.get(subtype)
+        if subclass:
+            return subclass.unpack(reader)
+
+:: #endif
         obj = ${ofclass.pyname}()
 :: include("_unpack.py", ofclass=ofclass)
         return obj
diff --git a/py_gen/templates/_unpack.py b/py_gen/templates/_unpack.py
index 81f74c7..cce9de3 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -42,7 +42,7 @@
 ::     elif type(m) == OFTypeMember:
         _${m.name} = ${gen_unpack_expr(m.oftype, 'reader', version=version)}
         assert(_${m.name} == ${m.value})
-::     elif type(m) == OFDataMember:
+::     elif type(m) == OFDataMember or type(m) == OFDiscriminatorMember:
 ::         if m.name in field_length_members:
 ::             reader_expr = 'reader.slice(_%s)' % field_length_members[m.name].name
 ::         else:
diff --git a/py_gen/templates/_virtual_ofclass.py b/py_gen/templates/_virtual_ofclass.py
deleted file mode 100644
index 1e5dcc2..0000000
--- a/py_gen/templates/_virtual_ofclass.py
+++ /dev/null
@@ -1,23 +0,0 @@
-:: import py_gen.util as util
-:: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
-:: fmts = { 1: "B", 2: "!H", 4: "!L" }
-:: fmt = fmts[ofclass.discriminator.length]
-:: trail = ' '.join([x.pyname for x in util.ancestors(ofclass)])
-class ${ofclass.pyname}(${superclass_pyname}):
-    subtypes = {}
-
-    @staticmethod
-    def unpack(reader):
-        subtype, = reader.peek(${repr(fmt)}, ${ofclass.discriminator.offset})
-        try:
-            subclass = ${ofclass.pyname}.subtypes[subtype]
-        except KeyError:
-            raise loxi.ProtocolError("unknown ${trail} subtype %#x" % subtype)
-        return subclass.unpack(reader)
-
-:: # Register with our superclass
-:: if ofclass.superclass:
-:: type_field_name = ofclass.superclass.discriminator.name
-:: type_value = ofclass.member_by_name(type_field_name).value
-${superclass_pyname}.subtypes[${type_value}] = ${ofclass.pyname}
-:: #endif
diff --git a/py_gen/templates/module.py b/py_gen/templates/module.py
index 02f0002..dfe23e8 100644
--- a/py_gen/templates/module.py
+++ b/py_gen/templates/module.py
@@ -52,11 +52,7 @@
 import loxi.generic_util
 
 :: for ofclass in ofclasses:
-:: if ofclass.virtual:
-:: include('_virtual_ofclass.py', ofclass=ofclass)
-:: else:
 :: include('_ofclass.py', ofclass=ofclass)
-:: #endif
 
 :: #endfor
 
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index d690939..85181dc 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -173,16 +173,6 @@
         x >>= 1
     return value
 
-def unpack_list_hello_elem(reader):
-    def deserializer(reader):
-        typ, length, = reader.peek('!HH')
-        reader = reader.slice(length)
-        try:
-            return common.hello_elem.unpack(reader)
-        except loxi.ProtocolError:
-            return None
-    return [x for x in loxi.generic_util.unpack_list(reader, deserializer) if x != None]
-
 def pack_checksum_128(value):
     return struct.pack("!QQ", (value >> 64) & MASK64, value & MASK64)
 
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index 250000f..7e10335 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -112,10 +112,10 @@
         with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
             loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
 
-    def test_invalid_action_type(self):
+    def test_unknown_action_type(self):
         buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
-        with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action subtype'):
-            loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+        result = loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+        self.assertEquals(result, [ofp.action.action(type=0xfffe)])
 
 class TestConstants(unittest.TestCase):
     def test_ports(self):
@@ -200,11 +200,19 @@
         test_klasses = [x for x in ofp.message.__dict__.values()
                         if type(x) == type
                            and issubclass(x, ofp.message.message)
-                           and hasattr(x, 'pack')]
+                           and not hasattr(x, 'subtypes')]
 
         for klass in test_klasses:
             self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
 
+    def test_parse_unknown_message(self):
+        import loxi
+        import loxi.of10 as ofp
+
+        buf = "\x01\xfe\x00\x08\x12\x34\x56\x78"
+        msg = ofp.message.parse_message(buf)
+        self.assertIsInstance(msg, ofp.message.message)
+
 class TestUtils(unittest.TestCase):
     def test_pretty_wildcards(self):
         self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
@@ -231,7 +239,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of11.py b/py_gen/tests/of11.py
index d620509..7ae6d8a 100644
--- a/py_gen/tests/of11.py
+++ b/py_gen/tests/of11.py
@@ -69,7 +69,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index 4774672..c463c50 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -78,7 +78,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 8e258df..c5a16b2 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -60,18 +60,6 @@
         self.assertTrue(hasattr(loxi.of13, "message"))
         self.assertTrue(hasattr(loxi.of13, "oxm"))
 
-class TestCommon(unittest.TestCase):
-    def test_list_hello_elem_unpack(self):
-        buf = ''.join([
-            '\x00\x01\x00\x04', # versionbitmap
-            '\x00\x00\x00\x04', # unknown type
-            '\x00\x01\x00\x04', # versionbitmap
-        ])
-        l = ofp.util.unpack_list_hello_elem(OFReader(buf))
-        self.assertEquals(len(l), 2)
-        self.assertTrue(isinstance(l[0], ofp.hello_elem_versionbitmap))
-        self.assertTrue(isinstance(l[1], ofp.hello_elem_versionbitmap))
-
 # The majority of the serialization tests are created here using the files in
 # the test_data directory.
 class TestDataFiles(unittest.TestCase):
@@ -91,7 +79,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/test_data/of13/bsn_set_aux_cxns_reply.data b/test_data/of13/bsn_set_aux_cxns_reply.data
new file mode 100644
index 0000000..d385d84
--- /dev/null
+++ b/test_data/of13/bsn_set_aux_cxns_reply.data
@@ -0,0 +1,20 @@
+-- binary
+04 04 # version, type
+00 18 # length
+12 34 56 78 # xid
+00 5c 16 c7 # experimenter
+00 00 00 3b # subtype
+00 00 00 01 # num_aux
+00 00 00 00 # status
+-- python
+ofp.message.bsn_set_aux_cxns_reply(
+    xid=0x12345678, num_aux=1, status=0)
+-- java
+builder.setXid(0x12345678)
+        .setNumAux(1)
+        .setStatus(0)
+-- c
+obj = of_bsn_set_aux_cxns_reply_new(OF_VERSION_1_3);
+of_bsn_set_aux_cxns_reply_xid_set(obj, 0x12345678);
+of_bsn_set_aux_cxns_reply_num_aux_set(obj, 1);
+of_bsn_set_aux_cxns_reply_status_set(obj, 0);
diff --git a/test_data/of13/bsn_set_aux_cxns_request.data b/test_data/of13/bsn_set_aux_cxns_request.data
new file mode 100644
index 0000000..ee48d98
--- /dev/null
+++ b/test_data/of13/bsn_set_aux_cxns_request.data
@@ -0,0 +1,17 @@
+-- binary
+04 04 # version, type
+00 14 # length
+12 34 56 78 # xid
+00 5c 16 c7 # experimenter
+00 00 00 3A # subtype
+00 00 00 01 # num_aux
+-- python
+ofp.message.bsn_set_aux_cxns_request(
+    xid=0x12345678, num_aux=1)
+-- java
+builder.setXid(0x12345678)
+       .setNumAux(1)
+-- c
+obj = of_bsn_set_aux_cxns_request_new(OF_VERSION_1_3);
+of_bsn_set_aux_cxns_request_xid_set(obj, 0x12345678);
+of_bsn_set_aux_cxns_request_num_aux_set(obj, 1);