Merge pull request #330 from rlane/of14-loci2
[of14] OF 1.4 support for LOCI
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 047b254..ff2dfe8 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -119,135 +119,6 @@
if not m_name in of_g.ordered_members[cls]:
of_g.ordered_members[cls].append(m_name)
-def update_offset(cls, wire_version, name, offset, m_type):
- """
- Update (and return) the offset based on type.
- @param cls The parent class
- @param wire_version The wire version being processed
- @param name The name of the data member
- @param offset The current offset
- @param m_type The type declaration being processed
- @returns A pair (next_offset, len_update) next_offset is the new offset
- of the next object or -1 if this is a var-length object. len_update
- is the increment that should be added to the length. Note that (for
- of_match_v3) it is variable length, but it adds 8 bytes to the fixed
- length of the object
- If offset is already -1, do not update
- Otherwise map to base type and count and update (if possible)
- """
- if offset < 0: # Don't update offset once set to -1
- return offset, 0
-
- count, base_type = loxi_utils.type_dec_to_count_base(m_type)
-
- len_update = 0
- if base_type in of_g.of_mixed_types:
- base_type = of_g.of_mixed_types[base_type][wire_version]
-
- base_class = base_type[:-2]
- if (base_class, wire_version) in of_g.is_fixed_length:
- bytes = of_g.base_length[(base_class, wire_version)]
- else:
- if base_type == "of_match_v3_t":
- # This is a special case: it has non-zero min length
- # but is variable length
- bytes = -1
- len_update = 8
- elif base_type == "of_oxm_header_t":
- # This is a special case: it has non-zero min length
- # but is variable length
- bytes = -1
- len_update = 4
- elif base_type == "of_bsn_vport_header_t":
- # This is a special case: it has non-zero min length
- # but is variable length
- bytes = -1
- len_update = 4
- elif base_type in of_g.of_base_types:
- bytes = of_g.of_base_types[base_type]["bytes"]
- else:
- print "UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type)
- log("UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type))
- bytes = -1
-
- # If bytes
- if bytes > 0:
- len_update = count * bytes
-
- if bytes == -1:
- return -1, len_update
-
- return offset + (count * bytes), len_update
-
-def calculate_offsets_and_lengths(ordered_classes, classes, wire_version):
- """
- Generate the offsets for fixed offset class members
- Also calculate the class_sizes when possible.
-
- @param classes The classes to process
- @param wire_version The wire version for this set of classes
-
- Updates global variables
- """
-
- lists = set()
-
- # Generate offsets
- for cls in ordered_classes:
- fixed_offset = 0 # The last "good" offset seen
- offset = 0
- last_offset = 0
- last_name = "-"
- for member in classes[cls]:
- m_type = member["m_type"]
- name = member["name"]
- if last_offset == -1:
- if name == "pad":
- log("Skipping pad for special offset for %s" % cls)
- else:
- log("SPECIAL OFS: Member %s (prev %s), class %s ver %d" %
- (name, last_name, cls, wire_version))
- if (((cls, name) in of_g.special_offsets) and
- (of_g.special_offsets[(cls, name)] != last_name)):
- debug("ERROR: special offset prev name changed")
- debug(" cls %s. name %s. version %d. was %s. now %s" %
- cls, name, wire_version,
- of_g.special_offsets[(cls, name)], last_name)
- sys.exit(1)
- of_g.special_offsets[(cls, name)] = last_name
-
- member["offset"] = offset
- if m_type.find("list(") == 0:
- (list_name, base_type) = loxi_utils.list_name_extract(m_type)
- lists.add(list_name)
- member["m_type"] = list_name + "_t"
- offset = -1
- elif m_type.find("struct") == 0:
- debug("ERROR found struct: %s.%s " % (cls, name))
- sys.exit(1)
- elif m_type == "octets":
- log("offset gen skipping octets: %s.%s " % (cls, name))
- offset = -1
- else:
- offset, len_update = update_offset(cls, wire_version, name,
- offset, m_type)
- if offset != -1:
- fixed_offset = offset
- else:
- fixed_offset += len_update
- log("offset is -1 for %s.%s version %d " %
- (cls, name, wire_version))
- last_offset = offset
- last_name = name
- of_g.base_length[(cls, wire_version)] = fixed_offset
- if (offset != -1):
- of_g.is_fixed_length.add((cls, wire_version))
-
- for list_type in lists:
- classes[list_type] = []
- of_g.ordered_classes[wire_version].append(list_type)
- of_g.base_length[(list_type, wire_version)] = 0
-
def order_and_assign_object_ids():
"""
Order all classes and assign object ids to all classes.
@@ -329,11 +200,7 @@
pad_count = 0
for m in ofclass.members:
if type(m) == OFPadMember:
- m_name = 'pad%d' % pad_count
- if m_name == 'pad0': m_name = 'pad'
- legacy_members.append(dict(m_type='uint8_t[%d]' % m.length,
- name=m_name))
- pad_count += 1
+ continue
else:
# HACK the C backend does not yet support of_oxm_t
if m.oftype == 'of_oxm_t':
@@ -341,15 +208,28 @@
# HACK the C backend does not yet support of_bsn_vport_t
elif m.oftype == 'of_bsn_vport_t':
m_type = 'of_bsn_vport_header_t'
+ elif m.oftype.find("list(") == 0:
+ (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
+ m_type = list_name + "_t"
else:
enum = find(lambda e: e.name == m.oftype, protocol.enums)
if enum and "wire_type" in enum.params:
m_type = enum.params["wire_type"]
else:
m_type = m.oftype
- legacy_members.append(dict(m_type=m_type, name=m.name))
+
+ if m.offset is None:
+ m_offset = -1
+ else:
+ m_offset = m.offset
+
+ legacy_members.append(dict(m_type=m_type, name=m.name, offset=m_offset))
versions[version_name]['classes'][ofclass.name] = legacy_members
+ of_g.base_length[(ofclass.name, version.wire_version)] = ofclass.base_length
+ if ofclass.is_fixed_length:
+ of_g.is_fixed_length.add((ofclass.name, version.wire_version))
+
for enum in protocol.enums:
for entry in enum.entries:
identifiers.add_identifier(
@@ -377,13 +257,42 @@
new_cls = cls + '_header'
of_g.ordered_classes[wire_version].append(new_cls)
classes[new_cls] = classes[cls]
+ of_g.base_length[(new_cls, wire_version)] = of_g.base_length[(cls, wire_version)]
+ if (cls, wire_version) in of_g.is_fixed_length:
+ of_g.is_fixed_length.add((cls, wire_version))
- for wire_version in of_g.wire_ver_map.keys():
- version_name = of_g.of_version_wire2name[wire_version]
- calculate_offsets_and_lengths(
- of_g.ordered_classes[wire_version],
- versions[version_name]['classes'],
- wire_version)
+ # Create lists
+ for version, protocol in loxi_globals.ir.items():
+ lists = set()
+ classes = versions[of_g.of_version_wire2name[version.wire_version]]['classes']
+
+ for ofclass in protocol.classes:
+ for m in ofclass.members:
+ if isinstance(m, OFDataMember) and m.oftype.find("list(") == 0:
+ (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
+ lists.add(list_name)
+
+ for list_type in lists:
+ classes[list_type] = []
+ of_g.ordered_classes[version.wire_version].append(list_type)
+ of_g.base_length[(list_type, version.wire_version)] = 0
+
+ # Find special offsets
+ # These are defined as members (except padding) that don't have a fixed
+ # offset. The special_offsets map stores the name of the previous member.
+ for version, protocol in loxi_globals.ir.items():
+ for ofclass in protocol.classes:
+ prev_member = None
+ for m in ofclass.members:
+ if isinstance(m, OFPadMember):
+ continue
+ if m.offset == None:
+ old = of_g.special_offsets.get((ofclass.name, m.name))
+ if old and old != prev_member.name:
+ raise Exception("Error: special offset changed: version=%s cls=%s member=%s old=%s new=%s" %
+ (version, ofclass.name, m.name, old, prev_member.name))
+ of_g.special_offsets[(ofclass.name, m.name)] = prev_member.name
+ prev_member = m
def unify_input():
"""
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 3229403..b708c65 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -403,7 +403,6 @@
int of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id, int max_len);
extern const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX];
-extern const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX];
""")
c_type_maps.gen_type_data_header(out)
c_match.gen_declarations(out)
@@ -530,7 +529,7 @@
/**
* Check if a version is supported
*/
-#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
+#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_4)
""")
gen_version_enum(out)
@@ -725,24 +724,6 @@
#define U64_HTON(val) U64_NTOH(val)
#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
#endif
-
-/****************************************************************
- *
- * The following are internal definitions used by the automatically
- * generated code. Users should not reference these definitions
- * as they may change between versions of this code
- *
- ****************************************************************/
-
-#define OF_MESSAGE_IN_MATCH_POINTER(obj) \\
- (WIRE_BUF_POINTER(&((obj)->wire_buffer), OF_MESSAGE_IN_MATCH_OFFSET))
-#define OF_MESSAGE_IN_MATCH_LEN(ptr) BUF_U16_GET(&ptr[2])
-#define OF_MESSAGE_IN_DATA_OFFSET(obj) \\
- (FIXED_LEN + OF_MESSAGE_IN_MATCH_LEN(OF_MESSAGE_IN_MATCH_POINTER(obj)) + 2)
-
-#define OF_MESSAGE_OUT_DATA_OFFSET(obj) \\
- (FIXED_LEN + of_message_out_actions_len_get(obj))
-
""")
def external_h_top_matter(out, name):
@@ -1618,7 +1599,7 @@
MEMSET(obj, 0, sizeof(*obj));
}
if (bytes < 0) {
- bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
+ bytes = of_object_fixed_len[version][%(enum)s];
}
obj->version = version;
obj->length = bytes;
@@ -1637,64 +1618,6 @@
""")
-## @fixme This should also be updated once there is a map from
-# class instance to wire length/type accessors
-def gen_wire_push_fn(cls, out):
- """
- Generate the calls to push values into the wire buffer
- """
- if type_maps.class_is_virtual(cls):
- print "Push fn gen called for virtual class " + cls
- return
-
- out.write("""
-/**
- * Helper function to push values into the wire buffer
- */
-static inline int
-%(cls)s_push_wire_values(%(cls)s_t *obj)
-{
-""" % dict(cls=cls))
-
- import loxi_globals
- uclass = loxi_globals.unified.class_by_name(cls)
- if uclass and not uclass.virtual and uclass.has_type_members:
- out.write("""
- %(cls)s_push_wire_types(obj);
-""" % dict(cls=cls))
-
- if loxi_utils.class_is_message(cls):
- out.write("""
- /* Message obj; set length */
- of_message_t msg;
-
- if ((msg = OF_OBJECT_TO_MESSAGE(obj)) != NULL) {
- of_message_length_set(msg, obj->length);
- }
-""" % dict(name = enum_name(cls)))
-
- else: # Not a message
- if loxi_utils.class_is_tlv16(cls):
- out.write("""
- /* TLV obj; set length */
- of_tlv16_wire_length_set((of_object_t *)obj, obj->length);
-""" % dict(enum=enum_name(cls)))
-
- if loxi_utils.class_is_u16_len(cls) or cls == "of_packet_queue":
- out.write("""
- of_object_wire_length_set((of_object_t *)obj, obj->length);
-""")
-
- if cls == "of_meter_stats":
- out.write("""
- of_meter_stats_wire_length_set((of_object_t *)obj, obj->length);
-""" % dict(enum=enum_name(cls)))
-
- out.write("""
- return OF_ERROR_NONE;
-}
-""")
-
def gen_new_fn_body(cls, out):
"""
Generate function body for new function
@@ -1708,9 +1631,6 @@
*/
""" % dict(cls=cls))
- if not type_maps.class_is_virtual(cls):
- gen_wire_push_fn(cls, out)
-
uclass = loxi_globals.unified.class_by_name(cls)
is_fixed_length = uclass and uclass.is_fixed_length
max_length = is_fixed_length and "bytes" or "OF_WIRE_BUFFER_MAX_LENGTH"
@@ -1734,7 +1654,7 @@
%(cls)s_t *obj;
int bytes;
- bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
+ bytes = of_object_fixed_len[version][%(enum)s];
if ((obj = (%(cls)s_t *)of_object_new(%(max_length)s)) == NULL) {
return NULL;
@@ -1743,20 +1663,25 @@
%(cls)s_init(obj, version, bytes, 0);
""" % dict(cls=cls, enum=enum_name(cls), max_length=max_length))
if not type_maps.class_is_virtual(cls):
- out.write("""
- if (%(cls)s_push_wire_values(obj) < 0) {
- FREE(obj);
- return NULL;
- }
-""" % dict(cls=cls))
+ from codegen import class_metadata_dict
+ metadata = class_metadata_dict[cls]
+
+ if metadata.wire_type_set != 'NULL':
+ out.write("""\
+ %s(obj);
+""" % metadata.wire_type_set)
+
+ if metadata.wire_length_set != 'NULL':
+ out.write("""\
+ %s(obj, obj->length);
+""" % metadata.wire_length_set)
match_offset = v3_match_offset_get(cls)
if match_offset >= 0:
# Init length field for match object
out.write("""
/* Initialize match TLV for 1.2 */
- /* FIXME: Check 1.3 below */
- if ((version == OF_VERSION_1_2) || (version == OF_VERSION_1_3)) {
+ if ((version >= OF_VERSION_1_2)) {
of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
}
""" % dict(match_offset=match_offset))
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index 2f41c35..4e7db7a 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -242,20 +242,20 @@
out.write("};\n\n")
out.write("""
-static const loci_obj_dump_f *const dump_funs[5] = {
- NULL,
- dump_funs_v1,
- dump_funs_v2,
- dump_funs_v3,
- dump_funs_v4
+static const loci_obj_dump_f *const dump_funs[] = {
+""")
+
+ for version in of_g.of_version_range:
+ out.write(" [%(v)d] = dump_funs_v%(v)d,\n" % dict(v=version))
+
+ out.write("""\
};
int
of_object_dump(loci_writer_f writer, void* cookie, of_object_t *obj)
{
if ((obj->object_id > 0) && (obj->object_id < OF_OBJECT_COUNT)) {
- if (((obj)->version > 0) && ((obj)->version <= OF_VERSION_1_3)) {
- /* @fixme VERSION */
+ if (OF_VERSION_OKAY(obj->version)) {
return dump_funs[obj->version][obj->object_id](writer, cookie, (of_object_t *)obj);
} else {
return writer(cookie, "Bad version %d\\n", obj->version);
diff --git a/c_gen/c_match.py b/c_gen/c_match.py
index 3afc99c..e5a41ef 100644
--- a/c_gen/c_match.py
+++ b/c_gen/c_match.py
@@ -104,6 +104,13 @@
#define of_match_v4_to_match of_match_v3_to_match
#define of_match_to_wire_match_v4 of_match_to_wire_match_v3
#define of_match_v4_delete of_match_v3_delete
+
+#define of_match_v5_t of_match_v3_t
+#define of_match_v5_init of_match_v3_init
+#define of_match_v5_new of_match_v3_new
+#define of_match_v5_to_match of_match_v3_to_match
+#define of_match_to_wire_match_v5 of_match_to_wire_match_v3
+#define of_match_v5_delete of_match_v3_delete
""")
def gen_match_macros(out):
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index f4d7945..9a13b43 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -311,20 +311,20 @@
out.write("};\n\n")
out.write("""
-static const loci_obj_show_f *const show_funs[5] = {
- NULL,
- show_funs_v1,
- show_funs_v2,
- show_funs_v3,
- show_funs_v4
+static const loci_obj_show_f *const show_funs[] = {
+""")
+
+ for version in of_g.of_version_range:
+ out.write(" [%(v)d] = show_funs_v%(v)d,\n" % dict(v=version))
+
+ out.write("""\
};
int
of_object_show(loci_writer_f writer, void* cookie, of_object_t *obj)
{
if ((obj->object_id > 0) && (obj->object_id < OF_OBJECT_COUNT)) {
- if (((obj)->version > 0) && ((obj)->version <= OF_VERSION_1_2)) {
- /* @fixme VERSION */
+ if (OF_VERSION_OKAY(obj->version)) {
return show_funs[obj->version][obj->object_id](writer, cookie, (of_object_t *)obj);
} else {
return writer(cookie, "Bad version %d\\n", obj->version);
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index 41fe122..dacc8bf 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -541,7 +541,7 @@
"""
members, member_types = scalar_member_types_get(cls, version)
- length = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+ length = of_g.base_length[(cls, version)]
v_name = loxi_utils.version_to_name(version)
out.write("""
@@ -773,13 +773,15 @@
out.write("""
%(base_type)s_t elt;
int cur_len = 0;
+ (void) elt;
+ (void) cur_len;
""" % dict(cls=cls, base_type=base_type))
sub_classes = type_maps.sub_class_map(base_type, version)
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
- if len(sub_classes) == 0:
+ if not type_maps.class_is_virtual(base_type):
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
out.write("\n elt_p = &elt;\n")
@@ -791,7 +793,7 @@
for instance, subcls in sub_classes:
out.write(" %s = &elt.%s;\n" % (instance, instance))
- if len(sub_classes) == 0: # No inheritance case
+ if not type_maps.class_is_virtual(base_type): # No inheritance case
setup_instance(out, cls, base_type, "elt_p", v_name, version)
else:
for instance, subcls in sub_classes:
@@ -819,13 +821,14 @@
base_type = loxi_utils.list_to_entry_type(cls)
out.write("""
%(base_type)s_t elt;
+ (void) elt;
""" % dict(cls=cls, base_type=base_type))
sub_classes = type_maps.sub_class_map(base_type, version)
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
- if len(sub_classes) == 0:
+ if not type_maps.class_is_virtual(base_type):
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
out.write("\n elt_p = &elt;\n")
@@ -837,8 +840,10 @@
for instance, subcls in sub_classes:
out.write(" %s = &elt.%s;\n" % (instance, instance))
- out.write(" TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
- if len(sub_classes) == 0: # No inheritance case
+ if not type_maps.class_is_virtual(base_type) or sub_classes:
+ out.write(" TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
+
+ if not type_maps.class_is_virtual(base_type): # No inheritance case
check_instance(out, cls, base_type, "elt_p", v_name, version, True)
else:
count = 0
@@ -963,10 +968,12 @@
static int
test_match_1(void)
{
- of_match_v1_t *m_v1;
- of_match_v2_t *m_v2;
- of_match_v3_t *m_v3;
- of_match_v4_t *m_v4;
+""")
+
+ for version in of_g.of_version_range:
+ out.write(" of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
+
+ out.write("""\
of_match_t match;
int value = 1;
int idx;
@@ -1001,10 +1008,12 @@
static int
test_match_2(void)
{
- of_match_v1_t *m_v1;
- of_match_v2_t *m_v2;
- of_match_v3_t *m_v3;
- of_match_v3_t *m_v4;
+""")
+
+ for version in of_g.of_version_range:
+ out.write(" of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
+
+ out.write("""\
of_match_t match1;
of_match_t match2;
int value = 1;
@@ -1088,7 +1097,7 @@
continue
if type_maps.class_is_virtual(cls):
continue
- bytes = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+ bytes = of_g.base_length[(cls, version)]
out.write("""
static int
test_%(cls)s_create_%(v_name)s(void)
@@ -1173,13 +1182,15 @@
out.write("""
%(base_type)s_t elt;
int cur_len = 0;
+ (void) elt;
+ (void) cur_len;
""" % dict(cls=cls, base_type=base_type))
sub_classes = type_maps.sub_class_map(base_type, version)
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
- if len(sub_classes) == 0:
+ if not type_maps.class_is_virtual(base_type):
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
out.write("\n elt_p = &elt;\n")
@@ -1191,7 +1202,7 @@
for instance, subcls in sub_classes:
out.write(" %s = &elt.%s;\n" % (instance, instance))
- if len(sub_classes) == 0: # No inheritance case
+ if not type_maps.class_is_virtual(base_type): # No inheritance case
setup_instance(out, cls, base_type, "elt_p", v_name, version)
else:
for instance, subcls in sub_classes:
@@ -1225,7 +1236,7 @@
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
- if len(sub_classes) == 0:
+ if not type_maps.class_is_virtual(base_type):
entry_count = 2
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
@@ -1239,8 +1250,10 @@
for instance, subcls in sub_classes:
out.write(" %s = &elt.%s;\n" % (instance, instance))
- out.write(" TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
- if len(sub_classes) == 0: # No inheritance case
+ if not type_maps.class_is_virtual(base_type) or sub_classes:
+ out.write(" TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
+
+ if not type_maps.class_is_virtual(base_type): # No inheritance case
check_instance(out, cls, base_type, "elt_p", v_name,
version, True)
else:
@@ -1518,7 +1531,7 @@
"""
members, member_types = scalar_member_types_get(cls, version)
- length = of_g.base_length[(cls, version)] + of_g.extra_length.get((cls, version), 0)
+ length = of_g.base_length[(cls, version)]
v_name = loxi_utils.version_to_name(version)
out.write("""
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 6c45d4d..17f63ca 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -64,6 +64,15 @@
extern void of_meter_stats_wire_length_get(of_object_t *obj, int *bytes);
extern void of_meter_stats_wire_length_set(of_object_t *obj, int bytes);
+extern void of_port_desc_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_port_desc_wire_length_set(of_object_t *obj, int bytes);
+
+extern void of_port_stats_entry_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_port_stats_entry_wire_length_set(of_object_t *obj, int bytes);
+
+extern void of_queue_stats_entry_wire_length_get(of_object_t *obj, int *bytes);
+extern void of_queue_stats_entry_wire_length_set(of_object_t *obj, int bytes);
+
""")
@@ -106,44 +115,3 @@
out.write("""
};
""")
-
-
-def gen_extra_length_array(out):
- """
- Generate an array giving the extra lengths of all objects/versions
- @param out The file handle to which to write
- """
- out.write("""
-/**
- * An array with the number of bytes in the extra length part
- * of each OF object
- */
-""")
-
- for version in of_g.of_version_range:
- out.write("""
-static const int\nof_object_extra_len_v%d[OF_OBJECT_COUNT] = {
- -1, /* of_object is not instantiable */
-""" % version)
- for i, cls in enumerate(of_g.all_class_order):
- comma = ","
- if i == len(of_g.all_class_order) - 1:
- comma = ""
- val = "-1" + comma
- if (cls, version) in of_g.base_length:
- val = str(of_g.extra_length.get((cls, version), 0)) + comma
- out.write(" %-5s /* %d: %s */\n" % (val, i + 1, cls))
- out.write("};\n")
-
- out.write("""
-/**
- * Unified map of extra length part of each object
- */
-const int *const of_object_extra_len[OF_VERSION_ARRAY_MAX] = {
- NULL,
-""")
- for version in of_g.of_version_range:
- out.write(" of_object_extra_len_v%d,\n" % version)
- out.write("""
-};
-""")
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
index c1b00ad..dba3d53 100644
--- a/c_gen/codegen.py
+++ b/c_gen/codegen.py
@@ -186,7 +186,6 @@
# Collect legacy code
tmp = StringIO()
c_type_maps.gen_length_array(tmp)
- c_type_maps.gen_extra_length_array(tmp)
with template_utils.open_output(install_dir, "loci/src/of_type_maps.c") as out:
util.render_template(out, "of_type_maps.c", legacy_code=tmp.getvalue())
@@ -197,6 +196,13 @@
class_metadata = []
class_metadata_dict = {}
+# These classes have handwritten C code to get/set their length fields
+# See templates/of_type_maps.c
+special_length_classes = set([
+ 'of_packet_queue', 'of_meter_stats', 'of_port_desc',
+ 'of_port_stats_entry', 'of_queue_stats_entry',
+])
+
def build_class_metadata():
for uclass in loxi_globals.unified.classes:
wire_length_get = 'NULL'
@@ -216,14 +222,9 @@
wire_length_set = 'of_object_message_wire_length_set'
elif uclass.is_oxm:
wire_length_get = 'of_oxm_wire_length_get'
- elif uclass.name == "of_packet_queue":
- # u16 len, but at offset 4
- wire_length_get = 'of_packet_queue_wire_length_get'
- wire_length_set = 'of_packet_queue_wire_length_set'
- elif uclass.name == "of_meter_stats":
- # u16 len, but at offset 4
- wire_length_get = 'of_meter_stats_wire_length_get'
- wire_length_set = 'of_meter_stats_wire_length_set'
+ elif uclass.name in special_length_classes:
+ wire_length_get = '%s_wire_length_get' % uclass.name
+ wire_length_set = '%s_wire_length_set' % uclass.name
elif loxi_utils_legacy.class_is_tlv16(uclass.name):
wire_length_set = 'of_tlv16_wire_length_set'
wire_length_get = 'of_tlv16_wire_length_get'
diff --git a/c_gen/match.py b/c_gen/match.py
index 7b7dcce..d6b43fa 100644
--- a/c_gen/match.py
+++ b/c_gen/match.py
@@ -131,6 +131,7 @@
2: of_v2_keys,
3: [],
4: [],
+ 5: [],
}
# Complete list of match keys, sorted by the standard order
diff --git a/c_gen/of_g_legacy.py b/c_gen/of_g_legacy.py
index 2372b31..87e0680 100644
--- a/c_gen/of_g_legacy.py
+++ b/c_gen/of_g_legacy.py
@@ -111,6 +111,7 @@
2: "uint32_t",
3: "uint32_t",
4: "uint32_t",
+ 5: "uint32_t",
"short_name":"port_no"
},
of_port_desc_t = {
@@ -118,6 +119,7 @@
2: "of_port_desc_t",
3: "of_port_desc_t",
4: "of_port_desc_t",
+ 5: "of_port_desc_t",
"short_name":"port_desc"
},
of_bsn_vport_t = {
@@ -125,6 +127,7 @@
2: "of_bsn_vport_t",
3: "of_bsn_vport_t",
4: "of_bsn_vport_t",
+ 5: "of_bsn_vport_t",
"short_name":"bsn_vport"
},
of_fm_cmd_t = { # Flow mod command went from u16 to u8
@@ -132,6 +135,7 @@
2: "uint8_t",
3: "uint8_t",
4: "uint8_t",
+ 5: "uint8_t",
"short_name":"fm_cmd"
},
of_wc_bmap_t = { # Wildcard bitmap
@@ -139,6 +143,7 @@
2: "uint32_t",
3: "uint64_t",
4: "uint64_t",
+ 5: "uint64_t",
"short_name":"wc_bmap"
},
of_match_bmap_t = { # Match bitmap
@@ -146,6 +151,7 @@
2: "uint32_t",
3: "uint64_t",
4: "uint64_t",
+ 5: "uint64_t",
"short_name":"match_bmap"
},
of_match_t = { # Match object
@@ -153,6 +159,7 @@
2: "of_match_v2_t",
3: "of_match_v3_t",
4: "of_match_v3_t", # Currently uses same match as 1.2 (v3).
+ 5: "of_match_v3_t", # Currently uses same match as 1.2 (v3).
"short_name":"match"
},
)
@@ -266,12 +273,6 @@
## Map from class, wire_version to size of fixed part of class
base_length = {}
-## Map from class, wire_version to size of variable-offset, fixed length part of class
-extra_length = {
- ("of_packet_in", 3): 2,
- ("of_packet_in", 4): 2,
-}
-
## Boolean indication of variable length, per class, wire_version,
is_fixed_length = set()
diff --git a/c_gen/templates/loci_int.h b/c_gen/templates/loci_int.h
index 1ea9eeb..eb1c923 100644
--- a/c_gen/templates/loci_int.h
+++ b/c_gen/templates/loci_int.h
@@ -30,6 +30,8 @@
:: flow_mod = of_g.base_length[("of_flow_modify",of_g.VERSION_1_2)]
:: packet_in = of_g.base_length[("of_packet_in",of_g.VERSION_1_2)]
:: packet_in_1_3 = of_g.base_length[("of_packet_in",of_g.VERSION_1_3)]
+:: packet_in_1_4 = of_g.base_length[("of_packet_in",of_g.VERSION_1_4)]
+:: assert packet_in_1_3 == packet_in_1_4
:: flow_stats = of_g.base_length[("of_flow_stats_entry", of_g.VERSION_1_2)]
:: match1 = of_g.base_length[("of_match_v1",of_g.VERSION_1_0)]
:: match2 = of_g.base_length[("of_match_v2",of_g.VERSION_1_1)]
@@ -239,13 +241,16 @@
* @param obj An object of type of_packet_in_t
*
* Get length of preceding match object and add to fixed length
- * Applies only to version 1.2 and 1.3
- * The +2 comes from the 2 bytes of padding between the match and packet data.
+ * Applies only to version 1.2+
+ * There are 2 bytes of padding between the match and data. The
+ * _OFFSET_FOLLOWING_MATCH_V3 macro assumes the match is at the end of the
+ * fixed length, so we need to subtract 2 from the fixed length we pass and
+ * then add 2 to the resulting offset.
*/
#define _PACKET_IN_DATA_OFFSET(obj) \
(_OFFSET_FOLLOWING_MATCH_V3((obj), (obj)->version == OF_VERSION_1_2 ? \
-${packet_in} : ${packet_in_1_3}) + 2)
+(${packet_in} - 2) : (${packet_in_1_3} - 2)) + 2)
/**
* Macro to calculate variable offset of data (packet) member in packet_out
diff --git a/c_gen/templates/loci_validator.c b/c_gen/templates/loci_validator.c
index 1f98b21..c4e8b5c 100644
--- a/c_gen/templates/loci_validator.c
+++ b/c_gen/templates/loci_validator.c
@@ -199,7 +199,7 @@
return ${validator_name(proto.class_by_name('of_header'))}(msg, len, &out_len);
:: #endfor
default:
- VALIDATOR_LOG("Bad version %d", OF_VERSION_1_3);
+ VALIDATOR_LOG("Bad version %d", version);
return -1;
}
}
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index 9fa59e0..8847515 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -305,3 +305,141 @@
of_wire_buffer_u16_set(wbuf,
OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_METER_STATS_LENGTH_OFFSET), bytes);
}
+
+/**
+ * Get the wire length for a port desc object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_port_desc_wire_length_get(of_object_t *obj, int *bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ uint16_t u16;
+
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 4),
+ &u16);
+ *bytes = u16;
+ } else {
+ *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+ }
+}
+
+/**
+ * Set the wire length for a port desc object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_port_desc_wire_length_set(of_object_t *obj, int bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 4),
+ bytes);
+ } else {
+ LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+ }
+}
+
+/**
+ * Get the wire length for a port stats_entry object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_port_stats_entry_wire_length_get(of_object_t *obj, int *bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ uint16_t u16;
+
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+ &u16);
+ *bytes = u16;
+ } else {
+ *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+ }
+}
+
+/**
+ * Set the wire length for a port stats_entry object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_port_stats_entry_wire_length_set(of_object_t *obj, int bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+ bytes);
+ } else {
+ LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+ }
+}
+
+/**
+ * Get the wire length for a queue stats_entry object
+ * @param obj The object being referenced
+ * @param bytes Pointer to location to store length
+ *
+ * The length is only present for OF 1.4+.
+ */
+void
+of_queue_stats_entry_wire_length_get(of_object_t *obj, int *bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ uint16_t u16;
+
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_get(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+ &u16);
+ *bytes = u16;
+ } else {
+ *bytes = OF_OBJECT_FIXED_LENGTH(obj);
+ }
+}
+
+/**
+ * Set the wire length for a queue stats_entry object
+ * @param obj The object being referenced
+ * @param bytes The length of the object
+ *
+ * The length is only present for OF 1.4+.
+ */
+
+void
+of_queue_stats_entry_wire_length_set(of_object_t *obj, int bytes)
+{
+ of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
+ LOCI_ASSERT(wbuf != NULL);
+
+ if (obj->version >= OF_VERSION_1_4) {
+ of_wire_buffer_u16_set(wbuf, OF_OBJECT_ABSOLUTE_OFFSET(obj, 0),
+ bytes);
+ } else {
+ LOCI_ASSERT(obj->length == OF_OBJECT_FIXED_LENGTH(obj));
+ }
+}
diff --git a/c_gen/templates/of_wire_buf.h b/c_gen/templates/of_wire_buf.h
index 0922a69..3d38f53 100644
--- a/c_gen/templates/of_wire_buf.h
+++ b/c_gen/templates/of_wire_buf.h
@@ -469,14 +469,10 @@
of_wire_buffer_u16_get(wbuf, offset, &v16);
*value = v16;
break;
- case OF_VERSION_1_1:
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u32_get(wbuf, offset, &v32);
*value = v32;
break;
- default:
- LOCI_ASSERT(0);
}
}
@@ -498,13 +494,9 @@
case OF_VERSION_1_0:
of_wire_buffer_u16_set(wbuf, offset, (uint16_t)value);
break;
- case OF_VERSION_1_1:
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u32_set(wbuf, offset, (uint32_t)value);
break;
- default:
- LOCI_ASSERT(0);
}
}
@@ -527,14 +519,10 @@
of_wire_buffer_u16_get(wbuf, offset, &v16);
*value = v16;
break;
- case OF_VERSION_1_1:
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u8_get(wbuf, offset, &v8);
*value = v8;
break;
- default:
- LOCI_ASSERT(0);
}
}
@@ -553,13 +541,9 @@
case OF_VERSION_1_0:
of_wire_buffer_u16_set(wbuf, offset, (uint16_t)value);
break;
- case OF_VERSION_1_1:
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u8_set(wbuf, offset, (uint8_t)value);
break;
- default:
- LOCI_ASSERT(0);
}
}
@@ -583,13 +567,10 @@
of_wire_buffer_u32_get(wbuf, offset, &v32);
*value = v32;
break;
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u64_get(wbuf, offset, &v64);
*value = v64;
break;
- default:
- LOCI_ASSERT(0);
}
}
@@ -609,12 +590,9 @@
case OF_VERSION_1_1:
of_wire_buffer_u32_set(wbuf, offset, (uint32_t)value);
break;
- case OF_VERSION_1_2:
- case OF_VERSION_1_3:
+ default:
of_wire_buffer_u64_set(wbuf, offset, (uint64_t)value);
break;
- default:
- LOCI_ASSERT(0);
}
}
diff --git a/loxi_ir/ir_offset.py b/loxi_ir/ir_offset.py
index c65cfed..db55f24 100644
--- a/loxi_ir/ir_offset.py
+++ b/loxi_ir/ir_offset.py
@@ -187,6 +187,8 @@
member_ir_class = existing_classes[base_class]
bytes = member_ir_class.base_length
length_fixed = member_ir_class.is_fixed_length
+ if member_ir_class.has_external_alignment:
+ bytes = (bytes + 7) & ~7
else:
if base_type in existing_enums:
enum = existing_enums[base_type]
diff --git a/openflow_input/standard-1.4 b/openflow_input/standard-1.4
index 2f495ed..9b923af 100644
--- a/openflow_input/standard-1.4
+++ b/openflow_input/standard-1.4
@@ -832,6 +832,22 @@
uint16_t length; /* Length in bytes of this property. */
};
+struct of_port_mod_prop_ethernet : of_port_mod_prop {
+ uint16_t type == 0;
+ uint16_t length;
+ uint32_t advertise;
+};
+
+struct of_port_mod_prop_optical : of_port_mod_prop {
+ uint16_t type == 1;
+ uint16_t length;
+ uint32_t configure;
+ uint32_t freq_ldma;
+ uint32_t fl_offset; /* TODO signed */
+ uint32_t grid_span;
+ uint32_t tx_pwr;
+};
+
struct of_port_mod : of_header {
uint8_t version;
uint8_t type == 16;
diff --git a/test_data/of13/port_status.data b/test_data/of13/port_status.data
index 10f7c16..e56705f 100644
--- a/test_data/of13/port_status.data
+++ b/test_data/of13/port_status.data
@@ -34,3 +34,24 @@
peer=ofp.OFPPF_100MB_FD,
curr_speed=10,
max_speed=20))
+-- c
+obj = of_port_status_new(OF_VERSION_1_3);
+of_port_status_xid_set(obj, 0x12345678);
+of_port_status_reason_set(obj, OF_PORT_CHANGE_REASON_MODIFY);
+{
+ of_object_t desc;
+ of_port_status_desc_bind(obj, &desc);
+ of_port_desc_port_no_set(&desc, 4);
+ of_mac_addr_t hw_addr = { { 1, 2, 3, 4, 5, 6 } };
+ of_port_desc_hw_addr_set(&desc, hw_addr);
+ of_port_name_t name = "foo";
+ of_port_desc_name_set(&desc, name);
+ of_port_desc_config_set(&desc, OF_PORT_CONFIG_FLAG_NO_FWD|OF_PORT_CONFIG_FLAG_NO_RECV);
+ of_port_desc_state_set(&desc, OF_PORT_STATE_FLAG_BLOCKED);
+ of_port_desc_curr_set(&desc, OF_PORT_FEATURE_FLAG_10MB_HD);
+ of_port_desc_advertised_set(&desc, OF_PORT_FEATURE_FLAG_10MB_FD);
+ of_port_desc_supported_set(&desc, OF_PORT_FEATURE_FLAG_100MB_HD);
+ of_port_desc_peer_set(&desc, OF_PORT_FEATURE_FLAG_100MB_FD);
+ of_port_desc_curr_speed_set(&desc, 10);
+ of_port_desc_max_speed_set(&desc, 20);
+}
diff --git a/test_data/of14/port_stats_reply.data b/test_data/of14/port_stats_reply.data
index 43dc471..0ff4902 100644
--- a/test_data/of14/port_stats_reply.data
+++ b/test_data/of14/port_stats_reply.data
@@ -65,7 +65,23 @@
of_port_stats_entry_tx_dropped_set(obj, 0);
of_port_stats_entry_rx_errors_set(obj, 0);
of_port_stats_entry_tx_errors_set(obj, 2);
- /* TODO append property */
+
+ /* Append property */
+ {
+ of_object_t list;
+ of_port_stats_entry_properties_bind(obj, &list);
+ {
+ of_object_t *obj = of_port_stats_prop_ethernet_new(OF_VERSION_1_4);
+ of_port_stats_prop_ethernet_rx_frame_err_set(obj, 1);
+ of_port_stats_prop_ethernet_rx_over_err_set(obj, 2);
+ of_port_stats_prop_ethernet_rx_crc_err_set(obj, 3);
+ of_port_stats_prop_ethernet_collisions_set(obj, 4);
+ of_list_append(&list, obj);
+ of_object_delete(obj);
+ }
+ }
+
+
of_list_append(&list, obj);
of_object_delete(obj);
}
diff --git a/test_data/of14/port_status.data b/test_data/of14/port_status.data
new file mode 100644
index 0000000..9fb5bde
--- /dev/null
+++ b/test_data/of14/port_status.data
@@ -0,0 +1,72 @@
+-- binary
+05 0c # version, type
+00 58 # length
+12 34 56 78 # xid
+02 # reason
+00 00 00 00 00 00 00 # pad
+00 00 00 04 # port_no
+00 48 # length
+00 00 # pad
+01 02 03 04 05 06 # hw_addr
+00 00 # pad
+66 6f 6f 00 00 00 00 00 # name
+00 00 00 00 00 00 00 00 # ...
+00 00 00 24 # config
+00 00 00 02 # state
+00 00 # properties[0].type (ethernet)
+00 20 # properties[0].length
+00 00 00 00 # pad
+00 00 00 01 # properties[0].curr
+00 00 00 02 # properties[0].advertised
+00 00 00 04 # properties[0].supported
+00 00 00 08 # properties[0].peer
+00 00 00 0a # properties[0].curr_speed
+00 00 00 14 # properties[0].max_speed
+-- python
+ofp.message.port_status(
+ xid=0x12345678,
+ reason=ofp.OFPPR_MODIFY,
+ desc=ofp.port_desc(
+ port_no=4,
+ hw_addr=[1,2,3,4,5,6],
+ name="foo",
+ config=ofp.OFPPC_NO_FWD|ofp.OFPPC_NO_RECV,
+ state=ofp.OFPPS_BLOCKED,
+ properties=[
+ ofp.port_desc_prop.ethernet(
+ curr=ofp.OFPPF_10MB_HD,
+ advertised=ofp.OFPPF_10MB_FD,
+ supported=ofp.OFPPF_100MB_HD,
+ peer=ofp.OFPPF_100MB_FD,
+ curr_speed=10,
+ max_speed=20)]))
+-- c
+obj = of_port_status_new(OF_VERSION_1_4);
+of_port_status_xid_set(obj, 0x12345678);
+of_port_status_reason_set(obj, OF_PORT_CHANGE_REASON_MODIFY);
+{
+ of_object_t desc;
+ of_port_status_desc_bind(obj, &desc);
+ of_port_desc_port_no_set(&desc, 4);
+ of_mac_addr_t hw_addr = { { 1, 2, 3, 4, 5, 6 } };
+ of_port_desc_hw_addr_set(&desc, hw_addr);
+ of_port_name_t name = "foo";
+ of_port_desc_name_set(&desc, name);
+ of_port_desc_config_set(&desc, OF_PORT_CONFIG_FLAG_NO_FWD|OF_PORT_CONFIG_FLAG_NO_RECV);
+ of_port_desc_state_set(&desc, OF_PORT_STATE_FLAG_BLOCKED);
+ {
+ of_list_port_desc_prop_t list;
+ of_port_desc_properties_bind(&desc, &list);
+ {
+ of_object_t *obj = of_port_desc_prop_ethernet_new(OF_VERSION_1_4);
+ of_port_desc_prop_ethernet_curr_set(obj, OF_PORT_FEATURE_FLAG_10MB_HD);
+ of_port_desc_prop_ethernet_advertised_set(obj, OF_PORT_FEATURE_FLAG_10MB_FD);
+ of_port_desc_prop_ethernet_supported_set(obj, OF_PORT_FEATURE_FLAG_100MB_HD);
+ of_port_desc_prop_ethernet_peer_set(obj, OF_PORT_FEATURE_FLAG_100MB_FD);
+ of_port_desc_prop_ethernet_curr_speed_set(obj, 10);
+ of_port_desc_prop_ethernet_max_speed_set(obj, 20);
+ of_list_append(&list, obj);
+ of_object_delete(obj);
+ }
+ }
+}