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("%s", sep);
113        sep = ", ";
114
115        opt_class = GET_BE_U_2(bp);
116        opt_type = GET_U_1(bp + 2);
117        opt_len = 4 + ((GET_U_1(bp + 3) & OPT_LEN_MASK) * 4);
118
119        ND_PRINT("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(" [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(" data");
133
134            for (i = 4; i < opt_len; i += 4) {
135                ND_PRINT(" %08x", GET_BE_U_4(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    ndo->ndo_protocol = "geneve";
157    ND_PRINT("Geneve");
158
159    if (len < 8) {
160        ND_PRINT(" [length %u < 8]", len);
161        nd_print_invalid(ndo);
162        return;
163    }
164
165    ND_TCHECK_8(bp);
166
167    ver_opt = GET_U_1(bp);
168    bp += 1;
169    len -= 1;
170
171    version = ver_opt >> VER_SHIFT;
172    if (version != 0) {
173        ND_PRINT(" ERROR: unknown-version %u", version);
174        return;
175    }
176
177    flags = GET_U_1(bp);
178    bp += 1;
179    len -= 1;
180
181    prot = GET_BE_U_2(bp);
182    bp += 2;
183    len -= 2;
184
185    vni = GET_BE_U_3(bp);
186    bp += 3;
187    len -= 3;
188
189    reserved = GET_U_1(bp);
190    bp += 1;
191    len -= 1;
192
193    ND_PRINT(", Flags [%s]",
194              bittok2str_nosep(geneve_flag_values, "none", flags));
195    ND_PRINT(", vni 0x%x", vni);
196
197    if (reserved)
198        ND_PRINT(", rsvd 0x%x", reserved);
199
200    if (ndo->ndo_eflag)
201        ND_PRINT(", proto %s (0x%04x)",
202                  tok2str(ethertype_values, "unknown", prot), prot);
203
204    opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;
205
206    if (len < opts_len) {
207        ND_PRINT(" truncated-geneve - %u bytes missing",
208                  opts_len - len);
209        return;
210    }
211
212    ND_TCHECK_LEN(bp, opts_len);
213
214    if (opts_len > 0) {
215        ND_PRINT(", options [");
216
217        if (ndo->ndo_vflag)
218            geneve_opts_print(ndo, bp, opts_len);
219        else
220            ND_PRINT("%u bytes", opts_len);
221
222        ND_PRINT("]");
223    }
224
225    bp += opts_len;
226    len -= opts_len;
227
228    if (ndo->ndo_vflag < 1)
229        ND_PRINT(": ");
230    else
231        ND_PRINT("\n\t");
232
233    if (ethertype_print(ndo, prot, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL) == 0) {
234        if (prot == ETHERTYPE_TEB)
235            ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
236        else
237            ND_PRINT("geneve-proto-0x%x", prot);
238    }
239
240    return;
241
242trunc:
243    nd_print_trunc(ndo);
244}
245