Home | History | Annotate | Line # | Download | only in dist
print-slow.c revision 1.10.2.1
      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 #include <sys/cdefs.h>
     22 #ifndef lint
     23 __RCSID("$NetBSD: print-slow.c,v 1.10.2.1 2025/08/02 05:23:26 perseant Exp $");
     24 #endif
     25 
     26 /* \summary: IEEE "slow protocols" (802.3ad/802.3ah) printer */
     27 
     28 #include <config.h>
     29 
     30 #include "netdissect-stdinc.h"
     31 
     32 #define ND_LONGJMP_FROM_TCHECK
     33 #include "netdissect.h"
     34 #include "extract.h"
     35 #include "addrtoname.h"
     36 #include "oui.h"
     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 
     46 static 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 
     53 static 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 
     71 static 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 
     81 struct slow_oam_info_t {
     82     nd_uint8_t info_type;
     83     nd_uint8_t info_length;
     84     nd_uint8_t oam_version;
     85     nd_uint16_t revision;
     86     nd_uint8_t state;
     87     nd_uint8_t oam_config;
     88     nd_uint16_t oam_pdu_config;
     89     nd_uint24_t oui;
     90     nd_uint32_t vendor_private;
     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 
     98 static 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
    107 static 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
    116 static const struct tok slow_oam_info_type_state_mux_values[] = {
    117     { 0x00, "forwarding" },
    118     { 0x04, "discarding" },
    119     { 0, NULL}
    120 };
    121 
    122 static 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 
    141 static 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 
    151 struct slow_oam_link_event_t {
    152     nd_uint8_t event_type;
    153     nd_uint8_t event_length;
    154     nd_uint16_t time_stamp;
    155     nd_uint64_t window;
    156     nd_uint64_t threshold;
    157     nd_uint64_t errors;
    158     nd_uint64_t errors_running_total;
    159     nd_uint32_t event_running_total;
    160 };
    161 
    162 struct slow_oam_variablerequest_t {
    163     nd_uint8_t branch;
    164     nd_uint16_t leaf;
    165 };
    166 
    167 struct slow_oam_variableresponse_t {
    168     nd_uint8_t branch;
    169     nd_uint16_t leaf;
    170     nd_uint8_t length;
    171 };
    172 
    173 struct slow_oam_loopbackctrl_t {
    174     nd_uint8_t command;
    175 };
    176 
    177 static 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 
    183 struct tlv_header_t {
    184     nd_uint8_t type;
    185     nd_uint8_t length;
    186 };
    187 
    188 #define LACP_MARKER_TLV_TERMINATOR     0x00  /* same code for LACP and Marker */
    189 
    190 #define LACP_TLV_ACTOR_INFO            0x01
    191 #define LACP_TLV_PARTNER_INFO          0x02
    192 #define LACP_TLV_COLLECTOR_INFO        0x03
    193 
    194 #define MARKER_TLV_MARKER_INFO         0x01
    195 
    196 static const struct tok slow_tlv_values[] = {
    197     { (SLOW_PROTO_LACP << 8) + LACP_MARKER_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) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
    203     { (SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO, "Marker Information"},
    204     { 0, NULL}
    205 };
    206 
    207 struct lacp_tlv_actor_partner_info_t {
    208     nd_uint16_t sys_pri;
    209     nd_mac_addr sys;
    210     nd_uint16_t key;
    211     nd_uint16_t port_pri;
    212     nd_uint16_t port;
    213     nd_uint8_t state;
    214     nd_byte    pad[3];
    215 };
    216 
    217 static 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 
    229 struct lacp_tlv_collector_info_t {
    230     nd_uint16_t max_delay;
    231     nd_byte     pad[12];
    232 };
    233 
    234 struct marker_tlv_marker_info_t {
    235     nd_uint16_t req_port;
    236     nd_mac_addr req_sys;
    237     nd_uint32_t req_trans_id;
    238     nd_byte     pad[2];
    239 };
    240 
    241 struct lacp_marker_tlv_terminator_t {
    242     nd_byte     pad[50];
    243 };
    244 
    245 static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
    246 static void slow_oam_print(netdissect_options *, const u_char *, u_int);
    247 
    248 void
    249 slow_print(netdissect_options *ndo,
    250            const u_char *pptr, u_int len)
    251 {
    252     int print_version;
    253     u_int subtype;
    254 
    255     ndo->ndo_protocol = "slow";
    256     if (len < 1)
    257         goto tooshort;
    258     subtype = GET_U_1(pptr);
    259 
    260     /*
    261      * Sanity checking of the header.
    262      */
    263     switch (subtype) {
    264     case SLOW_PROTO_LACP:
    265         if (len < 2)
    266             goto tooshort;
    267         if (GET_U_1(pptr + 1) != LACP_VERSION) {
    268             ND_PRINT("LACP version %u packet not supported",
    269                      GET_U_1(pptr + 1));
    270             return;
    271         }
    272         print_version = 1;
    273         break;
    274 
    275     case SLOW_PROTO_MARKER:
    276         if (len < 2)
    277             goto tooshort;
    278         if (GET_U_1(pptr + 1) != MARKER_VERSION) {
    279             ND_PRINT("MARKER version %u packet not supported",
    280                      GET_U_1(pptr + 1));
    281             return;
    282         }
    283         print_version = 1;
    284         break;
    285 
    286     case SLOW_PROTO_OAM: /* fall through */
    287         print_version = 0;
    288         break;
    289 
    290     default:
    291         /* print basic information and exit */
    292         print_version = -1;
    293         break;
    294     }
    295 
    296     if (print_version == 1) {
    297         ND_PRINT("%sv%u, length %u",
    298                tok2str(slow_proto_values, "unknown (%u)", subtype),
    299                GET_U_1((pptr + 1)),
    300                len);
    301     } else {
    302         /* some slow protos don't have a version number in the header */
    303         ND_PRINT("%s, length %u",
    304                tok2str(slow_proto_values, "unknown (%u)", subtype),
    305                len);
    306     }
    307 
    308     /* unrecognized subtype */
    309     if (print_version == -1) {
    310         print_unknown_data(ndo, pptr, "\n\t", len);
    311         return;
    312     }
    313 
    314     if (!ndo->ndo_vflag)
    315         return;
    316 
    317     switch (subtype) {
    318     default: /* should not happen */
    319         break;
    320 
    321     case SLOW_PROTO_OAM:
    322         /* skip subtype */
    323         len -= 1;
    324         pptr += 1;
    325         slow_oam_print(ndo, pptr, len);
    326         break;
    327 
    328     case SLOW_PROTO_LACP:   /* LACP and MARKER share the same semantics */
    329     case SLOW_PROTO_MARKER:
    330         /* skip subtype and version */
    331         len -= 2;
    332         pptr += 2;
    333         slow_marker_lacp_print(ndo, pptr, len, subtype);
    334         break;
    335     }
    336     return;
    337 
    338 tooshort:
    339     if (!ndo->ndo_vflag)
    340         ND_PRINT(" (packet is too short)");
    341     else
    342         ND_PRINT("\n\t\t packet is too short");
    343 }
    344 
    345 static void
    346 slow_marker_lacp_print(netdissect_options *ndo,
    347                        const u_char *tptr, u_int tlen,
    348                        u_int proto_subtype)
    349 {
    350     const struct tlv_header_t *tlv_header;
    351     const u_char *tlv_tptr;
    352     u_int tlv_type, tlv_len, tlv_tlen;
    353 
    354     union {
    355         const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator;
    356         const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info;
    357         const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info;
    358         const struct marker_tlv_marker_info_t *marker_tlv_marker_info;
    359     } tlv_ptr;
    360 
    361     while(tlen>0) {
    362         /* is the packet big enough to include the tlv header ? */
    363         if (tlen < sizeof(struct tlv_header_t))
    364             goto tooshort;
    365         /* did we capture enough for fully decoding the tlv header ? */
    366         tlv_header = (const struct tlv_header_t *)tptr;
    367         tlv_type = GET_U_1(tlv_header->type);
    368         tlv_len = GET_U_1(tlv_header->length);
    369 
    370         ND_PRINT("\n\t%s TLV (0x%02x), length %u",
    371                tok2str(slow_tlv_values,
    372                        "Unknown",
    373                        (proto_subtype << 8) + tlv_type),
    374                tlv_type,
    375                tlv_len);
    376 
    377         if (tlv_type == LACP_MARKER_TLV_TERMINATOR) {
    378             /*
    379              * This TLV has a length of zero, and means there are no
    380              * more TLVs to process.
    381              */
    382             return;
    383         }
    384 
    385         /* length includes the type and length fields */
    386         if (tlv_len < sizeof(struct tlv_header_t)) {
    387             ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
    388                      sizeof(struct tlv_header_t));
    389             return;
    390         }
    391 
    392         /* is the packet big enough to include the tlv ? */
    393         if (tlen < tlv_len)
    394             goto tooshort;
    395         /* did we capture enough for fully decoding the tlv ? */
    396         ND_TCHECK_LEN(tptr, tlv_len);
    397 
    398         tlv_tptr=tptr+sizeof(struct tlv_header_t);
    399         tlv_tlen=tlv_len-sizeof(struct tlv_header_t);
    400 
    401         switch((proto_subtype << 8) + tlv_type) {
    402 
    403             /* those two TLVs have the same structure -> fall through */
    404         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO):
    405         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO):
    406             if (tlv_tlen !=
    407                 sizeof(struct lacp_tlv_actor_partner_info_t)) {
    408                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
    409                          sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t));
    410                 goto badlength;
    411             }
    412 
    413             tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr;
    414 
    415             ND_PRINT("\n\t  System %s, System Priority %u, Key %u"
    416                    ", Port %u, Port Priority %u\n\t  State Flags [%s]",
    417                    GET_ETHERADDR_STRING(tlv_ptr.lacp_tlv_actor_partner_info->sys),
    418                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri),
    419                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key),
    420                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port),
    421                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri),
    422                    bittok2str(lacp_tlv_actor_partner_info_state_values,
    423                               "none",
    424                               GET_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state)));
    425 
    426             break;
    427 
    428         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO):
    429             if (tlv_tlen !=
    430                 sizeof(struct lacp_tlv_collector_info_t)) {
    431                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
    432                          sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t));
    433                 goto badlength;
    434             }
    435 
    436             tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr;
    437 
    438             ND_PRINT("\n\t  Max Delay %u",
    439                    GET_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay));
    440 
    441             break;
    442 
    443         case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO):
    444             if (tlv_tlen !=
    445                 sizeof(struct marker_tlv_marker_info_t)) {
    446                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
    447                          sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t));
    448                 goto badlength;
    449             }
    450 
    451             tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr;
    452 
    453             ND_PRINT("\n\t  Request System %s, Request Port %u, Request Transaction ID 0x%08x",
    454                    GET_ETHERADDR_STRING(tlv_ptr.marker_tlv_marker_info->req_sys),
    455                    GET_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port),
    456                    GET_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id));
    457 
    458             break;
    459 
    460         default:
    461             if (ndo->ndo_vflag <= 1)
    462                 print_unknown_data(ndo, tlv_tptr, "\n\t  ", tlv_tlen);
    463             break;
    464         }
    465 
    466     badlength:
    467         /* do we want to see an additional hexdump ? */
    468         if (ndo->ndo_vflag > 1) {
    469             print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t  ",
    470                                tlv_len-sizeof(struct tlv_header_t));
    471         }
    472 
    473         tptr+=tlv_len;
    474         tlen-=tlv_len;
    475     }
    476     return;
    477 
    478 tooshort:
    479     ND_PRINT("\n\t\t packet is too short");
    480 }
    481 
    482 static void
    483 slow_oam_print(netdissect_options *ndo,
    484                const u_char *tptr, u_int tlen)
    485 {
    486     uint8_t code;
    487     uint8_t type, length;
    488     uint8_t state;
    489     uint8_t command;
    490     u_int hexdump;
    491 
    492     struct slow_oam_common_header_t {
    493         nd_uint16_t flags;
    494         nd_uint8_t code;
    495     };
    496 
    497     struct slow_oam_tlv_header_t {
    498         nd_uint8_t type;
    499         nd_uint8_t length;
    500     };
    501 
    502     union {
    503         const struct slow_oam_common_header_t *slow_oam_common_header;
    504         const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
    505     } ptr;
    506 
    507     union {
    508         const struct slow_oam_info_t *slow_oam_info;
    509         const struct slow_oam_link_event_t *slow_oam_link_event;
    510         const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
    511         const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
    512         const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
    513     } tlv;
    514 
    515     ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
    516     if (tlen < sizeof(*ptr.slow_oam_common_header))
    517         goto tooshort;
    518     ND_TCHECK_SIZE(ptr.slow_oam_common_header);
    519     tptr += sizeof(struct slow_oam_common_header_t);
    520     tlen -= sizeof(struct slow_oam_common_header_t);
    521 
    522     code = GET_U_1(ptr.slow_oam_common_header->code);
    523     ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
    524            tok2str(slow_oam_code_values, "Unknown (%u)", code),
    525            bittok2str(slow_oam_flag_values,
    526                       "none",
    527                       GET_BE_U_2(ptr.slow_oam_common_header->flags)));
    528 
    529     switch (code) {
    530     case SLOW_OAM_CODE_INFO:
    531         while (tlen > 0) {
    532             ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
    533             if (tlen < sizeof(*ptr.slow_oam_tlv_header))
    534                 goto tooshort;
    535             ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
    536             type = GET_U_1(ptr.slow_oam_tlv_header->type);
    537             length = GET_U_1(ptr.slow_oam_tlv_header->length);
    538             ND_PRINT("\n\t  %s Information Type (%u), length %u",
    539                    tok2str(slow_oam_info_type_values, "Reserved", type),
    540                    type,
    541                    length);
    542 
    543             if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
    544                 /*
    545                  * As IEEE Std 802.3-2015 says for the End of TLV Marker,
    546                  * "(the length and value of the Type 0x00 TLV can be ignored)".
    547                  */
    548                 return;
    549             }
    550 
    551             /* length includes the type and length fields */
    552             if (length < sizeof(struct slow_oam_tlv_header_t)) {
    553                 ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
    554                          sizeof(struct slow_oam_tlv_header_t));
    555                 return;
    556             }
    557 
    558             if (tlen < length)
    559                 goto tooshort;
    560             ND_TCHECK_LEN(tptr, length);
    561 
    562             hexdump = FALSE;
    563             switch (type) {
    564             case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
    565             case SLOW_OAM_INFO_TYPE_REMOTE:
    566                 tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
    567 
    568                 if (GET_U_1(tlv.slow_oam_info->info_length) !=
    569                     sizeof(struct slow_oam_info_t)) {
    570                     ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
    571                            sizeof(struct slow_oam_info_t));
    572                     hexdump = TRUE;
    573                     goto badlength_code_info;
    574                 }
    575 
    576                 ND_PRINT("\n\t    OAM-Version %u, Revision %u",
    577                        GET_U_1(tlv.slow_oam_info->oam_version),
    578                        GET_BE_U_2(tlv.slow_oam_info->revision));
    579 
    580                 state = GET_U_1(tlv.slow_oam_info->state);
    581                 ND_PRINT("\n\t    State-Parser-Action %s, State-MUX-Action %s",
    582                        tok2str(slow_oam_info_type_state_parser_values, "Reserved",
    583                                state & OAM_INFO_TYPE_PARSER_MASK),
    584                        tok2str(slow_oam_info_type_state_mux_values, "Reserved",
    585                                state & OAM_INFO_TYPE_MUX_MASK));
    586                 ND_PRINT("\n\t    OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
    587                        bittok2str(slow_oam_info_type_oam_config_values, "none",
    588                                   GET_U_1(tlv.slow_oam_info->oam_config)),
    589                        GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
    590                        OAM_INFO_TYPE_PDU_SIZE_MASK);
    591                 ND_PRINT("\n\t    OUI %s (0x%06x), Vendor-Private 0x%08x",
    592                        tok2str(oui_values, "Unknown",
    593                                GET_BE_U_3(tlv.slow_oam_info->oui)),
    594                        GET_BE_U_3(tlv.slow_oam_info->oui),
    595                        GET_BE_U_4(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                                    length);
    612             }
    613 
    614             tlen -= length;
    615             tptr += length;
    616         }
    617         break;
    618 
    619     case SLOW_OAM_CODE_EVENT_NOTIF:
    620         /* Sequence number */
    621         if (tlen < 2)
    622             goto tooshort;
    623         ND_PRINT("\n\t  Sequence Number %u", GET_BE_U_2(tptr));
    624         tlen -= 2;
    625         tptr += 2;
    626 
    627         /* TLVs */
    628         while (tlen > 0) {
    629             ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
    630             if (tlen < sizeof(*ptr.slow_oam_tlv_header))
    631                 goto tooshort;
    632             type = GET_U_1(ptr.slow_oam_tlv_header->type);
    633             length = GET_U_1(ptr.slow_oam_tlv_header->length);
    634             ND_PRINT("\n\t  %s Link Event Type (%u), length %u",
    635                    tok2str(slow_oam_link_event_values, "Reserved",
    636                            type),
    637                    type,
    638                    length);
    639 
    640             if (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 (length < sizeof(struct slow_oam_tlv_header_t)) {
    650                 ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
    651                          sizeof(struct slow_oam_tlv_header_t));
    652                 return;
    653             }
    654 
    655             if (tlen < length)
    656                 goto tooshort;
    657             ND_TCHECK_LEN(tptr, length);
    658 
    659             hexdump = FALSE;
    660             switch (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 (GET_U_1(tlv.slow_oam_link_event->event_length) !=
    668                     sizeof(struct slow_oam_link_event_t)) {
    669                     ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
    670                              sizeof(struct slow_oam_link_event_t));
    671                     hexdump = TRUE;
    672                     goto badlength_event_notif;
    673                 }
    674 
    675                 ND_PRINT("\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                        GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
    681                        GET_BE_U_8(tlv.slow_oam_link_event->window),
    682                        GET_BE_U_8(tlv.slow_oam_link_event->threshold),
    683                        GET_BE_U_8(tlv.slow_oam_link_event->errors),
    684                        GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
    685                        GET_BE_U_4(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                                    length);
    702             }
    703 
    704             tlen -= length;
    705             tptr += 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         command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
    714         ND_PRINT("\n\t  Command %s (%u)",
    715                tok2str(slow_oam_loopbackctrl_cmd_values,
    716                        "Unknown",
    717                        command),
    718                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 
    738 tooshort:
    739     ND_PRINT("\n\t\t packet is too short");
    740 }
    741