print-cfm.c revision 1.2.8.1 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 (at) juniper.net)
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.2.8.1 2017/02/19 07:37:07 snj 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
224 cfm_network_addr_print(netdissect_options *ndo,
225 register const u_char *tptr)
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 network_addr_type = *tptr;
236 ND_PRINT((ndo, "\n\t Network Address Type %s (%u)",
237 tok2str(af_values, "Unknown", network_addr_type),
238 network_addr_type));
239
240 /*
241 * Resolve the passed in Address.
242 */
243 switch(network_addr_type) {
244 case AFNUM_INET:
245 ND_PRINT((ndo, ", %s", ipaddr_string(ndo, tptr + 1)));
246 break;
247
248 case AFNUM_INET6:
249 ND_PRINT((ndo, ", %s", ip6addr_string(ndo, tptr + 1)));
250 break;
251
252 default:
253 hexdump = TRUE;
254 break;
255 }
256
257 return hexdump;
258 }
259
260 void
261 cfm_print(netdissect_options *ndo,
262 register const u_char *pptr, register u_int length)
263 {
264 const struct cfm_common_header_t *cfm_common_header;
265 const struct cfm_tlv_header_t *cfm_tlv_header;
266 const uint8_t *tptr, *tlv_ptr;
267 const uint8_t *namesp;
268 u_int names_data_remaining;
269 uint8_t md_nameformat, md_namelength;
270 const uint8_t *md_name;
271 uint8_t ma_nameformat, ma_namelength;
272 const uint8_t *ma_name;
273 u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
274
275
276 union {
277 const struct cfm_ccm_t *cfm_ccm;
278 const struct cfm_lbm_t *cfm_lbm;
279 const struct cfm_ltm_t *cfm_ltm;
280 const struct cfm_ltr_t *cfm_ltr;
281 } msg_ptr;
282
283 tptr=pptr;
284 cfm_common_header = (const struct cfm_common_header_t *)pptr;
285 if (length < sizeof(*cfm_common_header))
286 goto tooshort;
287 ND_TCHECK(*cfm_common_header);
288
289 /*
290 * Sanity checking of the header.
291 */
292 if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) {
293 ND_PRINT((ndo, "CFMv%u not supported, length %u",
294 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length));
295 return;
296 }
297
298 ND_PRINT((ndo, "CFMv%u %s, MD Level %u, length %u",
299 CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version),
300 tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode),
301 CFM_EXTRACT_MD_LEVEL(cfm_common_header->mdlevel_version),
302 length));
303
304 /*
305 * In non-verbose mode just print the opcode and md-level.
306 */
307 if (ndo->ndo_vflag < 1) {
308 return;
309 }
310
311 ND_PRINT((ndo, "\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset));
312
313 tptr += sizeof(const struct cfm_common_header_t);
314 tlen = length - sizeof(struct cfm_common_header_t);
315
316 /*
317 * Sanity check the first TLV offset.
318 */
319 if (cfm_common_header->first_tlv_offset > tlen) {
320 ND_PRINT((ndo, " (too large, must be <= %u)", tlen));
321 return;
322 }
323
324 switch (cfm_common_header->opcode) {
325 case CFM_OPCODE_CCM:
326 msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
327 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
328 ND_PRINT((ndo, " (too small 1, must be >= %lu)",
329 (unsigned long) sizeof(*msg_ptr.cfm_ccm)));
330 return;
331 }
332 if (tlen < sizeof(*msg_ptr.cfm_ccm))
333 goto tooshort;
334 ND_TCHECK(*msg_ptr.cfm_ccm);
335
336 ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags);
337 ND_PRINT((ndo, ", Flags [CCM Interval %u%s]",
338 ccm_interval,
339 cfm_common_header->flags & CFM_CCM_RDI_FLAG ?
340 ", RDI" : ""));
341
342 /*
343 * Resolve the CCM interval field.
344 */
345 if (ccm_interval) {
346 ND_PRINT((ndo, "\n\t CCM Interval %.3fs"
347 ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
348 ccm_interval_base[ccm_interval],
349 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
350 ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER));
351 }
352
353 ND_PRINT((ndo, "\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
354 EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence),
355 EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi)));
356
357 namesp = msg_ptr.cfm_ccm->names;
358 names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
359
360 /*
361 * Resolve the MD fields.
362 */
363 md_nameformat = *namesp;
364 namesp++;
365 names_data_remaining--; /* We know this is != 0 */
366 if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
367 md_namelength = *namesp;
368 namesp++;
369 names_data_remaining--; /* We know this is !=0 */
370 ND_PRINT((ndo, "\n\t MD Name Format %s (%u), MD Name length %u",
371 tok2str(cfm_md_nameformat_values, "Unknown",
372 md_nameformat),
373 md_nameformat,
374 md_namelength));
375
376 /* -2 for the MA short name format and length */
377 if (md_namelength > names_data_remaining - 2) {
378 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining - 2));
379 return;
380 }
381
382 md_name = namesp;
383 ND_PRINT((ndo, "\n\t MD Name: "));
384 switch (md_nameformat) {
385 case CFM_CCM_MD_FORMAT_DNS:
386 case CFM_CCM_MD_FORMAT_CHAR:
387 safeputs(ndo, md_name, md_namelength);
388 break;
389
390 case CFM_CCM_MD_FORMAT_MAC:
391 if (md_namelength == 6) {
392 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo,
393 md_name)));
394 } else {
395 ND_PRINT((ndo, "\n\t MAC (length invalid)"));
396 }
397 break;
398
399 /* FIXME add printers for those MD formats - hexdump for now */
400 case CFM_CCM_MA_FORMAT_8021:
401 default:
402 print_unknown_data(ndo, md_name, "\n\t ",
403 md_namelength);
404 }
405 namesp += md_namelength;
406 names_data_remaining -= md_namelength;
407 } else {
408 ND_PRINT((ndo, "\n\t MD Name Format %s (%u)",
409 tok2str(cfm_md_nameformat_values, "Unknown",
410 md_nameformat),
411 md_nameformat));
412 }
413
414
415 /*
416 * Resolve the MA fields.
417 */
418 ma_nameformat = *namesp;
419 namesp++;
420 names_data_remaining--; /* We know this is != 0 */
421 ma_namelength = *namesp;
422 namesp++;
423 names_data_remaining--; /* We know this is != 0 */
424 ND_PRINT((ndo, "\n\t MA Name-Format %s (%u), MA name length %u",
425 tok2str(cfm_ma_nameformat_values, "Unknown",
426 ma_nameformat),
427 ma_nameformat,
428 ma_namelength));
429
430 if (ma_namelength > names_data_remaining) {
431 ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining));
432 return;
433 }
434
435 ma_name = namesp;
436 ND_PRINT((ndo, "\n\t MA Name: "));
437 switch (ma_nameformat) {
438 case CFM_CCM_MA_FORMAT_CHAR:
439 safeputs(ndo, ma_name, ma_namelength);
440 break;
441
442 /* FIXME add printers for those MA formats - hexdump for now */
443 case CFM_CCM_MA_FORMAT_8021:
444 case CFM_CCM_MA_FORMAT_VID:
445 case CFM_CCM_MA_FORMAT_INT:
446 case CFM_CCM_MA_FORMAT_VPN:
447 default:
448 print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength);
449 }
450 break;
451
452 case CFM_OPCODE_LTM:
453 msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
454 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
455 ND_PRINT((ndo, " (too small 4, must be >= %lu)",
456 (unsigned long) sizeof(*msg_ptr.cfm_ltm)));
457 return;
458 }
459 if (tlen < sizeof(*msg_ptr.cfm_ltm))
460 goto tooshort;
461 ND_TCHECK(*msg_ptr.cfm_ltm);
462
463 ND_PRINT((ndo, ", Flags [%s]",
464 bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags)));
465
466 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u",
467 EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id),
468 msg_ptr.cfm_ltm->ttl));
469
470 ND_PRINT((ndo, "\n\t Original-MAC %s, Target-MAC %s",
471 etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac),
472 etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac)));
473 break;
474
475 case CFM_OPCODE_LTR:
476 msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
477 if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
478 ND_PRINT((ndo, " (too small 5, must be >= %lu)",
479 (unsigned long) sizeof(*msg_ptr.cfm_ltr)));
480 return;
481 }
482 if (tlen < sizeof(*msg_ptr.cfm_ltr))
483 goto tooshort;
484 ND_TCHECK(*msg_ptr.cfm_ltr);
485
486 ND_PRINT((ndo, ", Flags [%s]",
487 bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags)));
488
489 ND_PRINT((ndo, "\n\t Transaction-ID 0x%08x, ttl %u",
490 EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id),
491 msg_ptr.cfm_ltr->ttl));
492
493 ND_PRINT((ndo, "\n\t Replay-Action %s (%u)",
494 tok2str(cfm_ltr_replay_action_values,
495 "Unknown",
496 msg_ptr.cfm_ltr->replay_action),
497 msg_ptr.cfm_ltr->replay_action));
498 break;
499
500 /*
501 * No message decoder yet.
502 * Hexdump everything up until the start of the TLVs
503 */
504 case CFM_OPCODE_LBR:
505 case CFM_OPCODE_LBM:
506 default:
507 print_unknown_data(ndo, tptr, "\n\t ",
508 tlen - cfm_common_header->first_tlv_offset);
509 break;
510 }
511
512 tptr += cfm_common_header->first_tlv_offset;
513 tlen -= cfm_common_header->first_tlv_offset;
514
515 while (tlen > 0) {
516 cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
517
518 /* Enough to read the tlv type ? */
519 ND_TCHECK2(*tptr, 1);
520 cfm_tlv_type=cfm_tlv_header->type;
521
522 ND_PRINT((ndo, "\n\t%s TLV (0x%02x)",
523 tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
524 cfm_tlv_type));
525
526 if (cfm_tlv_type == CFM_TLV_END) {
527 /* Length is "Not present if the Type field is 0." */
528 return;
529 }
530
531 /* do we have the full tlv header ? */
532 if (tlen < sizeof(struct cfm_tlv_header_t))
533 goto tooshort;
534 ND_TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t));
535 cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length);
536
537 ND_PRINT((ndo, ", length %u", cfm_tlv_len));
538
539 tptr += sizeof(struct cfm_tlv_header_t);
540 tlen -= sizeof(struct cfm_tlv_header_t);
541 tlv_ptr = tptr;
542
543 /* do we have the full tlv ? */
544 if (tlen < cfm_tlv_len)
545 goto tooshort;
546 ND_TCHECK2(*tptr, cfm_tlv_len);
547 hexdump = FALSE;
548
549 switch(cfm_tlv_type) {
550 case CFM_TLV_PORT_STATUS:
551 if (cfm_tlv_len < 1) {
552 ND_PRINT((ndo, " (too short, must be >= 1)"));
553 return;
554 }
555 ND_PRINT((ndo, ", Status: %s (%u)",
556 tok2str(cfm_tlv_port_status_values, "Unknown", *tptr),
557 *tptr));
558 break;
559
560 case CFM_TLV_INTERFACE_STATUS:
561 if (cfm_tlv_len < 1) {
562 ND_PRINT((ndo, " (too short, must be >= 1)"));
563 return;
564 }
565 ND_PRINT((ndo, ", Status: %s (%u)",
566 tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr),
567 *tptr));
568 break;
569
570 case CFM_TLV_PRIVATE:
571 if (cfm_tlv_len < 4) {
572 ND_PRINT((ndo, " (too short, must be >= 4)"));
573 return;
574 }
575 ND_PRINT((ndo, ", Vendor: %s (%u), Sub-Type %u",
576 tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)),
577 EXTRACT_24BITS(tptr),
578 *(tptr + 3)));
579 hexdump = TRUE;
580 break;
581
582 case CFM_TLV_SENDER_ID:
583 {
584 u_int chassis_id_type, chassis_id_length;
585 u_int mgmt_addr_length;
586
587 if (cfm_tlv_len < 1) {
588 ND_PRINT((ndo, " (too short, must be >= 1)"));
589 return;
590 }
591
592 /*
593 * Get the Chassis ID length and check it.
594 */
595 chassis_id_length = *tptr;
596 tptr++;
597 tlen--;
598 cfm_tlv_len--;
599
600 if (chassis_id_length) {
601 if (cfm_tlv_len < 1) {
602 ND_PRINT((ndo, "\n\t (TLV too short)"));
603 return;
604 }
605 chassis_id_type = *tptr;
606 cfm_tlv_len--;
607 ND_PRINT((ndo, "\n\t Chassis-ID Type %s (%u), Chassis-ID length %u",
608 tok2str(cfm_tlv_senderid_chassisid_values,
609 "Unknown",
610 chassis_id_type),
611 chassis_id_type,
612 chassis_id_length));
613
614 if (cfm_tlv_len < chassis_id_length) {
615 ND_PRINT((ndo, "\n\t (TLV too short)"));
616 return;
617 }
618
619 switch (chassis_id_type) {
620 case CFM_CHASSIS_ID_MAC_ADDRESS:
621 ND_PRINT((ndo, "\n\t MAC %s", etheraddr_string(ndo, tptr + 1)));
622 break;
623
624 case CFM_CHASSIS_ID_NETWORK_ADDRESS:
625 hexdump |= cfm_network_addr_print(ndo, tptr);
626 break;
627
628 case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
629 case CFM_CHASSIS_ID_INTERFACE_ALIAS:
630 case CFM_CHASSIS_ID_LOCAL:
631 case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
632 case CFM_CHASSIS_ID_PORT_COMPONENT:
633 safeputs(ndo, tptr + 1, chassis_id_length);
634 break;
635
636 default:
637 hexdump = TRUE;
638 break;
639 }
640 cfm_tlv_len -= chassis_id_length;
641
642 tptr += 1 + chassis_id_length;
643 tlen -= 1 + chassis_id_length;
644 }
645
646 /*
647 * Check if there is a Management Address.
648 */
649 if (cfm_tlv_len == 0) {
650 /* No, there isn't; we're done. */
651 return;
652 }
653
654 mgmt_addr_length = *tptr;
655 tptr++;
656 tlen--;
657 cfm_tlv_len--;
658 if (mgmt_addr_length) {
659 if (cfm_tlv_len < mgmt_addr_length) {
660 ND_PRINT((ndo, "\n\t (TLV too short)"));
661 return;
662 }
663 cfm_tlv_len -= mgmt_addr_length;
664 /*
665 * XXX - this is an OID; print it as such.
666 */
667 tptr += mgmt_addr_length;
668 tlen -= mgmt_addr_length;
669
670 if (cfm_tlv_len < 1) {
671 ND_PRINT((ndo, "\n\t (TLV too short)"));
672 return;
673 }
674
675 mgmt_addr_length = *tptr;
676 tptr++;
677 tlen--;
678 cfm_tlv_len--;
679 if (mgmt_addr_length) {
680 if (cfm_tlv_len < mgmt_addr_length) {
681 ND_PRINT((ndo, "\n\t (TLV too short)"));
682 return;
683 }
684 cfm_tlv_len -= mgmt_addr_length;
685 /*
686 * XXX - this is a TransportDomain; print it as such.
687 */
688 tptr += mgmt_addr_length;
689 tlen -= mgmt_addr_length;
690 }
691 }
692 break;
693 }
694
695 /*
696 * FIXME those are the defined TLVs that lack a decoder
697 * you are welcome to contribute code ;-)
698 */
699
700 case CFM_TLV_DATA:
701 case CFM_TLV_REPLY_INGRESS:
702 case CFM_TLV_REPLY_EGRESS:
703 default:
704 hexdump = TRUE;
705 break;
706 }
707 /* do we want to see an additional hexdump ? */
708 if (hexdump || ndo->ndo_vflag > 1)
709 print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len);
710
711 tptr+=cfm_tlv_len;
712 tlen-=cfm_tlv_len;
713 }
714 return;
715
716 tooshort:
717 ND_PRINT((ndo, "\n\t\t packet is too short"));
718 return;
719
720 trunc:
721 ND_PRINT((ndo, "\n\t\t packet exceeded snapshot"));
722 }
723