]> The Tcpdump Group git mirrors - tcpdump/blobdiff - print-geneve.c
gre: add support for MikroTik Ethernet-over-IP hack.
[tcpdump] / print-geneve.c
index cf6d9d1e74fb2f7a9eb87d54efc04fd96e397dfd..59dca93e047823de2ef2906decc6e7a314880a56 100644 (file)
  * FOR A PARTICULAR PURPOSE.
  */
 
+/* \summary: Generic Network Virtualization Encapsulation (Geneve) printer */
+/* specification: RFC 8926 */
+
 #ifdef HAVE_CONFIG_H
-#include "config.h"
+#include <config.h>
 #endif
 
-#include <tcpdump-stdinc.h>
+#include "netdissect-stdinc.h"
 
-#include "interface.h"
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
 #include "extract.h"
 #include "ethertype.h"
 
 /*
- * Geneve header, draft-gross-geneve-02
+ * Geneve header:
  *
  *    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
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *    |        Virtual Network Identifier (VNI)       |    Reserved   |
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *    |                    Variable Length Options                    |
+ *    |                                                               |
+ *    ~                    Variable-Length Options                    ~
+ *    |                                                               |
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  * Options:
+ *    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
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *    |          Option Class         |      Type     |R|R|R| Length  |
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *    |                      Variable Option Data                     |
+ *    |                                                               |
+ *    ~                  Variable-Length Option Data                  ~
+ *    |                                                               |
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
 
@@ -74,17 +84,82 @@ static const struct tok geneve_flag_values[] = {
 };
 
 static const char *
-format_opt_class(uint16_t opt_class)
+format_opt_class(const uint16_t opt_class)
 {
-    if (opt_class <= 0xff)
-        return "Standard";
-    else if (opt_class == 0xffff)
-        return "Experimental";
-    else
-        return "Unknown";
+    switch (opt_class) {
+    case 0x0100:
+        return "Linux";
+    case 0x0101:
+        return "Open vSwitch";
+    case 0x0102:
+        return "Open Virtual Networking (OVN)";
+    case 0x0103:
+        return "In-band Network Telemetry (INT)";
+    case 0x0104:
+        return "VMware";
+    case 0x0105:
+    case 0x0108:
+    case 0x0109:
+    case 0x010A:
+    case 0x010B:
+    case 0x010C:
+    case 0x010D:
+    case 0x010E:
+    case 0x010F:
+    case 0x0110:
+        return "Amazon";
+    case 0x0106:
+    case 0x0130:
+    case 0x0131:
+        return "Cisco";
+    case 0x0107:
+        return "Oracle";
+    case 0x0111:
+    case 0x0112:
+    case 0x0113:
+    case 0x0114:
+    case 0x0115:
+    case 0x0116:
+    case 0x0117:
+    case 0x0118:
+        return "IBM";
+    case 0x0119:
+    case 0x011A:
+    case 0x011B:
+    case 0x011C:
+    case 0x011D:
+    case 0x011E:
+    case 0x011F:
+    case 0x0120:
+    case 0x0121:
+    case 0x0122:
+    case 0x0123:
+    case 0x0124:
+    case 0x0125:
+    case 0x0126:
+    case 0x0127:
+    case 0x0128:
+        return "Ericsson";
+    case 0x0129:
+        return "Oxide";
+    case 0x0132:
+    case 0x0133:
+    case 0x0134:
+    case 0x0135:
+        return "Google";
+    case 0x0136:
+        return "InfoQuick";
+    default:
+        if (opt_class <= 0x00ff)
+            return "Standard";
+        else if (opt_class >= 0xfff0)
+            return "Experimental";
+    }
+
+    return "Unknown";
 }
 
-static void
+static unsigned
 geneve_opts_print(netdissect_options *ndo, const u_char *bp, u_int len)
 {
     const char *sep = "";
@@ -94,129 +169,138 @@ geneve_opts_print(netdissect_options *ndo, const u_char *bp, u_int len)
         uint8_t opt_type;
         uint8_t opt_len;
 
-        ND_PRINT((ndo, "%s", sep));
+        ND_ICHECKMSG_U("remaining options length", len, <, 4);
+        ND_PRINT("%s", sep);
         sep = ", ";
 
-        opt_class = EXTRACT_16BITS(bp);
-        opt_type = *(bp + 2);
-        opt_len = 4 + ((*(bp + 3) & OPT_LEN_MASK) * 4);
+        opt_class = GET_BE_U_2(bp);
+        opt_type = GET_U_1(bp + 2);
+        opt_len = 4 + ((GET_U_1(bp + 3) & OPT_LEN_MASK) * 4);
 
-        ND_PRINT((ndo, "class %s (0x%x) type 0x%x%s len %u",
+        ND_PRINT("class %s (0x%x) type 0x%x%s len %u",
                   format_opt_class(opt_class), opt_class, opt_type,
-                  opt_type & OPT_TYPE_CRITICAL ? "(C)" : "", opt_len));
+                  opt_type & OPT_TYPE_CRITICAL ? "(C)" : "", opt_len);
 
         if (opt_len > len) {
-            ND_PRINT((ndo, " [bad length]"));
-            return;
+            ND_PRINT(" [bad length]");
+            goto invalid;
         }
 
         if (ndo->ndo_vflag > 1 && opt_len > 4) {
-            const uint32_t *print_data = (const uint32_t *)(bp + 4);
+            const uint32_t *data = (const uint32_t *)(bp + 4);
             int i;
 
-            ND_PRINT((ndo, " data"));
+            ND_PRINT(" data");
 
             for (i = 4; i < opt_len; i += 4) {
-                ND_PRINT((ndo, " %08x", EXTRACT_32BITS(print_data)));
-                print_data++;
+                ND_PRINT(" %08x", GET_BE_U_4(data));
+                data++;
             }
-        }
+        } else
+            ND_TCHECK_LEN(bp, opt_len);
 
         bp += opt_len;
         len -= opt_len;
     }
+    return 1;
+invalid:
+    ND_TCHECK_LEN(bp, len);
+    return 0;
 }
 
 void
 geneve_print(netdissect_options *ndo, const u_char *bp, u_int len)
 {
     uint8_t ver_opt;
-    uint version;
+    u_int version;
     uint8_t flags;
     uint16_t prot;
     uint32_t vni;
     uint8_t reserved;
     u_int opts_len;
 
-    ND_PRINT((ndo, "Geneve"));
+    ndo->ndo_protocol = "geneve";
+    ND_PRINT("Geneve");
 
-    ND_TCHECK2(*bp, 8);
+    ND_ICHECK_U(len, <, 8);
 
-    ver_opt = *bp;
+    ver_opt = GET_U_1(bp);
     bp += 1;
     len -= 1;
 
     version = ver_opt >> VER_SHIFT;
     if (version != 0) {
-        ND_PRINT((ndo, " ERROR: unknown-version %u", version));
-        return;
+        ND_PRINT(" ERROR: unknown-version %u", version);
+        goto invalid;
     }
 
-    flags = *bp;
+    flags = GET_U_1(bp);
     bp += 1;
     len -= 1;
 
-    prot = EXTRACT_16BITS(bp);
+    prot = GET_BE_U_2(bp);
     bp += 2;
     len -= 2;
 
-    vni = EXTRACT_24BITS(bp);
+    vni = GET_BE_U_3(bp);
     bp += 3;
     len -= 3;
 
-    reserved = *bp;
+    reserved = GET_U_1(bp);
     bp += 1;
     len -= 1;
 
-    ND_PRINT((ndo, ", Flags [%s]",
-              bittok2str_nosep(geneve_flag_values, "none", flags)));
-    ND_PRINT((ndo, ", vni 0x%x", vni));
+    ND_PRINT(", Flags [%s]",
+              bittok2str_nosep(geneve_flag_values, "none", flags));
+    ND_PRINT(", vni 0x%x", vni);
 
     if (reserved)
-        ND_PRINT((ndo, ", rsvd 0x%x", reserved));
+        ND_PRINT(", rsvd 0x%x", reserved);
 
     if (ndo->ndo_eflag)
-        ND_PRINT((ndo, ", proto %s (0x%04x)",
-                  tok2str(ethertype_values, "unknown", prot), prot));
+        ND_PRINT(", proto %s (0x%04x)",
+                  tok2str(ethertype_values, "unknown", prot), prot);
 
     opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;
 
     if (len < opts_len) {
-        ND_PRINT((ndo, " truncated-geneve - %u bytes missing",
-                  len - opts_len));
-        return;
+        ND_PRINT(" (opts_len %u > %u", opts_len, len);
+        goto invalid;
     }
 
-    ND_TCHECK2(*bp, opts_len);
-
     if (opts_len > 0) {
-        ND_PRINT((ndo, ", options ["));
-
-        if (ndo->ndo_vflag)
-            geneve_opts_print(ndo, bp, opts_len);
-        else
-            ND_PRINT((ndo, "%u bytes", opts_len));
+        ND_PRINT(", options [");
+
+        if (ndo->ndo_vflag) {
+            if (! geneve_opts_print(ndo, bp, opts_len))
+                goto invalid;
+        } else {
+            ND_TCHECK_LEN(bp, opts_len);
+            ND_PRINT("%u bytes", opts_len);
+        }
 
-        ND_PRINT((ndo, "]"));
+        ND_PRINT("]");
     }
 
     bp += opts_len;
     len -= opts_len;
 
     if (ndo->ndo_vflag < 1)
-        ND_PRINT((ndo, ": "));
+        ND_PRINT(": ");
     else
-        ND_PRINT((ndo, "\n\t"));
+        ND_PRINT("\n\t");
 
-    if (ethertype_print(ndo, prot, bp, len, len) == 0) {
+    if (ethertype_print(ndo, prot, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL) == 0) {
         if (prot == ETHERTYPE_TEB)
-            ether_print(ndo, bp, len, len, NULL, NULL);
-        else
-            ND_PRINT((ndo, "geneve-proto-0x%x", prot));
+            ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+        else {
+            ND_PRINT("geneve-proto-0x%x", prot);
+            ND_TCHECK_LEN(bp, len);
+        }
     }
 
     return;
 
-trunc:
-    ND_PRINT((ndo, " [|geneve]"));
+invalid:
+    nd_print_invalid(ndo);
 }