1 /* 2 * Copyright (c) 1998-2006 The TCPDUMP project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that: (1) source code 6 * distributions retain the above copyright notice and this paragraph 7 * in its entirety, and (2) distributions including binary code include 8 * the above copyright notice and this paragraph in its entirety in 9 * the documentation or other materials provided with the distribution. 10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 * FOR A PARTICULAR PURPOSE. 14 * 15 * Original code by Hannes Gredler (hannes@gredler.at) 16 */ 17 18 /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */ 19 20 #include <sys/cdefs.h> 21 #ifndef lint 22 __RCSID("$NetBSD: print-cfm.c,v 1.10 2023/08/17 20:19:40 christos Exp $"); 23 #endif 24 25 #ifdef HAVE_CONFIG_H 26 #include <config.h> 27 #endif 28 29 #include "netdissect-stdinc.h" 30 31 #include "netdissect.h" 32 #include "extract.h" 33 #include "addrtoname.h" 34 #include "oui.h" 35 #include "af.h" 36 37 38 struct cfm_common_header_t { 39 nd_uint8_t mdlevel_version; 40 nd_uint8_t opcode; 41 nd_uint8_t flags; 42 nd_uint8_t first_tlv_offset; 43 }; 44 45 #define CFM_VERSION 0 46 #define CFM_EXTRACT_VERSION(x) ((x)&0x1f) 47 #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5) 48 49 #define CFM_OPCODE_CCM 1 50 #define CFM_OPCODE_LBR 2 51 #define CFM_OPCODE_LBM 3 52 #define CFM_OPCODE_LTR 4 53 #define CFM_OPCODE_LTM 5 54 55 static const struct tok cfm_opcode_values[] = { 56 { CFM_OPCODE_CCM, "Continuity Check Message"}, 57 { CFM_OPCODE_LBR, "Loopback Reply"}, 58 { CFM_OPCODE_LBM, "Loopback Message"}, 59 { CFM_OPCODE_LTR, "Linktrace Reply"}, 60 { CFM_OPCODE_LTM, "Linktrace Message"}, 61 { 0, NULL} 62 }; 63 64 /* 65 * Message Formats. 66 */ 67 struct cfm_ccm_t { 68 nd_uint32_t sequence; 69 nd_uint16_t ma_epi; 70 nd_byte names[48]; 71 nd_byte itu_t_y_1731[16]; 72 }; 73 74 /* 75 * Timer Bases for the CCM Interval field. 76 * Expressed in units of seconds. 77 */ 78 static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f}; 79 #define CCM_INTERVAL_MIN_MULTIPLIER 3.25 80 #define CCM_INTERVAL_MAX_MULTIPLIER 3.5 81 82 #define CFM_CCM_RDI_FLAG 0x80 83 #define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07) 84 85 #define CFM_CCM_MD_FORMAT_8021 0 86 #define CFM_CCM_MD_FORMAT_NONE 1 87 #define CFM_CCM_MD_FORMAT_DNS 2 88 #define CFM_CCM_MD_FORMAT_MAC 3 89 #define CFM_CCM_MD_FORMAT_CHAR 4 90 91 static const struct tok cfm_md_nameformat_values[] = { 92 { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"}, 93 { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"}, 94 { CFM_CCM_MD_FORMAT_DNS, "DNS string"}, 95 { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"}, 96 { CFM_CCM_MD_FORMAT_CHAR, "Character string"}, 97 { 0, NULL} 98 }; 99 100 #define CFM_CCM_MA_FORMAT_8021 0 101 #define CFM_CCM_MA_FORMAT_VID 1 102 #define CFM_CCM_MA_FORMAT_CHAR 2 103 #define CFM_CCM_MA_FORMAT_INT 3 104 #define CFM_CCM_MA_FORMAT_VPN 4 105 106 static const struct tok cfm_ma_nameformat_values[] = { 107 { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"}, 108 { CFM_CCM_MA_FORMAT_VID, "Primary VID"}, 109 { CFM_CCM_MA_FORMAT_CHAR, "Character string"}, 110 { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"}, 111 { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"}, 112 { 0, NULL} 113 }; 114 115 struct cfm_lbm_t { 116 nd_uint32_t transaction_id; 117 }; 118 119 struct cfm_ltm_t { 120 nd_uint32_t transaction_id; 121 nd_uint8_t ttl; 122 nd_mac_addr original_mac; 123 nd_mac_addr target_mac; 124 }; 125 126 static const struct tok cfm_ltm_flag_values[] = { 127 { 0x80, "Use Forwarding-DB only"}, 128 { 0, NULL} 129 }; 130 131 struct cfm_ltr_t { 132 nd_uint32_t transaction_id; 133 nd_uint8_t ttl; 134 nd_uint8_t replay_action; 135 }; 136 137 static const struct tok cfm_ltr_flag_values[] = { 138 { 0x80, "UseFDB Only"}, 139 { 0x40, "FwdYes"}, 140 { 0x20, "Terminal MEP"}, 141 { 0, NULL} 142 }; 143 144 static const struct tok cfm_ltr_replay_action_values[] = { 145 { 1, "Exact Match"}, 146 { 2, "Filtering DB"}, 147 { 3, "MIP CCM DB"}, 148 { 0, NULL} 149 }; 150 151 152 #define CFM_TLV_END 0 153 #define CFM_TLV_SENDER_ID 1 154 #define CFM_TLV_PORT_STATUS 2 155 #define CFM_TLV_INTERFACE_STATUS 3 156 #define CFM_TLV_DATA 4 157 #define CFM_TLV_REPLY_INGRESS 5 158 #define CFM_TLV_REPLY_EGRESS 6 159 #define CFM_TLV_PRIVATE 31 160 161 static const struct tok cfm_tlv_values[] = { 162 { CFM_TLV_END, "End"}, 163 { CFM_TLV_SENDER_ID, "Sender ID"}, 164 { CFM_TLV_PORT_STATUS, "Port status"}, 165 { CFM_TLV_INTERFACE_STATUS, "Interface status"}, 166 { CFM_TLV_DATA, "Data"}, 167 { CFM_TLV_REPLY_INGRESS, "Reply Ingress"}, 168 { CFM_TLV_REPLY_EGRESS, "Reply Egress"}, 169 { CFM_TLV_PRIVATE, "Organization Specific"}, 170 { 0, NULL} 171 }; 172 173 /* 174 * TLVs 175 */ 176 177 struct cfm_tlv_header_t { 178 nd_uint8_t type; 179 nd_uint16_t length; 180 }; 181 182 /* FIXME define TLV formats */ 183 184 static const struct tok cfm_tlv_port_status_values[] = { 185 { 1, "Blocked"}, 186 { 2, "Up"}, 187 { 0, NULL} 188 }; 189 190 static const struct tok cfm_tlv_interface_status_values[] = { 191 { 1, "Up"}, 192 { 2, "Down"}, 193 { 3, "Testing"}, 194 { 5, "Dormant"}, 195 { 6, "not present"}, 196 { 7, "lower Layer down"}, 197 { 0, NULL} 198 }; 199 200 #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1 201 #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2 202 #define CFM_CHASSIS_ID_PORT_COMPONENT 3 203 #define CFM_CHASSIS_ID_MAC_ADDRESS 4 204 #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5 205 #define CFM_CHASSIS_ID_INTERFACE_NAME 6 206 #define CFM_CHASSIS_ID_LOCAL 7 207 208 static const struct tok cfm_tlv_senderid_chassisid_values[] = { 209 { 0, "Reserved"}, 210 { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"}, 211 { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"}, 212 { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"}, 213 { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"}, 214 { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"}, 215 { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"}, 216 { CFM_CHASSIS_ID_LOCAL, "Locally assigned"}, 217 { 0, NULL} 218 }; 219 220 221 static int 222 cfm_network_addr_print(netdissect_options *ndo, 223 const u_char *tptr, const u_int length) 224 { 225 u_int network_addr_type; 226 u_int hexdump = FALSE; 227 228 /* 229 * Although AFIs are typically 2 octets wide, 230 * 802.1ab specifies that this field width 231 * is only one octet. 232 */ 233 if (length < 1) { 234 ND_PRINT("\n\t Network Address Type (invalid, no data"); 235 return hexdump; 236 } 237 /* The calling function must make any due ND_TCHECK calls. */ 238 network_addr_type = GET_U_1(tptr); 239 ND_PRINT("\n\t Network Address Type %s (%u)", 240 tok2str(af_values, "Unknown", network_addr_type), 241 network_addr_type); 242 243 /* 244 * Resolve the passed in Address. 245 */ 246 switch(network_addr_type) { 247 case AFNUM_INET: 248 if (length != 1 + 4) { 249 ND_PRINT("(invalid IPv4 address length %u)", length - 1); 250 hexdump = TRUE; 251 break; 252 } 253 ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1)); 254 break; 255 256 case AFNUM_INET6: 257 if (length != 1 + 16) { 258 ND_PRINT("(invalid IPv6 address length %u)", length - 1); 259 hexdump = TRUE; 260 break; 261 } 262 ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1)); 263 break; 264 265 default: 266 hexdump = TRUE; 267 break; 268 } 269 270 return hexdump; 271 } 272 273 void 274 cfm_print(netdissect_options *ndo, 275 const u_char *pptr, u_int length) 276 { 277 const struct cfm_common_header_t *cfm_common_header; 278 uint8_t mdlevel_version, opcode, flags, first_tlv_offset; 279 const struct cfm_tlv_header_t *cfm_tlv_header; 280 const uint8_t *tptr, *tlv_ptr; 281 const uint8_t *namesp; 282 u_int names_data_remaining; 283 uint8_t md_nameformat, md_namelength; 284 const uint8_t *md_name; 285 uint8_t ma_nameformat, ma_namelength; 286 const uint8_t *ma_name; 287 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 288 289 290 union { 291 const struct cfm_ccm_t *cfm_ccm; 292 const struct cfm_lbm_t *cfm_lbm; 293 const struct cfm_ltm_t *cfm_ltm; 294 const struct cfm_ltr_t *cfm_ltr; 295 } msg_ptr; 296 297 ndo->ndo_protocol = "cfm"; 298 tptr=pptr; 299 cfm_common_header = (const struct cfm_common_header_t *)pptr; 300 if (length < sizeof(*cfm_common_header)) 301 goto tooshort; 302 ND_TCHECK_SIZE(cfm_common_header); 303 304 /* 305 * Sanity checking of the header. 306 */ 307 mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version); 308 if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) { 309 ND_PRINT("CFMv%u not supported, length %u", 310 CFM_EXTRACT_VERSION(mdlevel_version), length); 311 return; 312 } 313 314 opcode = GET_U_1(cfm_common_header->opcode); 315 ND_PRINT("CFMv%u %s, MD Level %u, length %u", 316 CFM_EXTRACT_VERSION(mdlevel_version), 317 tok2str(cfm_opcode_values, "unknown (%u)", opcode), 318 CFM_EXTRACT_MD_LEVEL(mdlevel_version), 319 length); 320 321 /* 322 * In non-verbose mode just print the opcode and md-level. 323 */ 324 if (ndo->ndo_vflag < 1) { 325 return; 326 } 327 328 flags = GET_U_1(cfm_common_header->flags); 329 first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset); 330 ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset); 331 332 tptr += sizeof(struct cfm_common_header_t); 333 tlen = length - sizeof(struct cfm_common_header_t); 334 335 /* 336 * Sanity check the first TLV offset. 337 */ 338 if (first_tlv_offset > tlen) { 339 ND_PRINT(" (too large, must be <= %u)", tlen); 340 return; 341 } 342 343 switch (opcode) { 344 case CFM_OPCODE_CCM: 345 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 346 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) { 347 ND_PRINT(" (too small 1, must be >= %zu)", 348 sizeof(*msg_ptr.cfm_ccm)); 349 return; 350 } 351 if (tlen < sizeof(*msg_ptr.cfm_ccm)) 352 goto tooshort; 353 ND_TCHECK_SIZE(msg_ptr.cfm_ccm); 354 355 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags); 356 ND_PRINT(", Flags [CCM Interval %u%s]", 357 ccm_interval, 358 flags & CFM_CCM_RDI_FLAG ? 359 ", RDI" : ""); 360 361 /* 362 * Resolve the CCM interval field. 363 */ 364 if (ccm_interval) { 365 ND_PRINT("\n\t CCM Interval %.3fs" 366 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs", 367 ccm_interval_base[ccm_interval], 368 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER, 369 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER); 370 } 371 372 ND_PRINT("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 373 GET_BE_U_4(msg_ptr.cfm_ccm->sequence), 374 GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi)); 375 376 namesp = msg_ptr.cfm_ccm->names; 377 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names); 378 379 /* 380 * Resolve the MD fields. 381 */ 382 md_nameformat = GET_U_1(namesp); 383 namesp++; 384 names_data_remaining--; /* We know this is != 0 */ 385 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 386 md_namelength = GET_U_1(namesp); 387 namesp++; 388 names_data_remaining--; /* We know this is !=0 */ 389 ND_PRINT("\n\t MD Name Format %s (%u), MD Name length %u", 390 tok2str(cfm_md_nameformat_values, "Unknown", 391 md_nameformat), 392 md_nameformat, 393 md_namelength); 394 395 /* 396 * -3 for the MA short name format and length and one byte 397 * of MA short name. 398 */ 399 if (md_namelength > names_data_remaining - 3) { 400 ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2); 401 return; 402 } 403 404 md_name = namesp; 405 ND_PRINT("\n\t MD Name: "); 406 switch (md_nameformat) { 407 case CFM_CCM_MD_FORMAT_DNS: 408 case CFM_CCM_MD_FORMAT_CHAR: 409 nd_printjnp(ndo, md_name, md_namelength); 410 break; 411 412 case CFM_CCM_MD_FORMAT_MAC: 413 if (md_namelength == MAC_ADDR_LEN) { 414 ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(md_name)); 415 } else { 416 ND_PRINT("\n\t MAC (length invalid)"); 417 } 418 break; 419 420 /* FIXME add printers for those MD formats - hexdump for now */ 421 case CFM_CCM_MA_FORMAT_8021: 422 default: 423 print_unknown_data(ndo, md_name, "\n\t ", 424 md_namelength); 425 } 426 namesp += md_namelength; 427 names_data_remaining -= md_namelength; 428 } else { 429 ND_PRINT("\n\t MD Name Format %s (%u)", 430 tok2str(cfm_md_nameformat_values, "Unknown", 431 md_nameformat), 432 md_nameformat); 433 } 434 435 436 /* 437 * Resolve the MA fields. 438 */ 439 ma_nameformat = GET_U_1(namesp); 440 namesp++; 441 names_data_remaining--; /* We know this is != 0 */ 442 ma_namelength = GET_U_1(namesp); 443 namesp++; 444 names_data_remaining--; /* We know this is != 0 */ 445 ND_PRINT("\n\t MA Name-Format %s (%u), MA name length %u", 446 tok2str(cfm_ma_nameformat_values, "Unknown", 447 ma_nameformat), 448 ma_nameformat, 449 ma_namelength); 450 451 if (ma_namelength > names_data_remaining) { 452 ND_PRINT(" (too large, must be <= %u)", names_data_remaining); 453 return; 454 } 455 456 ma_name = namesp; 457 ND_PRINT("\n\t MA Name: "); 458 switch (ma_nameformat) { 459 case CFM_CCM_MA_FORMAT_CHAR: 460 nd_printjnp(ndo, ma_name, ma_namelength); 461 break; 462 463 /* FIXME add printers for those MA formats - hexdump for now */ 464 case CFM_CCM_MA_FORMAT_8021: 465 case CFM_CCM_MA_FORMAT_VID: 466 case CFM_CCM_MA_FORMAT_INT: 467 case CFM_CCM_MA_FORMAT_VPN: 468 default: 469 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength); 470 } 471 break; 472 473 case CFM_OPCODE_LTM: 474 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 475 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) { 476 ND_PRINT(" (too small 4, must be >= %zu)", 477 sizeof(*msg_ptr.cfm_ltm)); 478 return; 479 } 480 if (tlen < sizeof(*msg_ptr.cfm_ltm)) 481 goto tooshort; 482 ND_TCHECK_SIZE(msg_ptr.cfm_ltm); 483 484 ND_PRINT(", Flags [%s]", 485 bittok2str(cfm_ltm_flag_values, "none", flags)); 486 487 ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 488 GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id), 489 GET_U_1(msg_ptr.cfm_ltm->ttl)); 490 491 ND_PRINT("\n\t Original-MAC %s, Target-MAC %s", 492 GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac), 493 GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac)); 494 break; 495 496 case CFM_OPCODE_LTR: 497 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 498 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) { 499 ND_PRINT(" (too small 5, must be >= %zu)", 500 sizeof(*msg_ptr.cfm_ltr)); 501 return; 502 } 503 if (tlen < sizeof(*msg_ptr.cfm_ltr)) 504 goto tooshort; 505 ND_TCHECK_SIZE(msg_ptr.cfm_ltr); 506 507 ND_PRINT(", Flags [%s]", 508 bittok2str(cfm_ltr_flag_values, "none", flags)); 509 510 ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 511 GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id), 512 GET_U_1(msg_ptr.cfm_ltr->ttl)); 513 514 ND_PRINT("\n\t Replay-Action %s (%u)", 515 tok2str(cfm_ltr_replay_action_values, 516 "Unknown", 517 GET_U_1(msg_ptr.cfm_ltr->replay_action)), 518 GET_U_1(msg_ptr.cfm_ltr->replay_action)); 519 break; 520 521 /* 522 * No message decoder yet. 523 * Hexdump everything up until the start of the TLVs 524 */ 525 case CFM_OPCODE_LBR: 526 case CFM_OPCODE_LBM: 527 default: 528 print_unknown_data(ndo, tptr, "\n\t ", 529 tlen - first_tlv_offset); 530 break; 531 } 532 533 tptr += first_tlv_offset; 534 tlen -= first_tlv_offset; 535 536 while (tlen > 0) { 537 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 538 539 /* Enough to read the tlv type ? */ 540 cfm_tlv_type = GET_U_1(cfm_tlv_header->type); 541 542 ND_PRINT("\n\t%s TLV (0x%02x)", 543 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type), 544 cfm_tlv_type); 545 546 if (cfm_tlv_type == CFM_TLV_END) { 547 /* Length is "Not present if the Type field is 0." */ 548 return; 549 } 550 551 /* do we have the full tlv header ? */ 552 if (tlen < sizeof(struct cfm_tlv_header_t)) 553 goto tooshort; 554 ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t)); 555 cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length); 556 557 ND_PRINT(", length %u", cfm_tlv_len); 558 559 tptr += sizeof(struct cfm_tlv_header_t); 560 tlen -= sizeof(struct cfm_tlv_header_t); 561 tlv_ptr = tptr; 562 563 /* do we have the full tlv ? */ 564 if (tlen < cfm_tlv_len) 565 goto tooshort; 566 ND_TCHECK_LEN(tptr, cfm_tlv_len); 567 hexdump = FALSE; 568 569 switch(cfm_tlv_type) { 570 case CFM_TLV_PORT_STATUS: 571 if (cfm_tlv_len < 1) { 572 ND_PRINT(" (too short, must be >= 1)"); 573 return; 574 } 575 ND_PRINT(", Status: %s (%u)", 576 tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)), 577 GET_U_1(tptr)); 578 break; 579 580 case CFM_TLV_INTERFACE_STATUS: 581 if (cfm_tlv_len < 1) { 582 ND_PRINT(" (too short, must be >= 1)"); 583 return; 584 } 585 ND_PRINT(", Status: %s (%u)", 586 tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)), 587 GET_U_1(tptr)); 588 break; 589 590 case CFM_TLV_PRIVATE: 591 if (cfm_tlv_len < 4) { 592 ND_PRINT(" (too short, must be >= 4)"); 593 return; 594 } 595 ND_PRINT(", Vendor: %s (%u), Sub-Type %u", 596 tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)), 597 GET_BE_U_3(tptr), 598 GET_U_1(tptr + 3)); 599 hexdump = TRUE; 600 break; 601 602 case CFM_TLV_SENDER_ID: 603 { 604 u_int chassis_id_type, chassis_id_length; 605 u_int mgmt_addr_length; 606 607 if (cfm_tlv_len < 1) { 608 ND_PRINT(" (too short, must be >= 1)"); 609 goto next_tlv; 610 } 611 612 /* 613 * Get the Chassis ID length and check it. 614 * IEEE 802.1Q-2014 Section 21.5.3.1 615 */ 616 chassis_id_length = GET_U_1(tptr); 617 tptr++; 618 tlen--; 619 cfm_tlv_len--; 620 621 if (chassis_id_length) { 622 /* 623 * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references 624 * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently 625 * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype 626 */ 627 if (cfm_tlv_len < 1) { 628 ND_PRINT("\n\t (TLV too short)"); 629 goto next_tlv; 630 } 631 chassis_id_type = GET_U_1(tptr); 632 cfm_tlv_len--; 633 ND_PRINT("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u", 634 tok2str(cfm_tlv_senderid_chassisid_values, 635 "Unknown", 636 chassis_id_type), 637 chassis_id_type, 638 chassis_id_length); 639 640 if (cfm_tlv_len < chassis_id_length) { 641 ND_PRINT("\n\t (TLV too short)"); 642 goto next_tlv; 643 } 644 645 /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */ 646 switch (chassis_id_type) { 647 case CFM_CHASSIS_ID_MAC_ADDRESS: 648 if (chassis_id_length != MAC_ADDR_LEN) { 649 ND_PRINT(" (invalid MAC address length)"); 650 hexdump = TRUE; 651 break; 652 } 653 ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(tptr + 1)); 654 break; 655 656 case CFM_CHASSIS_ID_NETWORK_ADDRESS: 657 hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length); 658 break; 659 660 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */ 661 case CFM_CHASSIS_ID_INTERFACE_ALIAS: 662 case CFM_CHASSIS_ID_LOCAL: 663 case CFM_CHASSIS_ID_CHASSIS_COMPONENT: 664 case CFM_CHASSIS_ID_PORT_COMPONENT: 665 nd_printjnp(ndo, tptr + 1, chassis_id_length); 666 break; 667 668 default: 669 hexdump = TRUE; 670 break; 671 } 672 cfm_tlv_len -= chassis_id_length; 673 674 tptr += 1 + chassis_id_length; 675 tlen -= 1 + chassis_id_length; 676 } 677 678 /* 679 * Check if there is a Management Address. 680 * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length 681 * This and all subsequent fields are not present if the TLV length 682 * allows only the above fields. 683 */ 684 if (cfm_tlv_len == 0) { 685 /* No, there isn't; we're done. */ 686 break; 687 } 688 689 /* Here mgmt_addr_length stands for the management domain length. */ 690 mgmt_addr_length = GET_U_1(tptr); 691 tptr++; 692 tlen--; 693 cfm_tlv_len--; 694 ND_PRINT("\n\t Management Address Domain Length %u", mgmt_addr_length); 695 if (mgmt_addr_length) { 696 /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */ 697 if (cfm_tlv_len < mgmt_addr_length) { 698 ND_PRINT("\n\t (TLV too short)"); 699 goto next_tlv; 700 } 701 cfm_tlv_len -= mgmt_addr_length; 702 /* 703 * XXX - this is an OID; print it as such. 704 */ 705 hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length); 706 tptr += mgmt_addr_length; 707 tlen -= mgmt_addr_length; 708 709 /* 710 * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length 711 * This field is present if Management Address Domain Length is not 0. 712 */ 713 if (cfm_tlv_len < 1) { 714 ND_PRINT(" (Management Address Length is missing)"); 715 hexdump = TRUE; 716 break; 717 } 718 719 /* Here mgmt_addr_length stands for the management address length. */ 720 mgmt_addr_length = GET_U_1(tptr); 721 tptr++; 722 tlen--; 723 cfm_tlv_len--; 724 ND_PRINT("\n\t Management Address Length %u", mgmt_addr_length); 725 if (mgmt_addr_length) { 726 /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */ 727 if (cfm_tlv_len < mgmt_addr_length) { 728 ND_PRINT("\n\t (TLV too short)"); 729 return; 730 } 731 cfm_tlv_len -= mgmt_addr_length; 732 /* 733 * XXX - this is a TransportDomain; print it as such. 734 */ 735 hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length); 736 tptr += mgmt_addr_length; 737 tlen -= mgmt_addr_length; 738 } 739 } 740 break; 741 } 742 743 /* 744 * FIXME those are the defined TLVs that lack a decoder 745 * you are welcome to contribute code ;-) 746 */ 747 748 case CFM_TLV_DATA: 749 case CFM_TLV_REPLY_INGRESS: 750 case CFM_TLV_REPLY_EGRESS: 751 default: 752 hexdump = TRUE; 753 break; 754 } 755 /* do we want to see an additional hexdump ? */ 756 if (hexdump || ndo->ndo_vflag > 1) 757 print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len); 758 759 next_tlv: 760 tptr+=cfm_tlv_len; 761 tlen-=cfm_tlv_len; 762 } 763 return; 764 765 tooshort: 766 ND_PRINT("\n\t\t packet is too short"); 767 return; 768 769 trunc: 770 nd_print_trunc(ndo); 771 } 772