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