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