Merge into master from pull request #245:
Rewrite LOCI validator (https://github.com/floodlight/loxigen/pull/245)
diff --git a/c_gen/c_validator_gen.py b/c_gen/c_validator_gen.py
deleted file mode 100644
index 9bb0407..0000000
--- a/c_gen/c_validator_gen.py
+++ /dev/null
@@ -1,322 +0,0 @@
-# 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.
-
-"""
-@brief Validator function generation
-
-Generates validator function files.
-
-"""
-
-import sys
-import c_gen.of_g_legacy as of_g
-import c_gen.match as match
-import c_gen.flags as flags
-from generic_utils import *
-import c_gen.type_maps as type_maps
-import loxi_utils.loxi_utils as loxi_utils
-import c_gen.loxi_utils_legacy as loxi_utils
-import c_gen.identifiers as identifiers
-from c_test_gen import var_name_map
-from c_code_gen import v3_match_offset_get
-
-def gen_h(out, name):
- loxi_utils.gen_c_copy_license(out)
- out.write("""
-/**
- *
- * AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
- *
- * Declarations of message validation functions. These functions check that an
- * OpenFlow message is well formed. Specifically, they check internal length
- * fields.
- */
-
-#if !defined(_LOCI_VALIDATOR_H_)
-#define _LOCI_VALIDATOR_H_
-
-#include <loci/loci.h>
-
-/*
- * Validate an OpenFlow message.
- * @return 0 if message is valid, -1 otherwise.
- */
-extern int of_validate_message(of_message_t msg, int len);
-
-#endif /* _LOCI_VALIDATOR_H_ */
-""")
-
-def gen_c(out, name):
- loxi_utils.gen_c_copy_license(out)
- out.write("""
-/**
- *
- * AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
- *
- * Source file for OpenFlow message validation.
- *
- */
-
-#include "loci_log.h"
-#include <loci/loci.h>
-#include <loci/loci_validator.h>
-
-#define VALIDATOR_LOG(...) LOCI_LOG_ERROR("Validator Error: " __VA_ARGS__)
-
-""")
-
- # Declarations
- for version in of_g.of_version_range:
- ver_name = loxi_utils.version_to_name(version)
- for cls in reversed(of_g.standard_class_order):
- if not loxi_utils.class_in_version(cls, version):
- continue
- if cls in type_maps.inheritance_map:
- continue
- out.write("""
-static inline int %(cls)s_%(ver_name)s_validate(uint8_t *buf, int len);\
-""" % dict(cls=cls, ver_name=ver_name))
-
- out.write("\n")
-
- # Definitions
- for version in of_g.of_version_range:
- ver_name = loxi_utils.version_to_name(version)
- for cls in reversed(of_g.standard_class_order):
- if not loxi_utils.class_in_version(cls, version):
- continue
- if cls in type_maps.inheritance_map:
- continue
- if loxi_utils.class_is_list(cls):
- gen_list_validator(out, cls, version)
- else:
- gen_validator(out, cls, version)
-
- out.write("""
-int
-of_validate_message_%(ver_name)s(of_message_t msg, int len)
-{
- of_object_id_t object_id = of_message_to_object_id(msg, len);
- uint8_t *buf = OF_MESSAGE_TO_BUFFER(msg);
- switch (object_id) {
-""" % dict(ver_name=ver_name))
- for cls in reversed(of_g.standard_class_order):
- if not loxi_utils.class_in_version(cls, version):
- continue
- if cls in type_maps.inheritance_map:
- continue
- if loxi_utils.class_is_message(cls):
- out.write("""\
- case %(cls_id)s:
- return %(cls)s_%(ver_name)s_validate(buf, len);
-""" % dict(ver_name=ver_name, cls=cls, cls_id=cls.upper()))
- out.write("""\
- default:
- VALIDATOR_LOG("%(cls)s: could not map %(cls_id)s");
- return -1;
- }
-}
-""" % dict(ver_name=ver_name, cls=cls, cls_id=cls.upper()))
-
- out.write("""
-int
-of_validate_message(of_message_t msg, int len)
-{
- of_version_t version;
- if (len < OF_MESSAGE_MIN_LENGTH ||
- len != of_message_length_get(msg)) {
- VALIDATOR_LOG("message length %d != %d", len,
- of_message_length_get(msg));
- return -1;
- }
-
- version = of_message_version_get(msg);
- switch (version) {
-""")
-
- for version in of_g.of_version_range:
- ver_name = loxi_utils.version_to_name(version)
- out.write("""\
- case %(ver_name)s:
- return of_validate_message_%(ver_name)s(msg, len);
-""" % dict(ver_name=ver_name))
-
- out.write("""\
- default:
- VALIDATOR_LOG("Bad version %%d", %(ver_name)s);
- return -1;
- }
-}
-""" % dict(ver_name=ver_name))
-
-def gen_validator(out, cls, version):
- fixed_len = of_g.base_length[(cls, version)];
- ver_name = loxi_utils.version_to_name(version)
- out.write("""
-static inline int
-%(cls)s_%(ver_name)s_validate(uint8_t *buf, int len)
-{
- if (len < %(fixed_len)s) {
- VALIDATOR_LOG("Class %(cls)s. Len %%d too small, < %%d", len, %(fixed_len)s);
- return -1;
- }
-""" % dict(cls=cls, ver_name=ver_name, cls_id=cls.upper(), fixed_len=fixed_len))
- members, member_types = loxi_utils.all_member_types_get(cls, version)
- for member in members:
- m_type = member["m_type"]
- m_name = member["name"]
- m_offset = member['offset']
- m_cls = m_type[:-2] # Trim _t
- if loxi_utils.skip_member_name(m_name):
- continue
- if not loxi_utils.type_is_of_object(m_type):
- continue
- if not loxi_utils.class_is_var_len(m_cls, version):
- continue
- if cls == "of_packet_out" and m_name == "actions":
- # See _PACKET_OUT_ACTION_LEN
- out.write("""
- {
- uint16_t %(m_name)s_len;
- buf_u16_get(buf + %(m_offset)s - 2, &%(m_name)s_len);
- if (%(m_name)s_len + %(m_offset)s > len) {
- VALIDATOR_LOG("Class %(cls)s, member %(m_name)s. "
- "Len %%d and offset %%d too big for %%d",
- %(m_name)s_len, %(m_offset)s, len);
- return -1;
- }
-""" % dict(m_name=m_name, m_offset=m_offset, cls=cls))
- elif version >= of_g.VERSION_1_2 and loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions":
- # See _FLOW_MOD_INSTRUCTIONS_OFFSET
- match_offset = v3_match_offset_get(cls)
- m_offset = '%s_offset' % m_name
- out.write("""
- {
- uint16_t %(m_name)s_len, %(m_name)s_offset;
- uint16_t match_len;
- buf_u16_get(buf + %(match_offset)s + 2, &match_len);
- %(m_name)s_offset = %(match_offset)s + OF_MATCH_BYTES(match_len);
- %(m_name)s_len = len - %(m_name)s_offset;
-""" % dict(m_name=m_name, cls=cls, match_offset=match_offset))
- elif cls == "of_bsn_gentable_entry_add" and m_name == "value":
- continue;
- elif cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "value":
- continue;
- elif cls == "of_bsn_gentable_entry_stats_entry" and m_name == "stats":
- continue;
- else:
- out.write("""
-
- { int %(m_name)s_len = len - %(m_offset)s;
-
-""" % dict(m_name=m_name, m_offset=m_offset))
- out.write("""
- if (%(m_cls)s_%(ver_name)s_validate(buf + %(m_offset)s, %(m_name)s_len) < 0) {
- return -1;
- }
- }
-""" % dict(m_name=m_name, m_cls=m_cls, ver_name=ver_name, m_offset=m_offset))
- out.write("""
- return 0;
-}
-""")
-
-def gen_list_validator(out, cls, version):
- ver_name = loxi_utils.version_to_name(version)
- e_cls = loxi_utils.list_to_entry_type(cls)
- fixed_len = of_g.base_length[(e_cls, version)];
- out.write("""
-static inline int
-%(cls)s_%(ver_name)s_validate(uint8_t *buf, int len)
-{
-""" % dict(cls=cls, ver_name=ver_name, cls_id=cls.upper(), e_cls=e_cls))
-
- # TLV16
- if loxi_utils.class_is_tlv16(e_cls):
- subclasses = type_maps.inheritance_map[e_cls]
- out.write("""\
- while (len >= %(fixed_len)s) {
- of_object_id_t e_id;
- uint16_t e_type, e_len;
- buf_u16_get(buf, &e_type);
- buf_u16_get(buf+2, &e_len);
- e_id = %(e_cls)s_to_object_id(e_type, %(ver_name)s);
- switch (e_id) {
-""" % dict(fixed_len=fixed_len, ver_name=ver_name, e_cls=e_cls))
- for subcls in subclasses:
- subcls = e_cls + '_' + subcls
- if not loxi_utils.class_in_version(subcls, version):
- continue
- out.write("""\
- case %(subcls_enum)s:
- if (%(subcls)s_%(ver_name)s_validate(buf, e_len) < 0) {
- return -1;
- }
- break;
-""" % dict(ver_name=ver_name, subcls=subcls, subcls_enum=loxi_utils.enum_name(subcls)))
- out.write("""\
- default:
- return -1;
- }
- buf += e_len;
- len -= e_len;
- }
- if (len != 0) {
- return -1;
- }
-""" % dict(e_cls=e_cls, ver_name=ver_name))
-
- # U16 len
- elif loxi_utils.class_is_u16_len(e_cls) or loxi_utils.class_is_action(e_cls):
- out.write("""\
- /* TODO verify U16 len elements */
-""" % dict())
-
- # OXM
- elif loxi_utils.class_is_oxm(e_cls):
- out.write("""\
- /* TODO verify OXM elements */
-""" % dict())
-
- # Fixed length
- elif not loxi_utils.class_is_var_len(e_cls, version):
- out.write("""\
- if ((len / %(fixed_len)s) * %(fixed_len)s != len) {
- return -1;
- }
-""" % dict(fixed_len=fixed_len))
-
- # ???
- else:
- out.write("""\
- /* XXX unknown element format */
-""" % dict())
-
- out.write("""
- return 0;
-}
-""")
diff --git a/c_gen/templates/loci_validator.c b/c_gen/templates/loci_validator.c
new file mode 100644
index 0000000..4a5a692
--- /dev/null
+++ b/c_gen/templates/loci_validator.c
@@ -0,0 +1,202 @@
+:: # Copyright 2014, 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 2014, 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.
+::
+:: include('_copyright.c')
+:: import loxi_globals
+:: from loxi_ir import *
+
+/**
+ *
+ * AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
+ *
+ * Source file for OpenFlow message validation.
+ *
+ */
+
+#include "loci_log.h"
+#include <loci/loci.h>
+#include <loci/loci_validator.h>
+
+#define VALIDATOR_LOG(...) LOCI_LOG_ERROR("Validator Error: " __VA_ARGS__)
+
+:: raw_validator_name = lambda cls, version: "loci_validate_%s_%s" % (cls, version.constant_version(prefix='OF_VERSION_'))
+:: validator_name = lambda ofclass: "loci_validate_%s_%s" % (ofclass.name, ofclass.protocol.version.constant_version(prefix='OF_VERSION_'))
+
+/* Forward declarations */
+:: for version, proto in loxi_globals.ir.items():
+:: for ofclass in proto.classes:
+static int __attribute__((unused)) ${validator_name(ofclass)}(uint8_t *data, int len, int *out_len);
+:: #endfor
+:: #endfor
+
+:: readers = { 1: 'buf_u8_get', 2: 'buf_u16_get', 4: 'buf_u32_get' }
+:: types = { 1: 'uint8_t', 2: 'uint16_t', 4: 'uint32_t' }
+
+:: for version, proto in loxi_globals.ir.items():
+
+:: # Identify classes in lists and generate list validators
+:: seen_lists = set()
+:: for ofclass in proto.classes:
+:: for m in ofclass.members:
+:: if type(m) == OFDataMember and m.oftype.startswith('list'):
+:: element_name = m.oftype[8:-3]
+:: if element_name in seen_lists:
+:: continue
+:: #endif
+:: seen_lists.add(element_name)
+:: list_validator_name = raw_validator_name('of_list_' + element_name, version)
+static int __attribute__((unused))
+${list_validator_name}(uint8_t *data, int len, int *out_len)
+{
+ while (len > 0) {
+ int cur_len = 0xffff;
+ if (${raw_validator_name('of_' + element_name, version)}(data, len, &cur_len) < 0) {
+ return -1;
+ }
+ len -= cur_len;
+ data += cur_len;
+ }
+
+ return 0;
+}
+
+:: #endif
+:: #endfor
+:: #endfor
+
+:: for ofclass in proto.classes:
+static int
+${validator_name(ofclass)}(uint8_t *data, int len, int *out_len)
+{
+ if (len < ${ofclass.base_length}) {
+ return -1;
+ }
+
+:: if ofclass.is_fixed_length:
+ len = ${ofclass.base_length};
+:: #endif
+
+:: # Read and validate length fields
+:: field_length_members = {}
+:: for m in ofclass.members:
+:: if type(m) == OFLengthMember:
+ ${types[m.length]} wire_len;
+ ${readers[m.length]}(data + ${m.offset}, &wire_len);
+ if (wire_len > len || wire_len < ${ofclass.base_length}) {
+ return -1;
+ }
+
+ len = wire_len;
+
+:: elif type(m) == OFFieldLengthMember:
+:: # Read field length members
+:: field_length_members[m.field_name] = m
+ ${types[m.length]} wire_len_${m.field_name};
+ ${readers[m.length]}(data + ${m.offset}, &wire_len_${m.field_name});
+
+:: #endif
+:: #endfor
+
+:: # Dispatch to subclass validators
+:: if ofclass.virtual:
+:: discriminator = ofclass.discriminator
+ ${types[discriminator.length]} wire_type;
+ ${readers[discriminator.length]}(data + ${discriminator.offset}, &wire_type);
+ switch (wire_type) {
+:: for subclass in proto.classes:
+:: if subclass.superclass == ofclass:
+ case ${subclass.member_by_name(discriminator.name).value}:
+ return ${validator_name(subclass)}(data, len, out_len);
+:: #endif
+:: #endfor
+ }
+:: #endif
+
+:: for m in ofclass.members:
+:: # Validate field-length members
+:: if type(m) == OFDataMember and m.name in field_length_members and m.offset is not None:
+ if (${m.offset} + wire_len_${m.name} > len) {
+ return -1;
+ }
+
+:: #endif
+:: if type(m) == OFDataMember and m.oftype.startswith('list'):
+:: # Validate lists
+:: if m.offset is None:
+ // TODO validate non fixed offset member ${m.name}
+:: continue
+:: #endif
+:: if not m.name in field_length_members:
+ int wire_len_${m.name} = len - ${m.offset};
+:: #endif
+:: element_name = m.oftype[8:-3]
+:: list_validator_name = raw_validator_name('of_list_' + element_name, version)
+ if (${list_validator_name}(data + ${m.offset}, wire_len_${m.name}, out_len) < 0) {
+ return -1;
+ }
+
+:: elif type(m) == OFDataMember and m.oftype == "of_match_t":
+ // TODO validate of_match_t
+:: elif type(m) == OFDataMember and m.oftype == "of_bsn_vport_t":
+ // TODO validate of_bsn_vport_t
+:: elif type(m) == OFDataMember and m.oftype == "of_meter_config_t":
+ // TODO validate of_meter_config_t
+:: elif type(m) == OFDataMember and m.oftype == "of_meter_features_t":
+ // TODO validate of_meter_features_t
+:: #endif
+:: #endfor
+
+ *out_len = len;
+ return 0;
+}
+
+:: #endfor
+:: #endfor
+
+int
+of_validate_message(of_message_t msg, int len)
+{
+ of_version_t version;
+ if (len < OF_MESSAGE_MIN_LENGTH ||
+ len != of_message_length_get(msg)) {
+ VALIDATOR_LOG("message length %d != %d", len,
+ of_message_length_get(msg));
+ return -1;
+ }
+
+ version = of_message_version_get(msg);
+ int out_len;
+ switch (version) {
+:: for version, proto in loxi_globals.ir.items():
+ case ${version.constant_version(prefix='OF_VERSION_')}:
+ return ${validator_name(proto.class_by_name('of_header'))}(msg, len, &out_len);
+:: #endfor
+ default:
+ VALIDATOR_LOG("Bad version %d", OF_VERSION_1_3);
+ return -1;
+ }
+}
diff --git a/c_gen/templates/loci_validator.h b/c_gen/templates/loci_validator.h
new file mode 100644
index 0000000..de8cce8
--- /dev/null
+++ b/c_gen/templates/loci_validator.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University */
+/* Copyright (c) 2011, 2012 Open Networking Foundation */
+/* Copyright (c) 2012, 2013 Big Switch Networks, Inc. */
+/* See the file LICENSE.loci which should have been included in the source distribution */
+
+/**
+ *
+ * AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
+ *
+ * Declarations of message validation functions. These functions check that an
+ * OpenFlow message is well formed. Specifically, they check internal length
+ * fields.
+ */
+
+#if !defined(_LOCI_VALIDATOR_H_)
+#define _LOCI_VALIDATOR_H_
+
+#include <loci/loci.h>
+
+/*
+ * Validate an OpenFlow message.
+ * @return 0 if message is valid, -1 otherwise.
+ */
+extern int of_validate_message(of_message_t msg, int len);
+
+#endif /* _LOCI_VALIDATOR_H_ */
diff --git a/lang_c.py b/lang_c.py
index 1969a83..24ccc06 100644
--- a/lang_c.py
+++ b/lang_c.py
@@ -39,7 +39,6 @@
import c_gen.c_test_gen as c_test_gen
import c_gen.c_dump_gen as c_dump_gen
import c_gen.c_show_gen as c_show_gen
-import c_gen.c_validator_gen as c_validator_gen
import c_gen.util
import c_gen.codegen
import loxi_utils.loxi_utils as loxi_utils
@@ -57,7 +56,7 @@
'loci/inc/loci/loci_doc.h': c_code_gen.gen_accessor_doc,
'loci/inc/loci/loci_obj_dump.h': c_dump_gen.gen_obj_dump_h,
'loci/inc/loci/loci_obj_show.h': c_show_gen.gen_obj_show_h,
- 'loci/inc/loci/loci_validator.h': c_validator_gen.gen_h,
+ 'loci/inc/loci/loci_validator.h': static,
# Static LOCI headers
'loci/inc/loci/bsn_ext.h': static,
@@ -75,7 +74,7 @@
'loci/src/of_match.c': c_code_gen.match_c_gen,
'loci/src/loci_obj_dump.c': c_dump_gen.gen_obj_dump_c,
'loci/src/loci_obj_show.c': c_show_gen.gen_obj_show_c,
- 'loci/src/loci_validator.c': c_validator_gen.gen_c,
+ 'loci/src/loci_validator.c': static,
# Static LOCI code
'loci/src/loci_int.h': static,