naccyde / rpms / iproute

Forked from rpms/iproute 10 months ago
Clone

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

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