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