[PATCH v2 0/4] Modernize Netlink code
These patches tweaks the netlink code, bringing it closer to best practice. Signed-off-by: Asbjørn Sloth Tønnesen <ast@2e8.dk> --- v2: - Rework patch 4 to handle conflict with commit e3ee1a97f2c6 ("Bridge: Linux bridge interface - preliminary support") v1: https://trubka.network.cz/pipermail/bird-users/2026-February/018618.html Asbjørn Sloth Tønnesen (4): Netlink: Enable strict checking on nl_req Netlink: Skip request payload in acknowledgements Netlink: Enable and decode extended acknowledgements Netlink: Skip stats in device dumps sysdep/linux/netlink.c | 101 ++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 22 deletions(-) base-commit: 1d7f3e564e257d4fd345129b26af4dd9a1dd8bb1 -- 2.53.0
This patch adds support for extended acknowledgements. While there are more attributes, this patch only adds support for the extended error message, and uses it to augment the return code. We don't check the return value of setsockopt(), as any message with extended acknowledgements will have NLM_F_ACK_TLVS set in nlmsg_flags. NETLINK_EXT_ACK / NLM_F_ACK_TLVS / NLMSGERR_ATTR_MSG was all added in Linux v4.12 commit 2d4bc93368f5 ("netlink: extended ACK reporting"), so AFAICT theres no need to add them to netlink-sys.h NB: Currently NLMSG_DONE messages are ignored, but if that changes, we should remember to check for NLM_F_ACK_TLVS. See either Documentation/userspace-api/netlink/intro.rst[1] or or tools/net/ynl/lib/ynl.c in the Linux kernel. Examples: $ sudo ip addr add 192.0.2.1/30 dev eth0 $ sudo ip addr add 2001:db8::42/120 dev eth0 $ sudo ip route add unreachable 2001:db8::2048/128 metric 32 $ sudo timeout 0.25 ./bird -dlc test4.cfg bird: Started bird: Netlink: Invalid argument (Invalid prefsrc address) bird: Netlink: Invalid argument (Invalid scope) bird: Netlink: Invalid argument (Route with host scope can not have a gateway) bird: Netlink: Network is unreachable (Nexthop has invalid gateway) bird: Netlink: Invalid argument (Unknown tcp congestion algorithm) bird: Shutting down bird: Shutdown completed $ sudo timeout 0.25 ./bird -dlc test6.cfg bird: Started bird: Netlink: Invalid argument (Unknown tcp congestion algorithm) bird: Netlink: Invalid argument (Gateway can not be a local address) bird: Netlink: File exists bird: Netlink: Invalid argument (Invalid source address) bird: Shutting down bird: Shutdown completed The examples are split in two, as to avoid ratelimiting in log_rt(). The "File exists" error is an example of an error, that doesn't have an extended message (yet). $ cat test4.cfg router id 127.0.0.1; protocol device {} protocol static { ipv4; route 192.0.2.5/32 via 192.0.2.2 { krt_prefsrc = 255.255.255.255; }; route 192.0.2.6/32 via 192.0.2.2 { krt_scope = 253; }; route 192.0.2.7/32 via 192.0.2.2 { krt_scope = 254; }; route 192.0.2.8/32 via 192.0.2.2 { krt_scope = 255; }; route 192.0.2.9/32 via 192.0.2.2 { krt_congctl = "avian-carrier"; }; } protocol kernel { ipv4 { import none; export all; }; } $ cat test6.cfg router id 127.0.0.1; protocol device {} protocol static { ipv6; route 2001:db8::256/128 via 2001:db8::42; route 2001:db8::1337/128 via 2001:db8::43 { krt_prefsrc = ::1; }; route 2001:db8::1336/128 via 2001:db8::44 { krt_congctl = "rfc2549"; }; route 2001:db8::2048/128 unreachable; } protocol kernel { ipv6 { import none; export all; }; } [1] https://docs.kernel.org/userspace-api/netlink/intro.html Signed-off-by: Asbjørn Sloth Tønnesen <ast@2e8.dk> --- sysdep/linux/netlink.c | 81 ++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 68f075fc0..02efa0f6d 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -70,6 +70,8 @@ static linpool *nl_linpool; static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */ static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */ +static int nl_error(struct nlmsghdr *h, int ignore_esrch); + static void nl_set_cap_ack(struct nl_sock *nl UNUSED, int val UNUSED) { @@ -78,6 +80,14 @@ nl_set_cap_ack(struct nl_sock *nl UNUSED, int val UNUSED) #endif } +static void +nl_set_ext_ack(struct nl_sock *nl UNUSED, int val UNUSED) +{ +#ifdef SOL_NETLINK + setsockopt(nl->fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); +#endif +} + static void nl_open_sock(struct nl_sock *nl) { @@ -92,6 +102,7 @@ nl_open_sock(struct nl_sock *nl) nl->last_size = 0; nl_set_cap_ack(nl, 1); + nl_set_ext_ack(nl, 1); } } @@ -311,24 +322,6 @@ nl_get_reply(struct nl_sock *nl) static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS; -static int -nl_error(struct nlmsghdr *h, int ignore_esrch) -{ - struct nlmsgerr *e; - int ec; - - if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) - { - log(L_WARN "Netlink: Truncated error message received"); - return ENOBUFS; - } - e = (struct nlmsgerr *) NLMSG_DATA(h); - ec = netlink_error_to_os(e->error); - if (ec && !(ignore_esrch && (ec == ESRCH))) - log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec)); - return ec; -} - static struct nlmsghdr * nl_get_scan(void) { @@ -527,6 +520,11 @@ static struct nl_want_attrs rtm_attr_want_mpls[BIRD_RTA_MAX] = { }; #endif +#define BIRD_NLMSGERR_MAX (NLMSGERR_ATTR_MSG+1) + +static struct nl_want_attrs nlmsgerr_attr_want[BIRD_NLMSGERR_MAX] = { + [NLMSGERR_ATTR_MSG] = { 1, 0, 0 }, +}; #define BIRD_NDA_MAX (NDA_SRC_VNI+1) @@ -610,6 +608,53 @@ static inline ip_addr rta_get_via(struct rtattr *a) return IPA_NONE; } +static const char * +nl_get_ext_msg(struct nlmsghdr *h, struct nlmsgerr *e) +{ + struct rtattr *attrs[BIRD_NLMSGERR_MAX]; + struct rtattr *a; + size_t offset; + + if (!(h->nlmsg_flags & NLM_F_ACK_TLVS)) + return NULL; + offset = NLMSG_ALIGN(sizeof(*e)); + if (!(h->nlmsg_flags & NLM_F_CAPPED)) + offset += e->msg.nlmsg_len - NLMSG_HDRLEN; + offset = RTA_ALIGN(offset); + a = (struct rtattr*) (NLMSG_DATA(h) + offset); + nl_attr_len = h->nlmsg_len - (NLMSG_HDRLEN + offset); + if (!nl_parse_attrs(a, nlmsgerr_attr_want, attrs, sizeof(attrs))) + log_rl(&rl_netlink_err, L_WARN "Netlink: Error decoding extack"); + if (!attrs[NLMSGERR_ATTR_MSG]) + return NULL; + return rta_get_str(attrs[NLMSGERR_ATTR_MSG]); +} + +static int +nl_error(struct nlmsghdr *h, int ignore_esrch) +{ + const char *ext_msg; + struct nlmsgerr *e; + int ec; + + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + { + log(L_WARN "Netlink: Truncated error message received"); + return ENOBUFS; + } + e = (struct nlmsgerr *) NLMSG_DATA(h); + ec = netlink_error_to_os(e->error); + if (ec && !(ignore_esrch && (ec == ESRCH))) + { + ext_msg = nl_get_ext_msg(h, e); + if (ext_msg) + log_rl(&rl_netlink_err, L_WARN "Netlink: %s (%s)", strerror(ec), ext_msg); + else + log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec)); + } + return ec; +} + #ifdef HAVE_MPLS_KERNEL static u32 rta_mpls_stack[MPLS_MAX_LABEL_STACK]; static inline int rta_get_mpls(struct rtattr *a, u32 *stack) -- 2.53.0
Hi Asbjørn, thanks for all the patches and sorry for not getting to them sooner. After we release BIRD v3.3.0 and v2.19.0 this week, we will look into merging them. Thanks for your patience and happy routing! David David Petera (he/him) | BIRD Tech Support | CZ.NIC, z.s.p.o. On 5/18/26 10:59, Asbjørn Sloth Tønnesen via Bird-users wrote:
These patches tweaks the netlink code, bringing it closer to best practice.
Signed-off-by: Asbjørn Sloth Tønnesen <ast@2e8.dk> --- v2: - Rework patch 4 to handle conflict with commit e3ee1a97f2c6 ("Bridge: Linux bridge interface - preliminary support") v1: https://trubka.network.cz/pipermail/bird-users/2026-February/018618.html
Asbjørn Sloth Tønnesen (4): Netlink: Enable strict checking on nl_req Netlink: Skip request payload in acknowledgements Netlink: Enable and decode extended acknowledgements Netlink: Skip stats in device dumps
sysdep/linux/netlink.c | 101 ++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 22 deletions(-)
base-commit: 1d7f3e564e257d4fd345129b26af4dd9a1dd8bb1
On Mon, May 18, 2026 at 08:59:19AM +0000, Asbjørn Sloth Tønnesen via Bird-users wrote:
These patches tweaks the netlink code, bringing it closer to best practice.
Asbjørn Sloth Tønnesen (4): Netlink: Enable strict checking on nl_req Netlink: Skip request payload in acknowledgements Netlink: Enable and decode extended acknowledgements Netlink: Skip stats in device dumps
Thanks, merged: https://gitlab.nic.cz/labs/bird/-/commit/02d082a71257944a6038320b43b0f5d95af... (did some rewriting of extended acknowledgements code for clarity) -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) "To err is human -- to blame it on a computer is even more so."
participants (3)
-
Asbjørn Sloth Tønnesen -
David Petera -
Ondrej Zajicek