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