* [PATCH v2 4/6] backports: backport most of improved netlink policy validation
2018-10-02 19:32 [PATCH v2 1/6] backports: rename magic functions for netlink parsing Johannes Berg
2018-10-02 19:32 ` [PATCH v2 2/6] backports: fix genlmsg_nlhdr() backport Johannes Berg
2018-10-02 19:32 ` [PATCH v2 3/6] backports: add copy-list.hwsim Johannes Berg
@ 2018-10-02 19:32 ` Johannes Berg
2018-10-02 19:32 ` [PATCH v2 5/6] backports: add __skb_peek() Johannes Berg
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2018-10-02 19:32 UTC (permalink / raw)
To: backports; +Cc: Johannes Berg
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 --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);
--
2.14.4
--
To unsubscribe from this list: send the line "unsubscribe backports" in
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 6/6] backports: genetlink: update completely
2018-10-02 19:32 [PATCH v2 1/6] backports: rename magic functions for netlink parsing Johannes Berg
` (3 preceding siblings ...)
2018-10-02 19:32 ` [PATCH v2 5/6] backports: add __skb_peek() Johannes Berg
@ 2018-10-02 19:32 ` Johannes Berg
2018-10-02 19:33 ` [PATCH v2 1/6] backports: rename magic functions for netlink parsing Johannes Berg
5 siblings, 0 replies; 8+ messages in thread
From: Johannes Berg @ 2018-10-02 19:32 UTC (permalink / raw)
To: backports; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Replace all the different nested versions of generic netlink
backport with a single one, covering from < 3.13 all the way
to the upcoming netlink policy improvements in 4.20.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
backport/backport-include/linux/netlink.h | 2 +-
backport/backport-include/net/genetlink.h | 238 ++++++-----------
backport/compat/Makefile | 3 +-
backport/compat/backport-3.13.c | 66 -----
backport/compat/backport-4.12.c | 274 -------------------
backport/compat/backport-genetlink.c | 429 ++++++++++++++++++++++++++++++
backport/compat/compat-3.3.c | 13 -
7 files changed, 513 insertions(+), 512 deletions(-)
delete mode 100644 backport/compat/backport-4.12.c
create mode 100644 backport/compat/backport-genetlink.c
diff --git a/backport/backport-include/linux/netlink.h b/backport/backport-include/linux/netlink.h
index 366c9e27e8c2..f956a76966f3 100644
--- a/backport/backport-include/linux/netlink.h
+++ b/backport/backport-include/linux/netlink.h
@@ -20,7 +20,7 @@ struct netlink_ext_ack {
u8 cookie_len;
/* backport only field */
- void *__bp_genl_real_ops;
+ void *__bp_doit;
};
#define NL_SET_ERR_MSG(extack, msg) do { \
diff --git a/backport/backport-include/net/genetlink.h b/backport/backport-include/net/genetlink.h
index d80d979ac423..84011e72644e 100644
--- a/backport/backport-include/net/genetlink.h
+++ b/backport/backport-include/net/genetlink.h
@@ -15,18 +15,14 @@ static inline void *__bp_genl_info_userhdr(struct genl_info *info)
}
#if LINUX_VERSION_IS_LESS(4,12,0)
-#define GENL_SET_ERR_MSG(info, msg) do { } while (0)
+#define GENL_SET_ERR_MSG(info, msg) NL_SET_ERR_MSG(genl_info_extack(info), msg)
static inline int genl_err_attr(struct genl_info *info, int err,
struct nlattr *attr)
{
-#if LINUX_VERSION_IS_GEQ(4,12,0)
- info->extack->bad_attr = attr;
-#endif
-
return err;
}
-#endif
+#endif /* < 4.12 */
/* this is for patches we apply */
static inline struct netlink_ext_ack *genl_info_extack(struct genl_info *info)
@@ -51,12 +47,24 @@ static inline void *genl_info_userhdr(struct genl_info *info)
#define genl_info_snd_portid(__genl_info) (__genl_info->snd_portid)
#endif
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#define __genl_const
+#else /* < 3.13 */
+#define __genl_const const
+#endif /* < 3.13 */
+
#ifndef GENLMSG_DEFAULT_SIZE
#define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN)
#endif
#if LINUX_VERSION_IS_LESS(3,1,0)
-#define genl_dump_check_consistent(cb, user_hdr)
+#define genl_dump_check_consistent(cb, user_hdr) do { } while (0)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,10,0)
+#define __genl_ro_after_init
+#else
+#define __genl_ro_after_init __ro_after_init
#endif
#if LINUX_VERSION_IS_LESS(4,15,0)
@@ -83,148 +91,87 @@ void backport_genl_dump_check_consistent(struct netlink_callback *cb,
#endif
#endif /* LINUX_VERSION_IS_LESS(4,15,0) */
-#if LINUX_VERSION_IS_LESS(3,13,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
-static inline int __real_genl_register_family(struct genl_family *family)
+#if LINUX_VERSION_IS_LESS(4,20,0)
+static inline int
+__real_backport_genl_register_family(struct genl_family *family)
{
return genl_register_family(family);
}
-
-/* Needed for the mcgrps pointer */
-struct backport_genl_family {
- struct genl_family family;
-
- unsigned int id, hdrsize, version, maxattr;
- char name[GENL_NAMSIZ];
- bool netnsok;
- bool parallel_ops;
-
- struct nlattr **attrbuf;
-
- int (*pre_doit)(struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info);
-
- void (*post_doit)(struct genl_ops *ops, struct sk_buff *skb,
- struct genl_info *info);
-
- struct genl_multicast_group *mcgrps;
- struct genl_ops *ops;
- unsigned int n_mcgrps, n_ops;
-
- struct module *module;
-};
-#define genl_family LINUX_BACKPORT(genl_family)
-
-int __backport_genl_register_family(struct genl_family *family);
-
-#define genl_register_family LINUX_BACKPORT(genl_register_family)
static inline int
-genl_register_family(struct genl_family *family)
+__real_backport_genl_unregister_family(struct genl_family *family)
{
- family->module = THIS_MODULE;
- return __backport_genl_register_family(family);
+ return genl_unregister_family(family);
}
-#define _genl_register_family_with_ops_grps \
- _backport_genl_register_family_with_ops_grps
-static inline int
-_genl_register_family_with_ops_grps(struct genl_family *family,
- struct genl_ops *ops, size_t n_ops,
- struct genl_multicast_group *mcgrps,
- size_t n_mcgrps)
-{
- family->ops = ops;
- family->n_ops = n_ops;
- family->mcgrps = mcgrps;
- family->n_mcgrps = n_mcgrps;
- return genl_register_family(family);
-}
+struct backport_genl_family {
+ struct genl_family family;
+ const struct genl_ops * copy_ops;
+
+ /* copied */
+ int id; /* private */
+ unsigned int hdrsize;
+ char name[GENL_NAMSIZ];
+ unsigned int version;
+ unsigned int maxattr;
+ bool netnsok;
+ bool parallel_ops;
+ int (*pre_doit)(__genl_const struct genl_ops *ops,
+ struct sk_buff *skb,
+ struct genl_info *info);
+ void (*post_doit)(__genl_const struct genl_ops *ops,
+ struct sk_buff *skb,
+ struct genl_info *info);
+/*
+ * unsupported!
+ int (*mcast_bind)(struct net *net, int group);
+ void (*mcast_unbind)(struct net *net, int group);
+ */
+ struct nlattr ** attrbuf; /* private */
+ __genl_const struct genl_ops * ops;
+ __genl_const struct genl_multicast_group *mcgrps;
+ unsigned int n_ops;
+ unsigned int n_mcgrps;
+ struct module *module;
+};
+#undef genl_family
+#define genl_family backport_genl_family
-#define genl_register_family_with_ops(family, ops) \
- _genl_register_family_with_ops_grps((family), \
- (ops), ARRAY_SIZE(ops), \
- NULL, 0)
-#define genl_register_family_with_ops_groups(family, ops, grps) \
- _genl_register_family_with_ops_grps((family), \
- (ops), ARRAY_SIZE(ops), \
- (grps), ARRAY_SIZE(grps))
+#define genl_register_family backport_genl_register_family
+int genl_register_family(struct genl_family *family);
#define genl_unregister_family backport_genl_unregister_family
-int genl_unregister_family(struct genl_family *family);
+int backport_genl_unregister_family(struct genl_family *family);
-#if LINUX_VERSION_IS_LESS(3,3,0)
-extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid,
- u32 group, struct nlmsghdr *nlh, gfp_t flags);
-#endif
-#define genl_notify(_fam, _skb, _info, _group, _flags) \
- genl_notify(_skb, genl_info_net(_info), \
- genl_info_snd_portid(_info), \
- (_fam)->mcgrps[_group].id, _info->nlhdr, _flags)
-#define genlmsg_put(_skb, _pid, _seq, _fam, _flags, _cmd) \
- genlmsg_put(_skb, _pid, _seq, &(_fam)->family, _flags, _cmd)
+#define genl_notify LINUX_BACKPORT(genl_notify)
+void genl_notify(const struct genl_family *family, struct sk_buff *skb,
+ struct genl_info *info, u32 group, gfp_t flags);
+
+#define genlmsg_put LINUX_BACKPORT(genlmsg_put)
+void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
+ const struct genl_family *family, int flags, u8 cmd);
+
+#define genlmsg_put_reply LINUX_BACKPORT(genlmsg_put_reply)
+void *genlmsg_put_reply(struct sk_buff *skb,
+ struct genl_info *info,
+ const struct genl_family *family,
+ int flags, u8 cmd);
-#ifndef genlmsg_put_reply /* might already be there from _info override above */
-#define genlmsg_put_reply(_skb, _info, _fam, _flags, _cmd) \
- genlmsg_put_reply(_skb, _info, &(_fam)->family, _flags, _cmd)
-#endif
#define genlmsg_multicast_netns LINUX_BACKPORT(genlmsg_multicast_netns)
-static inline int genlmsg_multicast_netns(struct genl_family *family,
- struct net *net, struct sk_buff *skb,
- u32 portid, unsigned int group,
- gfp_t flags)
-{
- if (WARN_ON_ONCE(group >= family->n_mcgrps))
- return -EINVAL;
- group = family->mcgrps[group].id;
- return nlmsg_multicast(
- net->genl_sock,
- skb, portid, group, flags);
-}
+int genlmsg_multicast_netns(const struct genl_family *family,
+ struct net *net, struct sk_buff *skb,
+ u32 portid, unsigned int group,
+ gfp_t flags);
+
#define genlmsg_multicast LINUX_BACKPORT(genlmsg_multicast)
-static inline int genlmsg_multicast(struct genl_family *family,
- struct sk_buff *skb, u32 portid,
- unsigned int group, gfp_t flags)
-{
- if (WARN_ON_ONCE(group >= family->n_mcgrps))
- return -EINVAL;
- group = family->mcgrps[group].id;
- return nlmsg_multicast(
- init_net.genl_sock,
- skb, portid, group, flags);
-}
-static inline int
-backport_genlmsg_multicast_allns(struct genl_family *family,
- struct sk_buff *skb, u32 portid,
- unsigned int group, gfp_t flags)
-{
- if (WARN_ON_ONCE(group >= family->n_mcgrps))
- return -EINVAL;
- group = family->mcgrps[group].id;
- return genlmsg_multicast_allns(skb, portid, group, flags);
-}
-#define genlmsg_multicast_allns LINUX_BACKPORT(genlmsg_multicast_allns)
+int genlmsg_multicast(const struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags);
-#define __genl_const
-#else /* < 3.13 */
-#define __genl_const const
-#if LINUX_VERSION_IS_LESS(4,4,0)
-#define genl_notify(_fam, _skb, _info, _group, _flags) \
- genl_notify(_fam, _skb, genl_info_net(_info), \
- genl_info_snd_portid(_info), \
- _group, _info->nlhdr, _flags)
-#endif /* < 4.4 */
-#endif /* < 3.13 */
+#define genlmsg_multicast_allns LINUX_BACKPORT(genlmsg_multicast_allns)
+int backport_genlmsg_multicast_allns(const struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags);
-#if LINUX_VERSION_IS_LESS(4,10,0)
-/**
- * genl_family_attrbuf - return family's attrbuf
- * @family: the family
- *
- * Return the family's attrbuf, while validating that it's
- * actually valid to access it.
- *
- * You cannot use this function with a family that has parallel_ops
- * and you can only use it within (pre/post) doit/dumpit callbacks.
- */
#define genl_family_attrbuf LINUX_BACKPORT(genl_family_attrbuf)
static inline struct nlattr **genl_family_attrbuf(struct genl_family *family)
{
@@ -232,29 +179,6 @@ static inline struct nlattr **genl_family_attrbuf(struct genl_family *family)
return family->attrbuf;
}
-
-#define __genl_ro_after_init
-#else
-#define __genl_ro_after_init __ro_after_init
-#endif
-
-#if LINUX_VERSION_IS_LESS(4,12,0)
-static inline int
-__real_bp_extack_genl_register_family(struct genl_family *family)
-{
- return genl_register_family(family);
-}
-static inline int
-__real_bp_extack_genl_unregister_family(struct genl_family *family)
-{
- return genl_unregister_family(family);
-}
-int bp_extack_genl_register_family(struct genl_family *family);
-int bp_extack_genl_unregister_family(struct genl_family *family);
-#undef genl_register_family
-#define genl_register_family bp_extack_genl_register_family
-#undef genl_unregister_family
-#define genl_unregister_family bp_extack_genl_unregister_family
-#endif
+#endif /* LINUX_VERSION_IS_LESS(4,20,0) */
#endif /* __BACKPORT_NET_GENETLINK_H */
diff --git a/backport/compat/Makefile b/backport/compat/Makefile
index ff6c7e658380..fdc68c8b4600 100644
--- a/backport/compat/Makefile
+++ b/backport/compat/Makefile
@@ -36,10 +36,11 @@ compat-$(CPTCFG_KERNEL_4_6) += backport-4.6.o
compat-$(CPTCFG_KERNEL_4_7) += backport-4.7.o
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_KERNEL_4_20) += backport-genetlink.o
+
compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o
compat-$(CPTCFG_BPAUTO_BUILD_SYSTEM_DATA_VERIFICATION) += verification/verify.o
diff --git a/backport/compat/backport-3.13.c b/backport/compat/backport-3.13.c
index c33a04432ddc..2b61711d233d 100644
--- a/backport/compat/backport-3.13.c
+++ b/backport/compat/backport-3.13.c
@@ -11,78 +11,12 @@
*/
#include <linux/version.h>
#include <linux/kernel.h>
-#include <net/genetlink.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/net.h>
-/************* generic netlink backport *****************/
-#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
-
-#undef genl_register_family
-#undef genl_unregister_family
-
-int __backport_genl_register_family(struct genl_family *family)
-{
- int i, ret;
-
-#define __copy(_field) family->family._field = family->_field
- __copy(id);
- __copy(hdrsize);
- __copy(version);
- __copy(maxattr);
- strncpy(family->family.name, family->name, sizeof(family->family.name));
- __copy(netnsok);
- __copy(pre_doit);
- __copy(post_doit);
-#if LINUX_VERSION_IS_GEQ(3,10,0)
- __copy(parallel_ops);
-#endif
-#if LINUX_VERSION_IS_GEQ(3,11,0)
- __copy(module);
-#endif
-#undef __copy
-
- ret = genl_register_family(&family->family);
- if (ret < 0)
- return ret;
-
- family->attrbuf = family->family.attrbuf;
- family->id = family->family.id;
-
- for (i = 0; i < family->n_ops; i++) {
- ret = genl_register_ops(&family->family, &family->ops[i]);
- if (ret < 0)
- goto error;
- }
-
- for (i = 0; i < family->n_mcgrps; i++) {
- ret = genl_register_mc_group(&family->family,
- &family->mcgrps[i]);
- if (ret)
- goto error;
- }
-
- return 0;
-
- error:
- backport_genl_unregister_family(family);
- return ret;
-}
-EXPORT_SYMBOL_GPL(__backport_genl_register_family);
-
-int backport_genl_unregister_family(struct genl_family *family)
-{
- int err;
- err = genl_unregister_family(&family->family);
- return err;
-}
-EXPORT_SYMBOL_GPL(backport_genl_unregister_family);
-
-#endif /* RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) */
-
#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
struct __net_random_once_work {
struct work_struct work;
diff --git a/backport/compat/backport-4.12.c b/backport/compat/backport-4.12.c
deleted file mode 100644
index 5fc8c10cb8d8..000000000000
--- a/backport/compat/backport-4.12.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright 2017 Intel Deutschland GmbH
- */
-#include <net/genetlink.h>
-#include <net/sock.h>
-
-enum nlmsgerr_attrs {
- NLMSGERR_ATTR_UNUSED,
- NLMSGERR_ATTR_MSG,
- NLMSGERR_ATTR_OFFS,
- NLMSGERR_ATTR_COOKIE,
- __NLMSGERR_ATTR_MAX,
- NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
-};
-
-#define NLM_F_CAPPED 0x100 /* request was capped */
-#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
-
-struct bp_extack_genl_family {
- struct genl_family family;
- struct genl_family *real_family;
- struct list_head list;
-
- struct genl_ops ops[];
-};
-
-static LIST_HEAD(copies_list);
-static DEFINE_MUTEX(copies_mutex);
-
-static const struct nla_policy extack_dummy_policy[1] = {};
-
-static struct bp_extack_genl_family *get_copy(__genl_const struct genl_ops *op)
-{
- do {
- op--;
- } while (op->policy != extack_dummy_policy);
-
- return container_of(op, struct bp_extack_genl_family, ops[0]);
-}
-
-static int extack_pre_doit(__genl_const struct genl_ops *ops,
- struct sk_buff *skb,
- struct genl_info *info)
-{
- struct netlink_ext_ack *extack = kzalloc(sizeof(*extack), GFP_KERNEL);
- struct bp_extack_genl_family *copy = get_copy(ops);
- struct genl_ops *real_ops;
- int err;
-
- __bp_genl_info_userhdr_set(info, extack);
-
- if (!extack) {
- __bp_genl_info_userhdr_set(info, ERR_PTR(-ENOMEM));
- return -ENOMEM;
- }
-
- real_ops = (void *)©->real_family->ops[ops - ©->ops[1]];
- extack->__bp_genl_real_ops = real_ops;
-
- if (copy->real_family->pre_doit)
- err = copy->real_family->pre_doit(real_ops, skb, info);
- else
- err = 0;
-
- if (err) {
- __bp_genl_info_userhdr_set(info, ERR_PTR(err));
- kfree(extack);
- }
-
- return err;
-}
-
-static void extack_netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh,
- int err, const struct netlink_ext_ack *extack)
-{
- struct sk_buff *skb;
- struct nlmsghdr *rep;
- struct nlmsgerr *errmsg;
- size_t payload = sizeof(*errmsg);
- size_t tlvlen = 0;
- unsigned int flags = 0;
- /* backports assumes everyone supports this - libnl does so it's true */
- bool nlk_has_extack = true;
-
- /* Error messages get the original request appened, unless the user
- * requests to cap the error message, and get extra error data if
- * requested.
- * (ignored in backports)
- */
- if (nlk_has_extack && extack && extack->_msg)
- tlvlen += nla_total_size(strlen(extack->_msg) + 1);
-
- if (err) {
- if (1)
- payload += nlmsg_len(nlh);
- else
- flags |= NLM_F_CAPPED;
- if (nlk_has_extack && extack && extack->bad_attr)
- tlvlen += nla_total_size(sizeof(u32));
- } else {
- flags |= NLM_F_CAPPED;
-
- if (nlk_has_extack && extack && extack->cookie_len)
- tlvlen += nla_total_size(extack->cookie_len);
- }
-
- if (tlvlen)
- flags |= NLM_F_ACK_TLVS;
-
- skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
- if (!skb) {
- NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
- NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);
- return;
- }
-
- rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
- NLMSG_ERROR, payload, flags);
- errmsg = nlmsg_data(rep);
- errmsg->error = err;
- memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
-
- if (nlk_has_extack && extack) {
- if (extack->_msg) {
- WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
- extack->_msg));
- }
- if (err) {
- if (extack->bad_attr &&
- !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
- (u8 *)extack->bad_attr >= in_skb->data +
- in_skb->len))
- WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
- (u8 *)extack->bad_attr -
- in_skb->data));
- } else {
- if (extack->cookie_len)
- WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
- extack->cookie_len,
- extack->cookie));
- }
- }
-
- nlmsg_end(skb, rep);
-
- netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
-}
-
-static int extack_doit(struct sk_buff *skb, struct genl_info *info)
-{
- struct genl_ops *real_ops;
- int err;
-
- /* older kernels have a bug here */
- if (IS_ERR(__bp_genl_info_userhdr(info))) {
- extack_netlink_ack(skb, info->nlhdr,
- PTR_ERR(__bp_genl_info_userhdr(info)),
- genl_info_extack(info));
- goto out;
- }
-
- real_ops = genl_info_extack(info)->__bp_genl_real_ops;
- err = real_ops->doit(skb, info);
-
- if (err == -EINTR)
- return err;
-
- if (info->nlhdr->nlmsg_flags & NLM_F_ACK || err)
- extack_netlink_ack(skb, info->nlhdr, err,
- genl_info_extack(info));
-
-out:
- /* suppress sending ACK from normal netlink code */
- info->nlhdr->nlmsg_flags &= ~NLM_F_ACK;
- return 0;
-}
-
-static void extack_post_doit(__genl_const struct genl_ops *ops,
- struct sk_buff *skb,
- struct genl_info *info)
-{
- void (*post_doit)(__genl_const struct genl_ops *ops,
- struct sk_buff *skb,
- struct genl_info *info);
-
- post_doit = get_copy(ops)->real_family->post_doit;
-
- if (post_doit)
- post_doit(ops, skb, info);
- kfree(__bp_genl_info_userhdr(info));
-}
-
-int bp_extack_genl_register_family(struct genl_family *family)
-{
- unsigned int size = sizeof(struct bp_extack_genl_family) +
- sizeof(family->ops[0]) * (family->n_ops + 1);
- struct bp_extack_genl_family *copy;
- int i, err;
-
- copy = kzalloc(size, GFP_KERNEL);
- if (!copy)
- return -ENOMEM;
-
- copy->family = *family;
- copy->real_family = family;
- copy->family.ops = ©->ops[1];
-
- for (i = 0; i < family->n_ops; i++) {
- copy->ops[i + 1] = family->ops[i];
- if (family->ops[i].doit)
- copy->ops[i + 1].doit = extack_doit;
- }
-
- copy->ops[0].policy = extack_dummy_policy;
-
- copy->family.pre_doit = extack_pre_doit;
- copy->family.post_doit = extack_post_doit;
-
- err = __real_bp_extack_genl_register_family(©->family);
- if (err) {
- kfree(copy);
- return err;
- }
-
- /* copy this since the family might access it directly */
- family->id = copy->family.id;
- family->attrbuf = copy->family.attrbuf;
-
-#if LINUX_VERSION_IS_LESS(3,13,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
- /* family ID from the original family struct will be used when building
- * genl messages (sent as nlmsg_type), so the new id should be updated
- * in the original (older kernel format) family struct too
- */
- family->family.id = copy->family.id;
-#endif
-
-#if LINUX_VERSION_IS_GEQ(3,13,0)
- family->mcgrp_offset = copy->family.mcgrp_offset;
-#endif
-
- mutex_lock(&copies_mutex);
- list_add_tail(©->list, &copies_list);
- mutex_unlock(&copies_mutex);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bp_extack_genl_register_family);
-
-int bp_extack_genl_unregister_family(struct genl_family *family)
-{
- struct bp_extack_genl_family *tmp, *copy = NULL;
- int err;
-
- mutex_lock(&copies_mutex);
- list_for_each_entry(tmp, &copies_list, list) {
- if (tmp->real_family == family) {
- copy = tmp;
- break;
- }
- }
- if (copy)
- list_del(©->list);
- mutex_unlock(&copies_mutex);
-
- if (!copy)
- return -ENOENT;
-
- err = __real_bp_extack_genl_unregister_family(©->family);
- WARN_ON(err);
- kfree(copy);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bp_extack_genl_unregister_family);
diff --git a/backport/compat/backport-genetlink.c b/backport/compat/backport-genetlink.c
new file mode 100644
index 000000000000..cc70cd73d0c0
--- /dev/null
+++ b/backport/compat/backport-genetlink.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Backport functionality introduced in Linux 4.20.
+ * Much of this is based on 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/genetlink.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+
+static const struct genl_family *find_family_real_ops(__genl_const struct genl_ops **ops)
+{
+ const struct genl_family *family;
+ const struct genl_ops *tmp_ops = *ops;
+
+ /* find the family ... */
+ while (tmp_ops->doit || tmp_ops->dumpit)
+ tmp_ops++;
+ family = (void *)tmp_ops->done;
+
+ /* cast to suppress const warning */
+ *ops = (void *)(family->ops + (*ops - family->copy_ops));
+
+ return family;
+}
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+
+#define NLM_F_CAPPED 0x100 /* request was capped */
+#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
+
+static void extack_netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+ int err, const struct netlink_ext_ack *extack)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *rep;
+ struct nlmsgerr *errmsg;
+ size_t payload = sizeof(*errmsg);
+ size_t tlvlen = 0;
+ unsigned int flags = 0;
+ /* backports assumes everyone supports this - libnl does so it's true */
+ bool nlk_has_extack = true;
+
+ /* Error messages get the original request appened, unless the user
+ * requests to cap the error message, and get extra error data if
+ * requested.
+ * (ignored in backports)
+ */
+ if (nlk_has_extack && extack && extack->_msg)
+ tlvlen += nla_total_size(strlen(extack->_msg) + 1);
+
+ if (err) {
+ if (1)
+ payload += nlmsg_len(nlh);
+ else
+ flags |= NLM_F_CAPPED;
+ if (nlk_has_extack && extack && extack->bad_attr)
+ tlvlen += nla_total_size(sizeof(u32));
+ } else {
+ flags |= NLM_F_CAPPED;
+
+ if (nlk_has_extack && extack && extack->cookie_len)
+ tlvlen += nla_total_size(extack->cookie_len);
+ }
+
+ if (tlvlen)
+ flags |= NLM_F_ACK_TLVS;
+
+ skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
+ if (!skb) {
+ NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
+ NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);
+ return;
+ }
+
+ rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
+ NLMSG_ERROR, payload, flags);
+ errmsg = nlmsg_data(rep);
+ errmsg->error = err;
+ memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
+
+ if (nlk_has_extack && extack) {
+ if (extack->_msg) {
+ WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
+ extack->_msg));
+ }
+ if (err) {
+ if (extack->bad_attr &&
+ !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
+ (u8 *)extack->bad_attr >= in_skb->data +
+ in_skb->len))
+ WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
+ (u8 *)extack->bad_attr -
+ in_skb->data));
+ } else {
+ if (extack->cookie_len)
+ WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
+ extack->cookie_len,
+ extack->cookie));
+ }
+ }
+
+ nlmsg_end(skb, rep);
+
+ netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
+}
+
+static int extack_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ int (*doit)(struct sk_buff *, struct genl_info *);
+ int err;
+
+ doit = genl_info_extack(info)->__bp_doit;
+
+ /* signal from our pre_doit to not do anything */
+ if (!doit)
+ return 0;
+
+ err = doit(skb, info);
+
+ if (err == -EINTR)
+ return err;
+
+ if (info->nlhdr->nlmsg_flags & NLM_F_ACK || err)
+ extack_netlink_ack(skb, info->nlhdr, err,
+ genl_info_extack(info));
+
+ /* suppress sending ACK from normal netlink code */
+ info->nlhdr->nlmsg_flags &= ~NLM_F_ACK;
+ return 0;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,12,0) */
+
+static int backport_pre_doit(__genl_const struct genl_ops *ops,
+ struct sk_buff *skb,
+ struct genl_info *info)
+{
+ const struct genl_family *family = find_family_real_ops(&ops);
+ int err;
+#if LINUX_VERSION_IS_LESS(4,12,0)
+ struct netlink_ext_ack *extack = kzalloc(sizeof(*extack), GFP_KERNEL);
+
+ __bp_genl_info_userhdr_set(info, extack);
+
+ if (!extack) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ extack->__bp_doit = ops->doit;
+#else
+ struct netlink_ext_ack *extack = info->extack;
+#endif
+
+ err = nlmsg_validate(info->nlhdr, GENL_HDRLEN + family->hdrsize,
+ family->maxattr, ops->policy, extack);
+ if (!err)
+ err = family->pre_doit(ops, skb, info);
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+err:
+ if (err) {
+ /* signal to do nothing */
+ extack->__bp_doit = NULL;
+
+ extack_netlink_ack(skb, info->nlhdr, err, extack);
+
+ /* suppress sending ACK from normal netlink code */
+ info->nlhdr->nlmsg_flags &= ~NLM_F_ACK;
+
+ /* extack will be freed in post_doit as usual */
+
+ return 0;
+ }
+#endif
+
+ return err;
+}
+
+static void backport_post_doit(__genl_const struct genl_ops *ops,
+ struct sk_buff *skb,
+ struct genl_info *info)
+{
+ const struct genl_family *family = find_family_real_ops(&ops);
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+ if (genl_info_extack(info)->__bp_doit)
+#else
+ if (1)
+#endif
+ family->post_doit(ops, skb, info);
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+ kfree(__bp_genl_info_userhdr(info));
+#endif
+}
+
+int backport_genl_register_family(struct genl_family *family)
+{
+ struct genl_ops *ops;
+ int err, i;
+
+#define COPY(memb) family->family.memb = family->memb
+#define BACK(memb) family->memb = family->family.memb
+
+ /* we append one entry to the ops to find our family pointer ... */
+ ops = kzalloc(sizeof(*ops) * (family->n_ops + 1), GFP_KERNEL);
+ memcpy(ops, family->ops, sizeof(*ops) * family->n_ops);
+ /*
+ * Remove policy to skip validation as the struct nla_policy
+ * memory layout isn't compatible with the old version
+ */
+ for (i = 0; i < family->n_ops; i++) {
+ ops[i].policy = NULL;
+#if LINUX_VERSION_IS_LESS(4,12,0)
+ if (ops[i].doit)
+ ops[i].doit = extack_doit;
+#endif
+ }
+ /* keep doit/dumpit NULL - that's invalid */
+ ops[family->n_ops].done = (void *)family;
+
+ COPY(id);
+ memcpy(family->family.name, family->name, sizeof(family->name));
+ COPY(hdrsize);
+ COPY(version);
+ COPY(maxattr);
+ COPY(netnsok);
+#if LINUX_VERSION_IS_GEQ(3,10,0)
+ COPY(parallel_ops);
+#endif
+ family->family.pre_doit = backport_pre_doit;
+ family->family.post_doit = backport_post_doit;
+ /* attrbuf is output only */
+ family->copy_ops = ops;
+#if LINUX_VERSION_IS_GEQ(3,13,0)
+ family->family.ops = ops;
+ COPY(mcgrps);
+ COPY(n_ops);
+ COPY(n_mcgrps);
+#endif
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+ COPY(module);
+#endif
+
+ err = __real_backport_genl_register_family(&family->family);
+
+ BACK(id);
+ BACK(attrbuf);
+
+ if (err)
+ return err;
+
+#if LINUX_VERSION_IS_GEQ(3,13,0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0)
+ return 0;
+#else
+ for (i = 0; i < family->n_ops; i++) {
+ err = genl_register_ops(&family->family, ops + i);
+ if (err < 0)
+ goto error;
+ }
+
+ for (i = 0; i < family->n_mcgrps; i++) {
+ err = genl_register_mc_group(&family->family,
+ &family->mcgrps[i]);
+ if (err)
+ goto error;
+ }
+
+ return 0;
+ error:
+ genl_unregister_family(family);
+ return err;
+#endif /* LINUX_VERSION_IS_GEQ(3,13,0) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0) */
+}
+EXPORT_SYMBOL_GPL(backport_genl_register_family);
+
+int backport_genl_unregister_family(struct genl_family *family)
+{
+ return __real_backport_genl_unregister_family(&family->family);
+}
+EXPORT_SYMBOL_GPL(backport_genl_unregister_family);
+
+#define INVALID_GROUP 0xffffffff
+
+static u32 __backport_genl_group(const struct genl_family *family,
+ u32 group)
+{
+ if (WARN_ON_ONCE(group >= family->n_mcgrps))
+ return INVALID_GROUP;
+#if LINUX_VERSION_IS_LESS(3,13,0)
+ return family->mcgrps[group].id;
+#else
+ return family->family.mcgrp_offset + group;
+#endif
+}
+
+void genl_notify(const struct genl_family *family, struct sk_buff *skb,
+ struct genl_info *info, u32 group, gfp_t flags)
+{
+ struct net *net = genl_info_net(info);
+ struct sock *sk = net->genl_sock;
+ int report = 0;
+
+ if (info->nlhdr)
+ report = nlmsg_report(info->nlhdr);
+
+ group = __backport_genl_group(family, group);
+ if (group == INVALID_GROUP)
+ return;
+ nlmsg_notify(sk, skb, info->snd_portid, group, report, flags);
+}
+EXPORT_SYMBOL_GPL(genl_notify);
+
+void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
+ const struct genl_family *family, int flags, u8 cmd)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *hdr;
+
+ nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN +
+ family->hdrsize, flags);
+ if (nlh == NULL)
+ return NULL;
+
+ hdr = nlmsg_data(nlh);
+ hdr->cmd = cmd;
+ hdr->version = family->version;
+ hdr->reserved = 0;
+
+ return (char *) hdr + GENL_HDRLEN;
+}
+EXPORT_SYMBOL_GPL(genlmsg_put);
+
+void *genlmsg_put_reply(struct sk_buff *skb,
+ struct genl_info *info,
+ const struct genl_family *family,
+ int flags, u8 cmd)
+{
+ return genlmsg_put(skb, info->snd_portid, info->snd_seq, family,
+ flags, cmd);
+}
+EXPORT_SYMBOL_GPL(genlmsg_put_reply);
+
+int genlmsg_multicast_netns(const struct genl_family *family,
+ struct net *net, struct sk_buff *skb,
+ u32 portid, unsigned int group,
+ gfp_t flags)
+{
+ group = __backport_genl_group(family, group);
+ if (group == INVALID_GROUP)
+ return -EINVAL;
+ return nlmsg_multicast(net->genl_sock, skb, portid, group, flags);
+}
+EXPORT_SYMBOL_GPL(genlmsg_multicast_netns);
+
+int genlmsg_multicast(const struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags)
+{
+ return genlmsg_multicast_netns(family, &init_net, skb,
+ portid, group, flags);
+}
+EXPORT_SYMBOL_GPL(genlmsg_multicast);
+
+static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
+ gfp_t flags)
+{
+ struct sk_buff *tmp;
+ struct net *net, *prev = NULL;
+ bool delivered = false;
+ int err;
+
+ for_each_net_rcu(net) {
+ if (prev) {
+ tmp = skb_clone(skb, flags);
+ if (!tmp) {
+ err = -ENOMEM;
+ goto error;
+ }
+ err = nlmsg_multicast(prev->genl_sock, tmp,
+ portid, group, flags);
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
+ goto error;
+ }
+
+ prev = net;
+ }
+
+ err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
+ return err;
+ return delivered ? 0 : -ESRCH;
+ error:
+ kfree_skb(skb);
+ return err;
+}
+
+int backport_genlmsg_multicast_allns(const struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags)
+{
+ group = __backport_genl_group(family, group);
+ if (group == INVALID_GROUP)
+ return -EINVAL;
+ return genlmsg_mcast(skb, portid, group, flags);
+}
+EXPORT_SYMBOL_GPL(backport_genlmsg_multicast_allns);
diff --git a/backport/compat/compat-3.3.c b/backport/compat/compat-3.3.c
index c064f09d9abe..1185a5d2d4e0 100644
--- a/backport/compat/compat-3.3.c
+++ b/backport/compat/compat-3.3.c
@@ -224,16 +224,3 @@ void backport_destroy_workqueue(struct workqueue_struct *wq)
spin_unlock(&wq_name_lock);
}
EXPORT_SYMBOL_GPL(backport_destroy_workqueue);
-
-void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
- struct nlmsghdr *nlh, gfp_t flags)
-{
- struct sock *sk = net->genl_sock;
- int report = 0;
-
- if (nlh)
- report = nlmsg_report(nlh);
-
- nlmsg_notify(sk, skb, pid, group, report, flags);
-}
-EXPORT_SYMBOL_GPL(genl_notify);
--
2.14.4
--
To unsubscribe from this list: send the line "unsubscribe backports" in
^ permalink raw reply related [flat|nested] 8+ messages in thread