xref: /netbsd-src/external/bsd/tcpdump/dist/print-slow.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
10f74e101Schristos /*
20f74e101Schristos  * Copyright (c) 1998-2006 The TCPDUMP project
30f74e101Schristos  *
40f74e101Schristos  * Redistribution and use in source and binary forms, with or without
50f74e101Schristos  * modification, are permitted provided that: (1) source code
60f74e101Schristos  * distributions retain the above copyright notice and this paragraph
70f74e101Schristos  * in its entirety, and (2) distributions including binary code include
80f74e101Schristos  * the above copyright notice and this paragraph in its entirety in
90f74e101Schristos  * the documentation or other materials provided with the distribution.
100f74e101Schristos  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
110f74e101Schristos  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
120f74e101Schristos  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
130f74e101Schristos  * FOR A PARTICULAR PURPOSE.
140f74e101Schristos  *
150f74e101Schristos  * support for the IEEE "slow protocols" LACP, MARKER as per 802.3ad
160f74e101Schristos  *                                       OAM as per 802.3ah
170f74e101Schristos  *
1872c96ff3Schristos  * Original code by Hannes Gredler (hannes@gredler.at)
190f74e101Schristos  */
200f74e101Schristos 
2111b3aaa1Schristos #include <sys/cdefs.h>
220f74e101Schristos #ifndef lint
23*26ba0b50Schristos __RCSID("$NetBSD: print-slow.c,v 1.11 2024/09/02 16:15:33 christos Exp $");
240f74e101Schristos #endif
250f74e101Schristos 
26dc860a36Sspz /* \summary: IEEE "slow protocols" (802.3ad/802.3ah) printer */
27dc860a36Sspz 
28c74ad251Schristos #include <config.h>
290f74e101Schristos 
30c74ad251Schristos #include "netdissect-stdinc.h"
310f74e101Schristos 
32c74ad251Schristos #define ND_LONGJMP_FROM_TCHECK
33fdccd7e4Schristos #include "netdissect.h"
340f74e101Schristos #include "extract.h"
350f74e101Schristos #include "addrtoname.h"
360f74e101Schristos #include "oui.h"
370f74e101Schristos 
38c74ad251Schristos 
390f74e101Schristos #define	SLOW_PROTO_LACP                     1
400f74e101Schristos #define	SLOW_PROTO_MARKER                   2
410f74e101Schristos #define SLOW_PROTO_OAM                      3
420f74e101Schristos 
430f74e101Schristos #define	LACP_VERSION                        1
440f74e101Schristos #define	MARKER_VERSION                      1
450f74e101Schristos 
460f74e101Schristos static const struct tok slow_proto_values[] = {
470f74e101Schristos     { SLOW_PROTO_LACP, "LACP" },
480f74e101Schristos     { SLOW_PROTO_MARKER, "MARKER" },
490f74e101Schristos     { SLOW_PROTO_OAM, "OAM" },
500f74e101Schristos     { 0, NULL}
510f74e101Schristos };
520f74e101Schristos 
530f74e101Schristos static const struct tok slow_oam_flag_values[] = {
540f74e101Schristos     { 0x0001, "Link Fault" },
550f74e101Schristos     { 0x0002, "Dying Gasp" },
560f74e101Schristos     { 0x0004, "Critical Event" },
570f74e101Schristos     { 0x0008, "Local Evaluating" },
580f74e101Schristos     { 0x0010, "Local Stable" },
590f74e101Schristos     { 0x0020, "Remote Evaluating" },
600f74e101Schristos     { 0x0040, "Remote Stable" },
610f74e101Schristos     { 0, NULL}
620f74e101Schristos };
630f74e101Schristos 
640f74e101Schristos #define SLOW_OAM_CODE_INFO          0x00
650f74e101Schristos #define SLOW_OAM_CODE_EVENT_NOTIF   0x01
660f74e101Schristos #define SLOW_OAM_CODE_VAR_REQUEST   0x02
670f74e101Schristos #define SLOW_OAM_CODE_VAR_RESPONSE  0x03
680f74e101Schristos #define SLOW_OAM_CODE_LOOPBACK_CTRL 0x04
690f74e101Schristos #define SLOW_OAM_CODE_PRIVATE       0xfe
700f74e101Schristos 
710f74e101Schristos static const struct tok slow_oam_code_values[] = {
720f74e101Schristos     { SLOW_OAM_CODE_INFO, "Information" },
730f74e101Schristos     { SLOW_OAM_CODE_EVENT_NOTIF, "Event Notification" },
740f74e101Schristos     { SLOW_OAM_CODE_VAR_REQUEST, "Variable Request" },
750f74e101Schristos     { SLOW_OAM_CODE_VAR_RESPONSE, "Variable Response" },
760f74e101Schristos     { SLOW_OAM_CODE_LOOPBACK_CTRL, "Loopback Control" },
770f74e101Schristos     { SLOW_OAM_CODE_PRIVATE, "Vendor Private" },
780f74e101Schristos     { 0, NULL}
790f74e101Schristos };
800f74e101Schristos 
810f74e101Schristos struct slow_oam_info_t {
82c74ad251Schristos     nd_uint8_t info_type;
83c74ad251Schristos     nd_uint8_t info_length;
84c74ad251Schristos     nd_uint8_t oam_version;
85c74ad251Schristos     nd_uint16_t revision;
86c74ad251Schristos     nd_uint8_t state;
87c74ad251Schristos     nd_uint8_t oam_config;
88c74ad251Schristos     nd_uint16_t oam_pdu_config;
89c74ad251Schristos     nd_uint24_t oui;
90c74ad251Schristos     nd_uint32_t vendor_private;
910f74e101Schristos };
920f74e101Schristos 
930f74e101Schristos #define SLOW_OAM_INFO_TYPE_END_OF_TLV 0x00
940f74e101Schristos #define SLOW_OAM_INFO_TYPE_LOCAL 0x01
950f74e101Schristos #define SLOW_OAM_INFO_TYPE_REMOTE 0x02
960f74e101Schristos #define SLOW_OAM_INFO_TYPE_ORG_SPECIFIC 0xfe
970f74e101Schristos 
980f74e101Schristos static const struct tok slow_oam_info_type_values[] = {
990f74e101Schristos     { SLOW_OAM_INFO_TYPE_END_OF_TLV, "End of TLV marker" },
1000f74e101Schristos     { SLOW_OAM_INFO_TYPE_LOCAL, "Local" },
1010f74e101Schristos     { SLOW_OAM_INFO_TYPE_REMOTE, "Remote" },
1020f74e101Schristos     { SLOW_OAM_INFO_TYPE_ORG_SPECIFIC, "Organization specific" },
1030f74e101Schristos     { 0, NULL}
1040f74e101Schristos };
1050f74e101Schristos 
1060f74e101Schristos #define OAM_INFO_TYPE_PARSER_MASK 0x3
1070f74e101Schristos static const struct tok slow_oam_info_type_state_parser_values[] = {
1080f74e101Schristos     { 0x00, "forwarding" },
1090f74e101Schristos     { 0x01, "looping back" },
1100f74e101Schristos     { 0x02, "discarding" },
1110f74e101Schristos     { 0x03, "reserved" },
1120f74e101Schristos     { 0, NULL}
1130f74e101Schristos };
1140f74e101Schristos 
1150f74e101Schristos #define OAM_INFO_TYPE_MUX_MASK 0x4
1160f74e101Schristos static const struct tok slow_oam_info_type_state_mux_values[] = {
1170f74e101Schristos     { 0x00, "forwarding" },
1180f74e101Schristos     { 0x04, "discarding" },
1190f74e101Schristos     { 0, NULL}
1200f74e101Schristos };
1210f74e101Schristos 
1220f74e101Schristos static const struct tok slow_oam_info_type_oam_config_values[] = {
1230f74e101Schristos     { 0x01, "Active" },
1240f74e101Schristos     { 0x02, "Unidirectional" },
1250f74e101Schristos     { 0x04, "Remote-Loopback" },
1260f74e101Schristos     { 0x08, "Link-Events" },
1270f74e101Schristos     { 0x10, "Variable-Retrieval" },
1280f74e101Schristos     { 0, NULL}
1290f74e101Schristos };
1300f74e101Schristos 
1310f74e101Schristos /* 11 Bits */
1320f74e101Schristos #define OAM_INFO_TYPE_PDU_SIZE_MASK 0x7ff
1330f74e101Schristos 
1340f74e101Schristos #define SLOW_OAM_LINK_EVENT_END_OF_TLV 0x00
1350f74e101Schristos #define SLOW_OAM_LINK_EVENT_ERR_SYM_PER 0x01
1360f74e101Schristos #define SLOW_OAM_LINK_EVENT_ERR_FRM 0x02
1370f74e101Schristos #define SLOW_OAM_LINK_EVENT_ERR_FRM_PER 0x03
1380f74e101Schristos #define SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM 0x04
1390f74e101Schristos #define SLOW_OAM_LINK_EVENT_ORG_SPECIFIC 0xfe
1400f74e101Schristos 
1410f74e101Schristos static const struct tok slow_oam_link_event_values[] = {
1420f74e101Schristos     { SLOW_OAM_LINK_EVENT_END_OF_TLV, "End of TLV marker" },
1430f74e101Schristos     { SLOW_OAM_LINK_EVENT_ERR_SYM_PER, "Errored Symbol Period Event" },
1440f74e101Schristos     { SLOW_OAM_LINK_EVENT_ERR_FRM, "Errored Frame Event" },
1450f74e101Schristos     { SLOW_OAM_LINK_EVENT_ERR_FRM_PER, "Errored Frame Period Event" },
1460f74e101Schristos     { SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM, "Errored Frame Seconds Summary Event" },
1470f74e101Schristos     { SLOW_OAM_LINK_EVENT_ORG_SPECIFIC, "Organization specific" },
1480f74e101Schristos     { 0, NULL}
1490f74e101Schristos };
1500f74e101Schristos 
1510f74e101Schristos struct slow_oam_link_event_t {
152c74ad251Schristos     nd_uint8_t event_type;
153c74ad251Schristos     nd_uint8_t event_length;
154c74ad251Schristos     nd_uint16_t time_stamp;
155c74ad251Schristos     nd_uint64_t window;
156c74ad251Schristos     nd_uint64_t threshold;
157c74ad251Schristos     nd_uint64_t errors;
158c74ad251Schristos     nd_uint64_t errors_running_total;
159c74ad251Schristos     nd_uint32_t event_running_total;
1600f74e101Schristos };
1610f74e101Schristos 
1620f74e101Schristos struct slow_oam_variablerequest_t {
163c74ad251Schristos     nd_uint8_t branch;
164c74ad251Schristos     nd_uint16_t leaf;
1650f74e101Schristos };
1660f74e101Schristos 
1670f74e101Schristos struct slow_oam_variableresponse_t {
168c74ad251Schristos     nd_uint8_t branch;
169c74ad251Schristos     nd_uint16_t leaf;
170c74ad251Schristos     nd_uint8_t length;
1710f74e101Schristos };
1720f74e101Schristos 
1730f74e101Schristos struct slow_oam_loopbackctrl_t {
174c74ad251Schristos     nd_uint8_t command;
1750f74e101Schristos };
1760f74e101Schristos 
1770f74e101Schristos static const struct tok slow_oam_loopbackctrl_cmd_values[] = {
1780f74e101Schristos     { 0x01, "Enable OAM Remote Loopback" },
1790f74e101Schristos     { 0x02, "Disable OAM Remote Loopback" },
1800f74e101Schristos     { 0, NULL}
1810f74e101Schristos };
1820f74e101Schristos 
1830f74e101Schristos struct tlv_header_t {
184c74ad251Schristos     nd_uint8_t type;
185c74ad251Schristos     nd_uint8_t length;
1860f74e101Schristos };
1870f74e101Schristos 
188dc860a36Sspz #define LACP_MARKER_TLV_TERMINATOR     0x00  /* same code for LACP and Marker */
189dc860a36Sspz 
1900f74e101Schristos #define LACP_TLV_ACTOR_INFO            0x01
1910f74e101Schristos #define LACP_TLV_PARTNER_INFO          0x02
1920f74e101Schristos #define LACP_TLV_COLLECTOR_INFO        0x03
1930f74e101Schristos 
1940f74e101Schristos #define MARKER_TLV_MARKER_INFO         0x01
1950f74e101Schristos 
1960f74e101Schristos static const struct tok slow_tlv_values[] = {
197dc860a36Sspz     { (SLOW_PROTO_LACP << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
1980f74e101Schristos     { (SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO, "Actor Information"},
1990f74e101Schristos     { (SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO, "Partner Information"},
2000f74e101Schristos     { (SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO, "Collector Information"},
2010f74e101Schristos 
202dc860a36Sspz     { (SLOW_PROTO_MARKER << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
2030f74e101Schristos     { (SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO, "Marker Information"},
2040f74e101Schristos     { 0, NULL}
2050f74e101Schristos };
2060f74e101Schristos 
2070f74e101Schristos struct lacp_tlv_actor_partner_info_t {
208c74ad251Schristos     nd_uint16_t sys_pri;
209c74ad251Schristos     nd_mac_addr sys;
210c74ad251Schristos     nd_uint16_t key;
211c74ad251Schristos     nd_uint16_t port_pri;
212c74ad251Schristos     nd_uint16_t port;
213c74ad251Schristos     nd_uint8_t state;
214c74ad251Schristos     nd_byte    pad[3];
2150f74e101Schristos };
2160f74e101Schristos 
2170f74e101Schristos static const struct tok lacp_tlv_actor_partner_info_state_values[] = {
2180f74e101Schristos     { 0x01, "Activity"},
2190f74e101Schristos     { 0x02, "Timeout"},
2200f74e101Schristos     { 0x04, "Aggregation"},
2210f74e101Schristos     { 0x08, "Synchronization"},
2220f74e101Schristos     { 0x10, "Collecting"},
2230f74e101Schristos     { 0x20, "Distributing"},
2240f74e101Schristos     { 0x40, "Default"},
2250f74e101Schristos     { 0x80, "Expired"},
2260f74e101Schristos     { 0, NULL}
2270f74e101Schristos };
2280f74e101Schristos 
2290f74e101Schristos struct lacp_tlv_collector_info_t {
230c74ad251Schristos     nd_uint16_t max_delay;
231c74ad251Schristos     nd_byte     pad[12];
2320f74e101Schristos };
2330f74e101Schristos 
2340f74e101Schristos struct marker_tlv_marker_info_t {
235c74ad251Schristos     nd_uint16_t req_port;
236c74ad251Schristos     nd_mac_addr req_sys;
237c74ad251Schristos     nd_uint32_t req_trans_id;
238c74ad251Schristos     nd_byte     pad[2];
2390f74e101Schristos };
2400f74e101Schristos 
2410f74e101Schristos struct lacp_marker_tlv_terminator_t {
242c74ad251Schristos     nd_byte     pad[50];
2430f74e101Schristos };
2440f74e101Schristos 
245c74ad251Schristos static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
246c74ad251Schristos static void slow_oam_print(netdissect_options *, const u_char *, u_int);
2470f74e101Schristos 
2480f74e101Schristos void
249b3a00663Schristos slow_print(netdissect_options *ndo,
250c74ad251Schristos            const u_char *pptr, u_int len)
251ba2ff121Schristos {
2520f74e101Schristos     int print_version;
253dc860a36Sspz     u_int subtype;
2540f74e101Schristos 
255c74ad251Schristos     ndo->ndo_protocol = "slow";
256dc860a36Sspz     if (len < 1)
257dc860a36Sspz         goto tooshort;
258c74ad251Schristos     subtype = GET_U_1(pptr);
2590f74e101Schristos 
2600f74e101Schristos     /*
2610f74e101Schristos      * Sanity checking of the header.
2620f74e101Schristos      */
263dc860a36Sspz     switch (subtype) {
2640f74e101Schristos     case SLOW_PROTO_LACP:
265dc860a36Sspz         if (len < 2)
266dc860a36Sspz             goto tooshort;
267c74ad251Schristos         if (GET_U_1(pptr + 1) != LACP_VERSION) {
268c74ad251Schristos             ND_PRINT("LACP version %u packet not supported",
269c74ad251Schristos                      GET_U_1(pptr + 1));
2700f74e101Schristos             return;
2710f74e101Schristos         }
2720f74e101Schristos         print_version = 1;
2730f74e101Schristos         break;
2740f74e101Schristos 
2750f74e101Schristos     case SLOW_PROTO_MARKER:
276dc860a36Sspz         if (len < 2)
277dc860a36Sspz             goto tooshort;
278c74ad251Schristos         if (GET_U_1(pptr + 1) != MARKER_VERSION) {
279c74ad251Schristos             ND_PRINT("MARKER version %u packet not supported",
280c74ad251Schristos                      GET_U_1(pptr + 1));
2810f74e101Schristos             return;
2820f74e101Schristos         }
2830f74e101Schristos         print_version = 1;
2840f74e101Schristos         break;
2850f74e101Schristos 
2860f74e101Schristos     case SLOW_PROTO_OAM: /* fall through */
2870f74e101Schristos         print_version = 0;
2880f74e101Schristos         break;
2890f74e101Schristos 
2900f74e101Schristos     default:
2910f74e101Schristos         /* print basic information and exit */
2920f74e101Schristos         print_version = -1;
2930f74e101Schristos         break;
2940f74e101Schristos     }
2950f74e101Schristos 
296dc860a36Sspz     if (print_version == 1) {
297c74ad251Schristos         ND_PRINT("%sv%u, length %u",
298dc860a36Sspz                tok2str(slow_proto_values, "unknown (%u)", subtype),
299c74ad251Schristos                GET_U_1((pptr + 1)),
300c74ad251Schristos                len);
3010f74e101Schristos     } else {
3020f74e101Schristos         /* some slow protos don't have a version number in the header */
303c74ad251Schristos         ND_PRINT("%s, length %u",
304dc860a36Sspz                tok2str(slow_proto_values, "unknown (%u)", subtype),
305c74ad251Schristos                len);
3060f74e101Schristos     }
3070f74e101Schristos 
3080f74e101Schristos     /* unrecognized subtype */
3090f74e101Schristos     if (print_version == -1) {
310b3a00663Schristos         print_unknown_data(ndo, pptr, "\n\t", len);
3110f74e101Schristos         return;
3120f74e101Schristos     }
3130f74e101Schristos 
314b3a00663Schristos     if (!ndo->ndo_vflag)
3150f74e101Schristos         return;
3160f74e101Schristos 
317dc860a36Sspz     switch (subtype) {
3180f74e101Schristos     default: /* should not happen */
3190f74e101Schristos         break;
3200f74e101Schristos 
3210f74e101Schristos     case SLOW_PROTO_OAM:
322dc860a36Sspz         /* skip subtype */
323dc860a36Sspz         len -= 1;
324dc860a36Sspz         pptr += 1;
325dc860a36Sspz         slow_oam_print(ndo, pptr, len);
3260f74e101Schristos         break;
3270f74e101Schristos 
3280f74e101Schristos     case SLOW_PROTO_LACP:   /* LACP and MARKER share the same semantics */
3290f74e101Schristos     case SLOW_PROTO_MARKER:
330dc860a36Sspz         /* skip subtype and version */
331dc860a36Sspz         len -= 2;
332dc860a36Sspz         pptr += 2;
333dc860a36Sspz         slow_marker_lacp_print(ndo, pptr, len, subtype);
3340f74e101Schristos         break;
3350f74e101Schristos     }
3360f74e101Schristos     return;
3370f74e101Schristos 
338dc860a36Sspz tooshort:
339dc860a36Sspz     if (!ndo->ndo_vflag)
340c74ad251Schristos         ND_PRINT(" (packet is too short)");
341dc860a36Sspz     else
342c74ad251Schristos         ND_PRINT("\n\t\t packet is too short");
3430f74e101Schristos }
3440f74e101Schristos 
345b3a00663Schristos static void
346b3a00663Schristos slow_marker_lacp_print(netdissect_options *ndo,
347c74ad251Schristos                        const u_char *tptr, u_int tlen,
348dc860a36Sspz                        u_int proto_subtype)
349ba2ff121Schristos {
3500f74e101Schristos     const struct tlv_header_t *tlv_header;
3510f74e101Schristos     const u_char *tlv_tptr;
352c74ad251Schristos     u_int tlv_type, tlv_len, tlv_tlen;
3530f74e101Schristos 
3540f74e101Schristos     union {
3550f74e101Schristos         const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator;
3560f74e101Schristos         const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info;
3570f74e101Schristos         const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info;
3580f74e101Schristos         const struct marker_tlv_marker_info_t *marker_tlv_marker_info;
3590f74e101Schristos     } tlv_ptr;
3600f74e101Schristos 
3610f74e101Schristos     while(tlen>0) {
362dc860a36Sspz         /* is the packet big enough to include the tlv header ? */
363dc860a36Sspz         if (tlen < sizeof(struct tlv_header_t))
364dc860a36Sspz             goto tooshort;
3650f74e101Schristos         /* did we capture enough for fully decoding the tlv header ? */
3660f74e101Schristos         tlv_header = (const struct tlv_header_t *)tptr;
367c74ad251Schristos         tlv_type = GET_U_1(tlv_header->type);
368c74ad251Schristos         tlv_len = GET_U_1(tlv_header->length);
3690f74e101Schristos 
370c74ad251Schristos         ND_PRINT("\n\t%s TLV (0x%02x), length %u",
3710f74e101Schristos                tok2str(slow_tlv_values,
3720f74e101Schristos                        "Unknown",
373c74ad251Schristos                        (proto_subtype << 8) + tlv_type),
374c74ad251Schristos                tlv_type,
375c74ad251Schristos                tlv_len);
3760f74e101Schristos 
377c74ad251Schristos         if (tlv_type == LACP_MARKER_TLV_TERMINATOR) {
378dc860a36Sspz             /*
379dc860a36Sspz              * This TLV has a length of zero, and means there are no
380dc860a36Sspz              * more TLVs to process.
381dc860a36Sspz              */
3820f74e101Schristos             return;
3830f74e101Schristos         }
3840f74e101Schristos 
385dc860a36Sspz         /* length includes the type and length fields */
386dc860a36Sspz         if (tlv_len < sizeof(struct tlv_header_t)) {
387c74ad251Schristos             ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
388c74ad251Schristos                      sizeof(struct tlv_header_t));
389dc860a36Sspz             return;
390dc860a36Sspz         }
391dc860a36Sspz 
392dc860a36Sspz         /* is the packet big enough to include the tlv ? */
393dc860a36Sspz         if (tlen < tlv_len)
394dc860a36Sspz             goto tooshort;
395dc860a36Sspz         /* did we capture enough for fully decoding the tlv ? */
396c74ad251Schristos         ND_TCHECK_LEN(tptr, tlv_len);
397dc860a36Sspz 
3980f74e101Schristos         tlv_tptr=tptr+sizeof(struct tlv_header_t);
3990f74e101Schristos         tlv_tlen=tlv_len-sizeof(struct tlv_header_t);
4000f74e101Schristos 
401c74ad251Schristos         switch((proto_subtype << 8) + tlv_type) {
4020f74e101Schristos 
4030f74e101Schristos             /* those two TLVs have the same structure -> fall through */
4040f74e101Schristos         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO):
4050f74e101Schristos         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO):
406dc860a36Sspz             if (tlv_tlen !=
407dc860a36Sspz                 sizeof(struct lacp_tlv_actor_partner_info_t)) {
408c74ad251Schristos                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
409c74ad251Schristos                          sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t));
410dc860a36Sspz                 goto badlength;
411dc860a36Sspz             }
412dc860a36Sspz 
4130f74e101Schristos             tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr;
4140f74e101Schristos 
415c74ad251Schristos             ND_PRINT("\n\t  System %s, System Priority %u, Key %u"
4160f74e101Schristos                    ", Port %u, Port Priority %u\n\t  State Flags [%s]",
417c74ad251Schristos                    GET_ETHERADDR_STRING(tlv_ptr.lacp_tlv_actor_partner_info->sys),
418c74ad251Schristos                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri),
419c74ad251Schristos                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key),
420c74ad251Schristos                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port),
421c74ad251Schristos                    GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri),
4220f74e101Schristos                    bittok2str(lacp_tlv_actor_partner_info_state_values,
4230f74e101Schristos                               "none",
424c74ad251Schristos                               GET_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state)));
4250f74e101Schristos 
4260f74e101Schristos             break;
4270f74e101Schristos 
4280f74e101Schristos         case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO):
429dc860a36Sspz             if (tlv_tlen !=
430dc860a36Sspz                 sizeof(struct lacp_tlv_collector_info_t)) {
431c74ad251Schristos                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
432c74ad251Schristos                          sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t));
433dc860a36Sspz                 goto badlength;
434dc860a36Sspz             }
435dc860a36Sspz 
4360f74e101Schristos             tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr;
4370f74e101Schristos 
438c74ad251Schristos             ND_PRINT("\n\t  Max Delay %u",
439c74ad251Schristos                    GET_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay));
4400f74e101Schristos 
4410f74e101Schristos             break;
4420f74e101Schristos 
4430f74e101Schristos         case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO):
444dc860a36Sspz             if (tlv_tlen !=
445dc860a36Sspz                 sizeof(struct marker_tlv_marker_info_t)) {
446c74ad251Schristos                 ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
447c74ad251Schristos                          sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t));
448dc860a36Sspz                 goto badlength;
449dc860a36Sspz             }
450dc860a36Sspz 
4510f74e101Schristos             tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr;
4520f74e101Schristos 
453c74ad251Schristos             ND_PRINT("\n\t  Request System %s, Request Port %u, Request Transaction ID 0x%08x",
454c74ad251Schristos                    GET_ETHERADDR_STRING(tlv_ptr.marker_tlv_marker_info->req_sys),
455c74ad251Schristos                    GET_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port),
456c74ad251Schristos                    GET_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id));
4570f74e101Schristos 
4580f74e101Schristos             break;
4590f74e101Schristos 
4600f74e101Schristos         default:
461b3a00663Schristos             if (ndo->ndo_vflag <= 1)
462b3a00663Schristos                 print_unknown_data(ndo, tlv_tptr, "\n\t  ", tlv_tlen);
4630f74e101Schristos             break;
4640f74e101Schristos         }
465dc860a36Sspz 
466dc860a36Sspz     badlength:
4670f74e101Schristos         /* do we want to see an additional hexdump ? */
468b3a00663Schristos         if (ndo->ndo_vflag > 1) {
469b3a00663Schristos             print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t  ",
4700f74e101Schristos                                tlv_len-sizeof(struct tlv_header_t));
4710f74e101Schristos         }
4720f74e101Schristos 
4730f74e101Schristos         tptr+=tlv_len;
4740f74e101Schristos         tlen-=tlv_len;
4750f74e101Schristos     }
4760f74e101Schristos     return;
477dc860a36Sspz 
478dc860a36Sspz tooshort:
479c74ad251Schristos     ND_PRINT("\n\t\t packet is too short");
4800f74e101Schristos }
4810f74e101Schristos 
482b3a00663Schristos static void
483b3a00663Schristos slow_oam_print(netdissect_options *ndo,
484c74ad251Schristos                const u_char *tptr, u_int tlen)
485ba2ff121Schristos {
486c74ad251Schristos     uint8_t code;
487c74ad251Schristos     uint8_t type, length;
488c74ad251Schristos     uint8_t state;
489c74ad251Schristos     uint8_t command;
4900f74e101Schristos     u_int hexdump;
4910f74e101Schristos 
4920f74e101Schristos     struct slow_oam_common_header_t {
493c74ad251Schristos         nd_uint16_t flags;
494c74ad251Schristos         nd_uint8_t code;
4950f74e101Schristos     };
4960f74e101Schristos 
4970f74e101Schristos     struct slow_oam_tlv_header_t {
498c74ad251Schristos         nd_uint8_t type;
499c74ad251Schristos         nd_uint8_t length;
5000f74e101Schristos     };
5010f74e101Schristos 
5020f74e101Schristos     union {
5030f74e101Schristos         const struct slow_oam_common_header_t *slow_oam_common_header;
5040f74e101Schristos         const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
5050f74e101Schristos     } ptr;
5060f74e101Schristos 
5070f74e101Schristos     union {
5080f74e101Schristos         const struct slow_oam_info_t *slow_oam_info;
5090f74e101Schristos         const struct slow_oam_link_event_t *slow_oam_link_event;
5100f74e101Schristos         const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
5110f74e101Schristos         const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
5120f74e101Schristos         const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
5130f74e101Schristos     } tlv;
5140f74e101Schristos 
515fdccd7e4Schristos     ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
516dc860a36Sspz     if (tlen < sizeof(*ptr.slow_oam_common_header))
517dc860a36Sspz         goto tooshort;
518c74ad251Schristos     ND_TCHECK_SIZE(ptr.slow_oam_common_header);
5190f74e101Schristos     tptr += sizeof(struct slow_oam_common_header_t);
5200f74e101Schristos     tlen -= sizeof(struct slow_oam_common_header_t);
5210f74e101Schristos 
522c74ad251Schristos     code = GET_U_1(ptr.slow_oam_common_header->code);
523c74ad251Schristos     ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
524c74ad251Schristos            tok2str(slow_oam_code_values, "Unknown (%u)", code),
5250f74e101Schristos            bittok2str(slow_oam_flag_values,
5260f74e101Schristos                       "none",
527c74ad251Schristos                       GET_BE_U_2(ptr.slow_oam_common_header->flags)));
5280f74e101Schristos 
529c74ad251Schristos     switch (code) {
5300f74e101Schristos     case SLOW_OAM_CODE_INFO:
5310f74e101Schristos         while (tlen > 0) {
5320f74e101Schristos             ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
533dc860a36Sspz             if (tlen < sizeof(*ptr.slow_oam_tlv_header))
534dc860a36Sspz                 goto tooshort;
535c74ad251Schristos             ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
536c74ad251Schristos             type = GET_U_1(ptr.slow_oam_tlv_header->type);
537c74ad251Schristos             length = GET_U_1(ptr.slow_oam_tlv_header->length);
538c74ad251Schristos             ND_PRINT("\n\t  %s Information Type (%u), length %u",
539c74ad251Schristos                    tok2str(slow_oam_info_type_values, "Reserved", type),
540c74ad251Schristos                    type,
541c74ad251Schristos                    length);
5420f74e101Schristos 
543c74ad251Schristos             if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
544dc860a36Sspz                 /*
545dc860a36Sspz                  * As IEEE Std 802.3-2015 says for the End of TLV Marker,
546dc860a36Sspz                  * "(the length and value of the Type 0x00 TLV can be ignored)".
547dc860a36Sspz                  */
548dc860a36Sspz                 return;
549dc860a36Sspz             }
550dc860a36Sspz 
551dc860a36Sspz             /* length includes the type and length fields */
552c74ad251Schristos             if (length < sizeof(struct slow_oam_tlv_header_t)) {
553c74ad251Schristos                 ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
554c74ad251Schristos                          sizeof(struct slow_oam_tlv_header_t));
555dc860a36Sspz                 return;
556dc860a36Sspz             }
557dc860a36Sspz 
558c74ad251Schristos             if (tlen < length)
559dc860a36Sspz                 goto tooshort;
560c74ad251Schristos             ND_TCHECK_LEN(tptr, length);
561dc860a36Sspz 
5620f74e101Schristos             hexdump = FALSE;
563c74ad251Schristos             switch (type) {
5640f74e101Schristos             case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
5650f74e101Schristos             case SLOW_OAM_INFO_TYPE_REMOTE:
5660f74e101Schristos                 tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
5670f74e101Schristos 
568c74ad251Schristos                 if (GET_U_1(tlv.slow_oam_info->info_length) !=
5690f74e101Schristos                     sizeof(struct slow_oam_info_t)) {
570c74ad251Schristos                     ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
571c74ad251Schristos                            sizeof(struct slow_oam_info_t));
572dc860a36Sspz                     hexdump = TRUE;
573dc860a36Sspz                     goto badlength_code_info;
5740f74e101Schristos                 }
5750f74e101Schristos 
576c74ad251Schristos                 ND_PRINT("\n\t    OAM-Version %u, Revision %u",
577c74ad251Schristos                        GET_U_1(tlv.slow_oam_info->oam_version),
578c74ad251Schristos                        GET_BE_U_2(tlv.slow_oam_info->revision));
5790f74e101Schristos 
580c74ad251Schristos                 state = GET_U_1(tlv.slow_oam_info->state);
581c74ad251Schristos                 ND_PRINT("\n\t    State-Parser-Action %s, State-MUX-Action %s",
5820f74e101Schristos                        tok2str(slow_oam_info_type_state_parser_values, "Reserved",
583c74ad251Schristos                                state & OAM_INFO_TYPE_PARSER_MASK),
5840f74e101Schristos                        tok2str(slow_oam_info_type_state_mux_values, "Reserved",
585c74ad251Schristos                                state & OAM_INFO_TYPE_MUX_MASK));
586c74ad251Schristos                 ND_PRINT("\n\t    OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
5870f74e101Schristos                        bittok2str(slow_oam_info_type_oam_config_values, "none",
588c74ad251Schristos                                   GET_U_1(tlv.slow_oam_info->oam_config)),
589c74ad251Schristos                        GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
590c74ad251Schristos                        OAM_INFO_TYPE_PDU_SIZE_MASK);
591c74ad251Schristos                 ND_PRINT("\n\t    OUI %s (0x%06x), Vendor-Private 0x%08x",
5920f74e101Schristos                        tok2str(oui_values, "Unknown",
593c74ad251Schristos                                GET_BE_U_3(tlv.slow_oam_info->oui)),
594c74ad251Schristos                        GET_BE_U_3(tlv.slow_oam_info->oui),
595c74ad251Schristos                        GET_BE_U_4(tlv.slow_oam_info->vendor_private));
5960f74e101Schristos                 break;
5970f74e101Schristos 
5980f74e101Schristos             case SLOW_OAM_INFO_TYPE_ORG_SPECIFIC:
5990f74e101Schristos                 hexdump = TRUE;
6000f74e101Schristos                 break;
6010f74e101Schristos 
6020f74e101Schristos             default:
6030f74e101Schristos                 hexdump = TRUE;
6040f74e101Schristos                 break;
6050f74e101Schristos             }
6060f74e101Schristos 
607dc860a36Sspz         badlength_code_info:
6080f74e101Schristos             /* do we also want to see a hex dump ? */
609b3a00663Schristos             if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
610b3a00663Schristos                 print_unknown_data(ndo, tptr, "\n\t  ",
611c74ad251Schristos                                    length);
6120f74e101Schristos             }
6130f74e101Schristos 
614c74ad251Schristos             tlen -= length;
615c74ad251Schristos             tptr += length;
6160f74e101Schristos         }
6170f74e101Schristos         break;
6180f74e101Schristos 
6190f74e101Schristos     case SLOW_OAM_CODE_EVENT_NOTIF:
620dc860a36Sspz         /* Sequence number */
621dc860a36Sspz         if (tlen < 2)
622dc860a36Sspz             goto tooshort;
623c74ad251Schristos         ND_PRINT("\n\t  Sequence Number %u", GET_BE_U_2(tptr));
624dc860a36Sspz         tlen -= 2;
625dc860a36Sspz         tptr += 2;
626dc860a36Sspz 
627dc860a36Sspz         /* TLVs */
6280f74e101Schristos         while (tlen > 0) {
6290f74e101Schristos             ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
630dc860a36Sspz             if (tlen < sizeof(*ptr.slow_oam_tlv_header))
631dc860a36Sspz                 goto tooshort;
632c74ad251Schristos             type = GET_U_1(ptr.slow_oam_tlv_header->type);
633c74ad251Schristos             length = GET_U_1(ptr.slow_oam_tlv_header->length);
634c74ad251Schristos             ND_PRINT("\n\t  %s Link Event Type (%u), length %u",
6350f74e101Schristos                    tok2str(slow_oam_link_event_values, "Reserved",
636c74ad251Schristos                            type),
637c74ad251Schristos                    type,
638c74ad251Schristos                    length);
6390f74e101Schristos 
640c74ad251Schristos             if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
641dc860a36Sspz                 /*
642dc860a36Sspz                  * As IEEE Std 802.3-2015 says for the End of TLV Marker,
643dc860a36Sspz                  * "(the length and value of the Type 0x00 TLV can be ignored)".
644dc860a36Sspz                  */
645dc860a36Sspz                 return;
646dc860a36Sspz             }
647dc860a36Sspz 
648dc860a36Sspz             /* length includes the type and length fields */
649c74ad251Schristos             if (length < sizeof(struct slow_oam_tlv_header_t)) {
650c74ad251Schristos                 ND_PRINT("\n\t    ERROR: illegal length - should be >= %zu",
651c74ad251Schristos                          sizeof(struct slow_oam_tlv_header_t));
652dc860a36Sspz                 return;
653dc860a36Sspz             }
654dc860a36Sspz 
655c74ad251Schristos             if (tlen < length)
656dc860a36Sspz                 goto tooshort;
657c74ad251Schristos             ND_TCHECK_LEN(tptr, length);
658dc860a36Sspz 
6590f74e101Schristos             hexdump = FALSE;
660c74ad251Schristos             switch (type) {
6610f74e101Schristos             case SLOW_OAM_LINK_EVENT_ERR_SYM_PER: /* identical format - fall through */
6620f74e101Schristos             case SLOW_OAM_LINK_EVENT_ERR_FRM:
6630f74e101Schristos             case SLOW_OAM_LINK_EVENT_ERR_FRM_PER:
6640f74e101Schristos             case SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM:
6650f74e101Schristos                 tlv.slow_oam_link_event = (const struct slow_oam_link_event_t *)tptr;
6660f74e101Schristos 
667c74ad251Schristos                 if (GET_U_1(tlv.slow_oam_link_event->event_length) !=
6680f74e101Schristos                     sizeof(struct slow_oam_link_event_t)) {
669c74ad251Schristos                     ND_PRINT("\n\t    ERROR: illegal length - should be %zu",
670c74ad251Schristos                              sizeof(struct slow_oam_link_event_t));
671dc860a36Sspz                     hexdump = TRUE;
672dc860a36Sspz                     goto badlength_event_notif;
6730f74e101Schristos                 }
6740f74e101Schristos 
675c74ad251Schristos                 ND_PRINT("\n\t    Timestamp %u ms, Errored Window %" PRIu64
6760f74e101Schristos                        "\n\t    Errored Threshold %" PRIu64
6770f74e101Schristos                        "\n\t    Errors %" PRIu64
6780f74e101Schristos                        "\n\t    Error Running Total %" PRIu64
6790f74e101Schristos                        "\n\t    Event Running Total %u",
680c74ad251Schristos                        GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
681c74ad251Schristos                        GET_BE_U_8(tlv.slow_oam_link_event->window),
682c74ad251Schristos                        GET_BE_U_8(tlv.slow_oam_link_event->threshold),
683c74ad251Schristos                        GET_BE_U_8(tlv.slow_oam_link_event->errors),
684c74ad251Schristos                        GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
685c74ad251Schristos                        GET_BE_U_4(tlv.slow_oam_link_event->event_running_total));
6860f74e101Schristos                 break;
6870f74e101Schristos 
6880f74e101Schristos             case SLOW_OAM_LINK_EVENT_ORG_SPECIFIC:
6890f74e101Schristos                 hexdump = TRUE;
6900f74e101Schristos                 break;
6910f74e101Schristos 
6920f74e101Schristos             default:
6930f74e101Schristos                 hexdump = TRUE;
6940f74e101Schristos                 break;
6950f74e101Schristos             }
6960f74e101Schristos 
697dc860a36Sspz         badlength_event_notif:
6980f74e101Schristos             /* do we also want to see a hex dump ? */
699b3a00663Schristos             if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
700b3a00663Schristos                 print_unknown_data(ndo, tptr, "\n\t  ",
701c74ad251Schristos                                    length);
7020f74e101Schristos             }
7030f74e101Schristos 
704c74ad251Schristos             tlen -= length;
705c74ad251Schristos             tptr += length;
7060f74e101Schristos         }
7070f74e101Schristos         break;
7080f74e101Schristos 
7090f74e101Schristos     case SLOW_OAM_CODE_LOOPBACK_CTRL:
7100f74e101Schristos         tlv.slow_oam_loopbackctrl = (const struct slow_oam_loopbackctrl_t *)tptr;
711dc860a36Sspz         if (tlen < sizeof(*tlv.slow_oam_loopbackctrl))
712dc860a36Sspz             goto tooshort;
713c74ad251Schristos         command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
714c74ad251Schristos         ND_PRINT("\n\t  Command %s (%u)",
7150f74e101Schristos                tok2str(slow_oam_loopbackctrl_cmd_values,
7160f74e101Schristos                        "Unknown",
717c74ad251Schristos                        command),
718c74ad251Schristos                command);
7190f74e101Schristos         tptr ++;
7200f74e101Schristos         tlen --;
7210f74e101Schristos         break;
7220f74e101Schristos 
7230f74e101Schristos         /*
7240f74e101Schristos          * FIXME those are the defined codes that lack a decoder
7250f74e101Schristos          * you are welcome to contribute code ;-)
7260f74e101Schristos          */
7270f74e101Schristos     case SLOW_OAM_CODE_VAR_REQUEST:
7280f74e101Schristos     case SLOW_OAM_CODE_VAR_RESPONSE:
7290f74e101Schristos     case SLOW_OAM_CODE_PRIVATE:
7300f74e101Schristos     default:
731b3a00663Schristos         if (ndo->ndo_vflag <= 1) {
732b3a00663Schristos             print_unknown_data(ndo, tptr, "\n\t  ", tlen);
7330f74e101Schristos         }
7340f74e101Schristos         break;
7350f74e101Schristos     }
7360f74e101Schristos     return;
737dc860a36Sspz 
738dc860a36Sspz tooshort:
739c74ad251Schristos     ND_PRINT("\n\t\t packet is too short");
7400f74e101Schristos }
741