xref: /freebsd/contrib/tcpdump/print-mptcp.c (revision cbd30a72ca196976c1c700400ecd424baa1b9c16)
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 u_int mp_dss_len(const  struct mp_dss *m, int csum)
240 {
241         u_int len;
242 
243         len = 4;
244         if (m->flags & MP_DSS_A) {
245                 /* Ack present - 4 or 8 octets */
246                 len += (m->flags & MP_DSS_a) ? 8 : 4;
247         }
248         if (m->flags & MP_DSS_M) {
249                 /*
250                  * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
251                  * Data-Level Length present, and Checksum possibly present.
252                  * All but the Checksum are 10 bytes if the m flag is
253                  * clear (4-byte DSN) and 14 bytes if the m flag is set
254                  * (8-byte DSN).
255                  */
256                 len += (m->flags & MP_DSS_m) ? 14 : 10;
257 
258                 /*
259                  * The Checksum is present only if negotiated.
260                  */
261                 if (csum)
262                         len += 2;
263 	}
264 	return len;
265 }
266 
267 static int
268 mp_dss_print(netdissect_options *ndo,
269              const u_char *opt, u_int opt_len, u_char flags)
270 {
271         const struct mp_dss *mdss = (const struct mp_dss *) opt;
272 
273         if ((opt_len != mp_dss_len(mdss, 1) &&
274              opt_len != mp_dss_len(mdss, 0)) || flags & TH_SYN)
275                 return 0;
276 
277         if (mdss->flags & MP_DSS_F)
278                 ND_PRINT((ndo, " fin"));
279 
280         opt += 4;
281         if (mdss->flags & MP_DSS_A) {
282                 ND_PRINT((ndo, " ack "));
283                 if (mdss->flags & MP_DSS_a) {
284                         ND_PRINT((ndo, "%" PRIu64, EXTRACT_64BITS(opt)));
285                         opt += 8;
286                 } else {
287                         ND_PRINT((ndo, "%u", EXTRACT_32BITS(opt)));
288                         opt += 4;
289                 }
290         }
291 
292         if (mdss->flags & MP_DSS_M) {
293                 ND_PRINT((ndo, " seq "));
294                 if (mdss->flags & MP_DSS_m) {
295                         ND_PRINT((ndo, "%" PRIu64, EXTRACT_64BITS(opt)));
296                         opt += 8;
297                 } else {
298                         ND_PRINT((ndo, "%u", EXTRACT_32BITS(opt)));
299                         opt += 4;
300                 }
301                 ND_PRINT((ndo, " subseq %u", EXTRACT_32BITS(opt)));
302                 opt += 4;
303                 ND_PRINT((ndo, " len %u", EXTRACT_16BITS(opt)));
304                 opt += 2;
305 
306                 if (opt_len == mp_dss_len(mdss, 1))
307                         ND_PRINT((ndo, " csum 0x%x", EXTRACT_16BITS(opt)));
308         }
309         return 1;
310 }
311 
312 static int
313 add_addr_print(netdissect_options *ndo,
314                const u_char *opt, u_int opt_len, u_char flags _U_)
315 {
316         const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
317         u_int ipver = MP_ADD_ADDR_IPVER(add_addr->sub_ipver);
318 
319         if (!((opt_len == 8 || opt_len == 10) && ipver == 4) &&
320             !((opt_len == 20 || opt_len == 22) && ipver == 6))
321                 return 0;
322 
323         ND_PRINT((ndo, " id %u", add_addr->addr_id));
324         switch (ipver) {
325         case 4:
326                 ND_PRINT((ndo, " %s", ipaddr_string(ndo, add_addr->u.v4.addr)));
327                 if (opt_len == 10)
328                         ND_PRINT((ndo, ":%u", EXTRACT_16BITS(add_addr->u.v4.port)));
329                 break;
330         case 6:
331                 ND_PRINT((ndo, " %s", ip6addr_string(ndo, add_addr->u.v6.addr)));
332                 if (opt_len == 22)
333                         ND_PRINT((ndo, ":%u", EXTRACT_16BITS(add_addr->u.v6.port)));
334                 break;
335         default:
336                 return 0;
337         }
338 
339         return 1;
340 }
341 
342 static int
343 remove_addr_print(netdissect_options *ndo,
344                   const u_char *opt, u_int opt_len, u_char flags _U_)
345 {
346         const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
347         const uint8_t *addr_id = &remove_addr->addrs_id;
348 
349         if (opt_len < 4)
350                 return 0;
351 
352         opt_len -= 3;
353         ND_PRINT((ndo, " id"));
354         while (opt_len--)
355                 ND_PRINT((ndo, " %u", *addr_id++));
356         return 1;
357 }
358 
359 static int
360 mp_prio_print(netdissect_options *ndo,
361               const u_char *opt, u_int opt_len, u_char flags _U_)
362 {
363         const struct mp_prio *mpp = (const struct mp_prio *) opt;
364 
365         if (opt_len != 3 && opt_len != 4)
366                 return 0;
367 
368         if (mpp->sub_b & MP_PRIO_B)
369                 ND_PRINT((ndo, " backup"));
370         else
371                 ND_PRINT((ndo, " non-backup"));
372         if (opt_len == 4)
373                 ND_PRINT((ndo, " id %u", mpp->addr_id));
374 
375         return 1;
376 }
377 
378 static int
379 mp_fail_print(netdissect_options *ndo,
380               const u_char *opt, u_int opt_len, u_char flags _U_)
381 {
382         if (opt_len != 12)
383                 return 0;
384 
385         ND_PRINT((ndo, " seq %" PRIu64, EXTRACT_64BITS(opt + 4)));
386         return 1;
387 }
388 
389 static int
390 mp_fast_close_print(netdissect_options *ndo,
391                     const u_char *opt, u_int opt_len, u_char flags _U_)
392 {
393         if (opt_len != 12)
394                 return 0;
395 
396         ND_PRINT((ndo, " key 0x%" PRIx64, EXTRACT_64BITS(opt + 4)));
397         return 1;
398 }
399 
400 static const struct {
401         const char *name;
402         int (*print)(netdissect_options *, const u_char *, u_int, u_char);
403 } mptcp_options[] = {
404         { "capable", mp_capable_print},
405         { "join",       mp_join_print },
406         { "dss",        mp_dss_print },
407         { "add-addr",   add_addr_print },
408         { "rem-addr",   remove_addr_print },
409         { "prio",       mp_prio_print },
410         { "fail",       mp_fail_print },
411         { "fast-close", mp_fast_close_print },
412         { "unknown",    dummy_print },
413 };
414 
415 int
416 mptcp_print(netdissect_options *ndo,
417             const u_char *cp, u_int len, u_char flags)
418 {
419         const struct mptcp_option *opt;
420         u_int subtype;
421 
422         if (len < 3)
423                 return 0;
424 
425         opt = (const struct mptcp_option *) cp;
426         subtype = min(MPTCP_OPT_SUBTYPE(opt->sub_etc), MPTCP_SUB_FCLOSE + 1);
427 
428         ND_PRINT((ndo, " %s", mptcp_options[subtype].name));
429         return mptcp_options[subtype].print(ndo, cp, len, flags);
430 }
431