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 * Support for the IEEE Connectivity Fault Management Protocols as per 802.1ag. 16 * 17 * Original code by Hannes Gredler (hannes@juniper.net) 18 */ 19 20 #include <sys/cdefs.h> 21 #ifndef lint 22 #if 0 23 static const char rcsid[] _U_ = 24 "@(#) Header: /tcpdump/master/tcpdump/print-cfm.c,v 1.5 2007-07-24 16:01:42 hannes Exp"; 25 #else 26 __RCSID("$NetBSD: print-cfm.c,v 1.2 2010/12/05 05:11:30 christos Exp $"); 27 #endif 28 #endif 29 30 #ifdef HAVE_CONFIG_H 31 #include "config.h" 32 #endif 33 34 #include <tcpdump-stdinc.h> 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include "interface.h" 41 #include "extract.h" 42 #include "ether.h" 43 #include "addrtoname.h" 44 #include "oui.h" 45 #include "af.h" 46 47 /* 48 * Prototypes 49 */ 50 const char * cfm_egress_id_string(register const u_char *); 51 int cfm_mgmt_addr_print(register const u_char *); 52 53 struct cfm_common_header_t { 54 u_int8_t mdlevel_version; 55 u_int8_t opcode; 56 u_int8_t flags; 57 u_int8_t first_tlv_offset; 58 }; 59 60 #define CFM_VERSION 0 61 #define CFM_EXTRACT_VERSION(x) (((x)&0x1f)) 62 #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5) 63 64 #define CFM_OPCODE_CCM 1 65 #define CFM_OPCODE_LBR 2 66 #define CFM_OPCODE_LBM 3 67 #define CFM_OPCODE_LTR 4 68 #define CFM_OPCODE_LTM 5 69 70 static const struct tok cfm_opcode_values[] = { 71 { CFM_OPCODE_CCM, "Continouity Check Message"}, 72 { CFM_OPCODE_LBR, "Loopback Reply"}, 73 { CFM_OPCODE_LBM, "Loopback Message"}, 74 { CFM_OPCODE_LTR, "Linktrace Reply"}, 75 { CFM_OPCODE_LTM, "Linktrace Message"}, 76 { 0, NULL} 77 }; 78 79 /* 80 * Message Formats. 81 */ 82 struct cfm_ccm_t { 83 u_int8_t sequence[4]; 84 u_int8_t ma_epi[2]; 85 u_int8_t md_nameformat; 86 u_int8_t md_namelength; 87 u_int8_t md_name[46]; /* md name and short ma name */ 88 u_int8_t reserved_itu[16]; 89 u_int8_t reserved[6]; 90 }; 91 92 /* 93 * Timer Bases for the CCM Interval field. 94 * Expressed in units of seconds. 95 */ 96 const float ccm_interval_base[8] = {0, 0.003333, 0.01, 0.1, 1, 10, 60, 600}; 97 #define CCM_INTERVAL_MIN_MULTIPLIER 3.25 98 #define CCM_INTERVAL_MAX_MULTIPLIER 3.5 99 100 #define CFM_CCM_RDI_FLAG 0x80 101 #define CFM_EXTRACT_CCM_INTERVAL(x) (((x)&0x07)) 102 103 #define CFM_CCM_MD_FORMAT_8021 0 104 #define CFM_CCM_MD_FORMAT_NONE 1 105 #define CFM_CCM_MD_FORMAT_DNS 2 106 #define CFM_CCM_MD_FORMAT_MAC 3 107 #define CFM_CCM_MD_FORMAT_CHAR 4 108 109 static const struct tok cfm_md_nameformat_values[] = { 110 { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"}, 111 { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"}, 112 { CFM_CCM_MD_FORMAT_DNS, "DNS string"}, 113 { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"}, 114 { CFM_CCM_MD_FORMAT_CHAR, "Character string"}, 115 { 0, NULL} 116 }; 117 118 #define CFM_CCM_MA_FORMAT_8021 0 119 #define CFM_CCM_MA_FORMAT_VID 1 120 #define CFM_CCM_MA_FORMAT_CHAR 2 121 #define CFM_CCM_MA_FORMAT_INT 3 122 #define CFM_CCM_MA_FORMAT_VPN 4 123 124 static const struct tok cfm_ma_nameformat_values[] = { 125 { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"}, 126 { CFM_CCM_MA_FORMAT_VID, "Primary VID"}, 127 { CFM_CCM_MA_FORMAT_CHAR, "Character string"}, 128 { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"}, 129 { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"}, 130 { 0, NULL} 131 }; 132 133 struct cfm_lbm_t { 134 u_int8_t transaction_id[4]; 135 u_int8_t reserved[4]; 136 }; 137 138 struct cfm_ltm_t { 139 u_int8_t transaction_id[4]; 140 u_int8_t egress_id[8]; 141 u_int8_t ttl; 142 u_int8_t original_mac[ETHER_ADDR_LEN]; 143 u_int8_t target_mac[ETHER_ADDR_LEN]; 144 u_int8_t reserved[3]; 145 }; 146 147 static const struct tok cfm_ltm_flag_values[] = { 148 { 0x80, "Use Forwarding-DB only"}, 149 { 0, NULL} 150 }; 151 152 struct cfm_ltr_t { 153 u_int8_t transaction_id[4]; 154 u_int8_t last_egress_id[8]; 155 u_int8_t next_egress_id[8]; 156 u_int8_t ttl; 157 u_int8_t replay_action; 158 u_int8_t reserved[6]; 159 }; 160 161 static const struct tok cfm_ltr_flag_values[] = { 162 { 0x80, "Forwarded"}, 163 { 0x40, "Terminal MEP"}, 164 { 0, NULL} 165 }; 166 167 static const struct tok cfm_ltr_replay_action_values[] = { 168 { 1, "Exact Match"}, 169 { 2, "Filtering DB"}, 170 { 3, "MIP CCM DB"}, 171 { 0, NULL} 172 }; 173 174 175 #define CFM_TLV_END 0 176 #define CFM_TLV_SENDER_ID 1 177 #define CFM_TLV_PORT_STATUS 2 178 #define CFM_TLV_INTERFACE_STATUS 3 179 #define CFM_TLV_DATA 4 180 #define CFM_TLV_REPLY_INGRESS 5 181 #define CFM_TLV_REPLY_EGRESS 6 182 #define CFM_TLV_PRIVATE 31 183 184 static const struct tok cfm_tlv_values[] = { 185 { CFM_TLV_END, "End"}, 186 { CFM_TLV_SENDER_ID, "Sender ID"}, 187 { CFM_TLV_PORT_STATUS, "Port status"}, 188 { CFM_TLV_INTERFACE_STATUS, "Interface status"}, 189 { CFM_TLV_DATA, "Data"}, 190 { CFM_TLV_REPLY_INGRESS, "Reply Ingress"}, 191 { CFM_TLV_REPLY_EGRESS, "Reply Egress"}, 192 { CFM_TLV_PRIVATE, "Organization Specific"}, 193 { 0, NULL} 194 }; 195 196 /* 197 * TLVs 198 */ 199 200 struct cfm_tlv_header_t { 201 u_int8_t type; 202 u_int8_t length[2]; 203 }; 204 205 /* FIXME define TLV formats */ 206 207 static const struct tok cfm_tlv_port_status_values[] = { 208 { 1, "Blocked"}, 209 { 2, "Up"}, 210 { 0, NULL} 211 }; 212 213 static const struct tok cfm_tlv_interface_status_values[] = { 214 { 1, "Up"}, 215 { 2, "Down"}, 216 { 3, "Testing"}, 217 { 5, "Dormant"}, 218 { 6, "not present"}, 219 { 7, "lower Layer down"}, 220 { 0, NULL} 221 }; 222 223 #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1 224 #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2 225 #define CFM_CHASSIS_ID_PORT_COMPONENT 3 226 #define CFM_CHASSIS_ID_MAC_ADDRESS 4 227 #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5 228 #define CFM_CHASSIS_ID_INTERFACE_NAME 6 229 #define CFM_CHASSIS_ID_LOCAL 7 230 231 static const struct tok cfm_tlv_senderid_chassisid_values[] = { 232 { 0, "Reserved"}, 233 { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"}, 234 { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"}, 235 { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"}, 236 { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"}, 237 { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"}, 238 { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"}, 239 { CFM_CHASSIS_ID_LOCAL, "Locally assigned"}, 240 { 0, NULL} 241 }; 242 243 244 int 245 cfm_mgmt_addr_print(register const u_char *tptr) { 246 247 u_int mgmt_addr_type; 248 u_int hexdump = FALSE; 249 250 /* 251 * Altough AFIs are tpically 2 octects wide, 252 * 802.1ab specifies that this field width 253 * is only once octet 254 */ 255 mgmt_addr_type = *tptr; 256 printf("\n\t Management Address Type %s (%u)", 257 tok2str(af_values, "Unknown", mgmt_addr_type), 258 mgmt_addr_type); 259 260 /* 261 * Resolve the passed in Address. 262 */ 263 switch(mgmt_addr_type) { 264 case AFNUM_INET: 265 printf(", %s", ipaddr_string(tptr + 1)); 266 break; 267 268 #ifdef INET6 269 case AFNUM_INET6: 270 printf(", %s", ip6addr_string(tptr + 1)); 271 break; 272 #endif 273 274 default: 275 hexdump = TRUE; 276 break; 277 } 278 279 return hexdump; 280 } 281 282 /* 283 * The egress-ID string is a 16-Bit string plus a MAC address. 284 */ 285 const char * 286 cfm_egress_id_string(register const u_char *tptr) { 287 static char egress_id_buffer[80]; 288 289 snprintf(egress_id_buffer, sizeof(egress_id_buffer), 290 "MAC %0x4x-%s", 291 EXTRACT_16BITS(tptr), 292 etheraddr_string(tptr+2)); 293 294 return egress_id_buffer; 295 } 296 297 void 298 cfm_print(register const u_char *pptr, register u_int length) { 299 300 const struct cfm_common_header_t *cfm_common_header; 301 const struct cfm_tlv_header_t *cfm_tlv_header; 302 const u_int8_t *tptr, *tlv_ptr, *ma_name, *ma_nameformat, *ma_namelength; 303 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 304 305 306 union { 307 const struct cfm_ccm_t *cfm_ccm; 308 const struct cfm_lbm_t *cfm_lbm; 309 const struct cfm_ltm_t *cfm_ltm; 310 const struct cfm_ltr_t *cfm_ltr; 311 } msg_ptr; 312 313 tptr=pptr; 314 cfm_common_header = (const struct cfm_common_header_t *)pptr; 315 TCHECK(*cfm_common_header); 316 317 /* 318 * Sanity checking of the header. 319 */ 320 if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) { 321 printf("CFMv%u not supported, length %u", 322 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length); 323 return; 324 } 325 326 printf("CFMv%u %s, MD Level %u, length %u", 327 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), 328 tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode), 329 CFM_EXTRACT_MD_LEVEL(cfm_common_header->mdlevel_version), 330 length); 331 332 /* 333 * In non-verbose mode just print the opcode and md-level. 334 */ 335 if (vflag < 1) { 336 return; 337 } 338 339 printf("\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset); 340 341 tptr += sizeof(const struct cfm_common_header_t); 342 tlen = length - sizeof(struct cfm_common_header_t); 343 344 switch (cfm_common_header->opcode) { 345 case CFM_OPCODE_CCM: 346 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 347 348 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags); 349 printf(", Flags [CCM Interval %u%s]", 350 ccm_interval, 351 cfm_common_header->flags & CFM_CCM_RDI_FLAG ? 352 ", RDI" : ""); 353 354 /* 355 * Resolve the CCM interval field. 356 */ 357 if (ccm_interval) { 358 printf("\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 printf("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 366 EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence), 367 EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi)); 368 369 370 /* 371 * Resolve the MD fields. 372 */ 373 printf("\n\t MD Name Format %s (%u), MD Name length %u", 374 tok2str(cfm_md_nameformat_values, "Unknown", 375 msg_ptr.cfm_ccm->md_nameformat), 376 msg_ptr.cfm_ccm->md_nameformat, 377 msg_ptr.cfm_ccm->md_namelength); 378 379 if (msg_ptr.cfm_ccm->md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 380 printf("\n\t MD Name: "); 381 switch (msg_ptr.cfm_ccm->md_nameformat) { 382 case CFM_CCM_MD_FORMAT_DNS: 383 case CFM_CCM_MD_FORMAT_CHAR: 384 safeputs((const char *)msg_ptr.cfm_ccm->md_name, msg_ptr.cfm_ccm->md_namelength); 385 break; 386 387 case CFM_CCM_MD_FORMAT_MAC: 388 printf("\n\t MAC %s", etheraddr_string( 389 msg_ptr.cfm_ccm->md_name)); 390 break; 391 392 /* FIXME add printers for those MD formats - hexdump for now */ 393 case CFM_CCM_MA_FORMAT_8021: 394 default: 395 print_unknown_data(msg_ptr.cfm_ccm->md_name, "\n\t ", 396 msg_ptr.cfm_ccm->md_namelength); 397 } 398 } 399 400 401 /* 402 * Resolve the MA fields. 403 */ 404 ma_nameformat = msg_ptr.cfm_ccm->md_name + msg_ptr.cfm_ccm->md_namelength; 405 ma_namelength = msg_ptr.cfm_ccm->md_name + msg_ptr.cfm_ccm->md_namelength + 1; 406 ma_name = msg_ptr.cfm_ccm->md_name + msg_ptr.cfm_ccm->md_namelength + 2; 407 408 printf("\n\t MA Name-Format %s (%u), MA name length %u", 409 tok2str(cfm_ma_nameformat_values, "Unknown", 410 *ma_nameformat), 411 *ma_nameformat, 412 *ma_namelength); 413 414 printf("\n\t MA Name: "); 415 switch (*ma_nameformat) { 416 case CFM_CCM_MA_FORMAT_CHAR: 417 safeputs((const char *)ma_name, *ma_namelength); 418 break; 419 420 /* FIXME add printers for those MA formats - hexdump for now */ 421 case CFM_CCM_MA_FORMAT_8021: 422 case CFM_CCM_MA_FORMAT_VID: 423 case CFM_CCM_MA_FORMAT_INT: 424 case CFM_CCM_MA_FORMAT_VPN: 425 default: 426 print_unknown_data(ma_name, "\n\t ", *ma_namelength); 427 } 428 break; 429 430 case CFM_OPCODE_LTM: 431 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 432 433 printf(", Flags [%s]", 434 bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags)); 435 436 printf("\n\t Transaction-ID 0x%08x, Egress-ID %s, ttl %u", 437 EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id), 438 cfm_egress_id_string(msg_ptr.cfm_ltm->egress_id), 439 msg_ptr.cfm_ltm->ttl); 440 441 printf("\n\t Original-MAC %s, Target-MAC %s", 442 etheraddr_string(msg_ptr.cfm_ltm->original_mac), 443 etheraddr_string(msg_ptr.cfm_ltm->target_mac)); 444 break; 445 446 case CFM_OPCODE_LTR: 447 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 448 449 printf(", Flags [%s]", 450 bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags)); 451 452 printf("\n\t Transaction-ID 0x%08x, Last-Egress-ID %s", 453 EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id), 454 cfm_egress_id_string(msg_ptr.cfm_ltr->last_egress_id)); 455 456 printf("\n\t Next-Egress-ID %s, ttl %u", 457 cfm_egress_id_string(msg_ptr.cfm_ltr->next_egress_id), 458 msg_ptr.cfm_ltr->ttl); 459 460 printf("\n\t Replay-Action %s (%u)", 461 tok2str(cfm_ltr_replay_action_values, 462 "Unknown", 463 msg_ptr.cfm_ltr->replay_action), 464 msg_ptr.cfm_ltr->replay_action); 465 break; 466 467 /* 468 * No message decoder yet. 469 * Hexdump everything up until the start of the TLVs 470 */ 471 case CFM_OPCODE_LBR: 472 case CFM_OPCODE_LBM: 473 default: 474 if (tlen > cfm_common_header->first_tlv_offset) { 475 print_unknown_data(tptr, "\n\t ", 476 tlen - cfm_common_header->first_tlv_offset); 477 } 478 break; 479 } 480 481 /* 482 * Sanity check for not walking off. 483 */ 484 if (tlen <= cfm_common_header->first_tlv_offset) { 485 return; 486 } 487 488 tptr += cfm_common_header->first_tlv_offset; 489 tlen -= cfm_common_header->first_tlv_offset; 490 491 while (tlen > 0) { 492 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 493 494 /* Enough to read the tlv type ? */ 495 TCHECK2(*tptr, 1); 496 cfm_tlv_type=cfm_tlv_header->type; 497 498 if (cfm_tlv_type != CFM_TLV_END) { 499 /* did we capture enough for fully decoding the object header ? */ 500 TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t)); 501 cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length); 502 } else { 503 cfm_tlv_len = 0; 504 } 505 506 printf("\n\t%s TLV (0x%02x), length %u", 507 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type), 508 cfm_tlv_type, 509 cfm_tlv_len); 510 511 /* sanity check for not walking off and infinite loop check. */ 512 if ((cfm_tlv_type != CFM_TLV_END) && 513 ((cfm_tlv_len + sizeof(struct cfm_tlv_header_t) > tlen) || 514 (!cfm_tlv_len))) { 515 print_unknown_data(tptr,"\n\t ",tlen); 516 return; 517 } 518 519 tptr += sizeof(struct cfm_tlv_header_t); 520 tlen -= sizeof(struct cfm_tlv_header_t); 521 tlv_ptr = tptr; 522 523 /* did we capture enough for fully decoding the object ? */ 524 if (cfm_tlv_type != CFM_TLV_END) { 525 TCHECK2(*tptr, cfm_tlv_len); 526 } 527 hexdump = FALSE; 528 529 switch(cfm_tlv_type) { 530 case CFM_TLV_END: 531 /* we are done - bail out */ 532 return; 533 534 case CFM_TLV_PORT_STATUS: 535 printf(", Status: %s (%u)", 536 tok2str(cfm_tlv_port_status_values, "Unknown", *tptr), 537 *tptr); 538 break; 539 540 case CFM_TLV_INTERFACE_STATUS: 541 printf(", Status: %s (%u)", 542 tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr), 543 *tptr); 544 break; 545 546 case CFM_TLV_PRIVATE: 547 printf(", Vendor: %s (%u), Sub-Type %u", 548 tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)), 549 EXTRACT_24BITS(tptr), 550 *(tptr+3)); 551 hexdump = TRUE; 552 break; 553 554 case CFM_TLV_SENDER_ID: 555 { 556 u_int chassis_id_type, chassis_id_length; 557 u_int mgmt_addr_length; 558 559 /* 560 * Check if there is a Chassis-ID. 561 */ 562 chassis_id_length = *tptr; 563 if (chassis_id_length > tlen) { 564 hexdump = TRUE; 565 break; 566 } 567 568 tptr++; 569 tlen--; 570 571 if (chassis_id_length) { 572 chassis_id_type = *tptr; 573 printf("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u", 574 tok2str(cfm_tlv_senderid_chassisid_values, 575 "Unknown", 576 chassis_id_type), 577 chassis_id_type, 578 chassis_id_length); 579 580 switch (chassis_id_type) { 581 case CFM_CHASSIS_ID_MAC_ADDRESS: 582 printf("\n\t MAC %s", etheraddr_string(tptr+1)); 583 break; 584 585 case CFM_CHASSIS_ID_NETWORK_ADDRESS: 586 hexdump |= cfm_mgmt_addr_print(tptr); 587 break; 588 589 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */ 590 case CFM_CHASSIS_ID_INTERFACE_ALIAS: 591 case CFM_CHASSIS_ID_LOCAL: 592 case CFM_CHASSIS_ID_CHASSIS_COMPONENT: 593 case CFM_CHASSIS_ID_PORT_COMPONENT: 594 safeputs((const char *)tptr+1, chassis_id_length); 595 break; 596 597 default: 598 hexdump = TRUE; 599 break; 600 } 601 } 602 603 tptr += chassis_id_length; 604 tlen -= chassis_id_length; 605 606 /* 607 * Check if there is a Management Address. 608 */ 609 mgmt_addr_length = *tptr; 610 if (mgmt_addr_length > tlen) { 611 hexdump = TRUE; 612 break; 613 } 614 615 tptr++; 616 tlen--; 617 618 if (mgmt_addr_length) { 619 hexdump |= cfm_mgmt_addr_print(tptr); 620 } 621 622 tptr += mgmt_addr_length; 623 tlen -= mgmt_addr_length; 624 625 } 626 break; 627 628 /* 629 * FIXME those are the defined TLVs that lack a decoder 630 * you are welcome to contribute code ;-) 631 */ 632 633 case CFM_TLV_DATA: 634 case CFM_TLV_REPLY_INGRESS: 635 case CFM_TLV_REPLY_EGRESS: 636 default: 637 hexdump = TRUE; 638 break; 639 } 640 /* do we want to see an additional hexdump ? */ 641 if (hexdump || vflag > 1) 642 print_unknown_data(tlv_ptr, "\n\t ", cfm_tlv_len); 643 644 tptr+=cfm_tlv_len; 645 tlen-=cfm_tlv_len; 646 } 647 return; 648 trunc: 649 printf("\n\t\t packet exceeded snapshot"); 650 } 651