xref: /freebsd/contrib/tcpdump/print-geneve.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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 
65 static 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 
77 static const char *
78 format_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 
88 static void
89 geneve_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 
131 void
132 geneve_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 
221 trunc:
222     ND_PRINT((ndo, " [|geneve]"));
223 }
224