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