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