xref: /netbsd-src/external/bsd/tcpdump/dist/print-rpki-rtr.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
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