/* * Print NTP control message requests and responses */ static void ntp_control_print(netdissect_options *ndo, const struct ntp_control_data *cd, u_int length) { uint8_t control, R, E, M, opcode; uint16_t sequence, status, assoc, offset, count; if (length < NTP_CTRLMSG_MINLEN) goto invalid; ND_TCHECK_1(cd->control); control = EXTRACT_U_1(cd->control); R = (control & 0x80) != 0; E = (control & 0x40) != 0; M = (control & 0x20) != 0; opcode = control & 0x1f; ND_PRINT(", %s, %s, %s, OpCode=%u\n", R ? "Response" : "Request", E ? "Error" : "OK", M ? "More" : "Last", opcode); ND_TCHECK_2(cd->sequence); sequence = EXTRACT_BE_U_2(cd->sequence); ND_PRINT("\tSequence=%hu", sequence); ND_TCHECK_2(cd->status); status = EXTRACT_BE_U_2(cd->status); ND_PRINT(", Status=%#hx", status); ND_TCHECK_2(cd->assoc); assoc = EXTRACT_BE_U_2(cd->assoc); ND_PRINT(", Assoc.=%hu", assoc); ND_TCHECK_2(cd->offset); offset = EXTRACT_BE_U_2(cd->offset); ND_PRINT(", Offset=%hu", offset); ND_TCHECK_2(cd->count); count = EXTRACT_BE_U_2(cd->count); ND_PRINT(", Count=%hu", count); if (NTP_CTRLMSG_MINLEN + count > length) goto invalid; if (count != 0) { ND_TCHECK_LEN(cd->data, count); ND_PRINT("\n\tTO-BE-DONE: data not interpreted"); } return; invalid: nd_print_invalid(ndo); ND_TCHECK_LEN(cd, length); return; trunc: nd_print_trunc(ndo); }
void tcp_print(netdissect_options *ndo, const u_char *bp, u_int length, const u_char *bp2, int fragmented) { const struct tcphdr *tp; const struct ip *ip; u_char flags; u_int hlen; char ch; uint16_t sport, dport, win, urp; uint32_t seq, ack, thseq, thack; u_int utoval; uint16_t magic; int rev; const struct ip6_hdr *ip6; ndo->ndo_protocol = "tcp"; tp = (const struct tcphdr *)bp; ip = (const struct ip *)bp2; if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)bp2; else ip6 = NULL; ch = '\0'; if (!ND_TTEST_2(tp->th_dport)) { if (ip6) { ND_PRINT("%s > %s:", ip6addr_string(ndo, ip6->ip6_src), ip6addr_string(ndo, ip6->ip6_dst)); } else { ND_PRINT("%s > %s:", ipaddr_string(ndo, ip->ip_src), ipaddr_string(ndo, ip->ip_dst)); } nd_print_trunc(ndo); return; } sport = GET_BE_U_2(tp->th_sport); dport = GET_BE_U_2(tp->th_dport); if (ip6) { if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP) { ND_PRINT("%s.%s > %s.%s: ", ip6addr_string(ndo, ip6->ip6_src), tcpport_string(ndo, sport), ip6addr_string(ndo, ip6->ip6_dst), tcpport_string(ndo, dport)); } else { ND_PRINT("%s > %s: ", tcpport_string(ndo, sport), tcpport_string(ndo, dport)); } } else { if (GET_U_1(ip->ip_p) == IPPROTO_TCP) { ND_PRINT("%s.%s > %s.%s: ", ipaddr_string(ndo, ip->ip_src), tcpport_string(ndo, sport), ipaddr_string(ndo, ip->ip_dst), tcpport_string(ndo, dport)); } else { ND_PRINT("%s > %s: ", tcpport_string(ndo, sport), tcpport_string(ndo, dport)); } } ND_TCHECK_SIZE(tp); hlen = TH_OFF(tp) * 4; if (hlen < sizeof(*tp)) { ND_PRINT(" tcp %u [bad hdr length %u - too short, < %lu]", length - hlen, hlen, (unsigned long)sizeof(*tp)); return; } seq = GET_BE_U_4(tp->th_seq); ack = GET_BE_U_4(tp->th_ack); win = GET_BE_U_2(tp->th_win); urp = GET_BE_U_2(tp->th_urp); if (ndo->ndo_qflag) { ND_PRINT("tcp %u", length - hlen); if (hlen > length) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, length); } return; } flags = GET_U_1(tp->th_flags); ND_PRINT("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags)); if (!ndo->ndo_Sflag && (flags & TH_ACK)) { /* * Find (or record) the initial sequence numbers for * this conversation. (we pick an arbitrary * collating order so there's only one entry for * both directions). */ rev = 0; if (ip6) { struct tcp_seq_hash6 *th; struct tcp_seq_hash6 *tcp_seq_hash; const void *src, *dst; struct tha6 tha; tcp_seq_hash = tcp_seq_hash6; src = (const void *)ip6->ip6_src; dst = (const void *)ip6->ip6_dst; if (sport > dport) rev = 1; else if (sport == dport) { if (UNALIGNED_MEMCMP(src, dst, sizeof(ip6->ip6_dst)) > 0) rev = 1; } if (rev) { UNALIGNED_MEMCPY(&tha.src, dst, sizeof(ip6->ip6_dst)); UNALIGNED_MEMCPY(&tha.dst, src, sizeof(ip6->ip6_src)); tha.port = ((u_int)dport) << 16 | sport; } else { UNALIGNED_MEMCPY(&tha.dst, dst, sizeof(ip6->ip6_dst)); UNALIGNED_MEMCPY(&tha.src, src, sizeof(ip6->ip6_src)); tha.port = ((u_int)sport) << 16 | dport; } for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ /* calloc() return used by the 'tcp_seq_hash6' hash table: do not free() */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash6 *) calloc(1, sizeof(*th)); if (th->nxt == NULL) (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } else { struct tcp_seq_hash *th; struct tcp_seq_hash *tcp_seq_hash; struct tha tha; tcp_seq_hash = tcp_seq_hash4; if (sport > dport) rev = 1; else if (sport == dport) { if (UNALIGNED_MEMCMP(ip->ip_src, ip->ip_dst, sizeof(ip->ip_dst)) > 0) rev = 1; } if (rev) { UNALIGNED_MEMCPY(&tha.src, ip->ip_dst, sizeof(ip->ip_dst)); UNALIGNED_MEMCPY(&tha.dst, ip->ip_src, sizeof(ip->ip_src)); tha.port = ((u_int)dport) << 16 | sport; } else { UNALIGNED_MEMCPY(&tha.dst, ip->ip_dst, sizeof(ip->ip_dst)); UNALIGNED_MEMCPY(&tha.src, ip->ip_src, sizeof(ip->ip_src)); tha.port = ((u_int)sport) << 16 | dport; } for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; th->nxt; th = th->nxt) if (memcmp((char *)&tha, (char *)&th->addr, sizeof(th->addr)) == 0) break; if (!th->nxt || (flags & TH_SYN)) { /* didn't find it or new conversation */ /* calloc() return used by the 'tcp_seq_hash4' hash table: do not free() */ if (th->nxt == NULL) { th->nxt = (struct tcp_seq_hash *) calloc(1, sizeof(*th)); if (th->nxt == NULL) (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "tcp_print: calloc"); } th->addr = tha; if (rev) th->ack = seq, th->seq = ack - 1; else th->seq = seq, th->ack = ack - 1; } else { if (rev) seq -= th->ack, ack -= th->seq; else seq -= th->seq, ack -= th->ack; } thseq = th->seq; thack = th->ack; } } else { /*fool gcc*/ thseq = thack = rev = 0; } if (hlen > length) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, length); return; } if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) { /* Check the checksum, if possible. */ uint16_t sum, tcp_sum; if (IP_V(ip) == 4) { if (ND_TTEST_LEN(tp->th_sport, length)) { sum = tcp_cksum(ndo, ip, tp, length); tcp_sum = GET_BE_U_2(tp->th_sum); ND_PRINT(", cksum 0x%04x", tcp_sum); if (sum != 0) ND_PRINT(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else ND_PRINT(" (correct)"); } } else if (IP_V(ip) == 6 && ip6->ip6_plen) { if (ND_TTEST_LEN(tp->th_sport, length)) { sum = tcp6_cksum(ndo, ip6, tp, length); tcp_sum = GET_BE_U_2(tp->th_sum); ND_PRINT(", cksum 0x%04x", tcp_sum); if (sum != 0) ND_PRINT(" (incorrect -> 0x%04x)", in_cksum_shouldbe(tcp_sum, sum)); else ND_PRINT(" (correct)"); } } } length -= hlen; if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) { ND_PRINT(", seq %u", seq); if (length > 0) { ND_PRINT(":%u", seq + length); } } if (flags & TH_ACK) { ND_PRINT(", ack %u", ack); } ND_PRINT(", win %u", win); if (flags & TH_URG) ND_PRINT(", urg %u", urp); /* * Handle any options. */ if (hlen > sizeof(*tp)) { const u_char *cp; u_int i, opt, datalen; u_int len; hlen -= sizeof(*tp); cp = (const u_char *)tp + sizeof(*tp); ND_PRINT(", options ["); while (hlen > 0) { if (ch != '\0') ND_PRINT("%c", ch); ND_TCHECK_1(cp); opt = GET_U_1(cp); cp++; if (ZEROLENOPT(opt)) len = 1; else { ND_TCHECK_1(cp); len = GET_U_1(cp); cp++; /* total including type, len */ if (len < 2 || len > hlen) goto bad; --hlen; /* account for length byte */ } --hlen; /* account for type byte */ datalen = 0; /* Bail if "l" bytes of data are not left or were not captured */ #define LENCHECK(l) { if ((l) > hlen) goto bad; ND_TCHECK_LEN(cp, l); } ND_PRINT("%s", tok2str(tcp_option_values, "unknown-%u", opt)); switch (opt) { case TCPOPT_MAXSEG: datalen = 2; LENCHECK(datalen); ND_PRINT(" %u", GET_BE_U_2(cp)); break; case TCPOPT_WSCALE: datalen = 1; LENCHECK(datalen); ND_PRINT(" %u", GET_U_1(cp)); break; case TCPOPT_SACK: datalen = len - 2; if (datalen % 8 != 0) { ND_PRINT(" invalid sack"); } else { uint32_t s, e; ND_PRINT(" %u ", datalen / 8); for (i = 0; i < datalen; i += 8) { LENCHECK(i + 4); s = GET_BE_U_4(cp + i); LENCHECK(i + 8); e = GET_BE_U_4(cp + i + 4); if (rev) { s -= thseq; e -= thseq; } else { s -= thack; e -= thack; } ND_PRINT("{%u:%u}", s, e); } } break; case TCPOPT_CC: case TCPOPT_CCNEW: case TCPOPT_CCECHO: case TCPOPT_ECHO: case TCPOPT_ECHOREPLY: /* * those options share their semantics. * fall through */ datalen = 4; LENCHECK(datalen); ND_PRINT(" %u", GET_BE_U_4(cp)); break; case TCPOPT_TIMESTAMP: datalen = 8; LENCHECK(datalen); ND_PRINT(" val %u ecr %u", GET_BE_U_4(cp), GET_BE_U_4(cp + 4)); break; case TCPOPT_SIGNATURE: datalen = TCP_SIGLEN; LENCHECK(datalen); ND_PRINT(" "); #ifdef HAVE_LIBCRYPTO switch (tcp_verify_signature(ndo, ip, tp, bp + TH_OFF(tp) * 4, length, cp)) { case SIGNATURE_VALID: ND_PRINT("valid"); break; case SIGNATURE_INVALID: nd_print_invalid(ndo); break; case CANT_CHECK_SIGNATURE: ND_PRINT("can't check - "); for (i = 0; i < TCP_SIGLEN; ++i) ND_PRINT("%02x", GET_U_1(cp + i)); break; } #else for (i = 0; i < TCP_SIGLEN; ++i) ND_PRINT("%02x", GET_U_1(cp + i)); #endif break; case TCPOPT_SCPS: datalen = 2; LENCHECK(datalen); ND_PRINT(" cap %02x id %u", GET_U_1(cp), GET_U_1(cp + 1)); break; case TCPOPT_TCPAO: datalen = len - 2; /* RFC 5925 Section 2.2: * "The Length value MUST be greater than or equal to 4." * (This includes the Kind and Length fields already processed * at this point.) */ if (datalen < 2) { nd_print_invalid(ndo); } else { LENCHECK(1); ND_PRINT(" keyid %u", GET_U_1(cp)); LENCHECK(2); ND_PRINT(" rnextkeyid %u", GET_U_1(cp + 1)); if (datalen > 2) { ND_PRINT(" mac 0x"); for (i = 2; i < datalen; i++) { LENCHECK(i + 1); ND_PRINT("%02x", GET_U_1(cp + i)); } } } break; case TCPOPT_EOL: case TCPOPT_NOP: case TCPOPT_SACKOK: /* * Nothing interesting. * fall through */ break; case TCPOPT_UTO: datalen = 2; LENCHECK(datalen); utoval = GET_BE_U_2(cp); ND_PRINT(" 0x%x", utoval); if (utoval & 0x0001) utoval = (utoval >> 1) * 60; else utoval >>= 1; ND_PRINT(" %u", utoval); break; case TCPOPT_MPTCP: datalen = len - 2; LENCHECK(datalen); if (!mptcp_print(ndo, cp-2, len, flags)) goto bad; break; case TCPOPT_FASTOPEN: datalen = len - 2; LENCHECK(datalen); ND_PRINT(" "); print_tcp_fastopen_option(ndo, cp, datalen, FALSE); break; case TCPOPT_EXPERIMENT2: datalen = len - 2; LENCHECK(datalen); if (datalen < 2) goto bad; /* RFC6994 */ magic = GET_BE_U_2(cp); ND_PRINT("-"); switch(magic) { case 0xf989: /* TCP Fast Open RFC 7413 */ print_tcp_fastopen_option(ndo, cp + 2, datalen - 2, TRUE); break; default: /* Unknown magic number */ ND_PRINT("%04x", magic); break; } break; default: datalen = len - 2; if (datalen) ND_PRINT(" 0x"); for (i = 0; i < datalen; ++i) { LENCHECK(i + 1); ND_PRINT("%02x", GET_U_1(cp + i)); } break; } /* Account for data printed */ cp += datalen; hlen -= datalen; /* Check specification against observed length */ ++datalen; /* option octet */ if (!ZEROLENOPT(opt)) ++datalen; /* size octet */ if (datalen != len) ND_PRINT("[len %u]", len); ch = ','; if (opt == TCPOPT_EOL) break; } ND_PRINT("]"); }
/* * Print NTP time requests and responses */ static void ntp_time_print(netdissect_options *ndo, const struct ntp_time_data *bp, u_int length) { uint8_t stratum; if (length < NTP_TIMEMSG_MINLEN) goto invalid; ND_TCHECK_1(bp->stratum); stratum = EXTRACT_U_1(bp->stratum); ND_PRINT(", Stratum %u (%s)", stratum, tok2str(ntp_stratum_values, (stratum >=2 && stratum<=15) ? "secondary reference" : "reserved", stratum)); ND_TCHECK_1(bp->ppoll); ND_PRINT(", poll %d", EXTRACT_S_1(bp->ppoll)); p_poll(ndo, EXTRACT_S_1(bp->ppoll)); ND_TCHECK_1(bp->precision); ND_PRINT(", precision %d", EXTRACT_S_1(bp->precision)); ND_TCHECK_SIZE(&bp->root_delay); ND_PRINT("\n\tRoot Delay: "); p_sfix(ndo, &bp->root_delay); ND_TCHECK_SIZE(&bp->root_dispersion); ND_PRINT(", Root dispersion: "); p_sfix(ndo, &bp->root_dispersion); ND_TCHECK_4(bp->refid); ND_PRINT(", Reference-ID: "); /* Interpretation depends on stratum */ switch (stratum) { case UNSPECIFIED: ND_PRINT("(unspec)"); break; case PRIM_REF: if (nd_printn(ndo, (const u_char *)&(bp->refid), 4, ndo->ndo_snapend)) goto trunc; break; case INFO_QUERY: ND_PRINT("%s INFO_QUERY", ipaddr_string(ndo, bp->refid)); /* this doesn't have more content */ return; case INFO_REPLY: ND_PRINT("%s INFO_REPLY", ipaddr_string(ndo, bp->refid)); /* this is too complex to be worth printing */ return; default: /* In NTPv4 (RFC 5905) refid is an IPv4 address or first 32 bits of MD5 sum of IPv6 address */ ND_PRINT("0x%08x", EXTRACT_BE_U_4(bp->refid)); break; } ND_TCHECK_SIZE(&bp->ref_timestamp); ND_PRINT("\n\t Reference Timestamp: "); p_ntp_time(ndo, &(bp->ref_timestamp)); ND_TCHECK_SIZE(&bp->org_timestamp); ND_PRINT("\n\t Originator Timestamp: "); p_ntp_time(ndo, &(bp->org_timestamp)); ND_TCHECK_SIZE(&bp->rec_timestamp); ND_PRINT("\n\t Receive Timestamp: "); p_ntp_time(ndo, &(bp->rec_timestamp)); ND_TCHECK_SIZE(&bp->xmt_timestamp); ND_PRINT("\n\t Transmit Timestamp: "); p_ntp_time(ndo, &(bp->xmt_timestamp)); ND_PRINT("\n\t Originator - Receive Timestamp: "); p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->rec_timestamp)); ND_PRINT("\n\t Originator - Transmit Timestamp: "); p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->xmt_timestamp)); /* FIXME: this code is not aware of any extension fields */ if (length == NTP_TIMEMSG_MINLEN + 4) { /* Optional: key-id (crypto-NAK) */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); } else if (length == NTP_TIMEMSG_MINLEN + 4 + 16) { /* Optional: key-id + 128-bit digest */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); ND_TCHECK_LEN(bp->message_digest, 16); ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x", EXTRACT_BE_U_4(bp->message_digest), EXTRACT_BE_U_4(bp->message_digest + 4), EXTRACT_BE_U_4(bp->message_digest + 8), EXTRACT_BE_U_4(bp->message_digest + 12)); } else if (length == NTP_TIMEMSG_MINLEN + 4 + 20) { /* Optional: key-id + 160-bit digest */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); ND_TCHECK_LEN(bp->message_digest, 20); ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x%08x", EXTRACT_BE_U_4(bp->message_digest), EXTRACT_BE_U_4(bp->message_digest + 4), EXTRACT_BE_U_4(bp->message_digest + 8), EXTRACT_BE_U_4(bp->message_digest + 12), EXTRACT_BE_U_4(bp->message_digest + 16)); } else if (length > NTP_TIMEMSG_MINLEN) { ND_PRINT("\n\t(%u more bytes after the header)", length - NTP_TIMEMSG_MINLEN); } return; invalid: nd_print_invalid(ndo); ND_TCHECK_LEN(bp, length); return; trunc: nd_print_trunc(ndo); }
/* * Print a single PDU. */ static u_int rpki_rtr_pdu_print(netdissect_options *ndo, const u_char *tptr, const u_int len, const u_char recurse, const u_int indent) { const rpki_rtr_pdu *pdu_header; u_int pdu_type, pdu_len, hexdump; const u_char *msg; /* Protocol Version */ ND_TCHECK_1(tptr); if (GET_U_1(tptr) != 0) { /* Skip the rest of the input buffer because even if this is * a well-formed PDU of a future RPKI-Router protocol version * followed by a well-formed PDU of RPKI-Router protocol * version 0, there is no way to know exactly how to skip the * current PDU. */ ND_PRINT("%sRPKI-RTRv%u (unknown)", indent_string(8), GET_U_1(tptr)); return len; } if (len < sizeof(rpki_rtr_pdu)) { ND_PRINT("(%u bytes is too few to decode)", len); goto invalid; } ND_TCHECK_LEN(tptr, sizeof(rpki_rtr_pdu)); pdu_header = (const rpki_rtr_pdu *)tptr; pdu_type = pdu_header->pdu_type; pdu_len = GET_BE_U_4(pdu_header->length); /* Do not check bounds with pdu_len yet, do it in the case blocks * below to make it possible to decode at least the beginning of * a truncated Error Report PDU or a truncated encapsulated PDU. */ hexdump = FALSE; ND_PRINT("%sRPKI-RTRv%u, %s PDU (%u), length: %u", indent_string(8), pdu_header->version, tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type), pdu_type, pdu_len); if (pdu_len < sizeof(rpki_rtr_pdu) || pdu_len > len) goto invalid; switch (pdu_type) { /* * The following PDUs share the message format. */ case RPKI_RTR_SERIAL_NOTIFY_PDU: case RPKI_RTR_SERIAL_QUERY_PDU: case RPKI_RTR_END_OF_DATA_PDU: if (pdu_len != sizeof(rpki_rtr_pdu) + 4) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); msg = (const u_char *)(pdu_header + 1); ND_PRINT("%sSession ID: 0x%04x, Serial: %u", indent_string(indent+2), GET_BE_U_2(pdu_header->u.session_id), GET_BE_U_4(msg)); break; /* * The following PDUs share the message format. */ case RPKI_RTR_RESET_QUERY_PDU: case RPKI_RTR_CACHE_RESET_PDU: if (pdu_len != sizeof(rpki_rtr_pdu)) goto invalid; /* no additional boundary to check */ /* * Zero payload PDUs. */ break; case RPKI_RTR_CACHE_RESPONSE_PDU: if (pdu_len != sizeof(rpki_rtr_pdu)) goto invalid; /* no additional boundary to check */ ND_PRINT("%sSession ID: 0x%04x", indent_string(indent+2), GET_BE_U_2(pdu_header->u.session_id)); break; case RPKI_RTR_IPV4_PREFIX_PDU: { const rpki_rtr_pdu_ipv4_prefix *pdu; if (pdu_len != sizeof(rpki_rtr_pdu) + 12) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr; ND_PRINT("%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", indent_string(indent+2), ipaddr_string(ndo, pdu->prefix), pdu->prefix_length, pdu->max_length, GET_BE_U_4(pdu->as), pdu->flags); } break; case RPKI_RTR_IPV6_PREFIX_PDU: { const rpki_rtr_pdu_ipv6_prefix *pdu; if (pdu_len != sizeof(rpki_rtr_pdu) + 24) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr; ND_PRINT("%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", indent_string(indent+2), ip6addr_string(ndo, pdu->prefix), pdu->prefix_length, pdu->max_length, GET_BE_U_4(pdu->as), pdu->flags); } break; case RPKI_RTR_ERROR_REPORT_PDU: { const rpki_rtr_pdu_error_report *pdu; u_int encapsulated_pdu_length, text_length, tlen, error_code; tlen = sizeof(rpki_rtr_pdu); /* Do not test for the "Length of Error Text" data element yet. */ if (pdu_len < tlen + 4) goto invalid; ND_TCHECK_LEN(tptr, tlen + 4); /* Safe up to and including the "Length of Encapsulated PDU" * data element, more data elements may be present. */ pdu = (const rpki_rtr_pdu_error_report *)tptr; encapsulated_pdu_length = GET_BE_U_4(pdu->encapsulated_pdu_length); tlen += 4; error_code = GET_BE_U_2(pdu->pdu_header.u.error_code); ND_PRINT("%sError code: %s (%u), Encapsulated PDU length: %u", indent_string(indent+2), tok2str(rpki_rtr_error_codes, "Unknown", error_code), error_code, encapsulated_pdu_length); if (encapsulated_pdu_length) { /* Section 5.10 of RFC 6810 says: * "An Error Report PDU MUST NOT be sent for an Error Report PDU." * * However, as far as the protocol encoding goes Error Report PDUs can * happen to be nested in each other, however many times, in which case * the decoder should still print such semantically incorrect PDUs. * * That said, "the Erroneous PDU field MAY be truncated" (ibid), thus * to keep things simple this implementation decodes only the two * outermost layers of PDUs and makes bounds checks in the outer and * the inner PDU independently. */ if (pdu_len < tlen + encapsulated_pdu_length) goto invalid; if (! recurse) { ND_TCHECK_LEN(tptr, tlen + encapsulated_pdu_length); } else { ND_PRINT("%s-----encapsulated PDU-----", indent_string(indent+4)); rpki_rtr_pdu_print(ndo, tptr + tlen, encapsulated_pdu_length, 0, indent + 2); } tlen += encapsulated_pdu_length; } if (pdu_len < tlen + 4) goto invalid; ND_TCHECK_LEN(tptr, tlen + 4); /* Safe up to and including the "Length of Error Text" data element, * one more data element may be present. */ /* * Extract, trail-zero and print the Error message. */ text_length = GET_BE_U_4(tptr + tlen); tlen += 4; if (text_length) { if (pdu_len < tlen + text_length) goto invalid; /* nd_printn() makes the bounds check */ ND_PRINT("%sError text: ", indent_string(indent+2)); if (nd_printn(ndo, tptr + tlen, text_length, ndo->ndo_snapend)) goto trunc; } } break; default: ND_TCHECK_LEN(tptr, pdu_len); /* * Unknown data, please hexdump. */ hexdump = TRUE; } /* do we also want to see a hex dump ? */ if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) { print_unknown_data(ndo,tptr,"\n\t ", pdu_len); } return pdu_len; invalid: nd_print_invalid(ndo); ND_TCHECK_LEN(tptr, len); return len; trunc: nd_print_trunc(ndo); return len; }