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