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 <stdio.h>
27
28#include "netdissect.h"
29#include "extract.h"
30#include "ether.h"
31#include "addrtoname.h"
32#include "oui.h"
33#include "af.h"
34
35struct cfm_common_header_t {
36    uint8_t mdlevel_version;
37    uint8_t opcode;
38    uint8_t flags;
39    uint8_t first_tlv_offset;
40};
41
42#define	CFM_VERSION 0
43#define CFM_EXTRACT_VERSION(x) (((x)&0x1f))
44#define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
45
46#define	CFM_OPCODE_CCM 1
47#define	CFM_OPCODE_LBR 2
48#define	CFM_OPCODE_LBM 3
49#define	CFM_OPCODE_LTR 4
50#define	CFM_OPCODE_LTM 5
51
52static const struct tok cfm_opcode_values[] = {
53    { CFM_OPCODE_CCM, "Continouity Check Message"},
54    { CFM_OPCODE_LBR, "Loopback Reply"},
55    { CFM_OPCODE_LBM, "Loopback Message"},
56    { CFM_OPCODE_LTR, "Linktrace Reply"},
57    { CFM_OPCODE_LTM, "Linktrace Message"},
58    { 0, NULL}
59};
60
61/*
62 * Message Formats.
63 */
64struct cfm_ccm_t {
65    uint8_t sequence[4];
66    uint8_t ma_epi[2];
67    uint8_t names[48];
68    uint8_t itu_t_y_1731[16];
69};
70
71/*
72 * Timer Bases for the CCM Interval field.
73 * Expressed in units of seconds.
74 */
75static const float ccm_interval_base[8] = {0, 0.003333, 0.01, 0.1, 1, 10, 60, 600};
76#define CCM_INTERVAL_MIN_MULTIPLIER 3.25
77#define CCM_INTERVAL_MAX_MULTIPLIER 3.5
78
79#define CFM_CCM_RDI_FLAG 0x80
80#define CFM_EXTRACT_CCM_INTERVAL(x) (((x)&0x07))
81
82#define CFM_CCM_MD_FORMAT_8021 0
83#define CFM_CCM_MD_FORMAT_NONE 1
84#define CFM_CCM_MD_FORMAT_DNS  2
85#define CFM_CCM_MD_FORMAT_MAC  3
86#define CFM_CCM_MD_FORMAT_CHAR 4
87
88static const struct tok cfm_md_nameformat_values[] = {
89    { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"},
90    { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"},
91    { CFM_CCM_MD_FORMAT_DNS, "DNS string"},
92    { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"},
93    { CFM_CCM_MD_FORMAT_CHAR, "Character string"},
94    { 0, NULL}
95};
96
97#define CFM_CCM_MA_FORMAT_8021 0
98#define CFM_CCM_MA_FORMAT_VID  1
99#define CFM_CCM_MA_FORMAT_CHAR 2
100#define CFM_CCM_MA_FORMAT_INT  3
101#define CFM_CCM_MA_FORMAT_VPN  4
102
103static const struct tok cfm_ma_nameformat_values[] = {
104    { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"},
105    { CFM_CCM_MA_FORMAT_VID, "Primary VID"},
106    { CFM_CCM_MA_FORMAT_CHAR, "Character string"},
107    { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"},
108    { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"},
109    { 0, NULL}
110};
111
112struct cfm_lbm_t {
113    uint8_t transaction_id[4];
114};
115
116struct cfm_ltm_t {
117    uint8_t transaction_id[4];
118    uint8_t ttl;
119    uint8_t original_mac[ETHER_ADDR_LEN];
120    uint8_t target_mac[ETHER_ADDR_LEN];
121};
122
123static const struct tok cfm_ltm_flag_values[] = {
124    { 0x80, "Use Forwarding-DB only"},
125    { 0, NULL}
126};
127
128struct cfm_ltr_t {
129    uint8_t transaction_id[4];
130    uint8_t ttl;
131    uint8_t replay_action;
132};
133
134static const struct tok cfm_ltr_flag_values[] = {
135    { 0x80, "UseFDB Only"},
136    { 0x40, "FwdYes"},
137    { 0x20, "Terminal MEP"},
138    { 0, NULL}
139};
140
141static const struct tok cfm_ltr_replay_action_values[] = {
142    { 1, "Exact Match"},
143    { 2, "Filtering DB"},
144    { 3, "MIP CCM DB"},
145    { 0, NULL}
146};
147
148
149#define CFM_TLV_END 0
150#define CFM_TLV_SENDER_ID 1
151#define CFM_TLV_PORT_STATUS 2
152#define CFM_TLV_INTERFACE_STATUS 3
153#define CFM_TLV_DATA 4
154#define CFM_TLV_REPLY_INGRESS 5
155#define CFM_TLV_REPLY_EGRESS 6
156#define CFM_TLV_PRIVATE 31
157
158static const struct tok cfm_tlv_values[] = {
159    { CFM_TLV_END, "End"},
160    { CFM_TLV_SENDER_ID, "Sender ID"},
161    { CFM_TLV_PORT_STATUS, "Port status"},
162    { CFM_TLV_INTERFACE_STATUS, "Interface status"},
163    { CFM_TLV_DATA, "Data"},
164    { CFM_TLV_REPLY_INGRESS, "Reply Ingress"},
165    { CFM_TLV_REPLY_EGRESS, "Reply Egress"},
166    { CFM_TLV_PRIVATE, "Organization Specific"},
167    { 0, NULL}
168};
169
170/*
171 * TLVs
172 */
173
174struct cfm_tlv_header_t {
175    uint8_t type;
176    uint8_t length[2];
177};
178
179/* FIXME define TLV formats */
180
181static const struct tok cfm_tlv_port_status_values[] = {
182    { 1, "Blocked"},
183    { 2, "Up"},
184    { 0, NULL}
185};
186
187static const struct tok cfm_tlv_interface_status_values[] = {
188    { 1, "Up"},
189    { 2, "Down"},
190    { 3, "Testing"},
191    { 5, "Dormant"},
192    { 6, "not present"},
193    { 7, "lower Layer down"},
194    { 0, NULL}
195};
196
197#define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1
198#define CFM_CHASSIS_ID_INTERFACE_ALIAS 2
199#define CFM_CHASSIS_ID_PORT_COMPONENT 3
200#define CFM_CHASSIS_ID_MAC_ADDRESS 4
201#define CFM_CHASSIS_ID_NETWORK_ADDRESS 5
202#define CFM_CHASSIS_ID_INTERFACE_NAME 6
203#define CFM_CHASSIS_ID_LOCAL 7
204
205static const struct tok cfm_tlv_senderid_chassisid_values[] = {
206    { 0, "Reserved"},
207    { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"},
208    { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"},
209    { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"},
210    { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"},
211    { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"},
212    { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"},
213    { CFM_CHASSIS_ID_LOCAL, "Locally assigned"},
214    { 0, NULL}
215};
216
217
218static int
219cfm_network_addr_print(netdissect_options *ndo,
220                       register const u_char *tptr, const u_int length)
221{
222    u_int network_addr_type;
223    u_int hexdump =  FALSE;
224
225    /*
226     * Altough AFIs are tpically 2 octects wide,
227     * 802.1ab specifies that this field width
228     * is only once octet
229     */
230    if (length < 1) {
231        ND_PRINT((ndo, "\n\t  Network Address Type (invalid, no data"));
232        return hexdump;
233    }
234    /* The calling function must make any due ND_TCHECK calls. */
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        if (length != 1 + 4) {
246            ND_PRINT((ndo, "(invalid IPv4 address length %u)", length - 1));
247            hexdump = TRUE;
248            break;
249        }
250        ND_PRINT((ndo, ", %s", ipaddr_string(ndo, tptr + 1)));
251        break;
252
253    case AFNUM_INET6:
254        if (length != 1 + 16) {
255            ND_PRINT((ndo, "(invalid IPv6 address length %u)", length - 1));
256            hexdump = TRUE;
257            break;
258        }
259        ND_PRINT((ndo, ", %s", ip6addr_string(ndo, tptr + 1)));
260        break;
261
262    default:
263        hexdump = TRUE;
264        break;
265    }
266
267    return hexdump;
268}
269
270void
271cfm_print(netdissect_options *ndo,
272          register const u_char *pptr, register u_int length)
273{
274    const struct cfm_common_header_t *cfm_common_header;
275    const struct cfm_tlv_header_t *cfm_tlv_header;
276    const uint8_t *tptr, *tlv_ptr;
277    const uint8_t *namesp;
278    u_int names_data_remaining;
279    uint8_t md_nameformat, md_namelength;
280    const uint8_t *md_name;
281    uint8_t ma_nameformat, ma_namelength;
282    const uint8_t *ma_name;
283    u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
284
285
286    union {
287        const struct cfm_ccm_t *cfm_ccm;
288        const struct cfm_lbm_t *cfm_lbm;
289        const struct cfm_ltm_t *cfm_ltm;
290        const struct cfm_ltr_t *cfm_ltr;
291    } msg_ptr;
292
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(*cfm_common_header);
298
299    /*
300     * Sanity checking of the header.
301     */
302    if (CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version) != CFM_VERSION) {
303	ND_PRINT((ndo, "CFMv%u not supported, length %u",
304               CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version), length));
305	return;
306    }
307
308    ND_PRINT((ndo, "CFMv%u %s, MD Level %u, length %u",
309           CFM_EXTRACT_VERSION(cfm_common_header->mdlevel_version),
310           tok2str(cfm_opcode_values, "unknown (%u)", cfm_common_header->opcode),
311           CFM_EXTRACT_MD_LEVEL(cfm_common_header->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    ND_PRINT((ndo, "\n\tFirst TLV offset %u", cfm_common_header->first_tlv_offset));
322
323    tptr += sizeof(const struct cfm_common_header_t);
324    tlen = length - sizeof(struct cfm_common_header_t);
325
326    /*
327     * Sanity check the first TLV offset.
328     */
329    if (cfm_common_header->first_tlv_offset > tlen) {
330        ND_PRINT((ndo, " (too large, must be <= %u)", tlen));
331        return;
332    }
333
334    switch (cfm_common_header->opcode) {
335    case CFM_OPCODE_CCM:
336        msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
337        if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
338            ND_PRINT((ndo, " (too small 1, must be >= %lu)",
339                     (unsigned long) sizeof(*msg_ptr.cfm_ccm)));
340            return;
341        }
342        if (tlen < sizeof(*msg_ptr.cfm_ccm))
343            goto tooshort;
344        ND_TCHECK(*msg_ptr.cfm_ccm);
345
346        ccm_interval = CFM_EXTRACT_CCM_INTERVAL(cfm_common_header->flags);
347        ND_PRINT((ndo, ", Flags [CCM Interval %u%s]",
348               ccm_interval,
349               cfm_common_header->flags & CFM_CCM_RDI_FLAG ?
350               ", RDI" : ""));
351
352        /*
353         * Resolve the CCM interval field.
354         */
355        if (ccm_interval) {
356            ND_PRINT((ndo, "\n\t  CCM Interval %.3fs"
357                   ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
358                   ccm_interval_base[ccm_interval],
359                   ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
360                   ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER));
361        }
362
363        ND_PRINT((ndo, "\n\t  Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
364               EXTRACT_32BITS(msg_ptr.cfm_ccm->sequence),
365               EXTRACT_16BITS(msg_ptr.cfm_ccm->ma_epi)));
366
367        namesp = msg_ptr.cfm_ccm->names;
368        names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
369
370        /*
371         * Resolve the MD fields.
372         */
373        md_nameformat = *namesp;
374        namesp++;
375        names_data_remaining--;  /* We know this is != 0 */
376        if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
377            md_namelength = *namesp;
378            namesp++;
379            names_data_remaining--; /* We know this is !=0 */
380            ND_PRINT((ndo, "\n\t  MD Name Format %s (%u), MD Name length %u",
381                   tok2str(cfm_md_nameformat_values, "Unknown",
382                           md_nameformat),
383                   md_nameformat,
384                   md_namelength));
385
386            /*
387             * -3 for the MA short name format and length and one byte
388             * of MA short name.
389             */
390            if (md_namelength > names_data_remaining - 3) {
391                ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining - 2));
392                return;
393            }
394
395            md_name = namesp;
396            ND_PRINT((ndo, "\n\t  MD Name: "));
397            switch (md_nameformat) {
398            case CFM_CCM_MD_FORMAT_DNS:
399            case CFM_CCM_MD_FORMAT_CHAR:
400                safeputs(ndo, md_name, md_namelength);
401                break;
402
403            case CFM_CCM_MD_FORMAT_MAC:
404                if (md_namelength == 6) {
405                    ND_PRINT((ndo, "\n\t  MAC %s", etheraddr_string(ndo,
406                               md_name)));
407                } else {
408                    ND_PRINT((ndo, "\n\t  MAC (length invalid)"));
409                }
410                break;
411
412                /* FIXME add printers for those MD formats - hexdump for now */
413            case CFM_CCM_MA_FORMAT_8021:
414            default:
415                print_unknown_data(ndo, md_name, "\n\t    ",
416                                   md_namelength);
417            }
418            namesp += md_namelength;
419            names_data_remaining -= md_namelength;
420        } else {
421            ND_PRINT((ndo, "\n\t  MD Name Format %s (%u)",
422                   tok2str(cfm_md_nameformat_values, "Unknown",
423                           md_nameformat),
424                   md_nameformat));
425        }
426
427
428        /*
429         * Resolve the MA fields.
430         */
431        ma_nameformat = *namesp;
432        namesp++;
433        names_data_remaining--; /* We know this is != 0 */
434        ma_namelength = *namesp;
435        namesp++;
436        names_data_remaining--; /* We know this is != 0 */
437        ND_PRINT((ndo, "\n\t  MA Name-Format %s (%u), MA name length %u",
438               tok2str(cfm_ma_nameformat_values, "Unknown",
439                       ma_nameformat),
440               ma_nameformat,
441               ma_namelength));
442
443        if (ma_namelength > names_data_remaining) {
444            ND_PRINT((ndo, " (too large, must be <= %u)", names_data_remaining));
445            return;
446        }
447
448        ma_name = namesp;
449        ND_PRINT((ndo, "\n\t  MA Name: "));
450        switch (ma_nameformat) {
451        case CFM_CCM_MA_FORMAT_CHAR:
452            safeputs(ndo, ma_name, ma_namelength);
453            break;
454
455            /* FIXME add printers for those MA formats - hexdump for now */
456        case CFM_CCM_MA_FORMAT_8021:
457        case CFM_CCM_MA_FORMAT_VID:
458        case CFM_CCM_MA_FORMAT_INT:
459        case CFM_CCM_MA_FORMAT_VPN:
460        default:
461            print_unknown_data(ndo, ma_name, "\n\t    ", ma_namelength);
462        }
463        break;
464
465    case CFM_OPCODE_LTM:
466        msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
467        if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
468            ND_PRINT((ndo, " (too small 4, must be >= %lu)",
469                     (unsigned long) sizeof(*msg_ptr.cfm_ltm)));
470            return;
471        }
472        if (tlen < sizeof(*msg_ptr.cfm_ltm))
473            goto tooshort;
474        ND_TCHECK(*msg_ptr.cfm_ltm);
475
476        ND_PRINT((ndo, ", Flags [%s]",
477               bittok2str(cfm_ltm_flag_values, "none", cfm_common_header->flags)));
478
479        ND_PRINT((ndo, "\n\t  Transaction-ID 0x%08x, ttl %u",
480               EXTRACT_32BITS(msg_ptr.cfm_ltm->transaction_id),
481               msg_ptr.cfm_ltm->ttl));
482
483        ND_PRINT((ndo, "\n\t  Original-MAC %s, Target-MAC %s",
484               etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac),
485               etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac)));
486        break;
487
488    case CFM_OPCODE_LTR:
489        msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
490        if (cfm_common_header->first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
491            ND_PRINT((ndo, " (too small 5, must be >= %lu)",
492                     (unsigned long) sizeof(*msg_ptr.cfm_ltr)));
493            return;
494        }
495        if (tlen < sizeof(*msg_ptr.cfm_ltr))
496            goto tooshort;
497        ND_TCHECK(*msg_ptr.cfm_ltr);
498
499        ND_PRINT((ndo, ", Flags [%s]",
500               bittok2str(cfm_ltr_flag_values, "none", cfm_common_header->flags)));
501
502        ND_PRINT((ndo, "\n\t  Transaction-ID 0x%08x, ttl %u",
503               EXTRACT_32BITS(msg_ptr.cfm_ltr->transaction_id),
504               msg_ptr.cfm_ltr->ttl));
505
506        ND_PRINT((ndo, "\n\t  Replay-Action %s (%u)",
507               tok2str(cfm_ltr_replay_action_values,
508                       "Unknown",
509                       msg_ptr.cfm_ltr->replay_action),
510               msg_ptr.cfm_ltr->replay_action));
511        break;
512
513        /*
514         * No message decoder yet.
515         * Hexdump everything up until the start of the TLVs
516         */
517    case CFM_OPCODE_LBR:
518    case CFM_OPCODE_LBM:
519    default:
520        print_unknown_data(ndo, tptr, "\n\t  ",
521                           tlen -  cfm_common_header->first_tlv_offset);
522        break;
523    }
524
525    tptr += cfm_common_header->first_tlv_offset;
526    tlen -= cfm_common_header->first_tlv_offset;
527
528    while (tlen > 0) {
529        cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
530
531        /* Enough to read the tlv type ? */
532        ND_TCHECK2(*tptr, 1);
533        cfm_tlv_type=cfm_tlv_header->type;
534
535        ND_PRINT((ndo, "\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_TCHECK2(*tptr, sizeof(struct cfm_tlv_header_t));
548        cfm_tlv_len=EXTRACT_16BITS(&cfm_tlv_header->length);
549
550        ND_PRINT((ndo, ", 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_TCHECK2(*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((ndo, " (too short, must be >= 1)"));
566                return;
567            }
568            ND_PRINT((ndo, ", Status: %s (%u)",
569                   tok2str(cfm_tlv_port_status_values, "Unknown", *tptr),
570                   *tptr));
571            break;
572
573        case CFM_TLV_INTERFACE_STATUS:
574            if (cfm_tlv_len < 1) {
575                ND_PRINT((ndo, " (too short, must be >= 1)"));
576                return;
577            }
578            ND_PRINT((ndo, ", Status: %s (%u)",
579                   tok2str(cfm_tlv_interface_status_values, "Unknown", *tptr),
580                   *tptr));
581            break;
582
583        case CFM_TLV_PRIVATE:
584            if (cfm_tlv_len < 4) {
585                ND_PRINT((ndo, " (too short, must be >= 4)"));
586                return;
587            }
588            ND_PRINT((ndo, ", Vendor: %s (%u), Sub-Type %u",
589                   tok2str(oui_values,"Unknown", EXTRACT_24BITS(tptr)),
590                   EXTRACT_24BITS(tptr),
591                   *(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((ndo, " (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 = *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((ndo, "\n\t  (TLV too short)"));
622                    goto next_tlv;
623                }
624                chassis_id_type = *tptr;
625                cfm_tlv_len--;
626                ND_PRINT((ndo, "\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((ndo, "\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 != ETHER_ADDR_LEN) {
642                        ND_PRINT((ndo, " (invalid MAC address length)"));
643                        hexdump = TRUE;
644                        break;
645                    }
646                    ND_PRINT((ndo, "\n\t  MAC %s", etheraddr_string(ndo, 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                    safeputs(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 = *tptr;
684            tptr++;
685            tlen--;
686            cfm_tlv_len--;
687            ND_PRINT((ndo, "\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((ndo, "\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((ndo, " (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 = *tptr;
714                tptr++;
715                tlen--;
716                cfm_tlv_len--;
717                ND_PRINT((ndo, "\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((ndo, "\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
752next_tlv:
753        tptr+=cfm_tlv_len;
754        tlen-=cfm_tlv_len;
755    }
756    return;
757
758tooshort:
759    ND_PRINT((ndo, "\n\t\t packet is too short"));
760    return;
761
762trunc:
763    ND_PRINT((ndo, "\n\t\t packet exceeded snapshot"));
764}
765