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