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);