]> The Tcpdump Group git mirrors - tcpdump/blob - print-mptcp.c
Rename a test. It's about the AppleTalk protocol
[tcpdump] / print-mptcp.c
1 /**
2 * Copyright (c) 2012
3 *
4 * Gregory Detal <gregory.detal@uclouvain.be>
5 * Christoph Paasch <christoph.paasch@uclouvain.be>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of the University nor of the Laboratory may be used
19 * to endorse or promote products derived from this software without
20 * specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /* \summary: Multipath TCP (MPTCP) printer */
36
37 /* specification: RFC 6824 */
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include "netdissect-stdinc.h"
44
45 #include "netdissect.h"
46 #include "extract.h"
47 #include "addrtoname.h"
48
49 #include "tcp.h"
50
51 #define MPTCP_SUB_CAPABLE 0x0
52 #define MPTCP_SUB_JOIN 0x1
53 #define MPTCP_SUB_DSS 0x2
54 #define MPTCP_SUB_ADD_ADDR 0x3
55 #define MPTCP_SUB_REMOVE_ADDR 0x4
56 #define MPTCP_SUB_PRIO 0x5
57 #define MPTCP_SUB_FAIL 0x6
58 #define MPTCP_SUB_FCLOSE 0x7
59
60 struct mptcp_option {
61 nd_uint8_t kind;
62 nd_uint8_t len;
63 nd_uint8_t sub_etc; /* subtype upper 4 bits, other stuff lower 4 bits */
64 };
65
66 #define MPTCP_OPT_SUBTYPE(sub_etc) (((sub_etc) >> 4) & 0xF)
67
68 struct mp_capable {
69 nd_uint8_t kind;
70 nd_uint8_t len;
71 nd_uint8_t sub_ver;
72 nd_uint8_t flags;
73 nd_uint64_t sender_key;
74 nd_uint64_t receiver_key;
75 nd_uint16_t data_len;
76 };
77
78 #define MP_CAPABLE_OPT_VERSION(sub_ver) (((sub_ver) >> 0) & 0xF)
79 #define MP_CAPABLE_C 0x80
80 #define MP_CAPABLE_S 0x01
81
82 struct mp_join {
83 nd_uint8_t kind;
84 nd_uint8_t len;
85 nd_uint8_t sub_b;
86 nd_uint8_t addr_id;
87 union {
88 struct {
89 nd_uint32_t token;
90 nd_uint32_t nonce;
91 } syn;
92 struct {
93 nd_uint64_t mac;
94 nd_uint32_t nonce;
95 } synack;
96 struct {
97 nd_byte mac[20];
98 } ack;
99 } u;
100 };
101
102 #define MP_JOIN_B 0x01
103
104 struct mp_dss {
105 nd_uint8_t kind;
106 nd_uint8_t len;
107 nd_uint8_t sub;
108 nd_uint8_t flags;
109 };
110
111 #define MP_DSS_F 0x10
112 #define MP_DSS_m 0x08
113 #define MP_DSS_M 0x04
114 #define MP_DSS_a 0x02
115 #define MP_DSS_A 0x01
116
117 static const struct tok mptcp_addr_subecho_bits[] = {
118 { 0x6, "v0-ip6" },
119 { 0x4, "v0-ip4" },
120 { 0x1, "v1-echo" },
121 { 0x0, "v1" },
122 { 0, NULL }
123 };
124
125 struct mp_add_addr {
126 nd_uint8_t kind;
127 nd_uint8_t len;
128 nd_uint8_t sub_echo;
129 nd_uint8_t addr_id;
130 union {
131 struct {
132 nd_ipv4 addr;
133 nd_uint16_t port;
134 nd_uint64_t mac;
135 } v4;
136 struct {
137 nd_ipv4 addr;
138 nd_uint64_t mac;
139 } v4np;
140 struct {
141 nd_ipv6 addr;
142 nd_uint16_t port;
143 nd_uint64_t mac;
144 } v6;
145 struct {
146 nd_ipv6 addr;
147 nd_uint64_t mac;
148 } v6np;
149 } u;
150 };
151
152 struct mp_remove_addr {
153 nd_uint8_t kind;
154 nd_uint8_t len;
155 nd_uint8_t sub;
156 /* list of addr_id */
157 nd_uint8_t addrs_id[1];
158 };
159
160 struct mp_fail {
161 nd_uint8_t kind;
162 nd_uint8_t len;
163 nd_uint8_t sub;
164 nd_uint8_t resv;
165 nd_uint64_t data_seq;
166 };
167
168 struct mp_close {
169 nd_uint8_t kind;
170 nd_uint8_t len;
171 nd_uint8_t sub;
172 nd_uint8_t rsv;
173 nd_byte key[8];
174 };
175
176 struct mp_prio {
177 nd_uint8_t kind;
178 nd_uint8_t len;
179 nd_uint8_t sub_b;
180 nd_uint8_t addr_id;
181 };
182
183 #define MP_PRIO_B 0x01
184
185 static int
186 dummy_print(netdissect_options *ndo _U_,
187 const u_char *opt _U_, u_int opt_len _U_, u_char flags _U_)
188 {
189 return 1;
190 }
191
192 static int
193 mp_capable_print(netdissect_options *ndo,
194 const u_char *opt, u_int opt_len, u_char flags)
195 {
196 const struct mp_capable *mpc = (const struct mp_capable *) opt;
197 uint8_t version, csum_enabled;
198
199 if (!((opt_len == 12 || opt_len == 4) && flags & TH_SYN) &&
200 !((opt_len == 20 || opt_len == 22 || opt_len == 24) && (flags & (TH_SYN | TH_ACK)) ==
201 TH_ACK))
202 return 0;
203
204 version = MP_CAPABLE_OPT_VERSION(GET_U_1(mpc->sub_ver));
205 switch (version) {
206 case 0: /* fall through */
207 case 1:
208 ND_PRINT(" v%u", version);
209 break;
210 default:
211 ND_PRINT(" Unknown Version (%u)", version);
212 return 1;
213 }
214
215 csum_enabled = GET_U_1(mpc->flags) & MP_CAPABLE_C;
216 if (csum_enabled)
217 ND_PRINT(" csum");
218 if (opt_len == 12 || opt_len >= 20) {
219 ND_PRINT(" {0x%" PRIx64, GET_BE_U_8(mpc->sender_key));
220 if (opt_len >= 20)
221 ND_PRINT(",0x%" PRIx64, GET_BE_U_8(mpc->receiver_key));
222
223 /* RFC 8684 Section 3.1 */
224 if ((opt_len == 22 && !csum_enabled) || opt_len == 24)
225 ND_PRINT(",data_len=%u", GET_BE_U_2(mpc->data_len));
226 ND_PRINT("}");
227 }
228 return 1;
229 }
230
231 static int
232 mp_join_print(netdissect_options *ndo,
233 const u_char *opt, u_int opt_len, u_char flags)
234 {
235 const struct mp_join *mpj = (const struct mp_join *) opt;
236
237 if (!(opt_len == 12 && (flags & TH_SYN)) &&
238 !(opt_len == 16 && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) &&
239 !(opt_len == 24 && (flags & TH_ACK)))
240 return 0;
241
242 if (opt_len != 24) {
243 if (GET_U_1(mpj->sub_b) & MP_JOIN_B)
244 ND_PRINT(" backup");
245 ND_PRINT(" id %u", GET_U_1(mpj->addr_id));
246 }
247
248 switch (opt_len) {
249 case 12: /* SYN */
250 ND_PRINT(" token 0x%x" " nonce 0x%x",
251 GET_BE_U_4(mpj->u.syn.token),
252 GET_BE_U_4(mpj->u.syn.nonce));
253 break;
254 case 16: /* SYN/ACK */
255 ND_PRINT(" hmac 0x%" PRIx64 " nonce 0x%x",
256 GET_BE_U_8(mpj->u.synack.mac),
257 GET_BE_U_4(mpj->u.synack.nonce));
258 break;
259 case 24: {/* ACK */
260 size_t i;
261 ND_PRINT(" hmac 0x");
262 for (i = 0; i < sizeof(mpj->u.ack.mac); ++i)
263 ND_PRINT("%02x", mpj->u.ack.mac[i]);
264 }
265 default:
266 break;
267 }
268 return 1;
269 }
270
271 static int
272 mp_dss_print(netdissect_options *ndo,
273 const u_char *opt, u_int opt_len, u_char flags)
274 {
275 const struct mp_dss *mdss = (const struct mp_dss *) opt;
276 uint8_t mdss_flags;
277
278 /* We need the flags, at a minimum. */
279 if (opt_len < 4)
280 return 0;
281
282 if (flags & TH_SYN)
283 return 0;
284
285 mdss_flags = GET_U_1(mdss->flags);
286 if (mdss_flags & MP_DSS_F)
287 ND_PRINT(" fin");
288
289 opt += 4;
290 opt_len -= 4;
291 if (mdss_flags & MP_DSS_A) {
292 /* Ack present */
293 ND_PRINT(" ack ");
294 /*
295 * If the a flag is set, we have an 8-byte ack; if it's
296 * clear, we have a 4-byte ack.
297 */
298 if (mdss_flags & MP_DSS_a) {
299 if (opt_len < 8)
300 return 0;
301 ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
302 opt += 8;
303 opt_len -= 8;
304 } else {
305 if (opt_len < 4)
306 return 0;
307 ND_PRINT("%u", GET_BE_U_4(opt));
308 opt += 4;
309 opt_len -= 4;
310 }
311 }
312
313 if (mdss_flags & MP_DSS_M) {
314 /*
315 * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
316 * Data-Level Length present, and Checksum possibly present.
317 */
318 ND_PRINT(" seq ");
319 /*
320 * If the m flag is set, we have an 8-byte NDS; if it's clear,
321 * we have a 4-byte DSN.
322 */
323 if (mdss_flags & MP_DSS_m) {
324 if (opt_len < 8)
325 return 0;
326 ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
327 opt += 8;
328 opt_len -= 8;
329 } else {
330 if (opt_len < 4)
331 return 0;
332 ND_PRINT("%u", GET_BE_U_4(opt));
333 opt += 4;
334 opt_len -= 4;
335 }
336 if (opt_len < 4)
337 return 0;
338 ND_PRINT(" subseq %u", GET_BE_U_4(opt));
339 opt += 4;
340 opt_len -= 4;
341 if (opt_len < 2)
342 return 0;
343 ND_PRINT(" len %u", GET_BE_U_2(opt));
344 opt += 2;
345 opt_len -= 2;
346
347 /*
348 * The Checksum is present only if negotiated.
349 * If there are at least 2 bytes left, process the next 2
350 * bytes as the Checksum.
351 */
352 if (opt_len >= 2) {
353 ND_PRINT(" csum 0x%x", GET_BE_U_2(opt));
354 opt_len -= 2;
355 }
356 }
357 if (opt_len != 0)
358 return 0;
359 return 1;
360 }
361
362 static int
363 add_addr_print(netdissect_options *ndo,
364 const u_char *opt, u_int opt_len, u_char flags _U_)
365 {
366 const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
367
368 if (!(opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18 ||
369 opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30))
370 return 0;
371
372 ND_PRINT(" %s",
373 tok2str(mptcp_addr_subecho_bits, "[bad version/echo]",
374 GET_U_1(add_addr->sub_echo) & 0xF));
375 ND_PRINT(" id %u", GET_U_1(add_addr->addr_id));
376 if (opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18) {
377 ND_PRINT(" %s", GET_IPADDR_STRING(add_addr->u.v4.addr));
378 if (opt_len == 10 || opt_len == 18)
379 ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v4.port));
380 if (opt_len == 16)
381 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4np.mac));
382 if (opt_len == 18)
383 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4.mac));
384 }
385
386 if (opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30) {
387 ND_PRINT(" %s", GET_IP6ADDR_STRING(add_addr->u.v6.addr));
388 if (opt_len == 22 || opt_len == 30)
389 ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v6.port));
390 if (opt_len == 28)
391 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6np.mac));
392 if (opt_len == 30)
393 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6.mac));
394 }
395
396 return 1;
397 }
398
399 static int
400 remove_addr_print(netdissect_options *ndo,
401 const u_char *opt, u_int opt_len, u_char flags _U_)
402 {
403 const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
404 u_int i;
405
406 if (opt_len < 4)
407 return 0;
408
409 opt_len -= 3;
410 ND_PRINT(" id");
411 for (i = 0; i < opt_len; i++)
412 ND_PRINT(" %u", GET_U_1(remove_addr->addrs_id[i]));
413 return 1;
414 }
415
416 static int
417 mp_prio_print(netdissect_options *ndo,
418 const u_char *opt, u_int opt_len, u_char flags _U_)
419 {
420 const struct mp_prio *mpp = (const struct mp_prio *) opt;
421
422 if (opt_len != 3 && opt_len != 4)
423 return 0;
424
425 if (GET_U_1(mpp->sub_b) & MP_PRIO_B)
426 ND_PRINT(" backup");
427 else
428 ND_PRINT(" non-backup");
429 if (opt_len == 4)
430 ND_PRINT(" id %u", GET_U_1(mpp->addr_id));
431
432 return 1;
433 }
434
435 static int
436 mp_fail_print(netdissect_options *ndo,
437 const u_char *opt, u_int opt_len, u_char flags _U_)
438 {
439 if (opt_len != 12)
440 return 0;
441
442 ND_PRINT(" seq %" PRIu64, GET_BE_U_8(opt + 4));
443 return 1;
444 }
445
446 static int
447 mp_fast_close_print(netdissect_options *ndo,
448 const u_char *opt, u_int opt_len, u_char flags _U_)
449 {
450 if (opt_len != 12)
451 return 0;
452
453 ND_PRINT(" key 0x%" PRIx64, GET_BE_U_8(opt + 4));
454 return 1;
455 }
456
457 static const struct {
458 const char *name;
459 int (*print)(netdissect_options *, const u_char *, u_int, u_char);
460 } mptcp_options[] = {
461 { "capable", mp_capable_print },
462 { "join", mp_join_print },
463 { "dss", mp_dss_print },
464 { "add-addr", add_addr_print },
465 { "rem-addr", remove_addr_print },
466 { "prio", mp_prio_print },
467 { "fail", mp_fail_print },
468 { "fast-close", mp_fast_close_print },
469 { "unknown", dummy_print },
470 };
471
472 int
473 mptcp_print(netdissect_options *ndo,
474 const u_char *cp, u_int len, u_char flags)
475 {
476 const struct mptcp_option *opt;
477 u_int subtype;
478
479 ndo->ndo_protocol = "mptcp";
480 if (len < 3)
481 return 0;
482
483 opt = (const struct mptcp_option *) cp;
484 subtype = MPTCP_OPT_SUBTYPE(GET_U_1(opt->sub_etc));
485 subtype = ND_MIN(subtype, MPTCP_SUB_FCLOSE + 1);
486
487 ND_PRINT(" %u", len);
488
489 ND_PRINT(" %s", mptcp_options[subtype].name);
490 return mptcp_options[subtype].print(ndo, cp, len, flags);
491 }