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