Home | History | Annotate | Line # | Download | only in dist
print-slow.c revision 1.1.1.8
      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 (at) 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 
     43 static 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 
     50 static 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 
     68 static 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 
     78 struct 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 
     95 static 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
    104 static 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
    113 static const struct tok slow_oam_info_type_state_mux_values[] = {
    114     { 0x00, "forwarding" },
    115     { 0x04, "discarding" },
    116     { 0, NULL}
    117 };
    118 
    119 static 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 
    138 static 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 
    148 struct 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 
    159 struct slow_oam_variablerequest_t {
    160     nd_uint8_t branch;
    161     nd_uint16_t leaf;
    162 };
    163 
    164 struct slow_oam_variableresponse_t {
    165     nd_uint8_t branch;
    166     nd_uint16_t leaf;
    167     nd_uint8_t length;
    168 };
    169 
    170 struct slow_oam_loopbackctrl_t {
    171     nd_uint8_t command;
    172 };
    173 
    174 static 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 
    180 struct 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 
    193 static 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 
    204 struct 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 
    214 static 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 
    226 struct lacp_tlv_collector_info_t {
    227     nd_uint16_t max_delay;
    228     nd_byte     pad[12];
    229 };
    230 
    231 struct 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 
    238 struct lacp_marker_tlv_terminator_t {
    239     nd_byte     pad[50];
    240 };
    241 
    242 static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
    243 static void slow_oam_print(netdissect_options *, const u_char *, u_int);
    244 
    245 void
    246 slow_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 
    335 tooshort:
    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 
    342 static void
    343 slow_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 
    475 tooshort:
    476     ND_PRINT("\n\t\t packet is too short");
    477 }
    478 
    479 static void
    480 slow_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 
    735 tooshort:
    736     ND_PRINT("\n\t\t packet is too short");
    737 }
    738