Merge branch 'master' of github.com:floodlight/loxigen into funnel
diff --git a/Makefile b/Makefile
index 401b2f1..dbaf08b 100644
--- a/Makefile
+++ b/Makefile
@@ -84,6 +84,12 @@
 	# Unfortunately, mvn eclipse:eclipse resolves the symlink, which doesn't work with eclipse
 	cd ${OPENFLOWJ_WORKSPACE} && perl -pi -e 's{<classpathentry kind="src" path="[^"]*/java_gen/pre-written/src/}{<classpathentry kind="src" path="src/}' .classpath
 
+wireshark: .loxi_ts.wireshark
+
+.loxi_ts.wireshark: ${LOXI_PY_FILES} ${LOXI_TEMPLATE_FILES} ${INPUT_FILES}
+	./loxigen.py --install-dir=${LOXI_OUTPUT_DIR} --lang=wireshark
+	touch $@
+
 clean:
 	rm -rf loxi_output # only delete generated files in the default directory
 	rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python .loxi_ts.java
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 19ac5bc..d07ddaf 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -542,6 +542,8 @@
             return ("queueprop", "OFQueueProp", None)
         elif loxi_utils.class_is_hello_elem(self.c_name):
             return ("", "OFHelloElem", None)
+        elif loxi_utils.class_is_table_feature_prop(self.c_name):
+            return ("", "OFTableFeatureProp", None)
         else:
             return ("", None, None)
 
diff --git a/lang_wireshark.py b/lang_wireshark.py
new file mode 100644
index 0000000..9f73543
--- /dev/null
+++ b/lang_wireshark.py
@@ -0,0 +1,43 @@
+# 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.
+
+"""
+Wireshark dissector backend for LOXI
+
+Target directory structure:
+    wireshark:
+        openflow.lua
+
+The user will copy openflow.lua into ~/.wireshark/plugins, where it will be
+loaded automatically by Wireshark.
+"""
+
+import wireshark_gen
+
+targets = {
+    'wireshark/openflow.lua' : wireshark_gen.generate
+}
diff --git a/loxi_ir.py b/loxi_ir.py
index ae89207..a70f041 100644
--- a/loxi_ir.py
+++ b/loxi_ir.py
@@ -62,7 +62,12 @@
 @param classes List of OFClass objects
 @param enums List of Enum objects
 """
-OFProtocol = namedtuple('OFProtocol', ['wire_version', 'classes', 'enums'])
+class OFProtocol(namedtuple('OFProtocol', ['wire_version', 'classes', 'enums'])):
+    def class_by_name(self, name):
+        return find(lambda ofclass: ofclass.name == name, self.classes)
+
+    def enum_by_name(self, name):
+        return find(lambda enum: enum.name == name, self.enums)
 
 """
 An OpenFlow class
@@ -79,7 +84,11 @@
 """
 class OFClass(namedtuple('OFClass', ['name', 'superclass', 'members', 'virtual', 'params'])):
     def member_by_name(self, name):
-        return find(self.members, lambda m: hasattr(m, "name") and m.name == name)
+        return find(lambda m: hasattr(m, "name") and m.name == name, self.members)
+
+    @property
+    def discriminator(self):
+        return find(lambda m: type(m) == OFDiscriminatorMember, self.members)
 
 """
 Normal field
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index 5071848..767594a 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -1521,7 +1521,7 @@
 // FIXME: These are padded to 8 byte align beyond the length indicated
 
 struct of_table_feature_prop {
-    uint16_t         type;
+    uint16_t         type == ?;
     uint16_t         length;
 };
 
diff --git a/wireshark_gen/__init__.py b/wireshark_gen/__init__.py
new file mode 100644
index 0000000..316de21
--- /dev/null
+++ b/wireshark_gen/__init__.py
@@ -0,0 +1,98 @@
+# 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 os
+from collections import namedtuple
+import loxi_utils.loxi_utils as utils
+import loxi_front_end
+import of_g
+from loxi_ir import *
+import field_info
+
+templates_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates')
+
+DissectorField = namedtuple("DissectorField", ["fullname", "name", "type", "base"])
+
+proto_names = { 1: 'of10', 2: 'of11', 3: 'of12', 4: 'of13' }
+def make_field_name(wire_version, ofclass_name, member_name):
+    return "%s.%s.%s" % (proto_names[wire_version],
+                         ofclass_name[3:],
+                         member_name)
+
+def get_field_info(version, cls, name, oftype):
+    """
+    Decide on a Wireshark type and base for a given field.
+
+    Returns (type, base)
+    """
+    if oftype.startswith("list"):
+        return "BYTES", "NONE"
+
+    ofproto = of_g.ir[version]
+    enum = ofproto.enum_by_name(oftype)
+
+    if enum:
+        field_type = "UINT32"
+    elif oftype in field_info.oftype_to_wireshark_type:
+        field_type = field_info.oftype_to_wireshark_type[oftype]
+    else:
+        print "WARN missing oftype_to_wireshark_type for", oftype
+        field_type = "BYTES"
+
+    if enum:
+        if enum.is_bitmask:
+            field_base = "HEX"
+        else:
+            field_base = "DEC"
+    elif oftype in field_info.field_to_base:
+        field_base = field_info.field_to_base[name]
+    elif oftype in field_info.oftype_to_base:
+        field_base = field_info.oftype_to_base[oftype]
+    else:
+        print "WARN missing oftype_to_base for", oftype
+        field_base = "NONE"
+
+    return field_type, field_base
+
+def create_fields():
+    r = []
+    for wire_version, ofproto in of_g.ir.items():
+        for ofclass in ofproto.classes:
+            for m in ofclass.members:
+                if isinstance(m, OFPadMember):
+                    continue
+                fullname = make_field_name(wire_version, ofclass.name, m.name)
+                field_type, field_base = get_field_info(wire_version, ofclass.name, m.name, m.oftype)
+                r.append(DissectorField(fullname, m.name, field_type, field_base))
+
+    return r
+
+def generate(out, name):
+    context = {
+        'fields': create_fields(),
+    }
+    utils.render_template(out, "openflow.lua", [templates_dir], context)
diff --git a/wireshark_gen/field_info.py b/wireshark_gen/field_info.py
new file mode 100644
index 0000000..09011c7
--- /dev/null
+++ b/wireshark_gen/field_info.py
@@ -0,0 +1,87 @@
+# 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.
+
+# Map from LOXI types to Wireshark types
+oftype_to_wireshark_type = {
+    "char": "INT8",
+    "uint8_t": "UINT8",
+    "uint16_t": "UINT16",
+    "uint32_t": "UINT32",
+    "uint64_t": "UINT64",
+    "of_mac_addr_t": "ETHER",
+    "of_ipv4_t": "IPv4",
+    "of_ipv6_t": "IPv6",
+    "of_port_name_t": "STRINGZ",
+    "of_table_name_t": "STRINGZ",
+    "of_desc_str_t": "STRINGZ",
+    "of_serial_num_t": "STRINGZ",
+    "of_octets_t": "BYTES",
+    "of_port_no_t": "UINT32",
+    "of_port_desc_t": "STRINGZ",
+    "of_bsn_vport_t": "BYTES",
+    "of_bsn_vport_q_in_q_t": "BYTES",
+    "of_fm_cmd_t": "UINT16",
+    "of_wc_bmap_t": "UINT64",
+    "of_match_bmap_t": "UINT64",
+    "of_match_t": "BYTES",
+    "of_oxm_t": "BYTES",
+    "of_meter_features_t": "BYTES",
+}
+
+# Map from LOXI type to Wireshark base
+oftype_to_base = {
+    "char": "DEC",
+    "uint8_t": "DEC",
+    "uint16_t": "DEC",
+    "uint32_t": "DEC",
+    "uint64_t": "DEC",
+    "of_mac_addr_t": "NONE",
+    "of_ipv4_t": "NONE",
+    "of_ipv6_t": "NONE",
+    "of_port_name_t": "NONE",
+    "of_table_name_t": "NONE",
+    "of_desc_str_t": "NONE",
+    "of_serial_num_t": "NONE",
+    "of_octets_t": "NONE",
+    "of_port_no_t": "DEC",
+    "of_port_desc_t": "NONE",
+    "of_bsn_vport_t": "NONE",
+    "of_bsn_vport_q_in_q_t": "NONE",
+    "of_fm_cmd_t": "DEC",
+    "of_wc_bmap_t": "HEX",
+    "of_match_bmap_t": "HEX",
+    "of_match_t": "NONE",
+    "of_oxm_t": "NONE",
+    "of_meter_features_t": "NONE",
+}
+
+# Override oftype_to_base for certain field names
+field_to_base = {
+    "eth_type": "HEX",
+    "cookie": "HEX",
+    "datapath_id": "HEX",
+}
diff --git a/wireshark_gen/templates/.gitignore b/wireshark_gen/templates/.gitignore
new file mode 100644
index 0000000..c3ed10e
--- /dev/null
+++ b/wireshark_gen/templates/.gitignore
@@ -0,0 +1 @@
+*.cache
diff --git a/wireshark_gen/templates/_copyright.lua b/wireshark_gen/templates/_copyright.lua
new file mode 100644
index 0000000..8700ad3
--- /dev/null
+++ b/wireshark_gen/templates/_copyright.lua
@@ -0,0 +1,42 @@
+:: # 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.
+::
+-- Copyright 2013, Big Switch Networks, Inc. This library was generated
+-- by the LoxiGen Compiler.
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 2 of the License, or
+-- (at your option) any later version.
+-- 
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+-- 
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/wireshark_gen/templates/_ofclass_dissector.lua b/wireshark_gen/templates/_ofclass_dissector.lua
new file mode 100644
index 0000000..f1c81b0
--- /dev/null
+++ b/wireshark_gen/templates/_ofclass_dissector.lua
@@ -0,0 +1,52 @@
+:: # 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.
+::
+:: from loxi_ir import *
+:: from wireshark_gen import make_field_name
+:: attrs = []
+:: if ofclass.virtual: attrs.append("virtual")
+:: if ofclass.superclass: attrs.append("child")
+:: if not ofclass.superclass: attrs.append("top-level")
+-- ${' '.join(attrs)} class ${ofclass.name}
+:: if ofclass.superclass:
+-- Child of ${ofclass.superclass}
+:: #endif
+:: if ofclass.virtual:
+-- Discriminator is ${ofclass.discriminator.name}
+:: #endif
+function ${name}(reader, subtree)
+:: for m in ofclass.members:
+:: if isinstance(m, OFPadMember):
+    reader.skip(${m.length})
+:: continue
+:: #endif
+:: field_name = make_field_name(version, ofclass.name, m.name)
+:: reader_name = "read_" + m.oftype.replace(')', '').replace('(', '_')
+    ${reader_name}(reader, ${version}, subtree, '${field_name}')
+:: #endfor
+    return '${ofclass.name}'
+end
diff --git a/wireshark_gen/templates/_ofreader.lua b/wireshark_gen/templates/_ofreader.lua
new file mode 100644
index 0000000..b25f7c8
--- /dev/null
+++ b/wireshark_gen/templates/_ofreader.lua
@@ -0,0 +1,68 @@
+:: # 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.
+::
+OFReader = {}
+OFReader.new = function(buf, offset)
+    local self = {}
+    offset = offset or 0
+
+    self.read = function(len)
+        local r = buf(offset, len)
+        offset = offset + len
+        return r
+    end
+
+    self.read_all = function()
+        local r = buf(offset, buf:len() - offset)
+        offset = buf:len()
+        return r
+    end
+
+    self.peek = function(off, len)
+        return buf(offset + off, len)
+    end
+
+    self.peek_all = function(off)
+        return buf(offset + off, buf:len() - offset - off)
+    end
+
+    self.skip = function(len)
+        offset = offset + len
+    end
+
+    self.is_empty = function()
+        return offset == buf:len()
+    end
+
+    self.slice = function(len)
+        r = OFReader.new(buf(offset, len))
+        offset = offset + len
+        return r
+    end
+    
+    return self
+end
diff --git a/wireshark_gen/templates/_oftype_readers.lua b/wireshark_gen/templates/_oftype_readers.lua
new file mode 100644
index 0000000..fb6eb6f
--- /dev/null
+++ b/wireshark_gen/templates/_oftype_readers.lua
@@ -0,0 +1,125 @@
+:: # 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.
+
+function read_scalar(reader, subtree, field_name, length)
+    subtree:add(fields[field_name], reader.read(length))
+end
+
+function read_uint8_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 1)
+end
+
+function read_uint16_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 2)
+end
+
+function read_uint32_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 4)
+end
+
+function read_uint64_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 8)
+end
+
+function read_of_octets_t(reader, version, subtree, field_name)
+    if not reader.is_empty() then
+        subtree:add(fields[field_name], reader.read_all())
+    end
+end
+
+function read_list_of_hello_elem_t(reader, version, subtree, field_name)
+    -- TODO
+end
+
+function read_of_match_t(reader, version, subtree, field_name)
+    if version == 1 then
+        dissect_of_match_v1_v1(reader, subtree:add(fields[field_name]))
+    elseif version == 2 then
+        dissect_of_match_v2_v2(reader, subtree:add(fields[field_name]))
+    elseif version >= 3 then
+        dissect_of_match_v3_v3(reader, subtree:add(fields[field_name]))
+    end
+end
+
+function read_of_wc_bmap_t(reader, version, subtree, field_name)
+    if version <= 2 then
+        read_scalar(reader, subtree, field_name, 4)
+    else
+        read_scalar(reader, subtree, field_name, 8)
+    end
+end
+
+function read_of_port_no_t(reader, version, subtree, field_name)
+    if version == 1 then
+        read_scalar(reader, subtree, field_name, 2)
+    else
+        read_scalar(reader, subtree, field_name, 4)
+    end
+end
+
+function read_of_mac_addr_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 6)
+end
+
+function read_of_ipv4_t(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 4)
+end
+
+function read_of_fm_cmd_t(reader, version, subtree, field_name)
+    if version == 1 then
+        read_scalar(reader, subtree, field_name, 2)
+    else
+        read_scalar(reader, subtree, field_name, 1)
+    end
+end
+
+function read_ofp_flow_mod_flags(reader, version, subtree, field_name)
+    read_scalar(reader, subtree, field_name, 2)
+end
+
+function read_list_of_action_t(reader, version, subtree, field_name)
+    if reader.is_empty() then
+        return
+    end
+
+    local list = subtree:add(fields[field_name], reader.peek_all(0))
+    while not reader.is_empty() do
+        local action_len = reader.peek(2, 2):uint()
+        local child_reader = reader.slice(action_len)
+        local child_subtree = list:add(fields[field_name], child_reader.peek_all(0))
+        local info = dissect_of_action_v1(child_reader, child_subtree)
+        child_subtree:set_text(info)
+    end
+end
+
+function read_list_of_port_desc_t(reader, version, subtree, field_name)
+    -- TODO
+end
+
+function read_list_of_packet_queue_t(reader, version, subtree, field_name)
+    -- TODO
+end
diff --git a/wireshark_gen/templates/openflow.lua b/wireshark_gen/templates/openflow.lua
new file mode 100644
index 0000000..94b9153
--- /dev/null
+++ b/wireshark_gen/templates/openflow.lua
@@ -0,0 +1,155 @@
+:: # 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
+:: ir = of_g.ir
+:: include('_copyright.lua')
+
+:: include('_ofreader.lua')
+
+:: include('_oftype_readers.lua')
+
+p_of = Proto ("of", "OpenFlow")
+
+local openflow_versions = {
+:: for (version, name) in of_g.param_version_names.items():
+    [${version}] = "${name}",
+:: #endfor
+}
+
+:: for version, ofproto in ir.items():
+:: for enum in ofproto.enums:
+local enum_v${version}_${enum.name} = {
+:: for (name, value) in enum.values:
+    [${value}] = "${name}",
+:: #endfor
+}
+
+:: #endfor
+
+:: #endfor
+
+fields = {}
+:: for field in fields:
+fields[${repr(field.fullname)}] = ProtoField.new("${field.name}", "${field.fullname}", "FT_${field.type}", nil, "BASE_${field.base}")
+:: #endfor
+
+p_of.fields = {
+:: for field in fields:
+    fields[${repr(field.fullname)}],
+:: #endfor
+}
+
+-- Subclass maps for virtual classes
+:: for version, ofproto in ir.items():
+:: for ofclass in ofproto.classes:
+:: if ofclass.virtual:
+${ofclass.name}_v${version}_dissectors = {}
+:: #endif
+:: #endfor
+:: #endfor
+
+--- Dissectors for each class
+
+:: for version, ofproto in ir.items():
+:: for ofclass in ofproto.classes:
+:: name = 'dissect_%s_v%d' % (ofclass.name, version)
+:: include('_ofclass_dissector.lua', name=name, ofclass=ofclass, version=version)
+:: if ofclass.superclass:
+:: discriminator = ofproto.class_by_name(ofclass.superclass).discriminator
+:: discriminator_value = ofclass.member_by_name(discriminator.name).value
+${ofclass.superclass}_v${version}_dissectors[${discriminator_value}] = ${name}
+
+:: #endif
+:: #endfor
+:: #endfor
+
+local of_message_dissectors = {
+:: for version in ir:
+    [${version}] = of_header_v${version}_dissectors,
+:: #endfor
+}
+
+function dissect_of_message(buf, root)
+    local reader = OFReader.new(buf)
+    local subtree = root:add(p_of, buf(0))
+    local version_val = buf(0,1):uint()
+    local type_val = buf(1,1):uint()
+
+    local protocol = "OF ?"
+    if openflow_versions[version_val] then
+        protocol = "OF " .. openflow_versions[version_val]
+    end
+
+    local info = "unknown"
+    if of_message_dissectors[version_val] and of_message_dissectors[version_val][type_val] then
+        info = of_message_dissectors[version_val][type_val](reader, subtree)
+    end
+
+    return protocol, info
+end
+
+-- of dissector function
+function p_of.dissector (buf, pkt, root)
+    local offset = 0
+    repeat
+        if buf:len() - offset >= 4 then
+            msg_len = buf(offset+2,2):uint()
+            if offset + msg_len > buf:len() then
+                -- we don't have all the data we need yet
+                pkt.desegment_len = offset + msg_len - buf:len()
+                return
+            end
+
+            protocol, info = dissect_of_message(buf(offset, msg_len), root)
+
+            if offset == 0 then
+                pkt.cols.protocol:clear()
+                pkt.cols.info:clear()
+            else
+                pkt.cols.protocol:append(" + ")
+                pkt.cols.info:append(" + ")
+            end
+            pkt.cols.protocol:append(protocol)
+            pkt.cols.info:append(info)
+            offset = offset + msg_len
+        else
+            -- we don't have all of length field yet
+            pkt.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
+            return
+        end
+    until offset >= buf:len()
+end
+
+-- Initialization routine
+function p_of.init()
+end
+
+-- register a chained dissector for OpenFlow port numbers
+local tcp_dissector_table = DissectorTable.get("tcp.port")
+tcp_dissector_table:add(6633, p_of)
+tcp_dissector_table:add(6653, p_of)