[PATCH 3/4] Netlink: Enable and decode extended acknowledgements
Asbjørn Sloth Tønnesen
ast at 2e8.dk
Tue Feb 10 18:10:44 CET 2026
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 at 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 c9eedf8a4..844aefa16 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -67,6 +67,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)
{
@@ -75,6 +77,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)
{
@@ -89,6 +99,7 @@ nl_open_sock(struct nl_sock *nl)
nl->last_size = 0;
nl_set_cap_ack(nl, 1);
+ nl_set_ext_ack(nl, 1);
}
}
@@ -270,24 +281,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)
{
@@ -454,6 +447,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 },
+};
static int
nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize)
@@ -517,6 +515,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.51.0
More information about the Bird-users
mailing list