1 /*
2 * Copyright (c) 1998-2006 The TCPDUMP project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * Original code by Hannes Gredler (hannes@gredler.at)
16 */
17
18 /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */
19
20 #include <sys/cdefs.h>
21 #ifndef lint
22 __RCSID("$NetBSD: print-cfm.c,v 1.9 2017/09/08 14:01:13 christos Exp $");
23 #endif
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <netdissect-stdinc.h>
30
31 #include <stdio.h>
32
33 #include "netdissect.h"
34 #include "extract.h"
35 #include "ether.h"
36 #include "addrtoname.h"
37 #include "oui.h"
38 #include "af.h"
39
40 struct cfm_common_header_t {
41 uint8_t mdlevel_version;
42 uint8_t opcode;
43 uint8_t flags;
44 uint8_t first_tlv_offset;
45 };
46
47 #define CFM_VERSION 0
48 #define CFM_EXTRACT_VERSION(x) (((x)&0x1f))
49 #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
50
51 #define CFM_OPCODE_CCM 1
52 #define CFM_OPCODE_LBR 2
53 #define CFM_OPCODE_LBM 3
54 #define CFM_OPCODE_LTR 4
55 #define CFM_OPCODE_LTM 5
56
57 static const struct tok cfm_opcode_values[] = {
58 { CFM_OPCODE_CCM, "Continouity Check Message"},
59 { CFM_OPCODE_LBR, "Loopback Reply"},
60 { CFM_OPCODE_LBM, "Loopback Message"},
61 { CFM_OPCODE_LTR, "Linktrace Reply"},
62 { CFM_OPCODE_LTM, "Linktrace Message"},
63 { 0, NULL}
64 };
65
66 /*
67 * Message Formats.
68 */
69 struct cfm_ccm_t {
70 uint8_t sequence[4];
71 uint8_t ma_epi[2];
72 uint8_t names[48];
73 uint8_t itu_t_y_1731[16];
74 };
75
76 /*
77 * Timer Bases for the CCM Interval field.
78 * Expressed in units of seconds.
79 */
80 static const float ccm_interval_base[8] = {0, 0.003333, 0.01, 0.1, 1, 10, 60, 600};
81 #define CCM_INTERVAL_MIN_MULTIPLIER 3.25
82 #define CCM_INTERVAL_MAX_MULTIPLIER 3.5
83
84 #define CFM_CCM_RDI_FLAG 0x80
85 #define CFM_EXTRACT_CCM_INTERVAL(x) (((x)&0x07))
86
87 #define CFM_CCM_MD_FORMAT_8021 0
88 #define CFM_CCM_MD_FORMAT_NONE 1
89 #define CFM_CCM_MD_FORMAT_DNS 2
90 #define CFM_CCM_MD_FORMAT_MAC 3
91 #define CFM_CCM_MD_FORMAT_CHAR 4
92
93 static const struct tok cfm_md_nameformat_values[] = {
94 { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"},
95 { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"},
96 { CFM_CCM_MD_FORMAT_DNS, "DNS string"},
97 { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"},
98 { CFM_CCM_MD_FORMAT_CHAR, "Character string"},
99 { 0, NULL}
100 };
101
102 #define CFM_CCM_MA_FORMAT_8021 0
103 #define CFM_CCM_MA_FORMAT_VID 1
104 #define CFM_CCM_MA_FORMAT_CHAR 2
105 #define CFM_CCM_MA_FORMAT_INT 3
106 #define CFM_CCM_MA_FORMAT_VPN 4
107
108 static const struct tok cfm_ma_nameformat_values[] = {
109 { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"},
110 { CFM_CCM_MA_FORMAT_VID, "Primary VID"},
111 { CFM_CCM_MA_FORMAT_CHAR, "Character string"},
112 { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"},
113 { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"},
114 { 0, NULL}
115 };
116
117 struct cfm_lbm_t {
118 uint8_t transaction_id[4];
119 };
120
121 struct cfm_ltm_t {
122 uint8_t transaction_id[4];
123 uint8_t ttl;
124 uint8_t original_mac[ETHER_ADDR_LEN];
125 uint8_t target_mac[ETHER_ADDR_LEN];
126 };
127
128 static const struct tok cfm_ltm_flag_values[] = {
129 { 0x80, "Use Forwarding-DB only"},
130 { 0, NULL}
131 };
132
133 struct cfm_ltr_t {
134 uint8_t transaction_id[4];
135 uint8_t ttl;
136 uint8_t replay_action;
137 };
138
139 static const struct tok cfm_ltr_flag_values[] = {
140 { 0x80, "UseFDB Only"},
141 { 0x40, "FwdYes"},
142 { 0x20, "Terminal MEP"},
143 { 0, NULL}
144 };
145
146 static const struct tok cfm_ltr_replay_action_values[] = {
147 { 1, "Exact Match"},
148 { 2, "Filtering DB"},
149 { 3, "MIP CCM DB"},
150 { 0, NULL}
151 };
152
153
154 #define CFM_TLV_END 0
155 #define CFM_TLV_SENDER_ID 1
156 #define CFM_TLV_PORT_STATUS 2
157 #define CFM_TLV_INTERFACE_STATUS 3
158 #define CFM_TLV_DATA 4
159 #define CFM_TLV_REPLY_INGRESS 5
160 #define CFM_TLV_REPLY_EGRESS 6
161 #define CFM_TLV_PRIVATE 31
162
163 static const struct tok cfm_tlv_values[] = {
164 { CFM_TLV_END, "End"},
165 { CFM_TLV_SENDER_ID, "Sender ID"},
166 { CFM_TLV_PORT_STATUS, "Port status"},
167 { CFM_TLV_INTERFACE_STATUS, "Interface status"},
168 { CFM_TLV_DATA, "Data"},
169 { CFM_TLV_REPLY_INGRESS, "Reply Ingress"},
170 { CFM_TLV_REPLY_EGRESS, "Reply Egress"},
171 { CFM_TLV_PRIVATE, "Organization Specific"},
172 { 0, NULL}
173 };
174
175 /*
176 * TLVs
177 */
178
179 struct cfm_tlv_header_t {
180 uint8_t type;
181 uint8_t length[2];
182 };
183
184 /* FIXME define TLV formats */
185
186 static const struct tok cfm_tlv_port_status_values[] = {
187 { 1, "Blocked"},
188 { 2, "Up"},
189 { 0, NULL}
190 };
191
192 static const struct tok cfm_tlv_interface_status_values[] = {
193 { 1, "Up"},
194 { 2, "Down"},
195 { 3, "Testing"},
196 { 5, "Dormant"},
197 { 6, "not present"},
198 { 7, "lower Layer down"},
199 { 0, NULL}
200 };
201
202 #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1
203 #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2
204 #define CFM_CHASSIS_ID_PORT_COMPONENT 3
205 #define CFM_CHASSIS_ID_MAC_ADDRESS 4
206 #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5
207 #define CFM_CHASSIS_ID_INTERFACE_NAME 6
208 #define CFM_CHASSIS_ID_LOCAL 7
209
210 static const struct tok cfm_tlv_senderid_chassisid_values[] = {
211 { 0, "Reserved"},
212 { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"},
213 { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"},
214 { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"},
215 { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"},
216 { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"},
217 { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"},
218 { CFM_CHASSIS_ID_LOCAL, "Locally assigned"},
219 { 0, NULL}
220 };
221
222
223 static int
cfm_network_addr_print(netdissect_options * ndo,register const u_char * tptr,const u_int length)224 cfm_network_addr_print(netdissect_options *ndo,
225 register const u_char *tptr, const u_int length)
226 {
227 u_int network_addr_type;
228 u_int hexdump = FALSE;
229
230 /*
231 * Altough AFIs are tpically 2 octects wide,
232 * 802.1ab specifies that this field width
233 * is only once octet
234 */
235 if (length < 1) {
236 ND_PRINT((ndo, "\n\t Network Address Type (invalid, no data"));
237 return hexdump;
238 }
239 /* The calling function must make any due ND_TCHECK calls. */
240 network_addr_type = *tptr;
241 ND_PRINT((ndo, "\n\t Network Address Type %s (%u)",
242 tok2str(af_values, "Unknown", network_addr_type),
243 network_addr_type));
244
245 /*
246 * Resolve the passed in Address.
247 */
248 switch(network_addr_type) {
249 case AFNUM_INET:
250 if (length != 1 + 4) {
251 ND_PRINT((ndo, "(invalid IPv4 address length %u)", length - 1));
252 hexdump = TRUE;
253 break;
254 }
255 ND_PRINT((ndo, ", %s", ipaddr_string(ndo, tptr + 1)));
256 break;
257
258 case AFNUM_INET6:
259 if (length != 1 + 16) {
260 ND_PRINT((ndo, "(invalid IPv6 address length %u)", length - 1));
261 hexdump = TRUE;
262 break;
263 }
264 ND_PRINT((ndo, ", %s", ip6addr_string(ndo, tptr + 1)));
265 break;
266
267 default:
268 hexdump = TRUE;
269 break;
270 }
271
272 return hexdump;
273 }
274
275 void
cfm_print(netdissect_options * ndo,register const u_char * pptr,register u_int length)276 cfm_print(netdissect_options *ndo,
277 register const u_char *pptr, register u_int length)
278 {
279 const struct cfm_common_header_t *cfm_common_header;
280 const struct cfm_tlv_header_t *cfm_tlv_header;
281 const uint8_t *tptr, *tlv_ptr;
282 const uint8_t *namesp;
283 u_int names_data_remaining;
284 uint8_t md_nameformat, md_namelength;
285 const uint8_t *md_name;
286 uint8_t ma_nameformat, ma_namelength;
287 const uint8_t *ma_name;
288 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
289
290
291 union {
292 const struct cfm_ccm_t *cfm_ccm;
293 const struct cfm_lbm_t *cfm_lbm;
294 const struct cfm_ltm_t *cfm_ltm;
295 const struct cfm_ltr_t *cfm_ltr;
296 } msg_ptr;
297
298 tptr=pptr;
299 cfm_common_header = (const struct cfm_common_header_t *)pptr;
300 if (length < sizeof(*cfm_common_header))
301 goto tooshort;
302 ND_TCHECK(*cfm_common_header);
303
304 /*
305 * Sanity checking of the header.
306 */
307 if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) {
308 ND_PRINT((ndo, "CFMv%u not supported, length %u",
309 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length));
310 return;
311 }
312
313 ND_PRINT((ndo, "CFMv%u %s, MD Level %u, length %u",
314 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version),
315 tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode),
316 CFM_EXTRACT_MD_LEVEL(cfm_common_header->mdlevel_version),
317 length));
318
319 /*
320 * In non-verbose mode just print the opcode and md-level.
321 */
322 if (ndo->ndo_vflag < 1) {
323 return;
324 }
325
326 ND_PRINT((ndo, "\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset));
327
328 tptr += sizeof(const struct cfm_common_header_t);
329 tlen = length - sizeof(struct cfm_common_header_t);
330
331 /*
332 * Sanity check the first TLV offset.
333 */
334 if (cfm_common_header->first_tlv_offset > tlen) {
335 ND_PRINT((ndo, " (too large, must be <= %u)", tlen));
336 return;
337 }
338
339 switch (cfm_common_header->opcode) {
340 case CFM_OPCODE_CCM:
341 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
342 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
343 ND_PRINT((ndo, " (too small 1, must be >= %lu)",
344 (unsigned long) sizeof(*msg_ptr.cfm_ccm)));
345 return;
346 }
347 if (tlen < sizeof(*msg_ptr.cfm_ccm))
348 goto tooshort;
349 ND_TCHECK(*msg_ptr.cfm_ccm);
350
351 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags);
352 ND_PRINT((ndo, ", Flags [CCM Interval %u%s]",
353 ccm_interval,
354 cfm_common_header->flags & CFM_CCM_RDI_FLAG ?
355 ", RDI" : ""));
356
357 /*
358 * Resolve the CCM interval field.
359 */
360 if (ccm_interval) {
361 ND_PRINT((ndo, "\n\t CCM Interval %.3fs"
362 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
363 ccm_interval_base[ccm_interval],
364 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
365 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER));
366 }
367
368 ND_PRINT((ndo, "\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
369 EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence),
370 EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi)));
371
372 namesp = msg_ptr.cfm_ccm->names;
373 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
374
375 /*
376 * Resolve the MD fields.
377 */
378 md_nameformat = *namesp;
379 namesp++;
380 names_data_remaining--; /* We know this is != 0 */
381 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
382 md_namelength = *namesp;
383 namesp++;
384 names_data_remaining--; /* We know this is !=0 */
385 ND_PRINT((ndo, "\n\t MD Name Format %s (%u), MD Name length %u",
386 tok2str(cfm_md_nameformat_values, "Unknown",
387 md_nameformat),
388 md_nameformat,
389 md_namelength));
390
391 /*
392 * -3 for the MA short name format and length and one byte
393 * of MA short name.
394 */
395 if (md_namelength > names_data_remaining - 3) {
396 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining - 2));
397 return;
398 }
399
400 md_name = namesp;
401 ND_PRINT((ndo, "\n\t MD Name: "));
402 switch (md_nameformat) {
403 case CFM_CCM_MD_FORMAT_DNS:
404 case CFM_CCM_MD_FORMAT_CHAR:
405 safeputs(ndo, md_name, md_namelength);
406 break;
407
408 case CFM_CCM_MD_FORMAT_MAC:
409 if (md_namelength == 6) {
410 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo,
411 md_name)));
412 } else {
413 ND_PRINT((ndo, "\n\t MAC (length invalid)"));
414 }
415 break;
416
417 /* FIXME add printers for those MD formats - hexdump for now */
418 case CFM_CCM_MA_FORMAT_8021:
419 default:
420 print_unknown_data(ndo, md_name, "\n\t ",
421 md_namelength);
422 }
423 namesp += md_namelength;
424 names_data_remaining -= md_namelength;
425 } else {
426 ND_PRINT((ndo, "\n\t MD Name Format %s (%u)",
427 tok2str(cfm_md_nameformat_values, "Unknown",
428 md_nameformat),
429 md_nameformat));
430 }
431
432
433 /*
434 * Resolve the MA fields.
435 */
436 ma_nameformat = *namesp;
437 namesp++;
438 names_data_remaining--; /* We know this is != 0 */
439 ma_namelength = *namesp;
440 namesp++;
441 names_data_remaining--; /* We know this is != 0 */
442 ND_PRINT((ndo, "\n\t MA Name-Format %s (%u), MA name length %u",
443 tok2str(cfm_ma_nameformat_values, "Unknown",
444 ma_nameformat),
445 ma_nameformat,
446 ma_namelength));
447
448 if (ma_namelength > names_data_remaining) {
449 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining));
450 return;
451 }
452
453 ma_name = namesp;
454 ND_PRINT((ndo, "\n\t MA Name: "));
455 switch (ma_nameformat) {
456 case CFM_CCM_MA_FORMAT_CHAR:
457 safeputs(ndo, ma_name, ma_namelength);
458 break;
459
460 /* FIXME add printers for those MA formats - hexdump for now */
461 case CFM_CCM_MA_FORMAT_8021:
462 case CFM_CCM_MA_FORMAT_VID:
463 case CFM_CCM_MA_FORMAT_INT:
464 case CFM_CCM_MA_FORMAT_VPN:
465 default:
466 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength);
467 }
468 break;
469
470 case CFM_OPCODE_LTM:
471 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
472 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
473 ND_PRINT((ndo, " (too small 4, must be >= %lu)",
474 (unsigned long) sizeof(*msg_ptr.cfm_ltm)));
475 return;
476 }
477 if (tlen < sizeof(*msg_ptr.cfm_ltm))
478 goto tooshort;
479 ND_TCHECK(*msg_ptr.cfm_ltm);
480
481 ND_PRINT((ndo, ", Flags [%s]",
482 bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags)));
483
484 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u",
485 EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id),
486 msg_ptr.cfm_ltm->ttl));
487
488 ND_PRINT((ndo, "\n\t Original-MAC %s, Target-MAC %s",
489 etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac),
490 etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac)));
491 break;
492
493 case CFM_OPCODE_LTR:
494 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
495 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
496 ND_PRINT((ndo, " (too small 5, must be >= %lu)",
497 (unsigned long) sizeof(*msg_ptr.cfm_ltr)));
498 return;
499 }
500 if (tlen < sizeof(*msg_ptr.cfm_ltr))
501 goto tooshort;
502 ND_TCHECK(*msg_ptr.cfm_ltr);
503
504 ND_PRINT((ndo, ", Flags [%s]",
505 bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags)));
506
507 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u",
508 EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id),
509 msg_ptr.cfm_ltr->ttl));
510
511 ND_PRINT((ndo, "\n\t Replay-Action %s (%u)",
512 tok2str(cfm_ltr_replay_action_values,
513 "Unknown",
514 msg_ptr.cfm_ltr->replay_action),
515 msg_ptr.cfm_ltr->replay_action));
516 break;
517
518 /*
519 * No message decoder yet.
520 * Hexdump everything up until the start of the TLVs
521 */
522 case CFM_OPCODE_LBR:
523 case CFM_OPCODE_LBM:
524 default:
525 print_unknown_data(ndo, tptr, "\n\t ",
526 tlen - cfm_common_header->first_tlv_offset);
527 break;
528 }
529
530 tptr += cfm_common_header->first_tlv_offset;
531 tlen -= cfm_common_header->first_tlv_offset;
532
533 while (tlen > 0) {
534 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
535
536 /* Enough to read the tlv type ? */
537 ND_TCHECK2(*tptr, 1);
538 cfm_tlv_type=cfm_tlv_header->type;
539
540 ND_PRINT((ndo, "\n\t%s TLV (0x%02x)",
541 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
542 cfm_tlv_type));
543
544 if (cfm_tlv_type == CFM_TLV_END) {
545 /* Length is "Not present if the Type field is 0." */
546 return;
547 }
548
549 /* do we have the full tlv header ? */
550 if (tlen < sizeof(struct cfm_tlv_header_t))
551 goto tooshort;
552 ND_TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t));
553 cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length);
554
555 ND_PRINT((ndo, ", length %u", cfm_tlv_len));
556
557 tptr += sizeof(struct cfm_tlv_header_t);
558 tlen -= sizeof(struct cfm_tlv_header_t);
559 tlv_ptr = tptr;
560
561 /* do we have the full tlv ? */
562 if (tlen < cfm_tlv_len)
563 goto tooshort;
564 ND_TCHECK2(*tptr, cfm_tlv_len);
565 hexdump = FALSE;
566
567 switch(cfm_tlv_type) {
568 case CFM_TLV_PORT_STATUS:
569 if (cfm_tlv_len < 1) {
570 ND_PRINT((ndo, " (too short, must be >= 1)"));
571 return;
572 }
573 ND_PRINT((ndo, ", Status: %s (%u)",
574 tok2str(cfm_tlv_port_status_values, "Unknown", *tptr),
575 *tptr));
576 break;
577
578 case CFM_TLV_INTERFACE_STATUS:
579 if (cfm_tlv_len < 1) {
580 ND_PRINT((ndo, " (too short, must be >= 1)"));
581 return;
582 }
583 ND_PRINT((ndo, ", Status: %s (%u)",
584 tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr),
585 *tptr));
586 break;
587
588 case CFM_TLV_PRIVATE:
589 if (cfm_tlv_len < 4) {
590 ND_PRINT((ndo, " (too short, must be >= 4)"));
591 return;
592 }
593 ND_PRINT((ndo, ", Vendor: %s (%u), Sub-Type %u",
594 tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)),
595 EXTRACT_24BITS(tptr),
596 *(tptr + 3)));
597 hexdump = TRUE;
598 break;
599
600 case CFM_TLV_SENDER_ID:
601 {
602 u_int chassis_id_type, chassis_id_length;
603 u_int mgmt_addr_length;
604
605 if (cfm_tlv_len < 1) {
606 ND_PRINT((ndo, " (too short, must be >= 1)"));
607 goto next_tlv;
608 }
609
610 /*
611 * Get the Chassis ID length and check it.
612 * IEEE 802.1Q-2014 Section 21.5.3.1
613 */
614 chassis_id_length = *tptr;
615 tptr++;
616 tlen--;
617 cfm_tlv_len--;
618
619 if (chassis_id_length) {
620 /*
621 * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references
622 * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently
623 * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype
624 */
625 if (cfm_tlv_len < 1) {
626 ND_PRINT((ndo, "\n\t (TLV too short)"));
627 goto next_tlv;
628 }
629 chassis_id_type = *tptr;
630 cfm_tlv_len--;
631 ND_PRINT((ndo, "\n\t Chassis-ID Type %s (%u), Chassis-ID length %u",
632 tok2str(cfm_tlv_senderid_chassisid_values,
633 "Unknown",
634 chassis_id_type),
635 chassis_id_type,
636 chassis_id_length));
637
638 if (cfm_tlv_len < chassis_id_length) {
639 ND_PRINT((ndo, "\n\t (TLV too short)"));
640 goto next_tlv;
641 }
642
643 /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */
644 switch (chassis_id_type) {
645 case CFM_CHASSIS_ID_MAC_ADDRESS:
646 if (chassis_id_length != ETHER_ADDR_LEN) {
647 ND_PRINT((ndo, " (invalid MAC address length)"));
648 hexdump = TRUE;
649 break;
650 }
651 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, tptr + 1)));
652 break;
653
654 case CFM_CHASSIS_ID_NETWORK_ADDRESS:
655 hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
656 break;
657
658 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
659 case CFM_CHASSIS_ID_INTERFACE_ALIAS:
660 case CFM_CHASSIS_ID_LOCAL:
661 case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
662 case CFM_CHASSIS_ID_PORT_COMPONENT:
663 safeputs(ndo, tptr + 1, chassis_id_length);
664 break;
665
666 default:
667 hexdump = TRUE;
668 break;
669 }
670 cfm_tlv_len -= chassis_id_length;
671
672 tptr += 1 + chassis_id_length;
673 tlen -= 1 + chassis_id_length;
674 }
675
676 /*
677 * Check if there is a Management Address.
678 * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length
679 * This and all subsequent fields are not present if the TLV length
680 * allows only the above fields.
681 */
682 if (cfm_tlv_len == 0) {
683 /* No, there isn't; we're done. */
684 break;
685 }
686
687 /* Here mgmt_addr_length stands for the management domain length. */
688 mgmt_addr_length = *tptr;
689 tptr++;
690 tlen--;
691 cfm_tlv_len--;
692 ND_PRINT((ndo, "\n\t Management Address Domain Length %u", mgmt_addr_length));
693 if (mgmt_addr_length) {
694 /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */
695 if (cfm_tlv_len < mgmt_addr_length) {
696 ND_PRINT((ndo, "\n\t (TLV too short)"));
697 goto next_tlv;
698 }
699 cfm_tlv_len -= mgmt_addr_length;
700 /*
701 * XXX - this is an OID; print it as such.
702 */
703 hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length);
704 tptr += mgmt_addr_length;
705 tlen -= mgmt_addr_length;
706
707 /*
708 * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length
709 * This field is present if Management Address Domain Length is not 0.
710 */
711 if (cfm_tlv_len < 1) {
712 ND_PRINT((ndo, " (Management Address Length is missing)"));
713 hexdump = TRUE;
714 break;
715 }
716
717 /* Here mgmt_addr_length stands for the management address length. */
718 mgmt_addr_length = *tptr;
719 tptr++;
720 tlen--;
721 cfm_tlv_len--;
722 ND_PRINT((ndo, "\n\t Management Address Length %u", mgmt_addr_length));
723 if (mgmt_addr_length) {
724 /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */
725 if (cfm_tlv_len < mgmt_addr_length) {
726 ND_PRINT((ndo, "\n\t (TLV too short)"));
727 return;
728 }
729 cfm_tlv_len -= mgmt_addr_length;
730 /*
731 * XXX - this is a TransportDomain; print it as such.
732 */
733 hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length);
734 tptr += mgmt_addr_length;
735 tlen -= mgmt_addr_length;
736 }
737 }
738 break;
739 }
740
741 /*
742 * FIXME those are the defined TLVs that lack a decoder
743 * you are welcome to contribute code ;-)
744 */
745
746 case CFM_TLV_DATA:
747 case CFM_TLV_REPLY_INGRESS:
748 case CFM_TLV_REPLY_EGRESS:
749 default:
750 hexdump = TRUE;
751 break;
752 }
753 /* do we want to see an additional hexdump ? */
754 if (hexdump || ndo->ndo_vflag > 1)
755 print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len);
756
757 next_tlv:
758 tptr+=cfm_tlv_len;
759 tlen-=cfm_tlv_len;
760 }
761 return;
762
763 tooshort:
764 ND_PRINT((ndo, "\n\t\t packet is too short"));
765 return;
766
767 trunc:
768 ND_PRINT((ndo, "\n\t\t packet exceeded snapshot"));
769 }
770