From: Anders Broman <anders.broman@ericsson.com>
Date: Thu, 11 Jul 2013 21:13:27 +0000
Subject: [PATCH] The beginning of an openflow dissector.
svn path=/trunk/; revision=50513
Fix cut-and-pasteo found by a compiler warning.
Mark an unused parameter as unused.
svn path=/trunk/; revision=50515
Update dissection.
svn path=/trunk/; revision=50604
Fix some Dead Store (Dead assignement/Dead increment) Warning found by Clang
svn path=/trunk/; revision=50621
Fix warnings found by fix-encoding-args tools.(and remove some tabs also)
svn path=/trunk/; revision=50623
dissect OFPT_PACKET_IN.
svn path=/trunk/; revision=51633
Dissect more of openflow.
svn path=/trunk/; revision=51649
Fix
packet-openflow.c:318: warning: return type defaults to 'int'
packet-openflow.c: In function 'dissect_openflow_ofp_match_v_1_0':
packet-openflow.c:350: warning: control reaches end of non-void function
svn path=/trunk/; revision=51650
Fix compilation with Linux packet-openflow.c: In function 'dissect_openflow_pkt_out': packet-openflow.c:663:13: error: variable 'actions_len' set but not used [-Werror=unused-but-set-variable]
And make fix-encoding-args happy
svn path=/trunk/; revision=51651
Fix some Dead Store (Dead assignement/Dead increment) Warning found by Clang
svn path=/trunk/; revision=51737
Fix "malformed packet" and tidy up a bit.
svn path=/trunk/; revision=51740
diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt
index 9c547b3..22fd862 100644
--- a/epan/CMakeLists.txt
+++ b/epan/CMakeLists.txt
@@ -972,6 +972,7 @@ set(DISSECTOR_SRC
dissectors/packet-olsr.c
dissectors/packet-omapi.c
dissectors/packet-omron-fins.c
+ dissectors/packet-openflow.c
dissectors/packet-opensafety.c
dissectors/packet-openvpn.c
dissectors/packet-openwire.c
diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common
index 73217d7..3986ffd 100644
--- a/epan/dissectors/Makefile.common
+++ b/epan/dissectors/Makefile.common
@@ -893,6 +893,7 @@ DISSECTOR_SRC = \
packet-olsr.c \
packet-omapi.c \
packet-omron-fins.c \
+ packet-openflow.c \
packet-opensafety.c \
packet-openvpn.c \
packet-openwire.c \
diff --git a/epan/dissectors/packet-openflow.c b/epan/dissectors/packet-openflow.c
new file mode 100644
index 0000000..cc71824
--- /dev/null
+++ b/epan/dissectors/packet-openflow.c
@@ -0,0 +1,1430 @@
+/* packet-openflow.c
+ * Routines for OpenFlow dissection
+ * Copyright 2013, Anders Broman <anders.broman@ericsson.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Ref https://www.opennetworking.org/sdn-resources/onf-specifications/openflow
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+
+void proto_reg_handoff_openflow(void);
+
+static int g_openflow_port = 0;
+
+dissector_handle_t eth_withoutfcs_handle;
+
+/* Initialize the protocol and registered fields */
+static int proto_openflow = -1;
+static int hf_openflow_version = -1;
+static int hf_openflow_type = -1;
+static int hf_openflow_length = -1;
+static int hf_openflow_xid = -1;
+
+static int hf_openflow_datapath_id = -1;
+static int hf_openflow_datapath_mac = -1;
+static int hf_openflow_datapath_impl = -1;
+static int hf_openflow_n_buffers = -1;
+static int hf_openflow_n_tables = -1;
+static int hf_openflow_pad3 = -1;
+static int hf_openflow_capabilities = -1;
+static int hf_openflow_actions = -1;
+static int hf_openflow_reserved32 = -1;
+static int hf_openflow_cap_flow_stats = -1;
+static int hf_openflow_table_stats = -1;
+static int hf_openflow_port_stats = -1;
+static int hf_openflow_group_stats = -1;
+static int hf_openflow_ip_reasm = -1;
+static int hf_openflow_queue_stats = -1;
+static int hf_openflow_port_blocked = -1;
+
+static int hf_openflow_output = -1; /* Output to switch port. */
+static int hf_openflow_set_vlan_vid = -1; /* Set the 802.1q VLAN id. */
+static int hf_openflow_set_vlan_pcp = -1; /* Set the 802.1q priority. */
+static int hf_openflow_strip_vlan = -1; /* Strip the 802.1q header. */
+static int hf_openflow_set_dl_src = -1; /* Ethernet source address. */
+static int hf_openflow_set_dl_dst = -1; /* Ethernet destination address. */
+static int hf_openflow_set_nw_src = -1; /* IP source address. */
+static int hf_openflow_set_nw_dst = -1; /* IP destination address. */
+static int hf_openflow_set_nw_tos = -1; /* IP ToS (DSCP field, 6 bits). */
+static int hf_openflow_set_tp_src = -1; /* TCP/UDP source port. */
+static int hf_openflow_set_tp_dst = -1; /* TCP/UDP destination port. */
+static int hf_openflow_enqueue = -1; /* Output to queue. */
+
+static int hf_openflow_port_no = -1;
+static int hf_openflow_hw_addr = -1;
+static int hf_openflow_port_name = -1;
+
+
+static int hf_openflow_port_config = -1;
+static int hf_openflow_port_state = -1;
+static int hf_openflow_port_curr = -1;
+static int hf_openflow_port_advertised = -1;
+static int hf_openflow_port_supported = -1;
+static int hf_openflow_port_peer = -1;
+
+static int hf_openflow_port_down = -1; /* Port is administratively down. */
+static int hf_openflow_no_stp = -1; /* Disable 802.1D spanning tree on port. */
+static int hf_openflow_no_recv = -1; /* Drop all packets except 802.1D spanning tree packets. */
+static int hf_openflow_no_recv_stp = -1; /* Drop received 802.1D STP packets. */
+static int hf_openflow_no_flood = -1; /* Do not include this port when flooding. */
+static int hf_openflow_no_fwd = -1; /* Drop packets forwarded to port. */
+static int hf_openflow_no_packet_in = -1; /* Do not send packet-in msgs for port. */
+
+static int hf_openflow_link_down = -1; /* No physical link present. */
+
+static int hf_openflow_10mb_hd = -1; /* 10 Mb half-duplex rate support. */
+static int hf_openflow_10mb_fd = -1; /* 10 Mb full-duplex rate support. */
+static int hf_openflow_100mb_hd = -1; /* 100 Mb half-duplex rate support. */
+static int hf_openflow_100mb_fd = -1; /* 100 Mb full-duplex rate support. */
+static int hf_openflow_1gb_hd = -1; /* 1 Gb half-duplex rate support. */
+static int hf_openflow_1gb_fd = -1; /* 1 Gb full-duplex rate support. */
+static int hf_openflow_10gb_fd = -1; /* 10 Gb full-duplex rate support. */
+static int hf_openflow_copper = -1; /* Copper medium. */
+static int hf_openflow_fiber = -1; /* Fiber medium. */
+static int hf_openflow_autoneg = -1; /* Auto-negotiation. */
+static int hf_openflow_pause = -1; /* Pause. */
+static int hf_openflow_pause_asym = -1; /* Asymmetric pause. */
+
+static int hf_openflow_config_flags = -1;
+static int hf_openflow_miss_send_len = -1;
+
+static int hf_openflow_buffer_id = -1;
+static int hf_openflow_total_len = -1;
+static int hf_openflow_in_port = -1;
+static int hf_openflow_reason = -1;
+static int hf_openflow_table_id = -1;
+static int hf_openflow_cookie = -1;
+static int hf_openflow_cookie_mask = -1;
+static int hf_openflow_padd8 = -1;
+static int hf_openflow_padd16 = -1;
+static int hf_openflow_padd48 = -1;
+static int hf_openflow_actions_len = -1;
+static int hf_openflow_action_type = -1;
+static int hf_openflow_action_len = -1;
+static int hf_openflow_output_port = -1;
+static int hf_openflow_max_len = -1;
+static int hf_openflow_wildcards = -1;
+static int hf_openflow_command = -1;
+static int hf_openflow_eth_src = -1;
+static int hf_openflow_eth_dst = -1;
+static int hf_openflow_dl_vlan = -1;
+static int hf_openflow_dl_vlan_pcp = -1;
+static int hf_openflow_idle_timeout = -1;
+static int hf_openflow_hard_timeout = -1;
+static int hf_openflow_priority = -1;
+static int hf_openflow_out_port = -1;
+static int hf_openflow_out_group = -1;
+static int hf_openflow_flags = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_openflow = -1;
+static gint ett_openflow_path_id = -1;
+static gint ett_openflow_cap = -1;
+static gint ett_openflow_act = -1;
+static gint ett_openflow_port = -1;
+static gint ett_openflow_port_cnf = -1;
+static gint ett_openflow_port_state = -1;
+static gint ett_port_cf = -1;
+
+#define OFP_VERSION_1_0 1
+#define OFP_VERSION_1_1 2
+#define OFP_VERSION_1_2 3
+#define OFP_VERSION_1_3 4
+
+static const value_string openflow_version_values[] = {
+ { 0x01, "1.0" },
+ { 0x02, "1.1" },
+ { 0x03, "1.2" },
+ { 0x04, "1.3" },
+ { 0, NULL }
+};
+
+/* Immutable messages. */
+#define OFPT_HELLO 0 /* Symmetric message */
+#define OFPT_ERROR 1 /* Symmetric message */
+#define OFPT_ECHO_REQUEST 2 /* Symmetric message */
+#define OFPT_ECHO_REPLY 3 /* Symmetric message */
+#define OFPT_EXPERIMENTER 4 /* Symmetric message */
+/* Switch configuration messages. */
+#define OFPT_FEATURES_REQUEST 5 /* Controller/switch message */
+#define OFPT_FEATURES_REPLY 6 /* Controller/switch message */
+#define OFPT_GET_CONFIG_REQUEST 7 /* Controller/switch message */
+#define OFPT_GET_CONFIG_REPLY 8 /* Controller/switch message */
+#define OFPT_SET_CONFIG 9 /* Controller/switch message */
+/* Asynchronous messages. */
+#define OFPT_PACKET_IN 10 /* Async message */
+#define OFPT_FLOW_REMOVED 11 /* Async message */
+#define OFPT_PORT_STATUS 12 /* Async message */
+/* Controller command messages. */
+#define OFPT_PACKET_OUT 13 /* Controller/switch message */
+#define OFPT_FLOW_MOD 14 /* Controller/switch message */
+#define OFPT_GROUP_MOD 15 /* Controller/switch message */
+#define OFPT_PORT_MOD 16 /* Controller/switch message */
+#define OFPT_TABLE_MOD 17 /* Controller/switch message */
+/* Multipart messages. */
+#define OFPT_MULTIPART_REQUEST 18 /* Controller/switch message */
+#define OFPT_MULTIPART_REPLY 19 /* Controller/switch message */
+/* Barrier messages. */
+#define OFPT_BARRIER_REQUEST 20 /* Controller/switch message */
+#define OFPT_BARRIER_REPLY 21 /* Controller/switch message */
+/* Queue Configuration messages. */
+#define OFPT_QUEUE_GET_CONFIG_REQUEST 22 /* Controller/switch message */
+#define OFPT_QUEUE_GET_CONFIG_REPLY 23 /* Controller/switch message */
+/* Controller role change request messages. */
+#define OFPT_ROLE_REQUEST 24 /* Controller/switch message */
+#define OFPT_ROLE_REPLY 25 /* Controller/switch message */
+/* Asynchronous message configuration. */
+#define OFPT_GET_ASYNC_REQUEST 26 /* Controller/switch message */
+#define OFPT_GET_ASYNC_REPLY 27 /* Controller/switch message */
+#define OFPT_SET_ASYNC 28 /* Controller/switch message */
+/* Meters and rate limiters configuration messages. */
+#define OFPT_METER_MOD 29 /* Controller/switch message */
+
+static const value_string openflow_type_values[] = {
+/* Immutable messages. */
+ { 0, "OFPT_HELLO" }, /* Symmetric message */
+ { 1, "OFPT_ERROR" }, /* Symmetric message */
+ { 2, "OFPT_ECHO_REQUEST" }, /* Symmetric message */
+ { 3, "OFPT_ECHO_REPLY" }, /* Symmetric message */
+ { 4, "OFPT_EXPERIMENTER" }, /* Symmetric message */
+/* Switch configuration messages. */
+ { 5, "OFPT_FEATURES_REQUEST" }, /* Controller/switch message */
+ { 6, "OFPT_FEATURES_REPLY" }, /* Controller/switch message */
+ { 7, "OFPT_GET_CONFIG_REQUEST" }, /* Controller/switch message */
+ { 8, "OFPT_GET_CONFIG_REPLY" }, /* Controller/switch message */
+ { 9, "OFPT_SET_CONFIG" }, /* Controller/switch message */
+/* Asynchronous messages. */
+ { 10, "OFPT_PACKET_IN" }, /* Async message */
+ { 11, "OFPT_FLOW_REMOVED" }, /* Async message */
+ { 12, "OFPT_PORT_STATUS" }, /* Async message */
+/* Controller command messages. */
+ { 13, "OFPT_PACKET_OUT" }, /* Controller/switch message */
+ { 14, "OFPT_FLOW_MOD" }, /* Controller/switch message */
+ { 15, "OFPT_GROUP_MOD" }, /* Controller/switch message */
+ { 16, "OFPT_PORT_MOD" }, /* Controller/switch message */
+ { 17, "OFPT_TABLE_MOD" }, /* Controller/switch message */
+/* Multipart messages. */
+ { 18, "OFPT_MULTIPART_REQUEST" }, /* Controller/switch message */
+ { 19, "OFPT_MULTIPART_REPLY" }, /* Controller/switch message */
+/* Barrier messages. */
+ { 20, "OFPT_BARRIER_REQUEST" }, /* Controller/switch message */
+ { 21, "OFPT_BARRIER_REPLY" }, /* Controller/switch message */
+/* Queue Configuration messages. */
+ { 22, "OFPT_QUEUE_GET_CONFIG_REQUEST" }, /* Controller/switch message */
+ { 23, "OFPT_QUEUE_GET_CONFIG_REPLY" }, /* Controller/switch message */
+/* Controller role change request messages. */
+ { 24, "OFPT_ROLE_REQUEST" }, /* Controller/switch message */
+ { 25, "OFPT_ROLE_REPLY" }, /* Controller/switch message */
+/* Asynchronous message configuration. */
+ { 26, "OFPT_GET_ASYNC_REQUEST" }, /* Controller/switch message */
+ { 27, "OFPT_GET_ASYNC_REPLY" }, /* Controller/switch message */
+ { 28, "OFPT_SET_ASYNC" }, /* Controller/switch message */
+/* Meters and rate limiters configuration messages. */
+ { 29, "OFPT_METER_MOD" }, /* Controller/switch message */
+ { 0, NULL }
+};
+
+#define OFPC_FLOW_STATS 1<<0 /* Flow statistics. */
+#define OFPC_TABLE_STATS 1<<1 /* Table statistics. */
+#define OFPC_PORT_STATS 1<<2 /* Port statistics. */
+#define OFPC_GROUP_STATS 1<<3 /* Group statistics. */
+#define OFPC_IP_REASM 1<<5 /* Can reassemble IP fragments. */
+#define OFPC_QUEUE_STATS 1<<6 /* Queue statistics. */
+#define OFPC_PORT_BLOCKED 1<<8 /* Switch will block looping ports. */
+
+#define OFPAT_OUTPUT_MASK 1<<0 /* Output to switch port. */
+#define OFPAT_SET_VLAN_VID_MASK 1<<1 /* Set the 802.1q VLAN id. */
+#define OFPAT_SET_VLAN_PCP_MASK 1<<2 /* Set the 802.1q priority. */
+#define OFPAT_STRIP_VLAN_MASK 1<<3 /* Strip the 802.1q header. */
+#define OFPAT_SET_DL_SRC_MASK 1<<4 /* Ethernet source address. */
+#define OFPAT_SET_DL_DST_MASK 1<<5 /* Ethernet destination address. */
+#define OFPAT_SET_NW_SRC_MASK 1<<6 /* IP source address. */
+#define OFPAT_SET_NW_DST_MASK 1<<7 /* IP destination address. */
+#define OFPAT_SET_NW_TOS_MASK 1<<8 /* IP ToS (DSCP field, 6 bits). */
+#define OFPAT_SET_TP_SRC_MASK 1<<9 /* TCP/UDP source port. */
+#define OFPAT_SET_TP_DST_MASK 1<<10 /* TCP/UDP destination port. */
+#define OFPAT_ENQUEUE_MASK 1<<11 /* Output to queue. */
+
+#define OFPPC_PORT_DOWN 1<<0 /* Port is administratively down. */
+#define OFPPC_NO_STP 1<<1 /* Disable 802.1D spanning tree on port. */
+#define OFPPC_NO_RECV 1<<2 /* Drop all packets except 802.1D spanning tree packets. */
+#define OFPPC_NO_RECV_STP 1<<3 /* Drop received 802.1D STP packets. */
+#define OFPPC_NO_FLOOD 1<<4 /* Do not include this port when flooding. */
+#define OFPPC_NO_FWD 1<<5 /* Drop packets forwarded to port. */
+#define OFPPC_NO_PACKET_IN 1<<6 /* Do not send packet-in msgs for port. */
+
+#define OFP_MAX_PORT_NAME_LEN 16
+
+#define OFPPS_LINK_DOWN 1<<0 /* No physical link present. */
+#define OFPPS_STP_LISTEN 0<<8 /* Not learning or relaying frames. */
+#define OFPPS_STP_LEARN 1<<8 /* Learning but not relaying frames. */
+#define OFPPS_STP_FORWARD 2<<8 /* Learning and relaying frames. */
+#define OFPPS_STP_BLOCK 3<<8 /* Not part of spanning tree. */
+#define OFPPS_STP_MASK 3<<8 /* Bit mask for OFPPS_STP_* values. */
+
+
+#define OFPPF_10MB_HD 1<<0 /* 10 Mb half-duplex rate support. */
+#define OFPPF_10MB_FD 1<<1 /* 10 Mb full-duplex rate support. */
+#define OFPPF_100MB_HD 1<<2 /* 100 Mb half-duplex rate support. */
+#define OFPPF_100MB_FD 1<<3 /* 100 Mb full-duplex rate support. */
+#define OFPPF_1GB_HD 1<<4 /* 1 Gb half-duplex rate support. */
+#define OFPPF_1GB_FD 1<<5 /* 1 Gb full-duplex rate support. */
+#define OFPPF_10GB_FD 1<<6 /* 10 Gb full-duplex rate support. */
+#define OFPPF_COPPER 1<<7 /* Copper medium. */
+#define OFPPF_FIBER 1<<8 /* Fiber medium. */
+#define OFPPF_AUTONEG 1<<9 /* Auto-negotiation. */
+#define OFPPF_PAUSE 1<<10 /* Pause. */
+#define OFPPF_PAUSE_ASYM 1<<11 /* Asymmetric pause. */
+
+
+#define OFPAT_OUTPUT 0 /* Output to switch port. */
+#define OFPAT_SET_VLAN_VID 1 /* Set the 802.1q VLAN id. */
+#define OFPAT_SET_VLAN_PCP 2 /* Set the 802.1q priority. */
+#define OFPAT_STRIP_VLAN 3 /* Strip the 802.1q header. */
+#define OFPAT_SET_DL_SRC 4 /* Ethernet source address. */
+#define OFPAT_SET_DL_DST 5 /* Ethernet destination address. */
+#define OFPAT_SET_NW_SRC 6 /* IP source address. */
+#define OFPAT_SET_NW_DST 7 /* IP destination address. */
+#define OFPAT_SET_TP_SRC 8 /* TCP/UDP source port. */
+#define OFPAT_SET_TP_DST 9 /* TCP/UDP destination port. */
+#define OFPAT_VENDOR 0xffff
+
+static int
+dissect_openflow_ofp_match_v_1_0(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
+{
+
+ /* uint32_t wildcards; Wildcard fields. */
+ proto_tree_add_item(tree, hf_openflow_wildcards, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ /* uint16_t in_port; Input switch port. */
+ proto_tree_add_item(tree, hf_openflow_in_port, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ /* uint8_t dl_src[OFP_ETH_ALEN]; Ethernet source address. */
+ proto_tree_add_item(tree, hf_openflow_eth_src, tvb, offset, 6, ENC_NA);
+ offset+=6;
+ /* uint8_t dl_dst[OFP_ETH_ALEN]; Ethernet destination address. */
+ proto_tree_add_item(tree, hf_openflow_eth_dst, tvb, offset, 6, ENC_NA);
+ offset+=6;
+ /* uint16_t dl_vlan; Input VLAN id. */
+ proto_tree_add_item(tree, hf_openflow_dl_vlan, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* uint8_t dl_vlan_pcp; Input VLAN priority. */
+ proto_tree_add_item(tree, hf_openflow_dl_vlan_pcp, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ /* uint8_t pad1[1]; Align to 64-bits */
+ proto_tree_add_item(tree, hf_openflow_padd8, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ /* uint16_t dl_type; Ethernet frame type. */
+ /* uint8_t nw_tos; IP ToS (actually DSCP field, 6 bits). */
+ /* uint8_t nw_proto; IP protocol or lower 8 bits of
+ * ARP opcode.
+ */
+ /* uint8_t pad2[2]; Align to 64-bits */
+ /* uint32_t nw_src; IP source address. */
+ /* uint32_t nw_dst; IP destination address. */
+ /* uint16_t tp_src; TCP/UDP source port. */
+ /* uint16_t tp_dst; TCP/UDP destination port. */
+ proto_tree_add_text(tree, tvb, offset, 18, "Data not dissected yet");
+ offset +=18;
+
+ return offset;
+}
+
+
+static const value_string openflow_action_values[] = {
+ { OFPAT_OUTPUT, "Output to switch port" },
+ { OFPAT_SET_VLAN_VID, "Set the 802.1q VLAN id" },
+ { OFPAT_SET_VLAN_PCP, "Set the 802.1q priority" },
+ { OFPAT_STRIP_VLAN, "Strip the 802.1q header" },
+ { OFPAT_SET_DL_SRC, "Ethernet source address" },
+ { OFPAT_SET_DL_DST, "Ethernet destination address" },
+ { OFPAT_SET_NW_SRC, "IP source address" },
+ { OFPAT_SET_NW_DST, "IP destination address" },
+ { OFPAT_SET_TP_SRC, "TCP/UDP source port" },
+ { OFPAT_SET_TP_DST, "TCP/UDP destination port" },
+ { OFPAT_VENDOR, "Vendor specific action"},
+ { 0, NULL }
+};
+
+static int
+dissect_openflow_action_header(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
+{
+ guint16 action_type, action_len;
+
+ /* uint16_t type; One of OFPAT_*. */
+ action_type = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(tree, hf_openflow_action_type, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* Length of action, including this
+ * header. This is the length of action,
+ * including any padding to make it
+ * 64-bit aligned.
+ */
+ action_len = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(tree, hf_openflow_action_len, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ switch(action_type){
+ case OFPAT_OUTPUT:
+ /* uint16_t port; Output port. */
+ proto_tree_add_item(tree, hf_openflow_output_port, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* uint16_t max_len; Max length to send to controller. */
+ proto_tree_add_item(tree, hf_openflow_max_len, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ break;
+ default:
+ proto_tree_add_text(tree, tvb, offset, action_len-4, "Action not dissected yet");
+ offset+=(action_len-4);
+ break;
+ }
+
+ return offset;
+}
+static void
+dissect_openflow_phy_port(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
+{
+ proto_item *ti;
+ proto_tree *port_cnf_tree, *port_state_tree, *port_cf_tree;
+
+ proto_tree_add_item(tree, hf_openflow_port_no, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ proto_tree_add_item(tree, hf_openflow_hw_addr, tvb, offset, 6, ENC_NA);
+ offset+=6;
+ proto_tree_add_item(tree, hf_openflow_port_name, tvb, offset, OFP_MAX_PORT_NAME_LEN, ENC_ASCII|ENC_NA);
+ offset+=OFP_MAX_PORT_NAME_LEN;
+
+ /* Bitmap of OFPPC_* flags. */
+ ti = proto_tree_add_item(tree, hf_openflow_port_config, tvb, offset, 4, ENC_BIG_ENDIAN);
+ port_cnf_tree = proto_item_add_subtree(ti, ett_openflow_port_cnf);
+
+ /* Port is administratively down. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_port_down, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Disable 802.1D spanning tree on port. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_stp, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Drop all packets except 802.1D spanning tree packets. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_recv, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Drop received 802.1D STP packets. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_recv_stp, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Do not include this port when flooding. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_flood, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Drop packets forwarded to port. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_fwd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Do not send packet-in msgs for port. */
+ proto_tree_add_item(port_cnf_tree, hf_openflow_no_packet_in, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ /* Bitmap of OFPPS_* flags. */
+ ti = proto_tree_add_item(tree, hf_openflow_port_state, tvb, offset, 4, ENC_BIG_ENDIAN);
+ port_state_tree = proto_item_add_subtree(ti, ett_openflow_port_state);
+
+ /* No physical link present. */
+ proto_tree_add_item(port_state_tree, hf_openflow_link_down, tvb, offset, 4, ENC_BIG_ENDIAN);
+
+ offset+=4;
+
+ /* Current features. */
+ ti = proto_tree_add_item(tree, hf_openflow_port_curr, tvb, offset, 4, ENC_BIG_ENDIAN);
+ port_cf_tree = proto_item_add_subtree(ti, ett_port_cf);
+ /* 10 Mb half-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_10mb_hd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 10 Mb full-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_10mb_fd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 100 Mb half-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_100mb_hd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 100 Mb full-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_100mb_fd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 1 Gb half-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_1gb_hd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 1 Gb full-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_1gb_fd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* 10 Gb full-duplex rate support. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_10gb_fd, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Copper medium. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_copper, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Fiber medium. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_fiber, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Auto-negotiation. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_autoneg, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Pause. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_pause, tvb, offset, 4, ENC_BIG_ENDIAN);
+ /* Asymmetric pause. */
+ proto_tree_add_item(port_cf_tree, hf_openflow_pause_asym, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ /* Features being advertised by the port. */
+ proto_tree_add_item(tree, hf_openflow_port_advertised, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ /* Features supported by the port. */
+ proto_tree_add_item(tree, hf_openflow_port_supported, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ /* Features advertised by peer. */
+ proto_tree_add_item(tree, hf_openflow_port_peer, tvb, offset, 4, ENC_BIG_ENDIAN);
+
+
+}
+/*
+ * Switch features.
+ *
+ struct ofp_switch_features {
+ struct ofp_header header;
+ uint64_t datapath_id; * Datapath unique ID. The lower 48-bits are for
+ a MAC address, while the upper 16-bits are
+ implementer-defined. *
+ uint32_t n_buffers; * Max packets buffered at once. *
+ uint8_t n_tables; * Number of tables supported by datapath. *
+ uint8_t pad[3]; * Align to 64-bits. *
+* Features. *
+ uint32_t capabilities; * Bitmap of support "ofp_capabilities". *
+ uptill 1.1
+ uint32_t actions; * Bitmap of supported "ofp_action_type"s. *
+ from 1.2
+ uint32_t reserved;
+* Port info.*
+ struct ofp_phy_port ports[0]; * Port definitions. The number of ports
+ is inferred from the length field in
+ the header.
+ };
+*/
+static void
+dissect_openflow_features_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 version, guint16 length)
+{
+ proto_item *ti;
+ proto_tree *path_id_tree, *cap_tree, *act_tree;
+
+ guint16 length_remaining;
+
+ ti = proto_tree_add_item(tree, hf_openflow_datapath_id, tvb, offset, 8, ENC_BIG_ENDIAN);
+ path_id_tree = proto_item_add_subtree(ti, ett_openflow_path_id);
+ proto_tree_add_item(path_id_tree, hf_openflow_datapath_mac, tvb, offset, 6, ENC_NA);
+ offset+=6;
+ proto_tree_add_item(path_id_tree, hf_openflow_datapath_impl, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ proto_tree_add_item(tree, hf_openflow_n_buffers, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ proto_tree_add_item(tree, hf_openflow_n_tables, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+
+ if(version < OFP_VERSION_1_2){
+ proto_tree_add_item(tree, hf_openflow_pad3, tvb, offset, 3, ENC_BIG_ENDIAN);
+ offset+=3;
+ }else{
+ }
+
+ ti = proto_tree_add_item(tree, hf_openflow_capabilities, tvb, offset, 4, ENC_BIG_ENDIAN);
+ cap_tree = proto_item_add_subtree(ti, ett_openflow_cap);
+
+ /* Dissect flags */
+ proto_tree_add_item(cap_tree, hf_openflow_cap_flow_stats, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_table_stats, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_port_stats, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_group_stats, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_ip_reasm, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_queue_stats, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_openflow_port_blocked, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ if(version < OFP_VERSION_1_1){
+ ti = proto_tree_add_item(tree, hf_openflow_actions, tvb, offset, 4, ENC_BIG_ENDIAN);
+ act_tree = proto_item_add_subtree(ti, ett_openflow_act);
+ /* Dissect flags */
+ proto_tree_add_item(act_tree, hf_openflow_output, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_vlan_vid, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_vlan_pcp, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_strip_vlan, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_dl_src, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_dl_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_nw_src, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_nw_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_nw_tos, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_tp_src, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_set_tp_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
+ proto_tree_add_item(act_tree, hf_openflow_enqueue, tvb, offset, 4, ENC_BIG_ENDIAN);
+ }else{
+ proto_tree_add_item(tree, hf_openflow_reserved32, tvb, offset, 4, ENC_BIG_ENDIAN);
+ }
+ offset+=4;
+
+ length_remaining = length-32;
+ if(length_remaining > 0){
+ guint16 num_ports = length_remaining/48;
+ int i;
+ if ((length_remaining&0x003f) != 0){
+ /* protocol_error */
+ }
+ for(i=0; i<num_ports ;i++){
+ proto_tree *port_tree;
+
+ ti = proto_tree_add_text(tree, tvb, offset, 48, "Port data %u",i+1);
+ port_tree = proto_item_add_subtree(ti, ett_openflow_port);
+ dissect_openflow_phy_port(tvb, pinfo, port_tree, offset);
+ offset+=48;
+ }
+ }
+
+}
+
+static void
+dissect_openflow_switch_config(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, guint8 version _U_, guint16 length _U_)
+{
+
+ /* ofp_config_flags */
+ proto_tree_add_item(tree, hf_openflow_config_flags, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* miss_send_len */
+ proto_tree_add_item(tree, hf_openflow_miss_send_len, tvb, offset, 2, ENC_BIG_ENDIAN);
+ /*offset+=2;*/
+
+}
+
+#define OFPR_NO_MATCH 0 /* No matching flow (table-miss flow entry). */
+#define OFPR_ACTION 1 /* Action explicitly output to controller. */
+#define OFPR_INVALID_TTL 2 /* Packet has invalid TTL */
+
+static const value_string openflow_reason_values[] = {
+ { OFPR_NO_MATCH, "No matching flow (table-miss flow entry)" },
+ { OFPR_ACTION, "Action explicitly output to controller" },
+ { OFPR_INVALID_TTL, "Packet has invalid TTL" },
+ { 0, NULL }
+};
+
+static void
+dissect_openflow_pkt_in(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, guint8 version, guint16 length)
+{
+ tvbuff_t *next_tvb;
+
+ /* uint32_t buffer_id; ID assigned by datapath. */
+ proto_tree_add_item(tree, hf_openflow_buffer_id, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ /* uint16_t total_len; Full length of frame. */
+ proto_tree_add_item(tree, hf_openflow_total_len, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ if(version == OFP_VERSION_1_0){
+ /* uint16_t in_port; Port on which frame was received. */
+ proto_tree_add_item(tree, hf_openflow_in_port, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ }
+ /* uint8_t reason; Reason packet is being sent (one of OFPR_*) */
+ proto_tree_add_item(tree, hf_openflow_reason, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ if(version > OFP_VERSION_1_2){
+ /* uint8_t table_id; ID of the table that was looked up */
+ proto_tree_add_item(tree, hf_openflow_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ /* uint64_t cookie; Cookie of the flow entry that was looked up. */
+ proto_tree_add_item(tree, hf_openflow_cookie, tvb, offset, 8, ENC_BIG_ENDIAN);
+ offset+=8;
+ /* Followed by:
+ * - Exactly 2 all-zero padding bytes, then
+ * - An Ethernet frame whose length is inferred from header.length.
+ * The padding bytes preceding the Ethernet frame ensure that the IP
+ * header (if any) following the Ethernet header is 32-bit aligned.
+ */
+ proto_tree_add_item(tree, hf_openflow_padd16, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ }else{
+ proto_tree_add_item(tree, hf_openflow_padd8, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset+=1;
+ }
+
+ /*proto_tree_add_text(tree, tvb, offset, length-offset, "Offset=%u, remaining %u", offset, length-offset);*/
+ next_tvb = tvb_new_subset(tvb, offset, length-offset, length-offset);
+ call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree);
+
+}
+
+static void
+dissect_openflow_pkt_out(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, guint8 version, guint16 length _U_)
+{
+ tvbuff_t *next_tvb;
+ gint32 buffer_id;
+
+ /* uint32_t buffer_id; ID assigned by datapath. */
+ buffer_id = tvb_get_ntohl(tvb, offset);
+ proto_tree_add_item(tree, hf_openflow_buffer_id, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ /* uint32_t in_port; Packet's input port or OFPP_CONTROLLER. */
+ proto_tree_add_item(tree, hf_openflow_in_port, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ /* uint16_t actions_len; Size of action array in bytes. */
+ proto_tree_add_item(tree, hf_openflow_actions_len, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ if(version > OFP_VERSION_1_2){
+ /* uint8_t pad[6]; */
+ proto_tree_add_item(tree, hf_openflow_padd48, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=6;
+ }
+ /* struct ofp_action_header actions[0]; Action list. */
+ offset = dissect_openflow_action_header(tvb, pinfo, tree, offset);
+ /* Packet data. The length is inferred
+ from the length field in the header.
+ (Only meaningful if buffer_id == -1.)
+ */
+ if(buffer_id == -1){
+ /* proto_tree_add_text(tree, tvb, offset, -1, "Packet data"); */
+ next_tvb = tvb_new_subset(tvb, offset, length-offset, length-offset);
+ call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree);
+ }
+}
+
+#define OFPFC_ADD 0 /* New flow. */
+#define OFPFC_MODIFY 1 /* Modify all matching flows. */
+#define OFPFC_MODIFY_STRICT 2 /* Modify entry strictly matching wildcards */
+#define OFPFC_DELETE 3 /* Delete all matching flows. */
+#define OFPFC_DELETE_STRICT 4 /* Strictly match wildcards and priority. */
+
+static const value_string openflow_command_values[] = {
+ { OFPFC_ADD, "New flow" },
+ { OFPFC_MODIFY, "Modify all matching flows" },
+ { OFPFC_MODIFY_STRICT, "Modify entry strictly matching wildcards" },
+ { OFPFC_DELETE, "Delete all matching flows" },
+ { OFPFC_DELETE_STRICT, "Strictly match wildcards and priority" },
+ { 0, NULL }
+};
+
+static void
+dissect_openflow_flow_mod(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 version, guint16 length _U_)
+{
+
+ /* struct ofp_match match; Fields to match */
+ if(version < OFP_VERSION_1_2){
+ offset = dissect_openflow_ofp_match_v_1_0(tvb, pinfo, tree, offset);
+ }
+
+ /* uint64_t cookie; Opaque controller-issued identifier. */
+ proto_tree_add_item(tree, hf_openflow_cookie, tvb, offset, 8, ENC_BIG_ENDIAN);
+ offset+=8;
+ if(version > OFP_VERSION_1_2){
+ /* uint64_t cookie_mask; Mask used to restrict the cookie bits
+ * that must match when the command is
+ * OFPFC_MODIFY* or OFPFC_DELETE*. A value
+ * of 0 indicates no restriction.
+ */
+ proto_tree_add_item(tree, hf_openflow_cookie_mask, tvb, offset, 8, ENC_BIG_ENDIAN);
+ offset+=8;
+ /* Flow actions. */
+ /* uint8_t table_id; ID of the table to put the flow in.
+ For OFPFC_DELETE_* commands, OFPTT_ALL can also be used to delete matching
+ flows from all tables.
+ */
+ proto_tree_add_item(tree, hf_openflow_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ }
+ if(version > OFP_VERSION_1_2){
+ /* uint8_t command; One of OFPFC_*. */
+ proto_tree_add_item(tree, hf_openflow_command, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+ }else{
+ /* uint16_t command; One of OFPFC_*. */
+ proto_tree_add_item(tree, hf_openflow_command, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ }
+ /* uint16_t idle_timeout; Idle time before discarding (seconds). */
+ proto_tree_add_item(tree, hf_openflow_idle_timeout, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* uint16_t hard_timeout; Max time before discarding (seconds). */
+ proto_tree_add_item(tree, hf_openflow_hard_timeout, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* uint16_t priority; Priority level of flow entry. */
+ proto_tree_add_item(tree, hf_openflow_priority, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ /* uint32_t buffer_id; Buffered packet to apply to, or OFP_NO_BUFFER.
+ Not meaningful for OFPFC_DELETE*.
+ */
+ proto_tree_add_item(tree, hf_openflow_buffer_id, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ /* uint32_t out_port; For OFPFC_DELETE* commands, require
+ matching entries to include this as an output port. A value of OFPP_ANY
+ indicates no restriction.
+ */
+ if(version > OFP_VERSION_1_2){
+ proto_tree_add_item(tree, hf_openflow_out_port, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ }else{
+ proto_tree_add_item(tree, hf_openflow_out_port, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ }
+
+ if(version > OFP_VERSION_1_2){
+ /* uint32_t out_group; For OFPFC_DELETE* commands, require
+ matching entries to include this as an
+ output group. A value of OFPG_ANY
+ indicates no restriction.
+ */
+ proto_tree_add_item(tree, hf_openflow_out_group, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+ }
+ /* uint16_t flags; One of OFPFF_*. */
+ proto_tree_add_item(tree, hf_openflow_flags, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+ if(version > OFP_VERSION_1_2){
+ /* uint8_t pad[2]; */
+ proto_tree_add_item(tree, hf_openflow_padd16, tvb, offset, 2, ENC_BIG_ENDIAN);
+ }
+
+#if 0
+ offset+=2;
+ if(version < OFP_VERSION_1_2){
+ /* The action length is inferred
+ from the length field in the
+ header. */
+ /*struct ofp_action_header actions[0]; */
+ }else{
+ /* struct ofp_match match; Fields to match. Variable size. */
+ }
+#endif
+}
+/* Code to actually dissect the packets */
+static int
+dissect_openflow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ proto_item *ti;
+ proto_tree *openflow_tree;
+ guint offset = 0;
+ guint8 type, version;
+ guint16 length;
+
+
+ version = tvb_get_guint8(tvb, 0);
+ type = tvb_get_guint8(tvb, 1);
+ /* Set the Protocol column to the constant string of openflow */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpenFlow");
+ col_clear(pinfo->cinfo,COL_INFO);
+
+ if((version&0x80)==0x80){
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpenFlow experimental version");
+ proto_tree_add_text(tree, tvb, offset, -1, "Experimental versions not dissected");
+ }else{
+ version = version & 0x7f;
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpenFlow");
+ col_append_fstr(pinfo->cinfo, COL_INFO, "Type: %s",
+ val_to_str_const(type, openflow_type_values, "Unknown Messagetype"));
+ }
+ /* Stop the Ethernet frame from overwriting the columns */
+ if((type == OFPT_PACKET_IN) || (type == OFPT_PACKET_OUT)){
+ col_set_writable(pinfo->cinfo, FALSE);
+ }
+
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_openflow, tvb, 0, -1, ENC_NA);
+ openflow_tree = proto_item_add_subtree(ti, ett_openflow);
+
+ /* A.1 OpenFlow Header. */
+ /* OFP_VERSION. */
+ proto_tree_add_item(openflow_tree, hf_openflow_version, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+
+ /* One of the OFPT_ constants. */
+ proto_tree_add_item(openflow_tree, hf_openflow_type, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset++;
+
+ /* Length including this ofp_header. */
+ length = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(openflow_tree, hf_openflow_length, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset+=2;
+
+ /* Transaction id associated with this packet. Replies use the same id as was in the request
+ * to facilitate pairing.
+ */
+ proto_tree_add_item(openflow_tree, hf_openflow_xid, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset+=4;
+
+ switch(type){
+ case OFPT_HELLO: /* 0 */
+ /* 5.5.1 Hello
+ * The OFPT_HELLO message has no body;
+ */
+ break;
+ case OFPT_FEATURES_REQUEST: /* 5 */
+ /* 5.3.1 Handshake
+ * Upon TLS session establishment, the controller sends an OFPT_FEATURES_REQUEST
+ * message. This message does not contain a body beyond the OpenFlow header.
+ */
+ break;
+ case OFPT_FEATURES_REPLY: /* 6 */
+ dissect_openflow_features_reply(tvb, pinfo, openflow_tree, offset, version, length);
+ break;
+ case OFPT_GET_CONFIG_REQUEST: /* 7 */
+ /* A.3.2 There is no body for OFPT_GET_CONFIG_REQUEST beyond the OpenFlow header. */
+ break;
+ case OFPT_GET_CONFIG_REPLY: /* 8 */
+ /* Fall trough */
+ case OFPT_SET_CONFIG: /* 9 */
+ dissect_openflow_switch_config(tvb, pinfo, openflow_tree, offset, version, length);
+ break;
+ case OFPT_PACKET_IN: /* 10 */
+ dissect_openflow_pkt_in(tvb, pinfo, openflow_tree, offset, version, length);
+ break;
+ case OFPT_PACKET_OUT: /* 13 */
+ dissect_openflow_pkt_out(tvb, pinfo, openflow_tree, offset, version, length);
+ break;
+ case OFPT_FLOW_MOD: /* 14 */
+ dissect_openflow_flow_mod(tvb, pinfo, openflow_tree, offset, version, length);
+ break;
+ default:
+ if(length>8){
+ proto_tree_add_text(tree, tvb, offset, -1, "Message data not dissected yet");
+ }
+ break;
+ }
+
+ return tvb_length(tvb);
+}
+
+/* Register the protocol with Wireshark.
+ *
+ * This format is require because a script is used to build the C function that
+ * calls all the protocol registration.
+ */
+void
+proto_register_openflow(void)
+{
+ module_t *openflow_module;
+
+ static hf_register_info hf[] = {
+ { &hf_openflow_version,
+ { "Version", "openflow.version",
+ FT_UINT8, BASE_HEX, VALS(openflow_version_values), 0x7f,
+ NULL, HFILL }
+ },
+ { &hf_openflow_type,
+ { "Type", "openflow.type",
+ FT_UINT8, BASE_DEC, VALS(openflow_type_values), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_xid,
+ { "Transaction ID", "openflow.xid",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_length,
+ { "Length", "openflow.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_datapath_id,
+ { "Datapath unique ID", "openflow.datapath_id",
+ FT_UINT64, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_datapath_mac,
+ { "MAC addr", "openflow.datapath_mac",
+ FT_ETHER, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_datapath_impl,
+ { "Implementers part", "openflow.datapath_imp",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_n_buffers,
+ { "n_buffers", "openflow.n_buffers",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_n_tables,
+ { "n_tables", "openflow.n_tables",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_pad3,
+ { "Padding", "openflow.pad3",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_capabilities,
+ { "capabilities", "openflow.capabilities",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_actions,
+ { "actions", "openflow.actions",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_reserved32,
+ { "Reserved", "openflow.reserved32",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_cap_flow_stats,
+ { "Flow statistics", "openflow.flow_stats",
+ FT_BOOLEAN, 32, NULL, OFPC_FLOW_STATS,
+ NULL, HFILL }
+ },
+ { &hf_openflow_table_stats,
+ { "Table statistics", "openflow.table_stats",
+ FT_BOOLEAN, 32, NULL, OFPC_TABLE_STATS,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_stats,
+ { "Port statistics", "openflow.port_stats",
+ FT_BOOLEAN, 32, NULL, OFPC_PORT_STATS,
+ NULL, HFILL }
+ },
+ { &hf_openflow_group_stats,
+ { "Group statistics", "openflow.group_stats",
+ FT_BOOLEAN, 32, NULL, OFPC_GROUP_STATS,
+ NULL, HFILL }
+ },
+ { &hf_openflow_ip_reasm,
+ { "Can reassemble IP fragments", "openflow.ip_reasm",
+ FT_BOOLEAN, 32, NULL, OFPC_IP_REASM,
+ NULL, HFILL }
+ },
+ { &hf_openflow_queue_stats,
+ { "Queue statistics", "openflow.queue_stats",
+ FT_BOOLEAN, 32, NULL, OFPC_QUEUE_STATS,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_blocked,
+ { "Switch will block looping ports", "openflow.port_blocked",
+ FT_BOOLEAN, 32, NULL, OFPC_PORT_BLOCKED,
+ NULL, HFILL }
+ },
+ { &hf_openflow_output,
+ { "Output to switch port", "openflow.output",
+ FT_BOOLEAN, 32, NULL, OFPAT_OUTPUT_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_vlan_vid,
+ { "Set the 802.1q VLAN id", "openflow.set_vlan_vid",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_VLAN_VID_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_vlan_pcp,
+ { "Set the 802.1q priority", "openflow.set_vlan_pcp",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_VLAN_PCP_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_strip_vlan,
+ { "Strip the 802.1q header", "openflow.strip_vlan",
+ FT_BOOLEAN, 32, NULL, OFPAT_STRIP_VLAN_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_dl_src,
+ { "Ethernet source address", "openflow.set_dl_src",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_DL_SRC_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_dl_dst,
+ { "Ethernet destination address", "openflow.set_dl_ds",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_DL_DST_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_nw_src,
+ { "IP source address", "openflow.set_nw_src",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_NW_SRC_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_nw_dst,
+ { "IP destination address", "openflow.set_nw_ds",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_NW_DST_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_nw_tos,
+ { "IP ToS (DSCP field, 6 bits)", "openflow.set_nw_tos",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_NW_TOS_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_tp_src,
+ { "TCP/UDP source port", "openflow.set_tp_src",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_TP_SRC_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_set_tp_dst,
+ { "TCP/UDP destination port", "openflow.set_tp_dst",
+ FT_BOOLEAN, 32, NULL, OFPAT_SET_TP_DST_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_enqueue,
+ { "Output to queue", "openflow.enqueue",
+ FT_BOOLEAN, 32, NULL, OFPAT_ENQUEUE_MASK,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_no,
+ { "Port number", "openflow.port_no",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_hw_addr,
+ { "HW Address", "openflow.hw_add",
+ FT_ETHER, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_name,
+ { "Name", "openflow.hw_add",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_config,
+ { "Config flags", "openflow.port_config",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_state,
+ { "State flags", "openflow.port_state",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_curr,
+ { "Current features", "openflow.port_curr",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_advertised,
+ { "Advertised features", "openflow.port_advertised",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_supported,
+ { "Features supported", "openflow.port_supported",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_peer,
+ { "Features advertised by peer", "openflow.port_peer",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_port_down,
+ { "Port is administratively down", "openflow.port_down",
+ FT_BOOLEAN, 32, NULL, OFPPC_PORT_DOWN,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_stp,
+ { "Disable 802.1D spanning tree on port", "openflow.no_stp",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_STP,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_recv,
+ { "Drop all packets except 802.1D spanning tree packets", "openflow.no_recv",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_RECV,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_recv_stp,
+ { "Drop received 802.1D STP packets", "openflow.no_recv",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_RECV_STP,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_flood,
+ { "Do not include this port when flooding", "openflow.no_flood",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_FLOOD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_fwd,
+ { "Drop packets forwarded to port", "openflow.no_fwd",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_FWD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_no_packet_in,
+ { "Do not send packet-in msgs for port", "openflow.no_packet_in",
+ FT_BOOLEAN, 32, NULL, OFPPC_NO_PACKET_IN,
+ NULL, HFILL }
+ },
+ { &hf_openflow_link_down,
+ { "No physical link present", "openflow.link_down",
+ FT_BOOLEAN, 32, NULL, OFPPS_LINK_DOWN,
+ NULL, HFILL }
+ },
+ { &hf_openflow_10mb_hd,
+ { "10 Mb half-duplex rate support", "openflow.10mb_hd",
+ FT_BOOLEAN, 32, NULL, OFPPF_10MB_HD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_10mb_fd,
+ { "10 Mb full-duplex rate support", "openflow.10mb_fd",
+ FT_BOOLEAN, 32, NULL, OFPPF_10MB_FD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_100mb_hd,
+ { "100 Mb half-duplex rate support", "openflow.100mb_hd",
+ FT_BOOLEAN, 32, NULL, OFPPF_100MB_HD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_100mb_fd,
+ { "100 Mb full-duplex rate support", "openflow.100mb_0fd",
+ FT_BOOLEAN, 32, NULL, OFPPF_100MB_FD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_1gb_hd,
+ { "1 Gb half-duplex rate support", "openflow.1gb_hd",
+ FT_BOOLEAN, 32, NULL, OFPPF_1GB_HD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_1gb_fd,
+ { "1 Gb full-duplex rate support", "openflow.1gb_fd",
+ FT_BOOLEAN, 32, NULL, OFPPF_1GB_FD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_10gb_fd,
+ { "10 Gb full-duplex rate support", "openflow.10gb_fd",
+ FT_BOOLEAN, 32, NULL, OFPPF_10GB_FD,
+ NULL, HFILL }
+ },
+ { &hf_openflow_copper,
+ { "Copper medium", "openflow.copper",
+ FT_BOOLEAN, 32, NULL, OFPPF_COPPER,
+ NULL, HFILL }
+ },
+ { &hf_openflow_fiber,
+ { "Fiber medium", "openflow.fiber",
+ FT_BOOLEAN, 32, NULL, OFPPF_FIBER,
+ NULL, HFILL }
+ },
+ { &hf_openflow_autoneg,
+ { "Auto-negotiation", "openflow.autoneg",
+ FT_BOOLEAN, 32, NULL, OFPPF_AUTONEG,
+ NULL, HFILL }
+ },
+ { &hf_openflow_pause,
+ { "Pause", "openflow.pause",
+ FT_BOOLEAN, 32, NULL, OFPPF_PAUSE,
+ NULL, HFILL }
+ },
+ { &hf_openflow_pause_asym,
+ { "Asymmetric pause", "openflow.pause_asym",
+ FT_BOOLEAN, 32, NULL, OFPPF_PAUSE_ASYM,
+ NULL, HFILL }
+ },
+ { &hf_openflow_config_flags,
+ { "Config flags", "openflow.config_flags",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_miss_send_len,
+ { "Max bytes of packet", "openflow.miss_send_len",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_buffer_id,
+ { "Buffser Id", "openflow.buffer_id",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_total_len,
+ { "Total length", "openflow.total_len",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_in_port,
+ { "In port", "openflow.in_port",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_reason,
+ { "Reason", "openflow.reason",
+ FT_UINT8, BASE_DEC, VALS(openflow_reason_values), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_table_id,
+ { "Table Id", "openflow.table_id",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_cookie,
+ { "Cookie", "openflow.cookie",
+ FT_UINT64, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_cookie_mask,
+ { "Cookie mask", "openflow.cookie",
+ FT_UINT64, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_padd8,
+ { "Padding", "openflow.padding8",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_padd16,
+ { "Padding", "openflow.padding16",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_padd48,
+ { "Padding", "openflow.padding48",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_actions_len,
+ { "Actions length", "openflow.actions_len",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_action_type,
+ { "Actions type", "openflow.action_typ",
+ FT_UINT16, BASE_DEC, VALS(openflow_action_values), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_action_len,
+ { "Action length", "openflow.action_len",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_output_port,
+ { "Output port", "openflow.output_port",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_max_len,
+ { "Max length", "openflow.max_len",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_wildcards,
+ { "Wildcards", "openflow.wildcards",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_command,
+ { "Command", "openflow.command",
+ FT_UINT16, BASE_DEC, VALS(openflow_command_values), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_eth_src,
+ { "Ethernet source address", "openflow.eth_src",
+ FT_ETHER, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_eth_dst,
+ { "Ethernet destination address", "openflow.eth_src",
+ FT_ETHER, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_dl_vlan,
+ { "Input VLAN id", "openflow.dl_vlan",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_dl_vlan_pcp,
+ { "Input VLAN priority", "openflow.dl_vlan_pcp",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_idle_timeout,
+ { "Idle time-out", "openflow.idle_timeout",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_hard_timeout,
+ { "hard time-out", "openflow.hard_timeout",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_priority,
+ { "Priority", "openflow.priority",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_out_port,
+ { "Out port", "openflow.out_port",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_out_group,
+ { "Out group", "openflow.out_group",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_openflow_flags,
+ { "Flags", "openflow.flags",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ };
+
+ static gint *ett[] = {
+ &ett_openflow,
+ &ett_openflow_path_id,
+ &ett_openflow_cap,
+ &ett_openflow_act,
+ &ett_openflow_port,
+ &ett_openflow_port_cnf,
+ &ett_openflow_port_state,
+ &ett_port_cf
+ };
+
+ /* Register the protocol name and description */
+ proto_openflow = proto_register_protocol("OpenFlow",
+ "openflow", "openflow");
+
+ /* Required function calls to register the header fields and subtrees */
+ proto_register_field_array(proto_openflow, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ openflow_module = prefs_register_protocol(proto_openflow, proto_reg_handoff_openflow);
+
+ /* Register port preference */
+ prefs_register_uint_preference(openflow_module, "tcp.port", "openflow TCP Port",
+ " openflow TCP port if other than the default",
+ 10, &g_openflow_port);
+}
+
+void
+proto_reg_handoff_openflow(void)
+{
+ static gboolean initialized = FALSE;
+ static dissector_handle_t openflow_handle;
+ static int currentPort;
+
+ if (!initialized) {
+ openflow_handle = new_create_dissector_handle(dissect_openflow, proto_openflow);
+ initialized = TRUE;
+
+ } else {
+ dissector_delete_uint("tcp.port", currentPort, openflow_handle);
+ }
+
+ currentPort = g_openflow_port;
+
+ dissector_add_uint("tcp.port", currentPort, openflow_handle);
+ eth_withoutfcs_handle = find_dissector("eth_withoutfcs");
+
+}
+
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */