19546e36dSchristos /* 29546e36dSchristos * Copyright (c) 1998-2011 The TCPDUMP project 39546e36dSchristos * 49546e36dSchristos * Redistribution and use in source and binary forms, with or without 59546e36dSchristos * modification, are permitted provided that: (1) source code 69546e36dSchristos * distributions retain the above copyright notice and this paragraph 79546e36dSchristos * in its entirety, and (2) distributions including binary code include 89546e36dSchristos * the above copyright notice and this paragraph in its entirety in 99546e36dSchristos * the documentation or other materials provided with the distribution. 109546e36dSchristos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 119546e36dSchristos * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 129546e36dSchristos * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 139546e36dSchristos * FOR A PARTICULAR PURPOSE. 149546e36dSchristos * 1572c96ff3Schristos * Original code by Hannes Gredler (hannes@gredler.at) 169546e36dSchristos */ 179546e36dSchristos 18dc860a36Sspz /* \summary: Resource Public Key Infrastructure (RPKI) to Router Protocol printer */ 19dc860a36Sspz 20dc860a36Sspz /* specification: RFC 6810 */ 21dc860a36Sspz 22fdccd7e4Schristos #include <sys/cdefs.h> 23fdccd7e4Schristos #ifndef lint 24*26ba0b50Schristos __RCSID("$NetBSD: print-rpki-rtr.c,v 1.7 2024/09/02 16:15:32 christos Exp $"); 25fdccd7e4Schristos #endif 26fdccd7e4Schristos 27c74ad251Schristos #include <config.h> 289546e36dSchristos 29c74ad251Schristos #include "netdissect-stdinc.h" 309546e36dSchristos 31c74ad251Schristos #define ND_LONGJMP_FROM_TCHECK 32fdccd7e4Schristos #include "netdissect.h" 339546e36dSchristos #include "extract.h" 349546e36dSchristos #include "addrtoname.h" 359546e36dSchristos 36dc860a36Sspz 379546e36dSchristos /* 389546e36dSchristos * RPKI/Router PDU header 399546e36dSchristos * 409546e36dSchristos * Here's what the PDU header looks like. 419546e36dSchristos * The length does include the version and length fields. 429546e36dSchristos */ 439546e36dSchristos typedef struct rpki_rtr_pdu_ { 44c74ad251Schristos nd_uint8_t version; /* Version number */ 45c74ad251Schristos nd_uint8_t pdu_type; /* PDU type */ 469546e36dSchristos union { 47c74ad251Schristos nd_uint16_t session_id; /* Session id */ 48c74ad251Schristos nd_uint16_t error_code; /* Error code */ 499546e36dSchristos } u; 50c74ad251Schristos nd_uint32_t length; 519546e36dSchristos } rpki_rtr_pdu; 529546e36dSchristos 539546e36dSchristos /* 549546e36dSchristos * IPv4 Prefix PDU. 559546e36dSchristos */ 569546e36dSchristos typedef struct rpki_rtr_pdu_ipv4_prefix_ { 579546e36dSchristos rpki_rtr_pdu pdu_header; 58c74ad251Schristos nd_uint8_t flags; 59c74ad251Schristos nd_uint8_t prefix_length; 60c74ad251Schristos nd_uint8_t max_length; 61c74ad251Schristos nd_uint8_t zero; 62c74ad251Schristos nd_ipv4 prefix; 63c74ad251Schristos nd_uint32_t as; 649546e36dSchristos } rpki_rtr_pdu_ipv4_prefix; 659546e36dSchristos 669546e36dSchristos /* 679546e36dSchristos * IPv6 Prefix PDU. 689546e36dSchristos */ 699546e36dSchristos typedef struct rpki_rtr_pdu_ipv6_prefix_ { 709546e36dSchristos rpki_rtr_pdu pdu_header; 71c74ad251Schristos nd_uint8_t flags; 72c74ad251Schristos nd_uint8_t prefix_length; 73c74ad251Schristos nd_uint8_t max_length; 74c74ad251Schristos nd_uint8_t zero; 75c74ad251Schristos nd_ipv6 prefix; 76c74ad251Schristos nd_uint32_t as; 779546e36dSchristos } rpki_rtr_pdu_ipv6_prefix; 789546e36dSchristos 799546e36dSchristos /* 809546e36dSchristos * Error report PDU. 819546e36dSchristos */ 829546e36dSchristos typedef struct rpki_rtr_pdu_error_report_ { 839546e36dSchristos rpki_rtr_pdu pdu_header; 84c74ad251Schristos nd_uint32_t encapsulated_pdu_length; /* Encapsulated PDU length */ 8572c96ff3Schristos /* Copy of Erroneous PDU (variable, optional) */ 8672c96ff3Schristos /* Length of Error Text (4 octets in network byte order) */ 8772c96ff3Schristos /* Arbitrary Text of Error Diagnostic Message (variable, optional) */ 889546e36dSchristos } rpki_rtr_pdu_error_report; 899546e36dSchristos 909546e36dSchristos /* 919546e36dSchristos * PDU type codes 929546e36dSchristos */ 939546e36dSchristos #define RPKI_RTR_SERIAL_NOTIFY_PDU 0 949546e36dSchristos #define RPKI_RTR_SERIAL_QUERY_PDU 1 959546e36dSchristos #define RPKI_RTR_RESET_QUERY_PDU 2 969546e36dSchristos #define RPKI_RTR_CACHE_RESPONSE_PDU 3 979546e36dSchristos #define RPKI_RTR_IPV4_PREFIX_PDU 4 989546e36dSchristos #define RPKI_RTR_IPV6_PREFIX_PDU 6 999546e36dSchristos #define RPKI_RTR_END_OF_DATA_PDU 7 1009546e36dSchristos #define RPKI_RTR_CACHE_RESET_PDU 8 1019546e36dSchristos #define RPKI_RTR_ERROR_REPORT_PDU 10 1029546e36dSchristos 1039546e36dSchristos static const struct tok rpki_rtr_pdu_values[] = { 1049546e36dSchristos { RPKI_RTR_SERIAL_NOTIFY_PDU, "Serial Notify" }, 1059546e36dSchristos { RPKI_RTR_SERIAL_QUERY_PDU, "Serial Query" }, 1069546e36dSchristos { RPKI_RTR_RESET_QUERY_PDU, "Reset Query" }, 1079546e36dSchristos { RPKI_RTR_CACHE_RESPONSE_PDU, "Cache Response" }, 1089546e36dSchristos { RPKI_RTR_IPV4_PREFIX_PDU, "IPV4 Prefix" }, 1099546e36dSchristos { RPKI_RTR_IPV6_PREFIX_PDU, "IPV6 Prefix" }, 1109546e36dSchristos { RPKI_RTR_END_OF_DATA_PDU, "End of Data" }, 1119546e36dSchristos { RPKI_RTR_CACHE_RESET_PDU, "Cache Reset" }, 1129546e36dSchristos { RPKI_RTR_ERROR_REPORT_PDU, "Error Report" }, 1139546e36dSchristos { 0, NULL} 1149546e36dSchristos }; 1159546e36dSchristos 1169546e36dSchristos static const struct tok rpki_rtr_error_codes[] = { 1179546e36dSchristos { 0, "Corrupt Data" }, 1189546e36dSchristos { 1, "Internal Error" }, 1199546e36dSchristos { 2, "No Data Available" }, 1209546e36dSchristos { 3, "Invalid Request" }, 1219546e36dSchristos { 4, "Unsupported Protocol Version" }, 1229546e36dSchristos { 5, "Unsupported PDU Type" }, 1239546e36dSchristos { 6, "Withdrawal of Unknown Record" }, 1249546e36dSchristos { 7, "Duplicate Announcement Received" }, 1259546e36dSchristos { 0, NULL} 1269546e36dSchristos }; 1279546e36dSchristos 1289546e36dSchristos /* 1293d25ea14Schristos * Build a indentation string for a given indentation level. 1309546e36dSchristos * XXX this should be really in util.c 1319546e36dSchristos */ 1329546e36dSchristos static char * 1339546e36dSchristos indent_string (u_int indent) 1349546e36dSchristos { 1359546e36dSchristos static char buf[20]; 1369546e36dSchristos u_int idx; 1379546e36dSchristos 1389546e36dSchristos idx = 0; 1399546e36dSchristos buf[idx] = '\0'; 1409546e36dSchristos 1419546e36dSchristos /* 1429546e36dSchristos * Does the static buffer fit ? 1439546e36dSchristos */ 1449546e36dSchristos if (sizeof(buf) < ((indent/8) + (indent %8) + 2)) { 1459546e36dSchristos return buf; 1469546e36dSchristos } 1479546e36dSchristos 1489546e36dSchristos /* 1499546e36dSchristos * Heading newline. 1509546e36dSchristos */ 1519546e36dSchristos buf[idx] = '\n'; 1529546e36dSchristos idx++; 1539546e36dSchristos 1549546e36dSchristos while (indent >= 8) { 1559546e36dSchristos buf[idx] = '\t'; 1569546e36dSchristos idx++; 1579546e36dSchristos indent -= 8; 1589546e36dSchristos } 1599546e36dSchristos 1609546e36dSchristos while (indent > 0) { 1619546e36dSchristos buf[idx] = ' '; 1629546e36dSchristos idx++; 1639546e36dSchristos indent--; 1649546e36dSchristos } 1659546e36dSchristos 1669546e36dSchristos /* 1679546e36dSchristos * Trailing zero. 1689546e36dSchristos */ 1699546e36dSchristos buf[idx] = '\0'; 1709546e36dSchristos 1719546e36dSchristos return buf; 1729546e36dSchristos } 1739546e36dSchristos 1749546e36dSchristos /* 1759546e36dSchristos * Print a single PDU. 1769546e36dSchristos */ 17772c96ff3Schristos static u_int 17872c96ff3Schristos rpki_rtr_pdu_print(netdissect_options *ndo, const u_char *tptr, const u_int len, 17972c96ff3Schristos const u_char recurse, const u_int indent) 1809546e36dSchristos { 1819546e36dSchristos const rpki_rtr_pdu *pdu_header; 1829546e36dSchristos u_int pdu_type, pdu_len, hexdump; 1839546e36dSchristos const u_char *msg; 184*26ba0b50Schristos uint8_t pdu_ver; 1859546e36dSchristos 186*26ba0b50Schristos if (len < sizeof(rpki_rtr_pdu)) { 187*26ba0b50Schristos ND_PRINT("(%u bytes is too few to decode)", len); 188*26ba0b50Schristos goto invalid; 189*26ba0b50Schristos } 190*26ba0b50Schristos pdu_header = (const rpki_rtr_pdu *)tptr; 191*26ba0b50Schristos pdu_ver = GET_U_1(pdu_header->version); 192*26ba0b50Schristos if (pdu_ver != 0) { 19372c96ff3Schristos /* Skip the rest of the input buffer because even if this is 19472c96ff3Schristos * a well-formed PDU of a future RPKI-Router protocol version 19572c96ff3Schristos * followed by a well-formed PDU of RPKI-Router protocol 19672c96ff3Schristos * version 0, there is no way to know exactly how to skip the 19772c96ff3Schristos * current PDU. 19872c96ff3Schristos */ 199*26ba0b50Schristos ND_PRINT("%sRPKI-RTRv%u (unknown)", indent_string(8), pdu_ver); 20072c96ff3Schristos return len; 20172c96ff3Schristos } 202c74ad251Schristos pdu_type = GET_U_1(pdu_header->pdu_type); 203c74ad251Schristos pdu_len = GET_BE_U_4(pdu_header->length); 20472c96ff3Schristos /* Do not check bounds with pdu_len yet, do it in the case blocks 20572c96ff3Schristos * below to make it possible to decode at least the beginning of 20672c96ff3Schristos * a truncated Error Report PDU or a truncated encapsulated PDU. 20772c96ff3Schristos */ 2089546e36dSchristos hexdump = FALSE; 2099546e36dSchristos 210c74ad251Schristos ND_PRINT("%sRPKI-RTRv%u, %s PDU (%u), length: %u", 2119546e36dSchristos indent_string(8), 212*26ba0b50Schristos pdu_ver, 2139546e36dSchristos tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type), 214c74ad251Schristos pdu_type, pdu_len); 21572c96ff3Schristos if (pdu_len < sizeof(rpki_rtr_pdu) || pdu_len > len) 21672c96ff3Schristos goto invalid; 2179546e36dSchristos 2189546e36dSchristos switch (pdu_type) { 2199546e36dSchristos 2209546e36dSchristos /* 2219546e36dSchristos * The following PDUs share the message format. 2229546e36dSchristos */ 2239546e36dSchristos case RPKI_RTR_SERIAL_NOTIFY_PDU: 2249546e36dSchristos case RPKI_RTR_SERIAL_QUERY_PDU: 2259546e36dSchristos case RPKI_RTR_END_OF_DATA_PDU: 22672c96ff3Schristos if (pdu_len != sizeof(rpki_rtr_pdu) + 4) 22772c96ff3Schristos goto invalid; 2289546e36dSchristos msg = (const u_char *)(pdu_header + 1); 229c74ad251Schristos ND_PRINT("%sSession ID: 0x%04x, Serial: %u", 2309546e36dSchristos indent_string(indent+2), 231c74ad251Schristos GET_BE_U_2(pdu_header->u.session_id), 232c74ad251Schristos GET_BE_U_4(msg)); 2339546e36dSchristos break; 2349546e36dSchristos 2359546e36dSchristos /* 2369546e36dSchristos * The following PDUs share the message format. 2379546e36dSchristos */ 2389546e36dSchristos case RPKI_RTR_RESET_QUERY_PDU: 2399546e36dSchristos case RPKI_RTR_CACHE_RESET_PDU: 24072c96ff3Schristos if (pdu_len != sizeof(rpki_rtr_pdu)) 24172c96ff3Schristos goto invalid; 24272c96ff3Schristos /* no additional boundary to check */ 2439546e36dSchristos 2449546e36dSchristos /* 2459546e36dSchristos * Zero payload PDUs. 2469546e36dSchristos */ 2479546e36dSchristos break; 2489546e36dSchristos 2499546e36dSchristos case RPKI_RTR_CACHE_RESPONSE_PDU: 25072c96ff3Schristos if (pdu_len != sizeof(rpki_rtr_pdu)) 25172c96ff3Schristos goto invalid; 25272c96ff3Schristos /* no additional boundary to check */ 253c74ad251Schristos ND_PRINT("%sSession ID: 0x%04x", 2549546e36dSchristos indent_string(indent+2), 255c74ad251Schristos GET_BE_U_2(pdu_header->u.session_id)); 2569546e36dSchristos break; 2579546e36dSchristos 2589546e36dSchristos case RPKI_RTR_IPV4_PREFIX_PDU: 2599546e36dSchristos { 260fdccd7e4Schristos const rpki_rtr_pdu_ipv4_prefix *pdu; 2619546e36dSchristos 262c74ad251Schristos if (pdu_len != sizeof(rpki_rtr_pdu_ipv4_prefix)) 26372c96ff3Schristos goto invalid; 264fdccd7e4Schristos pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr; 265c74ad251Schristos ND_PRINT("%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", 2669546e36dSchristos indent_string(indent+2), 267c74ad251Schristos GET_IPADDR_STRING(pdu->prefix), 268c74ad251Schristos GET_U_1(pdu->prefix_length), GET_U_1(pdu->max_length), 269c74ad251Schristos GET_BE_U_4(pdu->as), GET_U_1(pdu->flags)); 2709546e36dSchristos } 2719546e36dSchristos break; 2729546e36dSchristos 2739546e36dSchristos case RPKI_RTR_IPV6_PREFIX_PDU: 2749546e36dSchristos { 275fdccd7e4Schristos const rpki_rtr_pdu_ipv6_prefix *pdu; 2769546e36dSchristos 277c74ad251Schristos if (pdu_len != sizeof(rpki_rtr_pdu_ipv6_prefix)) 27872c96ff3Schristos goto invalid; 279fdccd7e4Schristos pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr; 280c74ad251Schristos ND_PRINT("%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", 2819546e36dSchristos indent_string(indent+2), 282c74ad251Schristos GET_IP6ADDR_STRING(pdu->prefix), 283c74ad251Schristos GET_U_1(pdu->prefix_length), GET_U_1(pdu->max_length), 284c74ad251Schristos GET_BE_U_4(pdu->as), GET_U_1(pdu->flags)); 2859546e36dSchristos } 2869546e36dSchristos break; 2879546e36dSchristos 2889546e36dSchristos case RPKI_RTR_ERROR_REPORT_PDU: 2899546e36dSchristos { 290fdccd7e4Schristos const rpki_rtr_pdu_error_report *pdu; 2919546e36dSchristos u_int encapsulated_pdu_length, text_length, tlen, error_code; 2929546e36dSchristos 29372c96ff3Schristos tlen = sizeof(rpki_rtr_pdu); 29472c96ff3Schristos /* Do not test for the "Length of Error Text" data element yet. */ 29572c96ff3Schristos if (pdu_len < tlen + 4) 29672c96ff3Schristos goto invalid; 297fdccd7e4Schristos pdu = (const rpki_rtr_pdu_error_report *)tptr; 298c74ad251Schristos encapsulated_pdu_length = GET_BE_U_4(pdu->encapsulated_pdu_length); 29972c96ff3Schristos tlen += 4; 300*26ba0b50Schristos /* Safe up to and including the "Length of Encapsulated PDU" 301*26ba0b50Schristos * data element, more data elements may be present. 302*26ba0b50Schristos */ 3039546e36dSchristos 304c74ad251Schristos error_code = GET_BE_U_2(pdu->pdu_header.u.error_code); 305c74ad251Schristos ND_PRINT("%sError code: %s (%u), Encapsulated PDU length: %u", 3069546e36dSchristos indent_string(indent+2), 3079546e36dSchristos tok2str(rpki_rtr_error_codes, "Unknown", error_code), 308c74ad251Schristos error_code, encapsulated_pdu_length); 3099546e36dSchristos 31072c96ff3Schristos if (encapsulated_pdu_length) { 31172c96ff3Schristos /* Section 5.10 of RFC 6810 says: 31272c96ff3Schristos * "An Error Report PDU MUST NOT be sent for an Error Report PDU." 31372c96ff3Schristos * 31472c96ff3Schristos * However, as far as the protocol encoding goes Error Report PDUs can 31572c96ff3Schristos * happen to be nested in each other, however many times, in which case 31672c96ff3Schristos * the decoder should still print such semantically incorrect PDUs. 31772c96ff3Schristos * 31872c96ff3Schristos * That said, "the Erroneous PDU field MAY be truncated" (ibid), thus 31972c96ff3Schristos * to keep things simple this implementation decodes only the two 32072c96ff3Schristos * outermost layers of PDUs and makes bounds checks in the outer and 32172c96ff3Schristos * the inner PDU independently. 3229546e36dSchristos */ 32372c96ff3Schristos if (pdu_len < tlen + encapsulated_pdu_length) 32472c96ff3Schristos goto invalid; 32572c96ff3Schristos if (! recurse) { 326c74ad251Schristos ND_TCHECK_LEN(tptr, tlen + encapsulated_pdu_length); 327*26ba0b50Schristos } else { 328c74ad251Schristos ND_PRINT("%s-----encapsulated PDU-----", indent_string(indent+4)); 32972c96ff3Schristos rpki_rtr_pdu_print(ndo, tptr + tlen, 33072c96ff3Schristos encapsulated_pdu_length, 0, indent + 2); 33172c96ff3Schristos } 33272c96ff3Schristos tlen += encapsulated_pdu_length; 3339546e36dSchristos } 3349546e36dSchristos 33572c96ff3Schristos if (pdu_len < tlen + 4) 33672c96ff3Schristos goto invalid; 3379546e36dSchristos /* 3389546e36dSchristos * Extract, trail-zero and print the Error message. 3399546e36dSchristos */ 340c74ad251Schristos text_length = GET_BE_U_4(tptr + tlen); 34172c96ff3Schristos tlen += 4; 342*26ba0b50Schristos /* Safe up to and including the "Length of Error Text" data element, 343*26ba0b50Schristos * one more data element may be present. 344*26ba0b50Schristos */ 34572c96ff3Schristos 34672c96ff3Schristos if (text_length) { 34772c96ff3Schristos if (pdu_len < tlen + text_length) 34872c96ff3Schristos goto invalid; 349c74ad251Schristos /* nd_printn() makes the bounds check */ 350c74ad251Schristos ND_PRINT("%sError text: ", indent_string(indent+2)); 351c74ad251Schristos (void)nd_printn(ndo, tptr + tlen, text_length, NULL); 3529546e36dSchristos } 3539546e36dSchristos } 3549546e36dSchristos break; 3559546e36dSchristos 3569546e36dSchristos default: 357c74ad251Schristos ND_TCHECK_LEN(tptr, pdu_len); 3589546e36dSchristos 3599546e36dSchristos /* 3609546e36dSchristos * Unknown data, please hexdump. 3619546e36dSchristos */ 3629546e36dSchristos hexdump = TRUE; 3639546e36dSchristos } 3649546e36dSchristos 3659546e36dSchristos /* do we also want to see a hex dump ? */ 366c47fd378Schristos if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) { 367c47fd378Schristos print_unknown_data(ndo,tptr,"\n\t ", pdu_len); 3689546e36dSchristos } 36972c96ff3Schristos return pdu_len; 3703d25ea14Schristos 37172c96ff3Schristos invalid: 372c74ad251Schristos nd_print_invalid(ndo); 373c74ad251Schristos ND_TCHECK_LEN(tptr, len); 37472c96ff3Schristos return len; 3759546e36dSchristos } 3769546e36dSchristos 3779546e36dSchristos void 378c74ad251Schristos rpki_rtr_print(netdissect_options *ndo, const u_char *pptr, u_int len) 3793d25ea14Schristos { 380c74ad251Schristos ndo->ndo_protocol = "rpki_rtr"; 381c47fd378Schristos if (!ndo->ndo_vflag) { 382c74ad251Schristos ND_PRINT(", RPKI-RTR"); 3839546e36dSchristos return; 3849546e36dSchristos } 38572c96ff3Schristos while (len) { 38672c96ff3Schristos u_int pdu_len = rpki_rtr_pdu_print(ndo, pptr, len, 1, 8); 38772c96ff3Schristos len -= pdu_len; 38872c96ff3Schristos pptr += pdu_len; 3899546e36dSchristos } 3909546e36dSchristos } 391