]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Netlink: Enable and decode extended acknowledgements
authorOndrej Zajicek <santiago@crfreenet.org>
Wed, 27 May 2026 04:07:04 +0000 (06:07 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Wed, 27 May 2026 04:26:58 +0000 (06:26 +0200)
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

Based on the patch from Asbjørn Sloth Tønnesen <ast@2e8.dk>, thanks!

sysdep/linux/netlink.c

index 68f075fc0b355c27a6309a68be03685518166952..d9940ff3eb00bb764cb9e6f088bd238cbec970a0 100644 (file)
@@ -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 const char * nl_get_ext_msg(struct rtattr *a, int length);
+
 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,22 +322,53 @@ nl_get_reply(struct nl_sock *nl)
 
 static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS;
 
+#ifndef NLMSG_RTA
+#define NLMSG_RTA(nlh, len) (struct rtattr *)(((char *) nlh) + NLMSG_SPACE(len))
+#endif
+
 static int
 nl_error(struct nlmsghdr *h, int ignore_esrch)
 {
-  struct nlmsgerr *e;
-  int ec;
+  /*
+   * NLMSG_ERROR structure:
+   *
+   * struct nlmsghdr
+   * struct nlmsgerr (contains header of request)
+   * optional payload of request
+   * optional extended ACK
+   */
 
-  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;
+  int err = ENOBUFS;
+  uint body = sizeof(struct nlmsgerr);
+  const char *msg = NULL;
+
+  if (h->nlmsg_len < NLMSG_LENGTH(body))
+    goto err;
+
+  struct nlmsgerr *e = NLMSG_DATA(h);
+  err = netlink_error_to_os(e->error);
+
+  /* No error */
+  if (!err || (ignore_esrch && (err == ESRCH)))
+    return err;
+
+  if (!(h->nlmsg_flags & NLM_F_CAPPED))
+  {
+    body = NLMSG_ALIGN(body) + (e->msg.nlmsg_len - NLMSG_HDRLEN);
+
+    if (h->nlmsg_len < NLMSG_LENGTH(body))
+      goto err;
+  }
+
+  if (h->nlmsg_flags & NLM_F_ACK_TLVS)
+    msg = nl_get_ext_msg(NLMSG_RTA(h, body), NLMSG_PAYLOAD(h, body));
+
+  log_rl(&rl_netlink_err, L_WARN "Netlink: %s", msg ?: strerror(err));
+  return err;
+
+err:
+  log(L_WARN "Netlink: Truncated error message received");
+  return err;
 }
 
 static struct nlmsghdr *
@@ -385,6 +427,13 @@ struct nl_want_attrs {
 };
 
 
+#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_IFLA_MAX (IFLA_AF_SPEC+1)
 
 static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = {
@@ -632,6 +681,21 @@ static inline int rta_get_mpls(struct rtattr *a, u32 *stack)
 }
 #endif
 
+static const char *
+nl_get_ext_msg(struct rtattr *a, int length)
+{
+  struct rtattr *attrs[BIRD_NLMSGERR_MAX];
+
+  nl_attr_len = length;
+  if (!nl_parse_attrs(a, nlmsgerr_attr_want, attrs, sizeof(attrs)))
+    return NULL;
+
+  if (!attrs[NLMSGERR_ATTR_MSG])
+    return NULL;
+
+  return rta_get_str(attrs[NLMSGERR_ATTR_MSG]);
+}
+
 struct rtattr *
 nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
 {