]> The Tcpdump Group git mirrors - tcpdump/blob - print-mptcp.c
Include <string.h> only if required.
[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) ((GET_U_1(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 };
76
77 #define MP_CAPABLE_OPT_VERSION(sub_ver) ((GET_U_1(sub_ver) >> 0) & 0xF)
78 #define MP_CAPABLE_C 0x80
79 #define MP_CAPABLE_S 0x01
80
81 struct mp_join {
82 nd_uint8_t kind;
83 nd_uint8_t len;
84 nd_uint8_t sub_b;
85 nd_uint8_t addr_id;
86 union {
87 struct {
88 nd_uint32_t token;
89 nd_uint32_t nonce;
90 } syn;
91 struct {
92 nd_uint64_t mac;
93 nd_uint32_t nonce;
94 } synack;
95 struct {
96 nd_byte mac[20];
97 } ack;
98 } u;
99 };
100
101 #define MP_JOIN_B 0x01
102
103 struct mp_dss {
104 nd_uint8_t kind;
105 nd_uint8_t len;
106 nd_uint8_t sub;
107 nd_uint8_t flags;
108 };
109
110 #define MP_DSS_F 0x10
111 #define MP_DSS_m 0x08
112 #define MP_DSS_M 0x04
113 #define MP_DSS_a 0x02
114 #define MP_DSS_A 0x01
115
116 static const struct tok mptcp_addr_subecho_bits[] = {
117 { 0x6, "v0-ip6" },
118 { 0x4, "v0-ip4" },
119 { 0x1, "v1-echo" },
120 { 0x0, "v1" },
121 { 0, NULL }
122 };
123
124 struct mp_add_addr {
125 nd_uint8_t kind;
126 nd_uint8_t len;
127 nd_uint8_t sub_echo;
128 nd_uint8_t addr_id;
129 union {
130 struct {
131 nd_ipv4 addr;
132 nd_uint16_t port;
133 nd_uint64_t mac;
134 } v4;
135 struct {
136 nd_ipv4 addr;
137 nd_uint64_t mac;
138 } v4np;
139 struct {
140 nd_ipv6 addr;
141 nd_uint16_t port;
142 nd_uint64_t mac;
143 } v6;
144 struct {
145 nd_ipv6 addr;
146 nd_uint64_t mac;
147 } v6np;
148 } u;
149 };
150
151 struct mp_remove_addr {
152 nd_uint8_t kind;
153 nd_uint8_t len;
154 nd_uint8_t sub;
155 /* list of addr_id */
156 nd_uint8_t addrs_id[1];
157 };
158
159 struct mp_fail {
160 nd_uint8_t kind;
161 nd_uint8_t len;
162 nd_uint8_t sub;
163 nd_uint8_t resv;
164 nd_uint64_t data_seq;
165 };
166
167 struct mp_close {
168 nd_uint8_t kind;
169 nd_uint8_t len;
170 nd_uint8_t sub;
171 nd_uint8_t rsv;
172 nd_byte key[8];
173 };
174
175 struct mp_prio {
176 nd_uint8_t kind;
177 nd_uint8_t len;
178 nd_uint8_t sub_b;
179 nd_uint8_t addr_id;
180 };
181
182 #define MP_PRIO_B 0x01
183
184 static int
185 dummy_print(netdissect_options *ndo _U_,
186 const u_char *opt _U_, u_int opt_len _U_, u_char flags _U_)
187 {
188 return 1;
189 }
190
191 static int
192 mp_capable_print(netdissect_options *ndo,
193 const u_char *opt, u_int opt_len, u_char flags)
194 {
195 const struct mp_capable *mpc = (const struct mp_capable *) opt;
196
197 if (!((opt_len == 12 || opt_len == 4) && flags & TH_SYN) &&
198 !((opt_len == 20 || opt_len == 22) && (flags & (TH_SYN | TH_ACK)) ==
199 TH_ACK))
200 return 0;
201
202 switch (MP_CAPABLE_OPT_VERSION(mpc->sub_ver)) {
203 case 0: /* fall through */
204 case 1:
205 ND_PRINT(" v%u", MP_CAPABLE_OPT_VERSION(mpc->sub_ver));
206 break;
207 default:
208 ND_PRINT(" Unknown Version (%u)",
209 MP_CAPABLE_OPT_VERSION(mpc->sub_ver));
210 return 1;
211 }
212
213 if (GET_U_1(mpc->flags) & MP_CAPABLE_C)
214 ND_PRINT(" csum");
215 if (opt_len == 12 || opt_len >= 20) {
216 ND_PRINT(" {0x%" PRIx64, GET_BE_U_8(mpc->sender_key));
217 if (opt_len >= 20)
218 ND_PRINT(",0x%" PRIx64, GET_BE_U_8(mpc->receiver_key));
219 ND_PRINT("}");
220 }
221 return 1;
222 }
223
224 static int
225 mp_join_print(netdissect_options *ndo,
226 const u_char *opt, u_int opt_len, u_char flags)
227 {
228 const struct mp_join *mpj = (const struct mp_join *) opt;
229
230 if (!(opt_len == 12 && (flags & TH_SYN)) &&
231 !(opt_len == 16 && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) &&
232 !(opt_len == 24 && (flags & TH_ACK)))
233 return 0;
234
235 if (opt_len != 24) {
236 if (GET_U_1(mpj->sub_b) & MP_JOIN_B)
237 ND_PRINT(" backup");
238 ND_PRINT(" id %u", GET_U_1(mpj->addr_id));
239 }
240
241 switch (opt_len) {
242 case 12: /* SYN */
243 ND_PRINT(" token 0x%x" " nonce 0x%x",
244 GET_BE_U_4(mpj->u.syn.token),
245 GET_BE_U_4(mpj->u.syn.nonce));
246 break;
247 case 16: /* SYN/ACK */
248 ND_PRINT(" hmac 0x%" PRIx64 " nonce 0x%x",
249 GET_BE_U_8(mpj->u.synack.mac),
250 GET_BE_U_4(mpj->u.synack.nonce));
251 break;
252 case 24: {/* ACK */
253 size_t i;
254 ND_PRINT(" hmac 0x");
255 for (i = 0; i < sizeof(mpj->u.ack.mac); ++i)
256 ND_PRINT("%02x", mpj->u.ack.mac[i]);
257 }
258 default:
259 break;
260 }
261 return 1;
262 }
263
264 static int
265 mp_dss_print(netdissect_options *ndo,
266 const u_char *opt, u_int opt_len, u_char flags)
267 {
268 const struct mp_dss *mdss = (const struct mp_dss *) opt;
269 uint8_t mdss_flags;
270
271 /* We need the flags, at a minimum. */
272 if (opt_len < 4)
273 return 0;
274
275 if (flags & TH_SYN)
276 return 0;
277
278 mdss_flags = GET_U_1(mdss->flags);
279 if (mdss_flags & MP_DSS_F)
280 ND_PRINT(" fin");
281
282 opt += 4;
283 opt_len -= 4;
284 if (mdss_flags & MP_DSS_A) {
285 /* Ack present */
286 ND_PRINT(" ack ");
287 /*
288 * If the a flag is set, we have an 8-byte ack; if it's
289 * clear, we have a 4-byte ack.
290 */
291 if (mdss_flags & MP_DSS_a) {
292 if (opt_len < 8)
293 return 0;
294 ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
295 opt += 8;
296 opt_len -= 8;
297 } else {
298 if (opt_len < 4)
299 return 0;
300 ND_PRINT("%u", GET_BE_U_4(opt));
301 opt += 4;
302 opt_len -= 4;
303 }
304 }
305
306 if (mdss_flags & MP_DSS_M) {
307 /*
308 * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
309 * Data-Level Length present, and Checksum possibly present.
310 */
311 ND_PRINT(" seq ");
312 /*
313 * If the m flag is set, we have an 8-byte NDS; if it's clear,
314 * we have a 4-byte DSN.
315 */
316 if (mdss_flags & MP_DSS_m) {
317 if (opt_len < 8)
318 return 0;
319 ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
320 opt += 8;
321 opt_len -= 8;
322 } else {
323 if (opt_len < 4)
324 return 0;
325 ND_PRINT("%u", GET_BE_U_4(opt));
326 opt += 4;
327 opt_len -= 4;
328 }
329 if (opt_len < 4)
330 return 0;
331 ND_PRINT(" subseq %u", GET_BE_U_4(opt));
332 opt += 4;
333 opt_len -= 4;
334 if (opt_len < 2)
335 return 0;
336 ND_PRINT(" len %u", GET_BE_U_2(opt));
337 opt += 2;
338 opt_len -= 2;
339
340 /*
341 * The Checksum is present only if negotiated.
342 * If there are at least 2 bytes left, process the next 2
343 * bytes as the Checksum.
344 */
345 if (opt_len >= 2) {
346 ND_PRINT(" csum 0x%x", GET_BE_U_2(opt));
347 opt_len -= 2;
348 }
349 }
350 if (opt_len != 0)
351 return 0;
352 return 1;
353 }
354
355 static int
356 add_addr_print(netdissect_options *ndo,
357 const u_char *opt, u_int opt_len, u_char flags _U_)
358 {
359 const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
360
361 if (!(opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18 ||
362 opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30))
363 return 0;
364
365 ND_PRINT(" %s",
366 tok2str(mptcp_addr_subecho_bits, "[bad version/echo]",
367 GET_U_1(add_addr->sub_echo) & 0xF));
368 ND_PRINT(" id %u", GET_U_1(add_addr->addr_id));
369 if (opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18) {
370 ND_PRINT(" %s", GET_IPADDR_STRING(add_addr->u.v4.addr));
371 if (opt_len == 10 || opt_len == 18)
372 ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v4.port));
373 if (opt_len == 16)
374 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4np.mac));
375 if (opt_len == 18)
376 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4.mac));
377 }
378
379 if (opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30) {
380 ND_PRINT(" %s", GET_IP6ADDR_STRING(add_addr->u.v6.addr));
381 if (opt_len == 22 || opt_len == 30)
382 ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v6.port));
383 if (opt_len == 28)
384 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6np.mac));
385 if (opt_len == 30)
386 ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6.mac));
387 }
388
389 return 1;
390 }
391
392 static int
393 remove_addr_print(netdissect_options *ndo,
394 const u_char *opt, u_int opt_len, u_char flags _U_)
395 {
396 const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
397 u_int i;
398
399 if (opt_len < 4)
400 return 0;
401
402 opt_len -= 3;
403 ND_PRINT(" id");
404 for (i = 0; i < opt_len; i++)
405 ND_PRINT(" %u", GET_U_1(remove_addr->addrs_id[i]));
406 return 1;
407 }
408
409 static int
410 mp_prio_print(netdissect_options *ndo,
411 const u_char *opt, u_int opt_len, u_char flags _U_)
412 {
413 const struct mp_prio *mpp = (const struct mp_prio *) opt;
414
415 if (opt_len != 3 && opt_len != 4)
416 return 0;
417
418 if (GET_U_1(mpp->sub_b) & MP_PRIO_B)
419 ND_PRINT(" backup");
420 else
421 ND_PRINT(" non-backup");
422 if (opt_len == 4)
423 ND_PRINT(" id %u", GET_U_1(mpp->addr_id));
424
425 return 1;
426 }
427
428 static int
429 mp_fail_print(netdissect_options *ndo,
430 const u_char *opt, u_int opt_len, u_char flags _U_)
431 {
432 if (opt_len != 12)
433 return 0;
434
435 ND_PRINT(" seq %" PRIu64, GET_BE_U_8(opt + 4));
436 return 1;
437 }
438
439 static int
440 mp_fast_close_print(netdissect_options *ndo,
441 const u_char *opt, u_int opt_len, u_char flags _U_)
442 {
443 if (opt_len != 12)
444 return 0;
445
446 ND_PRINT(" key 0x%" PRIx64, GET_BE_U_8(opt + 4));
447 return 1;
448 }
449
450 static const struct {
451 const char *name;
452 int (*print)(netdissect_options *, const u_char *, u_int, u_char);
453 } mptcp_options[] = {
454 { "capable", mp_capable_print},
455 { "join", mp_join_print },
456 { "dss", mp_dss_print },
457 { "add-addr", add_addr_print },
458 { "rem-addr", remove_addr_print },
459 { "prio", mp_prio_print },
460 { "fail", mp_fail_print },
461 { "fast-close", mp_fast_close_print },
462 { "unknown", dummy_print },
463 };
464
465 int
466 mptcp_print(netdissect_options *ndo,
467 const u_char *cp, u_int len, u_char flags)
468 {
469 const struct mptcp_option *opt;
470 u_int subtype;
471
472 ndo->ndo_protocol = "mptcp";
473 if (len < 3)
474 return 0;
475
476 opt = (const struct mptcp_option *) cp;
477 ND_TCHECK_SIZE(opt);
478 subtype = ND_MIN(MPTCP_OPT_SUBTYPE(opt->sub_etc), MPTCP_SUB_FCLOSE + 1);
479
480 ND_PRINT(" %s", mptcp_options[subtype].name);
481 return mptcp_options[subtype].print(ndo, cp, len, flags);
482
483 trunc:
484 nd_print_trunc(ndo);
485 return 0;
486 }