Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright (c) 2014 VMware, Inc. All Rights Reserved.
      3  *
      4  * Jesse Gross <jesse (at) nicira.com>
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that: (1) source code
      8  * distributions retain the above copyright notice and this paragraph
      9  * in its entirety, and (2) distributions including binary code include
     10  * the above copyright notice and this paragraph in its entirety in
     11  * the documentation or other materials provided with the distribution.
     12  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
     13  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
     14  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     15  * FOR A PARTICULAR PURPOSE.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 #ifndef lint
     20 __RCSID("$NetBSD: print-geneve.c,v 1.5 2024/09/02 16:15:31 christos Exp $");
     21 #endif
     22 
     23 /* \summary: Generic Network Virtualization Encapsulation (Geneve) printer */
     24 
     25 #include <config.h>
     26 
     27 #include "netdissect-stdinc.h"
     28 
     29 #include "netdissect.h"
     30 #include "extract.h"
     31 #include "ethertype.h"
     32 
     33 /*
     34  * Geneve header, draft-ietf-nvo3-geneve
     35  *
     36  *    0                   1                   2                   3
     37  *    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
     38  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     39  *    |Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
     40  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     41  *    |        Virtual Network Identifier (VNI)       |    Reserved   |
     42  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     43  *    |                    Variable Length Options                    |
     44  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     45  *
     46  * Options:
     47  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     48  *    |          Option Class         |      Type     |R|R|R| Length  |
     49  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     50  *    |                      Variable Option Data                     |
     51  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     52  */
     53 
     54 #define VER_SHIFT 6
     55 #define HDR_OPTS_LEN_MASK 0x3F
     56 
     57 #define FLAG_OAM      (1 << 7)
     58 #define FLAG_CRITICAL (1 << 6)
     59 #define FLAG_R1       (1 << 5)
     60 #define FLAG_R2       (1 << 4)
     61 #define FLAG_R3       (1 << 3)
     62 #define FLAG_R4       (1 << 2)
     63 #define FLAG_R5       (1 << 1)
     64 #define FLAG_R6       (1 << 0)
     65 
     66 #define OPT_TYPE_CRITICAL (1 << 7)
     67 #define OPT_LEN_MASK 0x1F
     68 
     69 static const struct tok geneve_flag_values[] = {
     70         { FLAG_OAM, "O" },
     71         { FLAG_CRITICAL, "C" },
     72         { FLAG_R1, "R1" },
     73         { FLAG_R2, "R2" },
     74         { FLAG_R3, "R3" },
     75         { FLAG_R4, "R4" },
     76         { FLAG_R5, "R5" },
     77         { FLAG_R6, "R6" },
     78         { 0, NULL }
     79 };
     80 
     81 static const char *
     82 format_opt_class(uint16_t opt_class)
     83 {
     84     switch (opt_class) {
     85     case 0x0100:
     86         return "Linux";
     87     case 0x0101:
     88         return "Open vSwitch";
     89     case 0x0102:
     90         return "Open Virtual Networking (OVN)";
     91     case 0x0103:
     92         return "In-band Network Telemetry (INT)";
     93     case 0x0104:
     94         return "VMware";
     95     default:
     96         if (opt_class <= 0x00ff)
     97             return "Standard";
     98         else if (opt_class >= 0xfff0)
     99             return "Experimental";
    100     }
    101 
    102     return "Unknown";
    103 }
    104 
    105 static void
    106 geneve_opts_print(netdissect_options *ndo, const u_char *bp, u_int len)
    107 {
    108     const char *sep = "";
    109 
    110     while (len > 0) {
    111         uint16_t opt_class;
    112         uint8_t opt_type;
    113         uint8_t opt_len;
    114 
    115         ND_PRINT("%s", sep);
    116         sep = ", ";
    117 
    118         opt_class = GET_BE_U_2(bp);
    119         opt_type = GET_U_1(bp + 2);
    120         opt_len = 4 + ((GET_U_1(bp + 3) & OPT_LEN_MASK) * 4);
    121 
    122         ND_PRINT("class %s (0x%x) type 0x%x%s len %u",
    123                   format_opt_class(opt_class), opt_class, opt_type,
    124                   opt_type & OPT_TYPE_CRITICAL ? "(C)" : "", opt_len);
    125 
    126         if (opt_len > len) {
    127             ND_PRINT(" [bad length]");
    128             return;
    129         }
    130 
    131         if (ndo->ndo_vflag > 1 && opt_len > 4) {
    132             const uint32_t *data = (const uint32_t *)(bp + 4);
    133             int i;
    134 
    135             ND_PRINT(" data");
    136 
    137             for (i = 4; i < opt_len; i += 4) {
    138                 ND_PRINT(" %08x", GET_BE_U_4(data));
    139                 data++;
    140             }
    141         }
    142 
    143         bp += opt_len;
    144         len -= opt_len;
    145     }
    146 }
    147 
    148 void
    149 geneve_print(netdissect_options *ndo, const u_char *bp, u_int len)
    150 {
    151     uint8_t ver_opt;
    152     u_int version;
    153     uint8_t flags;
    154     uint16_t prot;
    155     uint32_t vni;
    156     uint8_t reserved;
    157     u_int opts_len;
    158 
    159     ndo->ndo_protocol = "geneve";
    160     ND_PRINT("Geneve");
    161 
    162     if (len < 8) {
    163         ND_PRINT(" [length %u < 8]", len);
    164         nd_print_invalid(ndo);
    165         return;
    166     }
    167 
    168     ND_TCHECK_8(bp);
    169 
    170     ver_opt = GET_U_1(bp);
    171     bp += 1;
    172     len -= 1;
    173 
    174     version = ver_opt >> VER_SHIFT;
    175     if (version != 0) {
    176         ND_PRINT(" ERROR: unknown-version %u", version);
    177         return;
    178     }
    179 
    180     flags = GET_U_1(bp);
    181     bp += 1;
    182     len -= 1;
    183 
    184     prot = GET_BE_U_2(bp);
    185     bp += 2;
    186     len -= 2;
    187 
    188     vni = GET_BE_U_3(bp);
    189     bp += 3;
    190     len -= 3;
    191 
    192     reserved = GET_U_1(bp);
    193     bp += 1;
    194     len -= 1;
    195 
    196     ND_PRINT(", Flags [%s]",
    197               bittok2str_nosep(geneve_flag_values, "none", flags));
    198     ND_PRINT(", vni 0x%x", vni);
    199 
    200     if (reserved)
    201         ND_PRINT(", rsvd 0x%x", reserved);
    202 
    203     if (ndo->ndo_eflag)
    204         ND_PRINT(", proto %s (0x%04x)",
    205                   tok2str(ethertype_values, "unknown", prot), prot);
    206 
    207     opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;
    208 
    209     if (len < opts_len) {
    210         ND_PRINT(" truncated-geneve - %u bytes missing",
    211                   opts_len - len);
    212         return;
    213     }
    214 
    215     ND_TCHECK_LEN(bp, opts_len);
    216 
    217     if (opts_len > 0) {
    218         ND_PRINT(", options [");
    219 
    220         if (ndo->ndo_vflag)
    221             geneve_opts_print(ndo, bp, opts_len);
    222         else
    223             ND_PRINT("%u bytes", opts_len);
    224 
    225         ND_PRINT("]");
    226     }
    227 
    228     bp += opts_len;
    229     len -= opts_len;
    230 
    231     if (ndo->ndo_vflag < 1)
    232         ND_PRINT(": ");
    233     else
    234         ND_PRINT("\n\t");
    235 
    236     if (ethertype_print(ndo, prot, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL) == 0) {
    237         if (prot == ETHERTYPE_TEB)
    238             ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
    239         else
    240             ND_PRINT("geneve-proto-0x%x", prot);
    241     }
    242 
    243     return;
    244 
    245 trunc:
    246     nd_print_trunc(ndo);
    247 }
    248