pyloxi: support OXM member in set-field actions
The C backend needs more work to support of_oxm_t, so I've added a hack so it
can keep using of_octets_t.
Also added several set-field action testcases.
diff --git a/loxigen.py b/loxigen.py
index 1d1f245..36d6872 100755
--- a/loxigen.py
+++ b/loxigen.py
@@ -442,7 +442,12 @@
name=m_name))
pad_count += 1
else:
- legacy_members.append(dict(m_type=m.oftype, name=m.name))
+ # HACK the C backend does not yet support of_oxm_t
+ if m.oftype == 'of_oxm_t':
+ m_type = 'of_octets_t'
+ else:
+ m_type = m.oftype
+ legacy_members.append(dict(m_type=m_type, name=m.name))
versions[version_name]['classes'][ofclass.name] = legacy_members
for enum in ofinput.enums:
diff --git a/openflow_input/standard-1.2 b/openflow_input/standard-1.2
index 4650e06..8b4fa3f 100644
--- a/openflow_input/standard-1.2
+++ b/openflow_input/standard-1.2
@@ -692,7 +692,7 @@
struct of_action_set_field {
uint16_t type == 25;
uint16_t len;
- of_octets_t field;
+ of_oxm_t field;
};
struct of_action_experimenter {
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index ccef4db..0d5cec3 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -824,7 +824,7 @@
struct of_action_set_field {
uint16_t type == 25;
uint16_t len;
- of_octets_t field;
+ of_oxm_t field;
};
struct of_action_experimenter {
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index cf87ded..67af1e7 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -99,6 +99,11 @@
pack='util.pack_list(%s)',
unpack='oxm.unpack_list(%s.slice(_length-4))'),
+ 'of_oxm_t': OFTypeData(
+ init='None',
+ pack='%s.pack()',
+ unpack='oxm.unpack(%s)'),
+
# TODO implement unpack
'list(of_table_features_t)': OFTypeData(
init='[]',
diff --git a/py_gen/templates/_pack.py b/py_gen/templates/_pack.py
index 4714e97..b6e8ef7 100644
--- a/py_gen/templates/_pack.py
+++ b/py_gen/templates/_pack.py
@@ -56,6 +56,11 @@
:: #endfor
:: if length_member_index != None:
length = sum([len(x) for x in packed])
+:: if ofclass.name == 'of_action_set_field':
+ pad_len = (length + 7)/8*8 - length
+ length += pad_len
+ packed.append('\x00' * pad_len)
+:: #endif
packed[${length_member_index}] = ${gen_pack_expr(length_member.oftype, 'length')}
:: #endif
:: if ofclass.name == 'of_match_v3':
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index dccb46e..fba5294 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -37,6 +37,9 @@
import util
import loxi.generic_util
import loxi
+:: if version >= 3:
+import oxm # for unpack
+:: #endif
def unpack_list(reader):
def deserializer(reader, typ):
diff --git a/py_gen/templates/oxm.py b/py_gen/templates/oxm.py
index d95dd8e..792c277 100644
--- a/py_gen/templates/oxm.py
+++ b/py_gen/templates/oxm.py
@@ -38,15 +38,16 @@
import loxi.generic_util
import loxi
+def unpack(reader):
+ type_len, = reader.peek('!L')
+ if type_len in parsers:
+ return parsers[type_len](reader)
+ else:
+ raise loxi.ProtocolError("unknown OXM cls=%#x type=%#x masked=%d len=%d (%#x)" % \
+ ((type_len >> 16) & 0xffff, (type_len >> 9) & 0x7f, (type_len >> 8) & 1, type_len & 0xff, type_len))
+
def unpack_list(reader):
- def deserializer(reader):
- type_len, = reader.peek('!L')
- if type_len in parsers:
- return parsers[type_len](reader)
- else:
- raise loxi.ProtocolError("unknown OXM cls=%#x type=%#x masked=%d len=%d (%#x)" % \
- ((type_len >> 16) & 0xffff, (type_len >> 9) & 0x7f, (type_len >> 8) & 1, type_len & 0xff, type_len))
- return loxi.generic_util.unpack_list(reader, deserializer)
+ return loxi.generic_util.unpack_list(reader, unpack)
class OXM(object):
type_len = None # override in subclass
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index c6c94ab..98d999b 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -78,7 +78,9 @@
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
- expected_failures = []
+ expected_failures = [
+ ofp.action.set_field, # field defaults to None
+ ]
for klass in self.klasses:
def fn():
obj = klass()
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 349881f..8c18f41 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -93,6 +93,7 @@
def test_serialization(self):
expected_failures = [
+ ofp.action.set_field, # field defaults to None
ofp.common.table_feature_prop_apply_actions,
ofp.common.table_feature_prop_apply_actions_miss,
ofp.common.table_feature_prop_write_actions,
diff --git a/test_data/of13/action_output.data b/test_data/of13/action_output.data
new file mode 100644
index 0000000..7cd52ce
--- /dev/null
+++ b/test_data/of13/action_output.data
@@ -0,0 +1,8 @@
+-- binary
+00 00 # type
+00 10 # length
+00 00 00 32 # port
+ff ff # max_len
+00 00 00 00 00 00 # pad
+-- python
+ofp.action.output(port=50, max_len=65535)
diff --git a/test_data/of13/action_set_field_eth_dst.data b/test_data/of13/action_set_field_eth_dst.data
new file mode 100644
index 0000000..1e4a971
--- /dev/null
+++ b/test_data/of13/action_set_field_eth_dst.data
@@ -0,0 +1,8 @@
+-- binary
+00 19 # type
+00 10 # length
+80 00 06 06 # OXM header
+00 01 02 03 04 05 # OXM value
+00 00 # pad
+-- python
+ofp.action.set_field(field=ofp.oxm.eth_dst([0, 1, 2, 3, 4, 5]))
diff --git a/test_data/of13/action_set_field_ipv6_src.data b/test_data/of13/action_set_field_ipv6_src.data
new file mode 100644
index 0000000..f440dee
--- /dev/null
+++ b/test_data/of13/action_set_field_ipv6_src.data
@@ -0,0 +1,7 @@
+-- binary
+00 19 # type
+00 18 # length
+80 00 34 10 # OXM header
+00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f # OXM value
+-- python
+ofp.action.set_field(field=ofp.oxm.ipv6_src("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"))
diff --git a/test_data/of13/action_set_field_tcp_src.data b/test_data/of13/action_set_field_tcp_src.data
new file mode 100644
index 0000000..a69c7c0
--- /dev/null
+++ b/test_data/of13/action_set_field_tcp_src.data
@@ -0,0 +1,8 @@
+-- binary
+00 19 # type
+00 10 # length
+80 00 1a 02 # OXM header
+00 32 # OXM value
+00 00 00 00 00 00 # pad
+-- python
+ofp.action.set_field(field=ofp.oxm.tcp_src(50))