blob: 2930724dae83dad7f2cd89f168407a634b003933 [file] [log] [blame]
# 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 of_g
import loxi_front_end.match as match
import loxi_front_end.flags as flags
from generic_utils import *
import loxi_front_end.type_maps as type_maps
import loxi_utils.loxi_utils as loxi_utils
import loxi_front_end.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))
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;
}
""")