naccyde / rpms / iproute

Forked from rpms/iproute 5 months ago
Clone

Blame SOURCES/0019-iproute-Add-support-for-extended-ack-to-rtnl_talk.patch

36cfb7
From a9f81b704c4e883a996927e77afdb960a7f47fd9 Mon Sep 17 00:00:00 2001
36cfb7
From: Hangbin Liu <haliu@redhat.com>
36cfb7
Date: Wed, 8 Nov 2017 14:39:09 +0800
36cfb7
Subject: [PATCH] iproute: Add support for extended ack to rtnl_talk
36cfb7
36cfb7
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1380803
36cfb7
Upstream Status: iproute2.git commit b6432e68ac2f
36cfb7
Conflicts: Manually added NETLINK_EXT_ACK define to linux headers.
36cfb7
36cfb7
commit b6432e68ac2f1f6b4ea50aa0d6d47e72c445c71c
36cfb7
Author: Stephen Hemminger <stephen@networkplumber.org>
36cfb7
Date:   Fri Aug 4 09:52:15 2017 -0700
36cfb7
36cfb7
    iproute: Add support for extended ack to rtnl_talk
36cfb7
36cfb7
    Add support for extended ack error reporting via libmnl.
36cfb7
    Add a new function rtnl_talk_extack that takes a callback as an input
36cfb7
    arg. If a netlink response contains extack attributes, the callback is
36cfb7
    is invoked with the the err string, offset in the message and a pointer
36cfb7
    to the message returned by the kernel.
36cfb7
36cfb7
    If iproute2 is built without libmnl, it will still work but
36cfb7
    extended error reports from kernel will not be available.
36cfb7
36cfb7
    Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
36cfb7
36cfb7
Signed-off-by: Hangbin Liu <haliu@redhat.com>
36cfb7
36cfb7
squash! iproute: Add support for extended ack to rtnl_talk
36cfb7
---
36cfb7
 include/libnetlink.h    |   6 +++
36cfb7
 include/linux/netlink.h |   1 +
e138d9
 lib/Makefile            |   7 +++
e138d9
 lib/libnetlink.c        | 109 +++++++++++++++++++++++++++++++++++++---
36cfb7
 4 files changed, 116 insertions(+), 7 deletions(-)
36cfb7
36cfb7
diff --git a/include/libnetlink.h b/include/libnetlink.h
e138d9
index bd0267dfcc02a..654aebc0f7632 100644
36cfb7
--- a/include/libnetlink.h
36cfb7
+++ b/include/libnetlink.h
36cfb7
@@ -65,6 +65,9 @@ typedef int (*rtnl_listen_filter_t)(const struct sockaddr_nl *,
36cfb7
 				    struct rtnl_ctrl_data *,
36cfb7
 				    struct nlmsghdr *n, void *);
36cfb7
 
36cfb7
+typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off,
36cfb7
+			       const struct nlmsghdr *inner_nlh);
36cfb7
+
36cfb7
 struct rtnl_dump_filter_arg {
36cfb7
 	rtnl_filter_t filter;
36cfb7
 	void *arg1;
36cfb7
@@ -81,6 +84,9 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth,
36cfb7
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 	      struct nlmsghdr *answer, size_t len)
36cfb7
 	__attribute__((warn_unused_result));
36cfb7
+int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
+	      struct nlmsghdr *answer, size_t len, nl_ext_ack_fn_t errfn)
36cfb7
+	__attribute__((warn_unused_result));
36cfb7
 int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 				   struct nlmsghdr *answer, size_t len)
36cfb7
 	__attribute__((warn_unused_result));
36cfb7
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
e138d9
index a982b3c004395..d1e26a2bcdcbb 100644
36cfb7
--- a/include/linux/netlink.h
36cfb7
+++ b/include/linux/netlink.h
36cfb7
@@ -113,6 +113,7 @@ struct nlmsgerr {
36cfb7
 #define NETLINK_LISTEN_ALL_NSID		8
36cfb7
 #define NETLINK_LIST_MEMBERSHIPS	9
36cfb7
 #define NETLINK_CAP_ACK			10
36cfb7
+#define NETLINK_EXT_ACK			11
36cfb7
 
36cfb7
 struct nl_pktinfo {
36cfb7
 	__u32	group;
36cfb7
diff --git a/lib/Makefile b/lib/Makefile
e138d9
index 1d24ca24b9a39..f81888cca974f 100644
36cfb7
--- a/lib/Makefile
36cfb7
+++ b/lib/Makefile
36cfb7
@@ -4,6 +4,13 @@ ifeq ($(IP_CONFIG_SETNS),y)
36cfb7
 	CFLAGS += -DHAVE_SETNS
36cfb7
 endif
36cfb7
 
36cfb7
+ifeq ($(HAVE_MNL),y)
36cfb7
+	CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags)
36cfb7
+	LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs)
36cfb7
+else
36cfb7
+@warn "libmnl required for error support"
36cfb7
+endif
36cfb7
+
36cfb7
 CFLAGS += -fPIC
36cfb7
 
36cfb7
 UTILOBJ = utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o \
36cfb7
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
e138d9
index b08518d81f2dd..a0578312e83f8 100644
36cfb7
--- a/lib/libnetlink.c
36cfb7
+++ b/lib/libnetlink.c
36cfb7
@@ -36,6 +36,79 @@
36cfb7
 
36cfb7
 int rcvbuf = 1024 * 1024;
36cfb7
 
36cfb7
+#ifdef HAVE_LIBMNL
36cfb7
+#include <libmnl/libmnl.h>
36cfb7
+
36cfb7
+static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = {
36cfb7
+	[NLMSGERR_ATTR_MSG]	= MNL_TYPE_NUL_STRING,
36cfb7
+	[NLMSGERR_ATTR_OFFS]	= MNL_TYPE_U32,
36cfb7
+};
36cfb7
+
36cfb7
+static int err_attr_cb(const struct nlattr *attr, void *data)
36cfb7
+{
36cfb7
+	const struct nlattr **tb = data;
36cfb7
+	uint16_t type;
36cfb7
+
36cfb7
+	if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0)
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+
36cfb7
+	type = mnl_attr_get_type(attr);
36cfb7
+	if (mnl_attr_validate(attr, extack_policy[type]) < 0)
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+
36cfb7
+
36cfb7
+	tb[type] = attr;
36cfb7
+	return MNL_CB_OK;
36cfb7
+}
36cfb7
+
36cfb7
+
36cfb7
+/* dump netlink extended ack error message */
36cfb7
+static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
36cfb7
+{
36cfb7
+	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
36cfb7
+	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
36cfb7
+	const struct nlmsghdr *err_nlh = NULL;
36cfb7
+	unsigned int hlen = sizeof(*err);
36cfb7
+	const char *errmsg = NULL;
36cfb7
+	uint32_t off = 0;
36cfb7
+
36cfb7
+	if (!errfn)
36cfb7
+		return 0;
36cfb7
+
36cfb7
+	/* no TLVs, nothing to do here */
36cfb7
+	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
36cfb7
+		return 0;
36cfb7
+
36cfb7
+	/* if NLM_F_CAPPED is set then the inner err msg was capped */
36cfb7
+	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
36cfb7
+		hlen += mnl_nlmsg_get_payload_len(&err->msg);
36cfb7
+
36cfb7
+	mnl_attr_parse(nlh, hlen, err_attr_cb, tb);
36cfb7
+
36cfb7
+	if (tb[NLMSGERR_ATTR_MSG])
36cfb7
+		errmsg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
36cfb7
+
36cfb7
+	if (tb[NLMSGERR_ATTR_OFFS]) {
36cfb7
+		off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
36cfb7
+
36cfb7
+		if (off > nlh->nlmsg_len) {
36cfb7
+			fprintf(stderr,
36cfb7
+				"Invalid offset for NLMSGERR_ATTR_OFFS\n");
36cfb7
+			off = 0;
36cfb7
+		} else if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
36cfb7
+			err_nlh = &err->msg;
36cfb7
+	}
36cfb7
+
36cfb7
+	return errfn(errmsg, off, err_nlh);
36cfb7
+}
36cfb7
+#else
36cfb7
+/* No extended error ack without libmnl */
36cfb7
+static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
36cfb7
+{
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+#endif
36cfb7
+
36cfb7
 void rtnl_close(struct rtnl_handle *rth)
36cfb7
 {
36cfb7
 	if (rth->fd >= 0) {
36cfb7
@@ -49,6 +122,7 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,
36cfb7
 {
36cfb7
 	socklen_t addr_len;
36cfb7
 	int sndbuf = 32768;
36cfb7
+	int one = 1;
36cfb7
 
36cfb7
 	memset(rth, 0, sizeof(*rth));
36cfb7
 
36cfb7
@@ -71,6 +145,10 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,
36cfb7
 		return -1;
36cfb7
 	}
36cfb7
 
36cfb7
+	/* Older kernels may no support extended ACK reporting */
36cfb7
+	setsockopt(rth->fd, SOL_NETLINK, NETLINK_EXT_ACK,
36cfb7
+		   &one, sizeof(one));
36cfb7
+
36cfb7
 	memset(&rth->local, 0, sizeof(rth->local));
36cfb7
 	rth->local.nl_family = AF_NETLINK;
36cfb7
 	rth->local.nl_groups = subscriptions;
36cfb7
@@ -421,9 +499,19 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth,
36cfb7
 	return rtnl_dump_filter_l(rth, a);
36cfb7
 }
36cfb7
 
36cfb7
+static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err,
36cfb7
+			    nl_ext_ack_fn_t errfn)
36cfb7
+{
36cfb7
+	if (nl_dump_ext_err(h, errfn))
36cfb7
+		return;
36cfb7
+
36cfb7
+	fprintf(stderr, "RTNETLINK answers: %s\n",
36cfb7
+		strerror(-err->error));
36cfb7
+}
36cfb7
+
36cfb7
 static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 		       struct nlmsghdr *answer, size_t maxlen,
36cfb7
-		       bool show_rtnl_err)
36cfb7
+		       bool show_rtnl_err, nl_ext_ack_fn_t errfn)
36cfb7
 {
36cfb7
 	int status;
36cfb7
 	unsigned int seq;
36cfb7
@@ -510,10 +598,10 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 					return 0;
36cfb7
 				}
36cfb7
 
36cfb7
-				if (rtnl->proto != NETLINK_SOCK_DIAG && show_rtnl_err)
36cfb7
-					fprintf(stderr,
36cfb7
-						"RTNETLINK answers: %s\n",
36cfb7
-						strerror(-err->error));
36cfb7
+				if (rtnl->proto != NETLINK_SOCK_DIAG &&
36cfb7
+				    show_rtnl_err)
36cfb7
+					rtnl_talk_error(h, err, errfn);
36cfb7
+
36cfb7
 				errno = -err->error;
36cfb7
 				return -1;
36cfb7
 			}
36cfb7
@@ -545,13 +633,20 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 	      struct nlmsghdr *answer, size_t maxlen)
36cfb7
 {
36cfb7
-	return __rtnl_talk(rtnl, n, answer, maxlen, true);
36cfb7
+	return __rtnl_talk(rtnl, n, answer, maxlen, true, NULL);
36cfb7
+}
36cfb7
+
36cfb7
+int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
+		     struct nlmsghdr *answer, size_t maxlen,
36cfb7
+		     nl_ext_ack_fn_t errfn)
36cfb7
+{
36cfb7
+	return __rtnl_talk(rtnl, n, answer, maxlen, true, errfn);
36cfb7
 }
36cfb7
 
36cfb7
 int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n,
36cfb7
 				   struct nlmsghdr *answer, size_t maxlen)
36cfb7
 {
36cfb7
-	return __rtnl_talk(rtnl, n, answer, maxlen, false);
36cfb7
+	return __rtnl_talk(rtnl, n, answer, maxlen, false, NULL);
36cfb7
 }
36cfb7
 
36cfb7
 int rtnl_listen_all_nsid(struct rtnl_handle *rth)
36cfb7
-- 
e138d9
2.21.0
36cfb7