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