blob: cce4d095548c1ab4d4dec3944d9458825ec02017 [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.
"""
@file code_gen.py
Code generation functions for LOCI
"""
import sys
import c_gen.of_g_legacy as of_g
import c_match
from generic_utils import *
from c_gen import flags, type_maps, c_type_maps
import c_gen.loxi_utils_legacy as loxi_utils
from c_gen.loxi_utils_legacy import config_check
import loxi_globals
import c_gen.identifiers as identifiers
# 'property' is for queues. Could be trouble
################################################################
#
# Misc helper functions
#
################################################################
def h_file_to_define(name):
"""
Convert a .h file name to the define used for the header
"""
h_name = name[:-2].upper()
h_name = "_" + h_name + "_H_"
return h_name
def enum_name(cls):
"""
Return the name used for an enum identifier for the given class
@param cls The class name
"""
return loxi_utils.enum_name(cls)
def member_returns_val(cls, m_name):
"""
Should get accessor return a value rather than void
@param cls The class name
@param m_name The member name
@return True if of_g config and the specific member allow a
return value. Otherwise False
"""
m_type = of_g.unified[cls]["union"][m_name]["m_type"]
return (config_check("get_returns") =="value" and
m_type in of_g.of_scalar_types)
# TODO serialize match outside accessor?
def accessor_return_type(a_type, m_type):
if loxi_utils.accessor_returns_error(a_type, m_type):
return "int WARN_UNUSED_RESULT"
else:
return "void"
def accessor_return_success(a_type, m_type):
if loxi_utils.accessor_returns_error(a_type, m_type):
return "OF_ERROR_NONE"
else:
return ""
################################################################
#
# Per-file generators, mapped to jump table below
#
################################################################
def base_h_gen(out, name):
"""
Generate code for base header file
@param out The file handle to write to
@param name The name of the file
"""
common_top_matter(out, name)
base_h_content(out)
gen_object_enum(out)
out.write("""
/****************************************************************
*
* Experimenter IDs
*
****************************************************************/
""")
for name, val in of_g.experimenter_name_to_id.items():
out.write("#define OF_EXPERIMENTER_ID_%s 0x%08x\n" %
(name.upper(), val))
out.write("""
/****************************************************************
*
* OpenFlow Match version specific and generic defines
*
****************************************************************/
""")
c_match.gen_v4_match_compat(out)
c_match.gen_match_macros(out)
c_match.gen_oxm_defines(out)
out.write("\n#endif /* Base header file */\n")
def identifiers_gen(out, filename):
"""
Generate the macros for LOCI identifiers
@param out The file handle to write to
@param filename The name of the file
"""
common_top_matter(out, filename)
out.write("""
/**
* For each identifier from an OpenFlow header file, a Loxi version
* of the identifier is generated. For example, ofp_port_flood becomes
* OF_PORT_DEST_FLOOD. Loxi provides the following macros related to
* OpenFlow identifiers (using OF_IDENT_ as an example below):
* OF_IDENT_BY_VERSION(version) Get the value for the specific version
* OF_IDENT_SUPPORTED(version) Boolean: Is OF_IDENT defined for version
* OF_IDENT The common value across all versions if defined
* OF_IDENT_GENERIC A unique value across all OF identifiers
*
* For identifiers marked as flags, the following are also defined
* OF_IDENT_SET(flags, version)
* OF_IDENT_CLEAR(flags, version)
* OF_IDENT_TEST(flags, version)
*
* Notes:
*
* OF_IDENT_BY_VERSION(version) returns an undefined value
* if the passed version does not define OF_IDENT. It does not generate an
* error, nor record anything to the log file. If the value is the same
* across all defined versions, the version is ignored.
*
* OF_IDENT is only defined if the value is the same across all
* target LOXI versions FOR WHICH IT IS DEFINED. No error checking is
* done. This allows code to be written without requiring the version
* to be known or referenced when it doesn't matter. It does mean
* that when porting to a new version of OpenFlow, compile errors may
* occur. However, this is an indication that the existing code must
* be updated to account for a change in the semantics with the newly
* supported OpenFlow version.
*
* @fixme Currently we do not handle multi-bit flags or field values; for
* example, OF_TABLE_CONFIG_TABLE_MISS_CONTROLLER is the meaning for
* a zero value in the bits indicated by OF_TABLE_CONFIG_TABLE_MISS_MASK.
*
* @fixme Need to decide (or make a code gen option) on the requirement
* for defining OF_IDENT: Is it that all target versions define it and
* the agree? Or only that the versions which define it agree?
*/
""")
# Build value-by-version parameters and c_code
if len(of_g.target_version_list) > 1: # Supporting more than one version
vbv_params = []
vbv_code = ""
first = True
for version in of_g.target_version_list:
vbv_params.append("value_%s" % of_g.short_version_names[version])
if not first:
vbv_code += "\\\n "
else:
first = False
last_value = "value_%s" % of_g.short_version_names[version]
vbv_code += "((version) == %s) ? (%s) : " % \
(of_g.of_version_wire2name[version], last_value)
# @todo Using last value, can optimize out last ?
vbv_code += "(%s)" % last_value
out.write("""
/**
* @brief True for the special case of all versions supported
*/
#define OF_IDENT_IN_ALL_VERSIONS 1 /* Indicates identifier in all versions */
/**
* @brief General macro to map version to value where values given as params
*
* If unknown version is passed, use the latest version's value
*/
#define OF_VALUE_BY_VERSION(version, %s) \\
(%s)
/**
* @brief Generic set a flag
*/
#define OF_FLAG_SET(flags, mask) (flags) = (flags) | (mask)
/**
* @brief Generic test if a flag is set
*/
#define OF_FLAG_CLEAR(flags, mask) (flags) = (flags) & ~(mask)
/**
* @brief Generic test if a flag is set
*/
#define OF_FLAG_TEST(flags, mask) ((flags) & (mask) ? 1 : 0)
/**
* @brief Set a flag where the value is an enum indication of bit shift
*/
#define OF_FLAG_ENUM_SET(flags, e_val) OF_FLAG_SET(flags, 1 << (e_val))
/**
* @brief Clear a flag where the value is an enum indication of bit shift
*/
#define OF_FLAG_ENUM_CLEAR(flags, e_val) OF_FLAG_CLEAR(flags, 1 << (e_val))
/**
* @brief Test a flag where the value is an enum indication of bit shift
*/
#define OF_FLAG_ENUM_TEST(flags, e_val) OF_FLAG_TEST(flags, 1 << (e_val))
""" % (", ".join(vbv_params), vbv_code))
# For each group of identifiers, bunch ident defns
count = 1
keys = of_g.identifiers_by_group.keys()
keys.sort()
for group in keys:
idents = of_g.identifiers_by_group[group]
idents.sort()
out.write("""
/****************************************************************
* Identifiers from %s
*****************************************************************/
""" % group)
for ident in idents:
info = of_g.identifiers[ident]
keys = info["values_by_version"].keys()
keys.sort()
out.write("""
/*
* Defines for %(ident)s
* Original name %(ofp_name)s
*/
""" % dict(ident=ident, ofp_name=info["ofp_name"]))
# Generate supported versions macro
if len(keys) == len(of_g.target_version_list): # Defined for all
out.write("""\
#define %(ident)s_SUPPORTED(version) OF_IDENT_IN_ALL_VERSIONS
""" % dict(ident=ident))
else: # Undefined for some version
sup_list = []
for version in keys:
sup_list.append("((version) == %s)" %
of_g.of_version_wire2name[version])
out.write("""\
#define %(ident)s_SUPPORTED(version) \\
(%(sup_str)s)
""" % dict(ident=ident, sup_str=" || \\\n ".join(sup_list)))
# Generate value macro
if identifiers.defined_versions_agree(of_g.identifiers,
of_g.target_version_list,
ident):
out.write("""\
#define %(ident)s (%(value)#x)
#define %(ident)s_BY_VERSION(version) (%(value)#x)
""" % dict(ident=ident,value=info["common_value"]))
else: # Values differ between versions
# Generate version check and value by version
val_list = []
# Order of params matters
for version in of_g.target_version_list:
if version in info["values_by_version"]:
value = info["values_by_version"][version]
else:
value = identifiers.UNDEFINED_IDENT_VALUE
val_list.append("%#x" % value)
out.write("""\
#define %(ident)s_BY_VERSION(version) \\
OF_VALUE_BY_VERSION(version, %(val_str)s)
""" % dict(ident=ident, val_str=", ".join(val_list)))
if flags.ident_is_flag(ident):
log("Treating %s as a flag" % ident)
out.write("""
#define %(ident)s_SET(flags, version) \\
OF_FLAG_SET(flags, %(ident)s_BY_VERSION(version))
#define %(ident)s_TEST(flags, version) \\
OF_FLAG_TEST(flags, %(ident)s_BY_VERSION(version))
#define %(ident)s_CLEAR(flags, version) \\
OF_FLAG_CLEAR(flags, %(ident)s_BY_VERSION(version))
""" % dict(ident=ident))
out.write("#define %(ident)s_GENERIC %(count)d\n"
% dict(ident=ident, count=count))
count += 1 # This count should probably be promoted higher
log("Generated %d identifiers" % (count - 1))
out.write("\n#endif /* Loci identifiers header file */\n")
def base_h_external(out, filename):
"""
Copy contents of external file to base header
The contents of the filename are copied literally into the
out file handler. This allows openflow common defines to
be entered into the LoxiGen code base. The content of this
code must depend only on standard C headers.
"""
infile = open(filename, "r")
contents = infile.read()
out.write(contents)
infile.close()
def match_h_gen(out, name):
"""
Generate code for
@param out The file handle to write to
@param name The name of the file
"""
c_match.match_h_top_matter(out, name)
c_match.gen_incompat_members(out)
c_match.gen_match_struct(out)
c_match.gen_match_comp(out)
# c_match.gen_match_accessors(out)
out.write("\n#endif /* Match header file */\n")
def top_h_gen(out, name):
"""
Generate code for
@param out The file handle to write to
@param name The name of the file
"""
external_h_top_matter(out, name)
out.write("""
typedef enum loci_log_level {
LOCI_LOG_LEVEL_TRACE,
LOCI_LOG_LEVEL_VERBOSE,
LOCI_LOG_LEVEL_INFO,
LOCI_LOG_LEVEL_WARN,
LOCI_LOG_LEVEL_ERROR,
LOCI_LOG_LEVEL_MSG
} loci_log_level_t;
/**
* @brief Output a log message.
* @param level The log level.
* @param fname The function name.
* @param file The file name.
* @param line The line number.
* @param format The message format string.
*/
typedef int (*loci_logger_f)(loci_log_level_t level,
const char *fname, const char *file, int line,
const char *format, ...);
/*
* This variable should be set by the user of the library to redirect logs to
* their log infrastructure. The default drops all logs.
*/
extern loci_logger_f loci_logger;
/**
* Map a generic object to the underlying wire buffer
*
* Treat as private
*/
#define OF_OBJECT_TO_MESSAGE(obj) \\
((of_message_t)(WBUF_BUF((obj)->wire_object.wbuf)))
/**
* Macro for the fixed length part of an object
*
* @param obj The object whose extended length is being calculated
* @returns length in bytes of non-variable part of the object
*/
#define OF_OBJECT_FIXED_LENGTH(obj) \\
(of_object_fixed_len[(obj)->version][(obj)->object_id])
/**
* Return the length of the object beyond its fixed length
*
* @param obj The object whose extended length is being calculated
* @returns length in bytes of non-variable part of the object
*
* Most variable length fields are alone at the end of a structure.
* Their length is a simple calculation, just the total length of
* the parent minus the length of the non-variable part of the
* parent's class type.
*/
#define OF_OBJECT_VARIABLE_LENGTH(obj) \\
((obj)->length - OF_OBJECT_FIXED_LENGTH(obj))
/* FIXME: Where do these go? */
/* Low level maps btwn wire version + type and object ids */
extern int of_message_is_stats_request(int type, int w_ver);
extern int of_message_is_stats_reply(int type, int w_ver);
extern int of_message_stats_reply_to_object_id(int stats_type, int w_ver);
extern int of_message_stats_request_to_object_id(int stats_type, int w_ver);
extern int of_message_type_to_object_id(int type, int w_ver);
extern int of_wire_buffer_of_match_get(of_object_t *obj, int offset,
of_match_t *match);
extern int of_wire_buffer_of_match_set(of_object_t *obj, int offset,
of_match_t *match, int cur_len);
""")
# gen_base_types(out)
gen_struct_typedefs(out)
gen_acc_pointer_typedefs(out)
gen_new_function_declarations(out)
if config_check("gen_unified_fns"):
gen_accessor_declarations(out)
gen_common_struct_definitions(out)
gen_flow_add_setup_function_declarations(out)
if config_check("gen_fn_ptrs"): # Otherwise, all classes are from generic cls
gen_struct_definitions(out)
gen_generic_union(out)
gen_generics(out)
gen_top_static_functions(out)
out.write("""
/****************************************************************
*
* Declarations of maps between on-the-wire type values and LOCI identifiers
*
****************************************************************/
""")
c_type_maps.gen_type_maps_header(out)
c_type_maps.gen_type_data_header(out)
c_match.gen_declarations(out)
# @fixme Move debug stuff to own fn
out.write("""
/**
* Macro to check consistency of length for top level objects
*
* If the object has no parent then its length should match the
* underlying wire buffer's current bytes.
*/
#define OF_LENGTH_CHECK_ASSERT(obj) \\
ASSERT(((obj)->parent != NULL) || \\
((obj)->wire_object.wbuf == NULL) || \\
(WBUF_CURRENT_BYTES((obj)->wire_object.wbuf) == (obj)->length))
#define OF_DEBUG_DUMP
#if defined(OF_DEBUG_DUMP)
extern void dump_match(of_match_t *match);
#endif /* OF_DEBUG_DUMP */
""")
out.write("\n#endif /* Top header file */\n")
def match_c_gen(out, name):
"""
Generate code for
@param out The file handle to write to
@param name The name of the file
"""
c_match.match_c_top_matter(out, name)
c_match.gen_match_conversions(out)
c_match.gen_serialize(out)
c_match.gen_deserialize(out)
def type_data_c_gen(out, name):
common_top_matter(out, name)
c_type_maps.gen_type_maps(out)
c_type_maps.gen_length_array(out)
c_type_maps.gen_extra_length_array(out)
################################################################
# Top Matter
################################################################
def common_top_matter(out, name):
loxi_utils.gen_c_copy_license(out)
out.write("""\
/****************************************************************
* File: %s
*
* DO NOT EDIT
*
* This file is automatically generated
*
****************************************************************/
""" % name)
if name[-2:] == ".h":
out.write("""
#if !defined(%(h)s)
#define %(h)s
""" % dict(h=h_file_to_define(name)))
def base_h_content(out):
"""
Generate base header file content
@param out The output file object
"""
# @fixme Supported version should be generated based on input to LoxiGen
out.write("""
/*
* Base OpenFlow definitions. These depend only on standard C headers
*/
#include <string.h>
#include <stdint.h>
/* g++ requires this to pick up PRI, etc.
* See http://gcc.gnu.org/ml/gcc-help/2006-10/msg00223.html
*/
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <stdlib.h>
#include <assert.h>
#include <loci/loci_idents.h>
/**
* Macro to enable debugging for LOCI.
*
* This enables debug output to stdout.
*/
#define OF_DEBUG_ENABLE
#if defined(OF_DEBUG_ENABLE)
#include <stdio.h> /* Currently for debugging */
#define FIXME(str) do { \\
fprintf(stderr, "%s\\n", str); \\
exit(1); \\
} while (0)
#define debug printf
#else
#define FIXME(str)
#define debug(str, ...)
#endif /* OF_DEBUG_ENABLE */
/**
* The type of a function used by the LOCI dump/show functions to
* output text. Essentially the same signature as fprintf. May
* be called many times per invocation of e.g. of_object_show().
*/
typedef int (*loci_writer_f)(void *cookie, const char *fmt, ...);
/**
* Check if a version is supported
*/
#define OF_VERSION_OKAY(v) ((v) >= OF_VERSION_1_0 && (v) <= OF_VERSION_1_3)
""")
gen_version_enum(out)
out.write("\n")
# for c_name in of_g.ofp_constants:
# val = str(of_g.ofp_constants[c_name])
# out.write("#define %s %s\n" % (c_name, val))
# out.write("\n")
out.write("""
typedef enum of_error_codes_e {
OF_ERROR_NONE = 0,
OF_ERROR_RESOURCE = -1, /* Could not allocate space */
OF_ERROR_PARAM = -2, /* Bad parameter */
OF_ERROR_VERSION = -3, /* Version not supported */
OF_ERROR_RANGE = -4, /* End of list indication */
OF_ERROR_COMPAT = -5, /* Incompatible assignment */
OF_ERROR_PARSE = -6, /* Error in parsing data */
OF_ERROR_INIT = -7, /* Uninitialized data */
OF_ERROR_UNKNOWN = -8 /* Unknown error */
} of_error_codes_t;
#define OF_ERROR_STRINGS "none", \\
"resource", \\
"parameter", \\
"version", \\
"range", \\
"incompatible", \\
"parse", \\
"init", \\
"unknown"
extern const char *const of_error_strings[];
#ifndef NDEBUG
/* #define ASSERT(val) assert(val) */
#define FORCE_FAULT *(volatile int *)0 = 1
#define ASSERT(val) if (!(val)) \\
fprintf(stderr, "\\nASSERT %s. %s:%d\\n", #val, __FILE__, __LINE__), \\
FORCE_FAULT
#else
#define ASSERT(val)
#endif
/*
* Some LOCI object accessors can fail, and it's easy to forget to check.
* On certain compilers we can trigger a warning if the error code
* is ignored.
*/
#ifndef DISABLE_WARN_UNUSED_RESULT
#ifdef __GNUC__
#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
#else
#define WARN_UNUSED_RESULT
#endif
#else
#define WARN_UNUSED_RESULT
#endif
typedef union of_generic_u of_generic_t;
typedef struct of_object_s of_object_t;
/* Define ipv4 address as uint32 */
typedef uint32_t of_ipv4_t;
/* Table ID is the OF standard uint8 */
typedef uint8_t of_table_id_t;
#define OF_MAC_ADDR_BYTES 6
typedef struct of_mac_addr_s {
uint8_t addr[OF_MAC_ADDR_BYTES];
} of_mac_addr_t;
#define OF_IPV6_BYTES 16
typedef struct of_ipv6_s {
uint8_t addr[OF_IPV6_BYTES];
} of_ipv6_t;
extern const of_mac_addr_t of_mac_addr_all_ones;
extern const of_mac_addr_t of_mac_addr_all_zeros;
extern const of_ipv6_t of_ipv6_all_ones;
extern const of_ipv6_t of_ipv6_all_zeros;
/**
* Generic zero and all-ones values of size 16 bytes.
*
* IPv6 is longest data type we worry about for comparisons
*/
#define of_all_zero_value of_ipv6_all_zeros
#define of_all_ones_value of_ipv6_all_ones
/**
* Non-zero/all ones check for arbitrary type of size <= 16 bytes
*/
#define OF_VARIABLE_IS_NON_ZERO(_ptr) \\
(MEMCMP(&of_all_zero_value, (_ptr), sizeof(*(_ptr))))
#define OF_VARIABLE_IS_ALL_ONES(_ptr) \\
(!MEMCMP(&of_all_ones_value, (_ptr), sizeof(*(_ptr))))
/* The octets object is a struct holding pointer and length */
typedef struct of_octets_s {
uint8_t *data;
int bytes;
} of_octets_t;
/* Macro to convert an octet object to a pointer; currently trivial */
#define OF_OCTETS_POINTER_GET(octet_ptr) ((octet_ptr)->data)
#define OF_OCTETS_POINTER_SET(octet_ptr, ptr) (octet_ptr)->data = (ptr)
#define OF_OCTETS_BYTES_GET(octet_ptr) ((octet_ptr)->bytes)
#define OF_OCTETS_BYTES_SET(octet_ptr, bytes) (octet_ptr)->bytes = (bytes)
/* Currently these are categorized as scalars */
typedef char of_port_name_t[OF_MAX_PORT_NAME_LEN];
typedef char of_table_name_t[OF_MAX_TABLE_NAME_LEN];
typedef char of_desc_str_t[OF_DESC_STR_LEN];
typedef char of_serial_num_t[OF_SERIAL_NUM_LEN];
typedef struct of_bitmap_128_s {
uint64_t hi;
uint64_t lo;
} of_bitmap_128_t;
/* These are types which change across versions. */
typedef uint32_t of_port_no_t;
typedef uint16_t of_fm_cmd_t;
typedef uint64_t of_wc_bmap_t;
typedef uint64_t of_match_bmap_t;
#define MEMMOVE(dest, src, bytes) memmove(dest, src, bytes)
#define MEMSET(dest, val, bytes) memset(dest, val, bytes)
#define MEMCPY(dest, src, bytes) memcpy(dest, src, bytes)
#define MEMCMP(a, b, bytes) memcmp(a, b, bytes)
#define MALLOC(bytes) malloc(bytes)
#define FREE(ptr) free(ptr)
/** Try an operation and return on failure. */
#define OF_TRY(op) do { \\
int _rv; \\
if ((_rv = (op)) < 0) { \\
LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
return _rv; \\
} \\
} while (0)
/* The extent of an OF match object is determined by its length field, but
* aligned to 8 bytes
*/
#define OF_MATCH_BYTES(length) (((length) + 7) & 0xfff8)
#if __BYTE_ORDER == __BIG_ENDIAN
#define U16_NTOH(val) (val)
#define U32_NTOH(val) (val)
#define U64_NTOH(val) (val)
#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
#define U16_HTON(val) (val)
#define U32_HTON(val) (val)
#define U64_HTON(val) (val)
#define IPV6_HTON(dst, src) /* NOTE different syntax; currently no-op */
#else /* Little Endian */
#define U16_NTOH(val) (((val) >> 8) | ((val) << 8))
#define U32_NTOH(val) ((((val) & 0xff000000) >> 24) | \\
(((val) & 0x00ff0000) >> 8) | \\
(((val) & 0x0000ff00) << 8) | \\
(((val) & 0x000000ff) << 24))
#define U64_NTOH(val) ((((val) & 0xff00000000000000LL) >> 56) | \\
(((val) & 0x00ff000000000000LL) >> 40) | \\
(((val) & 0x0000ff0000000000LL) >> 24) | \\
(((val) & 0x000000ff00000000LL) >> 8) | \\
(((val) & 0x00000000ff000000LL) << 8) | \\
(((val) & 0x0000000000ff0000LL) << 24) | \\
(((val) & 0x000000000000ff00LL) << 40) | \\
(((val) & 0x00000000000000ffLL) << 56))
#define IPV6_NTOH(dst, src) /* NOTE different syntax; currently no-op */
#define U16_HTON(val) U16_NTOH(val)
#define U32_HTON(val) U32_NTOH(val)
#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):
"""
Generate top matter for external header file
@param name The name of the output file
@param out The output file object
"""
common_top_matter(out, name)
out.write("""
#include <loci/loci_base.h>
#include <loci/of_message.h>
#include <loci/of_match.h>
#include <loci/of_object.h>
#include <loci/of_wire_buf.h>
/****************************************************************
*
* This file is divided into the following sections.
*
* A few object specific macros
* Class typedefs (no struct definitions)
* Per-data type accessor function typedefs
* Per-class new/delete function typedefs
* Per-class static delete functions
* Per-class, per-member accessor declarations
* Per-class structure definitions
* Generic union (inheritance) definitions
* Pointer set function declarations
* Some special case macros
*
****************************************************************/
""")
def gen_top_static_functions(out):
out.write("""
#define _MAX_PARENT_ITERATIONS 4
/**
* Iteratively update parent lengths thru hierarchy
* @param obj The object whose length is being updated
* @param delta The difference between the current and new lengths
*
* Note that this includes updating the object itself. It will
* iterate thru parents.
*
* Assumes delta > 0.
*/
static inline void
of_object_parent_length_update(of_object_t *obj, int delta)
{
#ifndef NDEBUG
int count = 0;
of_wire_buffer_t *wbuf; /* For debug asserts only */
#endif
while (obj != NULL) {
ASSERT(count++ < _MAX_PARENT_ITERATIONS);
obj->length += delta;
if (obj->wire_length_set != NULL) {
obj->wire_length_set(obj, obj->length);
}
#ifndef NDEBUG
wbuf = obj->wire_object.wbuf;
#endif
/* Asserts for wire length checking */
ASSERT(obj->length + obj->wire_object.obj_offset <=
WBUF_CURRENT_BYTES(wbuf));
if (obj->parent == NULL) {
ASSERT(obj->length + obj->wire_object.obj_offset ==
WBUF_CURRENT_BYTES(wbuf));
}
obj = obj->parent;
}
}
""")
################################################################
#
################################################################
def gen_version_enum(out):
"""
Generate the enumerated type for versions in LoxiGen
@param out The file object to which to write the decs
This just uses the wire versions for now
"""
out.write("""
/**
* Enumeration of OpenFlow versions
*
* The wire protocol numbers are currently used for values of the corresponding
* version identifiers.
*/
typedef enum of_version_e {
OF_VERSION_UNKNOWN = 0,
""")
is_first = True
max = 0
for v in of_g.wire_ver_map:
if is_first:
is_first = False
else:
out.write(",\n")
if v > max:
max = v
out.write(" %s = %d" % (of_g.wire_ver_map[v], v))
out.write("""
} of_version_t;
/**
* @brief Use this when declaring arrays indexed by wire version
*/
#define OF_VERSION_ARRAY_MAX %d
""" % (max + 1))
def gen_object_enum(out):
"""
Generate the enumerated type for object identification in LoxiGen
@param out The file object to which to write the decs
"""
out.write("""
/**
* Enumeration of OpenFlow objects
*
* We enumerate the OpenFlow objects used internally. Note that some
* message types are determined both by an outer type (message type like
* stats_request) and an inner type (port stats). These are different
* messages in ofC.
*
* These values are for internal use only. They will change with
* different versions of ofC.
*/
typedef enum of_object_id_e {
/* Root object type */
OF_OBJECT_INVALID = -1, /* "invalid" return value for mappings */
OF_OBJECT = 0, /* Generic, untyped object */
/* OpenFlow message objects */
""")
last = 0
msg_count = 0
for cls in of_g.ordered_messages:
out.write(" %s = %d,\n" % (enum_name(cls),
of_g.unified[cls]["object_id"]))
msg_count = of_g.unified[cls]["object_id"] + 1
out.write("\n /* Non-message objects */\n")
for cls in of_g.ordered_non_messages:
out.write(" %s = %d,\n" % (enum_name(cls),
of_g.unified[cls]["object_id"]))
last = of_g.unified[cls]["object_id"]
out.write("\n /* List objects */\n")
for cls in of_g.ordered_list_objects:
out.write(" %s = %d,\n" % (enum_name(cls),
of_g.unified[cls]["object_id"]))
last = of_g.unified[cls]["object_id"]
out.write("\n /* Generic stats request/reply types; pseudo objects */\n")
for cls in of_g.ordered_pseudo_objects:
out.write(" %s = %d,\n" % (enum_name(cls),
of_g.unified[cls]["object_id"]))
last = of_g.unified[cls]["object_id"]
out.write("""
OF_OBJECT_COUNT = %d
} of_object_id_t;
extern const char *const of_object_id_str[];
#define OF_MESSAGE_OBJECT_COUNT %d
""" % ((last + 1), msg_count))
# Generate object type range checking for inheritance classes
# @fixme These should be determined algorithmicly
out.write("""
/*
* Macros to check if an object ID is within an inheritance class range
*/
""")
# Alphabetical order for 'last'
last_ids = dict(of_action="OF_ACTION_STRIP_VLAN",
of_oxm="OF_OXM_VLAN_VID_MASKED",
of_instruction="OF_INSTRUCTION_WRITE_METADATA",
of_queue_prop="OF_QUEUE_PROP_MIN_RATE",
of_table_feature_prop="OF_TABLE_FEATURE_PROP_WRITE_SETFIELD_MISS",
# @FIXME add meter_band ?
)
for cls, last in last_ids.items():
out.write("""
#define %(enum)s_FIRST_ID (%(enum)s + 1)
#define %(enum)s_LAST_ID %(last)s
#define %(enum)s_VALID_ID(id) \\
((id) >= %(enum)s_FIRST_ID && \\
(id) <= %(enum)s_LAST_ID)
""" % dict(enum=enum_name(cls), last=last))
out.write("""
/**
* Function to check a wire ID
* @param object_id The ID to check
* @param base_object_id The inheritance parent, if applicable
* @returns boolean: If base_object_id is an inheritance class, check if
* object_id is valid as a subclass. Otherwise return 1.
*
* Note: Could check that object_id == base_object_id in the
* second case.
*/
static inline int
of_wire_id_valid(int object_id, int base_object_id) {
switch (base_object_id) {
case OF_ACTION:
return OF_ACTION_VALID_ID(object_id);
case OF_OXM:
return OF_OXM_VALID_ID(object_id);
case OF_QUEUE_PROP:
return OF_QUEUE_PROP_VALID_ID(object_id);
case OF_TABLE_FEATURE_PROP:
return OF_TABLE_FEATURE_PROP_VALID_ID(object_id);
case OF_INSTRUCTION:
return OF_INSTRUCTION_VALID_ID(object_id);
default:
break;
}
return 1;
}
""")
################################################################
#
# Internal Utility Functions
#
################################################################
def acc_name(cls, m_name):
"""
Generate the root name of an accessor function for typedef
@param cls The class name
@param m_name The member name
"""
(m_type, get_rv) = get_acc_rv(cls, m_name)
return "%s_%s" % (cls, m_type)
def get_acc_rv(cls, m_name):
"""
Determine the data type and return type for a get accessor.
The return type may be "void" or it may be the accessor type
depending on the system configuration and on the data type.
@param cls The class name
@param m_name The member name
@return A pair (m_type, rv) where m_type is the unified type of the
member and rv is the get_accessor return type
"""
member = of_g.unified[cls]["union"][m_name]
m_type = member["m_type"]
rv = "int"
if member_returns_val(cls, m_name):
rv = m_type
if m_type[-2:] == "_t":
m_type = m_type[:-2]
return (m_type, rv)
def param_list(cls, m_name, a_type):
"""
Generate the parameter list (no parens) for an a_type accessor
@param cls The class name
@param m_name The member name
@param a_type One of "set" or "get" or TBD
"""
member = of_g.unified[cls]["union"][m_name]
m_type = member["m_type"]
params = ["%s_t *obj" % cls]
if a_type == "set":
if loxi_utils.type_is_scalar(m_type):
params.append("%s %s" % (m_type, m_name))
else:
params.append("%s *%s" % (m_type, m_name))
elif a_type in ["get", "bind"]:
params.append("%s *%s" % (m_type, m_name))
else:
debug("Class %s, name %s Bad param list a_type: %s" %
(cls, m_name, a_type))
sys.exit(1)
return params
def typed_function_base(cls, m_name):
"""
Generate the core name for accessors based on the type
@param cls The class name
@param m_name The member name
"""
(m_type, get_rv) = get_acc_rv(cls, m_name)
return "%s_%s" % (cls, m_type)
def member_function_base(cls, m_name):
"""
Generate the core name for accessors based on the member name
@param cls The class name
@param m_name The member name
"""
return "%s_%s" % (cls, m_name)
def field_ver_get(cls, m_name):
"""
Generate a dict, indexed by wire version, giving a pair (type, offset)
@param cls The class name
@param m_name The name of the class member
If offset is not known for m_name, the type
A dict is used for more convenient indexing.
"""
result = {}
for ver in of_g.unified[cls]:
if type(ver) == type(0): # It's a version
if "use_version" in of_g.unified[cls][ver]: # deref version ref
ref_ver = of_g.unified[cls][ver]["use_version"]
members = of_g.unified[cls][ref_ver]["members"]
else:
members = of_g.unified[cls][ver]["members"]
idx = loxi_utils.member_to_index(m_name, members)
if (idx < 0):
continue # Member not in this version
m_type = members[idx]["m_type"]
offset = members[idx]["offset"]
# If m_type is mixed, get wire version type from global data
if m_type in of_g.of_mixed_types and \
ver in of_g.of_mixed_types[m_type]:
m_type = of_g.of_mixed_types[m_type][ver]
# add version to result list
result[ver] = (m_type, offset)
return result
def v3_match_offset_get(cls):
"""
Return the offset of an OF 1.2 match in an object if it has such;
otherwise return -1
"""
result = field_ver_get(cls, "match")
if of_g.VERSION_1_2 in result:
if result[of_g.VERSION_1_2][0] == "of_match_v3_t":
return result[of_g.VERSION_1_2][1]
return -1
################################################################
#
# OpenFlow Object Definitions
#
################################################################
def gen_of_object_defs(out):
"""
Generate low level of_object core operations
@param out The file for output, already open
"""
def gen_generics(out):
for (cls, subclasses) in type_maps.inheritance_map.items():
out.write("""
/**
* Inheritance super class for %(cls)s
*
* This class is the union of %(cls)s classes. You can refer
* to it untyped by refering to the member 'header' whose structure
* is common across all sub-classes.
*/
union %(cls)s_u {
%(cls)s_header_t header; /* Generic instance */
""" % dict(cls=cls))
for subcls in sorted(subclasses):
out.write(" %s_%s_t %s;\n" % (cls, subcls, subcls))
out.write("};\n")
def gen_struct_typedefs(out):
"""
Generate typedefs for all struct objects
@param out The file for output, already open
"""
out.write("\n/* LOCI inheritance parent typedefs */\n")
for cls in type_maps.inheritance_map:
out.write("typedef union %(cls)s_u %(cls)s_t;\n" % dict(cls=cls))
out.write("\n/* LOCI object typedefs */\n")
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
continue
if config_check("gen_fn_ptrs"):
out.write("typedef struct %(cls)s_s %(cls)s_t;\n" % dict(cls=cls))
else:
template = "typedef of_object_t %(cls)s_t;\n"
out.write(template % dict(cls=cls))
out.write("""
/****************************************************************
*
* Additional of_object defines
* These are needed for some static inline ops, so we put them here.
*
****************************************************************/
/* Delete an OpenFlow object without reference to its type */
extern void of_object_delete(of_object_t *obj);
""")
def gen_generic_union(out):
"""
Generate the generic union object composing all LOCI objects
@param out The file to which to write the decs
"""
out.write("""
/**
* The common LOCI object is a union of all possible objects.
*/
union of_generic_u {
of_object_t object; /* Common base class with fundamental accessors */
/* Message objects */
""")
for cls in of_g.ordered_messages:
out.write(" %s_t %s;\n" % (cls, cls))
out.write("\n /* Non-message composite objects */\n")
for cls in of_g.ordered_non_messages:
if cls in type_maps.inheritance_map:
continue
out.write(" %s_t %s;\n" % (cls, cls))
out.write("\n /* List objects */\n")
for cls in of_g.ordered_list_objects:
out.write(" %s_t %s;\n" % (cls, cls))
out.write("};\n")
def gen_common_struct_definitions(out):
out.write("""
/****************************************************************
*
* Unified structure definitions
*
****************************************************************/
struct of_object_s {
/* Common members */
%(common)s
};
""" % dict(common=of_g.base_object_members))
def gen_flow_add_setup_function_declarations(out):
"""
Add the declarations for functions that can be initialized
by a flow add. These are defined external to LOXI.
"""
out.write("""
/****************************************************************
* Functions for objects that can be initialized by a flow add message.
* These are defined in a non-autogenerated file
****************************************************************/
/**
* @brief Set up a flow removed message from the original add
* @param obj The flow removed message being updated
* @param flow_add The flow_add message to use
*
* Initialize the following fields of obj to be identical
* to what was originally on the wire from the flow_add object:
* match
* cookie
* priority
* idle_timeout
* hard_timeout
*
*/
extern int
of_flow_removed_setup_from_flow_add(of_flow_removed_t *obj,
of_flow_add_t *flow_add);
/**
* @brief Set up the packet in match structure from the original add
* @param obj The packet in message being updated
* @param flow_add The flow_add message to use
* @returns Indigo error code. Does not return a version error if
* the version does not require initializing obj.
*
* Initialize the match member of obj to be identical to what was originally
* on the wire from the flow_add object. If applicable, the table ID is also
* initialized from the flow_add object.
*
* This API applies to 1.2 and later only.
*/
extern int
of_packet_in_setup_from_flow_add(of_packet_in_t *obj,
of_flow_add_t *flow_add);
/**
* @brief Set up the flow stats entry from the original add
* @param obj The packet in message being updated
* @param flow_add The flow_add message to use
* @param effects Optional actions or instructions; see below.
*
* Initialize the following fields of obj to be identical
* to what was originally on the wire from the flow_add object:
* match
* actions/instructions (effects)
* cookie
* priority
* idle_timeout
* hard_timeout
*
* Note that the actions/instructions of a flow may be modified by a
* subsequent flow modify message. To facilitate implementations,
* the "effects" parameter is provided. If effects is NULL, the
* actions/instructions are taken from the flow_add message.
* Otherwise, effects is coerced to the proper type (actions or
* instructions) and used to init obj.
*/
extern int
of_flow_stats_entry_setup_from_flow_add(of_flow_stats_entry_t *obj,
of_flow_add_t *flow_add,
of_object_t *effects);
""")
def gen_struct_definitions(out):
"""
Generate the declaration of all of_ C structures
@param out The file to which to write the decs
"""
# This should only get called if gen_fn_ptr is true in code_gen_config
if not config_check("gen_fn_ptrs"):
debug("Error: gen_struct_defs called, but no fn ptrs set")
return
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
continue # These are generated elsewhere
note = ""
if loxi_utils.class_is_message(cls):
note = " /* Class is message */"
out.write("struct %s_s {%s\n" % (cls, note))
out.write(""" /* Common members */
%s
/* Class specific members */
""" % of_g.base_object_members)
if loxi_utils.class_is_list(cls):
out.write("""
%(cls)s_first_f first;
%(cls)s_next_f next;
%(cls)s_append_bind_f append_bind;
%(cls)s_append_f append;
};
""" % {"cls": cls})
continue # All done with list object
# Else, not a list instance; add accessors for all data members
for m_name in of_g.ordered_members[cls]:
if m_name in of_g.skip_members:
# These members (length, etc) are handled internally
continue
f_name = acc_name(cls, m_name)
out.write(" %s_get_f %s;\n" % (f_name, m_name + "_get"))
out.write(" %s_set_f %s;\n" % (f_name, m_name + "_set"))
out.write("};\n\n")
################################################################
#
# List accessor code generation
#
# Currently these all implement copy on read semantics
#
################################################################
def init_call(e_type, obj, ver, length, cw):
"""
Generate the init call given the strings for params
"""
hdr = "" # If inheritance type, coerce to hdr object
obj_name = obj
if e_type in type_maps.inheritance_map:
hdr = "_header"
obj_name = "(%s_header_t *)" % e_type + obj
return """\
%(e_type)s%(hdr)s_init(%(obj_name)s,
%(ver)s, %(length)s, %(cw)s)\
""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
length=length, cw=cw)
def gen_list_first(out, cls, e_type):
"""
Generate the body of a list_first operation
@param cls The class name for which code is being generated
@param e_type The element type of the list
@param out The file to which to write
"""
i_call = init_call(e_type, "obj", "list->version", "0", "1")
if e_type in type_maps.inheritance_map:
len_str = "obj->header.length"
else:
len_str = "obj->length"
out.write("""
/**
* Associate an iterator with a list
* @param list The list to iterate over
* @param obj The list entry iteration pointer
* @return OF_ERROR_RANGE if the list is empty (end of list)
*
* The obj instance is completely initialized. The caller is responsible
* for cleaning up any wire buffers associated with obj before this call
*/
int
%(cls)s_first(%(cls)s_t *list,
%(e_type)s_t *obj)
{
int rv;
%(i_call)s;
if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
return rv;
}
""" % dict(cls=cls, e_type=e_type, i_call=i_call))
# Special case flow_stats_entry lists
out.write("""
of_object_wire_init((of_object_t *) obj, %(u_type)s,
list->length);
if (%(len_str)s == 0) {
return OF_ERROR_PARSE;
}
return rv;
}
""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
def gen_bind(out, cls, m_name, m_type):
"""
Generate the body of a bind function
@param out The file to which to write
@param cls The class name for which code is being generated
@param m_name The name of the data member
@param m_type The type of the data member
"""
bparams = ",\n ".join(param_list(cls, m_name, "bind"))
i_call = init_call(e_type, "child", "parent->version", "0", "1")
out.write("""
/**
* Bind the child object to the parent object for read processing
* @param parent The parent object
* @param child The child object
*
* The child obj instance is completely initialized.
*/
int
%(cls)s_%(m_name)_bind(%(cls)s_t *parent,
%(e_type)s_t *child)
{
int rv;
%(i_call)s;
/* Derive offset and length of child in parent */
OF_TRY(of_object_child_attach(parent, child,
if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
return rv;
}
""" % dict(cls=cls, e_type=e_type, i_call=i_call))
# Special case flow_stats_entry lists
out.write("""
rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
list->length);
if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
return OF_ERROR_PARSE;
}
return rv;
}
""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
def gen_list_next(out, cls, e_type):
"""
Generate the body of a list_next operation
@param cls The class name for which code is being generated
@param e_type The element type of the list
@param out The file to which to write
"""
if e_type in type_maps.inheritance_map:
len_str = "obj->header.length"
else:
len_str = "obj->length"
out.write("""
/**
* Advance an iterator to the next element in a list
* @param list The list being iterated
* @param obj The list entry iteration pointer
* @return OF_ERROR_RANGE if already at the last entry on the list
*
*/
int
%(cls)s_next(%(cls)s_t *list,
%(e_type)s_t *obj)
{
int rv;
if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
return rv;
}
rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
list->length);
if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
return OF_ERROR_PARSE;
}
return rv;
}
""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
def gen_list_append(out, cls, e_type):
"""
Generate the body of a list append functions
@param cls The class name for which code is being generated
@param e_type The element type of the list
@param out The file to which to write
"""
out.write("""
/**
* Set up to append an object of type %(e_type)s to an %(cls)s.
* @param list The list that is prepared for append
* @param obj Pointer to object to hold data to append
*
* The obj instance is completely initialized. The caller is responsible
* for cleaning up any wire buffers associated with obj before this call.
*
* See the generic documentation for of_list_append_bind.
*/
int
%(cls)s_append_bind(%(cls)s_t *list,
%(e_type)s_t *obj)
{
return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
}
/**
* Append an item to a %(cls)s list.
*
* This copies data from item and leaves item untouched.
*
* See the generic documentation for of_list_append.
*/
int
%(cls)s_append(%(cls)s_t *list,
%(e_type)s_t *item)
{
return of_list_append((of_object_t *)list, (of_object_t *)item);
}
""" % dict(cls=cls, e_type=e_type))
def gen_list_accessors(out, cls):
e_type = loxi_utils.list_to_entry_type(cls)
gen_list_first(out, cls, e_type)
gen_list_next(out, cls, e_type)
gen_list_append(out, cls, e_type)
################################################################
#
# Accessor Functions
#
################################################################
def gen_accessor_declarations(out):
"""
Generate the declaration of each version independent accessor
@param out The file to which to write the decs
"""
out.write("""
/****************************************************************
*
* Unified, per-member accessor function declarations
*
****************************************************************/
""")
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
continue
out.write("\n/* Unified accessor functions for %s */\n" % cls)
for m_name in of_g.ordered_members[cls]:
if m_name in of_g.skip_members:
continue
m_type = loxi_utils.member_base_type(cls, m_name)
base_name = "%s_%s" % (cls, m_name)
gparams = ",\n ".join(param_list(cls, m_name, "get"))
get_ret_type = accessor_return_type("get", m_type)
sparams = ",\n ".join(param_list(cls, m_name, "set"))
set_ret_type = accessor_return_type("set", m_type)
bparams = ",\n ".join(param_list(cls, m_name, "bind"))
bind_ret_type = accessor_return_type("bind", m_type)
if loxi_utils.type_is_of_object(m_type):
# Generate bind accessors, but not get accessor
out.write("""
extern %(set_ret_type)s %(base_name)s_set(
%(sparams)s);
extern %(bind_ret_type)s %(base_name)s_bind(
%(bparams)s);
extern %(m_type)s *%(cls)s_%(m_name)s_get(
%(cls)s_t *obj);
""" % dict(base_name=base_name, sparams=sparams, bparams=bparams,
m_name=m_name, m_type=m_type, cls=cls,
set_ret_type=set_ret_type, bind_ret_type=bind_ret_type))
else:
out.write("""
extern %(set_ret_type)s %(base_name)s_set(
%(sparams)s);
extern %(get_ret_type)s %(base_name)s_get(
%(gparams)s);
""" % dict(base_name=base_name, gparams=gparams, sparams=sparams,
get_ret_type=get_ret_type, set_ret_type=set_ret_type))
if loxi_utils.class_is_list(cls):
e_type = loxi_utils.list_to_entry_type(cls)
out.write("""
extern int %(cls)s_first(
%(cls)s_t *list, %(e_type)s_t *obj);
extern int %(cls)s_next(
%(cls)s_t *list, %(e_type)s_t *obj);
extern int %(cls)s_append_bind(
%(cls)s_t *list, %(e_type)s_t *obj);
extern int %(cls)s_append(
%(cls)s_t *list, %(e_type)s_t *obj);
/**
* Iteration macro for list of type %(cls)s
* @param list Pointer to the list being iterated over of
* type %(cls)s
* @param elt Pointer to an element of type %(e_type)s
* @param rv On exiting the loop will have the value OF_ERROR_RANGE.
*/
#define %(u_cls)s_ITER(list, elt, rv) \\
for ((rv) = %(cls)s_first((list), (elt)); \\
(rv) == OF_ERROR_NONE; \\
(rv) = %(cls)s_next((list), (elt)))
""" % dict(u_cls=cls.upper(), cls=cls, e_type=e_type))
def wire_accessor(m_type, a_type):
"""
Returns the name of the a_type accessor for low level wire buff offset
@param m_type The member type
@param a_type The accessor type (set or get)
"""
# Strip off _t if present
if m_type in of_g.of_base_types:
m_type = of_g.of_base_types[m_type]["short_name"]
if m_type in of_g.of_mixed_types:
m_type = of_g.of_mixed_types[m_type]["short_name"]
if m_type[-2:] == "_t":
m_type = m_type[:-2]
if m_type == "octets":
m_type = "octets_data"
return "of_wire_buffer_%s_%s" % (m_type, a_type)
def get_len_macro(cls, m_type, version):
"""
Get the length macro for m_type in cls
"""
if m_type.find("of_match") == 0:
return "_WIRE_MATCH_PADDED_LEN(obj, offset)"
if m_type.find("of_list_oxm") == 0:
return "wire_match_len(obj, 0) - 4"
if loxi_utils.class_is_tlv16(m_type):
return "_TLV16_LEN(obj, offset)"
if cls == "of_packet_out" and m_type == "of_list_action_t":
return "_PACKET_OUT_ACTION_LEN(obj)"
# Default is everything to the end of the object
return "_END_LEN(obj, offset)"
def gen_accessor_offsets(out, cls, m_name, version, a_type, m_type, offset):
"""
Generate the sub-object offset and length calculations for accessors
@param out File being written
@param m_name Name of member
@param version Wire version being processed
@param a_type The accessor type (set or get)
@param m_type The original member type
@param offset The offset of the object or -1 if not fixed
"""
# determine offset
o_str = "%d" % offset # Default is fixed length
if offset == -1:
# There are currently 4 special cases for this
# In general, get offset and length of predecessor
if (loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions"):
pass
elif (cls == "of_flow_stats_entry" and m_name == "instructions"):
pass
elif (cls == "of_packet_in" and m_name == "data"):
pass
elif (cls == "of_packet_out" and m_name == "data"):
pass
else:
debug("Error: Unknown member with offset == -1")
debug(" cls %s, m_name %s, version %d" % (cls, m_name, version))
sys.exit(1)
o_str = "_%s_%s_OFFSET(obj)" % (cls.upper()[3:], m_name.upper())
out.write("""\
offset = %s;
""" % o_str);
# This could be moved to main body but for version check
if not loxi_utils.type_is_scalar(m_type):
if loxi_utils.class_is_var_len(m_type[:-2], version) or \
m_type == "of_match_t":
len_macro = get_len_macro(cls, m_type, version)
else:
len_macro = "%d" % of_g.base_length[(m_type[:-2], version)]
out.write(" cur_len = %s;\n" % len_macro)
out.write(" break;\n")
def length_of(m_type, version):
"""
Return the length of a type based on the version
"""
if m_type in of_g.of_mixed_types:
m_type = of_g.of_mixed_types[m_type][version]
if m_type in of_g.of_base_types:
return of_g.of_base_types[m_type]["bytes"]
if (m_type[:-2], version) in of_g.base_length:
return of_g.base_length[(m_type[:-2], version)]
print "Unknown length request", m_type, version
sys.exit(1)
def gen_get_accessor_body(out, cls, m_type, m_name):
"""
Generate the common operations for a get accessor
"""
if loxi_utils.type_is_scalar(m_type):
ver = "" # See if version required for scalar update
if m_type in of_g.of_mixed_types:
ver = "ver, "
out.write("""\
%(wa)s(%(ver)swbuf, abs_offset, %(m_name)s);
""" % dict(wa=wire_accessor(m_type, "get"), ver=ver, m_name=m_name))
if m_type == "of_port_no_t":
out.write(" OF_PORT_NO_VALUE_CHECK(*%s, ver);\n" % m_name)
elif m_type == "of_octets_t":
out.write("""\
ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
%(m_name)s->bytes = cur_len;
%(m_name)s->data = OF_WIRE_BUFFER_INDEX(wbuf, abs_offset);
""" % dict(m_name=m_name))
elif m_type == "of_match_t":
out.write("""
ASSERT(cur_len + abs_offset <= WBUF_CURRENT_BYTES(wbuf));
match_octets.bytes = cur_len;
match_octets.data = OF_OBJECT_BUFFER_INDEX(obj, offset);
OF_TRY(of_match_deserialize(ver, %(m_name)s, &match_octets));
""" % dict(m_name=m_name))
else:
out.write("""
/* Initialize child */
%(m_type)s_init(%(m_name)s, obj->version, 0, 1);
/* Attach to parent */
%(m_name)s->parent = (of_object_t *)obj;
%(m_name)s->wire_object.wbuf = obj->wire_object.wbuf;
%(m_name)s->wire_object.obj_offset = abs_offset;
%(m_name)s->wire_object.owned = 0;
%(m_name)s->length = cur_len;
""" % dict(m_type=m_type[:-2], m_name=m_name))
def gen_set_accessor_body(out, cls, m_type, m_name):
"""
Generate the contents of a set accessor
"""
if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
ver = "" # See if version required for scalar update
if m_type in of_g.of_mixed_types:
ver = "ver, "
cur_len = "" # See if version required for scalar update
if m_type == "of_octets_t":
cur_len = ", cur_len"
out.write("""\
new_len = %(m_name)s->bytes;
of_wire_buffer_grow(wbuf, abs_offset + (new_len - cur_len));
""" % dict(m_name=m_name))
out.write("""\
%(wa)s(%(ver)swbuf, abs_offset, %(m_name)s%(cur_len)s);
""" % dict(wa=wire_accessor(m_type, "set"), ver=ver, cur_len=cur_len,
m_name=m_name))
elif m_type == "of_match_t":
out.write("""
/* Match object */
OF_TRY(of_match_serialize(ver, %(m_name)s, &match_octets));
new_len = match_octets.bytes;
of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
match_octets.data, new_len);
/* Free match serialized octets */
FREE(match_octets.data);
""" % dict(m_name=m_name))
else: # Other object type
out.write("\n /* LOCI object type */")
# Need to special case OXM list
out.write("""
new_len = %(m_name)s->length;
/* If underlying buffer already shared; nothing to do */
if (obj->wire_object.wbuf == %(m_name)s->wire_object.wbuf) {
of_wire_buffer_grow(wbuf, abs_offset + new_len);
/* Verify that the offsets are correct */
ASSERT(abs_offset == OF_OBJECT_ABSOLUTE_OFFSET(%(m_name)s, 0));
/* ASSERT(new_len == cur_len); */ /* fixme: may fail for OXM lists */
return %(ret_success)s;
}
/* Otherwise, replace existing object in data buffer */
of_wire_buffer_replace_data(wbuf, abs_offset, cur_len,
OF_OBJECT_BUFFER_INDEX(%(m_name)s, 0), new_len);
""" % dict(m_name=m_name, ret_success=accessor_return_success("set", m_type)))
if not loxi_utils.type_is_scalar(m_type):
if cls == "of_packet_out" and m_type == "of_list_action_t":
out.write("""
/* Special case for setting action lengths */
_PACKET_OUT_ACTION_LEN_SET(obj, %(m_name)s->length);
""" % dict(m_name=m_name))
elif m_type not in ["of_match_t", "of_octets_t"]:
out.write("""
/* @fixme Shouldn't this precede copying value's data to buffer? */
if (%(m_name)s->wire_length_set != NULL) {
%(m_name)s->wire_length_set((of_object_t *)%(m_name)s, %(m_name)s->length);
}
""" % dict(m_name=m_name))
out.write("""
/* Not scalar, update lengths if needed */
delta = new_len - cur_len;
if (delta != 0) {
/* Update parent(s) */
of_object_parent_length_update((of_object_t *)obj, delta);
}
""")
def obj_assert_check(cls):
"""
The body of the assert check for an accessor
We allow all versions of add/delete/modify to use the same accessors
"""
if cls in ["of_flow_modify", "of_flow_modify_strict",
"of_flow_delete", "of_flow_delete_strict",
"of_flow_add"]:
return "IS_FLOW_MOD_SUBTYPE(obj->object_id)"
else:
return "obj->object_id == %s" % cls.upper()
def gen_of_object_get(out, cls, m_name, m_type):
sub_cls = m_type[:-2]
out.write("""
/**
* Create a copy of %(m_name)s into a new variable of type %(m_type)s from
* a %(cls)s instance.
*
* @param obj Pointer to the source of type %(cls)s_t
* @returns A pointer to a new instance of type %(m_type)s whose contents
* match that of %(m_name)s from source
* @returns NULL if an error occurs
*/
%(m_type)s *
%(cls)s_%(m_name)s_get(%(cls)s_t *obj) {
%(m_type)s _%(m_name)s;
%(m_type)s *_%(m_name)s_ptr;
%(cls)s_%(m_name)s_bind(obj, &_%(m_name)s);
_%(m_name)s_ptr = (%(m_type)s *)of_object_dup(&_%(m_name)s);
return _%(m_name)s_ptr;
}
""" % dict(m_name=m_name, m_type=m_type, cls=cls, sub_cls=sub_cls))
def gen_unified_acc_body(out, cls, m_name, ver_type_map, a_type, m_type):
"""
Generate the body of a set or get accessor function
@param out The file to which to write the decs
@param cls The class name
@param m_name The member name
@param ver_type_map Maps (type, offset) pairs to a list of versions
@param a_type The accessor type, set or get
@param m_type The original member type
The type values in ver_type_map are now ignored as we've pushed down
the type munging to the lower level.
This is unified because the version switch case processing is the
same for both set and get
"""
out.write("""{
of_wire_buffer_t *wbuf;
int offset = 0; /* Offset of value relative to the start obj */
int abs_offset; /* Offset of value relative to start of wbuf */
of_version_t ver;
""")
if not loxi_utils.type_is_scalar(m_type):
out.write("""\
int cur_len = 0; /* Current length of object data */
""")
if a_type == "set":
out.write("""\
int new_len, delta; /* For set, need new length and delta */
""")
# For match, need octet string for set/get
if m_type == "of_match_t":
out.write("""\
of_octets_t match_octets; /* Serialized string for match */
""")
out.write("""
ASSERT(%(assert_str)s);
ver = obj->version;
wbuf = OF_OBJECT_TO_WBUF(obj);
ASSERT(wbuf != NULL);
/* By version, determine offset and current length (where needed) */
switch (ver) {
""" % dict(assert_str=obj_assert_check(cls)))
for first in sorted(ver_type_map):
(prev_t, prev_o) = ver_type_map[first]
prev_len = length_of(prev_t, first)
prev = first
out.write(" case %s:\n" % of_g.wire_ver_map[first])
break
for next in sorted(ver_type_map):
if next == first:
continue
(t, o) = ver_type_map[next]
cur_len = length_of(t, next)
if o == prev_o and cur_len == prev_len:
out.write(" case %s:\n" % of_g.wire_ver_map[next])
continue
gen_accessor_offsets(out, cls, m_name, prev, a_type, m_type, prev_o)
out.write(" case %s:\n" % of_g.wire_ver_map[next])
(prev_t, prev_o, prev_len, prev) = (t, o, cur_len, next)
gen_accessor_offsets(out, cls, m_name, next, a_type, m_type, prev_o)
out.write("""\
default:
ASSERT(0);
}
abs_offset = OF_OBJECT_ABSOLUTE_OFFSET(obj, offset);
ASSERT(abs_offset >= 0);
""")
if not loxi_utils.type_is_scalar(m_type):
out.write(" ASSERT(cur_len >= 0 && cur_len < 64 * 1024);\n")
# Now generate the common accessor code
if a_type in ["get", "bind"]:
gen_get_accessor_body(out, cls, m_type, m_name)
else:
gen_set_accessor_body(out, cls, m_type, m_name)
out.write("""
OF_LENGTH_CHECK_ASSERT(obj);
return %s;
}
""" % accessor_return_success(a_type, m_type))
def gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map):
"""
For generating the bind call for OF sub-objects
"""
params = ",\n ".join(param_list(cls, m_name, "bind"))
out.write("""
/**
* Bind an object of type %(m_type)s to the parent of type %(cls)s for
* member %(m_name)s
* @param obj Pointer to an object of type %(cls)s.
* @param %(m_name)s Pointer to the child object of type
* %(m_type)s to be filled out.
* \ingroup %(cls)s
*
* The parameter %(m_name)s is filled out to point to the same underlying
* wire buffer as its parent.
*
*/
""" % dict(m_name=m_name, cls=cls, m_type=m_type))
ret_type = accessor_return_type("bind", m_type)
out.write("%s\n%s_%s_bind(\n %s)\n" % (ret_type, cls, m_name, params))
gen_unified_acc_body(out, cls, m_name, ver_type_map, "bind", m_type)
def gen_get_accessor(out, cls, m_name, m_type, ver_type_map):
"""
For generating the get call for non- OF sub-objects
"""
params = ",\n ".join(param_list(cls, m_name, "get"))
out.write("""
/**
* Get %(m_name)s from an object of type %(cls)s.
* @param obj Pointer to an object of type %(cls)s.
* @param %(m_name)s Pointer to the child object of type
* %(m_type)s to be filled out.
*
*/
""" % dict(m_name=m_name, cls=cls, m_type=m_type))
ret_type = accessor_return_type("get", m_type)
out.write("%s\n%s_%s_get(\n %s)\n" % (ret_type, cls, m_name, params))
gen_unified_acc_body(out, cls, m_name, ver_type_map, "get", m_type)
def gen_accessor_definitions(out, cls):
for m_name in of_g.ordered_members[cls]:
if m_name in of_g.skip_members:
continue
m_type = loxi_utils.member_base_type(cls, m_name)
ver_type_map = field_ver_get(cls, m_name)
# Generate get/bind pending on member type
# FIXME: Does this do the right thing for match?
if loxi_utils.type_is_of_object(m_type):
gen_of_obj_bind(out, cls, m_name, m_type, ver_type_map)
gen_of_object_get(out, cls, m_name, m_type)
else:
gen_get_accessor(out, cls, m_name, m_type, ver_type_map)
# Now generate set accessor for all objects
params = ",\n ".join(param_list(cls, m_name, "set"))
out.write("""
/**
* Set %(m_name)s in an object of type %(cls)s.
* @param obj Pointer to an object of type %(cls)s.
""" % dict(m_name=m_name, cls=cls, m_type=m_type))
if loxi_utils.type_is_scalar(m_type) or m_type == "of_octets_t":
out.write("""\
* @param %(m_name)s The value to write into the object
*/
""" % dict(m_name=m_name, cls=cls, m_type=m_type))
else:
out.write("""\
* @param %(m_name)s Pointer to the child of type %(m_type)s.
*
* If the child's wire buffer is the same as the parent's, then
* nothing is done as the changes have already been registered in the
* parent. Otherwise, the data in the child's wire buffer is inserted
* into the parent's and the appropriate lengths are updated.
*/
""" % dict(m_name=m_name, cls=cls, m_type=m_type))
ret_type = accessor_return_type("set", m_type)
out.write("%s\n%s_%s_set(\n %s)\n" % (ret_type, cls, m_name, params))
gen_unified_acc_body(out, cls, m_name, ver_type_map, "set", m_type)
def gen_acc_pointer_typedefs(out):
"""
Generate the function pointer typedefs for in-struct accessors
@param out The file to which to write the typedefs
"""
out.write("""
/****************************************************************
*
* Accessor function pointer typedefs
*
****************************************************************/
/*
* Generic accessors:
*
* Many objects have a length represented in the wire buffer
* wire_length_get and wire_length_set access these values directly on the
* wire.
*
* Many objects have a length represented in the wire buffer
* wire_length_get and wire_length_set access these values directly on the
* wire.
*
* FIXME: TBD if wire_length_set and wire_type_set are required.
*/
typedef void (*of_wire_length_get_f)(of_object_t *obj, int *bytes);
typedef void (*of_wire_length_set_f)(of_object_t *obj, int bytes);
typedef void (*of_wire_type_get_f)(of_object_t *obj, of_object_id_t *id);
typedef void (*of_wire_type_set_f)(of_object_t *obj);
""")
# If not using function pointers in classes, don't gen typedefs below
if not config_check("gen_fn_ptrs"):
return
# For each class, for each type it uses, generate a typedef
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
continue
out.write("\n/* Accessor function pointer typedefs for %s */\n" % cls)
types_done = list()
for m_name in of_g.ordered_members[cls]:
(m_type, get_rv) = get_acc_rv(cls, m_name)
if (m_type, get_rv) in types_done:
continue
types_done.append((m_type, get_rv))
fn = "%s_%s" % (cls, m_type)
params = ", ".join(param_list(cls, m_name, "get"))
out.write("typedef int (*%s_get_f)(\n %s);\n" %
(fn, params))
params = ", ".join(param_list(cls, m_name, "set"))
out.write("typedef int (*%s_set_f)(\n %s);\n" %
(fn, params))
if loxi_utils.class_is_list(cls):
obj_type = loxi_utils.list_to_entry_type(cls)
out.write("""typedef int (*%(cls)s_first_f)(
%(cls)s_t *list,
%(obj_type)s_t *obj);
typedef int (*%(cls)s_next_f)(
%(cls)s_t *list,
%(obj_type)s_t *obj);
typedef int (*%(cls)s_append_bind_f)(
%(cls)s_t *list,
%(obj_type)s_t *obj);
typedef int (*%(cls)s_append_f)(
%(cls)s_t *list,
%(obj_type)s_t *obj);
""" % {"cls":cls, "obj_type":obj_type})
# out.write("""
# typedef int (*%(cls)s_get_f)(
# %(cls)s_t *list,
# %(obj_type)s_t *obj, int index);
# typedef int (*%(cls)s_set_f)(
# %(cls)s_t *list,
# %(obj_type)s_t *obj, int index);
# typedef int (*%(cls)s_append_f)(
# %(cls)s_t *list,
# %(obj_type)s_t *obj, int index);
# typedef int (*%(cls)s_insert_f)(
# %(cls)s_t *list,
# %(obj_type)s_t *obj, int index);
# typedef int (*%(cls)s_remove_f)(
# %(cls)s_t *list,
# int index);
# """ % {"cls":cls, "obj_type":obj_type})
################################################################
#
# New/Delete Function Definitions
#
################################################################
################################################################
# First, some utility functions for new/delete
################################################################
def del_function_proto(cls):
"""
Return the prototype for the delete operator for the given class
@param cls The class name
"""
fn = "void\n"
return fn
def instantiate_fn_ptrs(cls, ilvl, out):
"""
Generate the C code to instantiate function pointers for a class
@param cls The class name
@param ilvl The base indentation level
@param out The file to which to write the functions
"""
for m_name in of_g.ordered_members[cls]:
if m_name in of_g.skip_members:
continue
out.write(" " * ilvl + "obj->%s_get = %s_%s_get;\n" %
(m_name, cls, m_name))
out.write(" " * ilvl + "obj->%s_set = %s_%s_set;\n" %
(m_name, cls, m_name))
################################################################
# Routines to generate the body of new/delete functions
################################################################
def gen_init_fn_body(cls, out):
"""
Generate function body for init function
@param cls The class name for the function
@param out The file to which to write
"""
if cls in type_maps.inheritance_map:
param = "obj_p"
else:
param = "obj"
out.write("""
/**
* Initialize an object of type %(cls)s.
*
* @param obj Pointer to the object to initialize
* @param version The wire version to use for the object
* @param bytes How many bytes in the object
* @param clean_wire Boolean: If true, clear the wire object control struct
*
* If bytes < 0, then the default fixed length is used for the object
*
* This is a "coerce" function that sets up the pointers for the
* accessors properly.
*
* If anything other than 0 is passed in for the buffer size, the underlying
* wire buffer will have 'grow' called.
*/
void
%(cls)s_init(%(cls)s_t *%(param)s,
of_version_t version, int bytes, int clean_wire)
{
""" % dict(cls=cls, param=param))
# Use an extra pointer to deal with inheritance classes
if cls in type_maps.inheritance_map:
out.write("""\
%s_header_t *obj;
obj = &obj_p->header; /* Need instantiable subclass */
""" % cls)
out.write("""
ASSERT(of_object_fixed_len[version][%(enum)s] >= 0);
if (clean_wire) {
MEMSET(obj, 0, sizeof(*obj));
}
if (bytes < 0) {
bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
}
obj->version = version;
obj->length = bytes;
obj->object_id = %(enum)s;
""" % dict(cls=cls, enum=enum_name(cls)))
gen_coerce_ops(out, cls)
out.write("""
/* Grow the wire buffer */
if (obj->wire_object.wbuf != NULL) {
int tot_bytes;
tot_bytes = bytes + obj->wire_object.obj_offset;
of_wire_buffer_grow(obj->wire_object.wbuf, tot_bytes);
}
}
""")
## @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("""
obj->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
@param cls The class name for the function
@param out The file to which to write
"""
out.write("""
/**
* \\defgroup %(cls)s %(cls)s
*/
""" % dict(cls=cls))
if not type_maps.class_is_virtual(cls):
gen_wire_push_fn(cls, out)
out.write("""
/**
* Create a new %(cls)s object
*
* @param version The wire version to use for the object
* @return Pointer to the newly create object or NULL on error
*
* Initializes the new object with it's default fixed length associating
* a new underlying wire buffer.
*
* Use new_from_message to bind an existing message to a message object,
* or a _get function for non-message objects.
*
* \\ingroup %(cls)s
*/
%(cls)s_t *
%(cls)s_new_(of_version_t version)
{
%(cls)s_t *obj;
int bytes;
bytes = of_object_fixed_len[version][%(enum)s] + of_object_extra_len[version][%(enum)s];
/* Allocate a maximum-length wire buffer assuming we'll be appending to it. */
if ((obj = (%(cls)s_t *)of_object_new(OF_WIRE_BUFFER_MAX_LENGTH)) == NULL) {
return NULL;
}
%(cls)s_init(obj, version, bytes, 0);
""" % dict(cls=cls, enum=enum_name(cls)))
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))
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)) {
of_object_u16_set((of_object_t *)obj, %(match_offset)d + 2, 4);
}
""" % dict(match_offset=match_offset))
out.write("""
return obj;
}
#if defined(OF_OBJECT_TRACKING)
/*
* Tracking objects. Call the new function and then record location
*/
%(cls)s_t *
%(cls)s_new_tracking(of_version_t version,
const char *file, int line)
{
%(cls)s_t *obj;
obj = %(cls)s_new_(version);
of_object_track((of_object_t *)obj, file, line);
return obj;
}
#endif
""" % dict(cls=cls))
def gen_from_message_fn_body(cls, out):
"""
Generate function body for from_message function
@param cls The class name for the function
@param out The file to which to write
"""
out.write("""
/**
* Create a new %(cls)s object and bind it to an existing message
*
* @param msg The message to bind the new object to
* @return Pointer to the newly create object or NULL on error
*
* \ingroup %(cls)s
*/
%(cls)s_t *
%(cls)s_new_from_message_(of_message_t msg)
{
%(cls)s_t *obj = NULL;
of_version_t version;
int length;
if (msg == NULL) return NULL;
version = of_message_version_get(msg);
if (!OF_VERSION_OKAY(version)) return NULL;
length = of_message_length_get(msg);
if ((obj = (%(cls)s_t *)of_object_new(-1)) == NULL) {
return NULL;
}
%(cls)s_init(obj, version, 0, 0);
if ((of_object_buffer_bind((of_object_t *)obj, OF_MESSAGE_TO_BUFFER(msg),
length, OF_MESSAGE_FREE_FUNCTION)) < 0) {
FREE(obj);
return NULL;
}
obj->length = length;
obj->version = version;
return obj;
}
#if defined(OF_OBJECT_TRACKING)
/*
* Tracking objects. Call the new function and then record location
*/
%(cls)s_t *
%(cls)s_new_from_message_tracking(of_message_t msg,
const char *file, int line)
{
%(cls)s_t *obj;
obj = %(cls)s_new_from_message_(msg);
of_object_track((of_object_t *)obj, file, line);
return obj;
}
#endif
""" % dict(cls=cls))
################################################################
# Now the top level generator functions
################################################################
def gen_new_function_declarations(out):
"""
Gerenate the header file declarations for new operators for all classes
@param out The file to which to write the decs
"""
out.write("""
/****************************************************************
*
* New operator declarations
*
* _new: Create a new object for writing; includes init
* _new_from_message: Create a new instance of the object and bind the
* message data to the object
* _init: Initialize and optionally allocate buffer space for an
* automatic instance
*
* _new and _from_message require a delete operation to be called
* on the object.
*
****************************************************************/
""")
out.write("""
/*
* If object tracking is enabled, map "new" and "new from msg"
* calls to tracking versions; otherwise, directly to internal
* versions of fns which have the same name but end in _.
*/
#if defined(OF_OBJECT_TRACKING)
""")
for cls in of_g.standard_class_order:
out.write("""
extern %(cls)s_t *
%(cls)s_new_tracking(of_version_t version,
const char *file, int line);
#define %(cls)s_new(version) \\
%(cls)s_new_tracking(version, \\
__FILE__, __LINE__)
""" % dict(cls=cls))
if loxi_utils.class_is_message(cls):
out.write("""extern %(cls)s_t *
%(cls)s_new_from_message_tracking(of_message_t msg,
const char *file, int line);
#define %(cls)s_new_from_message(msg) \\
%(cls)s_new_from_message_tracking(msg, \\
__FILE__, __LINE__)
""" % dict(cls=cls))
out.write("""
#else /* No object tracking */
""")
for cls in of_g.standard_class_order:
out.write("""
#define %(cls)s_new(version) \\
%(cls)s_new_(version)
""" % dict(cls=cls))
if loxi_utils.class_is_message(cls):
out.write("""#define %(cls)s_new_from_message(msg) \\
%(cls)s_new_from_message_(msg)
""" % dict(cls=cls))
out.write("""
#endif /* Object tracking */
""")
for cls in of_g.standard_class_order:
out.write("""
extern %(cls)s_t *
%(cls)s_new_(of_version_t version);
""" % dict(cls=cls))
if loxi_utils.class_is_message(cls):
out.write("""extern %(cls)s_t *
%(cls)s_new_from_message_(of_message_t msg);
""" % dict(cls=cls))
out.write("""extern void %(cls)s_init(
%(cls)s_t *obj, of_version_t version, int bytes, int clean_wire);
""" % dict(cls=cls))
out.write("""
/****************************************************************
*
* Delete operator static inline definitions.
* These are here for type checking purposes only
*
****************************************************************/
""")
for cls in of_g.standard_class_order:
# if cls in type_maps.inheritance_map:
# continue
out.write("""
/**
* Delete an object of type %(cls)s_t
* @param obj An instance of type %(cls)s_t
*
* \ingroup %(cls)s
*/
static inline void
%(cls)s_delete(%(cls)s_t *obj) {
of_object_delete((of_object_t *)(obj));
}
""" % dict(cls=cls))
out.write("""
typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
int bytes, int clean_wire);
extern const of_object_init_f of_object_init_map[];
""")
out.write("""
/****************************************************************
*
* Function pointer initialization functions
* These are part of the "coerce" type casting for objects
*
****************************************************************/
""")
#
# @fixme Not clear that these should all be set for virtual fns
#
# @fixme Clean up. should have a (language specific) map from class
# to length and type get/set functions
#
def gen_coerce_ops(out, cls):
out.write("""
/* Set up the object's function pointers */
""")
uclass = loxi_globals.unified.class_by_name(cls)
if uclass and not uclass.virtual and uclass.has_type_members:
out.write("""
obj->wire_type_set = %(cls)s_push_wire_types;
""" % dict(cls=cls))
if loxi_utils.class_is_message(cls):
out.write("""
obj->wire_length_get = of_object_message_wire_length_get;
obj->wire_length_set = of_object_message_wire_length_set;
""")
else:
if loxi_utils.class_is_tlv16(cls):
if not (cls in type_maps.inheritance_map): # Don't set for super
out.write("""
obj->wire_length_set = of_tlv16_wire_length_set;
""")
out.write("""
obj->wire_length_get = of_tlv16_wire_length_get;
""")
if loxi_utils.class_is_action(cls):
out.write("""
obj->wire_type_get = of_action_wire_object_id_get;
""")
if loxi_utils.class_is_action_id(cls):
out.write("""
obj->wire_type_get = of_action_id_wire_object_id_get;
""")
if loxi_utils.class_is_instruction(cls):
out.write("""
obj->wire_type_get = of_instruction_wire_object_id_get;
""")
if loxi_utils.class_is_queue_prop(cls):
out.write("""
obj->wire_type_get = of_queue_prop_wire_object_id_get;
""")
if loxi_utils.class_is_table_feature_prop(cls):
out.write("""
obj->wire_type_get = of_table_feature_prop_wire_object_id_get;
""")
if loxi_utils.class_is_meter_band(cls):
out.write("""
obj->wire_type_get = of_meter_band_wire_object_id_get;
""")
if loxi_utils.class_is_hello_elem(cls):
out.write("""
obj->wire_type_get = of_hello_elem_wire_object_id_get;
""")
if loxi_utils.class_is_oxm(cls):
out.write("""
obj->wire_length_get = of_oxm_wire_length_get;
obj->wire_type_get = of_oxm_wire_object_id_get;
""")
if loxi_utils.class_is_u16_len(cls):
out.write("""
obj->wire_length_get = of_u16_len_wire_length_get;
obj->wire_length_set = of_u16_len_wire_length_set;
""")
if cls == "of_packet_queue":
out.write("""
obj->wire_length_get = of_packet_queue_wire_length_get;
obj->wire_length_set = of_packet_queue_wire_length_set;
""")
# if cls == "of_list_meter_band_stats":
# out.write("""
# obj->wire_length_get = of_list_meter_band_stats_wire_length_get;
#""")
if cls == "of_meter_stats":
out.write("""
obj->wire_length_get = of_meter_stats_wire_length_get;
obj->wire_length_set = of_meter_stats_wire_length_set;
""")
if config_check("gen_fn_ptrs"):
if loxi_utils.class_is_list(cls):
out.write("""
obj->first = %(cls)s_first;
obj->next = %(cls)s_next;
obj->append = %(cls)s_append;
obj->append_bind = %(cls)s_append_bind;
""" % dict(cls=cls))
else:
instantiate_fn_ptrs(cls, 4, out)
def gen_new_function_definitions(out, cls):
"""
Generate the new operator for all classes
@param out The file to which to write the functions
"""
gen_new_fn_body(cls, out)
gen_init_fn_body(cls, out)
if loxi_utils.class_is_message(cls):
gen_from_message_fn_body(cls, out)
"""
Document generation functions
The main reason this is here is to generate documentation per
accessor that indicates the versions that support the interface.
"""
def gen_accessor_doc(out, name):
"""
Generate documentation for each accessor function that indicates
the versions supporting the accessor.
"""
common_top_matter(out, name)
out.write("/* DOCUMENTATION ONLY */\n")
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
pass # Check this
out.write("""
/**
* Structure for %(cls)s object. Get/set
* accessors available in all versions unless noted otherwise
*
""" % dict(cls=cls))
if loxi_utils.class_is_list(cls):
out.write("""\
* @param first Function of type %(cls)s_first_f.
* Setup a TBD class object to the first entry in the list
* @param next Function of type %(cls)s_next_f.
* Advance a TBD class object to the next entry in the list
* @param append_bind Function of type %(cls)s_append_bind_f
* Setup a TBD class object for append to the end of the current list
* @param append Function of type @ref %(cls)s_append_f.
* Copy an item to the end of a list
""" % dict(cls=cls))
for m_name in of_g.ordered_members[cls]:
if m_name in of_g.skip_members:
continue
ver_type_map = field_ver_get(cls, m_name)
(m_type, get_rv) = get_acc_rv(cls, m_name)
if len(ver_type_map) == 3:
# ver_string = "Available in all versions"
ver_string = ""
else:
ver_string = "("
for ver in sorted(ver_type_map):
ver_string += " " + of_g.short_version_names[ver]
ver_string += ")."
f_name = acc_name(cls, m_name)
out.write("""\
* @param %(m_name)s_get/set %(ver_string)s
* Accessors for %(m_name)s, a variable of type %(m_type)s. Functions
* are of type %(f_name)s_get_f and _set_f.
*
""" % dict(f_name=f_name, m_name=m_name, ver_string=ver_string, m_type=m_type))
out.write("""\
*/
typedef struct %(cls)s_s %(cls)s_t;
""" % dict(cls=cls))
out.write("#endif /* _LOCI_DOC_H_ */\n")
################################################################
#
# For fun, here are some unified, traditional C structure representation
#
################################################################
def gen_cof_to_wire(out):
pass
def gen_wire_to_cof(out):
pass
def gen_cof_instance(out, cls):
out.write("struct c%s_s {\n" % cls)
for m in of_g.ordered_members[cls]:
if m in of_g.skip_members:
continue
entry = of_g.unified[cls]["union"][m]
cof_type = type_to_cof_type(entry["m_type"])
out.write(" %-20s %s;\n" % (cof_type, m))
out.write("};\n\n");
def gen_cof_structs(out):
"""
Generate non-version specific (common) representation of structures
@param out The file to which to write the functions
"""
out.write("\n/* Common, unified OpenFlow structure representations */\n")
for cls in of_g.standard_class_order:
if cls in type_maps.inheritance_map:
continue
gen_cof_instance(out, cls)
################################################################
#
# Generate code samples for applications.
#
################################################################
def gen_code_samples(out, name):
out.write("""
#if 0 /* Do not compile in */
/**
* @file %(name)s
*
* These are code samples for inclusion in other components
*/
""" % dict(name=name))
gen_jump_table_template(out)
# These are messages that a switch might expect.
msg_list = ["of_echo_request",
"of_hello",
"of_packet_in",
"of_packet_out",
"of_port_mod",
"of_port_stats_request",
"of_queue_get_config_request",
"of_queue_stats_request",
"of_flow_add",
"of_flow_modify",
"of_flow_modify_strict",
"of_flow_delete",
"of_flow_delete_strict",
"of_get_config_request",
"of_flow_stats_request",
"of_barrier_request",
"of_echo_reply",
"of_aggregate_stats_request",
"of_desc_stats_request",
"of_table_stats_request",
"of_features_request",
"of_table_mod",
"of_set_config",
"of_experimenter",
"of_experimenter_stats_request",
"of_group_desc_stats_request",
"of_group_features_stats_request",
"of_role_request"]
gen_message_handler_templates(out, msgs=msg_list)
out.write("""
#endif
""")
def gen_jump_table_template(out=sys.stdout, all_unhandled=True,
cxn_type="ls_cxn_handle_t",
unhandled="unhandled_message"):
"""
Generate a template for a jump table.
@param out The file to which to write the functions
"""
out.write("""
/*
* Simple jump table definition for message handling
*/
typedef int (*msg_handler_f)(%(cxn_type)s cxn, of_object_t *obj);
typedef msg_handler_f msg_jump_table_t[OF_MESSAGE_OBJECT_COUNT];
/* Jump table template for message objects */
extern msg_jump_table_t jump_table;
/* C-code template */
msg_jump_table_t jump_table = {
%(unhandled)s, /* OF_OBJECT; place holder for generic object */
""" % dict(unhandled=unhandled, cxn_type=cxn_type))
count = 0
fn_name = unhandled
for cls in of_g.ordered_messages:
comma = ","
count += 1
if count == len(of_g.ordered_messages):
comma = " "
if not all_unhandled:
fn_name = "%s_handler" % cls[3:]
out.write(" %s%s /* %s */\n" % (fn_name, comma, enum_name(cls)))
out.write("};\n")
def gen_message_switch_stmt_tmeplate(out=sys.stdout, all_unhandled=True,
cxn_type="ls_cxn_handle_t",
unhandled="unhandled_message"):
out.write("""
/*
* Simple switch statement for message handling
*/
switch (obj->object_id):
""")
fn_name = unhandled
for cls in of_g.ordered_messages:
if not all_unhandled:
fn_name = "%s_handler" % cls[3:]
out.write("""
case %(enum)s:
rv = %(fn_name)s(cxn, obj);
break;
""" % dict(fn_name=fn_name, cls=cls, enum=enum_name(cls)))
out.write("""
default:
rv = LS_ERROR_PARAM;
break;
}
TRACE("Handled msg %p with rv %d (%s)", obj, rv, ls_error_strings[rv]);
return rv;
""")
def gen_message_handler_templates(out=sys.stdout, cxn_type="ls_cxn_handle_t",
unhandled="unhandled_message", msgs=None):
gen_jump_table_template(out, False, cxn_type)
out.write("""
/**
* Function for unhandled message
*/
static int
unhandled_message(%(cxn_type)s cxn, of_object_t *obj)
{
(void)cxn;
(void)obj;
TRACE("Unhandled message %%p. Object id %%d", obj, obj->object_id);
return LS_ERROR_UNAVAIL;
}
""" % dict(unhandled=unhandled, cxn_type=cxn_type))
if not msgs:
msgs = of_g.ordered_messages
for cls in msgs:
out.write("""
/**
* Handle a %(s_cls)s message
* @param cxn Connection handler for the owning connection
* @param _obj Generic type object for the message to be coerced
* @returns Error code
*/
static int
%(s_cls)s_handler(%(cxn_type)s cxn, of_object_t *_obj)
{
%(cls)s_t *obj;
TRACE("Handling %(cls)s message: %%p.", obj);
obj = (%(cls)s_t *)_obj;
/* Handle object of type %(cls)s_t */
return LS_ERROR_NONE;
}
""" % dict(s_cls=cls[3:], cls=cls, cxn_type=cxn_type))
gen_message_switch_stmt_tmeplate(out, False, cxn_type)