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