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@juniper.net) 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.8 2017/02/05 04:05:05 spz 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) 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 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 ND_PRINT((ndo, ", %s", ipaddr_string(ndo, tptr + 1))); 246 break; 247 248 case AFNUM_INET6: 249 ND_PRINT((ndo, ", %s", ip6addr_string(ndo, tptr + 1))); 250 break; 251 252 default: 253 hexdump = TRUE; 254 break; 255 } 256 257 return hexdump; 258 } 259 260 void 261 cfm_print(netdissect_options *ndo, 262 register const u_char *pptr, register u_int length) 263 { 264 const struct cfm_common_header_t *cfm_common_header; 265 const struct cfm_tlv_header_t *cfm_tlv_header; 266 const uint8_t *tptr, *tlv_ptr; 267 const uint8_t *namesp; 268 u_int names_data_remaining; 269 uint8_t md_nameformat, md_namelength; 270 const uint8_t *md_name; 271 uint8_t ma_nameformat, ma_namelength; 272 const uint8_t *ma_name; 273 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 274 275 276 union { 277 const struct cfm_ccm_t *cfm_ccm; 278 const struct cfm_lbm_t *cfm_lbm; 279 const struct cfm_ltm_t *cfm_ltm; 280 const struct cfm_ltr_t *cfm_ltr; 281 } msg_ptr; 282 283 tptr=pptr; 284 cfm_common_header = (const struct cfm_common_header_t *)pptr; 285 if (length < sizeof(*cfm_common_header)) 286 goto tooshort; 287 ND_TCHECK(*cfm_common_header); 288 289 /* 290 * Sanity checking of the header. 291 */ 292 if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) { 293 ND_PRINT((ndo, "CFMv%u not supported, length %u", 294 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length)); 295 return; 296 } 297 298 ND_PRINT((ndo, "CFMv%u %s, MD Level %u, length %u", 299 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), 300 tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode), 301 CFM_EXTRACT_MD_LEVEL(cfm_common_header->mdlevel_version), 302 length)); 303 304 /* 305 * In non-verbose mode just print the opcode and md-level. 306 */ 307 if (ndo->ndo_vflag < 1) { 308 return; 309 } 310 311 ND_PRINT((ndo, "\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset)); 312 313 tptr += sizeof(const struct cfm_common_header_t); 314 tlen = length - sizeof(struct cfm_common_header_t); 315 316 /* 317 * Sanity check the first TLV offset. 318 */ 319 if (cfm_common_header->first_tlv_offset > tlen) { 320 ND_PRINT((ndo, " (too large, must be <= %u)", tlen)); 321 return; 322 } 323 324 switch (cfm_common_header->opcode) { 325 case CFM_OPCODE_CCM: 326 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 327 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) { 328 ND_PRINT((ndo, " (too small 1, must be >= %lu)", 329 (unsigned long) sizeof(*msg_ptr.cfm_ccm))); 330 return; 331 } 332 if (tlen < sizeof(*msg_ptr.cfm_ccm)) 333 goto tooshort; 334 ND_TCHECK(*msg_ptr.cfm_ccm); 335 336 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags); 337 ND_PRINT((ndo, ", Flags [CCM Interval %u%s]", 338 ccm_interval, 339 cfm_common_header->flags & CFM_CCM_RDI_FLAG ? 340 ", RDI" : "")); 341 342 /* 343 * Resolve the CCM interval field. 344 */ 345 if (ccm_interval) { 346 ND_PRINT((ndo, "\n\t CCM Interval %.3fs" 347 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs", 348 ccm_interval_base[ccm_interval], 349 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER, 350 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER)); 351 } 352 353 ND_PRINT((ndo, "\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 354 EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence), 355 EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi))); 356 357 namesp = msg_ptr.cfm_ccm->names; 358 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names); 359 360 /* 361 * Resolve the MD fields. 362 */ 363 md_nameformat = *namesp; 364 namesp++; 365 names_data_remaining--; /* We know this is != 0 */ 366 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 367 md_namelength = *namesp; 368 namesp++; 369 names_data_remaining--; /* We know this is !=0 */ 370 ND_PRINT((ndo, "\n\t MD Name Format %s (%u), MD Name length %u", 371 tok2str(cfm_md_nameformat_values, "Unknown", 372 md_nameformat), 373 md_nameformat, 374 md_namelength)); 375 376 /* -2 for the MA short name format and length */ 377 if (md_namelength > names_data_remaining - 2) { 378 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining - 2)); 379 return; 380 } 381 382 md_name = namesp; 383 ND_PRINT((ndo, "\n\t MD Name: ")); 384 switch (md_nameformat) { 385 case CFM_CCM_MD_FORMAT_DNS: 386 case CFM_CCM_MD_FORMAT_CHAR: 387 safeputs(ndo, md_name, md_namelength); 388 break; 389 390 case CFM_CCM_MD_FORMAT_MAC: 391 if (md_namelength == 6) { 392 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, 393 md_name))); 394 } else { 395 ND_PRINT((ndo, "\n\t MAC (length invalid)")); 396 } 397 break; 398 399 /* FIXME add printers for those MD formats - hexdump for now */ 400 case CFM_CCM_MA_FORMAT_8021: 401 default: 402 print_unknown_data(ndo, md_name, "\n\t ", 403 md_namelength); 404 } 405 namesp += md_namelength; 406 names_data_remaining -= md_namelength; 407 } else { 408 ND_PRINT((ndo, "\n\t MD Name Format %s (%u)", 409 tok2str(cfm_md_nameformat_values, "Unknown", 410 md_nameformat), 411 md_nameformat)); 412 } 413 414 415 /* 416 * Resolve the MA fields. 417 */ 418 ma_nameformat = *namesp; 419 namesp++; 420 names_data_remaining--; /* We know this is != 0 */ 421 ma_namelength = *namesp; 422 namesp++; 423 names_data_remaining--; /* We know this is != 0 */ 424 ND_PRINT((ndo, "\n\t MA Name-Format %s (%u), MA name length %u", 425 tok2str(cfm_ma_nameformat_values, "Unknown", 426 ma_nameformat), 427 ma_nameformat, 428 ma_namelength)); 429 430 if (ma_namelength > names_data_remaining) { 431 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining)); 432 return; 433 } 434 435 ma_name = namesp; 436 ND_PRINT((ndo, "\n\t MA Name: ")); 437 switch (ma_nameformat) { 438 case CFM_CCM_MA_FORMAT_CHAR: 439 safeputs(ndo, ma_name, ma_namelength); 440 break; 441 442 /* FIXME add printers for those MA formats - hexdump for now */ 443 case CFM_CCM_MA_FORMAT_8021: 444 case CFM_CCM_MA_FORMAT_VID: 445 case CFM_CCM_MA_FORMAT_INT: 446 case CFM_CCM_MA_FORMAT_VPN: 447 default: 448 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength); 449 } 450 break; 451 452 case CFM_OPCODE_LTM: 453 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 454 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) { 455 ND_PRINT((ndo, " (too small 4, must be >= %lu)", 456 (unsigned long) sizeof(*msg_ptr.cfm_ltm))); 457 return; 458 } 459 if (tlen < sizeof(*msg_ptr.cfm_ltm)) 460 goto tooshort; 461 ND_TCHECK(*msg_ptr.cfm_ltm); 462 463 ND_PRINT((ndo, ", Flags [%s]", 464 bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags))); 465 466 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u", 467 EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id), 468 msg_ptr.cfm_ltm->ttl)); 469 470 ND_PRINT((ndo, "\n\t Original-MAC %s, Target-MAC %s", 471 etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac), 472 etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac))); 473 break; 474 475 case CFM_OPCODE_LTR: 476 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 477 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) { 478 ND_PRINT((ndo, " (too small 5, must be >= %lu)", 479 (unsigned long) sizeof(*msg_ptr.cfm_ltr))); 480 return; 481 } 482 if (tlen < sizeof(*msg_ptr.cfm_ltr)) 483 goto tooshort; 484 ND_TCHECK(*msg_ptr.cfm_ltr); 485 486 ND_PRINT((ndo, ", Flags [%s]", 487 bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags))); 488 489 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u", 490 EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id), 491 msg_ptr.cfm_ltr->ttl)); 492 493 ND_PRINT((ndo, "\n\t Replay-Action %s (%u)", 494 tok2str(cfm_ltr_replay_action_values, 495 "Unknown", 496 msg_ptr.cfm_ltr->replay_action), 497 msg_ptr.cfm_ltr->replay_action)); 498 break; 499 500 /* 501 * No message decoder yet. 502 * Hexdump everything up until the start of the TLVs 503 */ 504 case CFM_OPCODE_LBR: 505 case CFM_OPCODE_LBM: 506 default: 507 print_unknown_data(ndo, tptr, "\n\t ", 508 tlen - cfm_common_header->first_tlv_offset); 509 break; 510 } 511 512 tptr += cfm_common_header->first_tlv_offset; 513 tlen -= cfm_common_header->first_tlv_offset; 514 515 while (tlen > 0) { 516 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 517 518 /* Enough to read the tlv type ? */ 519 ND_TCHECK2(*tptr, 1); 520 cfm_tlv_type=cfm_tlv_header->type; 521 522 ND_PRINT((ndo, "\n\t%s TLV (0x%02x)", 523 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type), 524 cfm_tlv_type)); 525 526 if (cfm_tlv_type == CFM_TLV_END) { 527 /* Length is "Not present if the Type field is 0." */ 528 return; 529 } 530 531 /* do we have the full tlv header ? */ 532 if (tlen < sizeof(struct cfm_tlv_header_t)) 533 goto tooshort; 534 ND_TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t)); 535 cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length); 536 537 ND_PRINT((ndo, ", length %u", cfm_tlv_len)); 538 539 tptr += sizeof(struct cfm_tlv_header_t); 540 tlen -= sizeof(struct cfm_tlv_header_t); 541 tlv_ptr = tptr; 542 543 /* do we have the full tlv ? */ 544 if (tlen < cfm_tlv_len) 545 goto tooshort; 546 ND_TCHECK2(*tptr, cfm_tlv_len); 547 hexdump = FALSE; 548 549 switch(cfm_tlv_type) { 550 case CFM_TLV_PORT_STATUS: 551 if (cfm_tlv_len < 1) { 552 ND_PRINT((ndo, " (too short, must be >= 1)")); 553 return; 554 } 555 ND_PRINT((ndo, ", Status: %s (%u)", 556 tok2str(cfm_tlv_port_status_values, "Unknown", *tptr), 557 *tptr)); 558 break; 559 560 case CFM_TLV_INTERFACE_STATUS: 561 if (cfm_tlv_len < 1) { 562 ND_PRINT((ndo, " (too short, must be >= 1)")); 563 return; 564 } 565 ND_PRINT((ndo, ", Status: %s (%u)", 566 tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr), 567 *tptr)); 568 break; 569 570 case CFM_TLV_PRIVATE: 571 if (cfm_tlv_len < 4) { 572 ND_PRINT((ndo, " (too short, must be >= 4)")); 573 return; 574 } 575 ND_PRINT((ndo, ", Vendor: %s (%u), Sub-Type %u", 576 tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)), 577 EXTRACT_24BITS(tptr), 578 *(tptr + 3))); 579 hexdump = TRUE; 580 break; 581 582 case CFM_TLV_SENDER_ID: 583 { 584 u_int chassis_id_type, chassis_id_length; 585 u_int mgmt_addr_length; 586 587 if (cfm_tlv_len < 1) { 588 ND_PRINT((ndo, " (too short, must be >= 1)")); 589 return; 590 } 591 592 /* 593 * Get the Chassis ID length and check it. 594 */ 595 chassis_id_length = *tptr; 596 tptr++; 597 tlen--; 598 cfm_tlv_len--; 599 600 if (chassis_id_length) { 601 if (cfm_tlv_len < 1) { 602 ND_PRINT((ndo, "\n\t (TLV too short)")); 603 return; 604 } 605 chassis_id_type = *tptr; 606 cfm_tlv_len--; 607 ND_PRINT((ndo, "\n\t Chassis-ID Type %s (%u), Chassis-ID length %u", 608 tok2str(cfm_tlv_senderid_chassisid_values, 609 "Unknown", 610 chassis_id_type), 611 chassis_id_type, 612 chassis_id_length)); 613 614 if (cfm_tlv_len < chassis_id_length) { 615 ND_PRINT((ndo, "\n\t (TLV too short)")); 616 return; 617 } 618 619 switch (chassis_id_type) { 620 case CFM_CHASSIS_ID_MAC_ADDRESS: 621 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, tptr + 1))); 622 break; 623 624 case CFM_CHASSIS_ID_NETWORK_ADDRESS: 625 hexdump |= cfm_network_addr_print(ndo, tptr); 626 break; 627 628 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */ 629 case CFM_CHASSIS_ID_INTERFACE_ALIAS: 630 case CFM_CHASSIS_ID_LOCAL: 631 case CFM_CHASSIS_ID_CHASSIS_COMPONENT: 632 case CFM_CHASSIS_ID_PORT_COMPONENT: 633 safeputs(ndo, tptr + 1, chassis_id_length); 634 break; 635 636 default: 637 hexdump = TRUE; 638 break; 639 } 640 cfm_tlv_len -= chassis_id_length; 641 642 tptr += 1 + chassis_id_length; 643 tlen -= 1 + chassis_id_length; 644 } 645 646 /* 647 * Check if there is a Management Address. 648 */ 649 if (cfm_tlv_len == 0) { 650 /* No, there isn't; we're done. */ 651 return; 652 } 653 654 mgmt_addr_length = *tptr; 655 tptr++; 656 tlen--; 657 cfm_tlv_len--; 658 if (mgmt_addr_length) { 659 if (cfm_tlv_len < mgmt_addr_length) { 660 ND_PRINT((ndo, "\n\t (TLV too short)")); 661 return; 662 } 663 cfm_tlv_len -= mgmt_addr_length; 664 /* 665 * XXX - this is an OID; print it as such. 666 */ 667 tptr += mgmt_addr_length; 668 tlen -= mgmt_addr_length; 669 670 if (cfm_tlv_len < 1) { 671 ND_PRINT((ndo, "\n\t (TLV too short)")); 672 return; 673 } 674 675 mgmt_addr_length = *tptr; 676 tptr++; 677 tlen--; 678 cfm_tlv_len--; 679 if (mgmt_addr_length) { 680 if (cfm_tlv_len < mgmt_addr_length) { 681 ND_PRINT((ndo, "\n\t (TLV too short)")); 682 return; 683 } 684 cfm_tlv_len -= mgmt_addr_length; 685 /* 686 * XXX - this is a TransportDomain; print it as such. 687 */ 688 tptr += mgmt_addr_length; 689 tlen -= mgmt_addr_length; 690 } 691 } 692 break; 693 } 694 695 /* 696 * FIXME those are the defined TLVs that lack a decoder 697 * you are welcome to contribute code ;-) 698 */ 699 700 case CFM_TLV_DATA: 701 case CFM_TLV_REPLY_INGRESS: 702 case CFM_TLV_REPLY_EGRESS: 703 default: 704 hexdump = TRUE; 705 break; 706 } 707 /* do we want to see an additional hexdump ? */ 708 if (hexdump || ndo->ndo_vflag > 1) 709 print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len); 710 711 tptr+=cfm_tlv_len; 712 tlen-=cfm_tlv_len; 713 } 714 return; 715 716 tooshort: 717 ND_PRINT((ndo, "\n\t\t packet is too short")); 718 return; 719 720 trunc: 721 ND_PRINT((ndo, "\n\t\t packet exceeded snapshot")); 722 } 723