xref: /freebsd-src/contrib/tcpdump/print-cfm.c (revision 0a7e5f1f02aad2ff5fff1c60f44c6975fd07e1d9)
1a5779b6eSRui Paulo /*
2a5779b6eSRui Paulo  * Copyright (c) 1998-2006 The TCPDUMP project
3a5779b6eSRui Paulo  *
4a5779b6eSRui Paulo  * Redistribution and use in source and binary forms, with or without
5a5779b6eSRui Paulo  * modification, are permitted provided that: (1) source code
6a5779b6eSRui Paulo  * distributions retain the above copyright notice and this paragraph
7a5779b6eSRui Paulo  * in its entirety, and (2) distributions including binary code include
8a5779b6eSRui Paulo  * the above copyright notice and this paragraph in its entirety in
9a5779b6eSRui Paulo  * the documentation or other materials provided with the distribution.
10a5779b6eSRui Paulo  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11a5779b6eSRui Paulo  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12a5779b6eSRui Paulo  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13a5779b6eSRui Paulo  * FOR A PARTICULAR PURPOSE.
14a5779b6eSRui Paulo  *
150bff6a5aSEd Maste  * Original code by Hannes Gredler (hannes@gredler.at)
16a5779b6eSRui Paulo  */
17a5779b6eSRui Paulo 
183340d773SGleb Smirnoff /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */
193340d773SGleb Smirnoff 
20*ee67461eSJoseph Mingrone #include <config.h>
21a5779b6eSRui Paulo 
22*ee67461eSJoseph Mingrone #include "netdissect-stdinc.h"
23a5779b6eSRui Paulo 
243340d773SGleb Smirnoff #include "netdissect.h"
25a5779b6eSRui Paulo #include "extract.h"
26a5779b6eSRui Paulo #include "addrtoname.h"
27a5779b6eSRui Paulo #include "oui.h"
28a5779b6eSRui Paulo #include "af.h"
29a5779b6eSRui Paulo 
30*ee67461eSJoseph Mingrone 
31a5779b6eSRui Paulo struct cfm_common_header_t {
32*ee67461eSJoseph Mingrone     nd_uint8_t mdlevel_version;
33*ee67461eSJoseph Mingrone     nd_uint8_t opcode;
34*ee67461eSJoseph Mingrone     nd_uint8_t flags;
35*ee67461eSJoseph Mingrone     nd_uint8_t first_tlv_offset;
36a5779b6eSRui Paulo };
37a5779b6eSRui Paulo 
38a5779b6eSRui Paulo #define	CFM_VERSION 0
39*ee67461eSJoseph Mingrone #define CFM_EXTRACT_VERSION(x) ((x)&0x1f)
40a5779b6eSRui Paulo #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
41a5779b6eSRui Paulo 
42a5779b6eSRui Paulo #define	CFM_OPCODE_CCM 1
43a5779b6eSRui Paulo #define	CFM_OPCODE_LBR 2
44a5779b6eSRui Paulo #define	CFM_OPCODE_LBM 3
45a5779b6eSRui Paulo #define	CFM_OPCODE_LTR 4
46a5779b6eSRui Paulo #define	CFM_OPCODE_LTM 5
47a5779b6eSRui Paulo 
48a5779b6eSRui Paulo static const struct tok cfm_opcode_values[] = {
49*ee67461eSJoseph Mingrone     { CFM_OPCODE_CCM, "Continuity Check Message"},
50a5779b6eSRui Paulo     { CFM_OPCODE_LBR, "Loopback Reply"},
51a5779b6eSRui Paulo     { CFM_OPCODE_LBM, "Loopback Message"},
52a5779b6eSRui Paulo     { CFM_OPCODE_LTR, "Linktrace Reply"},
53a5779b6eSRui Paulo     { CFM_OPCODE_LTM, "Linktrace Message"},
54a5779b6eSRui Paulo     { 0, NULL}
55a5779b6eSRui Paulo };
56a5779b6eSRui Paulo 
57a5779b6eSRui Paulo /*
58a5779b6eSRui Paulo  * Message Formats.
59a5779b6eSRui Paulo  */
60a5779b6eSRui Paulo struct cfm_ccm_t {
61*ee67461eSJoseph Mingrone     nd_uint32_t sequence;
62*ee67461eSJoseph Mingrone     nd_uint16_t ma_epi;
63*ee67461eSJoseph Mingrone     nd_byte     names[48];
64*ee67461eSJoseph Mingrone     nd_byte     itu_t_y_1731[16];
65a5779b6eSRui Paulo };
66a5779b6eSRui Paulo 
67a5779b6eSRui Paulo /*
68a5779b6eSRui Paulo  * Timer Bases for the CCM Interval field.
69a5779b6eSRui Paulo  * Expressed in units of seconds.
70a5779b6eSRui Paulo  */
71*ee67461eSJoseph Mingrone static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f};
72a5779b6eSRui Paulo #define CCM_INTERVAL_MIN_MULTIPLIER 3.25
73a5779b6eSRui Paulo #define CCM_INTERVAL_MAX_MULTIPLIER 3.5
74a5779b6eSRui Paulo 
75a5779b6eSRui Paulo #define CFM_CCM_RDI_FLAG 0x80
76*ee67461eSJoseph Mingrone #define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07)
77a5779b6eSRui Paulo 
78a5779b6eSRui Paulo #define CFM_CCM_MD_FORMAT_8021 0
79a5779b6eSRui Paulo #define CFM_CCM_MD_FORMAT_NONE 1
80a5779b6eSRui Paulo #define CFM_CCM_MD_FORMAT_DNS  2
81a5779b6eSRui Paulo #define CFM_CCM_MD_FORMAT_MAC  3
82a5779b6eSRui Paulo #define CFM_CCM_MD_FORMAT_CHAR 4
83a5779b6eSRui Paulo 
84a5779b6eSRui Paulo static const struct tok cfm_md_nameformat_values[] = {
85a5779b6eSRui Paulo     { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"},
86a5779b6eSRui Paulo     { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"},
87a5779b6eSRui Paulo     { CFM_CCM_MD_FORMAT_DNS, "DNS string"},
88a5779b6eSRui Paulo     { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"},
89a5779b6eSRui Paulo     { CFM_CCM_MD_FORMAT_CHAR, "Character string"},
90a5779b6eSRui Paulo     { 0, NULL}
91a5779b6eSRui Paulo };
92a5779b6eSRui Paulo 
93a5779b6eSRui Paulo #define CFM_CCM_MA_FORMAT_8021 0
94a5779b6eSRui Paulo #define CFM_CCM_MA_FORMAT_VID  1
95a5779b6eSRui Paulo #define CFM_CCM_MA_FORMAT_CHAR 2
96a5779b6eSRui Paulo #define CFM_CCM_MA_FORMAT_INT  3
97a5779b6eSRui Paulo #define CFM_CCM_MA_FORMAT_VPN  4
98a5779b6eSRui Paulo 
99a5779b6eSRui Paulo static const struct tok cfm_ma_nameformat_values[] = {
100a5779b6eSRui Paulo     { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"},
101a5779b6eSRui Paulo     { CFM_CCM_MA_FORMAT_VID, "Primary VID"},
102a5779b6eSRui Paulo     { CFM_CCM_MA_FORMAT_CHAR, "Character string"},
103a5779b6eSRui Paulo     { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"},
104a5779b6eSRui Paulo     { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"},
105a5779b6eSRui Paulo     { 0, NULL}
106a5779b6eSRui Paulo };
107a5779b6eSRui Paulo 
108a5779b6eSRui Paulo struct cfm_lbm_t {
109*ee67461eSJoseph Mingrone     nd_uint32_t transaction_id;
110a5779b6eSRui Paulo };
111a5779b6eSRui Paulo 
112a5779b6eSRui Paulo struct cfm_ltm_t {
113*ee67461eSJoseph Mingrone     nd_uint32_t transaction_id;
114*ee67461eSJoseph Mingrone     nd_uint8_t  ttl;
115*ee67461eSJoseph Mingrone     nd_mac_addr original_mac;
116*ee67461eSJoseph Mingrone     nd_mac_addr target_mac;
117a5779b6eSRui Paulo };
118a5779b6eSRui Paulo 
119a5779b6eSRui Paulo static const struct tok cfm_ltm_flag_values[] = {
120a5779b6eSRui Paulo     { 0x80, "Use Forwarding-DB only"},
121a5779b6eSRui Paulo     { 0, NULL}
122a5779b6eSRui Paulo };
123a5779b6eSRui Paulo 
124a5779b6eSRui Paulo struct cfm_ltr_t {
125*ee67461eSJoseph Mingrone     nd_uint32_t transaction_id;
126*ee67461eSJoseph Mingrone     nd_uint8_t  ttl;
127*ee67461eSJoseph Mingrone     nd_uint8_t  replay_action;
128a5779b6eSRui Paulo };
129a5779b6eSRui Paulo 
130a5779b6eSRui Paulo static const struct tok cfm_ltr_flag_values[] = {
1313c602fabSXin LI     { 0x80, "UseFDB Only"},
1323c602fabSXin LI     { 0x40, "FwdYes"},
1333c602fabSXin LI     { 0x20, "Terminal MEP"},
134a5779b6eSRui Paulo     { 0, NULL}
135a5779b6eSRui Paulo };
136a5779b6eSRui Paulo 
137a5779b6eSRui Paulo static const struct tok cfm_ltr_replay_action_values[] = {
138a5779b6eSRui Paulo     { 1, "Exact Match"},
139a5779b6eSRui Paulo     { 2, "Filtering DB"},
140a5779b6eSRui Paulo     { 3, "MIP CCM DB"},
141a5779b6eSRui Paulo     { 0, NULL}
142a5779b6eSRui Paulo };
143a5779b6eSRui Paulo 
144a5779b6eSRui Paulo 
145a5779b6eSRui Paulo #define CFM_TLV_END 0
146a5779b6eSRui Paulo #define CFM_TLV_SENDER_ID 1
147a5779b6eSRui Paulo #define CFM_TLV_PORT_STATUS 2
148a5779b6eSRui Paulo #define CFM_TLV_INTERFACE_STATUS 3
149a5779b6eSRui Paulo #define CFM_TLV_DATA 4
150a5779b6eSRui Paulo #define CFM_TLV_REPLY_INGRESS 5
151a5779b6eSRui Paulo #define CFM_TLV_REPLY_EGRESS 6
152a5779b6eSRui Paulo #define CFM_TLV_PRIVATE 31
153a5779b6eSRui Paulo 
154a5779b6eSRui Paulo static const struct tok cfm_tlv_values[] = {
155a5779b6eSRui Paulo     { CFM_TLV_END, "End"},
156a5779b6eSRui Paulo     { CFM_TLV_SENDER_ID, "Sender ID"},
157a5779b6eSRui Paulo     { CFM_TLV_PORT_STATUS, "Port status"},
158a5779b6eSRui Paulo     { CFM_TLV_INTERFACE_STATUS, "Interface status"},
159a5779b6eSRui Paulo     { CFM_TLV_DATA, "Data"},
160a5779b6eSRui Paulo     { CFM_TLV_REPLY_INGRESS, "Reply Ingress"},
161a5779b6eSRui Paulo     { CFM_TLV_REPLY_EGRESS, "Reply Egress"},
162a5779b6eSRui Paulo     { CFM_TLV_PRIVATE, "Organization Specific"},
163a5779b6eSRui Paulo     { 0, NULL}
164a5779b6eSRui Paulo };
165a5779b6eSRui Paulo 
166a5779b6eSRui Paulo /*
167a5779b6eSRui Paulo  * TLVs
168a5779b6eSRui Paulo  */
169a5779b6eSRui Paulo 
170a5779b6eSRui Paulo struct cfm_tlv_header_t {
171*ee67461eSJoseph Mingrone     nd_uint8_t  type;
172*ee67461eSJoseph Mingrone     nd_uint16_t length;
173a5779b6eSRui Paulo };
174a5779b6eSRui Paulo 
175a5779b6eSRui Paulo /* FIXME define TLV formats */
176a5779b6eSRui Paulo 
177a5779b6eSRui Paulo static const struct tok cfm_tlv_port_status_values[] = {
178a5779b6eSRui Paulo     { 1, "Blocked"},
179a5779b6eSRui Paulo     { 2, "Up"},
180a5779b6eSRui Paulo     { 0, NULL}
181a5779b6eSRui Paulo };
182a5779b6eSRui Paulo 
183a5779b6eSRui Paulo static const struct tok cfm_tlv_interface_status_values[] = {
184a5779b6eSRui Paulo     { 1, "Up"},
185a5779b6eSRui Paulo     { 2, "Down"},
186a5779b6eSRui Paulo     { 3, "Testing"},
187a5779b6eSRui Paulo     { 5, "Dormant"},
188a5779b6eSRui Paulo     { 6, "not present"},
189a5779b6eSRui Paulo     { 7, "lower Layer down"},
190a5779b6eSRui Paulo     { 0, NULL}
191a5779b6eSRui Paulo };
192a5779b6eSRui Paulo 
193a5779b6eSRui Paulo #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1
194a5779b6eSRui Paulo #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2
195a5779b6eSRui Paulo #define CFM_CHASSIS_ID_PORT_COMPONENT 3
196a5779b6eSRui Paulo #define CFM_CHASSIS_ID_MAC_ADDRESS 4
197a5779b6eSRui Paulo #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5
198a5779b6eSRui Paulo #define CFM_CHASSIS_ID_INTERFACE_NAME 6
199a5779b6eSRui Paulo #define CFM_CHASSIS_ID_LOCAL 7
200a5779b6eSRui Paulo 
201a5779b6eSRui Paulo static const struct tok cfm_tlv_senderid_chassisid_values[] = {
202a5779b6eSRui Paulo     { 0, "Reserved"},
203a5779b6eSRui Paulo     { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"},
204a5779b6eSRui Paulo     { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"},
205a5779b6eSRui Paulo     { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"},
206a5779b6eSRui Paulo     { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"},
207a5779b6eSRui Paulo     { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"},
208a5779b6eSRui Paulo     { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"},
209a5779b6eSRui Paulo     { CFM_CHASSIS_ID_LOCAL, "Locally assigned"},
210a5779b6eSRui Paulo     { 0, NULL}
211a5779b6eSRui Paulo };
212a5779b6eSRui Paulo 
213a5779b6eSRui Paulo 
2143c602fabSXin LI static int
2153340d773SGleb Smirnoff cfm_network_addr_print(netdissect_options *ndo,
216*ee67461eSJoseph Mingrone                        const u_char *tptr, const u_int length)
2178bdc5a62SPatrick Kelsey {
2183340d773SGleb Smirnoff     u_int network_addr_type;
219a5779b6eSRui Paulo     u_int hexdump =  FALSE;
220a5779b6eSRui Paulo 
221a5779b6eSRui Paulo     /*
222*ee67461eSJoseph Mingrone      * Although AFIs are typically 2 octets wide,
223a5779b6eSRui Paulo      * 802.1ab specifies that this field width
224*ee67461eSJoseph Mingrone      * is only one octet.
225a5779b6eSRui Paulo      */
2260bff6a5aSEd Maste     if (length < 1) {
227*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Network Address Type (invalid, no data");
2280bff6a5aSEd Maste         return hexdump;
2290bff6a5aSEd Maste     }
2300bff6a5aSEd Maste     /* The calling function must make any due ND_TCHECK calls. */
231*ee67461eSJoseph Mingrone     network_addr_type = GET_U_1(tptr);
232*ee67461eSJoseph Mingrone     ND_PRINT("\n\t  Network Address Type %s (%u)",
2333340d773SGleb Smirnoff            tok2str(af_values, "Unknown", network_addr_type),
234*ee67461eSJoseph Mingrone            network_addr_type);
235a5779b6eSRui Paulo 
236a5779b6eSRui Paulo     /*
237a5779b6eSRui Paulo      * Resolve the passed in Address.
238a5779b6eSRui Paulo      */
2393340d773SGleb Smirnoff     switch(network_addr_type) {
240a5779b6eSRui Paulo     case AFNUM_INET:
2410bff6a5aSEd Maste         if (length != 1 + 4) {
242*ee67461eSJoseph Mingrone             ND_PRINT("(invalid IPv4 address length %u)", length - 1);
2430bff6a5aSEd Maste             hexdump = TRUE;
2440bff6a5aSEd Maste             break;
2450bff6a5aSEd Maste         }
246*ee67461eSJoseph Mingrone         ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1));
247a5779b6eSRui Paulo         break;
248a5779b6eSRui Paulo 
249a5779b6eSRui Paulo     case AFNUM_INET6:
2500bff6a5aSEd Maste         if (length != 1 + 16) {
251*ee67461eSJoseph Mingrone             ND_PRINT("(invalid IPv6 address length %u)", length - 1);
2520bff6a5aSEd Maste             hexdump = TRUE;
2530bff6a5aSEd Maste             break;
2540bff6a5aSEd Maste         }
255*ee67461eSJoseph Mingrone         ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1));
256a5779b6eSRui Paulo         break;
257a5779b6eSRui Paulo 
258a5779b6eSRui Paulo     default:
259a5779b6eSRui Paulo         hexdump = TRUE;
260a5779b6eSRui Paulo         break;
261a5779b6eSRui Paulo     }
262a5779b6eSRui Paulo 
263a5779b6eSRui Paulo     return hexdump;
264a5779b6eSRui Paulo }
265a5779b6eSRui Paulo 
266a5779b6eSRui Paulo void
2673c602fabSXin LI cfm_print(netdissect_options *ndo,
268*ee67461eSJoseph Mingrone           const u_char *pptr, u_int length)
2698bdc5a62SPatrick Kelsey {
270a5779b6eSRui Paulo     const struct cfm_common_header_t *cfm_common_header;
271*ee67461eSJoseph Mingrone     uint8_t mdlevel_version, opcode, flags, first_tlv_offset;
272a5779b6eSRui Paulo     const struct cfm_tlv_header_t *cfm_tlv_header;
2733340d773SGleb Smirnoff     const uint8_t *tptr, *tlv_ptr;
2743340d773SGleb Smirnoff     const uint8_t *namesp;
2753340d773SGleb Smirnoff     u_int names_data_remaining;
2763340d773SGleb Smirnoff     uint8_t md_nameformat, md_namelength;
2773340d773SGleb Smirnoff     const uint8_t *md_name;
2783340d773SGleb Smirnoff     uint8_t ma_nameformat, ma_namelength;
2793340d773SGleb Smirnoff     const uint8_t *ma_name;
280a5779b6eSRui Paulo     u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
281a5779b6eSRui Paulo 
282a5779b6eSRui Paulo 
283a5779b6eSRui Paulo     union {
284a5779b6eSRui Paulo         const struct cfm_ccm_t *cfm_ccm;
285a5779b6eSRui Paulo         const struct cfm_lbm_t *cfm_lbm;
286a5779b6eSRui Paulo         const struct cfm_ltm_t *cfm_ltm;
287a5779b6eSRui Paulo         const struct cfm_ltr_t *cfm_ltr;
288a5779b6eSRui Paulo     } msg_ptr;
289a5779b6eSRui Paulo 
290*ee67461eSJoseph Mingrone     ndo->ndo_protocol = "cfm";
291a5779b6eSRui Paulo     tptr=pptr;
292a5779b6eSRui Paulo     cfm_common_header = (const struct cfm_common_header_t *)pptr;
2933340d773SGleb Smirnoff     if (length < sizeof(*cfm_common_header))
2943340d773SGleb Smirnoff         goto tooshort;
295*ee67461eSJoseph Mingrone     ND_TCHECK_SIZE(cfm_common_header);
296a5779b6eSRui Paulo 
297a5779b6eSRui Paulo     /*
298a5779b6eSRui Paulo      * Sanity checking of the header.
299a5779b6eSRui Paulo      */
300*ee67461eSJoseph Mingrone     mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version);
301*ee67461eSJoseph Mingrone     if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) {
302*ee67461eSJoseph Mingrone 	ND_PRINT("CFMv%u not supported, length %u",
303*ee67461eSJoseph Mingrone                CFM_EXTRACT_VERSION(mdlevel_version), length);
304a5779b6eSRui Paulo 	return;
305a5779b6eSRui Paulo     }
306a5779b6eSRui Paulo 
307*ee67461eSJoseph Mingrone     opcode = GET_U_1(cfm_common_header->opcode);
308*ee67461eSJoseph Mingrone     ND_PRINT("CFMv%u %s, MD Level %u, length %u",
309*ee67461eSJoseph Mingrone            CFM_EXTRACT_VERSION(mdlevel_version),
310*ee67461eSJoseph Mingrone            tok2str(cfm_opcode_values, "unknown (%u)", opcode),
311*ee67461eSJoseph Mingrone            CFM_EXTRACT_MD_LEVEL(mdlevel_version),
312*ee67461eSJoseph Mingrone            length);
313a5779b6eSRui Paulo 
314a5779b6eSRui Paulo     /*
315a5779b6eSRui Paulo      * In non-verbose mode just print the opcode and md-level.
316a5779b6eSRui Paulo      */
3173c602fabSXin LI     if (ndo->ndo_vflag < 1) {
318a5779b6eSRui Paulo         return;
319a5779b6eSRui Paulo     }
320a5779b6eSRui Paulo 
321*ee67461eSJoseph Mingrone     flags = GET_U_1(cfm_common_header->flags);
322*ee67461eSJoseph Mingrone     first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset);
323*ee67461eSJoseph Mingrone     ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset);
324a5779b6eSRui Paulo 
325*ee67461eSJoseph Mingrone     tptr += sizeof(struct cfm_common_header_t);
326a5779b6eSRui Paulo     tlen = length - sizeof(struct cfm_common_header_t);
327a5779b6eSRui Paulo 
3283340d773SGleb Smirnoff     /*
3293340d773SGleb Smirnoff      * Sanity check the first TLV offset.
3303340d773SGleb Smirnoff      */
331*ee67461eSJoseph Mingrone     if (first_tlv_offset > tlen) {
332*ee67461eSJoseph Mingrone         ND_PRINT(" (too large, must be <= %u)", tlen);
3333340d773SGleb Smirnoff         return;
3343340d773SGleb Smirnoff     }
3353340d773SGleb Smirnoff 
336*ee67461eSJoseph Mingrone     switch (opcode) {
337a5779b6eSRui Paulo     case CFM_OPCODE_CCM:
338a5779b6eSRui Paulo         msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
339*ee67461eSJoseph Mingrone         if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
340*ee67461eSJoseph Mingrone             ND_PRINT(" (too small 1, must be >= %zu)",
341*ee67461eSJoseph Mingrone                      sizeof(*msg_ptr.cfm_ccm));
3423340d773SGleb Smirnoff             return;
3433340d773SGleb Smirnoff         }
3443340d773SGleb Smirnoff         if (tlen < sizeof(*msg_ptr.cfm_ccm))
3453340d773SGleb Smirnoff             goto tooshort;
346*ee67461eSJoseph Mingrone         ND_TCHECK_SIZE(msg_ptr.cfm_ccm);
347a5779b6eSRui Paulo 
348*ee67461eSJoseph Mingrone         ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags);
349*ee67461eSJoseph Mingrone         ND_PRINT(", Flags [CCM Interval %u%s]",
350a5779b6eSRui Paulo                ccm_interval,
351*ee67461eSJoseph Mingrone                flags & CFM_CCM_RDI_FLAG ?
352*ee67461eSJoseph Mingrone                ", RDI" : "");
353a5779b6eSRui Paulo 
354a5779b6eSRui Paulo         /*
355a5779b6eSRui Paulo          * Resolve the CCM interval field.
356a5779b6eSRui Paulo          */
357a5779b6eSRui Paulo         if (ccm_interval) {
358*ee67461eSJoseph Mingrone             ND_PRINT("\n\t  CCM Interval %.3fs"
359a5779b6eSRui Paulo                    ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
360a5779b6eSRui Paulo                    ccm_interval_base[ccm_interval],
361a5779b6eSRui Paulo                    ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
362*ee67461eSJoseph Mingrone                    ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER);
363a5779b6eSRui Paulo         }
364a5779b6eSRui Paulo 
365*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
366*ee67461eSJoseph Mingrone                GET_BE_U_4(msg_ptr.cfm_ccm->sequence),
367*ee67461eSJoseph Mingrone                GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi));
368a5779b6eSRui Paulo 
3693340d773SGleb Smirnoff         namesp = msg_ptr.cfm_ccm->names;
3703340d773SGleb Smirnoff         names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
371a5779b6eSRui Paulo 
372a5779b6eSRui Paulo         /*
373a5779b6eSRui Paulo          * Resolve the MD fields.
374a5779b6eSRui Paulo          */
375*ee67461eSJoseph Mingrone         md_nameformat = GET_U_1(namesp);
3763340d773SGleb Smirnoff         namesp++;
3773340d773SGleb Smirnoff         names_data_remaining--;  /* We know this is != 0 */
3783340d773SGleb Smirnoff         if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
379*ee67461eSJoseph Mingrone             md_namelength = GET_U_1(namesp);
3803340d773SGleb Smirnoff             namesp++;
3813340d773SGleb Smirnoff             names_data_remaining--; /* We know this is !=0 */
382*ee67461eSJoseph Mingrone             ND_PRINT("\n\t  MD Name Format %s (%u), MD Name length %u",
383a5779b6eSRui Paulo                    tok2str(cfm_md_nameformat_values, "Unknown",
3843340d773SGleb Smirnoff                            md_nameformat),
3853340d773SGleb Smirnoff                    md_nameformat,
386*ee67461eSJoseph Mingrone                    md_namelength);
387a5779b6eSRui Paulo 
3880bff6a5aSEd Maste             /*
3890bff6a5aSEd Maste              * -3 for the MA short name format and length and one byte
3900bff6a5aSEd Maste              * of MA short name.
3910bff6a5aSEd Maste              */
3920bff6a5aSEd Maste             if (md_namelength > names_data_remaining - 3) {
393*ee67461eSJoseph Mingrone                 ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2);
3943340d773SGleb Smirnoff                 return;
3953340d773SGleb Smirnoff             }
3963340d773SGleb Smirnoff 
3973340d773SGleb Smirnoff             md_name = namesp;
398*ee67461eSJoseph Mingrone             ND_PRINT("\n\t  MD Name: ");
3993340d773SGleb Smirnoff             switch (md_nameformat) {
400a5779b6eSRui Paulo             case CFM_CCM_MD_FORMAT_DNS:
401a5779b6eSRui Paulo             case CFM_CCM_MD_FORMAT_CHAR:
402*ee67461eSJoseph Mingrone                 nd_printjnp(ndo, md_name, md_namelength);
403a5779b6eSRui Paulo                 break;
404a5779b6eSRui Paulo 
405a5779b6eSRui Paulo             case CFM_CCM_MD_FORMAT_MAC:
406*ee67461eSJoseph Mingrone                 if (md_namelength == MAC_ADDR_LEN) {
407*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  MAC %s", GET_ETHERADDR_STRING(md_name));
4083340d773SGleb Smirnoff                 } else {
409*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  MAC (length invalid)");
4103340d773SGleb Smirnoff                 }
411a5779b6eSRui Paulo                 break;
412a5779b6eSRui Paulo 
413a5779b6eSRui Paulo                 /* FIXME add printers for those MD formats - hexdump for now */
414a5779b6eSRui Paulo             case CFM_CCM_MA_FORMAT_8021:
415a5779b6eSRui Paulo             default:
4163340d773SGleb Smirnoff                 print_unknown_data(ndo, md_name, "\n\t    ",
4173340d773SGleb Smirnoff                                    md_namelength);
418a5779b6eSRui Paulo             }
4193340d773SGleb Smirnoff             namesp += md_namelength;
4203340d773SGleb Smirnoff             names_data_remaining -= md_namelength;
4213340d773SGleb Smirnoff         } else {
422*ee67461eSJoseph Mingrone             ND_PRINT("\n\t  MD Name Format %s (%u)",
4233340d773SGleb Smirnoff                    tok2str(cfm_md_nameformat_values, "Unknown",
4243340d773SGleb Smirnoff                            md_nameformat),
425*ee67461eSJoseph Mingrone                    md_nameformat);
426a5779b6eSRui Paulo         }
427a5779b6eSRui Paulo 
428a5779b6eSRui Paulo 
429a5779b6eSRui Paulo         /*
430a5779b6eSRui Paulo          * Resolve the MA fields.
431a5779b6eSRui Paulo          */
432*ee67461eSJoseph Mingrone         ma_nameformat = GET_U_1(namesp);
4333340d773SGleb Smirnoff         namesp++;
4343340d773SGleb Smirnoff         names_data_remaining--; /* We know this is != 0 */
435*ee67461eSJoseph Mingrone         ma_namelength = GET_U_1(namesp);
4363340d773SGleb Smirnoff         namesp++;
4373340d773SGleb Smirnoff         names_data_remaining--; /* We know this is != 0 */
438*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  MA Name-Format %s (%u), MA name length %u",
439a5779b6eSRui Paulo                tok2str(cfm_ma_nameformat_values, "Unknown",
4403340d773SGleb Smirnoff                        ma_nameformat),
4413340d773SGleb Smirnoff                ma_nameformat,
442*ee67461eSJoseph Mingrone                ma_namelength);
443a5779b6eSRui Paulo 
4443340d773SGleb Smirnoff         if (ma_namelength > names_data_remaining) {
445*ee67461eSJoseph Mingrone             ND_PRINT(" (too large, must be <= %u)", names_data_remaining);
4463340d773SGleb Smirnoff             return;
4473340d773SGleb Smirnoff         }
4483340d773SGleb Smirnoff 
4493340d773SGleb Smirnoff         ma_name = namesp;
450*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  MA Name: ");
4513340d773SGleb Smirnoff         switch (ma_nameformat) {
452a5779b6eSRui Paulo         case CFM_CCM_MA_FORMAT_CHAR:
453*ee67461eSJoseph Mingrone             nd_printjnp(ndo, ma_name, ma_namelength);
454a5779b6eSRui Paulo             break;
455a5779b6eSRui Paulo 
456a5779b6eSRui Paulo             /* FIXME add printers for those MA formats - hexdump for now */
457a5779b6eSRui Paulo         case CFM_CCM_MA_FORMAT_8021:
458a5779b6eSRui Paulo         case CFM_CCM_MA_FORMAT_VID:
459a5779b6eSRui Paulo         case CFM_CCM_MA_FORMAT_INT:
460a5779b6eSRui Paulo         case CFM_CCM_MA_FORMAT_VPN:
461a5779b6eSRui Paulo         default:
4623340d773SGleb Smirnoff             print_unknown_data(ndo, ma_name, "\n\t    ", ma_namelength);
463a5779b6eSRui Paulo         }
464a5779b6eSRui Paulo         break;
465a5779b6eSRui Paulo 
466a5779b6eSRui Paulo     case CFM_OPCODE_LTM:
46727df3f5dSRui Paulo         msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
468*ee67461eSJoseph Mingrone         if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
469*ee67461eSJoseph Mingrone             ND_PRINT(" (too small 4, must be >= %zu)",
470*ee67461eSJoseph Mingrone                      sizeof(*msg_ptr.cfm_ltm));
4713340d773SGleb Smirnoff             return;
4723340d773SGleb Smirnoff         }
4733340d773SGleb Smirnoff         if (tlen < sizeof(*msg_ptr.cfm_ltm))
4743340d773SGleb Smirnoff             goto tooshort;
475*ee67461eSJoseph Mingrone         ND_TCHECK_SIZE(msg_ptr.cfm_ltm);
47627df3f5dSRui Paulo 
477*ee67461eSJoseph Mingrone         ND_PRINT(", Flags [%s]",
478*ee67461eSJoseph Mingrone                bittok2str(cfm_ltm_flag_values, "none", flags));
479a5779b6eSRui Paulo 
480*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Transaction-ID 0x%08x, ttl %u",
481*ee67461eSJoseph Mingrone                GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id),
482*ee67461eSJoseph Mingrone                GET_U_1(msg_ptr.cfm_ltm->ttl));
483a5779b6eSRui Paulo 
484*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Original-MAC %s, Target-MAC %s",
485*ee67461eSJoseph Mingrone                GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac),
486*ee67461eSJoseph Mingrone                GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac));
487a5779b6eSRui Paulo         break;
488a5779b6eSRui Paulo 
489a5779b6eSRui Paulo     case CFM_OPCODE_LTR:
49027df3f5dSRui Paulo         msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
491*ee67461eSJoseph Mingrone         if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
492*ee67461eSJoseph Mingrone             ND_PRINT(" (too small 5, must be >= %zu)",
493*ee67461eSJoseph Mingrone                      sizeof(*msg_ptr.cfm_ltr));
4943340d773SGleb Smirnoff             return;
4953340d773SGleb Smirnoff         }
4963340d773SGleb Smirnoff         if (tlen < sizeof(*msg_ptr.cfm_ltr))
4973340d773SGleb Smirnoff             goto tooshort;
498*ee67461eSJoseph Mingrone         ND_TCHECK_SIZE(msg_ptr.cfm_ltr);
49927df3f5dSRui Paulo 
500*ee67461eSJoseph Mingrone         ND_PRINT(", Flags [%s]",
501*ee67461eSJoseph Mingrone                bittok2str(cfm_ltr_flag_values, "none", flags));
502a5779b6eSRui Paulo 
503*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Transaction-ID 0x%08x, ttl %u",
504*ee67461eSJoseph Mingrone                GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id),
505*ee67461eSJoseph Mingrone                GET_U_1(msg_ptr.cfm_ltr->ttl));
506a5779b6eSRui Paulo 
507*ee67461eSJoseph Mingrone         ND_PRINT("\n\t  Replay-Action %s (%u)",
508a5779b6eSRui Paulo                tok2str(cfm_ltr_replay_action_values,
509a5779b6eSRui Paulo                        "Unknown",
510*ee67461eSJoseph Mingrone                        GET_U_1(msg_ptr.cfm_ltr->replay_action)),
511*ee67461eSJoseph Mingrone                GET_U_1(msg_ptr.cfm_ltr->replay_action));
512a5779b6eSRui Paulo         break;
513a5779b6eSRui Paulo 
514a5779b6eSRui Paulo         /*
515a5779b6eSRui Paulo          * No message decoder yet.
516a5779b6eSRui Paulo          * Hexdump everything up until the start of the TLVs
517a5779b6eSRui Paulo          */
518a5779b6eSRui Paulo     case CFM_OPCODE_LBR:
519a5779b6eSRui Paulo     case CFM_OPCODE_LBM:
520a5779b6eSRui Paulo     default:
5213c602fabSXin LI         print_unknown_data(ndo, tptr, "\n\t  ",
522*ee67461eSJoseph Mingrone                            tlen -  first_tlv_offset);
523a5779b6eSRui Paulo         break;
524a5779b6eSRui Paulo     }
525a5779b6eSRui Paulo 
526*ee67461eSJoseph Mingrone     tptr += first_tlv_offset;
527*ee67461eSJoseph Mingrone     tlen -= first_tlv_offset;
528a5779b6eSRui Paulo 
529a5779b6eSRui Paulo     while (tlen > 0) {
530a5779b6eSRui Paulo         cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
531a5779b6eSRui Paulo 
532a5779b6eSRui Paulo         /* Enough to read the tlv type ? */
533*ee67461eSJoseph Mingrone         cfm_tlv_type = GET_U_1(cfm_tlv_header->type);
534a5779b6eSRui Paulo 
535*ee67461eSJoseph Mingrone         ND_PRINT("\n\t%s TLV (0x%02x)",
536a5779b6eSRui Paulo                tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
537*ee67461eSJoseph Mingrone                cfm_tlv_type);
538a5779b6eSRui Paulo 
5393340d773SGleb Smirnoff         if (cfm_tlv_type == CFM_TLV_END) {
5403340d773SGleb Smirnoff             /* Length is "Not present if the Type field is 0." */
541a5779b6eSRui Paulo             return;
542a5779b6eSRui Paulo         }
543a5779b6eSRui Paulo 
5443340d773SGleb Smirnoff         /* do we have the full tlv header ? */
5453340d773SGleb Smirnoff         if (tlen < sizeof(struct cfm_tlv_header_t))
5463340d773SGleb Smirnoff             goto tooshort;
547*ee67461eSJoseph Mingrone         ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t));
548*ee67461eSJoseph Mingrone         cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length);
5493340d773SGleb Smirnoff 
550*ee67461eSJoseph Mingrone         ND_PRINT(", length %u", cfm_tlv_len);
5513340d773SGleb Smirnoff 
552a5779b6eSRui Paulo         tptr += sizeof(struct cfm_tlv_header_t);
553a5779b6eSRui Paulo         tlen -= sizeof(struct cfm_tlv_header_t);
554a5779b6eSRui Paulo         tlv_ptr = tptr;
555a5779b6eSRui Paulo 
5563340d773SGleb Smirnoff         /* do we have the full tlv ? */
5573340d773SGleb Smirnoff         if (tlen < cfm_tlv_len)
5583340d773SGleb Smirnoff             goto tooshort;
559*ee67461eSJoseph Mingrone         ND_TCHECK_LEN(tptr, cfm_tlv_len);
560a5779b6eSRui Paulo         hexdump = FALSE;
561a5779b6eSRui Paulo 
562a5779b6eSRui Paulo         switch(cfm_tlv_type) {
563a5779b6eSRui Paulo         case CFM_TLV_PORT_STATUS:
5643340d773SGleb Smirnoff             if (cfm_tlv_len < 1) {
565*ee67461eSJoseph Mingrone                 ND_PRINT(" (too short, must be >= 1)");
5663340d773SGleb Smirnoff                 return;
5673340d773SGleb Smirnoff             }
568*ee67461eSJoseph Mingrone             ND_PRINT(", Status: %s (%u)",
569*ee67461eSJoseph Mingrone                    tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)),
570*ee67461eSJoseph Mingrone                    GET_U_1(tptr));
571a5779b6eSRui Paulo             break;
572a5779b6eSRui Paulo 
573a5779b6eSRui Paulo         case CFM_TLV_INTERFACE_STATUS:
5743340d773SGleb Smirnoff             if (cfm_tlv_len < 1) {
575*ee67461eSJoseph Mingrone                 ND_PRINT(" (too short, must be >= 1)");
5763340d773SGleb Smirnoff                 return;
5773340d773SGleb Smirnoff             }
578*ee67461eSJoseph Mingrone             ND_PRINT(", Status: %s (%u)",
579*ee67461eSJoseph Mingrone                    tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)),
580*ee67461eSJoseph Mingrone                    GET_U_1(tptr));
581a5779b6eSRui Paulo             break;
582a5779b6eSRui Paulo 
583a5779b6eSRui Paulo         case CFM_TLV_PRIVATE:
5843340d773SGleb Smirnoff             if (cfm_tlv_len < 4) {
585*ee67461eSJoseph Mingrone                 ND_PRINT(" (too short, must be >= 4)");
5863340d773SGleb Smirnoff                 return;
5873340d773SGleb Smirnoff             }
588*ee67461eSJoseph Mingrone             ND_PRINT(", Vendor: %s (%u), Sub-Type %u",
589*ee67461eSJoseph Mingrone                    tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)),
590*ee67461eSJoseph Mingrone                    GET_BE_U_3(tptr),
591*ee67461eSJoseph Mingrone                    GET_U_1(tptr + 3));
592a5779b6eSRui Paulo             hexdump = TRUE;
593a5779b6eSRui Paulo             break;
594a5779b6eSRui Paulo 
595a5779b6eSRui Paulo         case CFM_TLV_SENDER_ID:
596a5779b6eSRui Paulo         {
597a5779b6eSRui Paulo             u_int chassis_id_type, chassis_id_length;
598a5779b6eSRui Paulo             u_int mgmt_addr_length;
599a5779b6eSRui Paulo 
6003340d773SGleb Smirnoff             if (cfm_tlv_len < 1) {
601*ee67461eSJoseph Mingrone                 ND_PRINT(" (too short, must be >= 1)");
6020bff6a5aSEd Maste                 goto next_tlv;
603a5779b6eSRui Paulo             }
604a5779b6eSRui Paulo 
6053340d773SGleb Smirnoff             /*
6063340d773SGleb Smirnoff              * Get the Chassis ID length and check it.
6070bff6a5aSEd Maste              * IEEE 802.1Q-2014 Section 21.5.3.1
6083340d773SGleb Smirnoff              */
609*ee67461eSJoseph Mingrone             chassis_id_length = GET_U_1(tptr);
610a5779b6eSRui Paulo             tptr++;
611a5779b6eSRui Paulo             tlen--;
6123340d773SGleb Smirnoff             cfm_tlv_len--;
613a5779b6eSRui Paulo 
614a5779b6eSRui Paulo             if (chassis_id_length) {
6150bff6a5aSEd Maste                 /*
6160bff6a5aSEd Maste                  * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references
6170bff6a5aSEd Maste                  * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently
6180bff6a5aSEd Maste                  * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype
6190bff6a5aSEd Maste                  */
6203340d773SGleb Smirnoff                 if (cfm_tlv_len < 1) {
621*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  (TLV too short)");
6220bff6a5aSEd Maste                     goto next_tlv;
6233340d773SGleb Smirnoff                 }
624*ee67461eSJoseph Mingrone                 chassis_id_type = GET_U_1(tptr);
6253340d773SGleb Smirnoff                 cfm_tlv_len--;
626*ee67461eSJoseph Mingrone                 ND_PRINT("\n\t  Chassis-ID Type %s (%u), Chassis-ID length %u",
627a5779b6eSRui Paulo                        tok2str(cfm_tlv_senderid_chassisid_values,
628a5779b6eSRui Paulo                                "Unknown",
629a5779b6eSRui Paulo                                chassis_id_type),
630a5779b6eSRui Paulo                        chassis_id_type,
631*ee67461eSJoseph Mingrone                        chassis_id_length);
632a5779b6eSRui Paulo 
6333340d773SGleb Smirnoff                 if (cfm_tlv_len < chassis_id_length) {
634*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  (TLV too short)");
6350bff6a5aSEd Maste                     goto next_tlv;
6363340d773SGleb Smirnoff                 }
6373340d773SGleb Smirnoff 
6380bff6a5aSEd Maste                 /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */
639a5779b6eSRui Paulo                 switch (chassis_id_type) {
640a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_MAC_ADDRESS:
641*ee67461eSJoseph Mingrone                     if (chassis_id_length != MAC_ADDR_LEN) {
642*ee67461eSJoseph Mingrone                         ND_PRINT(" (invalid MAC address length)");
6430bff6a5aSEd Maste                         hexdump = TRUE;
6440bff6a5aSEd Maste                         break;
6450bff6a5aSEd Maste                     }
646*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  MAC %s", GET_ETHERADDR_STRING(tptr + 1));
647a5779b6eSRui Paulo                     break;
648a5779b6eSRui Paulo 
649a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_NETWORK_ADDRESS:
6500bff6a5aSEd Maste                     hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
651a5779b6eSRui Paulo                     break;
652a5779b6eSRui Paulo 
653a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
654a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_INTERFACE_ALIAS:
655a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_LOCAL:
656a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
657a5779b6eSRui Paulo                 case CFM_CHASSIS_ID_PORT_COMPONENT:
658*ee67461eSJoseph Mingrone                     nd_printjnp(ndo, tptr + 1, chassis_id_length);
659a5779b6eSRui Paulo                     break;
660a5779b6eSRui Paulo 
661a5779b6eSRui Paulo                 default:
662a5779b6eSRui Paulo                     hexdump = TRUE;
663a5779b6eSRui Paulo                     break;
664a5779b6eSRui Paulo                 }
6653340d773SGleb Smirnoff                 cfm_tlv_len -= chassis_id_length;
666a5779b6eSRui Paulo 
6673340d773SGleb Smirnoff                 tptr += 1 + chassis_id_length;
6683340d773SGleb Smirnoff                 tlen -= 1 + chassis_id_length;
6693340d773SGleb Smirnoff             }
670a5779b6eSRui Paulo 
671a5779b6eSRui Paulo             /*
672a5779b6eSRui Paulo              * Check if there is a Management Address.
6730bff6a5aSEd Maste              * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length
6740bff6a5aSEd Maste              * This and all subsequent fields are not present if the TLV length
6750bff6a5aSEd Maste              * allows only the above fields.
676a5779b6eSRui Paulo              */
6773340d773SGleb Smirnoff             if (cfm_tlv_len == 0) {
6783340d773SGleb Smirnoff                 /* No, there isn't; we're done. */
6790bff6a5aSEd Maste                 break;
680a5779b6eSRui Paulo             }
681a5779b6eSRui Paulo 
6820bff6a5aSEd Maste             /* Here mgmt_addr_length stands for the management domain length. */
683*ee67461eSJoseph Mingrone             mgmt_addr_length = GET_U_1(tptr);
684a5779b6eSRui Paulo             tptr++;
685a5779b6eSRui Paulo             tlen--;
6863340d773SGleb Smirnoff             cfm_tlv_len--;
687*ee67461eSJoseph Mingrone             ND_PRINT("\n\t  Management Address Domain Length %u", mgmt_addr_length);
688a5779b6eSRui Paulo             if (mgmt_addr_length) {
6890bff6a5aSEd Maste                 /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */
6903340d773SGleb Smirnoff                 if (cfm_tlv_len < mgmt_addr_length) {
691*ee67461eSJoseph Mingrone                     ND_PRINT("\n\t  (TLV too short)");
6920bff6a5aSEd Maste                     goto next_tlv;
693a5779b6eSRui Paulo                 }
6943340d773SGleb Smirnoff                 cfm_tlv_len -= mgmt_addr_length;
6953340d773SGleb Smirnoff                 /*
6963340d773SGleb Smirnoff                  * XXX - this is an OID; print it as such.
6973340d773SGleb Smirnoff                  */
6980bff6a5aSEd Maste                 hex_print(ndo, "\n\t  Management Address Domain: ", tptr, mgmt_addr_length);
699a5779b6eSRui Paulo                 tptr += mgmt_addr_length;
700a5779b6eSRui Paulo                 tlen -= mgmt_addr_length;
701a5779b6eSRui Paulo 
7020bff6a5aSEd Maste                 /*
7030bff6a5aSEd Maste                  * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length
7040bff6a5aSEd Maste                  * This field is present if Management Address Domain Length is not 0.
7050bff6a5aSEd Maste                  */
7063340d773SGleb Smirnoff                 if (cfm_tlv_len < 1) {
707*ee67461eSJoseph Mingrone                     ND_PRINT(" (Management Address Length is missing)");
7080bff6a5aSEd Maste                     hexdump = TRUE;
7090bff6a5aSEd Maste                     break;
7103340d773SGleb Smirnoff                 }
7113340d773SGleb Smirnoff 
7120bff6a5aSEd Maste                 /* Here mgmt_addr_length stands for the management address length. */
713*ee67461eSJoseph Mingrone                 mgmt_addr_length = GET_U_1(tptr);
7143340d773SGleb Smirnoff                 tptr++;
7153340d773SGleb Smirnoff                 tlen--;
7163340d773SGleb Smirnoff                 cfm_tlv_len--;
717*ee67461eSJoseph Mingrone                 ND_PRINT("\n\t  Management Address Length %u", mgmt_addr_length);
7183340d773SGleb Smirnoff                 if (mgmt_addr_length) {
7190bff6a5aSEd Maste                     /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */
7203340d773SGleb Smirnoff                     if (cfm_tlv_len < mgmt_addr_length) {
721*ee67461eSJoseph Mingrone                         ND_PRINT("\n\t  (TLV too short)");
7223340d773SGleb Smirnoff                         return;
7233340d773SGleb Smirnoff                     }
7243340d773SGleb Smirnoff                     cfm_tlv_len -= mgmt_addr_length;
7253340d773SGleb Smirnoff                     /*
7263340d773SGleb Smirnoff                      * XXX - this is a TransportDomain; print it as such.
7273340d773SGleb Smirnoff                      */
7280bff6a5aSEd Maste                     hex_print(ndo, "\n\t  Management Address: ", tptr, mgmt_addr_length);
7293340d773SGleb Smirnoff                     tptr += mgmt_addr_length;
7303340d773SGleb Smirnoff                     tlen -= mgmt_addr_length;
7313340d773SGleb Smirnoff                 }
732a5779b6eSRui Paulo             }
733a5779b6eSRui Paulo             break;
7343340d773SGleb Smirnoff         }
735a5779b6eSRui Paulo 
736a5779b6eSRui Paulo             /*
737a5779b6eSRui Paulo              * FIXME those are the defined TLVs that lack a decoder
738a5779b6eSRui Paulo              * you are welcome to contribute code ;-)
739a5779b6eSRui Paulo              */
740a5779b6eSRui Paulo 
741a5779b6eSRui Paulo         case CFM_TLV_DATA:
742a5779b6eSRui Paulo         case CFM_TLV_REPLY_INGRESS:
743a5779b6eSRui Paulo         case CFM_TLV_REPLY_EGRESS:
744a5779b6eSRui Paulo         default:
745a5779b6eSRui Paulo             hexdump = TRUE;
746a5779b6eSRui Paulo             break;
747a5779b6eSRui Paulo         }
748a5779b6eSRui Paulo         /* do we want to see an additional hexdump ? */
7493c602fabSXin LI         if (hexdump || ndo->ndo_vflag > 1)
7503c602fabSXin LI             print_unknown_data(ndo, tlv_ptr, "\n\t  ", cfm_tlv_len);
751a5779b6eSRui Paulo 
7520bff6a5aSEd Maste next_tlv:
753a5779b6eSRui Paulo         tptr+=cfm_tlv_len;
754a5779b6eSRui Paulo         tlen-=cfm_tlv_len;
755a5779b6eSRui Paulo     }
756a5779b6eSRui Paulo     return;
7573340d773SGleb Smirnoff 
7583340d773SGleb Smirnoff tooshort:
759*ee67461eSJoseph Mingrone     ND_PRINT("\n\t\t packet is too short");
7603340d773SGleb Smirnoff     return;
7613340d773SGleb Smirnoff 
762a5779b6eSRui Paulo trunc:
763*ee67461eSJoseph Mingrone     nd_print_trunc(ndo);
764a5779b6eSRui Paulo }
765