git: 4516e09a236b - main - net/bird3: Add new branch 3.x (multithreaded)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 09 Jan 2025 22:08:14 UTC
The branch main has been updated by olivier:
URL: https://cgit.FreeBSD.org/ports/commit/?id=4516e09a236bb31d6e852eadfb05f9576e4db7da
commit 4516e09a236bb31d6e852eadfb05f9576e4db7da
Author: Olivier Cochard <olivier@FreeBSD.org>
AuthorDate: 2025-01-09 21:58:36 +0000
Commit: Olivier Cochard <olivier@FreeBSD.org>
CommitDate: 2025-01-09 22:07:38 +0000
net/bird3: Add new branch 3.x (multithreaded)
Warning: Consider version 3.0.0 to be unstable.
PR: 283403
Sponsored by: Netflix
---
net/Makefile | 1 +
net/bird2/Makefile | 3 +-
net/bird3/Makefile | 62 ++++
net/bird3/distinfo | 3 +
net/bird3/files/bird.in | 30 ++
.../patch-00-kernel-Fix-crash-for-merge-paths | 38 ++
net/bird3/files/patch-01-Table-not-feeding-twice | 39 ++
.../files/patch-02-kernel-trace-the-final-result | 53 +++
net/bird3/files/patch-03-BGP-fix-locking-order | 176 +++++++++
.../files/patch-04-BFD-Fix-session-locking-order | 400 +++++++++++++++++++++
.../files/patch-05-mainloop-dropped-old-socket | 86 +++++
net/bird3/files/patch-06-cli-allocate-tx-buffers | 134 +++++++
net/bird3/files/patch-07-cli-flushing-tmp-linpool | 29 ++
net/bird3/files/patch-08-kernel-feed-only-once | 274 ++++++++++++++
net/bird3/files/patch-09-graceful-recovery | 311 ++++++++++++++++
net/bird3/files/patch-10-stonehenge | 116 ++++++
net/bird3/files/patch-11-route-attribute-storage | 80 +++++
net/bird3/files/patch-12-BGP-tx-bucket-storage | 84 +++++
.../files/patch-13-allocate-normalization-buckets | 100 ++++++
net/bird3/files/patch-14-BGP-fix-dislpay-name | 25 ++
.../patch-15-BGP-fixed-deterministic-med-crashes | 65 ++++
.../files/patch-16-Table-old-best-route-refeed-fix | 87 +++++
net/bird3/files/patch-Makefile.in | 11 +
net/bird3/files/pkg-message.in | 11 +
net/bird3/pkg-descr | 14 +
net/bird3/pkg-plist | 4 +
26 files changed, 2234 insertions(+), 2 deletions(-)
diff --git a/net/Makefile b/net/Makefile
index 151a98fa8881..9093da9bdb0b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -57,6 +57,7 @@
SUBDIR += bindtest
SUBDIR += binkd
SUBDIR += bird2
+ SUBDIR += bird3
SUBDIR += bittwist
SUBDIR += bmon
SUBDIR += boinc-client
diff --git a/net/bird2/Makefile b/net/bird2/Makefile
index 7a6d87747d6f..3a5dc86df54a 100644
--- a/net/bird2/Makefile
+++ b/net/bird2/Makefile
@@ -15,8 +15,7 @@ rtsock_PKGNAMESUFFIX= 2-rtsock
USES= bison cpe gmake ncurses readline
-CONFLICTS= bird
-CONFLICTS+= bird6
+CONFLICTS= bird3
CPE_VENDOR= nic
diff --git a/net/bird3/Makefile b/net/bird3/Makefile
new file mode 100644
index 000000000000..8c27bcafdc41
--- /dev/null
+++ b/net/bird3/Makefile
@@ -0,0 +1,62 @@
+PORTNAME= bird
+DISTVERSION= 3.0.0
+CATEGORIES= net
+MASTER_SITES= https://bird.network.cz/download/
+PKGNAMESUFFIX= 3
+
+MAINTAINER= olivier@FreeBSD.org
+COMMENT= Dynamic multithreaded IP routing daemon
+WWW= https://bird.network.cz/
+
+LICENSE= GPLv2
+
+USES= bison cpe gmake ncurses readline
+
+CONFLICTS= bird2
+
+CPE_VENDOR= nic
+
+USE_CSTD= gnu99
+GNU_CONFIGURE= yes
+CONFIGURE_ARGS= --localstatedir=/var
+USE_RC_SUBR= bird
+SUB_FILES= pkg-message
+
+GROUPS= birdvty
+
+MAKE_JOBS_UNSAFE= yes
+
+OPTIONS_MULTI= RP
+RP_DESC= Routing Protocols
+OPTIONS_MULTI_RP= BFD BABEL BMP BGP MRT OSPF PIPE RADV RIP RPKI STATIC
+OPTIONS_DEFAULT= BFD BABEL BGP MRT OSPF PIPE RADV RIP RPKI STATIC
+
+BFD_DESC= Bidirectional Forwarding Detection
+BABEL_DESC= Babel routing protocol
+BGP_DESC= Border Gateway Protocol
+BMP_DESC= BGP Monitoring Protocol
+MRT_DESC= Dumping Routing Information in MRT Format
+OSPF_DESC= Open Short Path First
+PIPE_DESC= PIPE routing
+RADV_DESC= Router Advertisement
+RIP_DESC= Routing Information Protocol
+RPKI_DESC= Resource Public Key Infrastructure
+STATIC_DESC= Static routing
+
+BFD_VARS= rt_prot+=bfd
+BABEL_VARS= rt_prot+=babel
+BGP_VARS= rt_prot+=bgp
+BMP_VARS= rt_prot+=bmp
+MRT_VARS= rt_prot+=mrt
+OSPF_VARS= rt_prot+=ospf
+PIPE_VARS= rt_prot+=pipe
+RADV_VARS= rt_prot+=radv
+RIP_VARS= rt_prot+=rip
+RPKI_VARS= rt_prot+=rpki
+STATIC_VARS= rt_prot+=static
+
+CONFIGURE_ARGS+=--with-protocols="${RT_PROT}"
+CONFIGURE_ARGS+=--with-sysconfig=bsd-netlink
+RPKI_LIB_DEPENDS= libssh.so:security/libssh
+
+.include <bsd.port.mk>
diff --git a/net/bird3/distinfo b/net/bird3/distinfo
new file mode 100644
index 000000000000..66fda3f7d35c
--- /dev/null
+++ b/net/bird3/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1734554961
+SHA256 (bird-3.0.0.tar.gz) = 8130440a2e273ba6456df2fb3acb43da7cb4d566f94a294a3a52a1b118f2512a
+SIZE (bird-3.0.0.tar.gz) = 2641569
diff --git a/net/bird3/files/bird.in b/net/bird3/files/bird.in
new file mode 100644
index 000000000000..de800bd69b81
--- /dev/null
+++ b/net/bird3/files/bird.in
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# PROVIDE: bird dynamicrouting
+# REQUIRE: LOGIN
+# KEYWORD: shutdown
+#
+# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
+# to enable this service:
+#
+# bird_enable (bool): Set to NO by default.
+# Set it to YES to enable bird.
+# bird_config (path): Set to %%PREFIX%%/etc/bird.conf
+# by default.
+#
+
+. /etc/rc.subr
+
+name="bird"
+rcvar=bird_enable
+
+load_rc_config $name
+
+: ${bird_enable="NO"}
+: ${bird_config="%%PREFIX%%/etc/bird.conf"}
+: ${bird_group="birdvty"}
+
+command=%%PREFIX%%/sbin/${name}
+command_args="-c $bird_config -g $bird_group"
+
+run_rc_command "$1"
diff --git a/net/bird3/files/patch-00-kernel-Fix-crash-for-merge-paths b/net/bird3/files/patch-00-kernel-Fix-crash-for-merge-paths
new file mode 100644
index 000000000000..d008d4cf070c
--- /dev/null
+++ b/net/bird3/files/patch-00-kernel-Fix-crash-for-merge-paths
@@ -0,0 +1,38 @@
+From b6caccfd45fb639b6dd3a8d140d3c5ba4cc79311 Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Thu, 19 Dec 2024 11:00:15 +0100
+Subject: [PATCH] Kernel: Fix crash for merge paths on if no route is in BIRD
+
+There was a missing check for a NULL return value.
+Also fixed an indenting error.
+
+Thanks to Radu Anghel for reporting it:
+https://bird.network.cz/pipermail/bird-users/2024-December/017977.html
+---
+ nest/rt-table.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/nest/rt-table.c b/nest/rt-table.c
+index fd8bb50dd..05191d743 100644
+--- nest/rt-table.c
++++ nest/rt-table.c
+@@ -5265,14 +5265,14 @@ krt_export_net(struct channel *c, const net_addr *a, linpool *lp)
+ if (c->ra_mode == RA_MERGED)
+ {
+ struct rt_export_feed *feed = rt_net_feed(c->table, a, NULL);
+- if (!feed->count_routes)
++ if (!feed || !feed->count_routes)
+ return NULL;
+
+ if (!bmap_test(&c->export_accepted_map, feed->block[0].id))
+ return NULL;
+
+ return rt_export_merged(c, feed, lp, 1);
+- }
++ }
+
+ static _Thread_local rte best;
+ best = rt_net_best(c->table, a);
+--
+GitLab
+
diff --git a/net/bird3/files/patch-01-Table-not-feeding-twice b/net/bird3/files/patch-01-Table-not-feeding-twice
new file mode 100644
index 000000000000..4fb40a644fb2
--- /dev/null
+++ b/net/bird3/files/patch-01-Table-not-feeding-twice
@@ -0,0 +1,39 @@
+From 0a2f92ad205d96d0be0945ecf2bb740b68d5a3c1 Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Thu, 19 Dec 2024 11:54:05 +0100
+Subject: [PATCH] Table: not feeding twice, once is enough
+
+If there is no feed pending, the requested one should be
+activated immediately, otherwise it is activated only after
+the full run, effectively running first a full feed and
+then the requested one.
+---
+ nest/rt-export.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/nest/rt-export.c b/nest/rt-export.c
+index 7368447de..7d51e54cf 100644
+--- nest/rt-export.c
++++ nest/rt-export.c
+@@ -357,8 +357,16 @@ rt_export_refeed_feeder(struct rt_export_feeder *f, struct rt_feeding_request *r
+ if (!rfr)
+ return;
+
+- rfr->next = f->feed_pending;
+- f->feed_pending = rfr;
++ if (f->feeding)
++ {
++ rfr->next = f->feed_pending;
++ f->feed_pending = rfr;
++ }
++ else
++ {
++ rfr->next = NULL;
++ f->feeding = rfr;
++ }
+ }
+
+ void rt_export_refeed_request(struct rt_export_request *rer, struct rt_feeding_request *rfr)
+--
+GitLab
+
diff --git a/net/bird3/files/patch-02-kernel-trace-the-final-result b/net/bird3/files/patch-02-kernel-trace-the-final-result
new file mode 100644
index 000000000000..a3c97320f30e
--- /dev/null
+++ b/net/bird3/files/patch-02-kernel-trace-the-final-result
@@ -0,0 +1,53 @@
+From ab74652f96c301dd2d2d2a831dd1a159ae1d5e02 Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Thu, 19 Dec 2024 12:28:27 +0100
+Subject: [PATCH] Kernel: when channel traces, we have to trace the final
+ result
+
+Otherwise it looks like we are sending too much traffic to netlink
+every other while, which is not true. Now we can disambiguate between
+in-kernel updates and ignored routes.
+---
+ sysdep/unix/krt.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
+index 2770b8be2..34882b88f 100644
+--- sysdep/unix/krt.c
++++ sysdep/unix/krt.c
+@@ -672,7 +672,7 @@ krt_preexport(struct channel *C, rte *e)
+ }
+
+ static void
+-krt_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *net,
++krt_rt_notify(struct proto *P, struct channel *ch, const net_addr *net,
+ rte *new, const rte *old)
+ {
+ struct krt_proto *p = (struct krt_proto *) P;
+@@ -688,13 +688,21 @@ krt_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *net,
+ case KPS_IDLE:
+ case KPS_PRUNING:
+ if (new && bmap_test(&p->seen_map, new->id))
+- /* Already installed and seen in the kernel dump */
+- return;
++ if (ch->debug & D_ROUTES)
++ {
++ /* Already installed and seen in the kernel dump */
++ log(L_TRACE "%s.%s: %N already in kernel",
++ P->name, ch->name, net);
++ return;
++ }
+
+ /* fall through */
+ case KPS_SCANNING:
+ /* Actually replace the route */
+ krt_replace_rte(p, net, new, old);
++ if (ch->debug & D_ROUTES)
++ log(L_TRACE "%s.%s: %N %s kernel",
++ P->name, ch->name, net, old ? "replaced in" : "added to");
+ break;
+
+ }
+--
+GitLab
+
diff --git a/net/bird3/files/patch-03-BGP-fix-locking-order b/net/bird3/files/patch-03-BGP-fix-locking-order
new file mode 100644
index 000000000000..51b73c26f8f8
--- /dev/null
+++ b/net/bird3/files/patch-03-BGP-fix-locking-order
@@ -0,0 +1,176 @@
+From 6779e5da698feb9b9e02411859ad81885ba46c01 Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Fri, 20 Dec 2024 11:28:00 +0100
+Subject: [PATCH] BGP: fix locking order error on dynamic protocol spawn
+
+We missed that the protocol spawner violates the prescribed
+locking order. When the rtable level is locked, no new protocol can be
+started, thus we need to:
+
+* create the protocol from a clean mainloop context
+* in protocol start hook, take the socket
+
+Testsuite: cf-bgp-autopeer
+Fixes: #136
+
+Thanks to Job Snijders <job@fastly.com> for reporting:
+https://trubka.network.cz/pipermail/bird-users/2024-December/017980.html
+---
+ nest/proto.c | 19 +++++++++++++++++++
+ nest/protocol.h | 2 ++
+ proto/bgp/bgp.c | 46 +++++++++++++++++++++++++++++++++++-----------
+ 3 files changed, 56 insertions(+), 11 deletions(-)
+
+diff --git a/nest/proto.c b/nest/proto.c
+index dded84f51..678697d69 100644
+--- nest/proto.c
++++ nest/proto.c
+@@ -1867,6 +1867,25 @@ proto_spawn(struct proto_config *cf, uint disabled)
+ return p;
+ }
+
++bool
++proto_disable(struct proto *p)
++{
++ ASSERT_DIE(birdloop_inside(&main_birdloop));
++ bool changed = !p->disabled;
++ p->disabled = 1;
++ proto_rethink_goal(p);
++ return changed;
++}
++
++bool
++proto_enable(struct proto *p)
++{
++ ASSERT_DIE(birdloop_inside(&main_birdloop));
++ bool changed = p->disabled;
++ p->disabled = 0;
++ proto_rethink_goal(p);
++ return changed;
++}
+
+ /**
+ * DOC: Graceful restart recovery
+diff --git a/nest/protocol.h b/nest/protocol.h
+index 25ed6f553..cf7ecb898 100644
+--- nest/protocol.h
++++ nest/protocol.h
+@@ -78,6 +78,8 @@ void proto_build(struct protocol *); /* Called from protocol to register itself
+ void protos_preconfig(struct config *);
+ void protos_commit(struct config *new, struct config *old, int type);
+ struct proto * proto_spawn(struct proto_config *cf, uint disabled);
++bool proto_disable(struct proto *p);
++bool proto_enable(struct proto *p);
+ void protos_dump_all(struct dump_request *);
+
+ #define GA_UNKNOWN 0 /* Attribute not recognized */
+diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
+index 5fc2b5fff..3170e3a42 100644
+--- proto/bgp/bgp.c
++++ proto/bgp/bgp.c
+@@ -378,8 +378,6 @@ bgp_startup(struct bgp_proto *p)
+ if (p->postponed_sk)
+ {
+ /* Apply postponed incoming connection */
+- sk_reloop(p->postponed_sk, p->p.loop);
+-
+ bgp_setup_conn(p, &p->incoming_conn);
+ bgp_setup_sk(&p->incoming_conn, p->postponed_sk);
+ bgp_send_open(&p->incoming_conn);
+@@ -583,6 +581,9 @@ bgp_graceful_close_conn(struct bgp_conn *conn, int subcode, byte *data, uint len
+ static void
+ bgp_down(struct bgp_proto *p)
+ {
++ /* Check that the dynamic BGP socket has been picked up */
++ ASSERT_DIE(p->postponed_sk == NULL);
++
+ if (bgp_start_state(p) > BSS_PREPARE)
+ {
+ bgp_setup_auth(p, 0);
+@@ -617,8 +618,8 @@ bgp_decision(void *vp)
+ bgp_down(p);
+ }
+
+-static struct bgp_proto *
+-bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
++static void
++bgp_spawn(struct bgp_proto *pp, struct birdsock *sk)
+ {
+ struct symbol *sym;
+ char fmt[SYM_MAX_LEN];
+@@ -635,9 +636,16 @@ bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
+ cfg_mem = NULL;
+
+ /* Just pass remote_ip to bgp_init() */
+- ((struct bgp_config *) sym->proto)->remote_ip = remote_ip;
++ ((struct bgp_config *) sym->proto)->remote_ip = sk->daddr;
++
++ /* Create the protocol disabled initially */
++ SKIP_BACK_DECLARE(struct bgp_proto, p, p, proto_spawn(sym->proto, 1));
+
+- return (void *) proto_spawn(sym->proto, 0);
++ /* Pass the socket */
++ p->postponed_sk = sk;
++
++ /* And enable the protocol */
++ proto_enable(&p->p);
+ }
+
+ void
+@@ -1489,10 +1497,15 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
+ /* For dynamic BGP, spawn new instance and postpone the socket */
+ if (bgp_is_dynamic(p))
+ {
+- p = bgp_spawn(p, sk->daddr);
+- p->postponed_sk = sk;
+- rmove(sk, p->p.pool);
+- goto leave;
++ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
++
++ /* The dynamic protocol must be in the START state */
++ ASSERT_DIE(p->p.proto_state == PS_START);
++ birdloop_leave(p->p.loop);
++
++ /* Now we have a clean mainloop */
++ bgp_spawn(p, sk);
++ return 0;
+ }
+
+ rmove(sk, p->p.pool);
+@@ -1806,7 +1819,6 @@ bgp_start(struct proto *P)
+ p->incoming_conn.state = BS_IDLE;
+ p->neigh = NULL;
+ p->bfd_req = NULL;
+- p->postponed_sk = NULL;
+ p->gr_ready = 0;
+ p->gr_active_num = 0;
+
+@@ -1848,6 +1860,16 @@ bgp_start(struct proto *P)
+ channel_graceful_restart_lock(&c->c);
+ }
+
++ /* Now it's the last chance to move the postponed socket to this BGP,
++ * as bgp_start is the only hook running from main loop. */
++ if (p->postponed_sk)
++ {
++ LOCK_DOMAIN(rtable, bgp_listen_domain);
++ rmove(p->postponed_sk, p->p.pool);
++ sk_reloop(p->postponed_sk, p->p.loop);
++ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
++ }
++
+ /*
+ * Before attempting to create the connection, we need to lock the port,
+ * so that we are the only instance attempting to talk with that neighbor.
+@@ -1999,6 +2021,8 @@ bgp_init(struct proto_config *CF)
+ p->remote_ip = cf->remote_ip;
+ p->remote_as = cf->remote_as;
+
++ p->postponed_sk = NULL;
++
+ /* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
+ if (cf->c.parent)
+ cf->remote_ip = IPA_NONE;
+--
+GitLab
+
diff --git a/net/bird3/files/patch-04-BFD-Fix-session-locking-order b/net/bird3/files/patch-04-BFD-Fix-session-locking-order
new file mode 100644
index 000000000000..3f5500500691
--- /dev/null
+++ b/net/bird3/files/patch-04-BFD-Fix-session-locking-order
@@ -0,0 +1,400 @@
+From 83495362789d961914c4bfaa590e31cb17370ed0 Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Sat, 21 Dec 2024 19:02:22 +0100
+Subject: [PATCH] BFD: Fix session reconfiguration locking order
+
+The sessions have to be updated asynchronously to avoid
+cross-locking between protocols.
+
+Testsuite: cf-ibgp-bfd-switch, cf-ibgp-multi-bfd-auth
+Fixes: #139
+
+Thanks to Daniel Suchy <danny@danysek.cz> for reporting:
+https://trubka.network.cz/pipermail/bird-users/2024-December/017984.html
+---
+ nest/bfd.h | 7 ++-
+ proto/bfd/bfd.c | 144 +++++++++++++++++++++++---------------------
+ proto/bfd/bfd.h | 21 +------
+ proto/bfd/config.Y | 42 +++++--------
+ proto/bfd/packets.c | 4 +-
+ 5 files changed, 98 insertions(+), 120 deletions(-)
+
+diff --git a/nest/bfd.h b/nest/bfd.h
+index 5dacff5d7..c046152f8 100644
+--- nest/bfd.h
++++ nest/bfd.h
+@@ -18,8 +18,11 @@ struct bfd_options {
+ u32 min_tx_int;
+ u32 idle_tx_int;
+ u8 multiplier;
+- u8 passive;
+- u8 passive_set;
++ PACKED enum bfd_opt_passive {
++ BFD_OPT_PASSIVE_UNKNOWN = 0,
++ BFD_OPT_PASSIVE,
++ BFD_OPT_NOT_PASSIVE,
++ } passive;
+ u8 mode;
+ u8 auth_type; /* Authentication type (BFD_AUTH_*) */
+ list *passwords; /* Passwords for authentication */
+diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
+index 34f992b93..4997f803a 100644
+--- proto/bfd/bfd.c
++++ proto/bfd/bfd.c
+@@ -172,17 +172,17 @@ static void bfd_free_iface(struct bfd_iface *ifa);
+ * BFD sessions
+ */
+
+-static inline struct bfd_session_config
+-bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *opts)
++static inline struct bfd_options
++bfd_merge_options(const struct bfd_options *bottom, const struct bfd_options *top)
+ {
+- return (struct bfd_session_config) {
+- .min_rx_int = opts->min_rx_int ?: cf->min_rx_int,
+- .min_tx_int = opts->min_tx_int ?: cf->min_tx_int,
+- .idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
+- .multiplier = opts->multiplier ?: cf->multiplier,
+- .passive = opts->passive_set ? opts->passive : cf->passive,
+- .auth_type = opts->auth_type ?: cf->auth_type,
+- .passwords = opts->passwords ?: cf->passwords,
++ return (struct bfd_options) {
++ .min_rx_int = top->min_rx_int ?: bottom->min_rx_int,
++ .min_tx_int = top->min_tx_int ?: bottom->min_tx_int,
++ .idle_tx_int = top->idle_tx_int ?: bottom->idle_tx_int,
++ .multiplier = top->multiplier ?: bottom->multiplier,
++ .passive = top->passive ?: bottom->passive,
++ .auth_type = top->auth_type ?: bottom->auth_type,
++ .passwords = top->passwords ?: bottom->passwords,
+ };
+ }
+
+@@ -478,7 +478,7 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
+ HASH_INSERT(p->session_hash_id, HASH_ID, s);
+ HASH_INSERT(p->session_hash_ip, HASH_IP, s);
+
+- s->cf = bfd_merge_options(ifa->cf, opts);
++ s->cf = bfd_merge_options(&ifa->cf->opts, opts);
+
+ /* Initialization of state variables - see RFC 5880 6.8.1 */
+ s->loc_state = BFD_STATE_DOWN;
+@@ -561,26 +561,58 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
+ birdloop_leave(p->p.loop);
+ }
+
++struct bfd_reconfigure_sessions_deferred_call {
++ struct deferred_call dc;
++ struct bfd_proto *p;
++ config_ref old_config;
++};
++
+ static void
+-bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
++bfd_reconfigure_sessions(struct deferred_call *dc)
+ {
+- if (EMPTY_LIST(s->request_list))
+- return;
++ SKIP_BACK_DECLARE(struct bfd_reconfigure_sessions_deferred_call,
++ brsdc, dc, dc);
+
+- ASSERT_DIE(birdloop_inside(p->p.loop));
++ struct bfd_proto *p = brsdc->p;
++ birdloop_enter(p->p.loop);
+
+- SKIP_BACK_DECLARE(struct bfd_request, req, n, HEAD(s->request_list));
+- s->cf = bfd_merge_options(s->ifa->cf, &req->opts);
++ HASH_WALK(p->session_hash_id, next_id, s)
++ {
++ if (!EMPTY_LIST(s->request_list))
++ {
++ SKIP_BACK_DECLARE(struct bfd_request, req, n, HEAD(s->request_list));
++ struct bfd_options opts = bfd_merge_options(&s->ifa->cf->opts, &req->opts);
+
+- u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
+- bfd_session_set_min_tx(s, tx);
+- bfd_session_set_min_rx(s, s->cf.min_rx_int);
+- s->detect_mult = s->cf.multiplier;
+- s->passive = s->cf.passive;
++#define CHK(x) (opts.x != s->cf.x) ||
++ bool reload = MACRO_FOREACH(CHK,
++ min_rx_int,
++ min_tx_int,
++ idle_tx_int,
++ multiplier,
++ passive) false; /* terminating the || chain */
++#undef CHK
+
+- bfd_session_control_tx_timer(s, 0);
++ s->cf = opts;
++
++ if (reload)
++ {
++ u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
++ bfd_session_set_min_tx(s, tx);
++ bfd_session_set_min_rx(s, s->cf.min_rx_int);
++ s->detect_mult = s->cf.multiplier;
++ s->passive = s->cf.passive;
++
++ bfd_session_control_tx_timer(s, 0);
++
++ TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
++ }
++ }
++ }
++ HASH_WALK_END;
++ birdloop_leave(p->p.loop);
+
+- TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
++ /* Now the config is clean */
++ OBSREF_CLEAR(brsdc->old_config);
+ }
+
+
+@@ -589,10 +621,12 @@ bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
+ */
+
+ static struct bfd_iface_config bfd_default_iface = {
+- .min_rx_int = BFD_DEFAULT_MIN_RX_INT,
+- .min_tx_int = BFD_DEFAULT_MIN_TX_INT,
+- .idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
+- .multiplier = BFD_DEFAULT_MULTIPLIER,
++ .opts = {
++ .min_rx_int = BFD_DEFAULT_MIN_RX_INT,
++ .min_tx_int = BFD_DEFAULT_MIN_TX_INT,
++ .idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
++ .multiplier = BFD_DEFAULT_MULTIPLIER,
++ },
+ };
+
+ static inline struct bfd_iface_config *
+@@ -650,24 +684,6 @@ bfd_free_iface(struct bfd_iface *ifa)
+ mb_free(ifa);
+ }
+
+-static void
+-bfd_reconfigure_iface(struct bfd_proto *p UNUSED, struct bfd_iface *ifa, struct bfd_config *nc)
+-{
+- struct bfd_iface_config *new = bfd_find_iface_config(nc, ifa->iface);
+- struct bfd_iface_config *old = ifa->cf;
+-
+- /* Check options that are handled in bfd_reconfigure_session() */
+- ifa->changed =
+- (new->min_rx_int != old->min_rx_int) ||
+- (new->min_tx_int != old->min_tx_int) ||
+- (new->idle_tx_int != old->idle_tx_int) ||
+- (new->multiplier != old->multiplier) ||
+- (new->passive != old->passive);
+-
+- /* This should be probably changed to not access ifa->cf from the BFD thread */
+- ifa->cf = new;
+-}
+-
+
+ /*
+ * BFD requests
+@@ -900,20 +916,7 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
+ void
+ bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
+ {
+- struct bfd_session *s = req->session;
+-
+- if (!memcmp(opts, &req->opts, sizeof(const struct bfd_options)))
+- return;
+-
+ req->opts = *opts;
+-
+- if (s)
+- {
+- struct bfd_proto *p = s->ifa->bfd;
+- birdloop_enter(p->p.loop);
+- bfd_reconfigure_session(p, s);
+- birdloop_leave(p->p.loop);
+- }
+ }
+
+ static void
+@@ -1193,21 +1196,22 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
+ (new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
+ return 0;
+
+- birdloop_mask_wakeups(p->p.loop);
+-
+ WALK_LIST(ifa, p->iface_list)
+- bfd_reconfigure_iface(p, ifa, new);
+-
+- HASH_WALK(p->session_hash_id, next_id, s)
+- {
+- if (s->ifa->changed)
+- bfd_reconfigure_session(p, s);
+- }
+- HASH_WALK_END;
++ ifa->cf = bfd_find_iface_config(new, ifa->iface);
+
+ bfd_reconfigure_neighbors(p, new);
+
+- birdloop_unmask_wakeups(p->p.loop);
++ /* Sessions get reconfigured after all the config is applied */
++ struct bfd_reconfigure_sessions_deferred_call brsdc = {
++ .dc.hook = bfd_reconfigure_sessions,
++ .p = p,
++ };
++ SKIP_BACK_DECLARE(struct bfd_reconfigure_sessions_deferred_call,
++ brsdcp, dc, defer_call(&brsdc.dc, sizeof brsdc));
++
++ /* We need to keep the old config alive until all the sessions get
++ * reconfigured */
++ OBSREF_SET(brsdcp->old_config, P->cf->global);
+
+ return 1;
+ }
+diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h
+index 578ce8755..107829b72 100644
+--- proto/bfd/bfd.h
++++ proto/bfd/bfd.h
+@@ -54,24 +54,7 @@ struct bfd_config
+ struct bfd_iface_config
+ {
+ struct iface_patt i;
+- u32 min_rx_int;
+- u32 min_tx_int;
+- u32 idle_tx_int;
+- u8 multiplier;
+- u8 passive;
+- u8 auth_type; /* Authentication type (BFD_AUTH_*) */
+- list *passwords; /* Passwords for authentication */
+-};
+-
+-struct bfd_session_config
+-{
+- u32 min_rx_int;
+- u32 min_tx_int;
+- u32 idle_tx_int;
+- u8 multiplier;
+- u8 passive;
+- u8 auth_type; /* Authentication type (BFD_AUTH_*) */
+- list *passwords; /* Passwords for authentication */
++ struct bfd_options opts;
+ };
+
+ struct bfd_neighbor
+@@ -146,7 +129,7 @@ struct bfd_session
+ u32 loc_id; /* Local session ID (local discriminator) */
+ u32 rem_id; /* Remote session ID (remote discriminator) */
+
+- struct bfd_session_config cf; /* Static configuration parameters */
++ struct bfd_options cf; /* Static configuration parameters */
+
+ u32 des_min_tx_int; /* Desired min rx interval, local option */
+ u32 des_min_tx_new; /* Used for des_min_tx_int change */
+diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
+index 9e9919c4e..56d1ffac4 100644
+--- proto/bfd/config.Y
++++ proto/bfd/config.Y
+@@ -86,44 +86,37 @@ bfd_iface_start:
+ add_tail(&BFD_CFG->patt_list, NODE this_ipatt);
+ init_list(&this_ipatt->ipn_list);
+
+- BFD_IFACE->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
+- BFD_IFACE->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
+- BFD_IFACE->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
+- BFD_IFACE->multiplier = BFD_DEFAULT_MULTIPLIER;
++ this_bfd_opts = &BFD_IFACE->opts;
++
++ this_bfd_opts->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
++ this_bfd_opts->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
++ this_bfd_opts->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
++ this_bfd_opts->multiplier = BFD_DEFAULT_MULTIPLIER;
+
+ reset_passwords();
+ };
+
+ bfd_iface_finish:
+ {
+- BFD_IFACE->passwords = get_passwords();
++ this_bfd_opts->passwords = get_passwords();
+
+- if (!BFD_IFACE->auth_type != !BFD_IFACE->passwords)
++ if (!this_bfd_opts->auth_type != !this_bfd_opts->passwords)
+ cf_warn("Authentication and password options should be used together");
+
+- if (BFD_IFACE->passwords)
++ if (this_bfd_opts->passwords)
+ {
+ struct password_item *pass;
+- WALK_LIST(pass, *BFD_IFACE->passwords)
++ WALK_LIST(pass, *this_bfd_opts->passwords)
+ {
+ if (pass->alg)
+ cf_error("Password algorithm option not available in BFD protocol");
+
+- pass->alg = bfd_auth_type_to_hash_alg[BFD_IFACE->auth_type];
++ pass->alg = bfd_auth_type_to_hash_alg[this_bfd_opts->auth_type];
+ }
+ }
+-};
+
+-bfd_iface_item:
+- INTERVAL expr_us { BFD_IFACE->min_rx_int = BFD_IFACE->min_tx_int = $2; }
+- | MIN RX INTERVAL expr_us { BFD_IFACE->min_rx_int = $4; }
+- | MIN TX INTERVAL expr_us { BFD_IFACE->min_tx_int = $4; }
+- | IDLE TX INTERVAL expr_us { BFD_IFACE->idle_tx_int = $4; }
+- | MULTIPLIER expr { BFD_IFACE->multiplier = $2; }
+- | PASSIVE bool { BFD_IFACE->passive = $2; }
+- | AUTHENTICATION bfd_auth_type { BFD_IFACE->auth_type = $2; }
+- | password_list {}
+- ;
++ this_bfd_opts = NULL;
++};
+
+ bfd_auth_type:
+ NONE { $$ = BFD_AUTH_NONE; }
+@@ -134,14 +127,9 @@ bfd_auth_type:
+ | METICULOUS KEYED SHA1 { $$ = BFD_AUTH_METICULOUS_KEYED_SHA1; }
+ ;
+
+-bfd_iface_opts:
+- /* empty */
+- | bfd_iface_opts bfd_iface_item ';'
+- ;
+-
+ bfd_iface_opt_list:
+ /* empty */
+- | '{' bfd_iface_opts '}'
++ | '{' bfd_items '}'
+ ;
+
+ bfd_iface:
+@@ -194,7 +182,7 @@ bfd_item:
+ | MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
+ | IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
+ | MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
+- | PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
++ | PASSIVE bool { this_bfd_opts->passive = $2 ? BFD_OPT_PASSIVE : BFD_OPT_NOT_PASSIVE; }
+ | GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
+ | AUTHENTICATION bfd_auth_type { this_bfd_opts->auth_type = $2; }
+ | password_list {}
+diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
+index 1ceb470c1..f8bd63d73 100644
+--- proto/bfd/packets.c
++++ proto/bfd/packets.c
+@@ -109,7 +109,7 @@ const u8 bfd_auth_type_to_hash_alg[] = {
+ static void
+ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
+ {
+- struct bfd_session_config *cf = &s->cf;
++ struct bfd_options *cf = &s->cf;
+ struct password_item *pass = password_find(cf->passwords, 0);
+ uint meticulous = 0;
+
+@@ -179,7 +179,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c
+ static int
+ bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
+ {
+- struct bfd_session_config *cf = &s->cf;
++ struct bfd_options *cf = &s->cf;
+ const char *err_dsc = NULL;
+ uint err_val = 0;
+ uint auth_type = 0;
+--
+GitLab
+
diff --git a/net/bird3/files/patch-05-mainloop-dropped-old-socket b/net/bird3/files/patch-05-mainloop-dropped-old-socket
new file mode 100644
index 000000000000..eea4d1d26af2
--- /dev/null
+++ b/net/bird3/files/patch-05-mainloop-dropped-old-socket
@@ -0,0 +1,86 @@
+From 3d1f19e335f55c8cfa3cb7ca9d7b88ca03173d8e Mon Sep 17 00:00:00 2001
+From: Maria Matejka <mq@ucw.cz>
+Date: Sun, 22 Dec 2024 21:32:28 +0100
+Subject: [PATCH] Mainloop: Dropped old socket prioritization magic
+
+This is now done in worker threads and the mainloop needs to do other things,
+most notably kernel and CLI, with less overhead of repeatedly checking poll.
+---
+ sysdep/unix/io-loop.c | 2 +-
+ sysdep/unix/io.c | 21 +++++++--------------
+ 2 files changed, 8 insertions(+), 15 deletions(-)
+
+diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c
+index f69189e06..a72c69a03 100644
+--- sysdep/unix/io-loop.c
++++ sysdep/unix/io-loop.c
+@@ -1403,7 +1403,7 @@ bool task_still_in_limit(void)
+ {
+ static u64 main_counter = 0;
+ if (this_birdloop == &main_birdloop)
+- return (++main_counter % 2048); /* This is a hack because of no accounting in mainloop */
++ return (++main_counter % 512); /* This is a hack because of no accounting in mainloop */
+ else
+ return ns_now() < account_last + this_thread->max_loop_time_ns;
+ }
+diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
+index f9785c074..51395e1e9 100644
+--- sysdep/unix/io.c
++++ sysdep/unix/io.c
+@@ -53,14 +53,15 @@
+
+ /* Maximum number of calls of tx handler for one socket in one
+ * poll iteration. Should be small enough to not monopolize CPU by
+- * one protocol instance.
++ * one protocol instance. But as most of the problems are now offloaded
++ * to worker threads, too low values may actually bring problems with
++ * latency.
+ */
+-#define MAX_STEPS 4
++#define MAX_STEPS 2048
+
+ /* Maximum number of calls of rx handler for all sockets in one poll
+- iteration. RX callbacks are often much more costly so we limit
+- this to gen small latencies */
+-#define MAX_RX_STEPS 4
++ iteration. RX callbacks are often a little bit more costly. */
++#define MAX_RX_STEPS 512
+
+
+ /*
+@@ -2581,8 +2582,6 @@ io_init(void)
+ srandom((uint) (now ^ (now >> 32)));
+ }
+
+-static int short_loops = 0;
+-#define SHORT_LOOP_MAX 10
+ #define WORK_EVENTS_MAX 10
+
+ sock *stored_sock;
+@@ -2670,10 +2669,9 @@ io_loop(void)
*** 1461 LINES SKIPPED ***