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