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 * support for the IEEE "slow protocols" LACP, MARKER as per 802.3ad
16 *                                       OAM as per 802.3ah
17 *
18 * Original code by Hannes Gredler (hannes@gredler.at)
19 */
20
21/* \summary: IEEE "slow protocols" (802.3ad/802.3ah) printer */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include "netdissect-stdinc.h"
28
29#define ND_LONGJMP_FROM_TCHECK
30#include "netdissect.h"
31#include "extract.h"
32#include "addrtoname.h"
33#include "oui.h"
34
35
36#define	SLOW_PROTO_LACP                     1
37#define	SLOW_PROTO_MARKER                   2
38#define SLOW_PROTO_OAM                      3
39
40#define	LACP_VERSION                        1
41#define	MARKER_VERSION                      1
42
43static const struct tok slow_proto_values[] = {
44    { SLOW_PROTO_LACP, "LACP" },
45    { SLOW_PROTO_MARKER, "MARKER" },
46    { SLOW_PROTO_OAM, "OAM" },
47    { 0, NULL}
48};
49
50static const struct tok slow_oam_flag_values[] = {
51    { 0x0001, "Link Fault" },
52    { 0x0002, "Dying Gasp" },
53    { 0x0004, "Critical Event" },
54    { 0x0008, "Local Evaluating" },
55    { 0x0010, "Local Stable" },
56    { 0x0020, "Remote Evaluating" },
57    { 0x0040, "Remote Stable" },
58    { 0, NULL}
59};
60
61#define SLOW_OAM_CODE_INFO          0x00
62#define SLOW_OAM_CODE_EVENT_NOTIF   0x01
63#define SLOW_OAM_CODE_VAR_REQUEST   0x02
64#define SLOW_OAM_CODE_VAR_RESPONSE  0x03
65#define SLOW_OAM_CODE_LOOPBACK_CTRL 0x04
66#define SLOW_OAM_CODE_PRIVATE       0xfe
67
68static const struct tok slow_oam_code_values[] = {
69    { SLOW_OAM_CODE_INFO, "Information" },
70    { SLOW_OAM_CODE_EVENT_NOTIF, "Event Notification" },
71    { SLOW_OAM_CODE_VAR_REQUEST, "Variable Request" },
72    { SLOW_OAM_CODE_VAR_RESPONSE, "Variable Response" },
73    { SLOW_OAM_CODE_LOOPBACK_CTRL, "Loopback Control" },
74    { SLOW_OAM_CODE_PRIVATE, "Vendor Private" },
75    { 0, NULL}
76};
77
78struct slow_oam_info_t {
79    nd_uint8_t info_type;
80    nd_uint8_t info_length;
81    nd_uint8_t oam_version;
82    nd_uint16_t revision;
83    nd_uint8_t state;
84    nd_uint8_t oam_config;
85    nd_uint16_t oam_pdu_config;
86    nd_uint24_t oui;
87    nd_uint32_t vendor_private;
88};
89
90#define SLOW_OAM_INFO_TYPE_END_OF_TLV 0x00
91#define SLOW_OAM_INFO_TYPE_LOCAL 0x01
92#define SLOW_OAM_INFO_TYPE_REMOTE 0x02
93#define SLOW_OAM_INFO_TYPE_ORG_SPECIFIC 0xfe
94
95static const struct tok slow_oam_info_type_values[] = {
96    { SLOW_OAM_INFO_TYPE_END_OF_TLV, "End of TLV marker" },
97    { SLOW_OAM_INFO_TYPE_LOCAL, "Local" },
98    { SLOW_OAM_INFO_TYPE_REMOTE, "Remote" },
99    { SLOW_OAM_INFO_TYPE_ORG_SPECIFIC, "Organization specific" },
100    { 0, NULL}
101};
102
103#define OAM_INFO_TYPE_PARSER_MASK 0x3
104static const struct tok slow_oam_info_type_state_parser_values[] = {
105    { 0x00, "forwarding" },
106    { 0x01, "looping back" },
107    { 0x02, "discarding" },
108    { 0x03, "reserved" },
109    { 0, NULL}
110};
111
112#define OAM_INFO_TYPE_MUX_MASK 0x4
113static const struct tok slow_oam_info_type_state_mux_values[] = {
114    { 0x00, "forwarding" },
115    { 0x04, "discarding" },
116    { 0, NULL}
117};
118
119static const struct tok slow_oam_info_type_oam_config_values[] = {
120    { 0x01, "Active" },
121    { 0x02, "Unidirectional" },
122    { 0x04, "Remote-Loopback" },
123    { 0x08, "Link-Events" },
124    { 0x10, "Variable-Retrieval" },
125    { 0, NULL}
126};
127
128/* 11 Bits */
129#define OAM_INFO_TYPE_PDU_SIZE_MASK 0x7ff
130
131#define SLOW_OAM_LINK_EVENT_END_OF_TLV 0x00
132#define SLOW_OAM_LINK_EVENT_ERR_SYM_PER 0x01
133#define SLOW_OAM_LINK_EVENT_ERR_FRM 0x02
134#define SLOW_OAM_LINK_EVENT_ERR_FRM_PER 0x03
135#define SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM 0x04
136#define SLOW_OAM_LINK_EVENT_ORG_SPECIFIC 0xfe
137
138static const struct tok slow_oam_link_event_values[] = {
139    { SLOW_OAM_LINK_EVENT_END_OF_TLV, "End of TLV marker" },
140    { SLOW_OAM_LINK_EVENT_ERR_SYM_PER, "Errored Symbol Period Event" },
141    { SLOW_OAM_LINK_EVENT_ERR_FRM, "Errored Frame Event" },
142    { SLOW_OAM_LINK_EVENT_ERR_FRM_PER, "Errored Frame Period Event" },
143    { SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM, "Errored Frame Seconds Summary Event" },
144    { SLOW_OAM_LINK_EVENT_ORG_SPECIFIC, "Organization specific" },
145    { 0, NULL}
146};
147
148struct slow_oam_link_event_t {
149    nd_uint8_t event_type;
150    nd_uint8_t event_length;
151    nd_uint16_t time_stamp;
152    nd_uint64_t window;
153    nd_uint64_t threshold;
154    nd_uint64_t errors;
155    nd_uint64_t errors_running_total;
156    nd_uint32_t event_running_total;
157};
158
159struct slow_oam_variablerequest_t {
160    nd_uint8_t branch;
161    nd_uint16_t leaf;
162};
163
164struct slow_oam_variableresponse_t {
165    nd_uint8_t branch;
166    nd_uint16_t leaf;
167    nd_uint8_t length;
168};
169
170struct slow_oam_loopbackctrl_t {
171    nd_uint8_t command;
172};
173
174static const struct tok slow_oam_loopbackctrl_cmd_values[] = {
175    { 0x01, "Enable OAM Remote Loopback" },
176    { 0x02, "Disable OAM Remote Loopback" },
177    { 0, NULL}
178};
179
180struct tlv_header_t {
181    nd_uint8_t type;
182    nd_uint8_t length;
183};
184
185#define LACP_MARKER_TLV_TERMINATOR     0x00  /* same code for LACP and Marker */
186
187#define LACP_TLV_ACTOR_INFO            0x01
188#define LACP_TLV_PARTNER_INFO          0x02
189#define LACP_TLV_COLLECTOR_INFO        0x03
190
191#define MARKER_TLV_MARKER_INFO         0x01
192
193static const struct tok slow_tlv_values[] = {
194    { (SLOW_PROTO_LACP << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
195    { (SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO, "Actor Information"},
196    { (SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO, "Partner Information"},
197    { (SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO, "Collector Information"},
198
199    { (SLOW_PROTO_MARKER << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
200    { (SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO, "Marker Information"},
201    { 0, NULL}
202};
203
204struct lacp_tlv_actor_partner_info_t {
205    nd_uint16_t sys_pri;
206    nd_mac_addr sys;
207    nd_uint16_t key;
208    nd_uint16_t port_pri;
209    nd_uint16_t port;
210    nd_uint8_t state;
211    nd_byte    pad[3];
212};
213
214static const struct tok lacp_tlv_actor_partner_info_state_values[] = {
215    { 0x01, "Activity"},
216    { 0x02, "Timeout"},
217    { 0x04, "Aggregation"},
218    { 0x08, "Synchronization"},
219    { 0x10, "Collecting"},
220    { 0x20, "Distributing"},
221    { 0x40, "Default"},
222    { 0x80, "Expired"},
223    { 0, NULL}
224};
225
226struct lacp_tlv_collector_info_t {
227    nd_uint16_t max_delay;
228    nd_byte     pad[12];
229};
230
231struct marker_tlv_marker_info_t {
232    nd_uint16_t req_port;
233    nd_mac_addr req_sys;
234    nd_uint32_t req_trans_id;
235    nd_byte     pad[2];
236};
237
238struct lacp_marker_tlv_terminator_t {
239    nd_byte     pad[50];
240};
241
242static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
243static void slow_oam_print(netdissect_options *, const u_char *, u_int);
244
245void
246slow_print(netdissect_options *ndo,
247           const u_char *pptr, u_int len)
248{
249    int print_version;
250    u_int subtype;
251
252    ndo->ndo_protocol = "slow";
253    if (len < 1)
254        goto tooshort;
255    subtype = GET_U_1(pptr);
256
257    /*
258     * Sanity checking of the header.
259     */
260    switch (subtype) {
261    case SLOW_PROTO_LACP:
262        if (len < 2)
263            goto tooshort;
264        if (GET_U_1(pptr + 1) != LACP_VERSION) {
265            ND_PRINT("LACP version %u packet not supported",
266                     GET_U_1(pptr + 1));
267            return;
268        }
269        print_version = 1;
270        break;
271
272    case SLOW_PROTO_MARKER:
273        if (len < 2)
274            goto tooshort;
275        if (GET_U_1(pptr + 1) != MARKER_VERSION) {
276            ND_PRINT("MARKER version %u packet not supported",
277                     GET_U_1(pptr + 1));
278            return;
279        }
280        print_version = 1;
281        break;
282
283    case SLOW_PROTO_OAM: /* fall through */
284        print_version = 0;
285        break;
286
287    default:
288        /* print basic information and exit */
289        print_version = -1;
290        break;
291    }
292
293    if (print_version == 1) {
294        ND_PRINT("%sv%u, length %u",
295               tok2str(slow_proto_values, "unknown (%u)", subtype),
296               GET_U_1((pptr + 1)),
297               len);
298    } else {
299        /* some slow protos don't have a version number in the header */
300        ND_PRINT("%s, length %u",
301               tok2str(slow_proto_values, "unknown (%u)", subtype),
302               len);
303    }
304
305    /* unrecognized subtype */
306    if (print_version == -1) {
307        print_unknown_data(ndo, pptr, "\n\t", len);
308        return;
309    }
310
311    if (!ndo->ndo_vflag)
312        return;
313
314    switch (subtype) {
315    default: /* should not happen */
316        break;
317
318    case SLOW_PROTO_OAM:
319        /* skip subtype */
320        len -= 1;
321        pptr += 1;
322        slow_oam_print(ndo, pptr, len);
323        break;
324
325    case SLOW_PROTO_LACP:   /* LACP and MARKER share the same semantics */
326    case SLOW_PROTO_MARKER:
327        /* skip subtype and version */
328        len -= 2;
329        pptr += 2;
330        slow_marker_lacp_print(ndo, pptr, len, subtype);
331        break;
332    }
333    return;
334
335tooshort:
336    if (!ndo->ndo_vflag)
337        ND_PRINT(" (packet is too short)");
338    else
339        ND_PRINT("\n\t\t packet is too short");
340}
341
342static void
343slow_marker_lacp_print(netdissect_options *ndo,
344                       const u_char *tptr, u_int tlen,
345                       u_int proto_subtype)
346{
347    const struct tlv_header_t *tlv_header;
348    const u_char *tlv_tptr;
349    u_int tlv_type, tlv_len, tlv_tlen;
350
351    union {
352        const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator;
353        const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info;
354        const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info;
355        const struct marker_tlv_marker_info_t *marker_tlv_marker_info;
356    } tlv_ptr;
357
358    while(tlen>0) {
359        /* is the packet big enough to include the tlv header ? */
360        if (tlen < sizeof(struct tlv_header_t))
361            goto tooshort;
362        /* did we capture enough for fully decoding the tlv header ? */
363        tlv_header = (const struct tlv_header_t *)tptr;
364        tlv_type = GET_U_1(tlv_header->type);
365        tlv_len = GET_U_1(tlv_header->length);
366
367        ND_PRINT("\n\t%s TLV (0x%02x), length %u",
368               tok2str(slow_tlv_values,
369                       "Unknown",
370                       (proto_subtype << 8) + tlv_type),
371               tlv_type,
372               tlv_len);
373
374        if (tlv_type == LACP_MARKER_TLV_TERMINATOR) {
375            /*
376             * This TLV has a length of zero, and means there are no
377             * more TLVs to process.
378             */
379            return;
380        }
381
382        /* length includes the type and length fields */
383        if (tlv_len < sizeof(struct tlv_header_t)) {
384            ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
385                     sizeof(struct tlv_header_t));
386            return;
387        }
388
389        /* is the packet big enough to include the tlv ? */
390        if (tlen < tlv_len)
391            goto tooshort;
392        /* did we capture enough for fully decoding the tlv ? */
393        ND_TCHECK_LEN(tptr, tlv_len);
394
395        tlv_tptr=tptr+sizeof(struct tlv_header_t);
396        tlv_tlen=tlv_len-sizeof(struct tlv_header_t);
397
398        switch((proto_subtype << 8) + tlv_type) {
399
400            /* those two TLVs have the same structure -> fall through */
401        case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO):
402        case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO):
403            if (tlv_tlen !=
404                sizeof(struct lacp_tlv_actor_partner_info_t)) {
405                ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
406                         sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t));
407                goto badlength;
408            }
409
410            tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr;
411
412            ND_PRINT("\n\t  System %s, System Priority %u, Key %u"
413                   ", Port %u, Port Priority %u\n\t  State Flags [%s]",
414                   GET_ETHERADDR_STRING(tlv_ptr.lacp_tlv_actor_partner_info->sys),
415                   GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri),
416                   GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key),
417                   GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port),
418                   GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri),
419                   bittok2str(lacp_tlv_actor_partner_info_state_values,
420                              "none",
421                              GET_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state)));
422
423            break;
424
425        case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO):
426            if (tlv_tlen !=
427                sizeof(struct lacp_tlv_collector_info_t)) {
428                ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
429                         sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t));
430                goto badlength;
431            }
432
433            tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr;
434
435            ND_PRINT("\n\t  Max Delay %u",
436                   GET_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay));
437
438            break;
439
440        case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO):
441            if (tlv_tlen !=
442                sizeof(struct marker_tlv_marker_info_t)) {
443                ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
444                         sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t));
445                goto badlength;
446            }
447
448            tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr;
449
450            ND_PRINT("\n\t  Request System %s, Request Port %u, Request Transaction ID 0x%08x",
451                   GET_ETHERADDR_STRING(tlv_ptr.marker_tlv_marker_info->req_sys),
452                   GET_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port),
453                   GET_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id));
454
455            break;
456
457        default:
458            if (ndo->ndo_vflag <= 1)
459                print_unknown_data(ndo, tlv_tptr, "\n\t  ", tlv_tlen);
460            break;
461        }
462
463    badlength:
464        /* do we want to see an additional hexdump ? */
465        if (ndo->ndo_vflag > 1) {
466            print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t  ",
467                               tlv_len-sizeof(struct tlv_header_t));
468        }
469
470        tptr+=tlv_len;
471        tlen-=tlv_len;
472    }
473    return;
474
475tooshort:
476    ND_PRINT("\n\t\t packet is too short");
477}
478
479static void
480slow_oam_print(netdissect_options *ndo,
481               const u_char *tptr, u_int tlen)
482{
483    uint8_t code;
484    uint8_t type, length;
485    uint8_t state;
486    uint8_t command;
487    u_int hexdump;
488
489    struct slow_oam_common_header_t {
490        nd_uint16_t flags;
491        nd_uint8_t code;
492    };
493
494    struct slow_oam_tlv_header_t {
495        nd_uint8_t type;
496        nd_uint8_t length;
497    };
498
499    union {
500        const struct slow_oam_common_header_t *slow_oam_common_header;
501        const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
502    } ptr;
503
504    union {
505        const struct slow_oam_info_t *slow_oam_info;
506        const struct slow_oam_link_event_t *slow_oam_link_event;
507        const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
508        const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
509        const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
510    } tlv;
511
512    ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
513    if (tlen < sizeof(*ptr.slow_oam_common_header))
514        goto tooshort;
515    ND_TCHECK_SIZE(ptr.slow_oam_common_header);
516    tptr += sizeof(struct slow_oam_common_header_t);
517    tlen -= sizeof(struct slow_oam_common_header_t);
518
519    code = GET_U_1(ptr.slow_oam_common_header->code);
520    ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
521           tok2str(slow_oam_code_values, "Unknown (%u)", code),
522           bittok2str(slow_oam_flag_values,
523                      "none",
524                      GET_BE_U_2(ptr.slow_oam_common_header->flags)));
525
526    switch (code) {
527    case SLOW_OAM_CODE_INFO:
528        while (tlen > 0) {
529            ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
530            if (tlen < sizeof(*ptr.slow_oam_tlv_header))
531                goto tooshort;
532            ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
533            type = GET_U_1(ptr.slow_oam_tlv_header->type);
534            length = GET_U_1(ptr.slow_oam_tlv_header->length);
535            ND_PRINT("\n\t  %s Information Type (%u), length %u",
536                   tok2str(slow_oam_info_type_values, "Reserved", type),
537                   type,
538                   length);
539
540            if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
541                /*
542                 * As IEEE Std 802.3-2015 says for the End of TLV Marker,
543                 * "(the length and value of the Type 0x00 TLV can be ignored)".
544                 */
545                return;
546            }
547
548            /* length includes the type and length fields */
549            if (length < sizeof(struct slow_oam_tlv_header_t)) {
550                ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
551                         sizeof(struct slow_oam_tlv_header_t));
552                return;
553            }
554
555            if (tlen < length)
556                goto tooshort;
557            ND_TCHECK_LEN(tptr, length);
558
559            hexdump = FALSE;
560            switch (type) {
561            case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
562            case SLOW_OAM_INFO_TYPE_REMOTE:
563                tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
564
565                if (GET_U_1(tlv.slow_oam_info->info_length) !=
566                    sizeof(struct slow_oam_info_t)) {
567                    ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
568                           sizeof(struct slow_oam_info_t));
569                    hexdump = TRUE;
570                    goto badlength_code_info;
571                }
572
573                ND_PRINT("\n\t    OAM-Version %u, Revision %u",
574                       GET_U_1(tlv.slow_oam_info->oam_version),
575                       GET_BE_U_2(tlv.slow_oam_info->revision));
576
577                state = GET_U_1(tlv.slow_oam_info->state);
578                ND_PRINT("\n\t    State-Parser-Action %s, State-MUX-Action %s",
579                       tok2str(slow_oam_info_type_state_parser_values, "Reserved",
580                               state & OAM_INFO_TYPE_PARSER_MASK),
581                       tok2str(slow_oam_info_type_state_mux_values, "Reserved",
582                               state & OAM_INFO_TYPE_MUX_MASK));
583                ND_PRINT("\n\t    OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
584                       bittok2str(slow_oam_info_type_oam_config_values, "none",
585                                  GET_U_1(tlv.slow_oam_info->oam_config)),
586                       GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
587                       OAM_INFO_TYPE_PDU_SIZE_MASK);
588                ND_PRINT("\n\t    OUI %s (0x%06x), Vendor-Private 0x%08x",
589                       tok2str(oui_values, "Unknown",
590                               GET_BE_U_3(tlv.slow_oam_info->oui)),
591                       GET_BE_U_3(tlv.slow_oam_info->oui),
592                       GET_BE_U_4(tlv.slow_oam_info->vendor_private));
593                break;
594
595            case SLOW_OAM_INFO_TYPE_ORG_SPECIFIC:
596                hexdump = TRUE;
597                break;
598
599            default:
600                hexdump = TRUE;
601                break;
602            }
603
604        badlength_code_info:
605            /* do we also want to see a hex dump ? */
606            if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
607                print_unknown_data(ndo, tptr, "\n\t  ",
608                                   length);
609            }
610
611            tlen -= length;
612            tptr += length;
613        }
614        break;
615
616    case SLOW_OAM_CODE_EVENT_NOTIF:
617        /* Sequence number */
618        if (tlen < 2)
619            goto tooshort;
620        ND_PRINT("\n\t  Sequence Number %u", GET_BE_U_2(tptr));
621        tlen -= 2;
622        tptr += 2;
623
624        /* TLVs */
625        while (tlen > 0) {
626            ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
627            if (tlen < sizeof(*ptr.slow_oam_tlv_header))
628                goto tooshort;
629            type = GET_U_1(ptr.slow_oam_tlv_header->type);
630            length = GET_U_1(ptr.slow_oam_tlv_header->length);
631            ND_PRINT("\n\t  %s Link Event Type (%u), length %u",
632                   tok2str(slow_oam_link_event_values, "Reserved",
633                           type),
634                   type,
635                   length);
636
637            if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
638                /*
639                 * As IEEE Std 802.3-2015 says for the End of TLV Marker,
640                 * "(the length and value of the Type 0x00 TLV can be ignored)".
641                 */
642                return;
643            }
644
645            /* length includes the type and length fields */
646            if (length < sizeof(struct slow_oam_tlv_header_t)) {
647                ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
648                         sizeof(struct slow_oam_tlv_header_t));
649                return;
650            }
651
652            if (tlen < length)
653                goto tooshort;
654            ND_TCHECK_LEN(tptr, length);
655
656            hexdump = FALSE;
657            switch (type) {
658            case SLOW_OAM_LINK_EVENT_ERR_SYM_PER: /* identical format - fall through */
659            case SLOW_OAM_LINK_EVENT_ERR_FRM:
660            case SLOW_OAM_LINK_EVENT_ERR_FRM_PER:
661            case SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM:
662                tlv.slow_oam_link_event = (const struct slow_oam_link_event_t *)tptr;
663
664                if (GET_U_1(tlv.slow_oam_link_event->event_length) !=
665                    sizeof(struct slow_oam_link_event_t)) {
666                    ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
667                             sizeof(struct slow_oam_link_event_t));
668                    hexdump = TRUE;
669                    goto badlength_event_notif;
670                }
671
672                ND_PRINT("\n\t    Timestamp %u ms, Errored Window %" PRIu64
673                       "\n\t    Errored Threshold %" PRIu64
674                       "\n\t    Errors %" PRIu64
675                       "\n\t    Error Running Total %" PRIu64
676                       "\n\t    Event Running Total %u",
677                       GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
678                       GET_BE_U_8(tlv.slow_oam_link_event->window),
679                       GET_BE_U_8(tlv.slow_oam_link_event->threshold),
680                       GET_BE_U_8(tlv.slow_oam_link_event->errors),
681                       GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
682                       GET_BE_U_4(tlv.slow_oam_link_event->event_running_total));
683                break;
684
685            case SLOW_OAM_LINK_EVENT_ORG_SPECIFIC:
686                hexdump = TRUE;
687                break;
688
689            default:
690                hexdump = TRUE;
691                break;
692            }
693
694        badlength_event_notif:
695            /* do we also want to see a hex dump ? */
696            if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
697                print_unknown_data(ndo, tptr, "\n\t  ",
698                                   length);
699            }
700
701            tlen -= length;
702            tptr += length;
703        }
704        break;
705
706    case SLOW_OAM_CODE_LOOPBACK_CTRL:
707        tlv.slow_oam_loopbackctrl = (const struct slow_oam_loopbackctrl_t *)tptr;
708        if (tlen < sizeof(*tlv.slow_oam_loopbackctrl))
709            goto tooshort;
710        command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
711        ND_PRINT("\n\t  Command %s (%u)",
712               tok2str(slow_oam_loopbackctrl_cmd_values,
713                       "Unknown",
714                       command),
715               command);
716        tptr ++;
717        tlen --;
718        break;
719
720        /*
721         * FIXME those are the defined codes that lack a decoder
722         * you are welcome to contribute code ;-)
723         */
724    case SLOW_OAM_CODE_VAR_REQUEST:
725    case SLOW_OAM_CODE_VAR_RESPONSE:
726    case SLOW_OAM_CODE_PRIVATE:
727    default:
728        if (ndo->ndo_vflag <= 1) {
729            print_unknown_data(ndo, tptr, "\n\t  ", tlen);
730        }
731        break;
732    }
733    return;
734
735tooshort:
736    ND_PRINT("\n\t\t packet is too short");
737}
738