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