print-slow.c revision 1.11 1 /*
2 * Copyright (c) 1998-2006 The TCPDUMP project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * support for the IEEE "slow protocols" LACP, MARKER as per 802.3ad
16 * OAM as per 802.3ah
17 *
18 * Original code by Hannes Gredler (hannes (at) gredler.at)
19 */
20
21 #include <sys/cdefs.h>
22 #ifndef lint
23 __RCSID("$NetBSD: print-slow.c,v 1.11 2024/09/02 16:15:33 christos Exp $");
24 #endif
25
26 /* \summary: IEEE "slow protocols" (802.3ad/802.3ah) printer */
27
28 #include <config.h>
29
30 #include "netdissect-stdinc.h"
31
32 #define ND_LONGJMP_FROM_TCHECK
33 #include "netdissect.h"
34 #include "extract.h"
35 #include "addrtoname.h"
36 #include "oui.h"
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 nd_uint8_t info_type;
83 nd_uint8_t info_length;
84 nd_uint8_t oam_version;
85 nd_uint16_t revision;
86 nd_uint8_t state;
87 nd_uint8_t oam_config;
88 nd_uint16_t oam_pdu_config;
89 nd_uint24_t oui;
90 nd_uint32_t vendor_private;
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 nd_uint8_t event_type;
153 nd_uint8_t event_length;
154 nd_uint16_t time_stamp;
155 nd_uint64_t window;
156 nd_uint64_t threshold;
157 nd_uint64_t errors;
158 nd_uint64_t errors_running_total;
159 nd_uint32_t event_running_total;
160 };
161
162 struct slow_oam_variablerequest_t {
163 nd_uint8_t branch;
164 nd_uint16_t leaf;
165 };
166
167 struct slow_oam_variableresponse_t {
168 nd_uint8_t branch;
169 nd_uint16_t leaf;
170 nd_uint8_t length;
171 };
172
173 struct slow_oam_loopbackctrl_t {
174 nd_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 nd_uint8_t type;
185 nd_uint8_t length;
186 };
187
188 #define LACP_MARKER_TLV_TERMINATOR 0x00 /* same code for LACP and Marker */
189
190 #define LACP_TLV_ACTOR_INFO 0x01
191 #define LACP_TLV_PARTNER_INFO 0x02
192 #define LACP_TLV_COLLECTOR_INFO 0x03
193
194 #define MARKER_TLV_MARKER_INFO 0x01
195
196 static const struct tok slow_tlv_values[] = {
197 { (SLOW_PROTO_LACP << 8) + LACP_MARKER_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) + LACP_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 nd_uint16_t sys_pri;
209 nd_mac_addr sys;
210 nd_uint16_t key;
211 nd_uint16_t port_pri;
212 nd_uint16_t port;
213 nd_uint8_t state;
214 nd_byte 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 nd_uint16_t max_delay;
231 nd_byte pad[12];
232 };
233
234 struct marker_tlv_marker_info_t {
235 nd_uint16_t req_port;
236 nd_mac_addr req_sys;
237 nd_uint32_t req_trans_id;
238 nd_byte pad[2];
239 };
240
241 struct lacp_marker_tlv_terminator_t {
242 nd_byte pad[50];
243 };
244
245 static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
246 static void slow_oam_print(netdissect_options *, const u_char *, u_int);
247
248 void
249 slow_print(netdissect_options *ndo,
250 const u_char *pptr, u_int len)
251 {
252 int print_version;
253 u_int subtype;
254
255 ndo->ndo_protocol = "slow";
256 if (len < 1)
257 goto tooshort;
258 subtype = GET_U_1(pptr);
259
260 /*
261 * Sanity checking of the header.
262 */
263 switch (subtype) {
264 case SLOW_PROTO_LACP:
265 if (len < 2)
266 goto tooshort;
267 if (GET_U_1(pptr + 1) != LACP_VERSION) {
268 ND_PRINT("LACP version %u packet not supported",
269 GET_U_1(pptr + 1));
270 return;
271 }
272 print_version = 1;
273 break;
274
275 case SLOW_PROTO_MARKER:
276 if (len < 2)
277 goto tooshort;
278 if (GET_U_1(pptr + 1) != MARKER_VERSION) {
279 ND_PRINT("MARKER version %u packet not supported",
280 GET_U_1(pptr + 1));
281 return;
282 }
283 print_version = 1;
284 break;
285
286 case SLOW_PROTO_OAM: /* fall through */
287 print_version = 0;
288 break;
289
290 default:
291 /* print basic information and exit */
292 print_version = -1;
293 break;
294 }
295
296 if (print_version == 1) {
297 ND_PRINT("%sv%u, length %u",
298 tok2str(slow_proto_values, "unknown (%u)", subtype),
299 GET_U_1((pptr + 1)),
300 len);
301 } else {
302 /* some slow protos don't have a version number in the header */
303 ND_PRINT("%s, length %u",
304 tok2str(slow_proto_values, "unknown (%u)", subtype),
305 len);
306 }
307
308 /* unrecognized subtype */
309 if (print_version == -1) {
310 print_unknown_data(ndo, pptr, "\n\t", len);
311 return;
312 }
313
314 if (!ndo->ndo_vflag)
315 return;
316
317 switch (subtype) {
318 default: /* should not happen */
319 break;
320
321 case SLOW_PROTO_OAM:
322 /* skip subtype */
323 len -= 1;
324 pptr += 1;
325 slow_oam_print(ndo, pptr, len);
326 break;
327
328 case SLOW_PROTO_LACP: /* LACP and MARKER share the same semantics */
329 case SLOW_PROTO_MARKER:
330 /* skip subtype and version */
331 len -= 2;
332 pptr += 2;
333 slow_marker_lacp_print(ndo, pptr, len, subtype);
334 break;
335 }
336 return;
337
338 tooshort:
339 if (!ndo->ndo_vflag)
340 ND_PRINT(" (packet is too short)");
341 else
342 ND_PRINT("\n\t\t packet is too short");
343 }
344
345 static void
346 slow_marker_lacp_print(netdissect_options *ndo,
347 const u_char *tptr, u_int tlen,
348 u_int proto_subtype)
349 {
350 const struct tlv_header_t *tlv_header;
351 const u_char *tlv_tptr;
352 u_int tlv_type, tlv_len, tlv_tlen;
353
354 union {
355 const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator;
356 const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info;
357 const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info;
358 const struct marker_tlv_marker_info_t *marker_tlv_marker_info;
359 } tlv_ptr;
360
361 while(tlen>0) {
362 /* is the packet big enough to include the tlv header ? */
363 if (tlen < sizeof(struct tlv_header_t))
364 goto tooshort;
365 /* did we capture enough for fully decoding the tlv header ? */
366 tlv_header = (const struct tlv_header_t *)tptr;
367 tlv_type = GET_U_1(tlv_header->type);
368 tlv_len = GET_U_1(tlv_header->length);
369
370 ND_PRINT("\n\t%s TLV (0x%02x), length %u",
371 tok2str(slow_tlv_values,
372 "Unknown",
373 (proto_subtype << 8) + tlv_type),
374 tlv_type,
375 tlv_len);
376
377 if (tlv_type == LACP_MARKER_TLV_TERMINATOR) {
378 /*
379 * This TLV has a length of zero, and means there are no
380 * more TLVs to process.
381 */
382 return;
383 }
384
385 /* length includes the type and length fields */
386 if (tlv_len < sizeof(struct tlv_header_t)) {
387 ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
388 sizeof(struct tlv_header_t));
389 return;
390 }
391
392 /* is the packet big enough to include the tlv ? */
393 if (tlen < tlv_len)
394 goto tooshort;
395 /* did we capture enough for fully decoding the tlv ? */
396 ND_TCHECK_LEN(tptr, tlv_len);
397
398 tlv_tptr=tptr+sizeof(struct tlv_header_t);
399 tlv_tlen=tlv_len-sizeof(struct tlv_header_t);
400
401 switch((proto_subtype << 8) + tlv_type) {
402
403 /* those two TLVs have the same structure -> fall through */
404 case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO):
405 case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO):
406 if (tlv_tlen !=
407 sizeof(struct lacp_tlv_actor_partner_info_t)) {
408 ND_PRINT("\n\t ERROR: illegal length - should be %zu",
409 sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t));
410 goto badlength;
411 }
412
413 tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr;
414
415 ND_PRINT("\n\t System %s, System Priority %u, Key %u"
416 ", Port %u, Port Priority %u\n\t State Flags [%s]",
417 GET_ETHERADDR_STRING(tlv_ptr.lacp_tlv_actor_partner_info->sys),
418 GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri),
419 GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key),
420 GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port),
421 GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri),
422 bittok2str(lacp_tlv_actor_partner_info_state_values,
423 "none",
424 GET_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state)));
425
426 break;
427
428 case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO):
429 if (tlv_tlen !=
430 sizeof(struct lacp_tlv_collector_info_t)) {
431 ND_PRINT("\n\t ERROR: illegal length - should be %zu",
432 sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t));
433 goto badlength;
434 }
435
436 tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr;
437
438 ND_PRINT("\n\t Max Delay %u",
439 GET_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay));
440
441 break;
442
443 case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO):
444 if (tlv_tlen !=
445 sizeof(struct marker_tlv_marker_info_t)) {
446 ND_PRINT("\n\t ERROR: illegal length - should be %zu",
447 sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t));
448 goto badlength;
449 }
450
451 tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr;
452
453 ND_PRINT("\n\t Request System %s, Request Port %u, Request Transaction ID 0x%08x",
454 GET_ETHERADDR_STRING(tlv_ptr.marker_tlv_marker_info->req_sys),
455 GET_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port),
456 GET_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id));
457
458 break;
459
460 default:
461 if (ndo->ndo_vflag <= 1)
462 print_unknown_data(ndo, tlv_tptr, "\n\t ", tlv_tlen);
463 break;
464 }
465
466 badlength:
467 /* do we want to see an additional hexdump ? */
468 if (ndo->ndo_vflag > 1) {
469 print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t ",
470 tlv_len-sizeof(struct tlv_header_t));
471 }
472
473 tptr+=tlv_len;
474 tlen-=tlv_len;
475 }
476 return;
477
478 tooshort:
479 ND_PRINT("\n\t\t packet is too short");
480 }
481
482 static void
483 slow_oam_print(netdissect_options *ndo,
484 const u_char *tptr, u_int tlen)
485 {
486 uint8_t code;
487 uint8_t type, length;
488 uint8_t state;
489 uint8_t command;
490 u_int hexdump;
491
492 struct slow_oam_common_header_t {
493 nd_uint16_t flags;
494 nd_uint8_t code;
495 };
496
497 struct slow_oam_tlv_header_t {
498 nd_uint8_t type;
499 nd_uint8_t length;
500 };
501
502 union {
503 const struct slow_oam_common_header_t *slow_oam_common_header;
504 const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
505 } ptr;
506
507 union {
508 const struct slow_oam_info_t *slow_oam_info;
509 const struct slow_oam_link_event_t *slow_oam_link_event;
510 const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
511 const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
512 const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
513 } tlv;
514
515 ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
516 if (tlen < sizeof(*ptr.slow_oam_common_header))
517 goto tooshort;
518 ND_TCHECK_SIZE(ptr.slow_oam_common_header);
519 tptr += sizeof(struct slow_oam_common_header_t);
520 tlen -= sizeof(struct slow_oam_common_header_t);
521
522 code = GET_U_1(ptr.slow_oam_common_header->code);
523 ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
524 tok2str(slow_oam_code_values, "Unknown (%u)", code),
525 bittok2str(slow_oam_flag_values,
526 "none",
527 GET_BE_U_2(ptr.slow_oam_common_header->flags)));
528
529 switch (code) {
530 case SLOW_OAM_CODE_INFO:
531 while (tlen > 0) {
532 ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
533 if (tlen < sizeof(*ptr.slow_oam_tlv_header))
534 goto tooshort;
535 ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
536 type = GET_U_1(ptr.slow_oam_tlv_header->type);
537 length = GET_U_1(ptr.slow_oam_tlv_header->length);
538 ND_PRINT("\n\t %s Information Type (%u), length %u",
539 tok2str(slow_oam_info_type_values, "Reserved", type),
540 type,
541 length);
542
543 if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
544 /*
545 * As IEEE Std 802.3-2015 says for the End of TLV Marker,
546 * "(the length and value of the Type 0x00 TLV can be ignored)".
547 */
548 return;
549 }
550
551 /* length includes the type and length fields */
552 if (length < sizeof(struct slow_oam_tlv_header_t)) {
553 ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
554 sizeof(struct slow_oam_tlv_header_t));
555 return;
556 }
557
558 if (tlen < length)
559 goto tooshort;
560 ND_TCHECK_LEN(tptr, length);
561
562 hexdump = FALSE;
563 switch (type) {
564 case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
565 case SLOW_OAM_INFO_TYPE_REMOTE:
566 tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
567
568 if (GET_U_1(tlv.slow_oam_info->info_length) !=
569 sizeof(struct slow_oam_info_t)) {
570 ND_PRINT("\n\t ERROR: illegal length - should be %zu",
571 sizeof(struct slow_oam_info_t));
572 hexdump = TRUE;
573 goto badlength_code_info;
574 }
575
576 ND_PRINT("\n\t OAM-Version %u, Revision %u",
577 GET_U_1(tlv.slow_oam_info->oam_version),
578 GET_BE_U_2(tlv.slow_oam_info->revision));
579
580 state = GET_U_1(tlv.slow_oam_info->state);
581 ND_PRINT("\n\t State-Parser-Action %s, State-MUX-Action %s",
582 tok2str(slow_oam_info_type_state_parser_values, "Reserved",
583 state & OAM_INFO_TYPE_PARSER_MASK),
584 tok2str(slow_oam_info_type_state_mux_values, "Reserved",
585 state & OAM_INFO_TYPE_MUX_MASK));
586 ND_PRINT("\n\t OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
587 bittok2str(slow_oam_info_type_oam_config_values, "none",
588 GET_U_1(tlv.slow_oam_info->oam_config)),
589 GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
590 OAM_INFO_TYPE_PDU_SIZE_MASK);
591 ND_PRINT("\n\t OUI %s (0x%06x), Vendor-Private 0x%08x",
592 tok2str(oui_values, "Unknown",
593 GET_BE_U_3(tlv.slow_oam_info->oui)),
594 GET_BE_U_3(tlv.slow_oam_info->oui),
595 GET_BE_U_4(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 length);
612 }
613
614 tlen -= length;
615 tptr += length;
616 }
617 break;
618
619 case SLOW_OAM_CODE_EVENT_NOTIF:
620 /* Sequence number */
621 if (tlen < 2)
622 goto tooshort;
623 ND_PRINT("\n\t Sequence Number %u", GET_BE_U_2(tptr));
624 tlen -= 2;
625 tptr += 2;
626
627 /* TLVs */
628 while (tlen > 0) {
629 ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
630 if (tlen < sizeof(*ptr.slow_oam_tlv_header))
631 goto tooshort;
632 type = GET_U_1(ptr.slow_oam_tlv_header->type);
633 length = GET_U_1(ptr.slow_oam_tlv_header->length);
634 ND_PRINT("\n\t %s Link Event Type (%u), length %u",
635 tok2str(slow_oam_link_event_values, "Reserved",
636 type),
637 type,
638 length);
639
640 if (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 (length < sizeof(struct slow_oam_tlv_header_t)) {
650 ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
651 sizeof(struct slow_oam_tlv_header_t));
652 return;
653 }
654
655 if (tlen < length)
656 goto tooshort;
657 ND_TCHECK_LEN(tptr, length);
658
659 hexdump = FALSE;
660 switch (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 (GET_U_1(tlv.slow_oam_link_event->event_length) !=
668 sizeof(struct slow_oam_link_event_t)) {
669 ND_PRINT("\n\t ERROR: illegal length - should be %zu",
670 sizeof(struct slow_oam_link_event_t));
671 hexdump = TRUE;
672 goto badlength_event_notif;
673 }
674
675 ND_PRINT("\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 GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
681 GET_BE_U_8(tlv.slow_oam_link_event->window),
682 GET_BE_U_8(tlv.slow_oam_link_event->threshold),
683 GET_BE_U_8(tlv.slow_oam_link_event->errors),
684 GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
685 GET_BE_U_4(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 length);
702 }
703
704 tlen -= length;
705 tptr += 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 command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
714 ND_PRINT("\n\t Command %s (%u)",
715 tok2str(slow_oam_loopbackctrl_cmd_values,
716 "Unknown",
717 command),
718 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("\n\t\t packet is too short");
740 }
741