IMPORT openssh-9.8p1
[dragonfly.git] / contrib / tcpdump / print-cfm.c
blobe950719e691d3dd40b6a25a0c272f0c7516ded65
1 /*
2 * Copyright (c) 1998-2006 The TCPDUMP project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
15 * Original code by Hannes Gredler (hannes@gredler.at)
18 /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include "netdissect-stdinc.h"
26 #include "netdissect.h"
27 #include "extract.h"
28 #include "addrtoname.h"
29 #include "oui.h"
30 #include "af.h"
33 struct cfm_common_header_t {
34 nd_uint8_t mdlevel_version;
35 nd_uint8_t opcode;
36 nd_uint8_t flags;
37 nd_uint8_t first_tlv_offset;
40 #define CFM_VERSION 0
41 #define CFM_EXTRACT_VERSION(x) ((x)&0x1f)
42 #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
44 #define CFM_OPCODE_CCM 1
45 #define CFM_OPCODE_LBR 2
46 #define CFM_OPCODE_LBM 3
47 #define CFM_OPCODE_LTR 4
48 #define CFM_OPCODE_LTM 5
50 static const struct tok cfm_opcode_values[] = {
51 { CFM_OPCODE_CCM, "Continuity Check Message"},
52 { CFM_OPCODE_LBR, "Loopback Reply"},
53 { CFM_OPCODE_LBM, "Loopback Message"},
54 { CFM_OPCODE_LTR, "Linktrace Reply"},
55 { CFM_OPCODE_LTM, "Linktrace Message"},
56 { 0, NULL}
60 * Message Formats.
62 struct cfm_ccm_t {
63 nd_uint32_t sequence;
64 nd_uint16_t ma_epi;
65 nd_byte names[48];
66 nd_byte itu_t_y_1731[16];
70 * Timer Bases for the CCM Interval field.
71 * Expressed in units of seconds.
73 static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f};
74 #define CCM_INTERVAL_MIN_MULTIPLIER 3.25
75 #define CCM_INTERVAL_MAX_MULTIPLIER 3.5
77 #define CFM_CCM_RDI_FLAG 0x80
78 #define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07)
80 #define CFM_CCM_MD_FORMAT_8021 0
81 #define CFM_CCM_MD_FORMAT_NONE 1
82 #define CFM_CCM_MD_FORMAT_DNS 2
83 #define CFM_CCM_MD_FORMAT_MAC 3
84 #define CFM_CCM_MD_FORMAT_CHAR 4
86 static const struct tok cfm_md_nameformat_values[] = {
87 { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"},
88 { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"},
89 { CFM_CCM_MD_FORMAT_DNS, "DNS string"},
90 { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"},
91 { CFM_CCM_MD_FORMAT_CHAR, "Character string"},
92 { 0, NULL}
95 #define CFM_CCM_MA_FORMAT_8021 0
96 #define CFM_CCM_MA_FORMAT_VID 1
97 #define CFM_CCM_MA_FORMAT_CHAR 2
98 #define CFM_CCM_MA_FORMAT_INT 3
99 #define CFM_CCM_MA_FORMAT_VPN 4
101 static const struct tok cfm_ma_nameformat_values[] = {
102 { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"},
103 { CFM_CCM_MA_FORMAT_VID, "Primary VID"},
104 { CFM_CCM_MA_FORMAT_CHAR, "Character string"},
105 { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"},
106 { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"},
107 { 0, NULL}
110 struct cfm_lbm_t {
111 nd_uint32_t transaction_id;
114 struct cfm_ltm_t {
115 nd_uint32_t transaction_id;
116 nd_uint8_t ttl;
117 nd_mac_addr original_mac;
118 nd_mac_addr target_mac;
121 static const struct tok cfm_ltm_flag_values[] = {
122 { 0x80, "Use Forwarding-DB only"},
123 { 0, NULL}
126 struct cfm_ltr_t {
127 nd_uint32_t transaction_id;
128 nd_uint8_t ttl;
129 nd_uint8_t replay_action;
132 static const struct tok cfm_ltr_flag_values[] = {
133 { 0x80, "UseFDB Only"},
134 { 0x40, "FwdYes"},
135 { 0x20, "Terminal MEP"},
136 { 0, NULL}
139 static const struct tok cfm_ltr_replay_action_values[] = {
140 { 1, "Exact Match"},
141 { 2, "Filtering DB"},
142 { 3, "MIP CCM DB"},
143 { 0, NULL}
147 #define CFM_TLV_END 0
148 #define CFM_TLV_SENDER_ID 1
149 #define CFM_TLV_PORT_STATUS 2
150 #define CFM_TLV_INTERFACE_STATUS 3
151 #define CFM_TLV_DATA 4
152 #define CFM_TLV_REPLY_INGRESS 5
153 #define CFM_TLV_REPLY_EGRESS 6
154 #define CFM_TLV_PRIVATE 31
156 static const struct tok cfm_tlv_values[] = {
157 { CFM_TLV_END, "End"},
158 { CFM_TLV_SENDER_ID, "Sender ID"},
159 { CFM_TLV_PORT_STATUS, "Port status"},
160 { CFM_TLV_INTERFACE_STATUS, "Interface status"},
161 { CFM_TLV_DATA, "Data"},
162 { CFM_TLV_REPLY_INGRESS, "Reply Ingress"},
163 { CFM_TLV_REPLY_EGRESS, "Reply Egress"},
164 { CFM_TLV_PRIVATE, "Organization Specific"},
165 { 0, NULL}
169 * TLVs
172 struct cfm_tlv_header_t {
173 nd_uint8_t type;
174 nd_uint16_t length;
177 /* FIXME define TLV formats */
179 static const struct tok cfm_tlv_port_status_values[] = {
180 { 1, "Blocked"},
181 { 2, "Up"},
182 { 0, NULL}
185 static const struct tok cfm_tlv_interface_status_values[] = {
186 { 1, "Up"},
187 { 2, "Down"},
188 { 3, "Testing"},
189 { 5, "Dormant"},
190 { 6, "not present"},
191 { 7, "lower Layer down"},
192 { 0, NULL}
195 #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1
196 #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2
197 #define CFM_CHASSIS_ID_PORT_COMPONENT 3
198 #define CFM_CHASSIS_ID_MAC_ADDRESS 4
199 #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5
200 #define CFM_CHASSIS_ID_INTERFACE_NAME 6
201 #define CFM_CHASSIS_ID_LOCAL 7
203 static const struct tok cfm_tlv_senderid_chassisid_values[] = {
204 { 0, "Reserved"},
205 { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"},
206 { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"},
207 { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"},
208 { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"},
209 { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"},
210 { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"},
211 { CFM_CHASSIS_ID_LOCAL, "Locally assigned"},
212 { 0, NULL}
216 static int
217 cfm_network_addr_print(netdissect_options *ndo,
218 const u_char *tptr, const u_int length)
220 u_int network_addr_type;
221 u_int hexdump = FALSE;
224 * Although AFIs are typically 2 octets wide,
225 * 802.1ab specifies that this field width
226 * is only one octet.
228 if (length < 1) {
229 ND_PRINT("\n\t Network Address Type (invalid, no data");
230 return hexdump;
232 /* The calling function must make any due ND_TCHECK calls. */
233 network_addr_type = GET_U_1(tptr);
234 ND_PRINT("\n\t Network Address Type %s (%u)",
235 tok2str(af_values, "Unknown", network_addr_type),
236 network_addr_type);
239 * Resolve the passed in Address.
241 switch(network_addr_type) {
242 case AFNUM_INET:
243 if (length != 1 + 4) {
244 ND_PRINT("(invalid IPv4 address length %u)", length - 1);
245 hexdump = TRUE;
246 break;
248 ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1));
249 break;
251 case AFNUM_INET6:
252 if (length != 1 + 16) {
253 ND_PRINT("(invalid IPv6 address length %u)", length - 1);
254 hexdump = TRUE;
255 break;
257 ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1));
258 break;
260 default:
261 hexdump = TRUE;
262 break;
265 return hexdump;
268 void
269 cfm_print(netdissect_options *ndo,
270 const u_char *pptr, u_int length)
272 const struct cfm_common_header_t *cfm_common_header;
273 uint8_t mdlevel_version, opcode, flags, first_tlv_offset;
274 const struct cfm_tlv_header_t *cfm_tlv_header;
275 const uint8_t *tptr, *tlv_ptr;
276 const uint8_t *namesp;
277 u_int names_data_remaining;
278 uint8_t md_nameformat, md_namelength;
279 const uint8_t *md_name;
280 uint8_t ma_nameformat, ma_namelength;
281 const uint8_t *ma_name;
282 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
285 union {
286 const struct cfm_ccm_t *cfm_ccm;
287 const struct cfm_lbm_t *cfm_lbm;
288 const struct cfm_ltm_t *cfm_ltm;
289 const struct cfm_ltr_t *cfm_ltr;
290 } msg_ptr;
292 ndo->ndo_protocol = "cfm";
293 tptr=pptr;
294 cfm_common_header = (const struct cfm_common_header_t *)pptr;
295 if (length < sizeof(*cfm_common_header))
296 goto tooshort;
297 ND_TCHECK_SIZE(cfm_common_header);
300 * Sanity checking of the header.
302 mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version);
303 if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) {
304 ND_PRINT("CFMv%u not supported, length %u",
305 CFM_EXTRACT_VERSION(mdlevel_version), length);
306 return;
309 opcode = GET_U_1(cfm_common_header->opcode);
310 ND_PRINT("CFMv%u %s, MD Level %u, length %u",
311 CFM_EXTRACT_VERSION(mdlevel_version),
312 tok2str(cfm_opcode_values, "unknown (%u)", opcode),
313 CFM_EXTRACT_MD_LEVEL(mdlevel_version),
314 length);
317 * In non-verbose mode just print the opcode and md-level.
319 if (ndo->ndo_vflag < 1) {
320 return;
323 flags = GET_U_1(cfm_common_header->flags);
324 first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset);
325 ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset);
327 tptr += sizeof(struct cfm_common_header_t);
328 tlen = length - sizeof(struct cfm_common_header_t);
331 * Sanity check the first TLV offset.
333 if (first_tlv_offset > tlen) {
334 ND_PRINT(" (too large, must be <= %u)", tlen);
335 return;
338 switch (opcode) {
339 case CFM_OPCODE_CCM:
340 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
341 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
342 ND_PRINT(" (too small 1, must be >= %zu)",
343 sizeof(*msg_ptr.cfm_ccm));
344 return;
346 if (tlen < sizeof(*msg_ptr.cfm_ccm))
347 goto tooshort;
348 ND_TCHECK_SIZE(msg_ptr.cfm_ccm);
350 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags);
351 ND_PRINT(", Flags [CCM Interval %u%s]",
352 ccm_interval,
353 flags & CFM_CCM_RDI_FLAG ?
354 ", RDI" : "");
357 * Resolve the CCM interval field.
359 if (ccm_interval) {
360 ND_PRINT("\n\t CCM Interval %.3fs"
361 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
362 ccm_interval_base[ccm_interval],
363 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
364 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER);
367 ND_PRINT("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
368 GET_BE_U_4(msg_ptr.cfm_ccm->sequence),
369 GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi));
371 namesp = msg_ptr.cfm_ccm->names;
372 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
375 * Resolve the MD fields.
377 md_nameformat = GET_U_1(namesp);
378 namesp++;
379 names_data_remaining--; /* We know this is != 0 */
380 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
381 md_namelength = GET_U_1(namesp);
382 namesp++;
383 names_data_remaining--; /* We know this is !=0 */
384 ND_PRINT("\n\t MD Name Format %s (%u), MD Name length %u",
385 tok2str(cfm_md_nameformat_values, "Unknown",
386 md_nameformat),
387 md_nameformat,
388 md_namelength);
391 * -3 for the MA short name format and length and one byte
392 * of MA short name.
394 if (md_namelength > names_data_remaining - 3) {
395 ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2);
396 return;
399 md_name = namesp;
400 ND_PRINT("\n\t MD Name: ");
401 switch (md_nameformat) {
402 case CFM_CCM_MD_FORMAT_DNS:
403 case CFM_CCM_MD_FORMAT_CHAR:
404 nd_printjnp(ndo, md_name, md_namelength);
405 break;
407 case CFM_CCM_MD_FORMAT_MAC:
408 if (md_namelength == MAC_ADDR_LEN) {
409 ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(md_name));
410 } else {
411 ND_PRINT("\n\t MAC (length invalid)");
413 break;
415 /* FIXME add printers for those MD formats - hexdump for now */
416 case CFM_CCM_MA_FORMAT_8021:
417 default:
418 print_unknown_data(ndo, md_name, "\n\t ",
419 md_namelength);
421 namesp += md_namelength;
422 names_data_remaining -= md_namelength;
423 } else {
424 ND_PRINT("\n\t MD Name Format %s (%u)",
425 tok2str(cfm_md_nameformat_values, "Unknown",
426 md_nameformat),
427 md_nameformat);
432 * Resolve the MA fields.
434 ma_nameformat = GET_U_1(namesp);
435 namesp++;
436 names_data_remaining--; /* We know this is != 0 */
437 ma_namelength = GET_U_1(namesp);
438 namesp++;
439 names_data_remaining--; /* We know this is != 0 */
440 ND_PRINT("\n\t MA Name-Format %s (%u), MA name length %u",
441 tok2str(cfm_ma_nameformat_values, "Unknown",
442 ma_nameformat),
443 ma_nameformat,
444 ma_namelength);
446 if (ma_namelength > names_data_remaining) {
447 ND_PRINT(" (too large, must be <= %u)", names_data_remaining);
448 return;
451 ma_name = namesp;
452 ND_PRINT("\n\t MA Name: ");
453 switch (ma_nameformat) {
454 case CFM_CCM_MA_FORMAT_CHAR:
455 nd_printjnp(ndo, ma_name, ma_namelength);
456 break;
458 /* FIXME add printers for those MA formats - hexdump for now */
459 case CFM_CCM_MA_FORMAT_8021:
460 case CFM_CCM_MA_FORMAT_VID:
461 case CFM_CCM_MA_FORMAT_INT:
462 case CFM_CCM_MA_FORMAT_VPN:
463 default:
464 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength);
466 break;
468 case CFM_OPCODE_LTM:
469 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
470 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
471 ND_PRINT(" (too small 4, must be >= %zu)",
472 sizeof(*msg_ptr.cfm_ltm));
473 return;
475 if (tlen < sizeof(*msg_ptr.cfm_ltm))
476 goto tooshort;
477 ND_TCHECK_SIZE(msg_ptr.cfm_ltm);
479 ND_PRINT(", Flags [%s]",
480 bittok2str(cfm_ltm_flag_values, "none", flags));
482 ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u",
483 GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id),
484 GET_U_1(msg_ptr.cfm_ltm->ttl));
486 ND_PRINT("\n\t Original-MAC %s, Target-MAC %s",
487 GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac),
488 GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac));
489 break;
491 case CFM_OPCODE_LTR:
492 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
493 if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
494 ND_PRINT(" (too small 5, must be >= %zu)",
495 sizeof(*msg_ptr.cfm_ltr));
496 return;
498 if (tlen < sizeof(*msg_ptr.cfm_ltr))
499 goto tooshort;
500 ND_TCHECK_SIZE(msg_ptr.cfm_ltr);
502 ND_PRINT(", Flags [%s]",
503 bittok2str(cfm_ltr_flag_values, "none", flags));
505 ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u",
506 GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id),
507 GET_U_1(msg_ptr.cfm_ltr->ttl));
509 ND_PRINT("\n\t Replay-Action %s (%u)",
510 tok2str(cfm_ltr_replay_action_values,
511 "Unknown",
512 GET_U_1(msg_ptr.cfm_ltr->replay_action)),
513 GET_U_1(msg_ptr.cfm_ltr->replay_action));
514 break;
517 * No message decoder yet.
518 * Hexdump everything up until the start of the TLVs
520 case CFM_OPCODE_LBR:
521 case CFM_OPCODE_LBM:
522 default:
523 print_unknown_data(ndo, tptr, "\n\t ",
524 tlen - first_tlv_offset);
525 break;
528 tptr += first_tlv_offset;
529 tlen -= first_tlv_offset;
531 while (tlen > 0) {
532 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
534 /* Enough to read the tlv type ? */
535 cfm_tlv_type = GET_U_1(cfm_tlv_header->type);
537 ND_PRINT("\n\t%s TLV (0x%02x)",
538 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
539 cfm_tlv_type);
541 if (cfm_tlv_type == CFM_TLV_END) {
542 /* Length is "Not present if the Type field is 0." */
543 return;
546 /* do we have the full tlv header ? */
547 if (tlen < sizeof(struct cfm_tlv_header_t))
548 goto tooshort;
549 ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t));
550 cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length);
552 ND_PRINT(", length %u", cfm_tlv_len);
554 tptr += sizeof(struct cfm_tlv_header_t);
555 tlen -= sizeof(struct cfm_tlv_header_t);
556 tlv_ptr = tptr;
558 /* do we have the full tlv ? */
559 if (tlen < cfm_tlv_len)
560 goto tooshort;
561 ND_TCHECK_LEN(tptr, cfm_tlv_len);
562 hexdump = FALSE;
564 switch(cfm_tlv_type) {
565 case CFM_TLV_PORT_STATUS:
566 if (cfm_tlv_len < 1) {
567 ND_PRINT(" (too short, must be >= 1)");
568 return;
570 ND_PRINT(", Status: %s (%u)",
571 tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)),
572 GET_U_1(tptr));
573 break;
575 case CFM_TLV_INTERFACE_STATUS:
576 if (cfm_tlv_len < 1) {
577 ND_PRINT(" (too short, must be >= 1)");
578 return;
580 ND_PRINT(", Status: %s (%u)",
581 tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)),
582 GET_U_1(tptr));
583 break;
585 case CFM_TLV_PRIVATE:
586 if (cfm_tlv_len < 4) {
587 ND_PRINT(" (too short, must be >= 4)");
588 return;
590 ND_PRINT(", Vendor: %s (%u), Sub-Type %u",
591 tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)),
592 GET_BE_U_3(tptr),
593 GET_U_1(tptr + 3));
594 hexdump = TRUE;
595 break;
597 case CFM_TLV_SENDER_ID:
599 u_int chassis_id_type, chassis_id_length;
600 u_int mgmt_addr_length;
602 if (cfm_tlv_len < 1) {
603 ND_PRINT(" (too short, must be >= 1)");
604 goto next_tlv;
608 * Get the Chassis ID length and check it.
609 * IEEE 802.1Q-2014 Section 21.5.3.1
611 chassis_id_length = GET_U_1(tptr);
612 tptr++;
613 tlen--;
614 cfm_tlv_len--;
616 if (chassis_id_length) {
618 * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references
619 * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently
620 * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype
622 if (cfm_tlv_len < 1) {
623 ND_PRINT("\n\t (TLV too short)");
624 goto next_tlv;
626 chassis_id_type = GET_U_1(tptr);
627 cfm_tlv_len--;
628 ND_PRINT("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u",
629 tok2str(cfm_tlv_senderid_chassisid_values,
630 "Unknown",
631 chassis_id_type),
632 chassis_id_type,
633 chassis_id_length);
635 if (cfm_tlv_len < chassis_id_length) {
636 ND_PRINT("\n\t (TLV too short)");
637 goto next_tlv;
640 /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */
641 switch (chassis_id_type) {
642 case CFM_CHASSIS_ID_MAC_ADDRESS:
643 if (chassis_id_length != MAC_ADDR_LEN) {
644 ND_PRINT(" (invalid MAC address length)");
645 hexdump = TRUE;
646 break;
648 ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(tptr + 1));
649 break;
651 case CFM_CHASSIS_ID_NETWORK_ADDRESS:
652 hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
653 break;
655 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
656 case CFM_CHASSIS_ID_INTERFACE_ALIAS:
657 case CFM_CHASSIS_ID_LOCAL:
658 case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
659 case CFM_CHASSIS_ID_PORT_COMPONENT:
660 nd_printjnp(ndo, tptr + 1, chassis_id_length);
661 break;
663 default:
664 hexdump = TRUE;
665 break;
667 cfm_tlv_len -= chassis_id_length;
669 tptr += 1 + chassis_id_length;
670 tlen -= 1 + chassis_id_length;
674 * Check if there is a Management Address.
675 * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length
676 * This and all subsequent fields are not present if the TLV length
677 * allows only the above fields.
679 if (cfm_tlv_len == 0) {
680 /* No, there isn't; we're done. */
681 break;
684 /* Here mgmt_addr_length stands for the management domain length. */
685 mgmt_addr_length = GET_U_1(tptr);
686 tptr++;
687 tlen--;
688 cfm_tlv_len--;
689 ND_PRINT("\n\t Management Address Domain Length %u", mgmt_addr_length);
690 if (mgmt_addr_length) {
691 /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */
692 if (cfm_tlv_len < mgmt_addr_length) {
693 ND_PRINT("\n\t (TLV too short)");
694 goto next_tlv;
696 cfm_tlv_len -= mgmt_addr_length;
698 * XXX - this is an OID; print it as such.
700 hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length);
701 tptr += mgmt_addr_length;
702 tlen -= mgmt_addr_length;
705 * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length
706 * This field is present if Management Address Domain Length is not 0.
708 if (cfm_tlv_len < 1) {
709 ND_PRINT(" (Management Address Length is missing)");
710 hexdump = TRUE;
711 break;
714 /* Here mgmt_addr_length stands for the management address length. */
715 mgmt_addr_length = GET_U_1(tptr);
716 tptr++;
717 tlen--;
718 cfm_tlv_len--;
719 ND_PRINT("\n\t Management Address Length %u", mgmt_addr_length);
720 if (mgmt_addr_length) {
721 /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */
722 if (cfm_tlv_len < mgmt_addr_length) {
723 ND_PRINT("\n\t (TLV too short)");
724 return;
726 cfm_tlv_len -= mgmt_addr_length;
728 * XXX - this is a TransportDomain; print it as such.
730 hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length);
731 tptr += mgmt_addr_length;
732 tlen -= mgmt_addr_length;
735 break;
739 * FIXME those are the defined TLVs that lack a decoder
740 * you are welcome to contribute code ;-)
743 case CFM_TLV_DATA:
744 case CFM_TLV_REPLY_INGRESS:
745 case CFM_TLV_REPLY_EGRESS:
746 default:
747 hexdump = TRUE;
748 break;
750 /* do we want to see an additional hexdump ? */
751 if (hexdump || ndo->ndo_vflag > 1)
752 print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len);
754 next_tlv:
755 tptr+=cfm_tlv_len;
756 tlen-=cfm_tlv_len;
758 return;
760 tooshort:
761 ND_PRINT("\n\t\t packet is too short");
762 return;
764 trunc:
765 nd_print_trunc(ndo);