1 /* 2 * Copyright (C) Arnaldo Carvalho de Melo 2004 3 * Copyright (C) Ian McDonald 2005 4 * Copyright (C) Yoshifumi Nishida 2005 5 * 6 * This software may be distributed either under the terms of the 7 * BSD-style license that accompanies tcpdump or the GNU GPL version 2 8 */ 9 10 #include <sys/cdefs.h> 11 #ifndef lint 12 __RCSID("$NetBSD: print-dccp.c,v 1.10 2024/09/02 16:15:31 christos Exp $"); 13 #endif 14 15 /* \summary: Datagram Congestion Control Protocol (DCCP) printer */ 16 17 /* specification: RFC 4340 */ 18 19 #include <config.h> 20 21 #include "netdissect-stdinc.h" 22 23 #include "netdissect.h" 24 #include "addrtoname.h" 25 #include "extract.h" 26 #include "ip.h" 27 #include "ip6.h" 28 #include "ipproto.h" 29 30 /* RFC4340: Datagram Congestion Control Protocol (DCCP) */ 31 32 /** 33 * struct dccp_hdr - generic part of DCCP packet header, with a 24-bit 34 * sequence number 35 * 36 * @dccph_sport - Relevant port on the endpoint that sent this packet 37 * @dccph_dport - Relevant port on the other endpoint 38 * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words 39 * @dccph_ccval - Used by the HC-Sender CCID 40 * @dccph_cscov - Parts of the packet that are covered by the Checksum field 41 * @dccph_checksum - Internet checksum, depends on dccph_cscov 42 * @dccph_x - 0 = 24 bit sequence number, 1 = 48 43 * @dccph_type - packet type, see DCCP_PKT_ prefixed macros 44 * @dccph_seq - 24-bit sequence number 45 */ 46 struct dccp_hdr { 47 nd_uint16_t dccph_sport, 48 dccph_dport; 49 nd_uint8_t dccph_doff; 50 nd_uint8_t dccph_ccval_cscov; 51 nd_uint16_t dccph_checksum; 52 nd_uint8_t dccph_xtr; 53 nd_uint24_t dccph_seq; 54 }; 55 56 /** 57 * struct dccp_hdr_ext - generic part of DCCP packet header, with a 48-bit 58 * sequence number 59 * 60 * @dccph_sport - Relevant port on the endpoint that sent this packet 61 * @dccph_dport - Relevant port on the other endpoint 62 * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words 63 * @dccph_ccval - Used by the HC-Sender CCID 64 * @dccph_cscov - Parts of the packet that are covered by the Checksum field 65 * @dccph_checksum - Internet checksum, depends on dccph_cscov 66 * @dccph_x - 0 = 24 bit sequence number, 1 = 48 67 * @dccph_type - packet type, see DCCP_PKT_ prefixed macros 68 * @dccph_seq - 48-bit sequence number 69 */ 70 struct dccp_hdr_ext { 71 nd_uint16_t dccph_sport, 72 dccph_dport; 73 nd_uint8_t dccph_doff; 74 nd_uint8_t dccph_ccval_cscov; 75 nd_uint16_t dccph_checksum; 76 nd_uint8_t dccph_xtr; 77 nd_uint8_t reserved; 78 nd_uint48_t dccph_seq; 79 }; 80 81 #define DCCPH_CCVAL(dh) ((GET_U_1((dh)->dccph_ccval_cscov) >> 4) & 0xF) 82 #define DCCPH_CSCOV(dh) (GET_U_1((dh)->dccph_ccval_cscov) & 0xF) 83 84 #define DCCPH_X(dh) (GET_U_1((dh)->dccph_xtr) & 1) 85 #define DCCPH_TYPE(dh) ((GET_U_1((dh)->dccph_xtr) >> 1) & 0xF) 86 87 /** 88 * struct dccp_hdr_request - Connection initiation request header 89 * 90 * @dccph_req_service - Service to which the client app wants to connect 91 */ 92 struct dccp_hdr_request { 93 nd_uint32_t dccph_req_service; 94 }; 95 96 /** 97 * struct dccp_hdr_response - Connection initiation response header 98 * 99 * @dccph_resp_ack - 48 bit ack number, contains GSR 100 * @dccph_resp_service - Echoes the Service Code on a received DCCP-Request 101 */ 102 struct dccp_hdr_response { 103 nd_uint64_t dccph_resp_ack; /* always 8 bytes, first 2 reserved */ 104 nd_uint32_t dccph_resp_service; 105 }; 106 107 /** 108 * struct dccp_hdr_reset - Unconditionally shut down a connection 109 * 110 * @dccph_resp_ack - 48 bit ack number 111 * @dccph_reset_service - Echoes the Service Code on a received DCCP-Request 112 */ 113 struct dccp_hdr_reset { 114 nd_uint64_t dccph_reset_ack; /* always 8 bytes, first 2 reserved */ 115 nd_uint8_t dccph_reset_code; 116 nd_uint8_t dccph_reset_data1; 117 nd_uint8_t dccph_reset_data2; 118 nd_uint8_t dccph_reset_data3; 119 }; 120 121 enum dccp_pkt_type { 122 DCCP_PKT_REQUEST = 0, 123 DCCP_PKT_RESPONSE, 124 DCCP_PKT_DATA, 125 DCCP_PKT_ACK, 126 DCCP_PKT_DATAACK, 127 DCCP_PKT_CLOSEREQ, 128 DCCP_PKT_CLOSE, 129 DCCP_PKT_RESET, 130 DCCP_PKT_SYNC, 131 DCCP_PKT_SYNCACK 132 }; 133 134 static const struct tok dccp_pkt_type_str[] = { 135 { DCCP_PKT_REQUEST, "DCCP-Request" }, 136 { DCCP_PKT_RESPONSE, "DCCP-Response" }, 137 { DCCP_PKT_DATA, "DCCP-Data" }, 138 { DCCP_PKT_ACK, "DCCP-Ack" }, 139 { DCCP_PKT_DATAACK, "DCCP-DataAck" }, 140 { DCCP_PKT_CLOSEREQ, "DCCP-CloseReq" }, 141 { DCCP_PKT_CLOSE, "DCCP-Close" }, 142 { DCCP_PKT_RESET, "DCCP-Reset" }, 143 { DCCP_PKT_SYNC, "DCCP-Sync" }, 144 { DCCP_PKT_SYNCACK, "DCCP-SyncAck" }, 145 { 0, NULL} 146 }; 147 148 enum dccp_reset_codes { 149 DCCP_RESET_CODE_UNSPECIFIED = 0, 150 DCCP_RESET_CODE_CLOSED, 151 DCCP_RESET_CODE_ABORTED, 152 DCCP_RESET_CODE_NO_CONNECTION, 153 DCCP_RESET_CODE_PACKET_ERROR, 154 DCCP_RESET_CODE_OPTION_ERROR, 155 DCCP_RESET_CODE_MANDATORY_ERROR, 156 DCCP_RESET_CODE_CONNECTION_REFUSED, 157 DCCP_RESET_CODE_BAD_SERVICE_CODE, 158 DCCP_RESET_CODE_TOO_BUSY, 159 DCCP_RESET_CODE_BAD_INIT_COOKIE, 160 DCCP_RESET_CODE_AGGRESSION_PENALTY, 161 __DCCP_RESET_CODE_LAST 162 }; 163 164 165 static const char *dccp_reset_codes[] = { 166 "unspecified", 167 "closed", 168 "aborted", 169 "no_connection", 170 "packet_error", 171 "option_error", 172 "mandatory_error", 173 "connection_refused", 174 "bad_service_code", 175 "too_busy", 176 "bad_init_cookie", 177 "aggression_penalty", 178 }; 179 180 static const char *dccp_feature_nums[] = { 181 "reserved", 182 "ccid", 183 "allow_short_seqno", 184 "sequence_window", 185 "ecn_incapable", 186 "ack_ratio", 187 "send_ack_vector", 188 "send_ndp_count", 189 "minimum checksum coverage", 190 "check data checksum", 191 }; 192 193 static u_int 194 dccp_csum_coverage(netdissect_options *ndo, 195 const struct dccp_hdr *dh, u_int len) 196 { 197 u_int cov; 198 199 if (DCCPH_CSCOV(dh) == 0) 200 return len; 201 cov = (GET_U_1(dh->dccph_doff) + DCCPH_CSCOV(dh) - 1) * sizeof(uint32_t); 202 return (cov > len)? len : cov; 203 } 204 205 static uint16_t dccp_cksum(netdissect_options *ndo, const struct ip *ip, 206 const struct dccp_hdr *dh, u_int len) 207 { 208 return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)dh, len, 209 dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP); 210 } 211 212 static uint16_t dccp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6, 213 const struct dccp_hdr *dh, u_int len) 214 { 215 return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)dh, len, 216 dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP); 217 } 218 219 static const char *dccp_reset_code(uint8_t code) 220 { 221 if (code >= __DCCP_RESET_CODE_LAST) 222 return "invalid"; 223 return dccp_reset_codes[code]; 224 } 225 226 static uint64_t 227 dccp_seqno(netdissect_options *ndo, const u_char *bp) 228 { 229 const struct dccp_hdr *dh = (const struct dccp_hdr *)bp; 230 uint64_t seqno; 231 232 if (DCCPH_X(dh) != 0) { 233 const struct dccp_hdr_ext *dhx = (const struct dccp_hdr_ext *)bp; 234 seqno = GET_BE_U_6(dhx->dccph_seq); 235 } else { 236 seqno = GET_BE_U_3(dh->dccph_seq); 237 } 238 239 return seqno; 240 } 241 242 static unsigned int 243 dccp_basic_hdr_len(netdissect_options *ndo, const struct dccp_hdr *dh) 244 { 245 return DCCPH_X(dh) ? sizeof(struct dccp_hdr_ext) : sizeof(struct dccp_hdr); 246 } 247 248 static void dccp_print_ack_no(netdissect_options *ndo, const u_char *bp) 249 { 250 const struct dccp_hdr *dh = (const struct dccp_hdr *)bp; 251 const u_char *ackp = bp + dccp_basic_hdr_len(ndo, dh); 252 uint64_t ackno; 253 254 if (DCCPH_X(dh) != 0) { 255 ackno = GET_BE_U_6(ackp + 2); 256 } else { 257 ackno = GET_BE_U_3(ackp + 1); 258 } 259 260 ND_PRINT("(ack=%" PRIu64 ") ", ackno); 261 } 262 263 static u_int dccp_print_option(netdissect_options *, const u_char *, u_int); 264 265 /** 266 * dccp_print - show dccp packet 267 * @bp - beginning of dccp packet 268 * @data2 - beginning of enclosing 269 * @len - length of ip packet 270 */ 271 void 272 dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2, 273 u_int len) 274 { 275 const struct dccp_hdr *dh; 276 const struct ip *ip; 277 const struct ip6_hdr *ip6; 278 const u_char *cp; 279 u_short sport, dport; 280 u_int hlen; 281 u_int fixed_hdrlen; 282 uint8_t dccph_type; 283 284 ndo->ndo_protocol = "dccp"; 285 dh = (const struct dccp_hdr *)bp; 286 287 ip = (const struct ip *)data2; 288 if (IP_V(ip) == 6) 289 ip6 = (const struct ip6_hdr *)data2; 290 else 291 ip6 = NULL; 292 293 /* make sure we have enough data to look at the X bit */ 294 cp = (const u_char *)(dh + 1); 295 if (cp > ndo->ndo_snapend) 296 goto trunc; 297 if (len < sizeof(struct dccp_hdr)) { 298 ND_PRINT("truncated-dccp - %zu bytes missing!", 299 sizeof(struct dccp_hdr) - len); 300 return; 301 } 302 303 /* get the length of the generic header */ 304 fixed_hdrlen = dccp_basic_hdr_len(ndo, dh); 305 if (len < fixed_hdrlen) { 306 ND_PRINT("truncated-dccp - %u bytes missing!", 307 fixed_hdrlen - len); 308 return; 309 } 310 ND_TCHECK_LEN(dh, fixed_hdrlen); 311 312 sport = GET_BE_U_2(dh->dccph_sport); 313 dport = GET_BE_U_2(dh->dccph_dport); 314 hlen = GET_U_1(dh->dccph_doff) * 4; 315 316 if (ip6) { 317 ND_PRINT("%s.%u > %s.%u: ", 318 GET_IP6ADDR_STRING(ip6->ip6_src), sport, 319 GET_IP6ADDR_STRING(ip6->ip6_dst), dport); 320 } else { 321 ND_PRINT("%s.%u > %s.%u: ", 322 GET_IPADDR_STRING(ip->ip_src), sport, 323 GET_IPADDR_STRING(ip->ip_dst), dport); 324 } 325 326 nd_print_protocol_caps(ndo); 327 328 if (ndo->ndo_qflag) { 329 ND_PRINT(" %u", len - hlen); 330 if (hlen > len) { 331 ND_PRINT(" [bad hdr length %u - too long, > %u]", 332 hlen, len); 333 } 334 return; 335 } 336 337 /* other variables in generic header */ 338 if (ndo->ndo_vflag) { 339 ND_PRINT(" (CCVal %u, CsCov %u", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh)); 340 } 341 342 /* checksum calculation */ 343 if (ndo->ndo_vflag && ND_TTEST_LEN(bp, len)) { 344 uint16_t sum = 0, dccp_sum; 345 346 dccp_sum = GET_BE_U_2(dh->dccph_checksum); 347 ND_PRINT(", cksum 0x%04x ", dccp_sum); 348 if (IP_V(ip) == 4) 349 sum = dccp_cksum(ndo, ip, dh, len); 350 else if (IP_V(ip) == 6) 351 sum = dccp6_cksum(ndo, ip6, dh, len); 352 if (sum != 0) 353 ND_PRINT("(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum)); 354 else 355 ND_PRINT("(correct)"); 356 } 357 358 if (ndo->ndo_vflag) 359 ND_PRINT(")"); 360 ND_PRINT(" "); 361 362 dccph_type = DCCPH_TYPE(dh); 363 switch (dccph_type) { 364 case DCCP_PKT_REQUEST: { 365 const struct dccp_hdr_request *dhr = 366 (const struct dccp_hdr_request *)(bp + fixed_hdrlen); 367 fixed_hdrlen += 4; 368 if (len < fixed_hdrlen) { 369 ND_PRINT("truncated-%s - %u bytes missing!", 370 tok2str(dccp_pkt_type_str, "", dccph_type), 371 fixed_hdrlen - len); 372 return; 373 } 374 ND_TCHECK_SIZE(dhr); 375 ND_PRINT("%s (service=%u) ", 376 tok2str(dccp_pkt_type_str, "", dccph_type), 377 GET_BE_U_4(dhr->dccph_req_service)); 378 break; 379 } 380 case DCCP_PKT_RESPONSE: { 381 const struct dccp_hdr_response *dhr = 382 (const struct dccp_hdr_response *)(bp + fixed_hdrlen); 383 fixed_hdrlen += 12; 384 if (len < fixed_hdrlen) { 385 ND_PRINT("truncated-%s - %u bytes missing!", 386 tok2str(dccp_pkt_type_str, "", dccph_type), 387 fixed_hdrlen - len); 388 return; 389 } 390 ND_TCHECK_SIZE(dhr); 391 ND_PRINT("%s (service=%u) ", 392 tok2str(dccp_pkt_type_str, "", dccph_type), 393 GET_BE_U_4(dhr->dccph_resp_service)); 394 break; 395 } 396 case DCCP_PKT_DATA: 397 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 398 break; 399 case DCCP_PKT_ACK: { 400 fixed_hdrlen += 8; 401 if (len < fixed_hdrlen) { 402 ND_PRINT("truncated-%s - %u bytes missing!", 403 tok2str(dccp_pkt_type_str, "", dccph_type), 404 fixed_hdrlen - len); 405 return; 406 } 407 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 408 break; 409 } 410 case DCCP_PKT_DATAACK: { 411 fixed_hdrlen += 8; 412 if (len < fixed_hdrlen) { 413 ND_PRINT("truncated-%s - %u bytes missing!", 414 tok2str(dccp_pkt_type_str, "", dccph_type), 415 fixed_hdrlen - len); 416 return; 417 } 418 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 419 break; 420 } 421 case DCCP_PKT_CLOSEREQ: 422 fixed_hdrlen += 8; 423 if (len < fixed_hdrlen) { 424 ND_PRINT("truncated-%s - %u bytes missing!", 425 tok2str(dccp_pkt_type_str, "", dccph_type), 426 fixed_hdrlen - len); 427 return; 428 } 429 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 430 break; 431 case DCCP_PKT_CLOSE: 432 fixed_hdrlen += 8; 433 if (len < fixed_hdrlen) { 434 ND_PRINT("truncated-%s - %u bytes missing!", 435 tok2str(dccp_pkt_type_str, "", dccph_type), 436 fixed_hdrlen - len); 437 return; 438 } 439 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 440 break; 441 case DCCP_PKT_RESET: { 442 const struct dccp_hdr_reset *dhr = 443 (const struct dccp_hdr_reset *)(bp + fixed_hdrlen); 444 fixed_hdrlen += 12; 445 if (len < fixed_hdrlen) { 446 ND_PRINT("truncated-%s - %u bytes missing!", 447 tok2str(dccp_pkt_type_str, "", dccph_type), 448 fixed_hdrlen - len); 449 return; 450 } 451 ND_TCHECK_SIZE(dhr); 452 ND_PRINT("%s (code=%s) ", 453 tok2str(dccp_pkt_type_str, "", dccph_type), 454 dccp_reset_code(GET_U_1(dhr->dccph_reset_code))); 455 break; 456 } 457 case DCCP_PKT_SYNC: 458 fixed_hdrlen += 8; 459 if (len < fixed_hdrlen) { 460 ND_PRINT("truncated-%s - %u bytes missing!", 461 tok2str(dccp_pkt_type_str, "", dccph_type), 462 fixed_hdrlen - len); 463 return; 464 } 465 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 466 break; 467 case DCCP_PKT_SYNCACK: 468 fixed_hdrlen += 8; 469 if (len < fixed_hdrlen) { 470 ND_PRINT("truncated-%s - %u bytes missing!", 471 tok2str(dccp_pkt_type_str, "", dccph_type), 472 fixed_hdrlen - len); 473 return; 474 } 475 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); 476 break; 477 default: 478 ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "unknown-type-%u", dccph_type)); 479 break; 480 } 481 482 if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) && 483 (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST)) 484 dccp_print_ack_no(ndo, bp); 485 486 if (ndo->ndo_vflag < 2) 487 return; 488 489 ND_PRINT("seq %" PRIu64, dccp_seqno(ndo, bp)); 490 491 /* process options */ 492 if (hlen > fixed_hdrlen){ 493 u_int optlen; 494 cp = bp + fixed_hdrlen; 495 ND_PRINT(" <"); 496 497 hlen -= fixed_hdrlen; 498 while(1){ 499 optlen = dccp_print_option(ndo, cp, hlen); 500 if (!optlen) 501 break; 502 if (hlen <= optlen) 503 break; 504 hlen -= optlen; 505 cp += optlen; 506 ND_PRINT(", "); 507 } 508 ND_PRINT(">"); 509 } 510 return; 511 trunc: 512 nd_print_trunc(ndo); 513 } 514 515 static const struct tok dccp_option_values[] = { 516 { 0, "nop" }, 517 { 1, "mandatory" }, 518 { 2, "slowreceiver" }, 519 { 32, "change_l" }, 520 { 33, "confirm_l" }, 521 { 34, "change_r" }, 522 { 35, "confirm_r" }, 523 { 36, "initcookie" }, 524 { 37, "ndp_count" }, 525 { 38, "ack_vector0" }, 526 { 39, "ack_vector1" }, 527 { 40, "data_dropped" }, 528 { 41, "timestamp" }, 529 { 42, "timestamp_echo" }, 530 { 43, "elapsed_time" }, 531 { 44, "data_checksum" }, 532 { 0, NULL } 533 }; 534 535 static u_int 536 dccp_print_option(netdissect_options *ndo, const u_char *option, u_int hlen) 537 { 538 uint8_t optlen, i; 539 540 if (GET_U_1(option) >= 32) { 541 optlen = GET_U_1(option + 1); 542 if (optlen < 2) { 543 if (GET_U_1(option) >= 128) 544 ND_PRINT("CCID option %u optlen too short", 545 GET_U_1(option)); 546 else 547 ND_PRINT("%s optlen too short", 548 tok2str(dccp_option_values, "Option %u", GET_U_1(option))); 549 return 0; 550 } 551 } else 552 optlen = 1; 553 554 if (hlen < optlen) { 555 if (GET_U_1(option) >= 128) 556 ND_PRINT("CCID option %u optlen goes past header length", 557 GET_U_1(option)); 558 else 559 ND_PRINT("%s optlen goes past header length", 560 tok2str(dccp_option_values, "Option %u", GET_U_1(option))); 561 return 0; 562 } 563 ND_TCHECK_LEN(option, optlen); 564 565 if (GET_U_1(option) >= 128) { 566 ND_PRINT("CCID option %u", GET_U_1(option)); 567 switch (optlen) { 568 case 4: 569 ND_PRINT(" %u", GET_BE_U_2(option + 2)); 570 break; 571 case 6: 572 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 573 break; 574 default: 575 break; 576 } 577 } else { 578 ND_PRINT("%s", 579 tok2str(dccp_option_values, "Option %u", GET_U_1(option))); 580 switch (GET_U_1(option)) { 581 case 32: 582 case 33: 583 case 34: 584 case 35: 585 if (optlen < 3) { 586 ND_PRINT(" optlen too short"); 587 return optlen; 588 } 589 if (GET_U_1(option + 2) < 10){ 590 ND_PRINT(" %s", 591 dccp_feature_nums[GET_U_1(option + 2)]); 592 for (i = 0; i < optlen - 3; i++) 593 ND_PRINT(" %u", 594 GET_U_1(option + 3 + i)); 595 } 596 break; 597 case 36: 598 if (optlen > 2) { 599 ND_PRINT(" 0x"); 600 for (i = 0; i < optlen - 2; i++) 601 ND_PRINT("%02x", 602 GET_U_1(option + 2 + i)); 603 } 604 break; 605 case 37: 606 for (i = 0; i < optlen - 2; i++) 607 ND_PRINT(" %u", GET_U_1(option + 2 + i)); 608 break; 609 case 38: 610 if (optlen > 2) { 611 ND_PRINT(" 0x"); 612 for (i = 0; i < optlen - 2; i++) 613 ND_PRINT("%02x", 614 GET_U_1(option + 2 + i)); 615 } 616 break; 617 case 39: 618 if (optlen > 2) { 619 ND_PRINT(" 0x"); 620 for (i = 0; i < optlen - 2; i++) 621 ND_PRINT("%02x", 622 GET_U_1(option + 2 + i)); 623 } 624 break; 625 case 40: 626 if (optlen > 2) { 627 ND_PRINT(" 0x"); 628 for (i = 0; i < optlen - 2; i++) 629 ND_PRINT("%02x", 630 GET_U_1(option + 2 + i)); 631 } 632 break; 633 case 41: 634 /* 635 * 13.1. Timestamp Option 636 * 637 * +--------+--------+--------+--------+--------+--------+ 638 * |00101001|00000110| Timestamp Value | 639 * +--------+--------+--------+--------+--------+--------+ 640 * Type=41 Length=6 641 */ 642 if (optlen == 6) 643 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 644 else 645 ND_PRINT(" [optlen != 6]"); 646 break; 647 case 42: 648 /* 649 * 13.3. Timestamp Echo Option 650 * 651 * +--------+--------+--------+--------+--------+--------+ 652 * |00101010|00000110| Timestamp Echo | 653 * +--------+--------+--------+--------+--------+--------+ 654 * Type=42 Len=6 655 * 656 * +--------+--------+------- ... -------+--------+--------+ 657 * |00101010|00001000| Timestamp Echo | Elapsed Time | 658 * +--------+--------+------- ... -------+--------+--------+ 659 * Type=42 Len=8 (4 bytes) 660 * 661 * +--------+--------+------- ... -------+------- ... -------+ 662 * |00101010|00001010| Timestamp Echo | Elapsed Time | 663 * +--------+--------+------- ... -------+------- ... -------+ 664 * Type=42 Len=10 (4 bytes) (4 bytes) 665 */ 666 switch (optlen) { 667 case 6: 668 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 669 break; 670 case 8: 671 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 672 ND_PRINT(" (elapsed time %u)", 673 GET_BE_U_2(option + 6)); 674 break; 675 case 10: 676 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 677 ND_PRINT(" (elapsed time %u)", 678 GET_BE_U_4(option + 6)); 679 break; 680 default: 681 ND_PRINT(" [optlen != 6 or 8 or 10]"); 682 break; 683 } 684 break; 685 case 43: 686 if (optlen == 6) 687 ND_PRINT(" %u", GET_BE_U_4(option + 2)); 688 else if (optlen == 4) 689 ND_PRINT(" %u", GET_BE_U_2(option + 2)); 690 else 691 ND_PRINT(" [optlen != 4 or 6]"); 692 break; 693 case 44: 694 if (optlen > 2) { 695 ND_PRINT(" "); 696 for (i = 0; i < optlen - 2; i++) 697 ND_PRINT("%02x", 698 GET_U_1(option + 2 + i)); 699 } 700 break; 701 } 702 } 703 704 return optlen; 705 trunc: 706 nd_print_trunc(ndo); 707 return 0; 708 } 709