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#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include "netdissect-stdinc.h"
25
26#include "netdissect.h"
27#include "extract.h"
28#include "addrtoname.h"
29#include "oui.h"
30#include "af.h"
31
32
33struct 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;
38};
39
40#define	CFM_VERSION 0
41#define CFM_EXTRACT_VERSION(x) ((x)&0x1f)
42#define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
43
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
49
50static 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}
57};
58
59/*
60 * Message Formats.
61 */
62struct 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];
67};
68
69/*
70 * Timer Bases for the CCM Interval field.
71 * Expressed in units of seconds.
72 */
73static 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
76
77#define CFM_CCM_RDI_FLAG 0x80
78#define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07)
79
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
85
86static 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}
93};
94
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
100
101static 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}
108};
109
110struct cfm_lbm_t {
111    nd_uint32_t transaction_id;
112};
113
114struct 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;
119};
120
121static const struct tok cfm_ltm_flag_values[] = {
122    { 0x80, "Use Forwarding-DB only"},
123    { 0, NULL}
124};
125
126struct cfm_ltr_t {
127    nd_uint32_t transaction_id;
128    nd_uint8_t  ttl;
129    nd_uint8_t  replay_action;
130};
131
132static const struct tok cfm_ltr_flag_values[] = {
133    { 0x80, "UseFDB Only"},
134    { 0x40, "FwdYes"},
135    { 0x20, "Terminal MEP"},
136    { 0, NULL}
137};
138
139static const struct tok cfm_ltr_replay_action_values[] = {
140    { 1, "Exact Match"},
141    { 2, "Filtering DB"},
142    { 3, "MIP CCM DB"},
143    { 0, NULL}
144};
145
146
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
155
156static 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}
166};
167
168/*
169 * TLVs
170 */
171
172struct cfm_tlv_header_t {
173    nd_uint8_t  type;
174    nd_uint16_t length;
175};
176
177/* FIXME define TLV formats */
178
179static const struct tok cfm_tlv_port_status_values[] = {
180    { 1, "Blocked"},
181    { 2, "Up"},
182    { 0, NULL}
183};
184
185static 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}
193};
194
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
202
203static 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}
213};
214
215
216static int
217cfm_network_addr_print(netdissect_options *ndo,
218                       const u_char *tptr, const u_int length)
219{
220    u_int network_addr_type;
221    u_int hexdump =  FALSE;
222
223    /*
224     * Although AFIs are typically 2 octets wide,
225     * 802.1ab specifies that this field width
226     * is only one octet.
227     */
228    if (length < 1) {
229        ND_PRINT("\n\t  Network Address Type (invalid, no data");
230        return hexdump;
231    }
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);
237
238    /*
239     * Resolve the passed in Address.
240     */
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;
247        }
248        ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1));
249        break;
250
251    case AFNUM_INET6:
252        if (length != 1 + 16) {
253            ND_PRINT("(invalid IPv6 address length %u)", length - 1);
254            hexdump = TRUE;
255            break;
256        }
257        ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1));
258        break;
259
260    default:
261        hexdump = TRUE;
262        break;
263    }
264
265    return hexdump;
266}
267
268void
269cfm_print(netdissect_options *ndo,
270          const u_char *pptr, u_int length)
271{
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;
283
284
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;
291
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);
298
299    /*
300     * Sanity checking of the header.
301     */
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;
307    }
308
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);
315
316    /*
317     * In non-verbose mode just print the opcode and md-level.
318     */
319    if (ndo->ndo_vflag < 1) {
320        return;
321    }
322
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);
326
327    tptr += sizeof(struct cfm_common_header_t);
328    tlen = length - sizeof(struct cfm_common_header_t);
329
330    /*
331     * Sanity check the first TLV offset.
332     */
333    if (first_tlv_offset > tlen) {
334        ND_PRINT(" (too large, must be <= %u)", tlen);
335        return;
336    }
337
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;
345        }
346        if (tlen < sizeof(*msg_ptr.cfm_ccm))
347            goto tooshort;
348        ND_TCHECK_SIZE(msg_ptr.cfm_ccm);
349
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" : "");
355
356        /*
357         * Resolve the CCM interval field.
358         */
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);
365        }
366
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));
370
371        namesp = msg_ptr.cfm_ccm->names;
372        names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
373
374        /*
375         * Resolve the MD fields.
376         */
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);
389
390            /*
391             * -3 for the MA short name format and length and one byte
392             * of MA short name.
393             */
394            if (md_namelength > names_data_remaining - 3) {
395                ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2);
396                return;
397            }
398
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;
406
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)");
412                }
413                break;
414
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);
420            }
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);
428        }
429
430
431        /*
432         * Resolve the MA fields.
433         */
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);
445
446        if (ma_namelength > names_data_remaining) {
447            ND_PRINT(" (too large, must be <= %u)", names_data_remaining);
448            return;
449        }
450
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;
457
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);
465        }
466        break;
467
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;
474        }
475        if (tlen < sizeof(*msg_ptr.cfm_ltm))
476            goto tooshort;
477        ND_TCHECK_SIZE(msg_ptr.cfm_ltm);
478
479        ND_PRINT(", Flags [%s]",
480               bittok2str(cfm_ltm_flag_values, "none", flags));
481
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));
485
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;
490
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;
497        }
498        if (tlen < sizeof(*msg_ptr.cfm_ltr))
499            goto tooshort;
500        ND_TCHECK_SIZE(msg_ptr.cfm_ltr);
501
502        ND_PRINT(", Flags [%s]",
503               bittok2str(cfm_ltr_flag_values, "none", flags));
504
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));
508
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;
515
516        /*
517         * No message decoder yet.
518         * Hexdump everything up until the start of the TLVs
519         */
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;
526    }
527
528    tptr += first_tlv_offset;
529    tlen -= first_tlv_offset;
530
531    while (tlen > 0) {
532        cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
533
534        /* Enough to read the tlv type ? */
535        cfm_tlv_type = GET_U_1(cfm_tlv_header->type);
536
537        ND_PRINT("\n\t%s TLV (0x%02x)",
538               tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
539               cfm_tlv_type);
540
541        if (cfm_tlv_type == CFM_TLV_END) {
542            /* Length is "Not present if the Type field is 0." */
543            return;
544        }
545
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);
551
552        ND_PRINT(", length %u", cfm_tlv_len);
553
554        tptr += sizeof(struct cfm_tlv_header_t);
555        tlen -= sizeof(struct cfm_tlv_header_t);
556        tlv_ptr = tptr;
557
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;
563
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;
569            }
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;
574
575        case CFM_TLV_INTERFACE_STATUS:
576            if (cfm_tlv_len < 1) {
577                ND_PRINT(" (too short, must be >= 1)");
578                return;
579            }
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;
584
585        case CFM_TLV_PRIVATE:
586            if (cfm_tlv_len < 4) {
587                ND_PRINT(" (too short, must be >= 4)");
588                return;
589            }
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;
596
597        case CFM_TLV_SENDER_ID:
598        {
599            u_int chassis_id_type, chassis_id_length;
600            u_int mgmt_addr_length;
601
602            if (cfm_tlv_len < 1) {
603                ND_PRINT(" (too short, must be >= 1)");
604                goto next_tlv;
605            }
606
607            /*
608             * Get the Chassis ID length and check it.
609             * IEEE 802.1Q-2014 Section 21.5.3.1
610             */
611            chassis_id_length = GET_U_1(tptr);
612            tptr++;
613            tlen--;
614            cfm_tlv_len--;
615
616            if (chassis_id_length) {
617                /*
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
621                 */
622                if (cfm_tlv_len < 1) {
623                    ND_PRINT("\n\t  (TLV too short)");
624                    goto next_tlv;
625                }
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);
634
635                if (cfm_tlv_len < chassis_id_length) {
636                    ND_PRINT("\n\t  (TLV too short)");
637                    goto next_tlv;
638                }
639
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;
647                    }
648                    ND_PRINT("\n\t  MAC %s", GET_ETHERADDR_STRING(tptr + 1));
649                    break;
650
651                case CFM_CHASSIS_ID_NETWORK_ADDRESS:
652                    hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
653                    break;
654
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;
662
663                default:
664                    hexdump = TRUE;
665                    break;
666                }
667                cfm_tlv_len -= chassis_id_length;
668
669                tptr += 1 + chassis_id_length;
670                tlen -= 1 + chassis_id_length;
671            }
672
673            /*
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.
678             */
679            if (cfm_tlv_len == 0) {
680                /* No, there isn't; we're done. */
681                break;
682            }
683
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;
695                }
696                cfm_tlv_len -= mgmt_addr_length;
697                /*
698                 * XXX - this is an OID; print it as such.
699                 */
700                hex_print(ndo, "\n\t  Management Address Domain: ", tptr, mgmt_addr_length);
701                tptr += mgmt_addr_length;
702                tlen -= mgmt_addr_length;
703
704                /*
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.
707                 */
708                if (cfm_tlv_len < 1) {
709                    ND_PRINT(" (Management Address Length is missing)");
710                    hexdump = TRUE;
711                    break;
712                }
713
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;
725                    }
726                    cfm_tlv_len -= mgmt_addr_length;
727                    /*
728                     * XXX - this is a TransportDomain; print it as such.
729                     */
730                    hex_print(ndo, "\n\t  Management Address: ", tptr, mgmt_addr_length);
731                    tptr += mgmt_addr_length;
732                    tlen -= mgmt_addr_length;
733                }
734            }
735            break;
736        }
737
738            /*
739             * FIXME those are the defined TLVs that lack a decoder
740             * you are welcome to contribute code ;-)
741             */
742
743        case CFM_TLV_DATA:
744        case CFM_TLV_REPLY_INGRESS:
745        case CFM_TLV_REPLY_EGRESS:
746        default:
747            hexdump = TRUE;
748            break;
749        }
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);
753
754next_tlv:
755        tptr+=cfm_tlv_len;
756        tlen-=cfm_tlv_len;
757    }
758    return;
759
760tooshort:
761    ND_PRINT("\n\t\t packet is too short");
762    return;
763
764trunc:
765    nd_print_trunc(ndo);
766}
767