On Wed, Oct 19, 2016 at 12:28:13PM +0200, Rafael Zalamena wrote:
> This is the first step to teach tcpdump(8) how to read OpenFlow protocol
> packets. I've implemented support for most messages switchd(8) uses and
> currently only for OpenFlow 1.3.5, but we might reuse some functions for
> OpenFlow 1.0 as well. For now we don't support showing strings of
> definitions instead of numbers.
> 
> Right now we might not be able to tcpdump local switchd(8) <-> switch(4),
> however for remote switchd(8) using TCP it is possible (remote switch(4),
> HP device, Mininet etc...).
> 
> Supported messages:
>  * Hello (with bitmap, there is a diff for switchd(8) as well);
>  * Error (with encapsulated ofp header printing);
>  * Echo Request/Reply;
>  * Features Request/Reply;
>  * Set Config;
>  * Packet-in (with encapsulated ethernet printing);
>  * Packet-out (with encapsulated ethernet printing);
>  * Flow removed;
> 
> Missing:
>  * Multipart request/reply;
>  * Flow mod;

I've updated the diff with the following changes:
 - Removed useless whitespaces/tabs;
 - Added missing spaces in "[truncated]" messages;
 - Recalculate remaining packet length in ofp_print() instead of trusting
   tcp_print() length;
 - Implemented flow_mod support along with instructions;

ok?

Index: Makefile
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/Makefile,v
retrieving revision 1.59
diff -u -p -r1.59 Makefile
--- Makefile    14 Oct 2015 04:55:17 -0000      1.59
+++ Makefile    18 Oct 2016 10:19:50 -0000
@@ -47,7 +47,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p
        print-ip6.c print-ip6opts.c print-icmp6.c print-dhcp6.c print-frag6.c \
        print-bgp.c print-ospf6.c print-ripng.c print-rt6.c print-stp.c \
        print-etherip.c print-lwres.c print-lldp.c print-cdp.c print-pflog.c \
-       print-pfsync.c pf_print_state.c \
+       print-pfsync.c pf_print_state.c print-ofp.c \
        print-udpencap.c print-carp.c \
        print-802_11.c print-iapp.c print-mpls.c print-slow.c \
        gmt2local.c savestr.c setsignal.c in_cksum.c
Index: interface.h
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.67
diff -u -p -r1.67 interface.h
--- interface.h 11 Jul 2016 00:27:50 -0000      1.67
+++ interface.h 21 Oct 2016 13:45:12 -0000
@@ -274,6 +274,7 @@ extern void mpls_print(const u_char *, u
 extern void lldp_print(const u_char *, u_int);
 extern void slow_print(const u_char *, u_int);
 extern void gtp_print(const u_char *, u_int, u_short, u_short);
+extern void ofp_print(const u_char *);
 
 #ifdef INET6
 extern void ip6_print(const u_char *, u_int);
Index: print-tcp.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/print-tcp.c,v
retrieving revision 1.35
diff -u -p -r1.35 print-tcp.c
--- print-tcp.c 16 Nov 2015 00:16:39 -0000      1.35
+++ print-tcp.c 21 Oct 2016 13:47:34 -0000
@@ -123,6 +123,10 @@ static struct tcp_seq_hash tcp_seq_hash[
 #endif
 #define NETBIOS_SSN_PORT 139
 
+/* OpenFlow TCP ports. */
+#define OLD_OFP_PORT   6633
+#define OFP_PORT       6653
+
 static int tcp_cksum(const struct ip *ip, const struct tcphdr *tp, int len)
 {
        union phu {
@@ -665,6 +669,9 @@ tcp_print(const u_char *bp, u_int length
        } else {
                if (sport == BGP_PORT || dport == BGP_PORT)
                        bgp_print(bp, length);
+               else if (sport == OLD_OFP_PORT || dport == OLD_OFP_PORT ||
+                   sport == OFP_PORT || dport == OFP_PORT)
+                       ofp_print(bp);
 #if 0
                else if (sport == NETBIOS_SSN_PORT || dport == NETBIOS_SSN_PORT)
                        nbt_tcp_print(bp, length);
--- /dev/null   Fri Oct 21 17:26:48 2016
+++ print-ofp.c Fri Oct 21 17:25:11 2016
@@ -0,0 +1,1166 @@
+/*     $$      */
+
+/*
+ * Copyright (c) 2016 Rafael Zalamena <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <net/ofp.h>
+
+#include <endian.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "interface.h"
+
+/* Size of action header without the padding. */
+#define AH_UNPADDED    (offsetof(struct ofp_action_header, ah_pad))
+
+void    ofp_print_hello(const u_char *, u_int, u_int);
+void    ofp_print_featuresreply(const u_char *, u_int);
+void    ofp_print_setconfig(const u_char *, u_int);
+void    ofp_print_packetin(const u_char *, u_int);
+void    ofp_print_packetout(const u_char *, u_int);
+void    ofp_print_flowremoved(const u_char *, u_int);
+void    ofp_print_flowmod(const u_char *, u_int);
+
+void    oxm_print_halfword(const u_char *, u_int, int, int);
+void    oxm_print_word(const u_char *, u_int, int, int);
+void    oxm_print_quad(const u_char *, u_int, int, int);
+void    oxm_print_ether(const u_char *, u_int, int);
+void    ofp_print_oxm(struct ofp_ox_match *, const u_char *, u_int);
+
+void    action_print_output(const u_char *, u_int);
+void    action_print_group(const u_char *, u_int);
+void    action_print_setmplsttl(const u_char *, u_int);
+void    action_print_setnwttl(const u_char *, u_int);
+void    action_print_push(const u_char *, u_int);
+void    action_print_popmpls(const u_char *, u_int);
+void    action_print_setfield(const u_char *, u_int);
+void    ofp_print_action(struct ofp_action_header *, const u_char *,
+           u_int);
+
+void    instruction_print_gototable(const char *, u_int);
+void    instruction_print_meta(const char *, u_int);
+void    instruction_print_actions(const char *, u_int);
+void    instruction_print_meter(const char *, u_int);
+void    instruction_print_experimenter(const char *, u_int);
+void    ofp_print_instruction(struct ofp_instruction *, const char *, u_int);
+
+void
+ofp_print_hello(const u_char *bp, u_int length, u_int ohlen)
+{
+       struct ofp_hello_element_header         *he;
+       int                                      hetype, helen, ver, i;
+       int                                      vmajor, vminor;
+       uint32_t                                 bmp;
+
+       /* Skip the OFP header. */
+       bp += sizeof(struct ofp_header);
+       length -= sizeof(struct ofp_header);
+
+       printf(" HELLO");
+
+       /* Check for header truncation. */
+       if (ohlen > sizeof(struct ofp_header) &&
+           length < sizeof(*he)) {
+               printf(" [truncated]");
+               return;
+       }
+
+ next_header:
+       /* Check for hello element headers. */
+       if (length < sizeof(*he))
+               return;
+
+       he = (struct ofp_hello_element_header *)bp;
+       hetype = ntohs(he->he_type);
+       helen = ntohs(he->he_length);
+
+       bp += sizeof(*he);
+       length -= sizeof(*he);
+       helen -= sizeof(*he);
+
+       switch (hetype) {
+       case OPF_HELLO_T_VERSION_BITMAP:
+               printf(" version bitmap <");
+               if (helen < sizeof(bmp)) {
+                       printf("invalid header>");
+                       break;
+               }
+
+ next_bitmap:
+               if (length < sizeof(bmp)) {
+                       printf("truncated>");
+                       break;
+               }
+
+               bmp = ntohl(*(uint32_t *)bp);
+               for (i = 0, ver = 9; i < 32; i++, ver++) {
+                       if ((bmp & (1 << i)) == 0)
+                               continue;
+
+                       vmajor = (ver / 10);
+                       vminor = ver % 10;
+                       printf("v%d.%d ", vmajor, vminor);
+               }
+               helen -= min(sizeof(bmp), helen);
+               length -= sizeof(bmp);
+               bp += sizeof(bmp);
+               if (helen)
+                       goto next_bitmap;
+
+               printf("\b>");
+               break;
+
+       default:
+               printf(" element header[type %d length %d]", hetype, helen);
+               break;
+       }
+
+       length -= min(helen, length);
+       bp += helen;
+       if (length)
+               goto next_header;
+}
+
+void
+ofp_print_error(const u_char *bp, u_int length)
+{
+       struct ofp_error                        *err;
+
+       printf(" ERROR");
+       if (length < sizeof(*err)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       err = (struct ofp_error *)bp;
+       printf(" <type %d code %d>", ntohs(err->err_type),
+           ntohs(err->err_code));
+
+       length -= min(sizeof(*err), length);
+       bp += sizeof(*err);
+       /* If there are still bytes left, print the optional error data. */
+       if (length) {
+               printf(" error data:");
+               ofp_print(bp);
+       }
+}
+
+void
+ofp_print_featuresreply(const u_char *bp, u_int length)
+{
+       struct ofp_switch_features              *swf;
+
+       printf(" FEATURES REPLY");
+       if (length < sizeof(*swf)) {
+               printf(" [trucanted]");
+               return;
+       }
+
+       swf = (struct ofp_switch_features *)bp;
+       printf(" <datapath_id %#016llx nbuffers %u ntables %d aux_id %d "
+           "capabilities %#08x actions %#08x>",
+           be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers),
+           swf->swf_ntables, swf->swf_aux_id,
+           ntohl(swf->swf_capabilities), ntohl(swf->swf_actions));
+}
+
+void
+ofp_print_setconfig(const u_char *bp, u_int length)
+{
+       struct ofp_switch_config                *cfg;
+
+       printf(" SET CONFIG");
+       if (length < sizeof(*cfg)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       cfg = (struct ofp_switch_config *)bp;
+       printf(" <flags %#04x miss_send_len %d>",
+           ntohs(cfg->cfg_flags), ntohs(cfg->cfg_miss_send_len));
+}
+
+void
+ofp_print_packetin(const u_char *bp, u_int length)
+{
+       struct ofp_packet_in            *pin;
+       struct ofp_ox_match             *oxm;
+       int                              omtype, omlen;
+       int                              haspacket = 0;
+       const u_char                    *pktptr;
+
+       printf(" PACKET-IN");
+       if (length < sizeof(*pin)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       pin = (struct ofp_packet_in *)bp;
+       omtype = ntohs(pin->pin_match.om_type);
+       omlen = ntohs(pin->pin_match.om_length);
+       printf(" <buffer_id %u total_len %d reason %d table_id %d "
+           "cookie %#016llx match type %d length %d>",
+           ntohl(pin->pin_buffer_id), ntohs(pin->pin_total_len),
+           pin->pin_reason, pin->pin_table_id, be64toh(pin->pin_cookie),
+           omtype, omlen);
+
+       if (pin->pin_buffer_id == OFP_PKTOUT_NO_BUFFER)
+               haspacket = 1;
+
+       /* We only support OXM. */
+       if (omtype != OFP_MATCH_OXM)
+               return;
+
+       bp += sizeof(*pin);
+       length -= sizeof(*pin);
+
+       /* Get packet start address. */
+       pktptr = (bp - sizeof(pin->pin_match)) +
+           OFP_ALIGN(omlen) + ETHER_ALIGN;
+
+       /* Don't count the header for the OXM fields. */
+       omlen -= min(sizeof(pin->pin_match), omlen);
+       if (omlen == 0)
+               goto print_packet;
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+
+ print_packet:
+       if (haspacket == 0)
+               return;
+
+       /*
+        * Recalculate length:
+        * pktptr skipped the omlen + padding and the ETHER_ALIGN, so
+        * instead of keeping track of that we just recalculate length
+        * using the encapsulated packet begin and snapend.
+        */
+       length = max(snapend - pktptr, 0);
+       if (length < ETHER_ADDR_LEN) {
+               printf(" [|ether]");
+               return;
+       }
+
+       printf(" ");
+       ether_tryprint(pktptr, length, 0);
+}
+
+void
+ofp_print_flowremoved(const u_char *bp, u_int length)
+{
+       struct ofp_flow_removed                 *fr;
+       struct ofp_ox_match                     *oxm;
+       int                                      omtype, omlen;
+
+       printf(" FLOW REMOVED");
+       if (length < sizeof(*fr)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       fr = (struct ofp_flow_removed *)bp;
+       omtype = ntohs(fr->fr_match.om_type);
+       omlen = ntohs(fr->fr_match.om_length);
+       printf(" <cookie %#016llx priority %d reason %d table_id %d "
+           "duration sec %u nsec %u timeout idle %d hard %d "
+           "packet count %llu byte count %llu match type %d length %d>",
+           be64toh(fr->fr_cookie), ntohs(fr->fr_priority), fr->fr_reason,
+           fr->fr_table_id, ntohl(fr->fr_duration_sec),
+           ntohl(fr->fr_duration_nsec), ntohs(fr->fr_idle_timeout),
+           ntohs(fr->fr_hard_timeout), be64toh(fr->fr_packet_count),
+           be64toh(fr->fr_byte_count), omtype, omlen);
+
+       /* We only support OXM. */
+       if (omtype != OFP_MATCH_OXM)
+               return;
+
+       omlen -= min(sizeof(fr->fr_match), omlen);
+       if (omlen == 0)
+               return;
+
+       bp += sizeof(*fr);
+       length -= sizeof(*fr);
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+}
+
+void
+ofp_print_packetout(const u_char *bp, u_int length)
+{
+       struct ofp_packet_out                   *pout;
+       struct ofp_action_header                *ah;
+       const u_char                            *pktptr;
+       int                                      actionslen, haspacket = 0;
+       int                                      ahlen;
+
+       printf(" PACKET-OUT");
+       if (length < sizeof(*pout)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       pout = (struct ofp_packet_out *)bp;
+       actionslen = ntohs(pout->pout_actions_len);
+       printf(" <buffer_id %u in_port %u actions_len %d>",
+           ntohl(pout->pout_buffer_id), ntohl(pout->pout_in_port),
+           actionslen);
+
+       if (pout->pout_buffer_id == OFP_PKTOUT_NO_BUFFER)
+               haspacket = 1;
+
+       bp += sizeof(*pout);
+       length -= sizeof(*pout);
+       pktptr = bp + actionslen;
+
+       /* No actions or unpadded header. */
+       if (actionslen < sizeof(*ah))
+               goto print_packet;
+
+ parse_next_action:
+       if (length < sizeof(*ah)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ah = (struct ofp_action_header *)bp;
+       bp += AH_UNPADDED;
+       length -= AH_UNPADDED;
+       actionslen -= AH_UNPADDED;
+       ahlen = ntohs(ah->ah_len) - AH_UNPADDED;
+       if (length < ahlen) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_action(ah, bp, length);
+
+       bp += ahlen;
+       length -= ahlen;
+       actionslen -= min(ahlen, actionslen);
+       if (actionslen)
+               goto parse_next_action;
+
+ print_packet:
+       if (haspacket == 0)
+               return;
+
+       /* Recalculate length using packet boundaries. */
+       length = max(snapend - pktptr, 0);
+       if (length < ETHER_ADDR_LEN) {
+               printf(" [|ether]");
+               return;
+       }
+
+       printf(" ");
+       ether_tryprint(pktptr, length, 0);
+}
+
+void
+ofp_print_flowmod(const u_char *bp, u_int length)
+{
+       struct ofp_flow_mod                     *fm;
+       struct ofp_ox_match                     *oxm;
+       struct ofp_instruction                  *i;
+       int                                      omtype, omlen, ilen;
+       int                                      instructionslen, padsize;
+
+       printf(" FLOW-MOD");
+       if (length < sizeof(*fm)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       fm = (struct ofp_flow_mod *)bp;
+       omtype = ntohs(fm->fm_match.om_type);
+       omlen = ntohs(fm->fm_match.om_length);
+       printf(" <cookie %llu cookie_mask %#016llx table_id %d command %d "
+           "timeout idle %d hard %d priority %d buffer_id %u out_port %u "
+           "out_group %u flags %#04x match type %d length %d>",
+           be64toh(fm->fm_cookie), be64toh(fm->fm_cookie_mask),
+           fm->fm_table_id, fm->fm_command, ntohs(fm->fm_idle_timeout),
+           ntohs(fm->fm_hard_timeout), fm->fm_priority,
+           ntohl(fm->fm_buffer_id), ntohl(fm->fm_out_port),
+           ntohl(fm->fm_out_group), ntohs(fm->fm_flags), omtype, omlen);
+
+       bp += sizeof(*fm);
+       length -= sizeof(*fm);
+
+       padsize = OFP_ALIGN(omlen) - omlen;
+       omlen -= min(sizeof(fm->fm_match), omlen);
+       instructionslen = length - (omlen + padsize);
+       if (omtype != OFP_MATCH_OXM || omlen == 0) {
+               if (instructionslen <= 0)
+                       return;
+
+               /* Skip padding if any. */
+               if (padsize) {
+                       bp += padsize;
+                       length -= padsize;
+               }
+               goto parse_next_instruction;
+       }
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+
+       /* Skip padding if any. */
+       if (padsize) {
+               bp += padsize;
+               length -= padsize;
+       }
+
+parse_next_instruction:
+       if (length < sizeof(*i)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       i = (struct ofp_instruction *)bp;
+       bp += sizeof(*i);
+       length -= sizeof(*i);
+       instructionslen -= sizeof(*i);
+       ilen = ntohs(i->i_len) - sizeof(*i);
+       if (length < ilen) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_instruction(i, bp, length);
+
+       bp += ilen;
+       length -= ilen;
+       instructionslen -= ilen;
+       if (instructionslen > 0)
+               goto parse_next_instruction;
+}
+
+void
+ofp_print(const u_char *bp)
+{
+       struct ofp_header                       *oh;
+       unsigned int                             ohlen;
+       u_int                                    length;
+
+       length = snapend - bp;
+       if (length < sizeof(*oh)) {
+               printf(" OpenFlow(truncated)");
+               return;
+       }
+
+       oh = (struct ofp_header *)bp;
+       ohlen = ntohs(oh->oh_length);
+       printf(" OpenFlow(ver %#02x type %d len %d xid %u)",
+           oh->oh_version, oh->oh_type, ohlen, ntohl(oh->oh_xid));
+
+       switch (oh->oh_version) {
+       case OFP_V_1_3:
+               break;
+
+       default:
+               return;
+       }
+
+       switch (oh->oh_type) {
+       case OFP_T_HELLO:
+               ofp_print_hello(bp, length, ohlen);
+               break;
+       case OFP_T_ERROR:
+               ofp_print_error(bp, length);
+               break;
+       case OFP_T_ECHO_REQUEST:
+               printf(" ECHO REQUEST");
+               break;
+       case OFP_T_ECHO_REPLY:
+               printf(" ECHO REPLY");
+               break;
+       case OFP_T_FEATURES_REQUEST:
+               printf(" FEATURES REQUEST");
+               break;
+       case OFP_T_FEATURES_REPLY:
+               ofp_print_featuresreply(bp, length);
+               break;
+       case OFP_T_SET_CONFIG:
+               ofp_print_setconfig(bp, length);
+               break;
+       case OFP_T_PACKET_IN:
+               ofp_print_packetin(bp, length);
+               break;
+       case OFP_T_FLOW_REMOVED:
+               ofp_print_flowremoved(bp, length);
+               break;
+       case OFP_T_PACKET_OUT:
+               ofp_print_packetout(bp, length);
+               break;
+       case OFP_T_FLOW_MOD:
+               ofp_print_flowmod(bp, length);
+               break;
+       }
+}
+
+void
+oxm_print_byte(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint8_t         *b;
+
+       if (length < sizeof(*b)) {
+               printf("[truncated]");
+               return;
+       }
+
+       b = (uint8_t *)bp;
+       if (hex)
+               printf("%#02x", ntohs(*b));
+       else
+               printf("%u", ntohs(*b));
+
+       if (hasmask) {
+               bp += sizeof(*b);
+               length -= sizeof(*b);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_halfword(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint16_t        *h;
+
+       if (length < sizeof(*h)) {
+               printf("[truncated]");
+               return;
+       }
+
+       h = (uint16_t *)bp;
+       if (hex)
+               printf("%#04x", ntohs(*h));
+       else
+               printf("%u", ntohs(*h));
+
+       if (hasmask) {
+               bp += sizeof(*h);
+               length -= sizeof(*h);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_word(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint32_t        *w;
+
+       if (length < sizeof(*w)) {
+               printf("[truncated]");
+               return;
+       }
+
+       w = (uint32_t *)bp;
+       if (hex)
+               printf("%#08x", ntohl(*w));
+       else
+               printf("%u", ntohl(*w));
+
+       if (hasmask) {
+               bp += sizeof(*w);
+               length -= sizeof(*w);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_quad(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint64_t        *q;
+
+       if (length < sizeof(*q)) {
+               printf("[truncated]");
+               return;
+       }
+
+       q = (uint64_t *)bp;
+       if (hex)
+               printf("%#016llx", be64toh(*q));
+       else
+               printf("%llu", be64toh(*q));
+
+       if (hasmask) {
+               bp += sizeof(*q);
+               length -= sizeof(*q);
+               printf(" mask ");
+               oxm_print_quad(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_ether(const u_char *bp, u_int length, int hasmask)
+{
+       uint8_t         *ptr;
+       int              i;
+       char             hex[8];
+
+       if (length < ETHER_HDR_LEN) {
+               printf("[truncated]");
+               return;
+       }
+
+       ptr = (uint8_t *)bp;
+       for (i = 0; i < ETHER_ADDR_LEN; i++) {
+               if (i)
+                       printf(":");
+
+               snprintf(hex, sizeof(hex), "%02x", ptr[i]);
+               printf("%s", hex);
+       }
+
+       if (hasmask) {
+               bp += ETHER_ADDR_LEN;
+               length -= ETHER_ADDR_LEN;
+               printf(" mask ");
+               oxm_print_ether(bp, length, 0);
+       }
+}
+
+void
+oxm_print_data(const u_char *bp, u_int length, int hasmask, size_t datalen)
+{
+       uint8_t         *ptr;
+       int              i;
+       char             hex[8];
+
+       if (length < datalen) {
+               printf("[truncated]");
+               return;
+       }
+
+       ptr = (uint8_t *)bp;
+       for (i = 0; i < datalen; i++) {
+               snprintf(hex, sizeof(hex), "%02x", ptr[i]);
+               printf("%s", hex);
+       }
+
+       if (hasmask) {
+               bp += datalen;
+               length -= datalen;
+               printf(" mask ");
+               oxm_print_data(bp, length, 0, datalen);
+       }
+}
+
+void
+ofp_print_oxm(struct ofp_ox_match *oxm, const u_char *bp, u_int length)
+{
+       int                              class, field, mask, len;
+       uint16_t                        *vlan;
+
+       class = ntohs(oxm->oxm_class);
+       field = OFP_OXM_GET_FIELD(oxm);
+       mask = OFP_OXM_GET_HASMASK(oxm);
+       len = oxm->oxm_length;
+       printf(" OXM <class %d field %d hasmask %d length %d",
+           class, field, mask, len);
+
+       switch (class) {
+       case OFP_OXM_C_OPENFLOW_BASIC:
+               break;
+
+       case OFP_OXM_C_NXM_0:
+       case OFP_OXM_C_NXM_1:
+       case OFP_OXM_C_OPENFLOW_EXPERIMENTER:
+       default:
+               printf(">");
+               return;
+       }
+
+       printf(" value ");
+
+       switch (field) {
+       case OFP_XM_T_IN_PORT:
+       case OFP_XM_T_IN_PHY_PORT:
+       case OFP_XM_T_MPLS_LABEL:
+               oxm_print_word(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_META:
+       case OFP_XM_T_TUNNEL_ID:
+               oxm_print_quad(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_ETH_DST:
+       case OFP_XM_T_ETH_SRC:
+       case OFP_XM_T_ARP_SHA:
+       case OFP_XM_T_ARP_THA:
+       case OFP_XM_T_IPV6_ND_SLL:
+       case OFP_XM_T_IPV6_ND_TLL:
+               oxm_print_ether(bp, length, mask);
+               break;
+
+       case OFP_XM_T_ETH_TYPE:
+               oxm_print_halfword(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_VLAN_VID:
+               /*
+                * VLAN has an exception: it uses the higher bits to signal
+                * the presence of the VLAN.
+                */
+               if (length < sizeof(*vlan)) {
+                       printf("[truncated]");
+                       break;
+               }
+
+               vlan = (uint16_t *)bp;
+               if (ntohs(*vlan) & OFP_XM_VID_PRESENT)
+                       printf("(VLAN %d) ",
+                           ntohs(*vlan) & (~OFP_XM_VID_PRESENT));
+               else
+                       printf("(no VLAN) ");
+               /* FALLTHROUGH */
+       case OFP_XM_T_TCP_SRC:
+       case OFP_XM_T_TCP_DST:
+       case OFP_XM_T_UDP_SRC:
+       case OFP_XM_T_UDP_DST:
+       case OFP_XM_T_SCTP_SRC:
+       case OFP_XM_T_SCTP_DST:
+       case OFP_XM_T_ARP_OP:
+       case OFP_XM_T_IPV6_EXTHDR:
+               oxm_print_halfword(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_VLAN_PCP:
+       case OFP_XM_T_IP_DSCP:
+       case OFP_XM_T_IP_ECN:
+       case OFP_XM_T_MPLS_TC:
+       case OFP_XM_T_MPLS_BOS:
+               oxm_print_byte(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_IPV4_SRC:
+       case OFP_XM_T_IPV4_DST:
+       case OFP_XM_T_ARP_SPA:
+       case OFP_XM_T_ARP_TPA:
+       case OFP_XM_T_IPV6_FLABEL:
+               oxm_print_word(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_IP_PROTO:
+       case OFP_XM_T_ICMPV4_TYPE:
+       case OFP_XM_T_ICMPV4_CODE:
+       case OFP_XM_T_ICMPV6_TYPE:
+       case OFP_XM_T_ICMPV6_CODE:
+               oxm_print_byte(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_IPV6_SRC:
+       case OFP_XM_T_IPV6_DST:
+       case OFP_XM_T_IPV6_ND_TARGET:
+               oxm_print_data(bp, length, mask, sizeof(struct in6_addr));
+               break;
+
+       case OFP_XM_T_PBB_ISID:
+               oxm_print_data(bp, length, mask, 3);
+               break;
+
+       default:
+               printf("unknown");
+               break;
+       }
+
+       printf(">");
+}
+
+void
+action_print_output(const u_char *bp, u_int length)
+{
+       struct ofp_action_output                *ao;
+
+       if (length < (sizeof(*ao) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ao = (struct ofp_action_output *)(bp - AH_UNPADDED);
+       printf(" port %u max_len %d",
+           ntohl(ao->ao_port), ntohs(ao->ao_max_len));
+}
+
+void
+action_print_group(const u_char *bp, u_int length)
+{
+       struct ofp_action_group         *ag;
+
+       if (length < (sizeof(*ag) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ag = (struct ofp_action_group *)(bp - AH_UNPADDED);
+       printf(" group_id %u", ntohl(ag->ag_group_id));
+}
+
+void
+action_print_setmplsttl(const u_char *bp, u_int length)
+{
+       struct ofp_action_mpls_ttl      *amt;
+
+       if (length < (sizeof(*amt) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       amt = (struct ofp_action_mpls_ttl *)(bp - AH_UNPADDED);
+       printf(" ttl %d", amt->amt_ttl);
+}
+
+void
+action_print_setnwttl(const u_char *bp, u_int length)
+{
+       struct ofp_action_nw_ttl        *ant;
+
+       if (length < (sizeof(*ant) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ant = (struct ofp_action_nw_ttl *)(bp - AH_UNPADDED);
+       printf(" ttl %d", ant->ant_ttl);
+}
+
+void
+action_print_push(const u_char *bp, u_int length)
+{
+       struct ofp_action_push          *ap;
+
+       if (length < (sizeof(*ap) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ap = (struct ofp_action_push *)(bp - AH_UNPADDED);
+       printf(" ethertype %#04x", ntohs(ap->ap_ethertype));
+}
+
+void
+action_print_popmpls(const u_char *bp, u_int length)
+{
+       struct ofp_action_pop_mpls      *apm;
+
+       if (length < (sizeof(*apm) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       apm = (struct ofp_action_pop_mpls *)(bp - AH_UNPADDED);
+       printf(" ethertype %#04x", ntohs(apm->apm_ethertype));
+}
+
+void
+action_print_setfield(const u_char *bp, u_int length)
+{
+       struct ofp_action_set_field     *asf;
+       struct ofp_ox_match             *oxm;
+       int                              omlen;
+
+       if (length < (sizeof(*asf) - AH_UNPADDED)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       asf = (struct ofp_action_set_field *)(bp - AH_UNPADDED);
+       omlen = ntohs(asf->asf_len) - AH_UNPADDED;
+       if (omlen == 0)
+               return;
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+}
+
+void
+ofp_print_action(struct ofp_action_header *ah, const u_char *bp, u_int length)
+{
+       int                     ahtype;
+
+       ahtype = ntohs(ah->ah_type);
+       printf(" ACTION <type %d length %d", ahtype, ntohs(ah->ah_len));
+
+       switch (ahtype) {
+       case OFP_ACTION_OUTPUT:
+               action_print_output(bp, length);
+               break;
+
+       case OFP_ACTION_GROUP:
+               action_print_group(bp, length);
+               break;
+
+       case OFP_ACTION_SET_QUEUE:
+               /* TODO missing struct in ofp.h header. */
+               break;
+
+       case OFP_ACTION_SET_MPLS_TTL:
+               action_print_setmplsttl(bp, length);
+               break;
+
+       case OFP_ACTION_SET_NW_TTL:
+               action_print_setnwttl(bp, length);
+               break;
+
+       case OFP_ACTION_PUSH_VLAN:
+       case OFP_ACTION_PUSH_MPLS:
+       case OFP_ACTION_PUSH_PBB:
+               action_print_push(bp, length);
+               break;
+
+       case OFP_ACTION_POP_MPLS:
+               action_print_popmpls(bp, length);
+               break;
+
+       case OFP_ACTION_SET_FIELD:
+               break;
+
+       case OFP_ACTION_COPY_TTL_OUT:
+       case OFP_ACTION_COPY_TTL_IN:
+       case OFP_ACTION_DEC_NW_TTL:
+       case OFP_ACTION_DEC_MPLS_TTL:
+       case OFP_ACTION_POP_VLAN:
+       case OFP_ACTION_POP_PBB:
+       case OFP_ACTION_EXPERIMENTER:
+       default:
+               /* Generic header, nothing to show here. */
+               break;
+       }
+
+       printf(">");
+}
+
+void
+instruction_print_gototable(const char *bp, u_int length)
+{
+       struct ofp_instruction_goto_table       *igt;
+
+       if (length < (sizeof(*igt) - sizeof(struct ofp_instruction))) {
+               printf(" [truncated]");
+               return;
+       }
+
+       igt = (struct ofp_instruction_goto_table *)
+           (bp - sizeof(struct ofp_instruction));
+       printf(" table_id %d", igt->igt_table_id);
+}
+
+void
+instruction_print_meta(const char *bp, u_int length)
+{
+       struct ofp_instruction_write_metadata   *iwm;
+
+       if (length < (sizeof(*iwm) - sizeof(struct ofp_instruction))) {
+               printf(" [truncated]");
+               return;
+       }
+
+       iwm = (struct ofp_instruction_write_metadata *)
+           (bp - sizeof(struct ofp_instruction));
+       printf(" metadata %llu metadata_mask %llu",
+           be64toh(iwm->iwm_metadata), be64toh(iwm->iwm_metadata_mask));
+}
+
+void
+instruction_print_actions(const char *bp, u_int length)
+{
+       struct ofp_instruction_actions          *ia;
+       struct ofp_action_header                *ah;
+       int                                      actionslen;
+       unsigned int                             ahlen;
+
+       if (length < (sizeof(*ia) - sizeof(struct ofp_instruction))) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ia = (struct ofp_instruction_actions *)
+           (bp - sizeof(struct ofp_instruction));
+
+       actionslen = ntohs(ia->ia_len) - sizeof(*ia);
+       if (actionslen <= 0)
+               return;
+
+       bp += sizeof(*ia) - sizeof(struct ofp_instruction);
+       length -= sizeof(*ia) - sizeof(struct ofp_instruction);
+
+parse_next_action:
+       if (length < sizeof(*ah)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ah = (struct ofp_action_header *)bp;
+       bp += AH_UNPADDED;
+       length -= AH_UNPADDED;
+       actionslen -= AH_UNPADDED;
+       ahlen = ntohs(ah->ah_len) - AH_UNPADDED;
+       if (length < ahlen) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ofp_print_action(ah, bp, length);
+
+       bp += ahlen;
+       length -= ahlen;
+       actionslen -= min(ahlen, actionslen);
+       if (actionslen)
+               goto parse_next_action;
+}
+
+void
+instruction_print_meter(const char *bp, u_int length)
+{
+       struct ofp_instruction_meter            *im;
+
+       if (length < (sizeof(*im) - sizeof(struct ofp_instruction))) {
+               printf(" [truncated]");
+               return;
+       }
+
+       im = (struct ofp_instruction_meter *)
+           (bp - sizeof(struct ofp_instruction));
+       printf(" meter_id %u", ntohl(im->im_meter_id));
+}
+
+void
+instruction_print_experimenter(const char *bp, u_int length)
+{
+       struct ofp_instruction_experimenter             *ie;
+
+       if (length < (sizeof(*ie) - sizeof(struct ofp_instruction))) {
+               printf(" [truncated]");
+               return;
+       }
+
+       ie = (struct ofp_instruction_experimenter *)
+           (bp - sizeof(struct ofp_instruction));
+       printf(" experimenter %u", ntohl(ie->ie_experimenter));
+}
+
+void
+ofp_print_instruction(struct ofp_instruction *i, const char *bp, u_int length)
+{
+       int                     itype;
+
+       itype = ntohs(i->i_type);
+       printf(" INSTRUCTION <type %d length %d", itype, ntohs(i->i_len));
+
+       switch (itype) {
+       case OFP_INSTRUCTION_T_GOTO_TABLE:
+               instruction_print_gototable(bp, length);
+               break;
+       case OFP_INSTRUCTION_T_WRITE_META:
+               instruction_print_meta(bp, length);
+               break;
+       case OFP_INSTRUCTION_T_WRITE_ACTIONS:
+       case OFP_INSTRUCTION_T_APPLY_ACTIONS:
+       case OFP_INSTRUCTION_T_CLEAR_ACTIONS:
+               instruction_print_actions(bp, length);
+               break;
+       case OFP_INSTRUCTION_T_METER:
+               instruction_print_meter(bp, length);
+               break;
+       case OFP_INSTRUCTION_T_EXPERIMENTER:
+               instruction_print_meter(bp, length);
+               break;
+       }
+
+       printf(">");
+}

Reply via email to