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