xref: /freebsd/contrib/tcpdump/print-mptcp.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 };
76 
77 #define MP_CAPABLE_OPT_VERSION(sub_ver) (((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         uint8_t version;
197 
198         if (!((opt_len == 12 || opt_len == 4) && flags & TH_SYN) &&
199             !((opt_len == 20 || opt_len == 22) && (flags & (TH_SYN | TH_ACK)) ==
200               TH_ACK))
201                 return 0;
202 
203         version = MP_CAPABLE_OPT_VERSION(GET_U_1(mpc->sub_ver));
204         switch (version) {
205                 case 0: /* fall through */
206                 case 1:
207                         ND_PRINT(" v%u", version);
208                         break;
209                 default:
210                         ND_PRINT(" Unknown Version (%u)", version);
211                         return 1;
212         }
213 
214         if (GET_U_1(mpc->flags) & MP_CAPABLE_C)
215                 ND_PRINT(" csum");
216         if (opt_len == 12 || opt_len >= 20) {
217                 ND_PRINT(" {0x%" PRIx64, GET_BE_U_8(mpc->sender_key));
218                 if (opt_len >= 20)
219                         ND_PRINT(",0x%" PRIx64, GET_BE_U_8(mpc->receiver_key));
220                 ND_PRINT("}");
221         }
222         return 1;
223 }
224 
225 static int
226 mp_join_print(netdissect_options *ndo,
227               const u_char *opt, u_int opt_len, u_char flags)
228 {
229         const struct mp_join *mpj = (const struct mp_join *) opt;
230 
231         if (!(opt_len == 12 && (flags & TH_SYN)) &&
232             !(opt_len == 16 && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) &&
233             !(opt_len == 24 && (flags & TH_ACK)))
234                 return 0;
235 
236         if (opt_len != 24) {
237                 if (GET_U_1(mpj->sub_b) & MP_JOIN_B)
238                         ND_PRINT(" backup");
239                 ND_PRINT(" id %u", GET_U_1(mpj->addr_id));
240         }
241 
242         switch (opt_len) {
243         case 12: /* SYN */
244                 ND_PRINT(" token 0x%x" " nonce 0x%x",
245                         GET_BE_U_4(mpj->u.syn.token),
246                         GET_BE_U_4(mpj->u.syn.nonce));
247                 break;
248         case 16: /* SYN/ACK */
249                 ND_PRINT(" hmac 0x%" PRIx64 " nonce 0x%x",
250                         GET_BE_U_8(mpj->u.synack.mac),
251                         GET_BE_U_4(mpj->u.synack.nonce));
252                 break;
253         case 24: {/* ACK */
254                 size_t i;
255                 ND_PRINT(" hmac 0x");
256                 for (i = 0; i < sizeof(mpj->u.ack.mac); ++i)
257                         ND_PRINT("%02x", mpj->u.ack.mac[i]);
258         }
259         default:
260                 break;
261         }
262         return 1;
263 }
264 
265 static int
266 mp_dss_print(netdissect_options *ndo,
267              const u_char *opt, u_int opt_len, u_char flags)
268 {
269         const struct mp_dss *mdss = (const struct mp_dss *) opt;
270         uint8_t mdss_flags;
271 
272         /* We need the flags, at a minimum. */
273         if (opt_len < 4)
274                 return 0;
275 
276         if (flags & TH_SYN)
277                 return 0;
278 
279         mdss_flags = GET_U_1(mdss->flags);
280         if (mdss_flags & MP_DSS_F)
281                 ND_PRINT(" fin");
282 
283         opt += 4;
284         opt_len -= 4;
285         if (mdss_flags & MP_DSS_A) {
286                 /* Ack present */
287                 ND_PRINT(" ack ");
288                 /*
289                  * If the a flag is set, we have an 8-byte ack; if it's
290                  * clear, we have a 4-byte ack.
291                  */
292                 if (mdss_flags & MP_DSS_a) {
293                         if (opt_len < 8)
294                                 return 0;
295                         ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
296                         opt += 8;
297                         opt_len -= 8;
298                 } else {
299                         if (opt_len < 4)
300                                 return 0;
301                         ND_PRINT("%u", GET_BE_U_4(opt));
302                         opt += 4;
303                         opt_len -= 4;
304                 }
305         }
306 
307         if (mdss_flags & MP_DSS_M) {
308                 /*
309                  * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
310                  * Data-Level Length present, and Checksum possibly present.
311                  */
312                 ND_PRINT(" seq ");
313                 /*
314                  * If the m flag is set, we have an 8-byte NDS; if it's clear,
315                  * we have a 4-byte DSN.
316                  */
317                 if (mdss_flags & MP_DSS_m) {
318                         if (opt_len < 8)
319                                 return 0;
320                         ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
321                         opt += 8;
322                         opt_len -= 8;
323                 } else {
324                         if (opt_len < 4)
325                                 return 0;
326                         ND_PRINT("%u", GET_BE_U_4(opt));
327                         opt += 4;
328                         opt_len -= 4;
329                 }
330                 if (opt_len < 4)
331                         return 0;
332                 ND_PRINT(" subseq %u", GET_BE_U_4(opt));
333                 opt += 4;
334                 opt_len -= 4;
335                 if (opt_len < 2)
336                         return 0;
337                 ND_PRINT(" len %u", GET_BE_U_2(opt));
338                 opt += 2;
339                 opt_len -= 2;
340 
341                 /*
342                  * The Checksum is present only if negotiated.
343                  * If there are at least 2 bytes left, process the next 2
344                  * bytes as the Checksum.
345                  */
346                 if (opt_len >= 2) {
347                         ND_PRINT(" csum 0x%x", GET_BE_U_2(opt));
348                         opt_len -= 2;
349                 }
350         }
351         if (opt_len != 0)
352                 return 0;
353         return 1;
354 }
355 
356 static int
357 add_addr_print(netdissect_options *ndo,
358                const u_char *opt, u_int opt_len, u_char flags _U_)
359 {
360         const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
361 
362         if (!(opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18 ||
363             opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30))
364                 return 0;
365 
366         ND_PRINT(" %s",
367                  tok2str(mptcp_addr_subecho_bits, "[bad version/echo]",
368                          GET_U_1(add_addr->sub_echo) & 0xF));
369         ND_PRINT(" id %u", GET_U_1(add_addr->addr_id));
370         if (opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18) {
371                 ND_PRINT(" %s", GET_IPADDR_STRING(add_addr->u.v4.addr));
372                 if (opt_len == 10 || opt_len == 18)
373                         ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v4.port));
374                 if (opt_len == 16)
375                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4np.mac));
376                 if (opt_len == 18)
377                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4.mac));
378         }
379 
380         if (opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30) {
381                 ND_PRINT(" %s", GET_IP6ADDR_STRING(add_addr->u.v6.addr));
382                 if (opt_len == 22 || opt_len == 30)
383                         ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v6.port));
384                 if (opt_len == 28)
385                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6np.mac));
386                 if (opt_len == 30)
387                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6.mac));
388         }
389 
390         return 1;
391 }
392 
393 static int
394 remove_addr_print(netdissect_options *ndo,
395                   const u_char *opt, u_int opt_len, u_char flags _U_)
396 {
397         const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
398         u_int i;
399 
400         if (opt_len < 4)
401                 return 0;
402 
403         opt_len -= 3;
404         ND_PRINT(" id");
405         for (i = 0; i < opt_len; i++)
406                 ND_PRINT(" %u", GET_U_1(remove_addr->addrs_id[i]));
407         return 1;
408 }
409 
410 static int
411 mp_prio_print(netdissect_options *ndo,
412               const u_char *opt, u_int opt_len, u_char flags _U_)
413 {
414         const struct mp_prio *mpp = (const struct mp_prio *) opt;
415 
416         if (opt_len != 3 && opt_len != 4)
417                 return 0;
418 
419         if (GET_U_1(mpp->sub_b) & MP_PRIO_B)
420                 ND_PRINT(" backup");
421         else
422                 ND_PRINT(" non-backup");
423         if (opt_len == 4)
424                 ND_PRINT(" id %u", GET_U_1(mpp->addr_id));
425 
426         return 1;
427 }
428 
429 static int
430 mp_fail_print(netdissect_options *ndo,
431               const u_char *opt, u_int opt_len, u_char flags _U_)
432 {
433         if (opt_len != 12)
434                 return 0;
435 
436         ND_PRINT(" seq %" PRIu64, GET_BE_U_8(opt + 4));
437         return 1;
438 }
439 
440 static int
441 mp_fast_close_print(netdissect_options *ndo,
442                     const u_char *opt, u_int opt_len, u_char flags _U_)
443 {
444         if (opt_len != 12)
445                 return 0;
446 
447         ND_PRINT(" key 0x%" PRIx64, GET_BE_U_8(opt + 4));
448         return 1;
449 }
450 
451 static const struct {
452         const char *name;
453         int (*print)(netdissect_options *, const u_char *, u_int, u_char);
454 } mptcp_options[] = {
455         { "capable",    mp_capable_print },
456         { "join",       mp_join_print },
457         { "dss",        mp_dss_print },
458         { "add-addr",   add_addr_print },
459         { "rem-addr",   remove_addr_print },
460         { "prio",       mp_prio_print },
461         { "fail",       mp_fail_print },
462         { "fast-close", mp_fast_close_print },
463         { "unknown",    dummy_print },
464 };
465 
466 int
467 mptcp_print(netdissect_options *ndo,
468             const u_char *cp, u_int len, u_char flags)
469 {
470         const struct mptcp_option *opt;
471         u_int subtype;
472 
473         ndo->ndo_protocol = "mptcp";
474         if (len < 3)
475                 return 0;
476 
477         opt = (const struct mptcp_option *) cp;
478         subtype = MPTCP_OPT_SUBTYPE(GET_U_1(opt->sub_etc));
479         subtype = ND_MIN(subtype, MPTCP_SUB_FCLOSE + 1);
480 
481         ND_PRINT(" %u", len);
482 
483         ND_PRINT(" %s", mptcp_options[subtype].name);
484         return mptcp_options[subtype].print(ndo, cp, len, flags);
485 }
486