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
60struct 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
68struct 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
81struct 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
103struct 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
116static 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
124struct 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
151struct 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
159struct 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
167struct 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
175struct 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
184static int
185dummy_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
191static int
192mp_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
225static int
226mp_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
265static int
266mp_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
356static int
357add_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
393static int
394remove_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
410static int
411mp_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
429static int
430mp_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
440static int
441mp_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
451static 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
466int
467mptcp_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