10f74e101Schristos /* 20f74e101Schristos * Copyright (c) 1998-2006 The TCPDUMP project 30f74e101Schristos * 40f74e101Schristos * Redistribution and use in source and binary forms, with or without 50f74e101Schristos * modification, are permitted provided that: (1) source code 60f74e101Schristos * distributions retain the above copyright notice and this paragraph 70f74e101Schristos * in its entirety, and (2) distributions including binary code include 80f74e101Schristos * the above copyright notice and this paragraph in its entirety in 90f74e101Schristos * the documentation or other materials provided with the distribution. 100f74e101Schristos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 110f74e101Schristos * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 120f74e101Schristos * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 130f74e101Schristos * FOR A PARTICULAR PURPOSE. 140f74e101Schristos * 1572c96ff3Schristos * Original code by Hannes Gredler (hannes@gredler.at) 160f74e101Schristos */ 170f74e101Schristos 18dc860a36Sspz /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */ 19dc860a36Sspz 2011b3aaa1Schristos #include <sys/cdefs.h> 210f74e101Schristos #ifndef lint 22*26ba0b50Schristos __RCSID("$NetBSD: print-cfm.c,v 1.11 2024/09/02 16:15:30 christos Exp $"); 230f74e101Schristos #endif 240f74e101Schristos 25c74ad251Schristos #include <config.h> 260f74e101Schristos 27c74ad251Schristos #include "netdissect-stdinc.h" 280f74e101Schristos 29fdccd7e4Schristos #include "netdissect.h" 300f74e101Schristos #include "extract.h" 310f74e101Schristos #include "addrtoname.h" 320f74e101Schristos #include "oui.h" 330f74e101Schristos #include "af.h" 340f74e101Schristos 35c74ad251Schristos 360f74e101Schristos struct cfm_common_header_t { 37c74ad251Schristos nd_uint8_t mdlevel_version; 38c74ad251Schristos nd_uint8_t opcode; 39c74ad251Schristos nd_uint8_t flags; 40c74ad251Schristos nd_uint8_t first_tlv_offset; 410f74e101Schristos }; 420f74e101Schristos 430f74e101Schristos #define CFM_VERSION 0 44c74ad251Schristos #define CFM_EXTRACT_VERSION(x) ((x)&0x1f) 450f74e101Schristos #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5) 460f74e101Schristos 470f74e101Schristos #define CFM_OPCODE_CCM 1 480f74e101Schristos #define CFM_OPCODE_LBR 2 490f74e101Schristos #define CFM_OPCODE_LBM 3 500f74e101Schristos #define CFM_OPCODE_LTR 4 510f74e101Schristos #define CFM_OPCODE_LTM 5 520f74e101Schristos 530f74e101Schristos static const struct tok cfm_opcode_values[] = { 54c74ad251Schristos { CFM_OPCODE_CCM, "Continuity Check Message"}, 550f74e101Schristos { CFM_OPCODE_LBR, "Loopback Reply"}, 560f74e101Schristos { CFM_OPCODE_LBM, "Loopback Message"}, 570f74e101Schristos { CFM_OPCODE_LTR, "Linktrace Reply"}, 580f74e101Schristos { CFM_OPCODE_LTM, "Linktrace Message"}, 590f74e101Schristos { 0, NULL} 600f74e101Schristos }; 610f74e101Schristos 620f74e101Schristos /* 630f74e101Schristos * Message Formats. 640f74e101Schristos */ 650f74e101Schristos struct cfm_ccm_t { 66c74ad251Schristos nd_uint32_t sequence; 67c74ad251Schristos nd_uint16_t ma_epi; 68c74ad251Schristos nd_byte names[48]; 69c74ad251Schristos nd_byte itu_t_y_1731[16]; 700f74e101Schristos }; 710f74e101Schristos 720f74e101Schristos /* 730f74e101Schristos * Timer Bases for the CCM Interval field. 740f74e101Schristos * Expressed in units of seconds. 750f74e101Schristos */ 76c74ad251Schristos static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f}; 770f74e101Schristos #define CCM_INTERVAL_MIN_MULTIPLIER 3.25 780f74e101Schristos #define CCM_INTERVAL_MAX_MULTIPLIER 3.5 790f74e101Schristos 800f74e101Schristos #define CFM_CCM_RDI_FLAG 0x80 81c74ad251Schristos #define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07) 820f74e101Schristos 830f74e101Schristos #define CFM_CCM_MD_FORMAT_8021 0 840f74e101Schristos #define CFM_CCM_MD_FORMAT_NONE 1 850f74e101Schristos #define CFM_CCM_MD_FORMAT_DNS 2 860f74e101Schristos #define CFM_CCM_MD_FORMAT_MAC 3 870f74e101Schristos #define CFM_CCM_MD_FORMAT_CHAR 4 880f74e101Schristos 890f74e101Schristos static const struct tok cfm_md_nameformat_values[] = { 900f74e101Schristos { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"}, 910f74e101Schristos { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"}, 920f74e101Schristos { CFM_CCM_MD_FORMAT_DNS, "DNS string"}, 930f74e101Schristos { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"}, 940f74e101Schristos { CFM_CCM_MD_FORMAT_CHAR, "Character string"}, 950f74e101Schristos { 0, NULL} 960f74e101Schristos }; 970f74e101Schristos 980f74e101Schristos #define CFM_CCM_MA_FORMAT_8021 0 990f74e101Schristos #define CFM_CCM_MA_FORMAT_VID 1 1000f74e101Schristos #define CFM_CCM_MA_FORMAT_CHAR 2 1010f74e101Schristos #define CFM_CCM_MA_FORMAT_INT 3 1020f74e101Schristos #define CFM_CCM_MA_FORMAT_VPN 4 1030f74e101Schristos 1040f74e101Schristos static const struct tok cfm_ma_nameformat_values[] = { 1050f74e101Schristos { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"}, 1060f74e101Schristos { CFM_CCM_MA_FORMAT_VID, "Primary VID"}, 1070f74e101Schristos { CFM_CCM_MA_FORMAT_CHAR, "Character string"}, 1080f74e101Schristos { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"}, 1090f74e101Schristos { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"}, 1100f74e101Schristos { 0, NULL} 1110f74e101Schristos }; 1120f74e101Schristos 1130f74e101Schristos struct cfm_lbm_t { 114c74ad251Schristos nd_uint32_t transaction_id; 1150f74e101Schristos }; 1160f74e101Schristos 1170f74e101Schristos struct cfm_ltm_t { 118c74ad251Schristos nd_uint32_t transaction_id; 119c74ad251Schristos nd_uint8_t ttl; 120c74ad251Schristos nd_mac_addr original_mac; 121c74ad251Schristos nd_mac_addr target_mac; 1220f74e101Schristos }; 1230f74e101Schristos 1240f74e101Schristos static const struct tok cfm_ltm_flag_values[] = { 1250f74e101Schristos { 0x80, "Use Forwarding-DB only"}, 1260f74e101Schristos { 0, NULL} 1270f74e101Schristos }; 1280f74e101Schristos 1290f74e101Schristos struct cfm_ltr_t { 130c74ad251Schristos nd_uint32_t transaction_id; 131c74ad251Schristos nd_uint8_t ttl; 132c74ad251Schristos nd_uint8_t replay_action; 1330f74e101Schristos }; 1340f74e101Schristos 1350f74e101Schristos static const struct tok cfm_ltr_flag_values[] = { 136b3a00663Schristos { 0x80, "UseFDB Only"}, 137b3a00663Schristos { 0x40, "FwdYes"}, 138b3a00663Schristos { 0x20, "Terminal MEP"}, 1390f74e101Schristos { 0, NULL} 1400f74e101Schristos }; 1410f74e101Schristos 1420f74e101Schristos static const struct tok cfm_ltr_replay_action_values[] = { 1430f74e101Schristos { 1, "Exact Match"}, 1440f74e101Schristos { 2, "Filtering DB"}, 1450f74e101Schristos { 3, "MIP CCM DB"}, 1460f74e101Schristos { 0, NULL} 1470f74e101Schristos }; 1480f74e101Schristos 1490f74e101Schristos 1500f74e101Schristos #define CFM_TLV_END 0 1510f74e101Schristos #define CFM_TLV_SENDER_ID 1 1520f74e101Schristos #define CFM_TLV_PORT_STATUS 2 1530f74e101Schristos #define CFM_TLV_INTERFACE_STATUS 3 1540f74e101Schristos #define CFM_TLV_DATA 4 1550f74e101Schristos #define CFM_TLV_REPLY_INGRESS 5 1560f74e101Schristos #define CFM_TLV_REPLY_EGRESS 6 1570f74e101Schristos #define CFM_TLV_PRIVATE 31 1580f74e101Schristos 1590f74e101Schristos static const struct tok cfm_tlv_values[] = { 1600f74e101Schristos { CFM_TLV_END, "End"}, 1610f74e101Schristos { CFM_TLV_SENDER_ID, "Sender ID"}, 1620f74e101Schristos { CFM_TLV_PORT_STATUS, "Port status"}, 1630f74e101Schristos { CFM_TLV_INTERFACE_STATUS, "Interface status"}, 1640f74e101Schristos { CFM_TLV_DATA, "Data"}, 1650f74e101Schristos { CFM_TLV_REPLY_INGRESS, "Reply Ingress"}, 1660f74e101Schristos { CFM_TLV_REPLY_EGRESS, "Reply Egress"}, 1670f74e101Schristos { CFM_TLV_PRIVATE, "Organization Specific"}, 1680f74e101Schristos { 0, NULL} 1690f74e101Schristos }; 1700f74e101Schristos 1710f74e101Schristos /* 1720f74e101Schristos * TLVs 1730f74e101Schristos */ 1740f74e101Schristos 1750f74e101Schristos struct cfm_tlv_header_t { 176c74ad251Schristos nd_uint8_t type; 177c74ad251Schristos nd_uint16_t length; 1780f74e101Schristos }; 1790f74e101Schristos 1800f74e101Schristos /* FIXME define TLV formats */ 1810f74e101Schristos 1820f74e101Schristos static const struct tok cfm_tlv_port_status_values[] = { 1830f74e101Schristos { 1, "Blocked"}, 1840f74e101Schristos { 2, "Up"}, 1850f74e101Schristos { 0, NULL} 1860f74e101Schristos }; 1870f74e101Schristos 1880f74e101Schristos static const struct tok cfm_tlv_interface_status_values[] = { 1890f74e101Schristos { 1, "Up"}, 1900f74e101Schristos { 2, "Down"}, 1910f74e101Schristos { 3, "Testing"}, 1920f74e101Schristos { 5, "Dormant"}, 1930f74e101Schristos { 6, "not present"}, 1940f74e101Schristos { 7, "lower Layer down"}, 1950f74e101Schristos { 0, NULL} 1960f74e101Schristos }; 1970f74e101Schristos 1980f74e101Schristos #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1 1990f74e101Schristos #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2 2000f74e101Schristos #define CFM_CHASSIS_ID_PORT_COMPONENT 3 2010f74e101Schristos #define CFM_CHASSIS_ID_MAC_ADDRESS 4 2020f74e101Schristos #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5 2030f74e101Schristos #define CFM_CHASSIS_ID_INTERFACE_NAME 6 2040f74e101Schristos #define CFM_CHASSIS_ID_LOCAL 7 2050f74e101Schristos 2060f74e101Schristos static const struct tok cfm_tlv_senderid_chassisid_values[] = { 2070f74e101Schristos { 0, "Reserved"}, 2080f74e101Schristos { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"}, 2090f74e101Schristos { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"}, 2100f74e101Schristos { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"}, 2110f74e101Schristos { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"}, 2120f74e101Schristos { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"}, 2130f74e101Schristos { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"}, 2140f74e101Schristos { CFM_CHASSIS_ID_LOCAL, "Locally assigned"}, 2150f74e101Schristos { 0, NULL} 2160f74e101Schristos }; 2170f74e101Schristos 2180f74e101Schristos 219b3a00663Schristos static int 220dc860a36Sspz cfm_network_addr_print(netdissect_options *ndo, 221c74ad251Schristos const u_char *tptr, const u_int length) 222ba2ff121Schristos { 223dc860a36Sspz u_int network_addr_type; 2240f74e101Schristos u_int hexdump = FALSE; 2250f74e101Schristos 2260f74e101Schristos /* 227c74ad251Schristos * Although AFIs are typically 2 octets wide, 2280f74e101Schristos * 802.1ab specifies that this field width 229c74ad251Schristos * is only one octet. 2300f74e101Schristos */ 23172c96ff3Schristos if (length < 1) { 232c74ad251Schristos ND_PRINT("\n\t Network Address Type (invalid, no data"); 23372c96ff3Schristos return hexdump; 23472c96ff3Schristos } 23572c96ff3Schristos /* The calling function must make any due ND_TCHECK calls. */ 236c74ad251Schristos network_addr_type = GET_U_1(tptr); 237c74ad251Schristos ND_PRINT("\n\t Network Address Type %s (%u)", 238dc860a36Sspz tok2str(af_values, "Unknown", network_addr_type), 239c74ad251Schristos network_addr_type); 2400f74e101Schristos 2410f74e101Schristos /* 2420f74e101Schristos * Resolve the passed in Address. 2430f74e101Schristos */ 244dc860a36Sspz switch(network_addr_type) { 2450f74e101Schristos case AFNUM_INET: 24672c96ff3Schristos if (length != 1 + 4) { 247c74ad251Schristos ND_PRINT("(invalid IPv4 address length %u)", length - 1); 24872c96ff3Schristos hexdump = TRUE; 24972c96ff3Schristos break; 25072c96ff3Schristos } 251c74ad251Schristos ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1)); 2520f74e101Schristos break; 2530f74e101Schristos 2540f74e101Schristos case AFNUM_INET6: 25572c96ff3Schristos if (length != 1 + 16) { 256c74ad251Schristos ND_PRINT("(invalid IPv6 address length %u)", length - 1); 25772c96ff3Schristos hexdump = TRUE; 25872c96ff3Schristos break; 25972c96ff3Schristos } 260c74ad251Schristos ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1)); 2610f74e101Schristos break; 2620f74e101Schristos 2630f74e101Schristos default: 2640f74e101Schristos hexdump = TRUE; 2650f74e101Schristos break; 2660f74e101Schristos } 2670f74e101Schristos 2680f74e101Schristos return hexdump; 2690f74e101Schristos } 2700f74e101Schristos 2710f74e101Schristos void 272b3a00663Schristos cfm_print(netdissect_options *ndo, 273c74ad251Schristos const u_char *pptr, u_int length) 274ba2ff121Schristos { 2750f74e101Schristos const struct cfm_common_header_t *cfm_common_header; 276c74ad251Schristos uint8_t mdlevel_version, opcode, flags, first_tlv_offset; 2770f74e101Schristos const struct cfm_tlv_header_t *cfm_tlv_header; 278dc860a36Sspz const uint8_t *tptr, *tlv_ptr; 279dc860a36Sspz const uint8_t *namesp; 280dc860a36Sspz u_int names_data_remaining; 281dc860a36Sspz uint8_t md_nameformat, md_namelength; 282dc860a36Sspz const uint8_t *md_name; 283dc860a36Sspz uint8_t ma_nameformat, ma_namelength; 284dc860a36Sspz const uint8_t *ma_name; 2850f74e101Schristos u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 2860f74e101Schristos 2870f74e101Schristos 2880f74e101Schristos union { 2890f74e101Schristos const struct cfm_ccm_t *cfm_ccm; 2900f74e101Schristos const struct cfm_lbm_t *cfm_lbm; 2910f74e101Schristos const struct cfm_ltm_t *cfm_ltm; 2920f74e101Schristos const struct cfm_ltr_t *cfm_ltr; 2930f74e101Schristos } msg_ptr; 2940f74e101Schristos 295c74ad251Schristos ndo->ndo_protocol = "cfm"; 2960f74e101Schristos tptr=pptr; 2970f74e101Schristos cfm_common_header = (const struct cfm_common_header_t *)pptr; 298dc860a36Sspz if (length < sizeof(*cfm_common_header)) 299dc860a36Sspz goto tooshort; 300c74ad251Schristos ND_TCHECK_SIZE(cfm_common_header); 3010f74e101Schristos 3020f74e101Schristos /* 3030f74e101Schristos * Sanity checking of the header. 3040f74e101Schristos */ 305c74ad251Schristos mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version); 306c74ad251Schristos if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) { 307c74ad251Schristos ND_PRINT("CFMv%u not supported, length %u", 308c74ad251Schristos CFM_EXTRACT_VERSION(mdlevel_version), length); 3090f74e101Schristos return; 3100f74e101Schristos } 3110f74e101Schristos 312c74ad251Schristos opcode = GET_U_1(cfm_common_header->opcode); 313c74ad251Schristos ND_PRINT("CFMv%u %s, MD Level %u, length %u", 314c74ad251Schristos CFM_EXTRACT_VERSION(mdlevel_version), 315c74ad251Schristos tok2str(cfm_opcode_values, "unknown (%u)", opcode), 316c74ad251Schristos CFM_EXTRACT_MD_LEVEL(mdlevel_version), 317c74ad251Schristos length); 3180f74e101Schristos 3190f74e101Schristos /* 3200f74e101Schristos * In non-verbose mode just print the opcode and md-level. 3210f74e101Schristos */ 322b3a00663Schristos if (ndo->ndo_vflag < 1) { 3230f74e101Schristos return; 3240f74e101Schristos } 3250f74e101Schristos 326c74ad251Schristos flags = GET_U_1(cfm_common_header->flags); 327c74ad251Schristos first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset); 328c74ad251Schristos ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset); 3290f74e101Schristos 330c74ad251Schristos tptr += sizeof(struct cfm_common_header_t); 3310f74e101Schristos tlen = length - sizeof(struct cfm_common_header_t); 3320f74e101Schristos 333dc860a36Sspz /* 334dc860a36Sspz * Sanity check the first TLV offset. 335dc860a36Sspz */ 336c74ad251Schristos if (first_tlv_offset > tlen) { 337c74ad251Schristos ND_PRINT(" (too large, must be <= %u)", tlen); 338dc860a36Sspz return; 339dc860a36Sspz } 340dc860a36Sspz 341c74ad251Schristos switch (opcode) { 3420f74e101Schristos case CFM_OPCODE_CCM: 3430f74e101Schristos msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 344c74ad251Schristos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) { 345c74ad251Schristos ND_PRINT(" (too small 1, must be >= %zu)", 346c74ad251Schristos sizeof(*msg_ptr.cfm_ccm)); 347dc860a36Sspz return; 348dc860a36Sspz } 349dc860a36Sspz if (tlen < sizeof(*msg_ptr.cfm_ccm)) 350dc860a36Sspz goto tooshort; 351c74ad251Schristos ND_TCHECK_SIZE(msg_ptr.cfm_ccm); 3520f74e101Schristos 353c74ad251Schristos ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags); 354c74ad251Schristos ND_PRINT(", Flags [CCM Interval %u%s]", 3550f74e101Schristos ccm_interval, 356c74ad251Schristos flags & CFM_CCM_RDI_FLAG ? 357c74ad251Schristos ", RDI" : ""); 3580f74e101Schristos 3590f74e101Schristos /* 3600f74e101Schristos * Resolve the CCM interval field. 3610f74e101Schristos */ 3620f74e101Schristos if (ccm_interval) { 363c74ad251Schristos ND_PRINT("\n\t CCM Interval %.3fs" 3640f74e101Schristos ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs", 3650f74e101Schristos ccm_interval_base[ccm_interval], 3660f74e101Schristos ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER, 367c74ad251Schristos ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER); 3680f74e101Schristos } 3690f74e101Schristos 370c74ad251Schristos ND_PRINT("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 371c74ad251Schristos GET_BE_U_4(msg_ptr.cfm_ccm->sequence), 372c74ad251Schristos GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi)); 3730f74e101Schristos 374dc860a36Sspz namesp = msg_ptr.cfm_ccm->names; 375dc860a36Sspz names_data_remaining = sizeof(msg_ptr.cfm_ccm->names); 3760f74e101Schristos 3770f74e101Schristos /* 3780f74e101Schristos * Resolve the MD fields. 3790f74e101Schristos */ 380c74ad251Schristos md_nameformat = GET_U_1(namesp); 381dc860a36Sspz namesp++; 382dc860a36Sspz names_data_remaining--; /* We know this is != 0 */ 383dc860a36Sspz if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 384c74ad251Schristos md_namelength = GET_U_1(namesp); 385dc860a36Sspz namesp++; 386dc860a36Sspz names_data_remaining--; /* We know this is !=0 */ 387c74ad251Schristos ND_PRINT("\n\t MD Name Format %s (%u), MD Name length %u", 3880f74e101Schristos tok2str(cfm_md_nameformat_values, "Unknown", 389dc860a36Sspz md_nameformat), 390dc860a36Sspz md_nameformat, 391c74ad251Schristos md_namelength); 3920f74e101Schristos 39372c96ff3Schristos /* 39472c96ff3Schristos * -3 for the MA short name format and length and one byte 39572c96ff3Schristos * of MA short name. 39672c96ff3Schristos */ 39772c96ff3Schristos if (md_namelength > names_data_remaining - 3) { 398c74ad251Schristos ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2); 399dc860a36Sspz return; 400dc860a36Sspz } 401dc860a36Sspz 402dc860a36Sspz md_name = namesp; 403c74ad251Schristos ND_PRINT("\n\t MD Name: "); 404dc860a36Sspz switch (md_nameformat) { 4050f74e101Schristos case CFM_CCM_MD_FORMAT_DNS: 4060f74e101Schristos case CFM_CCM_MD_FORMAT_CHAR: 407c74ad251Schristos nd_printjnp(ndo, md_name, md_namelength); 4080f74e101Schristos break; 4090f74e101Schristos 4100f74e101Schristos case CFM_CCM_MD_FORMAT_MAC: 411c74ad251Schristos if (md_namelength == MAC_ADDR_LEN) { 412c74ad251Schristos ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(md_name)); 413dc860a36Sspz } else { 414c74ad251Schristos ND_PRINT("\n\t MAC (length invalid)"); 415dc860a36Sspz } 4160f74e101Schristos break; 4170f74e101Schristos 4180f74e101Schristos /* FIXME add printers for those MD formats - hexdump for now */ 4190f74e101Schristos case CFM_CCM_MA_FORMAT_8021: 4200f74e101Schristos default: 421dc860a36Sspz print_unknown_data(ndo, md_name, "\n\t ", 422dc860a36Sspz md_namelength); 4230f74e101Schristos } 424dc860a36Sspz namesp += md_namelength; 425dc860a36Sspz names_data_remaining -= md_namelength; 426dc860a36Sspz } else { 427c74ad251Schristos ND_PRINT("\n\t MD Name Format %s (%u)", 428dc860a36Sspz tok2str(cfm_md_nameformat_values, "Unknown", 429dc860a36Sspz md_nameformat), 430c74ad251Schristos md_nameformat); 4310f74e101Schristos } 4320f74e101Schristos 4330f74e101Schristos 4340f74e101Schristos /* 4350f74e101Schristos * Resolve the MA fields. 4360f74e101Schristos */ 437c74ad251Schristos ma_nameformat = GET_U_1(namesp); 438dc860a36Sspz namesp++; 439dc860a36Sspz names_data_remaining--; /* We know this is != 0 */ 440c74ad251Schristos ma_namelength = GET_U_1(namesp); 441dc860a36Sspz namesp++; 442dc860a36Sspz names_data_remaining--; /* We know this is != 0 */ 443c74ad251Schristos ND_PRINT("\n\t MA Name-Format %s (%u), MA name length %u", 4440f74e101Schristos tok2str(cfm_ma_nameformat_values, "Unknown", 445dc860a36Sspz ma_nameformat), 446dc860a36Sspz ma_nameformat, 447c74ad251Schristos ma_namelength); 4480f74e101Schristos 449dc860a36Sspz if (ma_namelength > names_data_remaining) { 450c74ad251Schristos ND_PRINT(" (too large, must be <= %u)", names_data_remaining); 451dc860a36Sspz return; 452dc860a36Sspz } 453dc860a36Sspz 454dc860a36Sspz ma_name = namesp; 455c74ad251Schristos ND_PRINT("\n\t MA Name: "); 456dc860a36Sspz switch (ma_nameformat) { 4570f74e101Schristos case CFM_CCM_MA_FORMAT_CHAR: 458c74ad251Schristos nd_printjnp(ndo, ma_name, ma_namelength); 4590f74e101Schristos break; 4600f74e101Schristos 4610f74e101Schristos /* FIXME add printers for those MA formats - hexdump for now */ 4620f74e101Schristos case CFM_CCM_MA_FORMAT_8021: 4630f74e101Schristos case CFM_CCM_MA_FORMAT_VID: 4640f74e101Schristos case CFM_CCM_MA_FORMAT_INT: 4650f74e101Schristos case CFM_CCM_MA_FORMAT_VPN: 4660f74e101Schristos default: 467dc860a36Sspz print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength); 4680f74e101Schristos } 4690f74e101Schristos break; 4700f74e101Schristos 4710f74e101Schristos case CFM_OPCODE_LTM: 4720f74e101Schristos msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 473c74ad251Schristos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) { 474c74ad251Schristos ND_PRINT(" (too small 4, must be >= %zu)", 475c74ad251Schristos sizeof(*msg_ptr.cfm_ltm)); 476dc860a36Sspz return; 477dc860a36Sspz } 478dc860a36Sspz if (tlen < sizeof(*msg_ptr.cfm_ltm)) 479dc860a36Sspz goto tooshort; 480c74ad251Schristos ND_TCHECK_SIZE(msg_ptr.cfm_ltm); 4810f74e101Schristos 482c74ad251Schristos ND_PRINT(", Flags [%s]", 483c74ad251Schristos bittok2str(cfm_ltm_flag_values, "none", flags)); 4840f74e101Schristos 485c74ad251Schristos ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 486c74ad251Schristos GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id), 487c74ad251Schristos GET_U_1(msg_ptr.cfm_ltm->ttl)); 4880f74e101Schristos 489c74ad251Schristos ND_PRINT("\n\t Original-MAC %s, Target-MAC %s", 490c74ad251Schristos GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac), 491c74ad251Schristos GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac)); 4920f74e101Schristos break; 4930f74e101Schristos 4940f74e101Schristos case CFM_OPCODE_LTR: 4950f74e101Schristos msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 496c74ad251Schristos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) { 497c74ad251Schristos ND_PRINT(" (too small 5, must be >= %zu)", 498c74ad251Schristos sizeof(*msg_ptr.cfm_ltr)); 499dc860a36Sspz return; 500dc860a36Sspz } 501dc860a36Sspz if (tlen < sizeof(*msg_ptr.cfm_ltr)) 502dc860a36Sspz goto tooshort; 503c74ad251Schristos ND_TCHECK_SIZE(msg_ptr.cfm_ltr); 5040f74e101Schristos 505c74ad251Schristos ND_PRINT(", Flags [%s]", 506c74ad251Schristos bittok2str(cfm_ltr_flag_values, "none", flags)); 5070f74e101Schristos 508c74ad251Schristos ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 509c74ad251Schristos GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id), 510c74ad251Schristos GET_U_1(msg_ptr.cfm_ltr->ttl)); 5110f74e101Schristos 512c74ad251Schristos ND_PRINT("\n\t Replay-Action %s (%u)", 5130f74e101Schristos tok2str(cfm_ltr_replay_action_values, 5140f74e101Schristos "Unknown", 515c74ad251Schristos GET_U_1(msg_ptr.cfm_ltr->replay_action)), 516c74ad251Schristos GET_U_1(msg_ptr.cfm_ltr->replay_action)); 5170f74e101Schristos break; 5180f74e101Schristos 5190f74e101Schristos /* 5200f74e101Schristos * No message decoder yet. 5210f74e101Schristos * Hexdump everything up until the start of the TLVs 5220f74e101Schristos */ 5230f74e101Schristos case CFM_OPCODE_LBR: 5240f74e101Schristos case CFM_OPCODE_LBM: 5250f74e101Schristos default: 526b3a00663Schristos print_unknown_data(ndo, tptr, "\n\t ", 527c74ad251Schristos tlen - first_tlv_offset); 5280f74e101Schristos break; 5290f74e101Schristos } 5300f74e101Schristos 531c74ad251Schristos tptr += first_tlv_offset; 532c74ad251Schristos tlen -= first_tlv_offset; 5330f74e101Schristos 5340f74e101Schristos while (tlen > 0) { 5350f74e101Schristos cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 5360f74e101Schristos 5370f74e101Schristos /* Enough to read the tlv type ? */ 538c74ad251Schristos cfm_tlv_type = GET_U_1(cfm_tlv_header->type); 5390f74e101Schristos 540c74ad251Schristos ND_PRINT("\n\t%s TLV (0x%02x)", 5410f74e101Schristos tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type), 542c74ad251Schristos cfm_tlv_type); 5430f74e101Schristos 544dc860a36Sspz if (cfm_tlv_type == CFM_TLV_END) { 545dc860a36Sspz /* Length is "Not present if the Type field is 0." */ 5460f74e101Schristos return; 5470f74e101Schristos } 5480f74e101Schristos 549dc860a36Sspz /* do we have the full tlv header ? */ 550dc860a36Sspz if (tlen < sizeof(struct cfm_tlv_header_t)) 551dc860a36Sspz goto tooshort; 552c74ad251Schristos ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t)); 553c74ad251Schristos cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length); 554dc860a36Sspz 555c74ad251Schristos ND_PRINT(", length %u", cfm_tlv_len); 556dc860a36Sspz 5570f74e101Schristos tptr += sizeof(struct cfm_tlv_header_t); 5580f74e101Schristos tlen -= sizeof(struct cfm_tlv_header_t); 5590f74e101Schristos tlv_ptr = tptr; 5600f74e101Schristos 561dc860a36Sspz /* do we have the full tlv ? */ 562dc860a36Sspz if (tlen < cfm_tlv_len) 563dc860a36Sspz goto tooshort; 564c74ad251Schristos ND_TCHECK_LEN(tptr, cfm_tlv_len); 5650f74e101Schristos hexdump = FALSE; 5660f74e101Schristos 5670f74e101Schristos switch(cfm_tlv_type) { 5680f74e101Schristos case CFM_TLV_PORT_STATUS: 569dc860a36Sspz if (cfm_tlv_len < 1) { 570c74ad251Schristos ND_PRINT(" (too short, must be >= 1)"); 571dc860a36Sspz return; 572dc860a36Sspz } 573c74ad251Schristos ND_PRINT(", Status: %s (%u)", 574c74ad251Schristos tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)), 575c74ad251Schristos GET_U_1(tptr)); 5760f74e101Schristos break; 5770f74e101Schristos 5780f74e101Schristos case CFM_TLV_INTERFACE_STATUS: 579dc860a36Sspz if (cfm_tlv_len < 1) { 580c74ad251Schristos ND_PRINT(" (too short, must be >= 1)"); 581dc860a36Sspz return; 582dc860a36Sspz } 583c74ad251Schristos ND_PRINT(", Status: %s (%u)", 584c74ad251Schristos tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)), 585c74ad251Schristos GET_U_1(tptr)); 5860f74e101Schristos break; 5870f74e101Schristos 5880f74e101Schristos case CFM_TLV_PRIVATE: 589dc860a36Sspz if (cfm_tlv_len < 4) { 590c74ad251Schristos ND_PRINT(" (too short, must be >= 4)"); 591dc860a36Sspz return; 592dc860a36Sspz } 593c74ad251Schristos ND_PRINT(", Vendor: %s (%u), Sub-Type %u", 594c74ad251Schristos tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)), 595c74ad251Schristos GET_BE_U_3(tptr), 596c74ad251Schristos GET_U_1(tptr + 3)); 5970f74e101Schristos hexdump = TRUE; 5980f74e101Schristos break; 5990f74e101Schristos 6000f74e101Schristos case CFM_TLV_SENDER_ID: 6010f74e101Schristos { 6020f74e101Schristos u_int chassis_id_type, chassis_id_length; 6030f74e101Schristos u_int mgmt_addr_length; 6040f74e101Schristos 605dc860a36Sspz if (cfm_tlv_len < 1) { 606c74ad251Schristos ND_PRINT(" (too short, must be >= 1)"); 60772c96ff3Schristos goto next_tlv; 6080f74e101Schristos } 6090f74e101Schristos 610dc860a36Sspz /* 611dc860a36Sspz * Get the Chassis ID length and check it. 61272c96ff3Schristos * IEEE 802.1Q-2014 Section 21.5.3.1 613dc860a36Sspz */ 614c74ad251Schristos chassis_id_length = GET_U_1(tptr); 6150f74e101Schristos tptr++; 6160f74e101Schristos tlen--; 617dc860a36Sspz cfm_tlv_len--; 6180f74e101Schristos 6190f74e101Schristos if (chassis_id_length) { 62072c96ff3Schristos /* 62172c96ff3Schristos * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references 62272c96ff3Schristos * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently 62372c96ff3Schristos * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype 62472c96ff3Schristos */ 625dc860a36Sspz if (cfm_tlv_len < 1) { 626c74ad251Schristos ND_PRINT("\n\t (TLV too short)"); 62772c96ff3Schristos goto next_tlv; 628dc860a36Sspz } 629c74ad251Schristos chassis_id_type = GET_U_1(tptr); 630dc860a36Sspz cfm_tlv_len--; 631c74ad251Schristos ND_PRINT("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u", 6320f74e101Schristos tok2str(cfm_tlv_senderid_chassisid_values, 6330f74e101Schristos "Unknown", 6340f74e101Schristos chassis_id_type), 6350f74e101Schristos chassis_id_type, 636c74ad251Schristos chassis_id_length); 6370f74e101Schristos 638dc860a36Sspz if (cfm_tlv_len < chassis_id_length) { 639c74ad251Schristos ND_PRINT("\n\t (TLV too short)"); 64072c96ff3Schristos goto next_tlv; 641dc860a36Sspz } 642dc860a36Sspz 64372c96ff3Schristos /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */ 6440f74e101Schristos switch (chassis_id_type) { 6450f74e101Schristos case CFM_CHASSIS_ID_MAC_ADDRESS: 646c74ad251Schristos if (chassis_id_length != MAC_ADDR_LEN) { 647c74ad251Schristos ND_PRINT(" (invalid MAC address length)"); 64872c96ff3Schristos hexdump = TRUE; 64972c96ff3Schristos break; 65072c96ff3Schristos } 651c74ad251Schristos ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(tptr + 1)); 6520f74e101Schristos break; 6530f74e101Schristos 6540f74e101Schristos case CFM_CHASSIS_ID_NETWORK_ADDRESS: 65572c96ff3Schristos hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length); 6560f74e101Schristos break; 6570f74e101Schristos 6580f74e101Schristos case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */ 6590f74e101Schristos case CFM_CHASSIS_ID_INTERFACE_ALIAS: 6600f74e101Schristos case CFM_CHASSIS_ID_LOCAL: 6610f74e101Schristos case CFM_CHASSIS_ID_CHASSIS_COMPONENT: 6620f74e101Schristos case CFM_CHASSIS_ID_PORT_COMPONENT: 663c74ad251Schristos nd_printjnp(ndo, tptr + 1, chassis_id_length); 6640f74e101Schristos break; 6650f74e101Schristos 6660f74e101Schristos default: 6670f74e101Schristos hexdump = TRUE; 6680f74e101Schristos break; 6690f74e101Schristos } 670dc860a36Sspz cfm_tlv_len -= chassis_id_length; 6710f74e101Schristos 672dc860a36Sspz tptr += 1 + chassis_id_length; 673dc860a36Sspz tlen -= 1 + chassis_id_length; 674dc860a36Sspz } 6750f74e101Schristos 6760f74e101Schristos /* 6770f74e101Schristos * Check if there is a Management Address. 67872c96ff3Schristos * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length 67972c96ff3Schristos * This and all subsequent fields are not present if the TLV length 68072c96ff3Schristos * allows only the above fields. 6810f74e101Schristos */ 682dc860a36Sspz if (cfm_tlv_len == 0) { 683dc860a36Sspz /* No, there isn't; we're done. */ 68472c96ff3Schristos break; 6850f74e101Schristos } 6860f74e101Schristos 68772c96ff3Schristos /* Here mgmt_addr_length stands for the management domain length. */ 688c74ad251Schristos mgmt_addr_length = GET_U_1(tptr); 6890f74e101Schristos tptr++; 6900f74e101Schristos tlen--; 691dc860a36Sspz cfm_tlv_len--; 692c74ad251Schristos ND_PRINT("\n\t Management Address Domain Length %u", mgmt_addr_length); 6930f74e101Schristos if (mgmt_addr_length) { 69472c96ff3Schristos /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */ 695dc860a36Sspz if (cfm_tlv_len < mgmt_addr_length) { 696c74ad251Schristos ND_PRINT("\n\t (TLV too short)"); 69772c96ff3Schristos goto next_tlv; 6980f74e101Schristos } 699dc860a36Sspz cfm_tlv_len -= mgmt_addr_length; 700dc860a36Sspz /* 701dc860a36Sspz * XXX - this is an OID; print it as such. 702dc860a36Sspz */ 70372c96ff3Schristos hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length); 7040f74e101Schristos tptr += mgmt_addr_length; 7050f74e101Schristos tlen -= mgmt_addr_length; 7060f74e101Schristos 70772c96ff3Schristos /* 70872c96ff3Schristos * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length 70972c96ff3Schristos * This field is present if Management Address Domain Length is not 0. 71072c96ff3Schristos */ 711dc860a36Sspz if (cfm_tlv_len < 1) { 712c74ad251Schristos ND_PRINT(" (Management Address Length is missing)"); 71372c96ff3Schristos hexdump = TRUE; 71472c96ff3Schristos break; 715dc860a36Sspz } 716dc860a36Sspz 71772c96ff3Schristos /* Here mgmt_addr_length stands for the management address length. */ 718c74ad251Schristos mgmt_addr_length = GET_U_1(tptr); 719dc860a36Sspz tptr++; 720dc860a36Sspz tlen--; 721dc860a36Sspz cfm_tlv_len--; 722c74ad251Schristos ND_PRINT("\n\t Management Address Length %u", mgmt_addr_length); 723dc860a36Sspz if (mgmt_addr_length) { 72472c96ff3Schristos /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */ 725dc860a36Sspz if (cfm_tlv_len < mgmt_addr_length) { 726c74ad251Schristos ND_PRINT("\n\t (TLV too short)"); 727dc860a36Sspz return; 728dc860a36Sspz } 729dc860a36Sspz cfm_tlv_len -= mgmt_addr_length; 730dc860a36Sspz /* 731dc860a36Sspz * XXX - this is a TransportDomain; print it as such. 732dc860a36Sspz */ 73372c96ff3Schristos hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length); 734dc860a36Sspz tptr += mgmt_addr_length; 735dc860a36Sspz tlen -= mgmt_addr_length; 736dc860a36Sspz } 7370f74e101Schristos } 7380f74e101Schristos break; 739dc860a36Sspz } 7400f74e101Schristos 7410f74e101Schristos /* 7420f74e101Schristos * FIXME those are the defined TLVs that lack a decoder 7430f74e101Schristos * you are welcome to contribute code ;-) 7440f74e101Schristos */ 7450f74e101Schristos 7460f74e101Schristos case CFM_TLV_DATA: 7470f74e101Schristos case CFM_TLV_REPLY_INGRESS: 7480f74e101Schristos case CFM_TLV_REPLY_EGRESS: 7490f74e101Schristos default: 7500f74e101Schristos hexdump = TRUE; 7510f74e101Schristos break; 7520f74e101Schristos } 7530f74e101Schristos /* do we want to see an additional hexdump ? */ 754b3a00663Schristos if (hexdump || ndo->ndo_vflag > 1) 755b3a00663Schristos print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len); 7560f74e101Schristos 75772c96ff3Schristos next_tlv: 7580f74e101Schristos tptr+=cfm_tlv_len; 7590f74e101Schristos tlen-=cfm_tlv_len; 7600f74e101Schristos } 7610f74e101Schristos return; 762dc860a36Sspz 763dc860a36Sspz tooshort: 764c74ad251Schristos ND_PRINT("\n\t\t packet is too short"); 765dc860a36Sspz return; 766dc860a36Sspz 7670f74e101Schristos trunc: 768c74ad251Schristos nd_print_trunc(ndo); 7690f74e101Schristos } 770