]> Git Repo - J-linux.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
authorDavid S. Miller <[email protected]>
Tue, 23 Oct 2018 03:21:30 +0000 (20:21 -0700)
committerDavid S. Miller <[email protected]>
Tue, 23 Oct 2018 03:21:30 +0000 (20:21 -0700)
Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for your net tree:

1) rbtree lookup from control plane returns the left-hand side element
   of the range when the interval end flag is set on.

2) osf extension is not supported from the input path, reject this from
   the control plane, from Fernando Fernandez Mancera.

3) xt_TEE is leaving output interface unset due to a recent incorrect
   netns rework, from Taehee Yoo.

4) xt_TEE allows to select an interface which does not belong to this
   netnamespace, from Taehee Yoo.

5) Zero private extension area in nft_compat, just like we do in x_tables,
   otherwise we leak kernel memory to userspace.

6) Missing .checkentry and .destroy entries in new DNAT extensions breaks
   it since we never load nf_conntrack dependencies, from Paolo Abeni.

7) Do not remove flowtable hook from netns exit path, the netdevice handler
   already deals with this, also from Taehee Yoo.

8) Only cleanup flowtable entries that reside in this netnamespace, also
   from Taehee Yoo.
====================

Signed-off-by: David S. Miller <[email protected]>
1  2 
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_osf.c

index a3cc2ef8a48a8912b32af7abddd7ce3b64c55964,c188e27972c7ce7315f0c4841d97d68e675987a7..b7a4816add76530fe0831afbeaa417f3039e9a27
@@@ -120,7 -120,7 +120,7 @@@ static void flow_offload_fixup_ct_state
        if (l4num == IPPROTO_TCP)
                flow_offload_fixup_tcp(&ct->proto.tcp);
  
 -      l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), l4num);
 +      l4proto = __nf_ct_l4proto_find(l4num);
        if (!l4proto)
                return;
  
@@@ -233,8 -233,8 +233,8 @@@ flow_offload_lookup(struct nf_flowtabl
        struct flow_offload *flow;
        int dir;
  
 -      tuplehash = rhashtable_lookup_fast(&flow_table->rhashtable, tuple,
 -                                         nf_flow_offload_rhash_params);
 +      tuplehash = rhashtable_lookup(&flow_table->rhashtable, tuple,
 +                                    nf_flow_offload_rhash_params);
        if (!tuplehash)
                return NULL;
  
@@@ -254,17 -254,20 +254,17 @@@ int nf_flow_table_iterate(struct nf_flo
        struct flow_offload_tuple_rhash *tuplehash;
        struct rhashtable_iter hti;
        struct flow_offload *flow;
 -      int err;
 -
 -      err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL);
 -      if (err)
 -              return err;
 +      int err = 0;
  
 +      rhashtable_walk_enter(&flow_table->rhashtable, &hti);
        rhashtable_walk_start(&hti);
  
        while ((tuplehash = rhashtable_walk_next(&hti))) {
                if (IS_ERR(tuplehash)) {
 -                      err = PTR_ERR(tuplehash);
 -                      if (err != -EAGAIN)
 -                              goto out;
 -
 +                      if (PTR_ERR(tuplehash) != -EAGAIN) {
 +                              err = PTR_ERR(tuplehash);
 +                              break;
 +                      }
                        continue;
                }
                if (tuplehash->tuple.dir)
  
                iter(flow, data);
        }
 -out:
        rhashtable_walk_stop(&hti);
        rhashtable_walk_exit(&hti);
  
@@@ -286,19 -290,25 +286,19 @@@ static inline bool nf_flow_has_expired(
        return (__s32)(flow->timeout - (u32)jiffies) <= 0;
  }
  
 -static int nf_flow_offload_gc_step(struct nf_flowtable *flow_table)
 +static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table)
  {
        struct flow_offload_tuple_rhash *tuplehash;
        struct rhashtable_iter hti;
        struct flow_offload *flow;
 -      int err;
 -
 -      err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL);
 -      if (err)
 -              return 0;
  
 +      rhashtable_walk_enter(&flow_table->rhashtable, &hti);
        rhashtable_walk_start(&hti);
  
        while ((tuplehash = rhashtable_walk_next(&hti))) {
                if (IS_ERR(tuplehash)) {
 -                      err = PTR_ERR(tuplehash);
 -                      if (err != -EAGAIN)
 -                              goto out;
 -
 +                      if (PTR_ERR(tuplehash) != -EAGAIN)
 +                              break;
                        continue;
                }
                if (tuplehash->tuple.dir)
                                    FLOW_OFFLOAD_TEARDOWN)))
                        flow_offload_del(flow_table, flow);
        }
 -out:
        rhashtable_walk_stop(&hti);
        rhashtable_walk_exit(&hti);
 -
 -      return 1;
  }
  
  static void nf_flow_offload_work_gc(struct work_struct *work)
@@@ -465,14 -478,17 +465,17 @@@ EXPORT_SYMBOL_GPL(nf_flow_table_init)
  static void nf_flow_table_do_cleanup(struct flow_offload *flow, void *data)
  {
        struct net_device *dev = data;
+       struct flow_offload_entry *e;
+       e = container_of(flow, struct flow_offload_entry, flow);
  
        if (!dev) {
                flow_offload_teardown(flow);
                return;
        }
-       if (flow->tuplehash[0].tuple.iifidx == dev->ifindex ||
-           flow->tuplehash[1].tuple.iifidx == dev->ifindex)
+       if (net_eq(nf_ct_net(e->ct), dev_net(dev)) &&
+           (flow->tuplehash[0].tuple.iifidx == dev->ifindex ||
+            flow->tuplehash[1].tuple.iifidx == dev->ifindex))
                flow_offload_dead(flow);
  }
  
@@@ -483,7 -499,7 +486,7 @@@ static void nf_flow_table_iterate_clean
        flush_delayed_work(&flowtable->gc_work);
  }
  
 -void nf_flow_table_cleanup(struct net *net, struct net_device *dev)
 +void nf_flow_table_cleanup(struct net_device *dev)
  {
        struct nf_flowtable *flowtable;
  
@@@ -501,7 -517,7 +504,7 @@@ void nf_flow_table_free(struct nf_flowt
        mutex_unlock(&flowtable_lock);
        cancel_delayed_work_sync(&flow_table->gc_work);
        nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
 -      WARN_ON(!nf_flow_offload_gc_step(flow_table));
 +      nf_flow_offload_gc_step(flow_table);
        rhashtable_destroy(&flow_table->rhashtable);
  }
  EXPORT_SYMBOL_GPL(nf_flow_table_free);
index f0159eea29780ed93419bce343ec05094691c2a6,d83c0d01a26628ffb0d1d1cf80e245db7162ced6..42487d01a3eda2306f78fde4b45919e2e06e941a
@@@ -27,8 -27,6 +27,8 @@@
  static LIST_HEAD(nf_tables_expressions);
  static LIST_HEAD(nf_tables_objects);
  static LIST_HEAD(nf_tables_flowtables);
 +static LIST_HEAD(nf_tables_destroy_list);
 +static DEFINE_SPINLOCK(nf_tables_destroy_list_lock);
  static u64 table_handle;
  
  enum {
@@@ -66,8 -64,6 +66,8 @@@ static void nft_validate_state_update(s
  
        net->nft.validate_state = new_validate_state;
  }
 +static void nf_tables_trans_destroy_work(struct work_struct *w);
 +static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work);
  
  static void nft_ctx_init(struct nft_ctx *ctx,
                         struct net *net,
@@@ -211,18 -207,6 +211,18 @@@ static int nft_delchain(struct nft_ctx 
        return err;
  }
  
 +/* either expr ops provide both activate/deactivate, or neither */
 +static bool nft_expr_check_ops(const struct nft_expr_ops *ops)
 +{
 +      if (!ops)
 +              return true;
 +
 +      if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate)))
 +              return false;
 +
 +      return true;
 +}
 +
  static void nft_rule_expr_activate(const struct nft_ctx *ctx,
                                   struct nft_rule *rule)
  {
@@@ -314,7 -298,7 +314,7 @@@ static int nft_delrule_by_chain(struct 
        return 0;
  }
  
 -static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
 +static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
                             struct nft_set *set)
  {
        struct nft_trans *trans;
        return 0;
  }
  
 -static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
 +static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
  {
        int err;
  
@@@ -1021,8 -1005,7 +1021,8 @@@ static int nf_tables_deltable(struct ne
  
  static void nf_tables_table_destroy(struct nft_ctx *ctx)
  {
 -      BUG_ON(ctx->table->use > 0);
 +      if (WARN_ON(ctx->table->use > 0))
 +              return;
  
        rhltable_destroy(&ctx->table->chains_ht);
        kfree(ctx->table->name);
@@@ -1429,8 -1412,7 +1429,8 @@@ static void nf_tables_chain_destroy(str
  {
        struct nft_chain *chain = ctx->chain;
  
 -      BUG_ON(chain->use > 0);
 +      if (WARN_ON(chain->use > 0))
 +              return;
  
        /* no concurrent access possible anymore */
        nf_tables_chain_free_chain_rules(chain);
@@@ -1925,9 -1907,6 +1925,9 @@@ static int nf_tables_delchain(struct ne
   */
  int nft_register_expr(struct nft_expr_type *type)
  {
 +      if (!nft_expr_check_ops(type->ops))
 +              return -EINVAL;
 +
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
        if (type->family == NFPROTO_UNSPEC)
                list_add_tail_rcu(&type->list, &nf_tables_expressions);
@@@ -2075,10 -2054,6 +2075,10 @@@ static int nf_tables_expr_parse(const s
                        err = PTR_ERR(ops);
                        goto err1;
                }
 +              if (!nft_expr_check_ops(ops)) {
 +                      err = -EINVAL;
 +                      goto err1;
 +              }
        } else
                ops = type->ops;
  
@@@ -2459,6 -2434,7 +2459,6 @@@ static void nf_tables_rule_destroy(cons
  {
        struct nft_expr *expr;
  
 -      lockdep_assert_held(&ctx->net->nft.commit_mutex);
        /*
         * Careful: some expressions might not be initialized in case this
         * is called on error from nf_tables_newrule().
@@@ -3591,6 -3567,13 +3591,6 @@@ static void nft_set_destroy(struct nft_
        kvfree(set);
  }
  
 -static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
 -{
 -      list_del_rcu(&set->list);
 -      nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
 -      nft_set_destroy(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[],
@@@ -3685,38 -3668,17 +3685,38 @@@ bind
  }
  EXPORT_SYMBOL_GPL(nf_tables_bind_set);
  
 -void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
 +void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
                          struct nft_set_binding *binding)
 +{
 +      if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
 +          nft_is_active(ctx->net, set))
 +              list_add_tail_rcu(&set->list, &ctx->table->sets);
 +
 +      list_add_tail_rcu(&binding->list, &set->bindings);
 +}
 +EXPORT_SYMBOL_GPL(nf_tables_rebind_set);
 +
 +void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
 +                        struct nft_set_binding *binding)
  {
        list_del_rcu(&binding->list);
  
        if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
            nft_is_active(ctx->net, set))
 -              nf_tables_set_destroy(ctx, set);
 +              list_del_rcu(&set->list);
  }
  EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
  
 +void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
 +{
 +      if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
 +          nft_is_active(ctx->net, set)) {
 +              nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
 +              nft_set_destroy(set);
 +      }
 +}
 +EXPORT_SYMBOL_GPL(nf_tables_destroy_set);
 +
  const struct nft_set_ext_type nft_set_ext_types[] = {
        [NFT_SET_EXT_KEY]               = {
                .align  = __alignof__(u32),
@@@ -6229,28 -6191,19 +6229,28 @@@ static void nft_commit_release(struct n
                nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
                break;
        }
 +
 +      if (trans->put_net)
 +              put_net(trans->ctx.net);
 +
        kfree(trans);
  }
  
 -static void nf_tables_commit_release(struct net *net)
 +static void nf_tables_trans_destroy_work(struct work_struct *w)
  {
        struct nft_trans *trans, *next;
 +      LIST_HEAD(head);
  
 -      if (list_empty(&net->nft.commit_list))
 +      spin_lock(&nf_tables_destroy_list_lock);
 +      list_splice_init(&nf_tables_destroy_list, &head);
 +      spin_unlock(&nf_tables_destroy_list_lock);
 +
 +      if (list_empty(&head))
                return;
  
        synchronize_rcu();
  
 -      list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
 +      list_for_each_entry_safe(trans, next, &head, list) {
                list_del(&trans->list);
                nft_commit_release(trans);
        }
@@@ -6381,37 -6334,6 +6381,37 @@@ static void nft_chain_del(struct nft_ch
        list_del_rcu(&chain->list);
  }
  
 +static void nf_tables_commit_release(struct net *net)
 +{
 +      struct nft_trans *trans;
 +
 +      /* all side effects have to be made visible.
 +       * For example, if a chain named 'foo' has been deleted, a
 +       * new transaction must not find it anymore.
 +       *
 +       * Memory reclaim happens asynchronously from work queue
 +       * to prevent expensive synchronize_rcu() in commit phase.
 +       */
 +      if (list_empty(&net->nft.commit_list)) {
 +              mutex_unlock(&net->nft.commit_mutex);
 +              return;
 +      }
 +
 +      trans = list_last_entry(&net->nft.commit_list,
 +                              struct nft_trans, list);
 +      get_net(trans->ctx.net);
 +      WARN_ON_ONCE(trans->put_net);
 +
 +      trans->put_net = true;
 +      spin_lock(&nf_tables_destroy_list_lock);
 +      list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list);
 +      spin_unlock(&nf_tables_destroy_list_lock);
 +
 +      mutex_unlock(&net->nft.commit_mutex);
 +
 +      schedule_work(&trans_destroy_work);
 +}
 +
  static int nf_tables_commit(struct net *net, struct sk_buff *skb)
  {
        struct nft_trans *trans, *next;
                }
        }
  
 -      nf_tables_commit_release(net);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
 -      mutex_unlock(&net->nft.commit_mutex);
 +      nf_tables_commit_release(net);
  
        return 0;
  }
@@@ -7245,8 -7168,7 +7245,8 @@@ int __nft_release_basechain(struct nft_
  {
        struct nft_rule *rule, *nr;
  
 -      BUG_ON(!nft_is_base_chain(ctx->chain));
 +      if (WARN_ON(!nft_is_base_chain(ctx->chain)))
 +              return 0;
  
        nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
        list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
@@@ -7280,9 -7202,6 +7280,6 @@@ static void __nft_release_tables(struc
  
                list_for_each_entry(chain, &table->chains, list)
                        nf_tables_unregister_hook(net, table, chain);
-               list_for_each_entry(flowtable, &table->flowtables, list)
-                       nf_unregister_net_hooks(net, flowtable->ops,
-                                               flowtable->ops_len);
                /* No packets are walking on these chains anymore. */
                ctx.table = table;
                list_for_each_entry(chain, &table->chains, list) {
@@@ -7349,7 -7268,6 +7346,7 @@@ static int __init nf_tables_module_init
  {
        int err;
  
 +      spin_lock_init(&nf_tables_destroy_list_lock);
        err = register_pernet_subsys(&nf_tables_net_ops);
        if (err < 0)
                return err;
@@@ -7389,7 -7307,6 +7386,7 @@@ static void __exit nf_tables_module_exi
        unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
        nft_chain_filter_fini();
        unregister_pernet_subsys(&nf_tables_net_ops);
 +      cancel_work_sync(&trans_destroy_work);
        rcu_barrier();
        nf_tables_core_module_exit();
  }
diff --combined net/netfilter/nft_osf.c
index 0b452fd470c409121b95f30a33a1f77bd960dbcc,df4e3e0412ed3654d6a846156ccc281f2cf5bffe..ca5e5d8c5ef8b91cd61cb039d652f4549c343948
@@@ -6,12 -6,10 +6,12 @@@
  
  struct nft_osf {
        enum nft_registers      dreg:8;
 +      u8                      ttl;
  };
  
  static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = {
        [NFTA_OSF_DREG]         = { .type = NLA_U32 },
 +      [NFTA_OSF_TTL]          = { .type = NLA_U8 },
  };
  
  static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
@@@ -35,7 -33,7 +35,7 @@@
                return;
        }
  
 -      os_name = nf_osf_find(skb, nf_osf_fingers);
 +      os_name = nf_osf_find(skb, nf_osf_fingers, priv->ttl);
        if (!os_name)
                strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
        else
@@@ -48,14 -46,6 +48,14 @@@ static int nft_osf_init(const struct nf
  {
        struct nft_osf *priv = nft_expr_priv(expr);
        int err;
 +      u8 ttl;
 +
 +      if (nla_get_u8(tb[NFTA_OSF_TTL])) {
 +              ttl = nla_get_u8(tb[NFTA_OSF_TTL]);
 +              if (ttl > 2)
 +                      return -EINVAL;
 +              priv->ttl = ttl;
 +      }
  
        priv->dreg = nft_parse_register(tb[NFTA_OSF_DREG]);
        err = nft_validate_register_store(ctx, priv->dreg, NULL,
@@@ -70,9 -60,6 +70,9 @@@ static int nft_osf_dump(struct sk_buff 
  {
        const struct nft_osf *priv = nft_expr_priv(expr);
  
 +      if (nla_put_u8(skb, NFTA_OSF_TTL, priv->ttl))
 +              goto nla_put_failure;
 +
        if (nft_dump_register(skb, NFTA_OSF_DREG, priv->dreg))
                goto nla_put_failure;
  
@@@ -82,6 -69,15 +82,15 @@@ nla_put_failure
        return -1;
  }
  
+ static int nft_osf_validate(const struct nft_ctx *ctx,
+                           const struct nft_expr *expr,
+                           const struct nft_data **data)
+ {
+       return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) |
+                                                   (1 << NF_INET_PRE_ROUTING) |
+                                                   (1 << NF_INET_FORWARD));
+ }
  static struct nft_expr_type nft_osf_type;
  static const struct nft_expr_ops nft_osf_op = {
        .eval           = nft_osf_eval,
@@@ -89,6 -85,7 +98,7 @@@
        .init           = nft_osf_init,
        .dump           = nft_osf_dump,
        .type           = &nft_osf_type,
+       .validate       = nft_osf_validate,
  };
  
  static struct nft_expr_type nft_osf_type __read_mostly = {
This page took 0.072983 seconds and 4 git commands to generate.