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