xref: /dragonfly/contrib/tcpdump/print-slow.c (revision 2b7dbe20)
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 /* \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