From fc6837ad68e9724d7c15db6cb01bf9bb5beea8e5 Mon Sep 17 00:00:00 2001
From: Donatas Abraitis <donatas@opensourcerouting.org>
Date: Tue, 21 Jan 2025 16:07:10 +0200
Subject: [PATCH] bgpd: Validate only affected RPKI prefixes instead of a full
 RIB

This is backport of https://github.com/FRRouting/frr/commit/b0800bfdf04b4fcf48504737ebfe4ba7f05268d3 for 8.4.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>

CVE: CVE-2024-55553
Upstream-Status: Backport [https://github.com/opensourcerouting/frr/commit/cc1c66a7e8dd31c681f396f6635192c0d60a543c]

The original patch is adjusted to fit for the current version.(8.2.2)

Signed-off-by: Zhang Peng <peng.zhang1.cn@windriver.com>
---
 bgpd/bgp_rpki.c | 184 +++++++++++++++++++++---------------------------
 bgpd/bgpd.c     |   4 ++
 bgpd/bgpd.h     |   1 +
 3 files changed, 87 insertions(+), 102 deletions(-)

diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 0a51269d9b..69c5f44fac 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -67,6 +67,12 @@ static struct thread *t_rpki;
 
 DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
 DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_REVALIDATE, "BGP RPKI Revalidation");
+
+#define RPKI_VALID 1
+#define RPKI_NOTFOUND 2
+#define RPKI_INVALID 3
 
 #define POLLING_PERIOD_DEFAULT 3600
 #define EXPIRE_INTERVAL_DEFAULT 7200
@@ -129,7 +135,6 @@ static enum route_map_cmd_result_t route_match(void *rule,
 					       void *object);
 static void *route_match_compile(const char *arg);
 static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi);
-static void revalidate_all_routes(void);
 
 static struct rtr_mgr_config *rtr_config;
 static struct list *cache_list;
@@ -339,10 +344,9 @@ inline int is_running(void)
 	return rtr_is_running;
 }
 
-static struct prefix *pfx_record_to_prefix(struct pfx_record *record)
+static void pfx_record_to_prefix(struct pfx_record *record,
+				 struct prefix *prefix)
 {
-	struct prefix *prefix = prefix_new();
-
 	prefix->prefixlen = record->min_len;
 
 	if (record->prefix.ver == LRTR_IPV4) {
@@ -353,75 +357,102 @@ static struct prefix *pfx_record_to_prefix(struct pfx_record *record)
 		ipv6_addr_to_network_byte_order(record->prefix.u.addr6.addr,
 						prefix->u.prefix6.s6_addr32);
 	}
-
-	return prefix;
 }
 
-static int bgpd_sync_callback(struct thread *thread)
-{
+struct rpki_revalidate_prefix {
 	struct bgp *bgp;
-	struct listnode *node;
-	struct prefix *prefix;
-	struct pfx_record rec;
-	int retval;
-	int socket = THREAD_FD(thread);
+	struct prefix prefix;
+	afi_t afi;
+	safi_t safi;
+};
 
-	thread_add_read(bm->master, bgpd_sync_callback, NULL, socket, &t_rpki);
+static void rpki_revalidate_prefix(struct thread *thread)
+{
+	struct rpki_revalidate_prefix *rrp = THREAD_ARG(thread);
+	struct bgp_dest *match, *node;
 
-	if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
-		while (read(socket, &rec, sizeof(struct pfx_record)) != -1)
-			;
+	match = bgp_table_subtree_lookup(rrp->bgp->rib[rrp->afi][rrp->safi],
+					 &rrp->prefix);
 
-		atomic_store_explicit(&rtr_update_overflow, 0,
-				      memory_order_seq_cst);
-		revalidate_all_routes();
-		return 0;
-	}
+	node = match;
 
-	retval = read(socket, &rec, sizeof(struct pfx_record));
-	if (retval != sizeof(struct pfx_record)) {
-		RPKI_DEBUG("Could not read from socket");
-		return retval;
-	}
+	while (node) {
+		if (bgp_dest_has_bgp_path_info_data(node)) {
+			revalidate_bgp_node(node, rrp->afi, rrp->safi);
+		}
 
-	/* RTR-Server crashed/terminated, let's handle and switch
-	 * to the second available RTR-Server according to preference.
-	 */
-	if (rec.socket && rec.socket->state == RTR_ERROR_FATAL) {
-		reset(true);
-		return 0;
+		node = bgp_route_next_until(node, match);
 	}
 
-	prefix = pfx_record_to_prefix(&rec);
+	XFREE(MTYPE_BGP_RPKI_REVALIDATE, rrp);
+}
 
-	afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
+static void revalidate_single_prefix(struct prefix prefix, afi_t afi)
+{
+	struct bgp *bgp;
+	struct listnode *node;
 
 	for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
 		safi_t safi;
 
 		for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
-			if (!bgp->rib[afi][safi])
+			struct bgp_table *table = bgp->rib[afi][safi];
+			struct rpki_revalidate_prefix *rrp;
+
+			if (!table)
 				continue;
 
-			struct bgp_dest *match;
-			struct bgp_dest *node;
+			rrp = XCALLOC(MTYPE_BGP_RPKI_REVALIDATE, sizeof(*rrp));
+			rrp->bgp = bgp;
+			rrp->prefix = prefix;
+			rrp->afi = afi;
+			rrp->safi = safi;
+			thread_add_event(bm->master, rpki_revalidate_prefix,
+					 rrp, 0, &bgp->t_revalidate[afi][safi]);
+		}
+	}
+}
+
+static void bgpd_sync_callback(struct thread *thread)
+{
+	struct prefix prefix;
+	struct pfx_record rec;
+	afi_t afi;
+	int retval;
+
+	if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
+		ssize_t size = 0;
 
-			match = bgp_table_subtree_lookup(bgp->rib[afi][safi],
-							 prefix);
-			node = match;
+		retval = read(rpki_sync_socket_bgpd, &rec,
+			      sizeof(struct pfx_record));
+		while (retval != -1) {
+			if (retval != sizeof(struct pfx_record))
+				break;
 
-			while (node) {
-				if (bgp_dest_has_bgp_path_info_data(node)) {
-					revalidate_bgp_node(node, afi, safi);
-				}
+			size += retval;
+			pfx_record_to_prefix(&rec, &prefix);
+			afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
+			revalidate_single_prefix(prefix, afi);
 
-				node = bgp_route_next_until(node, match);
-			}
+			retval = read(rpki_sync_socket_bgpd, &rec,
+				      sizeof(struct pfx_record));
 		}
+
+		atomic_store_explicit(&rtr_update_overflow, 0,
+				      memory_order_seq_cst);
+		return;
 	}
 
-	prefix_free(&prefix);
-	return 0;
+	retval = read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
+	if (retval != sizeof(struct pfx_record)) {
+		RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
+		return;
+	}
+	pfx_record_to_prefix(&rec, &prefix);
+
+	afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
+
+	revalidate_single_prefix(prefix, afi);
 }
 
 static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
@@ -446,63 +477,12 @@ static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
 	}
 }
 
-static void revalidate_all_routes(void)
-{
-	struct bgp *bgp;
-	struct listnode *node;
-	afi_t afi;
-	safi_t safi;
-
-	for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
-		struct peer *peer;
-		struct listnode *peer_listnode;
-
-		for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
-			FOREACH_AFI_SAFI (afi, safi) {
-				if (!peer->afc_nego[afi][safi])
-					continue;
-
-				if (!peer->bgp->rib[afi][safi])
-					continue;
-
-				bgp_soft_reconfig_in(peer, afi, safi);
-			}
-		}
-	}
-}
-
-static void rpki_connection_status_cb(const struct rtr_mgr_group *group
-				      __attribute__((unused)),
-				      enum rtr_mgr_status status,
-				      const struct rtr_socket *socket
-				      __attribute__((unused)),
-				      void *data __attribute__((unused)))
-{
-	struct pfx_record rec = {0};
-	int retval;
-
-	if (rtr_is_stopping ||
-	    atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
-		return;
-
-	if (status == RTR_MGR_ERROR)
-		rec.socket = socket;
-
-	retval = write(rpki_sync_socket_rtr, &rec, sizeof(rec));
-	if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
-		atomic_store_explicit(&rtr_update_overflow, 1,
-				      memory_order_seq_cst);
-
-	else if (retval != sizeof(rec))
-		RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
-}
-
 static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
 				    const struct pfx_record rec,
 				    const bool added __attribute__((unused)))
 {
-	if (rtr_is_stopping
-	    || atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
+	if (rtr_is_stopping ||
+	    atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
 		return;
 
 	int retval =
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 7e528b2191..bfe96f0f01 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -3579,6 +3579,10 @@ int bgp_delete(struct bgp *bgp)
 
 	hook_call(bgp_inst_delete, bgp);
 
+	THREAD_OFF(bgp->t_condition_check);
+	FOREACH_AFI_SAFI (afi, safi)
+		THREAD_OFF(bgp->t_revalidate[afi][safi]);
+
 	THREAD_OFF(bgp->t_startup);
 	THREAD_OFF(bgp->t_maxmed_onstartup);
 	THREAD_OFF(bgp->t_update_delay);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 8b93c450e8..45db4752f4 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -426,6 +426,7 @@ struct bgp {
 	/* BGP update delay on startup */
 	struct thread *t_update_delay;
 	struct thread *t_establish_wait;
+	struct thread *t_revalidate[AFI_MAX][SAFI_MAX];
 	uint8_t update_delay_over;
 	uint8_t main_zebra_update_hold;
 	uint8_t main_peers_update_hold;
-- 
2.35.5

