diff options
-rw-r--r-- | backport/backport-include/linux/netlink.h | 3 | ||||
-rw-r--r-- | backport/backport-include/net/genetlink.h | 27 | ||||
-rw-r--r-- | backport/compat/Makefile | 1 | ||||
-rw-r--r-- | backport/compat/backport-4.12.c | 243 | ||||
-rw-r--r-- | patches/0078-genl-extack.cocci | 6 |
5 files changed, 279 insertions, 1 deletions
diff --git a/backport/backport-include/linux/netlink.h b/backport/backport-include/linux/netlink.h index 58fad589..44359918 100644 --- a/backport/backport-include/linux/netlink.h +++ b/backport/backport-include/linux/netlink.h @@ -11,6 +11,9 @@ struct netlink_ext_ack { const struct nlattr *bad_attr; u8 cookie[NETLINK_MAX_COOKIE_LEN]; u8 cookie_len; + + /* backport only field */ + const void *__bp_genl_real_ops; }; #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 b655d243..65a4a4b2 100644 --- a/backport/backport-include/net/genetlink.h +++ b/backport/backport-include/net/genetlink.h @@ -23,10 +23,16 @@ static inline struct netlink_ext_ack *genl_info_extack(struct genl_info *info) #if LINUX_VERSION_IS_GEQ(4,12,0) return info->extack; #else - return NULL; + return info->userhdr; #endif } +/* this gets put in place of info->userhdr, since we use that above */ +static inline void *genl_info_userhdr(struct genl_info *info) +{ + return (u8 *)info->genlhdr + GENL_HDRLEN; +} + /* this is for patches we apply */ #if LINUX_VERSION_IS_LESS(3,7,0) #define genl_info_snd_portid(__genl_info) (__genl_info->snd_pid) @@ -202,4 +208,23 @@ static inline struct nlattr **genl_family_attrbuf(struct genl_family *family) #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 /* __BACKPORT_NET_GENETLINK_H */ diff --git a/backport/compat/Makefile b/backport/compat/Makefile index 69cfd514..5c79b9b8 100644 --- a/backport/compat/Makefile +++ b/backport/compat/Makefile @@ -36,6 +36,7 @@ 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_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o diff --git a/backport/compat/backport-4.12.c b/backport/compat/backport-4.12.c new file mode 100644 index 00000000..73973bca --- /dev/null +++ b/backport/compat/backport-4.12.c @@ -0,0 +1,243 @@ +/* + * 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 genl_ops ops[]; +}; + +static const struct nla_policy extack_dummy_policy[1] = {}; + +static struct bp_extack_genl_family *get_copy(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(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); + const struct genl_ops *real_ops; + int err; + + info->userhdr = extack; + + if (!extack) { + info->userhdr = ERR_PTR(-ENOMEM); + return -ENOMEM; + } + + real_ops = ©->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) { + info->userhdr = 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 (err) { + if (1) + payload += nlmsg_len(nlh); + else + flags |= NLM_F_CAPPED; + if (nlk_has_extack && extack) { + if (extack->_msg) + tlvlen += nla_total_size(strlen(extack->_msg) + 1); + if (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 (err) { + if (extack->_msg) + WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, + extack->_msg)); + 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) +{ + const struct genl_ops *real_ops; + int err; + + /* older kernels have a bug here */ + if (IS_ERR(info->userhdr)) { + extack_netlink_ack(skb, info->nlhdr, + PTR_ERR(info->userhdr), + 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(const struct genl_ops *ops, + struct sk_buff *skb, + struct genl_info *info) +{ + void (*post_doit)(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(info->userhdr); +} + +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; + + /* + * store in attrbuf, so that even if we re-register the family + * the data will be overwritten and we don't overwrite data + * that's used again later... + */ + family->attrbuf = (void *)copy; + + err = __real_bp_extack_genl_register_family(©->family); + if (err) + kfree(copy); + return err; +} +EXPORT_SYMBOL_GPL(bp_extack_genl_register_family); + +int bp_extack_genl_unregister_family(struct genl_family *family) +{ + struct bp_extack_genl_family *copy = (void *)family->attrbuf; + int err; + + 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/patches/0078-genl-extack.cocci b/patches/0078-genl-extack.cocci index b2506174..5560e09d 100644 --- a/patches/0078-genl-extack.cocci +++ b/patches/0078-genl-extack.cocci @@ -3,3 +3,9 @@ struct genl_info *info; @@ -info->extack +genl_info_extack(info) + +@@ +struct genl_info *info; +@@ +-info->userhdr ++genl_info_userhdr(info) |