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