+
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");
+}
+
+static void
+slow_oam_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlen)
+{
+ uint8_t code;
+ uint8_t type, length;
+ uint8_t state;
+ uint8_t command;
+ u_int hexdump;
+
+ struct slow_oam_common_header_t {
+ nd_uint16_t flags;
+ nd_uint8_t code;
+ };
+
+ struct slow_oam_tlv_header_t {
+ nd_uint8_t type;
+ nd_uint8_t length;
+ };
+
+ union {
+ const struct slow_oam_common_header_t *slow_oam_common_header;
+ const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
+ } ptr;
+
+ union {
+ const struct slow_oam_info_t *slow_oam_info;
+ const struct slow_oam_link_event_t *slow_oam_link_event;
+ const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
+ const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
+ const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
+ } tlv;
+
+ ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_common_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(ptr.slow_oam_common_header);
+ tptr += sizeof(struct slow_oam_common_header_t);
+ tlen -= sizeof(struct slow_oam_common_header_t);
+
+ code = GET_U_1(ptr.slow_oam_common_header->code);
+ ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
+ tok2str(slow_oam_code_values, "Unknown (%u)", code),
+ bittok2str(slow_oam_flag_values,
+ "none",
+ GET_BE_U_2(ptr.slow_oam_common_header->flags)));
+
+ switch (code) {
+ case SLOW_OAM_CODE_INFO:
+ while (tlen != 0) {
+ ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_tlv_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
+ type = GET_U_1(ptr.slow_oam_tlv_header->type);
+ length = GET_U_1(ptr.slow_oam_tlv_header->length);
+ ND_PRINT("\n\t %s Information Type (%u), length %u",
+ tok2str(slow_oam_info_type_values, "Reserved", type),
+ type,
+ length);
+
+ if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
+ /*
+ * As IEEE Std 802.3-2015 says for the End of TLV Marker,
+ * "(the length and value of the Type 0x00 TLV can be ignored)".
+ */
+ return;
+ }
+
+ /* length includes the type and length fields */
+ if (length < sizeof(struct slow_oam_tlv_header_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
+ sizeof(struct slow_oam_tlv_header_t));
+ return;
+ }
+
+ if (tlen < length)
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, length);
+
+ hexdump = FALSE;
+ switch (type) {
+ case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
+ case SLOW_OAM_INFO_TYPE_REMOTE:
+ tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
+
+ if (GET_U_1(tlv.slow_oam_info->info_length) !=
+ sizeof(struct slow_oam_info_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct slow_oam_info_t));
+ hexdump = TRUE;
+ goto badlength_code_info;
+ }
+
+ ND_PRINT("\n\t OAM-Version %u, Revision %u",
+ GET_U_1(tlv.slow_oam_info->oam_version),
+ GET_BE_U_2(tlv.slow_oam_info->revision));
+
+ state = GET_U_1(tlv.slow_oam_info->state);
+ ND_PRINT("\n\t State-Parser-Action %s, State-MUX-Action %s",
+ tok2str(slow_oam_info_type_state_parser_values, "Reserved",
+ state & OAM_INFO_TYPE_PARSER_MASK),
+ tok2str(slow_oam_info_type_state_mux_values, "Reserved",
+ state & OAM_INFO_TYPE_MUX_MASK));
+ ND_PRINT("\n\t OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
+ bittok2str(slow_oam_info_type_oam_config_values, "none",
+ GET_U_1(tlv.slow_oam_info->oam_config)),
+ GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
+ OAM_INFO_TYPE_PDU_SIZE_MASK);
+ ND_PRINT("\n\t OUI %s (0x%06x), Vendor-Private 0x%08x",
+ tok2str(oui_values, "Unknown",
+ GET_BE_U_3(tlv.slow_oam_info->oui)),
+ GET_BE_U_3(tlv.slow_oam_info->oui),
+ GET_BE_U_4(tlv.slow_oam_info->vendor_private));
+ break;
+
+ case SLOW_OAM_INFO_TYPE_ORG_SPECIFIC:
+ hexdump = TRUE;
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ badlength_code_info:
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
+ print_unknown_data(ndo, tptr, "\n\t ",
+ length);
+ }
+
+ tlen -= length;
+ tptr += length;
+ }
+ break;
+
+ case SLOW_OAM_CODE_EVENT_NOTIF:
+ /* Sequence number */
+ if (tlen < 2)
+ goto tooshort;
+ ND_PRINT("\n\t Sequence Number %u", GET_BE_U_2(tptr));
+ tlen -= 2;
+ tptr += 2;
+
+ /* TLVs */
+ while (tlen != 0) {
+ ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_tlv_header))
+ goto tooshort;
+ type = GET_U_1(ptr.slow_oam_tlv_header->type);
+ length = GET_U_1(ptr.slow_oam_tlv_header->length);
+ ND_PRINT("\n\t %s Link Event Type (%u), length %u",
+ tok2str(slow_oam_link_event_values, "Reserved",
+ type),
+ type,
+ length);
+
+ if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
+ /*
+ * As IEEE Std 802.3-2015 says for the End of TLV Marker,
+ * "(the length and value of the Type 0x00 TLV can be ignored)".
+ */
+ return;
+ }
+
+ /* length includes the type and length fields */
+ if (length < sizeof(struct slow_oam_tlv_header_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
+ sizeof(struct slow_oam_tlv_header_t));
+ return;
+ }
+
+ if (tlen < length)
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, length);
+
+ hexdump = FALSE;
+ switch (type) {
+ case SLOW_OAM_LINK_EVENT_ERR_SYM_PER: /* identical format - fall through */
+ case SLOW_OAM_LINK_EVENT_ERR_FRM:
+ case SLOW_OAM_LINK_EVENT_ERR_FRM_PER:
+ case SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM:
+ tlv.slow_oam_link_event = (const struct slow_oam_link_event_t *)tptr;
+
+ if (GET_U_1(tlv.slow_oam_link_event->event_length) !=
+ sizeof(struct slow_oam_link_event_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct slow_oam_link_event_t));
+ hexdump = TRUE;
+ goto badlength_event_notif;
+ }
+
+ ND_PRINT("\n\t Timestamp %u ms, Errored Window %" PRIu64
+ "\n\t Errored Threshold %" PRIu64
+ "\n\t Errors %" PRIu64
+ "\n\t Error Running Total %" PRIu64
+ "\n\t Event Running Total %u",
+ GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
+ GET_BE_U_8(tlv.slow_oam_link_event->window),
+ GET_BE_U_8(tlv.slow_oam_link_event->threshold),
+ GET_BE_U_8(tlv.slow_oam_link_event->errors),
+ GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
+ GET_BE_U_4(tlv.slow_oam_link_event->event_running_total));
+ break;
+
+ case SLOW_OAM_LINK_EVENT_ORG_SPECIFIC:
+ hexdump = TRUE;
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ badlength_event_notif:
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
+ print_unknown_data(ndo, tptr, "\n\t ",
+ length);
+ }
+
+ tlen -= length;
+ tptr += length;
+ }
+ break;
+
+ case SLOW_OAM_CODE_LOOPBACK_CTRL:
+ tlv.slow_oam_loopbackctrl = (const struct slow_oam_loopbackctrl_t *)tptr;
+ if (tlen < sizeof(*tlv.slow_oam_loopbackctrl))
+ goto tooshort;
+ command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
+ ND_PRINT("\n\t Command %s (%u)",
+ tok2str(slow_oam_loopbackctrl_cmd_values,
+ "Unknown",
+ command),
+ command);
+ tptr ++;
+ tlen --;
+ break;
+
+ /*
+ * FIXME those are the defined codes that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+ case SLOW_OAM_CODE_VAR_REQUEST:
+ case SLOW_OAM_CODE_VAR_RESPONSE:
+ case SLOW_OAM_CODE_PRIVATE:
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo, tptr, "\n\t ", tlen);
+ }
+ break;
+ }
+ return;
+
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");