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