xref: /netbsd-src/external/bsd/tcpdump/dist/print-cfm.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
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