Home | History | Annotate | Line # | Download | only in dist
print-slow.c revision 1.1.1.7
      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 #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 
     42 static 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 
     49 static 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 
     67 static 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 
     77 struct 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 
     94 static 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
    103 static 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
    112 static const struct tok slow_oam_info_type_state_mux_values[] = {
    113     { 0x00, "forwarding" },
    114     { 0x04, "discarding" },
    115     { 0, NULL}
    116 };
    117 
    118 static 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 
    137 static 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 
    147 struct 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 
    158 struct slow_oam_variablerequest_t {
    159     uint8_t branch;
    160     uint8_t leaf[2];
    161 };
    162 
    163 struct slow_oam_variableresponse_t {
    164     uint8_t branch;
    165     uint8_t leaf[2];
    166     uint8_t length;
    167 };
    168 
    169 struct slow_oam_loopbackctrl_t {
    170     uint8_t command;
    171 };
    172 
    173 static 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 
    179 struct 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 
    192 static 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 
    203 struct 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 
    213 static 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 
    225 struct lacp_tlv_collector_info_t {
    226     uint8_t max_delay[2];
    227     uint8_t pad[12];
    228 };
    229 
    230 struct 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 
    237 struct lacp_marker_tlv_terminator_t {
    238     uint8_t pad[50];
    239 };
    240 
    241 static void slow_marker_lacp_print(netdissect_options *, register const u_char *, register u_int, u_int);
    242 static void slow_oam_print(netdissect_options *, register const u_char *, register u_int);
    243 
    244 void
    245 slow_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 
    334 tooshort:
    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 
    341 trunc:
    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 
    348 static void
    349 slow_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 
    481 tooshort:
    482     ND_PRINT((ndo, "\n\t\t packet is too short"));
    483     return;
    484 
    485 trunc:
    486     ND_PRINT((ndo, "\n\t\t packet exceeded snapshot"));
    487 }
    488 
    489 static void
    490 slow_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 
    738 tooshort:
    739     ND_PRINT((ndo, "\n\t\t packet is too short"));
    740     return;
    741 
    742 trunc:
    743     ND_PRINT((ndo, "\n\t\t packet exceeded snapshot"));
    744 }
    745