diff mbox series

[4/6] backports: backport most of improved netlink policy validation

Message ID 20181002175107.16336-4-johannes@sipsolutions.net (mailing list archive)
State Changes Requested
Headers show
Series [1/6] backports: rename magic functions for netlink parsing | expand

Commit Message

Johannes Berg Oct. 2, 2018, 5:51 p.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 backport/backport-include/linux/netlink.h |  19 ++
 backport/backport-include/net/netlink.h   | 185 ++++++++++++++-
 backport/compat/Makefile                  |   1 +
 backport/compat/backport-4.20.c           | 379 ++++++++++++++++++++++++++++++
 4 files changed, 572 insertions(+), 12 deletions(-)
 create mode 100644 backport/compat/backport-4.20.c
diff mbox series

Patch

diff --git a/backport/backport-include/linux/netlink.h b/backport/backport-include/linux/netlink.h
index 468a12d15dbe..366c9e27e8c2 100644
--- a/backport/backport-include/linux/netlink.h
+++ b/backport/backport-include/linux/netlink.h
@@ -3,6 +3,13 @@ 
 #include_next <linux/netlink.h>
 #include <linux/version.h>
 
+#if LINUX_VERSION_IS_LESS(4,14,0)
+struct nla_bitfield32 {
+	__u32 value;
+	__u32 selector;
+};
+#endif
+
 #if LINUX_VERSION_IS_LESS(4,12,0)
 #define NETLINK_MAX_COOKIE_LEN  20
 
@@ -23,6 +30,18 @@  struct netlink_ext_ack {
 } while (0)
 #endif
 
+#ifndef NL_SET_ERR_MSG_ATTR
+#define NL_SET_ERR_MSG_ATTR(extack, attr, msg) do {	\
+	static const char __msg[] = msg;		\
+	struct netlink_ext_ack *__extack = (extack);	\
+							\
+	if (__extack) {					\
+		__extack->_msg = __msg;			\
+		__extack->bad_attr = (attr);		\
+	}						\
+} while (0)
+#endif
+
 /* this is for patches we apply */
 #if LINUX_VERSION_IS_LESS(3,7,0)
 #define netlink_notify_portid(__notify) (__notify->pid)
diff --git a/backport/backport-include/net/netlink.h b/backport/backport-include/net/netlink.h
index 7775bc8295c3..4af73631115e 100644
--- a/backport/backport-include/net/netlink.h
+++ b/backport/backport-include/net/netlink.h
@@ -4,6 +4,162 @@ 
 #include <linux/version.h>
 #include <linux/in6.h>
 
+#if LINUX_VERSION_IS_LESS(4,20,0)
+/* can't backport using the enum - need to override */
+#define NLA_UNSPEC		0
+#define NLA_U8			1
+#define NLA_U16			2
+#define NLA_U32			3
+#define NLA_U64			4
+#define NLA_STRING		5
+#define NLA_FLAG		6
+#define NLA_MSECS		7
+#define NLA_NESTED		8
+#define NLA_NESTED_ARRAY	9
+#define NLA_NUL_STRING		10
+#define NLA_BINARY		11
+#define NLA_S8			12
+#define NLA_S16			13
+#define NLA_S32			14
+#define NLA_S64			15
+#define NLA_BITFIELD32		16
+#define NLA_REJECT		17
+#define NLA_EXACT_LEN		18
+#define NLA_EXACT_LEN_WARN	19
+#define __NLA_TYPE_MAX		20
+#define NLA_TYPE_MAX		(__NLA_TYPE_MAX - 1)
+
+enum nla_policy_validation {
+	NLA_VALIDATE_NONE,
+	NLA_VALIDATE_RANGE,
+	NLA_VALIDATE_MIN,
+	NLA_VALIDATE_MAX,
+	NLA_VALIDATE_FUNCTION,
+};
+
+struct backport_nla_policy {
+	u8		type;
+	u8		validation_type;
+	u16		len;
+	union {
+		const void *validation_data;
+		struct {
+			s16 min, max;
+		};
+		int (*validate)(const struct nlattr *attr,
+				struct netlink_ext_ack *extack);
+	};
+};
+#define nla_policy backport_nla_policy
+
+#define NLA_POLICY_EXACT_LEN(_len)	{ .type = NLA_EXACT_LEN, .len = _len }
+#define NLA_POLICY_EXACT_LEN_WARN(_len)	{ .type = NLA_EXACT_LEN_WARN, \
+					  .len = _len }
+
+#define NLA_POLICY_ETH_ADDR		NLA_POLICY_EXACT_LEN(ETH_ALEN)
+#define NLA_POLICY_ETH_ADDR_COMPAT	NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
+
+#define NLA_POLICY_NESTED(maxattr, policy) \
+	{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
+#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
+	{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
+
+#define __NLA_ENSURE(condition) (sizeof(char[1 - 2*!(condition)]) - 1)
+#define NLA_ENSURE_INT_TYPE(tp)				\
+	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 ||	\
+		      tp == NLA_S16 || tp == NLA_U16 ||	\
+		      tp == NLA_S32 || tp == NLA_U32 ||	\
+		      tp == NLA_S64 || tp == NLA_U64) + tp)
+#define NLA_ENSURE_NO_VALIDATION_PTR(tp)		\
+	(__NLA_ENSURE(tp != NLA_BITFIELD32 &&		\
+		      tp != NLA_REJECT &&		\
+		      tp != NLA_NESTED &&		\
+		      tp != NLA_NESTED_ARRAY) + tp)
+
+#define NLA_POLICY_RANGE(tp, _min, _max) {		\
+	.type = NLA_ENSURE_INT_TYPE(tp),		\
+	.validation_type = NLA_VALIDATE_RANGE,		\
+	.min = _min,					\
+	.max = _max					\
+}
+
+#define NLA_POLICY_MIN(tp, _min) {			\
+	.type = NLA_ENSURE_INT_TYPE(tp),		\
+	.validation_type = NLA_VALIDATE_MIN,		\
+	.min = _min,					\
+}
+
+#define NLA_POLICY_MAX(tp, _max) {			\
+	.type = NLA_ENSURE_INT_TYPE(tp),		\
+	.validation_type = NLA_VALIDATE_MAX,		\
+	.max = _max,					\
+}
+
+#define NLA_POLICY_VALIDATE_FN(tp, fn, ...) {		\
+	.type = NLA_ENSURE_NO_VALIDATION_PTR(tp),	\
+	.validation_type = NLA_VALIDATE_FUNCTION,	\
+	.validate = fn,					\
+	.len = __VA_ARGS__ + 0,				\
+}
+
+#define nla_validate LINUX_BACKPORT(nla_validate)
+int nla_validate(const struct nlattr *head, int len, int maxtype,
+		 const struct nla_policy *policy,
+		 struct netlink_ext_ack *extack);
+#define nla_parse LINUX_BACKPORT(nla_parse)
+int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
+	      int len, const struct nla_policy *policy,
+	      struct netlink_ext_ack *extack);
+#define nla_policy_len LINUX_BACKPORT(nla_policy_len)
+int nla_policy_len(const struct nla_policy *, int);
+
+#define nlmsg_parse LINUX_BACKPORT(nlmsg_parse)
+static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen,
+			      struct nlattr *tb[], int maxtype,
+			      const struct nla_policy *policy,
+			      struct netlink_ext_ack *extack)
+{
+	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+		return -EINVAL;
+
+	return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
+			 nlmsg_attrlen(nlh, hdrlen), policy, extack);
+}
+
+#define nlmsg_validate LINUX_BACKPORT(nlmsg_validate)
+static inline int nlmsg_validate(const struct nlmsghdr *nlh,
+				 int hdrlen, int maxtype,
+				 const struct nla_policy *policy,
+				 struct netlink_ext_ack *extack)
+{
+	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+		return -EINVAL;
+
+	return nla_validate(nlmsg_attrdata(nlh, hdrlen),
+			    nlmsg_attrlen(nlh, hdrlen), maxtype, policy,
+			    extack);
+}
+
+#define nla_parse_nested LINUX_BACKPORT(nla_parse_nested)
+static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
+				   const struct nlattr *nla,
+				   const struct nla_policy *policy,
+				   struct netlink_ext_ack *extack)
+{
+	return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy,
+			 extack);
+}
+
+#define nla_validate_nested LINUX_BACKPORT(nla_validate_nested)
+static inline int nla_validate_nested(const struct nlattr *start, int maxtype,
+				      const struct nla_policy *policy,
+				      struct netlink_ext_ack *extack)
+{
+	return nla_validate(nla_data(start), nla_len(start), maxtype, policy,
+			    extack);
+}
+#endif /* < 4.20 */
+
 #if LINUX_VERSION_IS_LESS(4,12,0)
 #include <backport/magic.h>
 
@@ -12,13 +168,13 @@  static inline int _nla_validate5(const struct nlattr *head,
 				 const struct nla_policy *policy,
 				 struct netlink_ext_ack *extack)
 {
-	return nla_validate(head, len, maxtype, policy);
+	return nla_validate(head, len, maxtype, policy, extack);
 }
 static inline int _nla_validate4(const struct nlattr *head,
 				 int len, int maxtype,
 				 const struct nla_policy *policy)
 {
-	return nla_validate(head, len, maxtype, policy);
+	return nla_validate(head, len, maxtype, policy, NULL);
 }
 #undef nla_validate
 #define nla_validate(...) \
@@ -29,14 +185,15 @@  static inline int _nla_parse6(struct nlattr **tb, int maxtype,
 			      int len, const struct nla_policy *policy,
 			      struct netlink_ext_ack *extack)
 {
-	return nla_parse(tb, maxtype, head, len, policy);
+	return nla_parse(tb, maxtype, head, len, policy, extack);
 }
 static inline int _nla_parse5(struct nlattr **tb, int maxtype,
 			      const struct nlattr *head,
 			      int len, const struct nla_policy *policy)
 {
-	return nla_parse(tb, maxtype, head, len, policy);
+	return nla_parse(tb, maxtype, head, len, policy, NULL);
 }
+#undef nla_parse
 #define nla_parse(...) \
 	macro_dispatcher(_nla_parse, __VA_ARGS__)(__VA_ARGS__)
 
@@ -45,14 +202,15 @@  static inline int _nlmsg_parse6(const struct nlmsghdr *nlh, int hdrlen,
 			        const struct nla_policy *policy,
 			        struct netlink_ext_ack *extack)
 {
-	return nlmsg_parse(nlh, hdrlen, tb, maxtype, policy);
+	return nlmsg_parse(nlh, hdrlen, tb, maxtype, policy, extack);
 }
 static inline int _nlmsg_parse5(const struct nlmsghdr *nlh, int hdrlen,
 			        struct nlattr *tb[], int maxtype,
 			        const struct nla_policy *policy)
 {
-	return nlmsg_parse(nlh, hdrlen, tb, maxtype, policy);
+	return nlmsg_parse(nlh, hdrlen, tb, maxtype, policy, NULL);
 }
+#undef nlmsg_parse
 #define nlmsg_parse(...) \
 	macro_dispatcher(_nlmsg_parse, __VA_ARGS__)(__VA_ARGS__)
 
@@ -61,14 +219,15 @@  static inline int _nlmsg_validate5(const struct nlmsghdr *nlh,
 				   const struct nla_policy *policy,
 				   struct netlink_ext_ack *extack)
 {
-	return nlmsg_validate(nlh, hdrlen, maxtype, policy);
+	return nlmsg_validate(nlh, hdrlen, maxtype, policy, extack);
 }
 static inline int _nlmsg_validate4(const struct nlmsghdr *nlh,
 				   int hdrlen, int maxtype,
 				   const struct nla_policy *policy)
 {
-	return nlmsg_validate(nlh, hdrlen, maxtype, policy);
+	return nlmsg_validate(nlh, hdrlen, maxtype, policy, NULL);
 }
+#undef nlmsg_validate
 #define nlmsg_validate(...) \
 	macro_dispatcher(_nlmsg_validate, __VA_ARGS__)(__VA_ARGS__)
 
@@ -77,14 +236,15 @@  static inline int _nla_parse_nested5(struct nlattr *tb[], int maxtype,
 				     const struct nla_policy *policy,
 				     struct netlink_ext_ack *extack)
 {
-	return nla_parse_nested(tb, maxtype, nla, policy);
+	return nla_parse_nested(tb, maxtype, nla, policy, extack);
 }
 static inline int _nla_parse_nested4(struct nlattr *tb[], int maxtype,
 				     const struct nlattr *nla,
 				     const struct nla_policy *policy)
 {
-	return nla_parse_nested(tb, maxtype, nla, policy);
+	return nla_parse_nested(tb, maxtype, nla, policy, NULL);
 }
+#undef nla_parse_nested
 #define nla_parse_nested(...) \
 	macro_dispatcher(_nla_parse_nested, __VA_ARGS__)(__VA_ARGS__)
 
@@ -92,13 +252,14 @@  static inline int _nla_validate_nested4(const struct nlattr *start, int maxtype,
 					const struct nla_policy *policy,
 					struct netlink_ext_ack *extack)
 {
-	return nla_validate_nested(start, maxtype, policy);
+	return nla_validate_nested(start, maxtype, policy, extack);
 }
 static inline int _nla_validate_nested3(const struct nlattr *start, int maxtype,
 					const struct nla_policy *policy)
 {
-	return nla_validate_nested(start, maxtype, policy);
+	return nla_validate_nested(start, maxtype, policy, NULL);
 }
+#undef nla_validate_nested
 #define nla_validate_nested(...) \
 	macro_dispatcher(_nla_validate_nested, __VA_ARGS__)(__VA_ARGS__)
 #endif /* LINUX_VERSION_IS_LESS(4,12,0) */
diff --git a/backport/compat/Makefile b/backport/compat/Makefile
index f5b1886e2d8e..ff6c7e658380 100644
--- a/backport/compat/Makefile
+++ b/backport/compat/Makefile
@@ -38,6 +38,7 @@  compat-$(CPTCFG_KERNEL_4_8) += backport-4.8.o
 compat-$(CPTCFG_KERNEL_4_10) += backport-4.10.o
 compat-$(CPTCFG_KERNEL_4_12) += backport-4.12.o
 compat-$(CPTCFG_KERNEL_4_18) += backport-4.18.o
+compat-$(CPTCFG_KERNEL_4_20) += backport-4.20.o
 
 compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o
 
diff --git a/backport/compat/backport-4.20.c b/backport/compat/backport-4.20.c
new file mode 100644
index 000000000000..e26f3b52fbbe
--- /dev/null
+++ b/backport/compat/backport-4.20.c
@@ -0,0 +1,379 @@ 
+/*
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Backport functionality introduced in Linux 4.20.
+ * This is basically upstream lib/nlattr.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <net/netlink.h>
+
+static const u8 nla_attr_len[NLA_TYPE_MAX+1] = {
+	[NLA_U8]	= sizeof(u8),
+	[NLA_U16]	= sizeof(u16),
+	[NLA_U32]	= sizeof(u32),
+	[NLA_U64]	= sizeof(u64),
+	[NLA_S8]	= sizeof(s8),
+	[NLA_S16]	= sizeof(s16),
+	[NLA_S32]	= sizeof(s32),
+	[NLA_S64]	= sizeof(s64),
+};
+
+static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
+	[NLA_U8]	= sizeof(u8),
+	[NLA_U16]	= sizeof(u16),
+	[NLA_U32]	= sizeof(u32),
+	[NLA_U64]	= sizeof(u64),
+	[NLA_MSECS]	= sizeof(u64),
+	[NLA_NESTED]	= NLA_HDRLEN,
+	[NLA_S8]	= sizeof(s8),
+	[NLA_S16]	= sizeof(s16),
+	[NLA_S32]	= sizeof(s32),
+	[NLA_S64]	= sizeof(s64),
+};
+
+static int validate_nla_bitfield32(const struct nlattr *nla,
+				   const u32 *valid_flags_mask)
+{
+	const struct nla_bitfield32 *bf = nla_data(nla);
+
+	if (!valid_flags_mask)
+		return -EINVAL;
+
+	/*disallow invalid bit selector */
+	if (bf->selector & ~*valid_flags_mask)
+		return -EINVAL;
+
+	/*disallow invalid bit values */
+	if (bf->value & ~*valid_flags_mask)
+		return -EINVAL;
+
+	/*disallow valid bit values that are not selected*/
+	if (bf->value & ~bf->selector)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
+			      const struct nla_policy *policy,
+			      struct netlink_ext_ack *extack)
+{
+	const struct nlattr *entry;
+	int rem;
+
+	nla_for_each_attr(entry, head, len, rem) {
+		int ret;
+
+		if (nla_len(entry) == 0)
+			continue;
+
+		if (nla_len(entry) < NLA_HDRLEN) {
+			NL_SET_ERR_MSG_ATTR(extack, entry,
+					    "Array element too short");
+			return -ERANGE;
+		}
+
+		ret = nla_validate(nla_data(entry), nla_len(entry),
+				   maxtype, policy, extack);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int nla_validate_int_range(const struct nla_policy *pt,
+				  const struct nlattr *nla,
+				  struct netlink_ext_ack *extack)
+{
+	bool validate_min, validate_max;
+	s64 value;
+
+	validate_min = pt->validation_type == NLA_VALIDATE_RANGE ||
+		       pt->validation_type == NLA_VALIDATE_MIN;
+	validate_max = pt->validation_type == NLA_VALIDATE_RANGE ||
+		       pt->validation_type == NLA_VALIDATE_MAX;
+
+	switch (pt->type) {
+	case NLA_U8:
+		value = nla_get_u8(nla);
+		break;
+	case NLA_U16:
+		value = nla_get_u16(nla);
+		break;
+	case NLA_U32:
+		value = nla_get_u32(nla);
+		break;
+	case NLA_S8:
+		value = nla_get_s8(nla);
+		break;
+	case NLA_S16:
+		value = nla_get_s16(nla);
+		break;
+	case NLA_S32:
+		value = nla_get_s32(nla);
+		break;
+	case NLA_S64:
+		value = nla_get_s64(nla);
+		break;
+	case NLA_U64:
+		/* treat this one specially, since it may not fit into s64 */
+		if ((validate_min && nla_get_u64(nla) < pt->min) ||
+		    (validate_max && nla_get_u64(nla) > pt->max)) {
+			NL_SET_ERR_MSG_ATTR(extack, nla,
+					    "integer out of range");
+			return -ERANGE;
+		}
+		return 0;
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	if ((validate_min && value < pt->min) ||
+	    (validate_max && value > pt->max)) {
+		NL_SET_ERR_MSG_ATTR(extack, nla,
+				    "integer out of range");
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+static int validate_nla(const struct nlattr *nla, int maxtype,
+			const struct nla_policy *policy,
+			struct netlink_ext_ack *extack)
+{
+	const struct nla_policy *pt;
+	int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
+	int err = -ERANGE;
+
+	if (type <= 0 || type > maxtype)
+		return 0;
+
+	pt = &policy[type];
+
+	BUG_ON(pt->type > NLA_TYPE_MAX);
+
+	if ((nla_attr_len[pt->type] && attrlen != nla_attr_len[pt->type]) ||
+	    (pt->type == NLA_EXACT_LEN_WARN && attrlen != pt->len)) {
+		pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
+				    current->comm, type);
+	}
+
+	switch (pt->type) {
+	case NLA_EXACT_LEN:
+		if (attrlen != pt->len)
+			goto out_err;
+		break;
+
+	case NLA_REJECT:
+		if (extack && pt->validation_data) {
+			NL_SET_BAD_ATTR(extack, nla);
+			extack->_msg = pt->validation_data;
+			return -EINVAL;
+		}
+		err = -EINVAL;
+		goto out_err;
+
+	case NLA_FLAG:
+		if (attrlen > 0)
+			goto out_err;
+		break;
+
+	case NLA_BITFIELD32:
+		if (attrlen != sizeof(struct nla_bitfield32))
+			goto out_err;
+
+		err = validate_nla_bitfield32(nla, pt->validation_data);
+		if (err)
+			goto out_err;
+		break;
+
+	case NLA_NUL_STRING:
+		if (pt->len)
+			minlen = min_t(int, attrlen, pt->len + 1);
+		else
+			minlen = attrlen;
+
+		if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) {
+			err = -EINVAL;
+			goto out_err;
+		}
+		/* fall through */
+
+	case NLA_STRING:
+		if (attrlen < 1)
+			goto out_err;
+
+		if (pt->len) {
+			char *buf = nla_data(nla);
+
+			if (buf[attrlen - 1] == '\0')
+				attrlen--;
+
+			if (attrlen > pt->len)
+				goto out_err;
+		}
+		break;
+
+	case NLA_BINARY:
+		if (pt->len && attrlen > pt->len)
+			goto out_err;
+		break;
+
+	case NLA_NESTED:
+		/* a nested attributes is allowed to be empty; if its not,
+		 * it must have a size of at least NLA_HDRLEN.
+		 */
+		if (attrlen == 0)
+			break;
+		if (attrlen < NLA_HDRLEN)
+			goto out_err;
+		if (pt->validation_data) {
+			err = nla_validate(nla_data(nla), nla_len(nla), pt->len,
+					   pt->validation_data, extack);
+			if (err < 0) {
+				/*
+				 * return directly to preserve the inner
+				 * error message/attribute pointer
+				 */
+				return err;
+			}
+		}
+		break;
+	case NLA_NESTED_ARRAY:
+		/* a nested array attribute is allowed to be empty; if its not,
+		 * it must have a size of at least NLA_HDRLEN.
+		 */
+		if (attrlen == 0)
+			break;
+		if (attrlen < NLA_HDRLEN)
+			goto out_err;
+		if (pt->validation_data) {
+			int err;
+
+			err = nla_validate_array(nla_data(nla), nla_len(nla),
+						 pt->len, pt->validation_data,
+						 extack);
+			if (err < 0) {
+				/*
+				 * return directly to preserve the inner
+				 * error message/attribute pointer
+				 */
+				return err;
+			}
+		}
+		break;
+	default:
+		if (pt->len)
+			minlen = pt->len;
+		else if (pt->type != NLA_UNSPEC)
+			minlen = nla_attr_minlen[pt->type];
+
+		if (attrlen < minlen)
+			goto out_err;
+	}
+
+	/* further validation */
+	switch (pt->validation_type) {
+	case NLA_VALIDATE_NONE:
+		/* nothing to do */
+		break;
+	case NLA_VALIDATE_RANGE:
+	case NLA_VALIDATE_MIN:
+	case NLA_VALIDATE_MAX:
+		err = nla_validate_int_range(pt, nla, extack);
+		if (err)
+			return err;
+		break;
+	case NLA_VALIDATE_FUNCTION:
+		if (pt->validate) {
+			err = pt->validate(nla, extack);
+			if (err)
+				return err;
+		}
+		break;
+	}
+
+	return 0;
+out_err:
+	NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
+	return err;
+}
+
+int backport_nla_validate(const struct nlattr *head, int len, int maxtype,
+			  const struct nla_policy *policy,
+			  struct netlink_ext_ack *extack)
+{
+	const struct nlattr *nla;
+	int rem;
+
+	nla_for_each_attr(nla, head, len, rem) {
+		int err = validate_nla(nla, maxtype, policy, extack);
+
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(backport_nla_validate);
+
+int backport_nla_policy_len(const struct nla_policy *p, int n)
+{
+	int i, len = 0;
+
+	for (i = 0; i < n; i++, p++) {
+		if (p->len)
+			len += nla_total_size(p->len);
+		else if (nla_attr_len[p->type])
+			len += nla_total_size(nla_attr_len[p->type]);
+		else if (nla_attr_minlen[p->type])
+			len += nla_total_size(nla_attr_minlen[p->type]);
+	}
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(backport_nla_policy_len);
+
+int backport_nla_parse(struct nlattr **tb, int maxtype,
+		       const struct nlattr *head,
+		       int len, const struct nla_policy *policy,
+		       struct netlink_ext_ack *extack)
+{
+	const struct nlattr *nla;
+	int rem;
+
+	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
+	nla_for_each_attr(nla, head, len, rem) {
+		u16 type = nla_type(nla);
+
+		if (type > 0 && type <= maxtype) {
+			if (policy) {
+				int err = validate_nla(nla, maxtype, policy,
+						       extack);
+
+				if (err < 0)
+					return err;
+			}
+
+			tb[type] = (struct nlattr *)nla;
+		}
+	}
+
+	if (unlikely(rem > 0))
+		pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
+				    rem, current->comm);
+
+	return 0;
+}
+EXPORT_SYMBOL(backport_nla_parse);