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