Initial import
LoxiGen is the work of several developers, not just myself.
diff --git a/loxi_front_end/__init__.py b/loxi_front_end/__init__.py
new file mode 100644
index 0000000..5e4e379
--- /dev/null
+++ b/loxi_front_end/__init__.py
@@ -0,0 +1,26 @@
+# 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.
diff --git a/loxi_front_end/c_parse_utils.py b/loxi_front_end/c_parse_utils.py
new file mode 100644
index 0000000..f06904b
--- /dev/null
+++ b/loxi_front_end/c_parse_utils.py
@@ -0,0 +1,166 @@
+# 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 Utilities related to parsing C files
+#
+import re
+import sys
+import os
+import of_g
+
+def type_dec_to_count_base(m_type):
+ """
+ Resolve a type declaration like uint8_t[4] to a count (4) and base_type
+ (uint8_t)
+
+ @param m_type The string type declaration to process
+ """
+ count = 1
+ chk_ar = m_type.split('[')
+ if len(chk_ar) > 1:
+ count_str = chk_ar[1].split(']')[0]
+ if count_str in of_g.ofp_constants:
+ count = of_g.ofp_constants[count_str]
+ else:
+ count = int(count_str)
+ base_type = chk_ar[0]
+ else:
+ base_type = m_type
+ return count, base_type
+
+def comment_remover(text):
+ """
+ Remove C and C++ comments from text
+ @param text Possibly multiline string of C code.
+
+ http://stackoverflow.com/questions/241327/python-snippet-to-remove-c-and-c-comments
+ """
+
+ def replacer(match):
+ s = match.group(0)
+ if s.startswith('/'):
+ return ""
+ else:
+ return s
+ pattern = re.compile(
+ r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+ re.DOTALL | re.MULTILINE
+ )
+ return re.sub(pattern, replacer, text)
+
+
+def clean_up_input(text):
+ text = comment_remover(text)
+ text_lines = text.splitlines()
+ all_lines = []
+ for line in text_lines:
+ line = re.sub("\t", " ", line) # get rid of tabs
+ line = re.sub(" +$", "", line) # Strip trailing blanks
+ if len(line):
+ all_lines.append(line)
+ text = "\n".join(all_lines)
+ return text
+
+def extract_structs(contents):
+ """
+ Extract the structures from raw C code input
+ @param contents The text of the original C code
+ """
+ contents = clean_up_input(contents)
+ struct_list = re.findall("struct .* \{[^}]+\};", contents)
+ return struct_list
+
+def extract_enums(contents):
+ """
+ Extract the enums from raw C code input
+ @param contents The text of the original C code
+ @return An array where each entry is an (unparsed) enum instance
+ """
+ contents = clean_up_input(contents)
+ enum_list = re.findall("enum .* \{[^}]+\};", contents)
+ return enum_list
+
+def extract_enum_vals(enum):
+ """
+ From a C enum, return a pair (name, values)
+ @param enum The C syntax enumeration
+ @returns (name, values), see below
+
+ name is the enum name
+ values is a list pairs (<ident>, <value>) where ident is the
+ identifier and value is the associated value.
+
+ The values are integers when possible, otherwise strings
+ """
+
+ rv_list = []
+ name = re.search("enum +(\w+)", enum).group(1)
+ lines = " ".join(enum.split("\n"))
+ body = re.search("\{(.+)\}", lines).group(1)
+ entries = body.split(",")
+ previous_value = -1
+ for m in entries:
+ if re.match(" *$", m): # Empty line
+ continue
+ # Parse with = first
+ search_obj = re.match(" +(\w+) *= *(.*) *", m)
+ if search_obj: # Okay, had =
+ e_name = search_obj.group(1)
+ e_value = search_obj.group(2)
+ else: # No equals
+ search_obj = re.match(" +(\w+)", m)
+ if not search_obj:
+ sys.stderr.write("\nError extracting enum for %s, member %s\n"
+ % (name, m))
+ sys.exit(1)
+ e_name = search_obj.group(1)
+ e_value = previous_value + 1
+ rv_list.append([e_name, e_value])
+
+ if type(e_value) is type(0):
+ previous_value = e_value
+ else:
+ try:
+ previous_value = int(e_value, 0)
+ except ValueError:
+ pass
+ return (name, rv_list)
+
+def extract_defines(contents):
+ """
+ Returns a list of pairs (<identifier>, <value>) where
+ #define <ident> <value> appears in the file
+ """
+ rv_list = []
+ contents = clean_up_input(contents)
+ define_list = re.findall("\#define +[^ ]+ .*\n", contents, re.M)
+ for entry in define_list:
+ match_obj = re.match("#define +([^ ]+) +(.+)$", entry)
+ rv_list.append([match_obj.group(1),match_obj.group(2)])
+ return rv_list
+
diff --git a/loxi_front_end/flags.py b/loxi_front_end/flags.py
new file mode 100644
index 0000000..3c401f9
--- /dev/null
+++ b/loxi_front_end/flags.py
@@ -0,0 +1,76 @@
+# 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.
+
+"""
+This file needs significant work and normalization. We need a better
+representation for flags, associating them to variables, and associating
+them to OF versions.
+
+@fixme Most of this will be going away soon
+"""
+
+import sys
+import copy
+import type_maps
+import of_g
+import re
+
+# These mark idents as _not_ flags and have precedence
+non_flag_rules = [
+ "OF_CONFIG_FRAG_NORMAL",
+ "OF_FLOW_MOD_FAILED_BAD_FLAGS",
+ "OF_SWITCH_CONFIG_FAILED_BAD_FLAGS",
+ "OF_PORT_STATE_FLAG_STP_LISTEN",
+ "OF_TABLE_CONFIG_TABLE_MISS_CONTROLLER",
+ ]
+
+# These mark idents as flags
+flag_rules = [
+ "OF_CONFIG_",
+ "OF_TABLE_CONFIG_",
+ ]
+
+def ident_is_flag(ident):
+ """
+ Return True if ident should be treated as a flag
+ """
+
+ # Do negative matches first
+ for entry in non_flag_rules:
+ if re.match(entry, ident):
+ return False
+
+ # General rule, if it says flag it is (unless indicated above)
+ if ident.find("FLAG") >= 0:
+ return True
+
+ for entry in flag_rules:
+ if re.match(entry, ident):
+ return True
+
+ return False
+
diff --git a/loxi_front_end/identifiers.py b/loxi_front_end/identifiers.py
new file mode 100644
index 0000000..a4122ee
--- /dev/null
+++ b/loxi_front_end/identifiers.py
@@ -0,0 +1,99 @@
+# 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 Process identifiers for updating of_g.identifiers
+#
+
+import sys
+import of_h_utils
+from generic_utils import *
+import of_g
+
+##
+# The value to use when an identifier is not defined for a version
+UNDEFINED_IDENT_VALUE = 0
+
+def add_identifiers(all_idents, idents_by_group, version, contents):
+ """
+ Update all_idents with identifiers from an openflow.h header file
+ @param all_idents A dict, usually of_g.identifiers
+ @param idents_by_group A dict for mapping LOXI idents to groups,
+ usually of_g.identifiers_by_group
+ @param version The OF wire version
+ @param contents The contents of an openflow.h file
+ """
+
+ # Get the dictionary of enums from the file text
+ enum_dict = of_h_utils.get_enum_dict(version,
+ contents)
+ for name, info in enum_dict.items():
+
+ # info is a DotDict w/ keys ofp_name, ofp_group and value.
+ if name in all_idents:
+ all_idents[name]["values_by_version"][version] = info.value
+ if ((all_idents[name]["ofp_name"] != info.ofp_name or
+ all_idents[name]["ofp_group"] != info.ofp_group) and
+ info.ofp_name.find("OFPP_") != 0):
+ log("""
+NOTE: Identifier %s has different ofp name or group in version %s
+From ofp name %s, group %s to name %s, group %s.
+This could indicate a name collision in LOXI identifier translation.
+""" % (name, str(version), all_idents[name]["ofp_name"],
+ all_idents[name]["ofp_group"], info.ofp_name, info.ofp_group))
+ # Update stuff assuming newer versions processed later
+ all_idents[name]["ofp_name"] = info.ofp_name
+ all_idents[name]["ofp_group"] = info.ofp_group
+
+ else: # New name
+ all_idents[name] = dict(
+ values_by_version = {version:info.value},
+ common_value = info["value"],
+ ofp_name = info["ofp_name"],
+ ofp_group = info["ofp_group"]
+ )
+ if info["ofp_group"] not in idents_by_group:
+ idents_by_group[info["ofp_group"]] = []
+ if name not in idents_by_group[info["ofp_group"]]:
+ idents_by_group[info["ofp_group"]].append(name)
+
+def all_versions_agree(all_idents, version_list, name):
+ val_list = all_idents[name]["values_by_version"]
+ for version in version_list:
+ if not version in val_list:
+ return False
+ if str(val_list[version]) != str(all_idents[name]["common_value"]):
+ return False
+ return True
+
+def defined_versions_agree(all_idents, version_list, name):
+ val_list = all_idents[name]["values_by_version"]
+ for version in version_list:
+ if version in val_list:
+ if str(val_list[version]) != str(all_idents[name]["common_value"]):
+ return False
+ return True
diff --git a/loxi_front_end/match.py b/loxi_front_end/match.py
new file mode 100644
index 0000000..4ab3126
--- /dev/null
+++ b/loxi_front_end/match.py
@@ -0,0 +1,488 @@
+# 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 Match data representation
+#
+# @fixme This still has lots of C specific code that should be moved into c_gen
+
+import sys
+import of_g
+from generic_utils import *
+import oxm
+import loxi_utils.loxi_utils as loxi_utils
+
+#
+# Use 1.2 match semantics for common case
+#
+# Generate maps between generic match and version specific matches
+# Generate dump functions for generic match
+# Generate dump functions for version specific matches
+
+## @var of_match_members
+# The dictionary from unified match members to type and indexing info
+#
+# Keys:
+# name The unified name used for the member
+# m_type The data type used for the object in unified structure
+# print_type The id to use when printing
+# conditions The condition underwhich the field could occur TBD
+# takes_mask_in_spec Shown as taking mask in OF 1.2 spec; IGNORED NOW
+# order Used to define an order for readability
+# v1_wc_shift The WC shift in OF 1.0
+# v2_wc_shift The WC shift in OF 1.1
+#
+# Unless noted otherwise, class is 0x8000, OFPXMC_OPENFLOW_BASIC
+# We use the 1.2 names and alias older names
+# Conditions:
+# is_ipv4(_m): ((_m)->eth_type == 0x0800)
+# is_ipv6(_m): ((_m)->eth_type == 0x86dd)
+# is_ip(_m): (is_ipv4(_m) || is_ipv6(_m))
+# is_arp(_m): ((_m)->eth_type == 0x0806)
+# is_tcp(_m): (is_ip(_m) && ((_m)->ip_proto == 6))
+# is_udp(_m): (is_ip(_m) && ((_m)->ip_proto == 17))
+# is_sctp(_m): (is_ip(_m) && ((_m)->ip_proto == 132))
+# is_icmpv4(_m): (is_ipv4(_m) && ((_m)->ip_proto == 1))
+# is_icmpv6(_m): (is_ipv6(_m) && ((_m)->ip_proto == 58))
+#
+
+of_match_members = dict(
+ in_port = dict(
+ name="in_port",
+ m_type="of_port_no_t",
+ print_type="PRIx32",
+ conditions="",
+ v1_wc_shift=0,
+ v2_wc_shift=0,
+ takes_mask_in_spec=False,
+ order=100,
+ ),
+ in_phy_port = dict(
+ name="in_phy_port",
+ m_type="of_port_no_t",
+ print_type="PRIx32",
+ conditions="", # OXM_OF_IN_PORT must be present
+ takes_mask_in_spec=False,
+ order=101,
+ ),
+ metadata = dict(
+ name="metadata",
+ m_type="uint64_t",
+ print_type="PRIx64",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=102,
+ ),
+
+ eth_dst = dict(
+ name="eth_dst",
+ m_type="of_mac_addr_t",
+ v1_wc_shift=3,
+ print_type="\"p\"",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=200,
+ ),
+ eth_src = dict(
+ name="eth_src",
+ m_type="of_mac_addr_t",
+ v1_wc_shift=2,
+ print_type="\"p\"",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=201,
+ ),
+ eth_type = dict(
+ name="eth_type",
+ m_type="uint16_t",
+ v1_wc_shift=4,
+ v2_wc_shift=3,
+ print_type="PRIx16",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=203,
+ ),
+ vlan_vid = dict( # FIXME: Semantics changed in 1.2
+ # Use CFI bit to indicate tag presence
+ name="vlan_vid",
+ m_type="uint16_t",
+ v1_wc_shift=1,
+ v2_wc_shift=1,
+ print_type="PRIx16",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=210,
+ ),
+ vlan_pcp = dict(
+ name="vlan_pcp",
+ m_type="uint8_t",
+ v1_wc_shift=20,
+ v2_wc_shift=2,
+ print_type="PRIx8",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=211,
+ ),
+ ipv4_src = dict(
+ name="ipv4_src",
+ m_type="uint32_t",
+ v1_wc_shift=8,
+ print_type="PRIx32",
+ conditions="is_ipv4(match)",
+ takes_mask_in_spec=True,
+ order=300,
+ ),
+ ipv4_dst = dict(
+ name="ipv4_dst",
+ m_type="uint32_t",
+ v1_wc_shift=14,
+ print_type="PRIx32",
+ conditions="is_ipv4(match)",
+ takes_mask_in_spec=True,
+ order=301,
+ ),
+ ip_dscp = dict(
+ name="ip_dscp",
+ m_type="uint8_t",
+ v1_wc_shift=21,
+ v2_wc_shift=4,
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=310,
+ ),
+ ip_ecn = dict(
+ name="ip_ecn",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=311,
+ ),
+ ip_proto = dict(
+ name="ip_proto",
+ m_type="uint8_t",
+ v1_wc_shift=5,
+ v2_wc_shift=5,
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=320,
+ ),
+
+ tcp_dst = dict(
+ name="tcp_dst",
+ m_type="uint16_t",
+ v1_wc_shift=7,
+ v2_wc_shift=7,
+ print_type="PRIx16",
+ conditions="is_tcp(match)",
+ takes_mask_in_spec=False,
+ order=400,
+ ),
+ tcp_src = dict(
+ name="tcp_src",
+ m_type="uint16_t",
+ v1_wc_shift=6,
+ v2_wc_shift=6,
+ print_type="PRIx16",
+ conditions="is_tcp(match)",
+ takes_mask_in_spec=False,
+ order=401,
+ ),
+
+ udp_dst = dict(
+ name="udp_dst",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_udp(match)",
+ takes_mask_in_spec=False,
+ order=410,
+ ),
+ udp_src = dict(
+ name="udp_src",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_udp(match)",
+ takes_mask_in_spec=False,
+ order=411,
+ ),
+
+ sctp_dst = dict(
+ name="sctp_dst",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_sctp(match)",
+ takes_mask_in_spec=False,
+ order=420,
+ ),
+ sctp_src = dict(
+ name="sctp_src",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_sctp(match)",
+ takes_mask_in_spec=False,
+ order=421,
+ ),
+
+ icmpv4_type = dict(
+ name="icmpv4_type",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v4(match)",
+ takes_mask_in_spec=False,
+ order=430,
+ ),
+ icmpv4_code = dict(
+ name="icmpv4_code",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v4(match)",
+ takes_mask_in_spec=False,
+ order=431,
+ ),
+
+ arp_op = dict(
+ name="arp_op",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=250,
+ ),
+
+ arp_spa = dict(
+ name="arp_spa",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=True,
+ order=251,
+ ),
+ arp_tpa = dict(
+ name="arp_tpa",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=True,
+ order=252,
+ ),
+
+ arp_sha = dict(
+ name="arp_sha",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=253,
+ ),
+ arp_tha = dict(
+ name="arp_tha",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=254,
+ ),
+
+ ipv6_src = dict(
+ name="ipv6_src",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=True,
+ order=350,
+ ),
+ ipv6_dst = dict(
+ name="ipv6_dst",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=True,
+ order=351,
+ ),
+
+ ipv6_flabel = dict(
+ name="ipv6_flabel",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=False, # Comment in openflow.h says True
+ order=360,
+ ),
+
+ icmpv6_type = dict(
+ name="icmpv6_type",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v6(match)",
+ takes_mask_in_spec=False,
+ order=440,
+ ),
+ icmpv6_code = dict(
+ name="icmpv6_code",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v6(match)",
+ takes_mask_in_spec=False,
+ order=441,
+ ),
+
+ ipv6_nd_target = dict(
+ name="ipv6_nd_target",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=442,
+ ),
+
+ ipv6_nd_sll = dict(
+ name="ipv6_nd_sll",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=443,
+ ),
+ ipv6_nd_tll = dict(
+ name="ipv6_nd_tll",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=444,
+ ),
+
+ mpls_label = dict(
+ name="mpls_label",
+ m_type="uint32_t",
+ v2_wc_shift=8,
+ print_type="PRIx32",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=500,
+ ),
+ mpls_tc = dict(
+ name="mpls_tc",
+ m_type="uint8_t",
+ v2_wc_shift=9,
+ print_type="PRIx8",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=501,
+ ),
+)
+
+match_keys_sorted = of_match_members.keys()
+match_keys_sorted.sort(key=lambda entry:of_match_members[entry]["order"])
+
+of_v1_keys = [
+ "eth_dst",
+ "eth_src",
+ "eth_type",
+ "in_port",
+ "ipv4_dst",
+ "ip_proto",
+ "ipv4_src",
+ "ip_dscp",
+ "tcp_dst", # Means UDP too for 1.0 and 1.1
+ "tcp_src", # Means UDP too for 1.0 and 1.1
+ "vlan_pcp",
+ "vlan_vid"
+ ]
+
+of_v2_keys = [
+ "eth_dst",
+ "eth_src",
+ "eth_type",
+ "in_port",
+ "ipv4_dst",
+ "ip_proto",
+ "ipv4_src",
+ "ip_dscp",
+ "tcp_dst", # Means UDP too for 1.0 and 1.1
+ "tcp_src", # Means UDP too for 1.0 and 1.1
+ "vlan_pcp",
+ "vlan_vid",
+ "mpls_label",
+ "mpls_tc",
+ "metadata"
+ ]
+
+of_v2_full_mask = [
+ "eth_dst",
+ "eth_src",
+ "ipv4_dst",
+ "ipv4_src",
+ "metadata"
+ ]
+
+def oxm_index(key):
+ """
+ What's the index called for a match key
+ """
+ return "OF_OXM_INDEX_" + key.upper()
+
+##
+# Check that all members in the hash are recognized as match keys
+def match_sanity_check():
+ count = 0
+ for match_v in ["of_match_v1", "of_match_v2"]:
+ count += 1
+ for mm in of_g.unified[match_v][count]["members"]:
+ key = mm["name"]
+ if key.find("_mask") >= 0:
+ continue
+ if loxi_utils.skip_member_name(key):
+ continue
+ if key == "wildcards":
+ continue
+ if not key in of_match_members:
+ print "Key %s not found in match struct, v %s" % (key, match_v)
+ sys.exit(1)
+
+ # Check oxm list and the list above
+ for key in oxm.oxm_types:
+ if not key in of_match_members:
+ if not (key.find("_masked") > 0):
+ debug("Key %s in oxm.oxm_types, not of_match_members" % key)
+ sys.exit(1)
+ if not key[:-7] in of_match_members:
+ debug("Key %s in oxm.oxm_types, but %s not in of_match_members"
+ % (key, key[:-7]))
+ sys.exit(1)
+
+ for key in of_match_members:
+ if not key in oxm.oxm_types:
+ debug("Key %s in of_match_members, not in oxm.oxm_types" % key)
+ sys.exit(1)
+ if of_match_members[key]["m_type"] != oxm.oxm_types[key]:
+ debug("Type mismatch for key %s in oxm data: %s vs %s" %
+ (key, of_match_members[key]["m_type"], oxm.oxm_types[key]))
+ sys.exit(1)
+
+
diff --git a/loxi_front_end/of_h_utils.py b/loxi_front_end/of_h_utils.py
new file mode 100644
index 0000000..1259a90
--- /dev/null
+++ b/loxi_front_end/of_h_utils.py
@@ -0,0 +1,154 @@
+# 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 Utilities related to processing OF header files
+#
+
+import re
+import sys
+import os
+import c_parse_utils
+import loxi_utils.py_utils as py_utils
+import translation
+from generic_utils import *
+import copy
+
+# Silently ignore idents matching any of these
+ignore_idents = [
+ "OFPXMT_OFB_", "OFPFMF_", "OXM_OF_", "OFPXMT_",
+ "OPENFLOW_OPENFLOW_H", "OFP_ASSERT", "OFP_PACKED", "OFP_VERSION"
+ ]
+
+def warn_unmapped_ident(ident, t_name="enum"):
+ for ignore_ident in ignore_idents:
+ if re.match(ignore_ident, ident):
+ return
+ log("Skipping ident %s. Did not map %s identifier to LOXI" %
+ (ident, t_name))
+
+def fixup_values(ident, value, version, ident_list):
+ """
+ Fix up values for LOXI reasons
+
+ Translate defintions that refer to other identifiers
+ This is really just needed for the special case of OFPFW in 1.0 and 1.1.
+
+ Also, LOXI is aware of the change in type of port numbers, so
+ translate those here.
+ """
+ value_string = str(value).strip()
+ if ident.find("OFPP_") == 0 and version == of_g.VERSION_1_0:
+ value_string = "0x%x" % (int(value, 0) + 0xffff0000)
+
+ # Otherwise, if no reference to a wildcard value, all done
+ if value_string.find("OFPFW") < 0:
+ return value_string
+ for ident, id_value in ident_list.items():
+ id_value_string = "(" + str(id_value).strip() + ")"
+ # If the identifier has (, etc., ignore it; not handling params
+ if ident.find("(") >= 0:
+ continue
+ value_string = re.sub(ident, id_value_string, value_string)
+ return value_string
+
+def get_enum_dict(version, contents):
+ """
+ Given openflow.h input, create a dict for its enums
+ @param contents The raw contents of the C file
+
+ The dict returned is indexed by LOXI identifier. Each entry is a
+ DotDict with three keys, value, ofp_name and ofp_group. The value is an
+ int value when possible, otherwise a string. The ofp_name is the original
+ name from the openflow header file. The ofp_group is the enum type.
+ """
+ rv_list = {}
+
+ version_ref = of_g.short_version_names[version]
+ enum_list = c_parse_utils.extract_enums(contents)
+ defines_list = c_parse_utils.extract_defines(contents)
+
+ # First generate a list of all original idents and values for translation
+ full_ident_list = {}
+
+ for enum in enum_list:
+ (name, values) = c_parse_utils.extract_enum_vals(enum)
+ for (ident, value) in values:
+ full_ident_list[ident] = str(value).strip()
+ for ident, value in defines_list:
+ full_ident_list[ident] = str(value).strip()
+
+ # Process enum idents
+ for enum in enum_list:
+ (name, values) = c_parse_utils.extract_enum_vals(enum)
+ for (ident, value) in values:
+ loxi_name = translation.loxi_name(ident)
+ if not loxi_name:
+ warn_unmapped_ident(ident)
+ continue
+ if loxi_name in rv_list:
+ sys.stderr.write("\nError: %s in ident list already\n" %
+ loxi_name)
+ sys.exit(1)
+
+ value_str = fixup_values(ident, value, version, full_ident_list)
+ log("Adding LOXI identifier %s from name %s" % (loxi_name, ident))
+ rv_list[loxi_name] = py_utils.DotDict(dict(
+ ofp_name = ident,
+ ofp_group = name,
+ value = value_str))
+
+ for ident, value in defines_list:
+ loxi_name = translation.loxi_name(ident)
+ if not loxi_name:
+ warn_unmapped_ident(ident, "macro defn")
+ continue
+
+ value_str = fixup_values(ident, value, version, full_ident_list)
+ if loxi_name in rv_list:
+ if value_str != rv_list[loxi_name].value:
+ sys.stderr.write("""
+ERROR: IDENT COLLISION. Version %s, LOXI Ident %s.
+New ofp_name %s, value %s.
+Previous ofp_name %s, value %s,
+""" % (version_ref, loxi_name, ident, value_str,
+ rv_list[loxi_name].ofp_name), rv_list[loxi_name].value)
+ sys.exit(1)
+ else:
+ log("Ignoring redundant entry %s, mapping to %s" %
+ (ident, loxi_name))
+
+ rv_list[loxi_name] = py_utils.DotDict(dict(
+ ofp_name = ident,
+ ofp_group = "macro_definitions",
+ value = value_str))
+
+ return rv_list
+
+
+
+
diff --git a/loxi_front_end/oxm.py b/loxi_front_end/oxm.py
new file mode 100644
index 0000000..d4cf273
--- /dev/null
+++ b/loxi_front_end/oxm.py
@@ -0,0 +1,228 @@
+# 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.
+
+import of_g
+
+oxm_types = dict(
+ in_port = "of_port_no_t",
+ in_port_masked = "of_port_no_t",
+ in_phy_port = "of_port_no_t",
+ in_phy_port_masked = "of_port_no_t",
+ metadata = "uint64_t",
+ metadata_masked = "uint64_t",
+ eth_dst = "of_mac_addr_t",
+ eth_dst_masked = "of_mac_addr_t",
+ eth_src = "of_mac_addr_t",
+ eth_src_masked = "of_mac_addr_t",
+ eth_type = "uint16_t",
+ eth_type_masked = "uint16_t",
+ vlan_vid = "uint16_t",
+ vlan_vid_masked = "uint16_t",
+ vlan_pcp = "uint8_t",
+ vlan_pcp_masked = "uint8_t",
+ ip_dscp = "uint8_t",
+ ip_dscp_masked = "uint8_t",
+ ip_ecn = "uint8_t",
+ ip_ecn_masked = "uint8_t",
+ ip_proto = "uint8_t",
+ ip_proto_masked = "uint8_t",
+ ipv4_src = "uint32_t",
+ ipv4_src_masked = "uint32_t",
+ ipv4_dst = "uint32_t",
+ ipv4_dst_masked = "uint32_t",
+ tcp_src = "uint16_t",
+ tcp_src_masked = "uint16_t",
+ tcp_dst = "uint16_t",
+ tcp_dst_masked = "uint16_t",
+ udp_src = "uint16_t",
+ udp_src_masked = "uint16_t",
+ udp_dst = "uint16_t",
+ udp_dst_masked = "uint16_t",
+ sctp_src = "uint16_t",
+ sctp_src_masked = "uint16_t",
+ sctp_dst = "uint16_t",
+ sctp_dst_masked = "uint16_t",
+ icmpv4_type = "uint8_t",
+ icmpv4_type_masked = "uint8_t",
+ icmpv4_code = "uint8_t",
+ icmpv4_code_masked = "uint8_t",
+ arp_op = "uint16_t",
+ arp_op_masked = "uint16_t",
+ arp_spa = "uint32_t",
+ arp_spa_masked = "uint32_t",
+ arp_tpa = "uint32_t",
+ arp_tpa_masked = "uint32_t",
+ arp_sha = "of_mac_addr_t",
+ arp_sha_masked = "of_mac_addr_t",
+ arp_tha = "of_mac_addr_t",
+ arp_tha_masked = "of_mac_addr_t",
+ ipv6_src = "of_ipv6_t",
+ ipv6_src_masked = "of_ipv6_t",
+ ipv6_dst = "of_ipv6_t",
+ ipv6_dst_masked = "of_ipv6_t",
+ ipv6_flabel = "uint32_t",
+ ipv6_flabel_masked = "uint32_t",
+ icmpv6_type = "uint8_t",
+ icmpv6_type_masked = "uint8_t",
+ icmpv6_code = "uint8_t",
+ icmpv6_code_masked = "uint8_t",
+ ipv6_nd_target = "of_ipv6_t",
+ ipv6_nd_target_masked = "of_ipv6_t",
+ ipv6_nd_sll = "of_mac_addr_t",
+ ipv6_nd_sll_masked = "of_mac_addr_t",
+ ipv6_nd_tll = "of_mac_addr_t",
+ ipv6_nd_tll_masked = "of_mac_addr_t",
+ mpls_label = "uint32_t",
+ mpls_label_masked = "uint32_t",
+ mpls_tc = "uint8_t",
+ mpls_tc_masked = "uint8_t"
+ # FIXME Add 1.3 oxm elts
+ )
+
+oxm_wire_type = dict(
+ in_port = (0 << 1),
+ in_port_masked = (0 << 1) + 1,
+ in_phy_port = (1 << 1),
+ in_phy_port_masked = (1 << 1) + 1,
+ metadata = (2 << 1),
+ metadata_masked = (2 << 1) + 1,
+ eth_dst = (3 << 1),
+ eth_dst_masked = (3 << 1) + 1,
+ eth_src = (4 << 1),
+ eth_src_masked = (4 << 1) + 1,
+ eth_type = (5 << 1),
+ eth_type_masked = (5 << 1) + 1,
+ vlan_vid = (6 << 1),
+ vlan_vid_masked = (6 << 1) + 1,
+ vlan_pcp = (7 << 1),
+ vlan_pcp_masked = (7 << 1) + 1,
+ ip_dscp = (8 << 1),
+ ip_dscp_masked = (8 << 1) + 1,
+ ip_ecn = (9 << 1),
+ ip_ecn_masked = (9 << 1) + 1,
+ ip_proto = (10 << 1),
+ ip_proto_masked = (10 << 1) + 1,
+ ipv4_src = (11 << 1),
+ ipv4_src_masked = (11 << 1) + 1,
+ ipv4_dst = (12 << 1),
+ ipv4_dst_masked = (12 << 1) + 1,
+ tcp_src = (13 << 1),
+ tcp_src_masked = (13 << 1) + 1,
+ tcp_dst = (14 << 1),
+ tcp_dst_masked = (14 << 1) + 1,
+ udp_src = (15 << 1),
+ udp_src_masked = (15 << 1) + 1,
+ udp_dst = (16 << 1),
+ udp_dst_masked = (16 << 1) + 1,
+ sctp_src = (17 << 1),
+ sctp_src_masked = (17 << 1) + 1,
+ sctp_dst = (18 << 1),
+ sctp_dst_masked = (18 << 1) + 1,
+ icmpv4_type = (19 << 1),
+ icmpv4_type_masked = (19 << 1) + 1,
+ icmpv4_code = (20 << 1),
+ icmpv4_code_masked = (20 << 1) + 1,
+ arp_op = (21 << 1),
+ arp_op_masked = (21 << 1) + 1,
+ arp_spa = (22 << 1),
+ arp_spa_masked = (22 << 1) + 1,
+ arp_tpa = (23 << 1),
+ arp_tpa_masked = (23 << 1) + 1,
+ arp_sha = (24 << 1),
+ arp_sha_masked = (24 << 1) + 1,
+ arp_tha = (25 << 1),
+ arp_tha_masked = (25 << 1) + 1,
+ ipv6_src = (26 << 1),
+ ipv6_src_masked = (26 << 1) + 1,
+ ipv6_dst = (27 << 1),
+ ipv6_dst_masked = (27 << 1) + 1,
+ ipv6_flabel = (28 << 1),
+ ipv6_flabel_masked = (28 << 1) + 1,
+ icmpv6_type = (29 << 1),
+ icmpv6_type_masked = (29 << 1) + 1,
+ icmpv6_code = (30 << 1),
+ icmpv6_code_masked = (30 << 1) + 1,
+ ipv6_nd_target = (31 << 1),
+ ipv6_nd_target_masked = (31 << 1) + 1,
+ ipv6_nd_sll = (32 << 1),
+ ipv6_nd_sll_masked = (32 << 1) + 1,
+ ipv6_nd_tll = (33 << 1),
+ ipv6_nd_tll_masked = (33 << 1) + 1,
+ mpls_label = (34 << 1),
+ mpls_label_masked = (34 << 1) + 1,
+ mpls_tc = (35 << 1),
+ mpls_tc_masked = (35 << 1) + 1
+ # FIXME Add 1.3 oxm elts
+)
+
+def add_oxm_classes_1_2(classes, version):
+ """
+ Add the OXM classes to object passed. This is a dictionary
+ indexed by class name whose value is an array of member objects.
+ """
+ # First the parent class:
+ if version not in [of_g.VERSION_1_2, of_g.VERSION_1_3]:
+ return
+
+ members = []
+ classes["of_oxm"] = []
+ of_g.ordered_classes[version].append("of_oxm")
+ members.append(dict(name="type_len", m_type="uint32_t"))
+ classes["of_oxm_header"] = members
+ of_g.ordered_classes[version].append("of_oxm_header")
+
+ for oxm in oxm_types:
+ members = []
+ # Assert oxm_types[oxm] in of_base_types
+ m_type = oxm_types[oxm]
+ if m_type in of_g.of_mixed_types:
+ m_type = of_g.of_mixed_types[m_type][version]
+ # m_name = "value_" + of_g.of_base_types[m_type]["short_name"]
+ members.append(dict(name="type_len", m_type="uint32_t"))
+ # members.append(dict(name=m_name, m_type=oxm_types[oxm]))
+ members.append(dict(name="value", m_type=oxm_types[oxm]))
+ if oxm.find("_masked") > 0:
+ members.append(dict(name="value_mask", m_type=oxm_types[oxm]))
+
+ name = "of_oxm_" + oxm
+ of_g.ordered_classes[version].append(name)
+ classes[name] = members
+
+# /* Header for OXM experimenter match fields. */
+# struct ofp_oxm_experimenter_header {
+# uint32_t oxm_header; /* oxm_class = OFPXMC_EXPERIMENTER */
+# uint32_t experimenter; /* Experimenter ID which takes the same
+# form as in struct ofp_experimenter_header. */
+# };
+
+
+# enum ofp_vlan_id {
+# OFPVID_PRESENT = 0x1000,
+# OFPVID_NONE = 0x0000,
+# };
+
+# #define OFP_VLAN_NONE OFPVID_NONE
diff --git a/loxi_front_end/parser.py b/loxi_front_end/parser.py
new file mode 100644
index 0000000..6b0e1eb
--- /dev/null
+++ b/loxi_front_end/parser.py
@@ -0,0 +1,62 @@
+# 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.
+
+import pyparsing as P
+
+kw = P.Keyword
+s = P.Suppress
+lit = P.Literal
+
+# Useful for marking the type of a parse result (matches the empty string, but
+# shows up in the result)
+tag = lambda name: P.Empty().setParseAction(P.replaceWith(name))
+
+word = P.Word(P.alphanums + '_')
+
+identifier = word.copy().setName("identifier")
+
+# Type names
+scalar_type = word
+array_type = P.Combine(word + lit('[') - P.Word(P.alphanums + '_') - lit(']'))
+list_type = P.Combine(kw('list') - lit('(') - identifier - lit(')'))
+any_type = (array_type | list_type | scalar_type).setName("type name")
+
+# Structs
+struct_member = P.Group(any_type - identifier - s(';'))
+struct = kw('struct') - identifier - s('{') + \
+ P.Group(P.ZeroOrMore(struct_member)) + \
+ s('}') - s(';')
+
+# Metadata
+metadata_key = P.Or(kw("version")).setName("metadata key")
+metadata = tag('metadata') + s('#') - metadata_key - word
+
+grammar = P.ZeroOrMore(P.Group(struct) | P.Group(metadata))
+grammar.ignore(P.cppStyleComment)
+
+def parse(src):
+ return grammar.parseString(src, parseAll=True)
diff --git a/loxi_front_end/translation.py b/loxi_front_end/translation.py
new file mode 100644
index 0000000..6c39a3a
--- /dev/null
+++ b/loxi_front_end/translation.py
@@ -0,0 +1,126 @@
+# 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 Translation data between openflow.h and LOXI
+#
+
+import re
+import sys
+
+def loxi_name(ident):
+ """
+ Return the LOXI name of an openflow.h identifier
+ """
+
+ # Order at the outer level matters as super strings are listed first
+ rules = [
+# The following are for #define macros and have highest precedence
+ dict(OFP_MAX_TABLE_NAME_LEN = "OF_MAX_TABLE_NAME_LEN"),
+ dict(OFP_MAX_PORT_NAME_LEN = "OF_MAX_PORT_NAME_LEN"),
+ dict(OFP_TCP_PORT = "OF_TCP_PORT"),
+ dict(OFP_SSL_PORT = "OF_SSL_PORT"),
+ dict(OFP_ETH_ALEN = "OF_ETH_ALEN"),
+ dict(OFP_DEFAULT_MISS_SEND_LEN = "OF_DEFAULT_MISS_SEND_LEN"),
+ dict(OFP_VLAN_NONE = "OF_VLAN_UNTAGGED"),
+ dict(OFP_DL_TYPE_ETH2_CUTOFF = "OF_DL_TYPE_ETH2_CUTOFF"),
+ dict(OFP_DL_TYPE_NOT_ETH_TYPE = "OF_DL_TYPE_NOT_ETH_TYPE"),
+ dict(OFP_FLOW_PERMANENT = "OF_FLOW_PERMANENT"),
+ dict(OFP_DEFAULT_PRIORITY = "OF_DEFAULT_PRIORITY"),
+ dict(DESC_STR_LEN = "OF_DESC_STR_LEN"),
+ dict(SERIAL_NUM_LEN = "OF_SERIAL_NUM_LEN"),
+ dict(OFPQ_ALL = "OF_QUEUE_ALL"),
+ dict(OFPQ_MIN_RATE_UNCFG = "OF_QUEUE_MIN_RATE_UNCFG"),
+ dict(OFPQ_MAX_RATE_UNCFG = "OF_QUEUE_MAX_RATE_UNCFG"),
+ dict(OFP_NO_BUFFER = "OF_BUFFER_ID_NO_BUFFER"),
+
+# These are for enums; they map the prefixes
+ dict(OFPP_MAX = "OF_PORT_NUMBER_MAX"), # Special case
+ dict(OFPP_TABLE = "OF_PORT_DEST_USE_TABLE"), # Special case
+ dict(OFPP_ANY = "OF_PORT_DEST_WILDCARD"), # Special case
+ dict(OFPTC_ = "OF_TABLE_CONFIG_"),
+ dict(OFPIEH_ = "OF_IPV6_EXT_HDR_FLAG_"),
+ dict(OFPMBT_ = "OF_METER_BAND_TYPE_"),
+ dict(OFPMC_ = "OF_METER_MOD_COMMAND_"),
+ dict(OFPMF_ = "OF_METER_FLAG_"),
+ dict(OFPTFFC_ = "OF_TABLE_REQUEST_FAILED_"),
+ dict(OFPMMFC_ = "OF_METER_MOD_FAILED_"),
+ dict(OFPPR_ = "OF_PORT_CHANGE_REASON_"),
+ dict(OFPPMFC_ = "OF_PORT_MOD_FAILED_"),
+ dict(OFPP_ = "OF_PORT_DEST_"),
+ dict(OFPRRFC_ = "OF_ROLE_REQUEST_FAILED_"),
+ dict(OFPRR_ = "OF_FLOW_REMOVED_REASON_"),
+ dict(OFPR_ = "OF_PACKET_IN_REASON_"),
+ dict(OFPC_FRAG_ = "OF_CONFIG_FRAG_"),
+ dict(OFPC_INVALID_ = "OF_CONFIG_INVALID_"),
+ dict(OFPCML_ = "OF_CONTROLLER_PKT_"),
+ dict(OFPCR_ROLE_ = "OF_CONTROLLER_ROLE_"),
+ dict(OFPC_ = "OF_CAPABILITIES_FLAG_"),
+ dict(OFPPC_ = "OF_PORT_CONFIG_FLAG_"),
+ dict(OFPPS_ = "OF_PORT_STATE_FLAG_"),
+ dict(OFPPF_ = "OF_PORT_FEATURE_FLAG_"),
+ dict(OFPTT_ = "OF_TABLE_"),
+ dict(OFPT_ = "OF_OBJ_TYPE_"),
+ dict(OFPMT_ = "OF_MATCH_TYPE_"),
+ dict(OFPM_ = "OF_METER_"),
+ dict(OFPXMC_ = "OF_OXM_CLASS_"),
+ dict(OFPVID_ = "OF_VLAN_TAG_"),
+ dict(OFPGC_ = "OF_GROUP_"),
+ dict(OFPGT_ = "OF_GROUP_TYPE_"),
+ dict(OFPG_ = "OF_GROUP_"),
+ dict(OFPET_ = "OF_ERROR_TYPE_"),
+ dict(OFPFC_ = "OF_FLOW_MOD_COMMAND_"),
+ dict(OFPHFC_ = "OF_HELLO_FAILED_"),
+ dict(OFPBRC_ = "OF_REQUEST_FAILED_"),
+ dict(OFPBAC_ = "OF_ACTION_FAILED_"),
+ dict(OFPBIC_ = "OF_INSTRUCTION_FAILED_"),
+ dict(OFPBMC_ = "OF_MATCH_FAILED_"),
+ dict(OFPGMFC_ = "OF_GROUP_MOD_FAILED_"),
+ dict(OFPTMFC_ = "OF_TABLE_MOD_FAILED_"),
+ dict(OFPFMFC_ = "OF_FLOW_MOD_FAILED_"),
+ dict(OFPQOFC_ = "OF_QUEUE_OP_FAILED_"),
+ dict(OFPSCFC_ = "OF_SWITCH_CONFIG_FAILED_"),
+ dict(OFPQCFC_ = "OF_SWITCH_CONFIG_FAILED_"), # See EXT-208
+ dict(OFPAT_ = "OF_ACTION_TYPE_"),
+ dict(OFPFW_ = "OF_FLOW_WC_V1_"),
+ dict(OFPFF_ = "OF_FLOW_MOD_FLAG_"),
+ dict(OFPST_ = "OF_STATS_TYPE_"),
+ dict(OFPSF_ = "OF_STATS_REPLY_FLAG_"),
+ dict(OFPQT_ = "OF_QUEUE_PROPERTY_"),
+ dict(OFPIT_ = "OF_INSTRUCTION_TYPE_"),
+ dict(OFPGFC_ = "OF_GROUP_CAPABILITIES_"),
+ dict(OFPMP_ = "OF_MULTIPART_"),
+ dict(OFPMPF_ = "OF_MULTIPART_FLAG_"),
+ dict(OFPTFPT_ = "OF_TABLE_FEATURE_"),
+ ]
+
+ for entry in rules:
+ for id_from, id_to in entry.items():
+ if re.match(id_from, ident):
+ return re.sub(id_from, id_to, ident)
+ return None
+
diff --git a/loxi_front_end/type_maps.py b/loxi_front_end/type_maps.py
new file mode 100644
index 0000000..ecfd850
--- /dev/null
+++ b/loxi_front_end/type_maps.py
@@ -0,0 +1,1101 @@
+# 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.
+
+#
+# Miscellaneous type information
+#
+# Define the map between sub-class types and wire values. In each
+# case, an array indexed by wire version gives a hash from identifier
+# to wire value.
+#
+
+import of_g
+import sys
+from generic_utils import *
+import oxm
+import loxi_utils.loxi_utils as loxi_utils
+
+invalid_type = "invalid_type"
+invalid_value = "0xeeee" # Note, as a string
+
+################################################################
+#
+# Define type data for inheritance classes:
+# instructions, actions, queue properties and OXM
+#
+# Messages are not in this group; they're treated specially for now
+#
+# These are indexed by wire protocol number
+#
+################################################################
+
+instruction_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ goto_table = 1,
+ write_metadata = 2,
+ write_actions = 3,
+ apply_actions = 4,
+ clear_actions = 5,
+ experimenter = 0xffff
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ goto_table = 1,
+ write_metadata = 2,
+ write_actions = 3,
+ apply_actions = 4,
+ clear_actions = 5,
+ experimenter = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ goto_table = 1,
+ write_metadata = 2,
+ write_actions = 3,
+ apply_actions = 4,
+ clear_actions = 5,
+ meter = 6,
+ experimenter = 0xffff
+ )
+ }
+
+of_1_3_action_types = dict(
+ output = 0,
+ copy_ttl_out = 11,
+ copy_ttl_in = 12,
+ set_mpls_ttl = 15,
+ dec_mpls_ttl = 16,
+ push_vlan = 17,
+ pop_vlan = 18,
+ push_mpls = 19,
+ pop_mpls = 20,
+ set_queue = 21,
+ group = 22,
+ set_nw_ttl = 23,
+ dec_nw_ttl = 24,
+ set_field = 25,
+ push_pbb = 26,
+ pop_pbb = 27,
+ experimenter = 0xffff,
+ bsn_mirror = 0xffff,
+ bsn_set_tunnel_dst = 0xffff,
+ nicira_dec_ttl = 0xffff
+ )
+
+# Indexed by OF version
+action_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ output = 0,
+ set_vlan_vid = 1,
+ set_vlan_pcp = 2,
+ strip_vlan = 3,
+ set_dl_src = 4,
+ set_dl_dst = 5,
+ set_nw_src = 6,
+ set_nw_dst = 7,
+ set_nw_tos = 8,
+ set_tp_src = 9,
+ set_tp_dst = 10,
+ enqueue = 11,
+ experimenter = 0xffff,
+ bsn_mirror = 0xffff,
+ bsn_set_tunnel_dst = 0xffff,
+ nicira_dec_ttl = 0xffff
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ output = 0,
+ set_vlan_vid = 1,
+ set_vlan_pcp = 2,
+ set_dl_src = 3,
+ set_dl_dst = 4,
+ set_nw_src = 5,
+ set_nw_dst = 6,
+ set_nw_tos = 7,
+ set_nw_ecn = 8,
+ set_tp_src = 9,
+ set_tp_dst = 10,
+ copy_ttl_out = 11,
+ copy_ttl_in = 12,
+ set_mpls_label = 13,
+ set_mpls_tc = 14,
+ set_mpls_ttl = 15,
+ dec_mpls_ttl = 16,
+ push_vlan = 17,
+ pop_vlan = 18,
+ push_mpls = 19,
+ pop_mpls = 20,
+ set_queue = 21,
+ group = 22,
+ set_nw_ttl = 23,
+ dec_nw_ttl = 24,
+ experimenter = 0xffff,
+ bsn_mirror = 0xffff,
+ bsn_set_tunnel_dst = 0xffff,
+ nicira_dec_ttl = 0xffff
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ output = 0,
+ copy_ttl_out = 11,
+ copy_ttl_in = 12,
+ set_mpls_ttl = 15,
+ dec_mpls_ttl = 16,
+ push_vlan = 17,
+ pop_vlan = 18,
+ push_mpls = 19,
+ pop_mpls = 20,
+ set_queue = 21,
+ group = 22,
+ set_nw_ttl = 23,
+ dec_nw_ttl = 24,
+ set_field = 25,
+ experimenter = 0xffff,
+ bsn_mirror = 0xffff,
+ bsn_set_tunnel_dst = 0xffff,
+ nicira_dec_ttl = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:of_1_3_action_types
+
+ }
+
+action_id_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:of_1_3_action_types
+ }
+
+queue_prop_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ min_rate = 1,
+ # experimenter = 0xffff
+ ),
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ min_rate = 1,
+ # experimenter = 0xffff
+ ),
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ min_rate = 1,
+ max_rate = 2,
+ experimenter = 0xffff
+ ),
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ min_rate = 1,
+ max_rate = 2,
+ experimenter = 0xffff
+ )
+ }
+
+oxm_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(),
+
+ # version 1.2
+ of_g.VERSION_1_2:oxm.oxm_wire_type,
+
+ # version 1.3
+ of_g.VERSION_1_3:oxm.oxm_wire_type # FIXME needs update for 1.3?
+ }
+
+hello_elem_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ versionbitmap = 1
+ )
+ }
+
+table_feature_prop_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ instructions = 0,
+ instructions_miss = 1,
+ next_tables = 2,
+ next_tables_miss = 3,
+ write_actions = 4,
+ write_actions_miss = 5,
+ apply_actions = 6,
+ apply_actions_miss = 7,
+ match = 8,
+ wildcards = 10,
+ write_setfield = 12,
+ write_setfield_miss = 13,
+ apply_setfield = 14,
+ apply_setfield_miss = 15,
+# experimenter = 0xFFFE,
+# experimenter_miss = 0xFFFF,
+ experimenter = 0xFFFF, # Wrong: should be experimenter_miss
+ )
+ }
+
+meter_band_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ drop = 1,
+ dscp_remark = 2,
+ experimenter = 0xFFFF,
+ )
+ }
+
+# All inheritance data for non-messages
+inheritance_data = dict(
+ of_instruction = instruction_types,
+ of_action = action_types,
+ of_action_id = action_id_types,
+ of_oxm = oxm_types,
+ of_queue_prop = queue_prop_types,
+ of_hello_elem = hello_elem_types,
+ of_table_feature_prop = table_feature_prop_types,
+ of_meter_band = meter_band_types
+ )
+
+################################################################
+# Now generate the maps from parent to list of subclasses
+################################################################
+
+# # These lists have entries which are a fixed type, no inheritance
+# fixed_lists = [
+# "of_list_bucket",
+# "of_list_bucket_counter",
+# "of_list_flow_stats_entry",
+# "of_list_group_desc_stats_entry",
+# "of_list_group_stats_entry",
+# "of_list_packet_queue",
+# "of_list_port_desc",
+# "of_list_port_stats_entry",
+# "of_list_queue_stats_entry",
+# "of_list_table_stats_entry"
+# ]
+
+# for cls in fixed_lists:
+# base_type = list_to_entry_type(cls)
+# of_g.inheritance_map[base_type] = [base_type]
+
+inheritance_map = dict()
+for parent, versioned in inheritance_data.items():
+ inheritance_map[parent] = set()
+ for ver, subclasses in versioned.items():
+ for subcls in subclasses:
+ inheritance_map[parent].add(subcls)
+
+def class_is_virtual(cls):
+ """
+ Returns True if cls is a virtual class
+ """
+ if cls in inheritance_map:
+ return True
+ if cls.find("header") > 0:
+ return True
+ if loxi_utils.class_is_list(cls):
+ return True
+ return False
+
+################################################################
+#
+# These are message types
+#
+################################################################
+
+message_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ hello = 0,
+ error_msg = 1,
+ echo_request = 2,
+ echo_reply = 3,
+ experimenter = 4,
+ features_request = 5,
+ features_reply = 6,
+ get_config_request = 7,
+ get_config_reply = 8,
+ set_config = 9,
+ packet_in = 10,
+ flow_removed = 11,
+ port_status = 12,
+ packet_out = 13,
+ flow_mod = 14,
+ port_mod = 15,
+ stats_request = 16,
+ stats_reply = 17,
+ barrier_request = 18,
+ barrier_reply = 19,
+ queue_get_config_request = 20,
+ queue_get_config_reply = 21,
+ table_mod = 22 # Unofficial 1.0 extension
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ hello = 0,
+ error_msg = 1,
+ echo_request = 2,
+ echo_reply = 3,
+ experimenter = 4,
+ features_request = 5,
+ features_reply = 6,
+ get_config_request = 7,
+ get_config_reply = 8,
+ set_config = 9,
+ packet_in = 10,
+ flow_removed = 11,
+ port_status = 12,
+ packet_out = 13,
+ flow_mod = 14,
+ group_mod = 15,
+ port_mod = 16,
+ table_mod = 17,
+ stats_request = 18,
+ stats_reply = 19,
+ barrier_request = 20,
+ barrier_reply = 21,
+ queue_get_config_request = 22,
+ queue_get_config_reply = 23
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ hello = 0,
+ error_msg = 1,
+ echo_request = 2,
+ echo_reply = 3,
+ experimenter = 4,
+ features_request = 5,
+ features_reply = 6,
+ get_config_request = 7,
+ get_config_reply = 8,
+ set_config = 9,
+ packet_in = 10,
+ flow_removed = 11,
+ port_status = 12,
+ packet_out = 13,
+ flow_mod = 14,
+ group_mod = 15,
+ port_mod = 16,
+ table_mod = 17,
+ stats_request = 18,
+ stats_reply = 19,
+ barrier_request = 20,
+ barrier_reply = 21,
+ queue_get_config_request = 22,
+ queue_get_config_reply = 23,
+ role_request = 24,
+ role_reply = 25,
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ hello = 0,
+ error_msg = 1,
+ echo_request = 2,
+ echo_reply = 3,
+ experimenter = 4,
+ features_request = 5,
+ features_reply = 6,
+ get_config_request = 7,
+ get_config_reply = 8,
+ set_config = 9,
+ packet_in = 10,
+ flow_removed = 11,
+ port_status = 12,
+ packet_out = 13,
+ flow_mod = 14,
+ group_mod = 15,
+ port_mod = 16,
+ table_mod = 17,
+ stats_request = 18, # FIXME Multipart
+ stats_reply = 19,
+ barrier_request = 20,
+ barrier_reply = 21,
+ queue_get_config_request = 22,
+ queue_get_config_reply = 23,
+ role_request = 24,
+ role_reply = 25,
+ async_get_request = 26,
+ async_get_reply = 27,
+ async_set = 28,
+ meter_mod = 29
+ )
+ }
+
+################################################################
+#
+# These are other objects that have a notion of type but are
+# not (yet) promoted to objects with inheritance
+#
+################################################################
+
+stats_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ experimenter = 0xffff
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ experimenter = 0xffff
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ group_features = 8,
+ experimenter = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ group_features = 8,
+ meter = 9,
+ meter_config = 10,
+ meter_features = 11,
+ table_features = 12,
+ port_desc = 13,
+ experimenter = 0xffff
+ )
+ }
+
+common_flow_mod_types = dict(
+ add = 0,
+ modify = 1,
+ modify_strict = 2,
+ delete = 3,
+ delete_strict = 4
+ )
+
+flow_mod_types = {
+ # version 1.0
+ of_g.VERSION_1_0:common_flow_mod_types,
+ of_g.VERSION_1_1:common_flow_mod_types,
+ of_g.VERSION_1_2:common_flow_mod_types,
+ of_g.VERSION_1_3:common_flow_mod_types
+ }
+
+# These do not translate to objects (yet)
+error_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ flow_mod_failed = 3,
+ port_mod_failed = 4,
+ queue_op_failed = 5
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10,
+ role_request_failed = 11,
+ experimenter = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10,
+ role_request_failed = 11,
+ meter_mod_failed = 12,
+ table_features_failed= 13,
+ experimenter = 0xffff
+ )
+ }
+
+##
+# These are the objects whose length is specified by an external
+# reference, specifically another data member in the class.
+#
+#external_length_spec = {
+# ("of_packet_out", "actions", OF_VERSION_1_0) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_1) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_2) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_3) : "actions_len"
+#}
+
+
+################################################################
+#
+# type_val is the primary data structure that maps an
+# (class_name, version) pair to the wire data type value
+#
+################################################################
+
+type_val = dict()
+
+for version, classes in message_types.items():
+ for cls in classes:
+ name = "of_" + cls
+ type_val[(name, version)] = classes[cls]
+
+for parent, versioned in inheritance_data.items():
+ for version, subclasses in versioned.items():
+ for subcls, value in subclasses.items():
+ name = parent + "_" + subcls
+ type_val[(name, version)] = value
+
+# Special case OF-1.2 match type
+type_val[("of_match_v3", of_g.VERSION_1_2)] = 0x8000
+type_val[("of_match_v3", of_g.VERSION_1_3)] = 0x8000
+
+# Utility function
+def dict_to_array(d, m_val, def_val=-1):
+ """
+ Given a dictionary, d, with each value a small integer,
+ produce an array indexed by the integer whose value is the key.
+ @param d The dictionary
+ @param m_val Ignore values greater than m_val
+ @param def_val The default value (for indices not in range of d)
+ """
+
+ # Get the max value in range for hash
+ max_val = 0
+ for key in d:
+ if (d[key] > max_val) and (d[key] < m_val):
+ max_val = d[key]
+ ar = []
+ for x in range(0, max_val + 1):
+ ar.append(def_val)
+ for key in d:
+ if (d[key] < m_val):
+ ar[d[key]] = key
+ return ar
+
+def type_array_len(version_indexed, max_val):
+ """
+ Given versioned information about a type, calculate how long
+ the unified array should be.
+
+ @param version_indexed A dict indexed by version. Each value is a
+ dict indexed by a name and whose value is an integer
+ @param max_val Ignore values greater than this for length calcs
+ """
+ # First, find the max length of all arrays
+ arr_len = 0
+ for version, val_dict in version_indexed.items():
+ ar = dict_to_array(val_dict, max_val, invalid_type)
+ if arr_len < len(ar):
+ arr_len = len(ar)
+ return arr_len
+
+# FIXME: Need to move update for multipart messages
+
+stats_reply_list = [
+ "of_aggregate_stats_reply",
+ "of_desc_stats_reply",
+ "of_experimenter_stats_reply",
+ "of_flow_stats_reply",
+ "of_group_stats_reply",
+ "of_group_desc_stats_reply",
+ "of_group_features_stats_reply",
+ "of_meter_stats_reply",
+ "of_meter_config_stats_reply",
+ "of_meter_features_stats_reply",
+ "of_port_stats_reply",
+ "of_port_desc_stats_reply",
+ "of_queue_stats_reply",
+ "of_table_stats_reply",
+ "of_table_features_stats_reply"
+]
+
+stats_request_list = [
+ "of_aggregate_stats_request",
+ "of_desc_stats_request",
+ "of_experimenter_stats_request",
+ "of_flow_stats_request",
+ "of_group_stats_request",
+ "of_group_desc_stats_request",
+ "of_group_features_stats_request",
+ "of_meter_stats_request",
+ "of_meter_config_stats_request",
+ "of_meter_features_stats_request",
+ "of_port_stats_request",
+ "of_port_desc_stats_request",
+ "of_queue_stats_request",
+ "of_table_stats_request",
+ "of_table_features_stats_request"
+]
+
+flow_mod_list = [
+ "of_flow_add",
+ "of_flow_modify",
+ "of_flow_modify_strict",
+ "of_flow_delete",
+ "of_flow_delete_strict"
+]
+
+def sub_class_map(base_type, version):
+ """
+ Returns an iterable object giving the instance nameys and subclass types
+ for the base_type, version values
+ """
+ rv = []
+ if base_type not in inheritance_map:
+ return rv
+
+ for instance in inheritance_map[base_type]:
+ subcls = loxi_utils.instance_to_class(instance, base_type)
+ if not loxi_utils.class_in_version(subcls, version):
+ continue
+ rv.append((instance, subcls))
+
+ return rv
+
+################################################################
+#
+# Extension related data and functions
+#
+################################################################
+
+# Per OF Version, per experimenter, map exp msg type (subtype) to object IDs
+# @fixme Generate defines for OF_<exp>_SUBTYPE_<msg> for the values below?
+extension_message_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ "of_bsn_set_ip_mask" : 0,
+ "of_bsn_get_ip_mask_request" : 1,
+ "of_bsn_get_ip_mask_reply" : 2,
+ "of_bsn_set_mirroring" : 3,
+ "of_bsn_get_mirroring_request" : 4,
+ "of_bsn_get_mirroring_reply" : 5,
+ "of_bsn_shell_command" : 6,
+ "of_bsn_shell_output" : 7,
+ "of_bsn_shell_status" : 8,
+ "of_bsn_get_interfaces_request" : 9,
+ "of_bsn_get_interfaces_reply" : 10,
+ },
+ nicira = { # Nicira extensions, value is subtype
+ "of_nicira_controller_role_request" : 10,
+ "of_nicira_controller_role_reply" : 11,
+ },
+ ),
+ of_g.VERSION_1_1:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ "of_bsn_set_mirroring" : 3,
+ "of_bsn_get_mirroring_request" : 4,
+ "of_bsn_get_mirroring_reply" : 5,
+ "of_bsn_get_interfaces_request" : 9,
+ "of_bsn_get_interfaces_reply" : 10,
+ },
+ ),
+ of_g.VERSION_1_2:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ "of_bsn_set_mirroring" : 3,
+ "of_bsn_get_mirroring_request" : 4,
+ "of_bsn_get_mirroring_reply" : 5,
+ "of_bsn_get_interfaces_request" : 9,
+ "of_bsn_get_interfaces_reply" : 10,
+ },
+ ),
+ of_g.VERSION_1_3:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ "of_bsn_set_mirroring" : 3,
+ "of_bsn_get_mirroring_request" : 4,
+ "of_bsn_get_mirroring_reply" : 5,
+ "of_bsn_get_interfaces_request" : 9,
+ "of_bsn_get_interfaces_reply" : 10,
+ },
+ ),
+}
+
+# Set to empty dict if no extension actions defined
+# Per OF Version, per experimenter, map actions to subtype
+extension_action_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ "of_action_bsn_mirror" : 1,
+ "of_action_bsn_set_tunnel_dst" : 2,
+ },
+ nicira = { # of_action_nicira_
+ "of_action_nicira_dec_ttl" : 18,
+ }
+ ),
+ of_g.VERSION_1_1:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ "of_action_bsn_mirror" : 1,
+ "of_action_bsn_set_tunnel_dst" : 2,
+ },
+ nicira = { # of_action_nicira_
+ "of_action_nicira_dec_ttl" : 18,
+ }
+ ),
+ of_g.VERSION_1_2:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ "of_action_bsn_mirror" : 1,
+ "of_action_bsn_set_tunnel_dst" : 2,
+ },
+ nicira = { # of_action_nicira_
+ "of_action_nicira_dec_ttl" : 18,
+ }
+ ),
+ of_g.VERSION_1_3:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ "of_action_bsn_mirror" : 1,
+ "of_action_bsn_set_tunnel_dst" : 2,
+ },
+ nicira = { # of_action_nicira_
+ "of_action_nicira_dec_ttl" : 18,
+ }
+ ),
+}
+
+# Set to empty dict if no extension actions defined
+# Per OF Version, per experimenter, map actions to subtype
+extension_action_id_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict( # Version 1.3 extensions
+ bsn = { # of_action_bsn_
+ "of_action_id_bsn_mirror" : 1,
+ "of_action_id_bsn_set_tunnel_dst" : 2,
+ },
+ nicira = { # of_action_nicira_
+ "of_action_id_nicira_dec_ttl" : 18,
+ }
+ ),
+}
+
+# Set to empty dict if no extension instructions defined
+extension_instruction_subtype = {}
+
+# Set to empty dict if no extension instructions defined
+extension_queue_prop_subtype = {}
+
+# Set to empty dict if no extension instructions defined
+extension_table_feature_prop_subtype = {}
+
+extension_objects = [
+ extension_message_subtype,
+ extension_action_subtype,
+ extension_action_id_subtype,
+ extension_instruction_subtype,
+ extension_queue_prop_subtype,
+ extension_table_feature_prop_subtype
+]
+
+################################################################
+# These are extension type generic (for messages, actions...)
+################################################################
+
+def extension_to_experimenter_name(cls):
+ """
+ Return the name of the experimenter if class is an
+ extension, else None
+
+ This is brute force; we search all extension data for a match
+ """
+
+ for ext_obj in extension_objects:
+ for version, exp_list in ext_obj.items():
+ for exp_name, classes in exp_list.items():
+ if cls in classes:
+ return exp_name
+ return None
+
+def extension_to_experimenter_id(cls):
+ """
+ Return the ID of the experimenter if class is an
+ extension, else None
+ """
+ exp_name = extension_to_experimenter_name(cls)
+ if exp_name:
+ return of_g.experimenter_name_to_id[exp_name]
+ return None
+
+def extension_to_experimenter_macro_name(cls):
+ """
+ Return the "macro name" of the ID of the experimenter if class is an
+ extension, else None
+ """
+ exp_name = extension_to_experimenter_name(cls)
+ if exp_name:
+ return "OF_EXPERIMENTER_ID_" + exp_name.upper()
+ return None
+
+def extension_to_subtype(cls, version):
+ """
+ Generic across all extension objects, return subtype identifier
+ """
+ for ext_obj in extension_objects:
+ for version, exp_list in ext_obj.items():
+ for exp_name, classes in exp_list.items():
+ if cls in classes:
+ return classes[cls]
+
+def class_is_extension(cls, version):
+ """
+ Return True if class, version is recognized as an extension
+ of any type (message, action....)
+
+ Accepts of_g.OF_VERSION_ANY
+ """
+
+ for ext_obj in extension_objects:
+ if cls_is_ext_obj(cls, version, ext_obj):
+ return True
+
+ return False
+
+# Internal
+def cls_is_ext_obj(cls, version, ext_obj):
+ """
+ @brief Return True if cls in an extension of type ext_obj
+ @param cls The class to check
+ @param version The version to check
+ @param ext_obj The extension object dictionary (messages, actions...)
+
+ Accepts of_g.VERSION_ANY
+ """
+
+ # Call with each version if "any" is passed
+ if version == of_g.VERSION_ANY:
+ for v in of_g.of_version_range:
+ if cls_is_ext_obj(cls, v, ext_obj):
+ return True
+ else: # Version specified
+ if version in ext_obj:
+ for exp, subtype_vals in ext_obj[version].items():
+ if cls in subtype_vals:
+ return True
+
+ return False
+
+################################################################
+# These are extension message specific
+################################################################
+
+def message_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_message_subtype)
+
+def extension_message_to_subtype(cls, version):
+ """
+ Return the subtype of the experimenter message if the class is an
+ extension, else None
+ """
+ if version in extension_message_subtype:
+ for exp, classes in extension_message_subtype[version].items():
+ for ext_class, subtype in classes.items():
+ if cls == ext_class:
+ return subtype
+ return None
+
+################################################################
+# These are extension action specific
+################################################################
+
+def action_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an action extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_action_subtype)
+
+def extension_action_to_subtype(cls, version):
+ """
+ Return the ID of the action subtype (for its experimenteer)
+ if class is an action extension, else None
+ """
+ if version in extension_action_subtype:
+ for exp, classes in extension_action_subtype[version].items():
+ if cls in classes:
+ return classes[cls]
+
+ return None
+
+################################################################
+# These are extension action specific
+################################################################
+
+def action_id_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an action ID extension
+ This is brute force, searching records for a match
+ """
+ if version not in [of_g.VERSION_1_3]: # Action IDs only 1.3
+ return False
+ return cls_is_ext_obj(cls, version, extension_action_id_subtype)
+
+def extension_action_id_to_subtype(cls, version):
+ """
+ Return the ID of the action ID subtype (for its experimenteer)
+ if class is an action ID extension, else None
+ """
+ if version in extension_action_id_subtype:
+ for exp, classes in extension_action_id_subtype[version].items():
+ if cls in classes:
+ return classes[cls]
+
+ return None
+
+################################################################
+# These are extension instruction specific
+################################################################
+
+def instruction_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_instruction_subtype)
+
+################################################################
+# These are extension queue_prop specific
+################################################################
+
+def queue_prop_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_queue_prop_subtype)
+
+################################################################
+# These are extension table_feature_prop specific
+################################################################
+
+def table_feature_prop_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version,
+ extension_table_feature_prop_subtype)