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