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