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