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