naccyde / rpms / iproute

Forked from rpms/iproute 9 months ago
Clone

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

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