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