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