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 <stdio.h> 27 28 #include "netdissect.h" 29 #include "extract.h" 30 #include "ether.h" 31 #include "addrtoname.h" 32 #include "oui.h" 33 #include "af.h" 34 35 struct cfm_common_header_t { 36 uint8_t mdlevel_version; 37 uint8_t opcode; 38 uint8_t flags; 39 uint8_t first_tlv_offset; 40 }; 41 42 #define CFM_VERSION 0 43 #define CFM_EXTRACT_VERSION(x) (((x)&0x1f)) 44 #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5) 45 46 #define CFM_OPCODE_CCM 1 47 #define CFM_OPCODE_LBR 2 48 #define CFM_OPCODE_LBM 3 49 #define CFM_OPCODE_LTR 4 50 #define CFM_OPCODE_LTM 5 51 52 static const struct tok cfm_opcode_values[] = { 53 { CFM_OPCODE_CCM, "Continouity Check Message"}, 54 { CFM_OPCODE_LBR, "Loopback Reply"}, 55 { CFM_OPCODE_LBM, "Loopback Message"}, 56 { CFM_OPCODE_LTR, "Linktrace Reply"}, 57 { CFM_OPCODE_LTM, "Linktrace Message"}, 58 { 0, NULL} 59 }; 60 61 /* 62 * Message Formats. 63 */ 64 struct cfm_ccm_t { 65 uint8_t sequence[4]; 66 uint8_t ma_epi[2]; 67 uint8_t names[48]; 68 uint8_t itu_t_y_1731[16]; 69 }; 70 71 /* 72 * Timer Bases for the CCM Interval field. 73 * Expressed in units of seconds. 74 */ 75 static const float ccm_interval_base[8] = {0, 0.003333, 0.01, 0.1, 1, 10, 60, 600}; 76 #define CCM_INTERVAL_MIN_MULTIPLIER 3.25 77 #define CCM_INTERVAL_MAX_MULTIPLIER 3.5 78 79 #define CFM_CCM_RDI_FLAG 0x80 80 #define CFM_EXTRACT_CCM_INTERVAL(x) (((x)&0x07)) 81 82 #define CFM_CCM_MD_FORMAT_8021 0 83 #define CFM_CCM_MD_FORMAT_NONE 1 84 #define CFM_CCM_MD_FORMAT_DNS 2 85 #define CFM_CCM_MD_FORMAT_MAC 3 86 #define CFM_CCM_MD_FORMAT_CHAR 4 87 88 static const struct tok cfm_md_nameformat_values[] = { 89 { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"}, 90 { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"}, 91 { CFM_CCM_MD_FORMAT_DNS, "DNS string"}, 92 { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"}, 93 { CFM_CCM_MD_FORMAT_CHAR, "Character string"}, 94 { 0, NULL} 95 }; 96 97 #define CFM_CCM_MA_FORMAT_8021 0 98 #define CFM_CCM_MA_FORMAT_VID 1 99 #define CFM_CCM_MA_FORMAT_CHAR 2 100 #define CFM_CCM_MA_FORMAT_INT 3 101 #define CFM_CCM_MA_FORMAT_VPN 4 102 103 static const struct tok cfm_ma_nameformat_values[] = { 104 { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"}, 105 { CFM_CCM_MA_FORMAT_VID, "Primary VID"}, 106 { CFM_CCM_MA_FORMAT_CHAR, "Character string"}, 107 { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"}, 108 { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"}, 109 { 0, NULL} 110 }; 111 112 struct cfm_lbm_t { 113 uint8_t transaction_id[4]; 114 }; 115 116 struct cfm_ltm_t { 117 uint8_t transaction_id[4]; 118 uint8_t ttl; 119 uint8_t original_mac[ETHER_ADDR_LEN]; 120 uint8_t target_mac[ETHER_ADDR_LEN]; 121 }; 122 123 static const struct tok cfm_ltm_flag_values[] = { 124 { 0x80, "Use Forwarding-DB only"}, 125 { 0, NULL} 126 }; 127 128 struct cfm_ltr_t { 129 uint8_t transaction_id[4]; 130 uint8_t ttl; 131 uint8_t replay_action; 132 }; 133 134 static const struct tok cfm_ltr_flag_values[] = { 135 { 0x80, "UseFDB Only"}, 136 { 0x40, "FwdYes"}, 137 { 0x20, "Terminal MEP"}, 138 { 0, NULL} 139 }; 140 141 static const struct tok cfm_ltr_replay_action_values[] = { 142 { 1, "Exact Match"}, 143 { 2, "Filtering DB"}, 144 { 3, "MIP CCM DB"}, 145 { 0, NULL} 146 }; 147 148 149 #define CFM_TLV_END 0 150 #define CFM_TLV_SENDER_ID 1 151 #define CFM_TLV_PORT_STATUS 2 152 #define CFM_TLV_INTERFACE_STATUS 3 153 #define CFM_TLV_DATA 4 154 #define CFM_TLV_REPLY_INGRESS 5 155 #define CFM_TLV_REPLY_EGRESS 6 156 #define CFM_TLV_PRIVATE 31 157 158 static const struct tok cfm_tlv_values[] = { 159 { CFM_TLV_END, "End"}, 160 { CFM_TLV_SENDER_ID, "Sender ID"}, 161 { CFM_TLV_PORT_STATUS, "Port status"}, 162 { CFM_TLV_INTERFACE_STATUS, "Interface status"}, 163 { CFM_TLV_DATA, "Data"}, 164 { CFM_TLV_REPLY_INGRESS, "Reply Ingress"}, 165 { CFM_TLV_REPLY_EGRESS, "Reply Egress"}, 166 { CFM_TLV_PRIVATE, "Organization Specific"}, 167 { 0, NULL} 168 }; 169 170 /* 171 * TLVs 172 */ 173 174 struct cfm_tlv_header_t { 175 uint8_t type; 176 uint8_t length[2]; 177 }; 178 179 /* FIXME define TLV formats */ 180 181 static const struct tok cfm_tlv_port_status_values[] = { 182 { 1, "Blocked"}, 183 { 2, "Up"}, 184 { 0, NULL} 185 }; 186 187 static const struct tok cfm_tlv_interface_status_values[] = { 188 { 1, "Up"}, 189 { 2, "Down"}, 190 { 3, "Testing"}, 191 { 5, "Dormant"}, 192 { 6, "not present"}, 193 { 7, "lower Layer down"}, 194 { 0, NULL} 195 }; 196 197 #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1 198 #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2 199 #define CFM_CHASSIS_ID_PORT_COMPONENT 3 200 #define CFM_CHASSIS_ID_MAC_ADDRESS 4 201 #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5 202 #define CFM_CHASSIS_ID_INTERFACE_NAME 6 203 #define CFM_CHASSIS_ID_LOCAL 7 204 205 static const struct tok cfm_tlv_senderid_chassisid_values[] = { 206 { 0, "Reserved"}, 207 { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"}, 208 { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"}, 209 { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"}, 210 { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"}, 211 { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"}, 212 { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"}, 213 { CFM_CHASSIS_ID_LOCAL, "Locally assigned"}, 214 { 0, NULL} 215 }; 216 217 218 static int 219 cfm_network_addr_print(netdissect_options *ndo, 220 register const u_char *tptr, const u_int length) 221 { 222 u_int network_addr_type; 223 u_int hexdump = FALSE; 224 225 /* 226 * Altough AFIs are tpically 2 octects wide, 227 * 802.1ab specifies that this field width 228 * is only once octet 229 */ 230 if (length < 1) { 231 ND_PRINT((ndo, "\n\t Network Address Type (invalid, no data")); 232 return hexdump; 233 } 234 /* The calling function must make any due ND_TCHECK calls. */ 235 network_addr_type = *tptr; 236 ND_PRINT((ndo, "\n\t Network Address Type %s (%u)", 237 tok2str(af_values, "Unknown", network_addr_type), 238 network_addr_type)); 239 240 /* 241 * Resolve the passed in Address. 242 */ 243 switch(network_addr_type) { 244 case AFNUM_INET: 245 if (length != 1 + 4) { 246 ND_PRINT((ndo, "(invalid IPv4 address length %u)", length - 1)); 247 hexdump = TRUE; 248 break; 249 } 250 ND_PRINT((ndo, ", %s", ipaddr_string(ndo, tptr + 1))); 251 break; 252 253 case AFNUM_INET6: 254 if (length != 1 + 16) { 255 ND_PRINT((ndo, "(invalid IPv6 address length %u)", length - 1)); 256 hexdump = TRUE; 257 break; 258 } 259 ND_PRINT((ndo, ", %s", ip6addr_string(ndo, tptr + 1))); 260 break; 261 262 default: 263 hexdump = TRUE; 264 break; 265 } 266 267 return hexdump; 268 } 269 270 void 271 cfm_print(netdissect_options *ndo, 272 register const u_char *pptr, register u_int length) 273 { 274 const struct cfm_common_header_t *cfm_common_header; 275 const struct cfm_tlv_header_t *cfm_tlv_header; 276 const uint8_t *tptr, *tlv_ptr; 277 const uint8_t *namesp; 278 u_int names_data_remaining; 279 uint8_t md_nameformat, md_namelength; 280 const uint8_t *md_name; 281 uint8_t ma_nameformat, ma_namelength; 282 const uint8_t *ma_name; 283 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 284 285 286 union { 287 const struct cfm_ccm_t *cfm_ccm; 288 const struct cfm_lbm_t *cfm_lbm; 289 const struct cfm_ltm_t *cfm_ltm; 290 const struct cfm_ltr_t *cfm_ltr; 291 } msg_ptr; 292 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(*cfm_common_header); 298 299 /* 300 * Sanity checking of the header. 301 */ 302 if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) { 303 ND_PRINT((ndo, "CFMv%u not supported, length %u", 304 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length)); 305 return; 306 } 307 308 ND_PRINT((ndo, "CFMv%u %s, MD Level %u, length %u", 309 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), 310 tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode), 311 CFM_EXTRACT_MD_LEVEL(cfm_common_header->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 ND_PRINT((ndo, "\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset)); 322 323 tptr += sizeof(const struct cfm_common_header_t); 324 tlen = length - sizeof(struct cfm_common_header_t); 325 326 /* 327 * Sanity check the first TLV offset. 328 */ 329 if (cfm_common_header->first_tlv_offset > tlen) { 330 ND_PRINT((ndo, " (too large, must be <= %u)", tlen)); 331 return; 332 } 333 334 switch (cfm_common_header->opcode) { 335 case CFM_OPCODE_CCM: 336 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 337 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) { 338 ND_PRINT((ndo, " (too small 1, must be >= %lu)", 339 (unsigned long) sizeof(*msg_ptr.cfm_ccm))); 340 return; 341 } 342 if (tlen < sizeof(*msg_ptr.cfm_ccm)) 343 goto tooshort; 344 ND_TCHECK(*msg_ptr.cfm_ccm); 345 346 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags); 347 ND_PRINT((ndo, ", Flags [CCM Interval %u%s]", 348 ccm_interval, 349 cfm_common_header->flags & CFM_CCM_RDI_FLAG ? 350 ", RDI" : "")); 351 352 /* 353 * Resolve the CCM interval field. 354 */ 355 if (ccm_interval) { 356 ND_PRINT((ndo, "\n\t CCM Interval %.3fs" 357 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs", 358 ccm_interval_base[ccm_interval], 359 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER, 360 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER)); 361 } 362 363 ND_PRINT((ndo, "\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 364 EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence), 365 EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi))); 366 367 namesp = msg_ptr.cfm_ccm->names; 368 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names); 369 370 /* 371 * Resolve the MD fields. 372 */ 373 md_nameformat = *namesp; 374 namesp++; 375 names_data_remaining--; /* We know this is != 0 */ 376 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 377 md_namelength = *namesp; 378 namesp++; 379 names_data_remaining--; /* We know this is !=0 */ 380 ND_PRINT((ndo, "\n\t MD Name Format %s (%u), MD Name length %u", 381 tok2str(cfm_md_nameformat_values, "Unknown", 382 md_nameformat), 383 md_nameformat, 384 md_namelength)); 385 386 /* 387 * -3 for the MA short name format and length and one byte 388 * of MA short name. 389 */ 390 if (md_namelength > names_data_remaining - 3) { 391 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining - 2)); 392 return; 393 } 394 395 md_name = namesp; 396 ND_PRINT((ndo, "\n\t MD Name: ")); 397 switch (md_nameformat) { 398 case CFM_CCM_MD_FORMAT_DNS: 399 case CFM_CCM_MD_FORMAT_CHAR: 400 safeputs(ndo, md_name, md_namelength); 401 break; 402 403 case CFM_CCM_MD_FORMAT_MAC: 404 if (md_namelength == 6) { 405 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, 406 md_name))); 407 } else { 408 ND_PRINT((ndo, "\n\t MAC (length invalid)")); 409 } 410 break; 411 412 /* FIXME add printers for those MD formats - hexdump for now */ 413 case CFM_CCM_MA_FORMAT_8021: 414 default: 415 print_unknown_data(ndo, md_name, "\n\t ", 416 md_namelength); 417 } 418 namesp += md_namelength; 419 names_data_remaining -= md_namelength; 420 } else { 421 ND_PRINT((ndo, "\n\t MD Name Format %s (%u)", 422 tok2str(cfm_md_nameformat_values, "Unknown", 423 md_nameformat), 424 md_nameformat)); 425 } 426 427 428 /* 429 * Resolve the MA fields. 430 */ 431 ma_nameformat = *namesp; 432 namesp++; 433 names_data_remaining--; /* We know this is != 0 */ 434 ma_namelength = *namesp; 435 namesp++; 436 names_data_remaining--; /* We know this is != 0 */ 437 ND_PRINT((ndo, "\n\t MA Name-Format %s (%u), MA name length %u", 438 tok2str(cfm_ma_nameformat_values, "Unknown", 439 ma_nameformat), 440 ma_nameformat, 441 ma_namelength)); 442 443 if (ma_namelength > names_data_remaining) { 444 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining)); 445 return; 446 } 447 448 ma_name = namesp; 449 ND_PRINT((ndo, "\n\t MA Name: ")); 450 switch (ma_nameformat) { 451 case CFM_CCM_MA_FORMAT_CHAR: 452 safeputs(ndo, ma_name, ma_namelength); 453 break; 454 455 /* FIXME add printers for those MA formats - hexdump for now */ 456 case CFM_CCM_MA_FORMAT_8021: 457 case CFM_CCM_MA_FORMAT_VID: 458 case CFM_CCM_MA_FORMAT_INT: 459 case CFM_CCM_MA_FORMAT_VPN: 460 default: 461 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength); 462 } 463 break; 464 465 case CFM_OPCODE_LTM: 466 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 467 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) { 468 ND_PRINT((ndo, " (too small 4, must be >= %lu)", 469 (unsigned long) sizeof(*msg_ptr.cfm_ltm))); 470 return; 471 } 472 if (tlen < sizeof(*msg_ptr.cfm_ltm)) 473 goto tooshort; 474 ND_TCHECK(*msg_ptr.cfm_ltm); 475 476 ND_PRINT((ndo, ", Flags [%s]", 477 bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags))); 478 479 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u", 480 EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id), 481 msg_ptr.cfm_ltm->ttl)); 482 483 ND_PRINT((ndo, "\n\t Original-MAC %s, Target-MAC %s", 484 etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac), 485 etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac))); 486 break; 487 488 case CFM_OPCODE_LTR: 489 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 490 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) { 491 ND_PRINT((ndo, " (too small 5, must be >= %lu)", 492 (unsigned long) sizeof(*msg_ptr.cfm_ltr))); 493 return; 494 } 495 if (tlen < sizeof(*msg_ptr.cfm_ltr)) 496 goto tooshort; 497 ND_TCHECK(*msg_ptr.cfm_ltr); 498 499 ND_PRINT((ndo, ", Flags [%s]", 500 bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags))); 501 502 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u", 503 EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id), 504 msg_ptr.cfm_ltr->ttl)); 505 506 ND_PRINT((ndo, "\n\t Replay-Action %s (%u)", 507 tok2str(cfm_ltr_replay_action_values, 508 "Unknown", 509 msg_ptr.cfm_ltr->replay_action), 510 msg_ptr.cfm_ltr->replay_action)); 511 break; 512 513 /* 514 * No message decoder yet. 515 * Hexdump everything up until the start of the TLVs 516 */ 517 case CFM_OPCODE_LBR: 518 case CFM_OPCODE_LBM: 519 default: 520 print_unknown_data(ndo, tptr, "\n\t ", 521 tlen - cfm_common_header->first_tlv_offset); 522 break; 523 } 524 525 tptr += cfm_common_header->first_tlv_offset; 526 tlen -= cfm_common_header->first_tlv_offset; 527 528 while (tlen > 0) { 529 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 530 531 /* Enough to read the tlv type ? */ 532 ND_TCHECK2(*tptr, 1); 533 cfm_tlv_type=cfm_tlv_header->type; 534 535 ND_PRINT((ndo, "\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_TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t)); 548 cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length); 549 550 ND_PRINT((ndo, ", 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_TCHECK2(*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((ndo, " (too short, must be >= 1)")); 566 return; 567 } 568 ND_PRINT((ndo, ", Status: %s (%u)", 569 tok2str(cfm_tlv_port_status_values, "Unknown", *tptr), 570 *tptr)); 571 break; 572 573 case CFM_TLV_INTERFACE_STATUS: 574 if (cfm_tlv_len < 1) { 575 ND_PRINT((ndo, " (too short, must be >= 1)")); 576 return; 577 } 578 ND_PRINT((ndo, ", Status: %s (%u)", 579 tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr), 580 *tptr)); 581 break; 582 583 case CFM_TLV_PRIVATE: 584 if (cfm_tlv_len < 4) { 585 ND_PRINT((ndo, " (too short, must be >= 4)")); 586 return; 587 } 588 ND_PRINT((ndo, ", Vendor: %s (%u), Sub-Type %u", 589 tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)), 590 EXTRACT_24BITS(tptr), 591 *(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((ndo, " (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 = *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((ndo, "\n\t (TLV too short)")); 622 goto next_tlv; 623 } 624 chassis_id_type = *tptr; 625 cfm_tlv_len--; 626 ND_PRINT((ndo, "\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((ndo, "\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 != ETHER_ADDR_LEN) { 642 ND_PRINT((ndo, " (invalid MAC address length)")); 643 hexdump = TRUE; 644 break; 645 } 646 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, 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 safeputs(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 = *tptr; 684 tptr++; 685 tlen--; 686 cfm_tlv_len--; 687 ND_PRINT((ndo, "\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((ndo, "\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((ndo, " (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 = *tptr; 714 tptr++; 715 tlen--; 716 cfm_tlv_len--; 717 ND_PRINT((ndo, "\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((ndo, "\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((ndo, "\n\t\t packet is too short")); 760 return; 761 762 trunc: 763 ND_PRINT((ndo, "\n\t\t packet exceeded snapshot")); 764 } 765