xref: /netbsd-src/external/bsd/tcpdump/dist/print-mptcp.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
1026d7285Schristos /**
2026d7285Schristos  * Copyright (c) 2012
3026d7285Schristos  *
4026d7285Schristos  * Gregory Detal <gregory.detal@uclouvain.be>
5026d7285Schristos  * Christoph Paasch <christoph.paasch@uclouvain.be>
6026d7285Schristos  *
7026d7285Schristos  * Redistribution and use in source and binary forms, with or without
8026d7285Schristos  * modification, are permitted provided that the following conditions
9026d7285Schristos  * are met:
10026d7285Schristos  *
11026d7285Schristos  * 1. Redistributions of source code must retain the above copyright
12026d7285Schristos  *    notice, this list of conditions and the following disclaimer.
13026d7285Schristos  *
14026d7285Schristos  * 2. Redistributions in binary form must reproduce the above copyright
15026d7285Schristos  *    notice, this list of conditions and the following disclaimer in the
16026d7285Schristos  *    documentation and/or other materials provided with the distribution.
17026d7285Schristos  *
18026d7285Schristos  * 3. Neither the name of the University nor of the Laboratory may be used
19026d7285Schristos  *    to endorse or promote products derived from this software without
20026d7285Schristos  *    specific prior written permission.
21026d7285Schristos  *
22026d7285Schristos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23026d7285Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24026d7285Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25026d7285Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26026d7285Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27026d7285Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28026d7285Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29026d7285Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30026d7285Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31026d7285Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32026d7285Schristos  * SUCH DAMAGE.
33026d7285Schristos  */
34026d7285Schristos 
35fdccd7e4Schristos #include <sys/cdefs.h>
36fdccd7e4Schristos #ifndef lint
37*26ba0b50Schristos __RCSID("$NetBSD: print-mptcp.c,v 1.6 2024/09/02 16:15:32 christos Exp $");
38fdccd7e4Schristos #endif
39fdccd7e4Schristos 
40dc860a36Sspz /* \summary: Multipath TCP (MPTCP) printer */
41dc860a36Sspz 
42dc860a36Sspz /* specification: RFC 6824 */
43dc860a36Sspz 
44c74ad251Schristos #include <config.h>
45026d7285Schristos 
46c74ad251Schristos #include "netdissect-stdinc.h"
47026d7285Schristos 
48784088dfSchristos #include "netdissect.h"
49026d7285Schristos #include "extract.h"
50026d7285Schristos #include "addrtoname.h"
51026d7285Schristos 
52026d7285Schristos #include "tcp.h"
53026d7285Schristos 
54c47fd378Schristos #define MPTCP_SUB_CAPABLE       0x0
55c47fd378Schristos #define MPTCP_SUB_JOIN          0x1
56c47fd378Schristos #define MPTCP_SUB_DSS           0x2
57c47fd378Schristos #define MPTCP_SUB_ADD_ADDR      0x3
58c47fd378Schristos #define MPTCP_SUB_REMOVE_ADDR   0x4
59c47fd378Schristos #define MPTCP_SUB_PRIO          0x5
60c47fd378Schristos #define MPTCP_SUB_FAIL          0x6
61c47fd378Schristos #define MPTCP_SUB_FCLOSE        0x7
62c47fd378Schristos 
63c47fd378Schristos struct mptcp_option {
64c74ad251Schristos         nd_uint8_t     kind;
65c74ad251Schristos         nd_uint8_t     len;
66c74ad251Schristos         nd_uint8_t     sub_etc;        /* subtype upper 4 bits, other stuff lower 4 bits */
67c47fd378Schristos };
68c47fd378Schristos 
69c47fd378Schristos #define MPTCP_OPT_SUBTYPE(sub_etc)      (((sub_etc) >> 4) & 0xF)
70c47fd378Schristos 
71c47fd378Schristos struct mp_capable {
72c74ad251Schristos         nd_uint8_t     kind;
73c74ad251Schristos         nd_uint8_t     len;
74c74ad251Schristos         nd_uint8_t     sub_ver;
75c74ad251Schristos         nd_uint8_t     flags;
76c74ad251Schristos         nd_uint64_t    sender_key;
77c74ad251Schristos         nd_uint64_t    receiver_key;
78c47fd378Schristos };
79c47fd378Schristos 
80c47fd378Schristos #define MP_CAPABLE_OPT_VERSION(sub_ver) (((sub_ver) >> 0) & 0xF)
81c47fd378Schristos #define MP_CAPABLE_C                    0x80
82c47fd378Schristos #define MP_CAPABLE_S                    0x01
83c47fd378Schristos 
84c47fd378Schristos struct mp_join {
85c74ad251Schristos         nd_uint8_t     kind;
86c74ad251Schristos         nd_uint8_t     len;
87c74ad251Schristos         nd_uint8_t     sub_b;
88c74ad251Schristos         nd_uint8_t     addr_id;
89c47fd378Schristos         union {
90c47fd378Schristos                 struct {
91c74ad251Schristos                         nd_uint32_t     token;
92c74ad251Schristos                         nd_uint32_t     nonce;
93c47fd378Schristos                 } syn;
94c47fd378Schristos                 struct {
95c74ad251Schristos                         nd_uint64_t     mac;
96c74ad251Schristos                         nd_uint32_t     nonce;
97c47fd378Schristos                 } synack;
98c47fd378Schristos                 struct {
99c74ad251Schristos                         nd_byte         mac[20];
100c47fd378Schristos                 } ack;
101c47fd378Schristos         } u;
102c47fd378Schristos };
103c47fd378Schristos 
104c47fd378Schristos #define MP_JOIN_B                       0x01
105c47fd378Schristos 
106c47fd378Schristos struct mp_dss {
107c74ad251Schristos         nd_uint8_t     kind;
108c74ad251Schristos         nd_uint8_t     len;
109c74ad251Schristos         nd_uint8_t     sub;
110c74ad251Schristos         nd_uint8_t     flags;
111c47fd378Schristos };
112c47fd378Schristos 
113c47fd378Schristos #define MP_DSS_F                        0x10
114c47fd378Schristos #define MP_DSS_m                        0x08
115c47fd378Schristos #define MP_DSS_M                        0x04
116c47fd378Schristos #define MP_DSS_a                        0x02
117c47fd378Schristos #define MP_DSS_A                        0x01
118c47fd378Schristos 
119c74ad251Schristos static const struct tok mptcp_addr_subecho_bits[] = {
120c74ad251Schristos         { 0x6, "v0-ip6" },
121c74ad251Schristos         { 0x4, "v0-ip4" },
122c74ad251Schristos         { 0x1, "v1-echo" },
123c74ad251Schristos         { 0x0, "v1" },
124c74ad251Schristos         { 0, NULL }
125c74ad251Schristos };
126c74ad251Schristos 
127c47fd378Schristos struct mp_add_addr {
128c74ad251Schristos         nd_uint8_t     kind;
129c74ad251Schristos         nd_uint8_t     len;
130c74ad251Schristos         nd_uint8_t     sub_echo;
131c74ad251Schristos         nd_uint8_t     addr_id;
132c47fd378Schristos         union {
133c47fd378Schristos                 struct {
134c74ad251Schristos                         nd_ipv4         addr;
135c74ad251Schristos                         nd_uint16_t     port;
136c74ad251Schristos                         nd_uint64_t     mac;
137c47fd378Schristos                 } v4;
138c47fd378Schristos                 struct {
139c74ad251Schristos                         nd_ipv4         addr;
140c74ad251Schristos                         nd_uint64_t     mac;
141c74ad251Schristos                 } v4np;
142c74ad251Schristos                 struct {
143c74ad251Schristos                         nd_ipv6         addr;
144c74ad251Schristos                         nd_uint16_t     port;
145c74ad251Schristos                         nd_uint64_t     mac;
146c47fd378Schristos                 } v6;
147c74ad251Schristos                 struct {
148c74ad251Schristos                         nd_ipv6         addr;
149c74ad251Schristos                         nd_uint64_t     mac;
150c74ad251Schristos                 } v6np;
151c47fd378Schristos         } u;
152c47fd378Schristos };
153c47fd378Schristos 
154c47fd378Schristos struct mp_remove_addr {
155c74ad251Schristos         nd_uint8_t     kind;
156c74ad251Schristos         nd_uint8_t     len;
157c74ad251Schristos         nd_uint8_t     sub;
158c47fd378Schristos         /* list of addr_id */
159c74ad251Schristos         nd_uint8_t     addrs_id[1];
160c47fd378Schristos };
161c47fd378Schristos 
162c47fd378Schristos struct mp_fail {
163c74ad251Schristos         nd_uint8_t     kind;
164c74ad251Schristos         nd_uint8_t     len;
165c74ad251Schristos         nd_uint8_t     sub;
166c74ad251Schristos         nd_uint8_t     resv;
167c74ad251Schristos         nd_uint64_t    data_seq;
168c47fd378Schristos };
169c47fd378Schristos 
170c47fd378Schristos struct mp_close {
171c74ad251Schristos         nd_uint8_t     kind;
172c74ad251Schristos         nd_uint8_t     len;
173c74ad251Schristos         nd_uint8_t     sub;
174c74ad251Schristos         nd_uint8_t     rsv;
175c74ad251Schristos         nd_byte        key[8];
176c47fd378Schristos };
177c47fd378Schristos 
178c47fd378Schristos struct mp_prio {
179c74ad251Schristos         nd_uint8_t     kind;
180c74ad251Schristos         nd_uint8_t     len;
181c74ad251Schristos         nd_uint8_t     sub_b;
182c74ad251Schristos         nd_uint8_t     addr_id;
183c47fd378Schristos };
184c47fd378Schristos 
185c47fd378Schristos #define MP_PRIO_B                       0x01
186c47fd378Schristos 
187c47fd378Schristos static int
188c47fd378Schristos dummy_print(netdissect_options *ndo _U_,
189c47fd378Schristos             const u_char *opt _U_, u_int opt_len _U_, u_char flags _U_)
190026d7285Schristos {
191026d7285Schristos         return 1;
192026d7285Schristos }
193026d7285Schristos 
194c47fd378Schristos static int
195c47fd378Schristos mp_capable_print(netdissect_options *ndo,
196c47fd378Schristos                  const u_char *opt, u_int opt_len, u_char flags)
197026d7285Schristos {
198784088dfSchristos         const struct mp_capable *mpc = (const struct mp_capable *) opt;
199c74ad251Schristos         uint8_t version;
200026d7285Schristos 
201c74ad251Schristos         if (!((opt_len == 12 || opt_len == 4) && flags & TH_SYN) &&
202c74ad251Schristos             !((opt_len == 20 || opt_len == 22) && (flags & (TH_SYN | TH_ACK)) ==
203c74ad251Schristos               TH_ACK))
204026d7285Schristos                 return 0;
205026d7285Schristos 
206c74ad251Schristos         version = MP_CAPABLE_OPT_VERSION(GET_U_1(mpc->sub_ver));
207c74ad251Schristos         switch (version) {
208c74ad251Schristos                 case 0: /* fall through */
209c74ad251Schristos                 case 1:
210c74ad251Schristos                         ND_PRINT(" v%u", version);
211c74ad251Schristos                         break;
212c74ad251Schristos                 default:
213c74ad251Schristos                         ND_PRINT(" Unknown Version (%u)", version);
214026d7285Schristos                         return 1;
215026d7285Schristos         }
216026d7285Schristos 
217c74ad251Schristos         if (GET_U_1(mpc->flags) & MP_CAPABLE_C)
218c74ad251Schristos                 ND_PRINT(" csum");
219c74ad251Schristos         if (opt_len == 12 || opt_len >= 20) {
220c74ad251Schristos                 ND_PRINT(" {0x%" PRIx64, GET_BE_U_8(mpc->sender_key));
221c74ad251Schristos                 if (opt_len >= 20)
222c74ad251Schristos                         ND_PRINT(",0x%" PRIx64, GET_BE_U_8(mpc->receiver_key));
223c74ad251Schristos                 ND_PRINT("}");
224c74ad251Schristos         }
225026d7285Schristos         return 1;
226026d7285Schristos }
227026d7285Schristos 
228c47fd378Schristos static int
229c47fd378Schristos mp_join_print(netdissect_options *ndo,
230c47fd378Schristos               const u_char *opt, u_int opt_len, u_char flags)
231026d7285Schristos {
232784088dfSchristos         const struct mp_join *mpj = (const struct mp_join *) opt;
233026d7285Schristos 
23472c96ff3Schristos         if (!(opt_len == 12 && (flags & TH_SYN)) &&
235026d7285Schristos             !(opt_len == 16 && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) &&
23672c96ff3Schristos             !(opt_len == 24 && (flags & TH_ACK)))
237026d7285Schristos                 return 0;
238026d7285Schristos 
239026d7285Schristos         if (opt_len != 24) {
240c74ad251Schristos                 if (GET_U_1(mpj->sub_b) & MP_JOIN_B)
241c74ad251Schristos                         ND_PRINT(" backup");
242c74ad251Schristos                 ND_PRINT(" id %u", GET_U_1(mpj->addr_id));
243026d7285Schristos         }
244026d7285Schristos 
245026d7285Schristos         switch (opt_len) {
246026d7285Schristos         case 12: /* SYN */
247c74ad251Schristos                 ND_PRINT(" token 0x%x" " nonce 0x%x",
248c74ad251Schristos                         GET_BE_U_4(mpj->u.syn.token),
249c74ad251Schristos                         GET_BE_U_4(mpj->u.syn.nonce));
250026d7285Schristos                 break;
251026d7285Schristos         case 16: /* SYN/ACK */
252c74ad251Schristos                 ND_PRINT(" hmac 0x%" PRIx64 " nonce 0x%x",
253c74ad251Schristos                         GET_BE_U_8(mpj->u.synack.mac),
254c74ad251Schristos                         GET_BE_U_4(mpj->u.synack.nonce));
255026d7285Schristos                 break;
256026d7285Schristos         case 24: {/* ACK */
257026d7285Schristos                 size_t i;
258c74ad251Schristos                 ND_PRINT(" hmac 0x");
259026d7285Schristos                 for (i = 0; i < sizeof(mpj->u.ack.mac); ++i)
260c74ad251Schristos                         ND_PRINT("%02x", mpj->u.ack.mac[i]);
261026d7285Schristos         }
262026d7285Schristos         default:
263026d7285Schristos                 break;
264026d7285Schristos         }
265026d7285Schristos         return 1;
266026d7285Schristos }
267026d7285Schristos 
268c47fd378Schristos static int
269c47fd378Schristos mp_dss_print(netdissect_options *ndo,
270c47fd378Schristos              const u_char *opt, u_int opt_len, u_char flags)
271026d7285Schristos {
272784088dfSchristos         const struct mp_dss *mdss = (const struct mp_dss *) opt;
273c74ad251Schristos         uint8_t mdss_flags;
274026d7285Schristos 
27572c96ff3Schristos         /* We need the flags, at a minimum. */
27672c96ff3Schristos         if (opt_len < 4)
27772c96ff3Schristos                 return 0;
27872c96ff3Schristos 
27972c96ff3Schristos         if (flags & TH_SYN)
280026d7285Schristos                 return 0;
281026d7285Schristos 
282c74ad251Schristos         mdss_flags = GET_U_1(mdss->flags);
283c74ad251Schristos         if (mdss_flags & MP_DSS_F)
284c74ad251Schristos                 ND_PRINT(" fin");
285026d7285Schristos 
286026d7285Schristos         opt += 4;
28772c96ff3Schristos         opt_len -= 4;
288c74ad251Schristos         if (mdss_flags & MP_DSS_A) {
28972c96ff3Schristos                 /* Ack present */
290c74ad251Schristos                 ND_PRINT(" ack ");
29172c96ff3Schristos                 /*
29272c96ff3Schristos                  * If the a flag is set, we have an 8-byte ack; if it's
29372c96ff3Schristos                  * clear, we have a 4-byte ack.
29472c96ff3Schristos                  */
295c74ad251Schristos                 if (mdss_flags & MP_DSS_a) {
29672c96ff3Schristos                         if (opt_len < 8)
29772c96ff3Schristos                                 return 0;
298c74ad251Schristos                         ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
299026d7285Schristos                         opt += 8;
30072c96ff3Schristos                         opt_len -= 8;
301026d7285Schristos                 } else {
30272c96ff3Schristos                         if (opt_len < 4)
30372c96ff3Schristos                                 return 0;
304c74ad251Schristos                         ND_PRINT("%u", GET_BE_U_4(opt));
305026d7285Schristos                         opt += 4;
30672c96ff3Schristos                         opt_len -= 4;
307026d7285Schristos                 }
308026d7285Schristos         }
309026d7285Schristos 
310c74ad251Schristos         if (mdss_flags & MP_DSS_M) {
31172c96ff3Schristos                 /*
31272c96ff3Schristos                  * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
31372c96ff3Schristos                  * Data-Level Length present, and Checksum possibly present.
31472c96ff3Schristos                  */
315c74ad251Schristos                 ND_PRINT(" seq ");
31672c96ff3Schristos                 /*
31772c96ff3Schristos                  * If the m flag is set, we have an 8-byte NDS; if it's clear,
31872c96ff3Schristos                  * we have a 4-byte DSN.
31972c96ff3Schristos                  */
320c74ad251Schristos                 if (mdss_flags & MP_DSS_m) {
32172c96ff3Schristos                         if (opt_len < 8)
32272c96ff3Schristos                                 return 0;
323c74ad251Schristos                         ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
324026d7285Schristos                         opt += 8;
32572c96ff3Schristos                         opt_len -= 8;
326026d7285Schristos                 } else {
32772c96ff3Schristos                         if (opt_len < 4)
32872c96ff3Schristos                                 return 0;
329c74ad251Schristos                         ND_PRINT("%u", GET_BE_U_4(opt));
330026d7285Schristos                         opt += 4;
33172c96ff3Schristos                         opt_len -= 4;
332026d7285Schristos                 }
33372c96ff3Schristos                 if (opt_len < 4)
33472c96ff3Schristos                         return 0;
335c74ad251Schristos                 ND_PRINT(" subseq %u", GET_BE_U_4(opt));
336026d7285Schristos                 opt += 4;
33772c96ff3Schristos                 opt_len -= 4;
33872c96ff3Schristos                 if (opt_len < 2)
33972c96ff3Schristos                         return 0;
340c74ad251Schristos                 ND_PRINT(" len %u", GET_BE_U_2(opt));
341026d7285Schristos                 opt += 2;
34272c96ff3Schristos                 opt_len -= 2;
343026d7285Schristos 
34472c96ff3Schristos                 /*
34572c96ff3Schristos                  * The Checksum is present only if negotiated.
34672c96ff3Schristos                  * If there are at least 2 bytes left, process the next 2
34772c96ff3Schristos                  * bytes as the Checksum.
34872c96ff3Schristos                  */
34972c96ff3Schristos                 if (opt_len >= 2) {
350c74ad251Schristos                         ND_PRINT(" csum 0x%x", GET_BE_U_2(opt));
35172c96ff3Schristos                         opt_len -= 2;
352026d7285Schristos                 }
35372c96ff3Schristos         }
35472c96ff3Schristos         if (opt_len != 0)
35572c96ff3Schristos                 return 0;
356026d7285Schristos         return 1;
357026d7285Schristos }
358026d7285Schristos 
359c47fd378Schristos static int
360c47fd378Schristos add_addr_print(netdissect_options *ndo,
361c47fd378Schristos                const u_char *opt, u_int opt_len, u_char flags _U_)
362026d7285Schristos {
363784088dfSchristos         const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
364026d7285Schristos 
365c74ad251Schristos         if (!(opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18 ||
366c74ad251Schristos             opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30))
367026d7285Schristos                 return 0;
368026d7285Schristos 
369c74ad251Schristos         ND_PRINT(" %s",
370c74ad251Schristos                  tok2str(mptcp_addr_subecho_bits, "[bad version/echo]",
371c74ad251Schristos                          GET_U_1(add_addr->sub_echo) & 0xF));
372c74ad251Schristos         ND_PRINT(" id %u", GET_U_1(add_addr->addr_id));
373c74ad251Schristos         if (opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18) {
374c74ad251Schristos                 ND_PRINT(" %s", GET_IPADDR_STRING(add_addr->u.v4.addr));
375c74ad251Schristos                 if (opt_len == 10 || opt_len == 18)
376c74ad251Schristos                         ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v4.port));
377c74ad251Schristos                 if (opt_len == 16)
378c74ad251Schristos                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4np.mac));
379c74ad251Schristos                 if (opt_len == 18)
380c74ad251Schristos                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4.mac));
381c74ad251Schristos         }
382c74ad251Schristos 
383c74ad251Schristos         if (opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30) {
384c74ad251Schristos                 ND_PRINT(" %s", GET_IP6ADDR_STRING(add_addr->u.v6.addr));
385c74ad251Schristos                 if (opt_len == 22 || opt_len == 30)
386c74ad251Schristos                         ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v6.port));
387c74ad251Schristos                 if (opt_len == 28)
388c74ad251Schristos                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6np.mac));
389c74ad251Schristos                 if (opt_len == 30)
390c74ad251Schristos                         ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6.mac));
391026d7285Schristos         }
392026d7285Schristos 
393026d7285Schristos         return 1;
394026d7285Schristos }
395026d7285Schristos 
396c47fd378Schristos static int
397c47fd378Schristos remove_addr_print(netdissect_options *ndo,
398c47fd378Schristos                   const u_char *opt, u_int opt_len, u_char flags _U_)
399026d7285Schristos {
400784088dfSchristos         const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
401c74ad251Schristos         u_int i;
402026d7285Schristos 
403026d7285Schristos         if (opt_len < 4)
404026d7285Schristos                 return 0;
405026d7285Schristos 
406026d7285Schristos         opt_len -= 3;
407c74ad251Schristos         ND_PRINT(" id");
408c74ad251Schristos         for (i = 0; i < opt_len; i++)
409c74ad251Schristos                 ND_PRINT(" %u", GET_U_1(remove_addr->addrs_id[i]));
410026d7285Schristos         return 1;
411026d7285Schristos }
412026d7285Schristos 
413c47fd378Schristos static int
414c47fd378Schristos mp_prio_print(netdissect_options *ndo,
415c47fd378Schristos               const u_char *opt, u_int opt_len, u_char flags _U_)
416026d7285Schristos {
417784088dfSchristos         const struct mp_prio *mpp = (const struct mp_prio *) opt;
418026d7285Schristos 
419026d7285Schristos         if (opt_len != 3 && opt_len != 4)
420026d7285Schristos                 return 0;
421026d7285Schristos 
422c74ad251Schristos         if (GET_U_1(mpp->sub_b) & MP_PRIO_B)
423c74ad251Schristos                 ND_PRINT(" backup");
424026d7285Schristos         else
425c74ad251Schristos                 ND_PRINT(" non-backup");
426026d7285Schristos         if (opt_len == 4)
427c74ad251Schristos                 ND_PRINT(" id %u", GET_U_1(mpp->addr_id));
428026d7285Schristos 
429026d7285Schristos         return 1;
430026d7285Schristos }
431026d7285Schristos 
432c47fd378Schristos static int
433c47fd378Schristos mp_fail_print(netdissect_options *ndo,
434c47fd378Schristos               const u_char *opt, u_int opt_len, u_char flags _U_)
435026d7285Schristos {
436026d7285Schristos         if (opt_len != 12)
437026d7285Schristos                 return 0;
438026d7285Schristos 
439c74ad251Schristos         ND_PRINT(" seq %" PRIu64, GET_BE_U_8(opt + 4));
440026d7285Schristos         return 1;
441026d7285Schristos }
442026d7285Schristos 
443c47fd378Schristos static int
444c47fd378Schristos mp_fast_close_print(netdissect_options *ndo,
445c47fd378Schristos                     const u_char *opt, u_int opt_len, u_char flags _U_)
446026d7285Schristos {
447026d7285Schristos         if (opt_len != 12)
448026d7285Schristos                 return 0;
449026d7285Schristos 
450c74ad251Schristos         ND_PRINT(" key 0x%" PRIx64, GET_BE_U_8(opt + 4));
451026d7285Schristos         return 1;
452026d7285Schristos }
453026d7285Schristos 
454c47fd378Schristos static const struct {
455026d7285Schristos         const char *name;
456c47fd378Schristos         int (*print)(netdissect_options *, const u_char *, u_int, u_char);
457026d7285Schristos } mptcp_options[] = {
458026d7285Schristos         { "capable",    mp_capable_print },
459026d7285Schristos         { "join",       mp_join_print },
460026d7285Schristos         { "dss",        mp_dss_print },
461026d7285Schristos         { "add-addr",   add_addr_print },
462026d7285Schristos         { "rem-addr",   remove_addr_print },
463026d7285Schristos         { "prio",       mp_prio_print },
464026d7285Schristos         { "fail",       mp_fail_print },
465026d7285Schristos         { "fast-close", mp_fast_close_print },
466026d7285Schristos         { "unknown",    dummy_print },
467026d7285Schristos };
468026d7285Schristos 
469c47fd378Schristos int
470c47fd378Schristos mptcp_print(netdissect_options *ndo,
471c47fd378Schristos             const u_char *cp, u_int len, u_char flags)
472026d7285Schristos {
473784088dfSchristos         const struct mptcp_option *opt;
474026d7285Schristos         u_int subtype;
475026d7285Schristos 
476c74ad251Schristos         ndo->ndo_protocol = "mptcp";
477026d7285Schristos         if (len < 3)
478026d7285Schristos                 return 0;
479026d7285Schristos 
480784088dfSchristos         opt = (const struct mptcp_option *) cp;
481c74ad251Schristos         subtype = MPTCP_OPT_SUBTYPE(GET_U_1(opt->sub_etc));
482c74ad251Schristos         subtype = ND_MIN(subtype, MPTCP_SUB_FCLOSE + 1);
483026d7285Schristos 
484c74ad251Schristos         ND_PRINT(" %u", len);
485c74ad251Schristos 
486c74ad251Schristos         ND_PRINT(" %s", mptcp_options[subtype].name);
487c47fd378Schristos         return mptcp_options[subtype].print(ndo, cp, len, flags);
488026d7285Schristos }
489