]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/netfilter/nf_tables_api.c
Merge branch 'work.probe_kernel_read' of git://git.kernel.org/pub/scm/linux/kernel...
[karo-tx-linux.git] / net / netfilter / nf_tables_api.c
index da314be0c048720172bbd153cd2f730b486603ce..7843efa33c598f9d1785bc64e99246941213d571 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/list.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
+#include <linux/vmalloc.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
@@ -386,7 +387,7 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
        return ++table->hgenerator;
 }
 
-static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
+static const struct nf_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
 
 static const struct nf_chain_type *
 __nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
@@ -534,7 +535,8 @@ done:
 
 static int nf_tables_gettable(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
@@ -677,7 +679,8 @@ err:
 
 static int nf_tables_newtable(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -830,7 +833,8 @@ out:
 
 static int nf_tables_deltable(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -869,6 +873,9 @@ int nft_register_chain_type(const struct nf_chain_type *ctype)
 {
        int err = 0;
 
+       if (WARN_ON(ctype->family >= NFPROTO_NUMPROTO))
+               return -EINVAL;
+
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
        if (chain_type[ctype->family][ctype->type] != NULL) {
                err = -EBUSY;
@@ -1123,7 +1130,8 @@ done:
 
 static int nf_tables_getchain(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
@@ -1319,7 +1327,8 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook)
 
 static int nf_tables_newchain(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        const struct nlattr * uninitialized_var(name);
@@ -1557,7 +1566,8 @@ err1:
 
 static int nf_tables_delchain(struct net *net, struct sock *nlsk,
                              struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                             const struct nlattr * const nla[],
+                             struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -2038,7 +2048,8 @@ static int nf_tables_dump_rules_done(struct netlink_callback *cb)
 
 static int nf_tables_getrule(struct net *net, struct sock *nlsk,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[])
+                            const struct nlattr * const nla[],
+                            struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
@@ -2131,7 +2142,8 @@ static struct nft_expr_info *info;
 
 static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[])
+                            const struct nlattr * const nla[],
+                            struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -2313,7 +2325,8 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
 
 static int nf_tables_delrule(struct net *net, struct sock *nlsk,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const nla[])
+                            const struct nlattr * const nla[],
+                            struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -2377,64 +2390,77 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
  * Sets
  */
 
-static LIST_HEAD(nf_tables_set_ops);
+static LIST_HEAD(nf_tables_set_types);
 
-int nft_register_set(struct nft_set_ops *ops)
+int nft_register_set(struct nft_set_type *type)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
-       list_add_tail_rcu(&ops->list, &nf_tables_set_ops);
+       list_add_tail_rcu(&type->list, &nf_tables_set_types);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
        return 0;
 }
 EXPORT_SYMBOL_GPL(nft_register_set);
 
-void nft_unregister_set(struct nft_set_ops *ops)
+void nft_unregister_set(struct nft_set_type *type)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
-       list_del_rcu(&ops->list);
+       list_del_rcu(&type->list);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 }
 EXPORT_SYMBOL_GPL(nft_unregister_set);
 
+#define NFT_SET_FEATURES       (NFT_SET_INTERVAL | NFT_SET_MAP | \
+                                NFT_SET_TIMEOUT | NFT_SET_OBJECT)
+
+static bool nft_set_ops_candidate(const struct nft_set_ops *ops, u32 flags)
+{
+       return (flags & ops->features) == (flags & NFT_SET_FEATURES);
+}
+
 /*
  * Select a set implementation based on the data characteristics and the
  * given policy. The total memory use might not be known if no size is
  * given, in that case the amount of memory per element is used.
  */
 static const struct nft_set_ops *
-nft_select_set_ops(const struct nlattr * const nla[],
+nft_select_set_ops(const struct nft_ctx *ctx,
+                  const struct nlattr * const nla[],
                   const struct nft_set_desc *desc,
                   enum nft_set_policies policy)
 {
        const struct nft_set_ops *ops, *bops;
        struct nft_set_estimate est, best;
-       u32 features;
+       const struct nft_set_type *type;
+       u32 flags = 0;
 
 #ifdef CONFIG_MODULES
-       if (list_empty(&nf_tables_set_ops)) {
+       if (list_empty(&nf_tables_set_types)) {
                nfnl_unlock(NFNL_SUBSYS_NFTABLES);
                request_module("nft-set");
                nfnl_lock(NFNL_SUBSYS_NFTABLES);
-               if (!list_empty(&nf_tables_set_ops))
+               if (!list_empty(&nf_tables_set_types))
                        return ERR_PTR(-EAGAIN);
        }
 #endif
-       features = 0;
-       if (nla[NFTA_SET_FLAGS] != NULL) {
-               features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
-               features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT |
-                           NFT_SET_OBJECT;
-       }
+       if (nla[NFTA_SET_FLAGS] != NULL)
+               flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
 
        bops        = NULL;
        best.size   = ~0;
        best.lookup = ~0;
        best.space  = ~0;
 
-       list_for_each_entry(ops, &nf_tables_set_ops, list) {
-               if ((ops->features & features) != features)
+       list_for_each_entry(type, &nf_tables_set_types, list) {
+               if (!type->select_ops)
+                       ops = type->ops;
+               else
+                       ops = type->select_ops(ctx, desc, flags);
+               if (!ops)
+                       continue;
+
+               if (!nft_set_ops_candidate(ops, flags))
                        continue;
-               if (!ops->estimate(desc, features, &est))
+               if (!ops->estimate(desc, flags, &est))
                        continue;
 
                switch (policy) {
@@ -2465,10 +2491,10 @@ nft_select_set_ops(const struct nlattr * const nla[],
                        break;
                }
 
-               if (!try_module_get(ops->owner))
+               if (!try_module_get(type->owner))
                        continue;
                if (bops != NULL)
-                       module_put(bops->owner);
+                       module_put(bops->type->owner);
 
                bops = ops;
                best = est;
@@ -2816,7 +2842,8 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
 
 static int nf_tables_getset(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        u8 genmask = nft_genmask_cur(net);
        const struct nft_set *set;
@@ -2892,7 +2919,8 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
 
 static int nf_tables_newset(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -3029,7 +3057,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        if (!(nlh->nlmsg_flags & NLM_F_CREATE))
                return -ENOENT;
 
-       ops = nft_select_set_ops(nla, &desc, policy);
+       ops = nft_select_set_ops(&ctx, nla, &desc, policy);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
 
@@ -3039,12 +3067,13 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
 
        size = 0;
        if (ops->privsize != NULL)
-               size = ops->privsize(nla);
+               size = ops->privsize(nla, &desc);
 
-       err = -ENOMEM;
-       set = kzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
-       if (set == NULL)
+       set = kvzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
+       if (!set) {
+               err = -ENOMEM;
                goto err1;
+       }
 
        nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name));
        err = nf_tables_set_alloc_name(&ctx, set, name);
@@ -3087,17 +3116,17 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
 err3:
        ops->destroy(set);
 err2:
-       kfree(set);
+       kvfree(set);
 err1:
-       module_put(ops->owner);
+       module_put(ops->type->owner);
        return err;
 }
 
 static void nft_set_destroy(struct nft_set *set)
 {
        set->ops->destroy(set);
-       module_put(set->ops->owner);
-       kfree(set);
+       module_put(set->ops->type->owner);
+       kvfree(set);
 }
 
 static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
@@ -3109,7 +3138,8 @@ static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set
 
 static int nf_tables_delset(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -3469,7 +3499,8 @@ static int nf_tables_dump_set_done(struct netlink_callback *cb)
 
 static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
                                struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[])
+                               const struct nlattr * const nla[],
+                               struct netlink_ext_ack *extack)
 {
        u8 genmask = nft_genmask_cur(net);
        const struct nft_set *set;
@@ -3870,7 +3901,8 @@ err1:
 
 static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
                                struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[])
+                               const struct nlattr * const nla[],
+                               struct netlink_ext_ack *extack)
 {
        u8 genmask = nft_genmask_next(net);
        const struct nlattr *attr;
@@ -4067,7 +4099,8 @@ err1:
 
 static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
                                struct sk_buff *skb, const struct nlmsghdr *nlh,
-                               const struct nlattr * const nla[])
+                               const struct nlattr * const nla[],
+                               struct netlink_ext_ack *extack)
 {
        u8 genmask = nft_genmask_next(net);
        const struct nlattr *attr;
@@ -4277,7 +4310,8 @@ static const struct nft_object_type *nft_obj_type_get(u32 objtype)
 
 static int nf_tables_newobj(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        const struct nft_object_type *type;
@@ -4471,7 +4505,8 @@ nft_obj_filter_alloc(const struct nlattr * const nla[])
 
 static int nf_tables_getobj(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_cur(net);
@@ -4549,8 +4584,9 @@ static void nft_obj_destroy(struct nft_object *obj)
 }
 
 static int nf_tables_delobj(struct net *net, struct sock *nlsk,
-                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[])
+                           struct sk_buff *skb, const struct nlmsghdr *nlh,
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u8 genmask = nft_genmask_next(net);
@@ -4680,7 +4716,8 @@ err:
 
 static int nf_tables_getgen(struct net *net, struct sock *nlsk,
                            struct sk_buff *skb, const struct nlmsghdr *nlh,
-                           const struct nlattr * const nla[])
+                           const struct nlattr * const nla[],
+                           struct netlink_ext_ack *extack)
 {
        struct sk_buff *skb2;
        int err;