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