]> The Tcpdump Group git mirrors - tcpdump/blobdiff - print-openflow-1.0.c
Travis: avoid useless lines from 'brew' command in the logs
[tcpdump] / print-openflow-1.0.c
index 03bb623f3b81832e9b8bace1ac29da4a23285faf..590caba48c4e7118f539a708f0c9dfd91884c418 100644 (file)
@@ -8,6 +8,16 @@
  *
  * [OF10] http://www.openflow.org/documents/openflow-spec-v1.0.0.pdf
  *
+ * Most functions in this file take 3 arguments into account:
+ * * cp -- the pointer to the first octet to decode
+ * * len -- the length of the current structure as declared on the wire
+ * * ep -- the pointer to the end of the captured frame
+ * They return either the pointer to the next not-yet-decoded part of the frame
+ * or the value of ep, which means the current frame processing is over as it
+ * has been fully decoded or is malformed or truncated. This way it is possible
+ * to chain and nest such functions uniformly to decode an OF1.0 message, which
+ * consists of several layers of nested structures.
+ *
  * Decoding of Ethernet frames nested in OFPT_PACKET_IN and OFPT_PACKET_OUT
  * messages is done only when the verbosity level set by command-line argument
  * is "-vvv" or higher. In that case the verbosity level is temporarily
@@ -15,6 +25,9 @@
  * tcpdump with "-vvvv" will do full decoding of OpenFlow and "-v" decoding of
  * the nested frames.
  *
+ * Partial decoding of Big Switch Networks vendor extensions is done after the
+ * oftest (OpenFlow Testing Framework) source code.
+ *
  *
  * Copyright (c) 2013 The TCPDUMP project
  * All rights reserved.
@@ -55,6 +68,7 @@
 #include "ether.h"
 #include "ethertype.h"
 #include "ipproto.h"
+#include "oui.h"
 #include "openflow.h"
 
 static const char tstr[] = " [|openflow]";
@@ -580,6 +594,80 @@ static const struct tok empty_str[] = {
 #define SERIAL_NUM_LEN             32
 #define OFP_VLAN_NONE          0xffff
 
+/* vendor extensions */
+#define BSN_SET_IP_MASK                    0
+#define BSN_GET_IP_MASK_REQUEST            1
+#define BSN_GET_IP_MASK_REPLY              2
+#define BSN_SET_MIRRORING                  3
+#define BSN_GET_MIRRORING_REQUEST          4
+#define BSN_GET_MIRRORING_REPLY            5
+#define BSN_SHELL_COMMAND                  6
+#define BSN_SHELL_OUTPUT                   7
+#define BSN_SHELL_STATUS                   8
+#define BSN_GET_INTERFACES_REQUEST         9
+#define BSN_GET_INTERFACES_REPLY          10
+#define BSN_SET_PKTIN_SUPPRESSION_REQUEST 11
+#define BSN_SET_L2_TABLE_REQUEST          12
+#define BSN_GET_L2_TABLE_REQUEST          13
+#define BSN_GET_L2_TABLE_REPLY            14
+#define BSN_VIRTUAL_PORT_CREATE_REQUEST   15
+#define BSN_VIRTUAL_PORT_CREATE_REPLY     16
+#define BSN_VIRTUAL_PORT_REMOVE_REQUEST   17
+#define BSN_BW_ENABLE_SET_REQUEST         18
+#define BSN_BW_ENABLE_GET_REQUEST         19
+#define BSN_BW_ENABLE_GET_REPLY           20
+#define BSN_BW_CLEAR_DATA_REQUEST         21
+#define BSN_BW_CLEAR_DATA_REPLY           22
+#define BSN_BW_ENABLE_SET_REPLY           23
+#define BSN_SET_L2_TABLE_REPLY            24
+#define BSN_SET_PKTIN_SUPPRESSION_REPLY   25
+#define BSN_VIRTUAL_PORT_REMOVE_REPLY     26
+#define BSN_HYBRID_GET_REQUEST            27
+#define BSN_HYBRID_GET_REPLY              28
+                                       /* 29 */
+                                       /* 30 */
+#define BSN_PDU_TX_REQUEST                31
+#define BSN_PDU_TX_REPLY                  32
+#define BSN_PDU_RX_REQUEST                33
+#define BSN_PDU_RX_REPLY                  34
+#define BSN_PDU_RX_TIMEOUT                35
+
+static const struct tok bsn_subtype_str[] = {
+       { BSN_SET_IP_MASK,                   "SET_IP_MASK"                   },
+       { BSN_GET_IP_MASK_REQUEST,           "GET_IP_MASK_REQUEST"           },
+       { BSN_GET_IP_MASK_REPLY,             "GET_IP_MASK_REPLY"             },
+       { BSN_SET_MIRRORING,                 "SET_MIRRORING"                 },
+       { BSN_GET_MIRRORING_REQUEST,         "GET_MIRRORING_REQUEST"         },
+       { BSN_GET_MIRRORING_REPLY,           "GET_MIRRORING_REPLY"           },
+       { BSN_GET_INTERFACES_REQUEST,        "GET_INTERFACES_REQUEST"        },
+       { BSN_GET_INTERFACES_REPLY,          "GET_INTERFACES_REPLY"          },
+       { BSN_SET_PKTIN_SUPPRESSION_REQUEST, "SET_PKTIN_SUPPRESSION_REQUEST" },
+       { BSN_SET_L2_TABLE_REQUEST,          "SET_L2_TABLE_REQUEST"          },
+       { BSN_GET_L2_TABLE_REQUEST,          "GET_L2_TABLE_REQUEST"          },
+       { BSN_GET_L2_TABLE_REPLY,            "GET_L2_TABLE_REPLY"            },
+       { BSN_VIRTUAL_PORT_CREATE_REQUEST,   "VIRTUAL_PORT_CREATE_REQUEST"   },
+       { BSN_VIRTUAL_PORT_CREATE_REPLY,     "VIRTUAL_PORT_CREATE_REPLY"     },
+       { BSN_VIRTUAL_PORT_REMOVE_REQUEST,   "VIRTUAL_PORT_REMOVE_REQUEST"   },
+       { BSN_BW_ENABLE_SET_REQUEST,         "BW_ENABLE_SET_REQUEST"         },
+       { BSN_BW_ENABLE_GET_REQUEST,         "BW_ENABLE_GET_REQUEST"         },
+       { BSN_BW_ENABLE_GET_REPLY,           "BW_ENABLE_GET_REPLY"           },
+       { BSN_BW_CLEAR_DATA_REQUEST,         "BW_CLEAR_DATA_REQUEST"         },
+       { BSN_BW_CLEAR_DATA_REPLY,           "BW_CLEAR_DATA_REPLY"           },
+       { BSN_BW_ENABLE_SET_REPLY,           "BW_ENABLE_SET_REPLY"           },
+       { BSN_SET_L2_TABLE_REPLY,            "SET_L2_TABLE_REPLY"            },
+       { BSN_SET_PKTIN_SUPPRESSION_REPLY,   "SET_PKTIN_SUPPRESSION_REPLY"   },
+       { BSN_VIRTUAL_PORT_REMOVE_REPLY,     "VIRTUAL_PORT_REMOVE_REPLY"     },
+       { BSN_HYBRID_GET_REQUEST,            "HYBRID_GET_REQUEST"            },
+       { BSN_HYBRID_GET_REPLY,              "HYBRID_GET_REPLY"              },
+       { BSN_PDU_TX_REQUEST,                "PDU_TX_REQUEST"                },
+       { BSN_PDU_TX_REPLY,                  "PDU_TX_REPLY"                  },
+       { BSN_PDU_RX_REQUEST,                "PDU_RX_REQUEST"                },
+       { BSN_PDU_RX_REPLY,                  "PDU_RX_REPLY"                  },
+       { BSN_PDU_RX_TIMEOUT,                "PDU_RX_TIMEOUT"                },
+       { 0, NULL }
+};
+
+
 static const char *
 vlan_str(const uint16_t vid) {
        static char buf[sizeof("65535 (bogus)")];
@@ -633,16 +721,184 @@ trunc:
        return ep;
 }
 
+static const u_char *
+of10_bsn_message_print(netdissect_options *ndo,
+                       const u_char *cp, const u_char *ep, const u_int len) {
+       const u_char *cp0 = cp;
+       uint32_t subtype;
+
+       if (len < 4)
+               goto corrupt;
+       /* subtype */
+       ND_TCHECK2(*cp, 4);
+       subtype = EXTRACT_32BITS(cp);
+       cp += 4;
+       ND_PRINT((ndo, "\n\t subtype %s", tok2str(bsn_subtype_str, "unknown (0x%08x)", subtype)));
+       switch (subtype) {
+       case BSN_GET_IP_MASK_REQUEST:
+               /*
+                *  0                   1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +---------------+---------------+---------------+---------------+
+                * |                            subtype                            |
+                * +---------------+---------------+---------------+---------------+
+                * |     index     |                      pad                      |
+                * +---------------+---------------+---------------+---------------+
+                * |                              pad                              |
+                * +---------------+---------------+---------------+---------------+
+                *
+                */
+               if (len != 12)
+                       goto corrupt;
+               /* index */
+               ND_TCHECK2(*cp, 1);
+               ND_PRINT((ndo, ", index %u", *cp));
+               cp += 1;
+               /* pad */
+               ND_TCHECK2(*cp, 7);
+               cp += 7;
+               break;
+       case BSN_SET_IP_MASK:
+       case BSN_GET_IP_MASK_REPLY:
+               /*
+                *  0                   1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +---------------+---------------+---------------+---------------+
+                * |                            subtype                            |
+                * +---------------+---------------+---------------+---------------+
+                * |     index     |                      pad                      |
+                * +---------------+---------------+---------------+---------------+
+                * |                              mask                             |
+                * +---------------+---------------+---------------+---------------+
+                *
+                */
+               if (len != 12)
+                       goto corrupt;
+               /* index */
+               ND_TCHECK2(*cp, 1);
+               ND_PRINT((ndo, ", index %u", *cp));
+               cp += 1;
+               /* pad */
+               ND_TCHECK2(*cp, 3);
+               cp += 3;
+               /* mask */
+               ND_TCHECK2(*cp, 4);
+               ND_PRINT((ndo, ", mask %s", ipaddr_string(ndo, cp)));
+               cp += 4;
+               break;
+       case BSN_SET_MIRRORING:
+       case BSN_GET_MIRRORING_REQUEST:
+       case BSN_GET_MIRRORING_REPLY:
+               /*
+                *  0                   1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +---------------+---------------+---------------+---------------+
+                * |                            subtype                            |
+                * +---------------+---------------+---------------+---------------+
+                * |     ports     |                      pad                      |
+                * +---------------+---------------+---------------+---------------+
+                *
+                */
+               if (len != 8)
+                       goto corrupt;
+               /* ports */
+               ND_TCHECK2(*cp, 1);
+               ND_PRINT((ndo, ", ports %u", *cp));
+               cp += 1;
+               /* pad */
+               ND_TCHECK2(*cp, 3);
+               cp += 3;
+               break;
+       case BSN_GET_INTERFACES_REQUEST:
+       case BSN_GET_L2_TABLE_REQUEST:
+       case BSN_BW_ENABLE_GET_REQUEST:
+       case BSN_BW_CLEAR_DATA_REQUEST:
+       case BSN_HYBRID_GET_REQUEST:
+               /*
+                *  0                   1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +---------------+---------------+---------------+---------------+
+                * |                            subtype                            |
+                * +---------------+---------------+---------------+---------------+
+                *
+                */
+               if (len != 4)
+                       goto corrupt;
+               break;
+       case BSN_VIRTUAL_PORT_REMOVE_REQUEST:
+               /*
+                *  0                   1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +---------------+---------------+---------------+---------------+
+                * |                            subtype                            |
+                * +---------------+---------------+---------------+---------------+
+                * |                           vport_no                            |
+                * +---------------+---------------+---------------+---------------+
+                *
+                */
+               if (len != 8)
+                       goto corrupt;
+               /* vport_no */
+               ND_TCHECK2(*cp, 4);
+               ND_PRINT((ndo, ", vport_no %u", EXTRACT_32BITS(cp)));
+               cp += 4;
+               break;
+       default:
+               ND_TCHECK2(*cp, len - 4);
+               cp += len - 4;
+       }
+       return cp;
+
+corrupt: /* skip the undersized data */
+       ND_PRINT((ndo, "%s", cstr));
+       ND_TCHECK2(*cp0, len);
+       return cp0 + len;
+trunc:
+       ND_PRINT((ndo, "%s", tstr));
+       return ep;
+}
+
+static const u_char *
+of10_vendor_message_print(netdissect_options *ndo,
+                          const u_char *cp, const u_char *ep, const u_int len) {
+       uint32_t vendor;
+       const u_char *(*decoder)(netdissect_options *, const u_char *, const u_char *, u_int);
+
+       if (len < 4)
+               goto corrupt;
+       /* vendor */
+       ND_TCHECK2(*cp, 4);
+       vendor = EXTRACT_32BITS(cp);
+       cp += 4;
+       ND_PRINT((ndo, ", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor)));
+       /* data */
+       decoder =
+               vendor == OUI_BSN         ? of10_bsn_message_print         :
+               of10_data_print;
+       return decoder(ndo, cp, ep, len - 4);
+
+corrupt: /* skip the undersized data */
+       ND_PRINT((ndo, "%s", cstr));
+       ND_TCHECK2(*cp, len);
+       return cp + len;
+trunc:
+       ND_PRINT((ndo, "%s", tstr));
+       return ep;
+}
+
 /* Vendor ID is mandatory, data is optional. */
 static const u_char *
 of10_vendor_data_print(netdissect_options *ndo,
                        const u_char *cp, const u_char *ep, const u_int len) {
+       uint32_t vendor;
+
        if (len < 4)
                goto corrupt;
        /* vendor */
        ND_TCHECK2(*cp, 4);
-       ND_PRINT((ndo, ", vendor 0x%08x", EXTRACT_32BITS(cp)));
+       vendor = EXTRACT_32BITS(cp);
        cp += 4;
+       ND_PRINT((ndo, ", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor)));
        /* data */
        return of10_data_print(ndo, cp, ep, len - 4);
 
@@ -692,12 +948,12 @@ of10_phy_ports_print(netdissect_options *ndo,
                cp += 2;
                /* hw_addr */
                ND_TCHECK2(*cp, ETHER_ADDR_LEN);
-               ND_PRINT((ndo, ", hw_addr %s", etheraddr_string(cp)));
+               ND_PRINT((ndo, ", hw_addr %s", etheraddr_string(ndo, cp)));
                cp += ETHER_ADDR_LEN;
                /* name */
                ND_TCHECK2(*cp, OFP_MAX_PORT_NAME_LEN);
                ND_PRINT((ndo, ", name '"));
-               fn_print(cp, cp + OFP_MAX_PORT_NAME_LEN);
+               fn_print(ndo, cp, cp + OFP_MAX_PORT_NAME_LEN);
                ND_PRINT((ndo, "'"));
                cp += OFP_MAX_PORT_NAME_LEN;
 
@@ -896,12 +1152,12 @@ of10_match_print(netdissect_options *ndo,
        /* dl_src */
        ND_TCHECK2(*cp, ETHER_ADDR_LEN);
        if (! (wildcards & OFPFW_DL_SRC))
-               ND_PRINT((ndo, "%smatch dl_src %s", pfx, etheraddr_string(cp)));
+               ND_PRINT((ndo, "%smatch dl_src %s", pfx, etheraddr_string(ndo, cp)));
        cp += ETHER_ADDR_LEN;
        /* dl_dst */
        ND_TCHECK2(*cp, ETHER_ADDR_LEN);
        if (! (wildcards & OFPFW_DL_DST))
-               ND_PRINT((ndo, "%smatch dl_dst %s", pfx, etheraddr_string(cp)));
+               ND_PRINT((ndo, "%smatch dl_dst %s", pfx, etheraddr_string(ndo, cp)));
        cp += ETHER_ADDR_LEN;
        /* dl_vlan */
        ND_TCHECK2(*cp, 2);
@@ -943,13 +1199,13 @@ of10_match_print(netdissect_options *ndo,
        ND_TCHECK2(*cp, 4);
        nw_bits = (wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT;
        if (nw_bits < 32)
-               ND_PRINT((ndo, "%smatch nw_src %s/%u", pfx, ipaddr_string(cp), 32 - nw_bits));
+               ND_PRINT((ndo, "%smatch nw_src %s/%u", pfx, ipaddr_string(ndo, cp), 32 - nw_bits));
        cp += 4;
        /* nw_dst */
        ND_TCHECK2(*cp, 4);
        nw_bits = (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT;
        if (nw_bits < 32)
-               ND_PRINT((ndo, "%smatch nw_dst %s/%u", pfx, ipaddr_string(cp), 32 - nw_bits));
+               ND_PRINT((ndo, "%smatch nw_dst %s/%u", pfx, ipaddr_string(ndo, cp), 32 - nw_bits));
        cp += 4;
        /* tp_src */
        ND_TCHECK2(*cp, 2);
@@ -1072,7 +1328,7 @@ of10_actions_print(netdissect_options *ndo,
                case OFPAT_SET_DL_DST:
                        /* dl_addr */
                        ND_TCHECK2(*cp, ETHER_ADDR_LEN);
-                       ND_PRINT((ndo, ", dl_addr %s", etheraddr_string(cp)));
+                       ND_PRINT((ndo, ", dl_addr %s", etheraddr_string(ndo, cp)));
                        cp += ETHER_ADDR_LEN;
                        /* pad */
                        ND_TCHECK2(*cp, 6);
@@ -1082,7 +1338,7 @@ of10_actions_print(netdissect_options *ndo,
                case OFPAT_SET_NW_DST:
                        /* nw_addr */
                        ND_TCHECK2(*cp, 4);
-                       ND_PRINT((ndo, ", nw_addr %s", ipaddr_string(cp)));
+                       ND_PRINT((ndo, ", nw_addr %s", ipaddr_string(ndo, cp)));
                        cp += 4;
                        break;
                case OFPAT_SET_NW_TOS:
@@ -1245,7 +1501,7 @@ of10_port_mod_print(netdissect_options *ndo,
        cp += 2;
        /* hw_addr */
        ND_TCHECK2(*cp, ETHER_ADDR_LEN);
-       ND_PRINT((ndo, ", hw_addr %s", etheraddr_string(cp)));
+       ND_PRINT((ndo, ", hw_addr %s", etheraddr_string(ndo, cp)));
        cp += ETHER_ADDR_LEN;
        /* config */
        ND_TCHECK2(*cp, 4);
@@ -1363,31 +1619,31 @@ of10_desc_stats_reply_print(netdissect_options *ndo,
        /* mfr_desc */
        ND_TCHECK2(*cp, DESC_STR_LEN);
        ND_PRINT((ndo, "\n\t  mfr_desc '"));
-       fn_print(cp, cp + DESC_STR_LEN);
+       fn_print(ndo, cp, cp + DESC_STR_LEN);
        ND_PRINT((ndo, "'"));
        cp += DESC_STR_LEN;
        /* hw_desc */
        ND_TCHECK2(*cp, DESC_STR_LEN);
        ND_PRINT((ndo, "\n\t  hw_desc '"));
-       fn_print(cp, cp + DESC_STR_LEN);
+       fn_print(ndo, cp, cp + DESC_STR_LEN);
        ND_PRINT((ndo, "'"));
        cp += DESC_STR_LEN;
        /* sw_desc */
        ND_TCHECK2(*cp, DESC_STR_LEN);
        ND_PRINT((ndo, "\n\t  sw_desc '"));
-       fn_print(cp, cp + DESC_STR_LEN);
+       fn_print(ndo, cp, cp + DESC_STR_LEN);
        ND_PRINT((ndo, "'"));
        cp += DESC_STR_LEN;
        /* serial_num */
        ND_TCHECK2(*cp, SERIAL_NUM_LEN);
        ND_PRINT((ndo, "\n\t  serial_num '"));
-       fn_print(cp, cp + SERIAL_NUM_LEN);
+       fn_print(ndo, cp, cp + SERIAL_NUM_LEN);
        ND_PRINT((ndo, "'"));
        cp += SERIAL_NUM_LEN;
        /* dp_desc */
        ND_TCHECK2(*cp, DESC_STR_LEN);
        ND_PRINT((ndo, "\n\t  dp_desc '"));
-       fn_print(cp, cp + DESC_STR_LEN);
+       fn_print(ndo, cp, cp + DESC_STR_LEN);
        ND_PRINT((ndo, "'"));
        return cp + DESC_STR_LEN;
 
@@ -1532,7 +1788,7 @@ of10_table_stats_reply_print(netdissect_options *ndo,
                /* name */
                ND_TCHECK2(*cp, OFP_MAX_TABLE_NAME_LEN);
                ND_PRINT((ndo, ", name '"));
-               fn_print(cp, cp + OFP_MAX_TABLE_NAME_LEN);
+               fn_print(ndo, cp, cp + OFP_MAX_TABLE_NAME_LEN);
                ND_PRINT((ndo, "'"));
                cp += OFP_MAX_TABLE_NAME_LEN;
                /* wildcards */
@@ -2003,7 +2259,7 @@ of10_header_body_print(netdissect_options *ndo,
                        goto corrupt;
                if (ndo->ndo_vflag < 1)
                        goto next_message;
-               return of10_vendor_data_print(ndo, cp, ep, len - OF_HEADER_LEN);
+               return of10_vendor_message_print(ndo, cp, ep, len - OF_HEADER_LEN);
        case OFPT_PACKET_IN:
                /* 2 mock octets count in OF_PACKET_IN_LEN but not in len */
                if (len < OF_PACKET_IN_LEN - 2)