Commit 3ebb41bf authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter fixes for net:

1) Postpone chain policy update to drop after transaction is complete,
   from Florian Westphal.

2) Add entry to flowtable after confirmation to fix UDP flows with
   packets going in one single direction.

3) Reference count leak in dst object, from Taehee Yoo.

4) Check for TTL field in flowtable datapath, from Taehee Yoo.

5) Fix h323 conntrack helper due to incorrect boundary check,
   from Jakub Jankowski.

6) Fix incorrect rcu dereference when fetching basechain stats,
   from Florian Westphal.

7) Missing error check when adding new entries to flowtable,
   from Taehee Yoo.

8) Use version field in nfnetlink message to honor the nfgen_family
   field, from Kristian Evensen.

9) Remove incorrect configuration check for CONFIG_NF_CONNTRACK_IPV6,
   from Subash Abhinov Kasiviswanathan.

10) Prevent dying entries from being added to the flowtable,
    from Taehee Yoo.

11) Don't hit WARN_ON() with malformed blob in ebtables with
    trailing data after last rule, reported by syzbot, patch
    from Florian Westphal.

12) Remove NFT_CT_TIMEOUT enumeration, never used in the kernel
    code.

13) Fix incorrect definition for NFT_LOGLEVEL_MAX, from Florian
    Westphal.

This batch comes with a conflict that can be fixed with this patch:

diff --cc include/uapi/linux/netfilter/nf_tables.h
index 7bdb234f3d8c,f0cf7b0f4f35..505393c6e959
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@@ -966,6 -966,8 +966,7 @@@ enum nft_socket_keys
   * @NFT_CT_DST_IP: conntrack layer 3 protocol destination (IPv4 address)
   * @NFT_CT_SRC_IP6: conntrack layer 3 protocol source (IPv6 address)
   * @NFT_CT_DST_IP6: conntrack layer 3 protocol destination (IPv6 address)
 - * @NFT_CT_TIMEOUT: connection tracking timeout policy assigned to conntrack
+  * @NFT_CT_ID: conntrack id
   */
  enum nft_ct_keys {
  	NFT_CT_STATE,
@@@ -991,6 -993,8 +992,7 @@@
  	NFT_CT_DST_IP,
  	NFT_CT_SRC_IP6,
  	NFT_CT_DST_IP6,
 -	NFT_CT_TIMEOUT,
+ 	NFT_CT_ID,
  	__NFT_CT_MAX
  };
  #define NFT_CT_MAX		(__NFT_CT_MAX - 1)

That replaces the unused NFT_CT_TIMEOUT definition by NFT_CT_ID. If you prefer,
I can also solve this conflict here, just let me know.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3ee9ae74 92285a07
...@@ -966,7 +966,6 @@ enum nft_socket_keys { ...@@ -966,7 +966,6 @@ enum nft_socket_keys {
* @NFT_CT_DST_IP: conntrack layer 3 protocol destination (IPv4 address) * @NFT_CT_DST_IP: conntrack layer 3 protocol destination (IPv4 address)
* @NFT_CT_SRC_IP6: conntrack layer 3 protocol source (IPv6 address) * @NFT_CT_SRC_IP6: conntrack layer 3 protocol source (IPv6 address)
* @NFT_CT_DST_IP6: conntrack layer 3 protocol destination (IPv6 address) * @NFT_CT_DST_IP6: conntrack layer 3 protocol destination (IPv6 address)
* @NFT_CT_TIMEOUT: connection tracking timeout policy assigned to conntrack
* @NFT_CT_ID: conntrack id * @NFT_CT_ID: conntrack id
*/ */
enum nft_ct_keys { enum nft_ct_keys {
...@@ -993,7 +992,6 @@ enum nft_ct_keys { ...@@ -993,7 +992,6 @@ enum nft_ct_keys {
NFT_CT_DST_IP, NFT_CT_DST_IP,
NFT_CT_SRC_IP6, NFT_CT_SRC_IP6,
NFT_CT_DST_IP6, NFT_CT_DST_IP6,
NFT_CT_TIMEOUT,
NFT_CT_ID, NFT_CT_ID,
__NFT_CT_MAX __NFT_CT_MAX
}; };
...@@ -1138,7 +1136,7 @@ enum nft_log_level { ...@@ -1138,7 +1136,7 @@ enum nft_log_level {
NFT_LOGLEVEL_AUDIT, NFT_LOGLEVEL_AUDIT,
__NFT_LOGLEVEL_MAX __NFT_LOGLEVEL_MAX
}; };
#define NFT_LOGLEVEL_MAX (__NFT_LOGLEVEL_MAX + 1) #define NFT_LOGLEVEL_MAX (__NFT_LOGLEVEL_MAX - 1)
/** /**
* enum nft_queue_attributes - nf_tables queue expression netlink attributes * enum nft_queue_attributes - nf_tables queue expression netlink attributes
......
...@@ -2153,7 +2153,9 @@ static int compat_copy_entries(unsigned char *data, unsigned int size_user, ...@@ -2153,7 +2153,9 @@ static int compat_copy_entries(unsigned char *data, unsigned int size_user,
if (ret < 0) if (ret < 0)
return ret; return ret;
WARN_ON(size_remaining); if (size_remaining)
return -EINVAL;
return state->buf_kern_offset; return state->buf_kern_offset;
} }
......
...@@ -172,7 +172,7 @@ static int nf_h323_error_boundary(struct bitstr *bs, size_t bytes, size_t bits) ...@@ -172,7 +172,7 @@ static int nf_h323_error_boundary(struct bitstr *bs, size_t bytes, size_t bits)
if (bits % BITS_PER_BYTE > 0) if (bits % BITS_PER_BYTE > 0)
bytes++; bytes++;
if (*bs->cur + bytes > *bs->end) if (bs->cur + bytes > bs->end)
return 1; return 1;
return 0; return 0;
......
...@@ -748,24 +748,19 @@ static int callforward_do_filter(struct net *net, ...@@ -748,24 +748,19 @@ static int callforward_do_filter(struct net *net,
} }
break; break;
} }
#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) #if IS_ENABLED(CONFIG_IPV6)
case AF_INET6: { case AF_INET6: {
const struct nf_ipv6_ops *v6ops;
struct rt6_info *rt1, *rt2; struct rt6_info *rt1, *rt2;
struct flowi6 fl1, fl2; struct flowi6 fl1, fl2;
v6ops = nf_get_ipv6_ops();
if (!v6ops)
return 0;
memset(&fl1, 0, sizeof(fl1)); memset(&fl1, 0, sizeof(fl1));
fl1.daddr = src->in6; fl1.daddr = src->in6;
memset(&fl2, 0, sizeof(fl2)); memset(&fl2, 0, sizeof(fl2));
fl2.daddr = dst->in6; fl2.daddr = dst->in6;
if (!v6ops->route(net, (struct dst_entry **)&rt1, if (!nf_ip6_route(net, (struct dst_entry **)&rt1,
flowi6_to_flowi(&fl1), false)) { flowi6_to_flowi(&fl1), false)) {
if (!v6ops->route(net, (struct dst_entry **)&rt2, if (!nf_ip6_route(net, (struct dst_entry **)&rt2,
flowi6_to_flowi(&fl2), false)) { flowi6_to_flowi(&fl2), false)) {
if (ipv6_addr_equal(rt6_nexthop(rt1, &fl1.daddr), if (ipv6_addr_equal(rt6_nexthop(rt1, &fl1.daddr),
rt6_nexthop(rt2, &fl2.daddr)) && rt6_nexthop(rt2, &fl2.daddr)) &&
......
...@@ -1256,7 +1256,7 @@ static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl, ...@@ -1256,7 +1256,7 @@ static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl,
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
struct nf_conn *ct; struct nf_conn *ct;
struct nfgenmsg *nfmsg = nlmsg_data(nlh); struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int8_t u3 = nfmsg->nfgen_family; u_int8_t u3 = nfmsg->version ? nfmsg->nfgen_family : AF_UNSPEC;
struct nf_conntrack_zone zone; struct nf_conntrack_zone zone;
int err; int err;
......
...@@ -185,14 +185,25 @@ static const struct rhashtable_params nf_flow_offload_rhash_params = { ...@@ -185,14 +185,25 @@ static const struct rhashtable_params nf_flow_offload_rhash_params = {
int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
{ {
flow->timeout = (u32)jiffies; int err;
rhashtable_insert_fast(&flow_table->rhashtable, err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, &flow->tuplehash[0].node,
nf_flow_offload_rhash_params); nf_flow_offload_rhash_params);
rhashtable_insert_fast(&flow_table->rhashtable, if (err < 0)
&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, return err;
nf_flow_offload_rhash_params);
err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[1].node,
nf_flow_offload_rhash_params);
if (err < 0) {
rhashtable_remove_fast(&flow_table->rhashtable,
&flow->tuplehash[0].node,
nf_flow_offload_rhash_params);
return err;
}
flow->timeout = (u32)jiffies;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(flow_offload_add); EXPORT_SYMBOL_GPL(flow_offload_add);
...@@ -232,6 +243,7 @@ flow_offload_lookup(struct nf_flowtable *flow_table, ...@@ -232,6 +243,7 @@ flow_offload_lookup(struct nf_flowtable *flow_table,
{ {
struct flow_offload_tuple_rhash *tuplehash; struct flow_offload_tuple_rhash *tuplehash;
struct flow_offload *flow; struct flow_offload *flow;
struct flow_offload_entry *e;
int dir; int dir;
tuplehash = rhashtable_lookup(&flow_table->rhashtable, tuple, tuplehash = rhashtable_lookup(&flow_table->rhashtable, tuple,
...@@ -244,6 +256,10 @@ flow_offload_lookup(struct nf_flowtable *flow_table, ...@@ -244,6 +256,10 @@ flow_offload_lookup(struct nf_flowtable *flow_table,
if (flow->flags & (FLOW_OFFLOAD_DYING | FLOW_OFFLOAD_TEARDOWN)) if (flow->flags & (FLOW_OFFLOAD_DYING | FLOW_OFFLOAD_TEARDOWN))
return NULL; return NULL;
e = container_of(flow, struct flow_offload_entry, flow);
if (unlikely(nf_ct_is_dying(e->ct)))
return NULL;
return tuplehash; return tuplehash;
} }
EXPORT_SYMBOL_GPL(flow_offload_lookup); EXPORT_SYMBOL_GPL(flow_offload_lookup);
...@@ -290,8 +306,10 @@ static inline bool nf_flow_has_expired(const struct flow_offload *flow) ...@@ -290,8 +306,10 @@ static inline bool nf_flow_has_expired(const struct flow_offload *flow)
static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
{ {
struct nf_flowtable *flow_table = data; struct nf_flowtable *flow_table = data;
struct flow_offload_entry *e;
if (nf_flow_has_expired(flow) || e = container_of(flow, struct flow_offload_entry, flow);
if (nf_flow_has_expired(flow) || nf_ct_is_dying(e->ct) ||
(flow->flags & (FLOW_OFFLOAD_DYING | FLOW_OFFLOAD_TEARDOWN))) (flow->flags & (FLOW_OFFLOAD_DYING | FLOW_OFFLOAD_TEARDOWN)))
flow_offload_del(flow_table, flow); flow_offload_del(flow_table, flow);
} }
......
...@@ -181,6 +181,9 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev, ...@@ -181,6 +181,9 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
iph->protocol != IPPROTO_UDP) iph->protocol != IPPROTO_UDP)
return -1; return -1;
if (iph->ttl <= 1)
return -1;
thoff = iph->ihl * 4; thoff = iph->ihl * 4;
if (!pskb_may_pull(skb, thoff + sizeof(*ports))) if (!pskb_may_pull(skb, thoff + sizeof(*ports)))
return -1; return -1;
...@@ -408,6 +411,9 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev, ...@@ -408,6 +411,9 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
ip6h->nexthdr != IPPROTO_UDP) ip6h->nexthdr != IPPROTO_UDP)
return -1; return -1;
if (ip6h->hop_limit <= 1)
return -1;
thoff = sizeof(*ip6h); thoff = sizeof(*ip6h);
if (!pskb_may_pull(skb, thoff + sizeof(*ports))) if (!pskb_may_pull(skb, thoff + sizeof(*ports)))
return -1; return -1;
......
...@@ -213,33 +213,33 @@ static int nft_deltable(struct nft_ctx *ctx) ...@@ -213,33 +213,33 @@ static int nft_deltable(struct nft_ctx *ctx)
return err; return err;
} }
static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
{ {
struct nft_trans *trans; struct nft_trans *trans;
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain)); trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
if (trans == NULL) if (trans == NULL)
return -ENOMEM; return ERR_PTR(-ENOMEM);
if (msg_type == NFT_MSG_NEWCHAIN) if (msg_type == NFT_MSG_NEWCHAIN)
nft_activate_next(ctx->net, ctx->chain); nft_activate_next(ctx->net, ctx->chain);
list_add_tail(&trans->list, &ctx->net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0; return trans;
} }
static int nft_delchain(struct nft_ctx *ctx) static int nft_delchain(struct nft_ctx *ctx)
{ {
int err; struct nft_trans *trans;
err = nft_trans_chain_add(ctx, NFT_MSG_DELCHAIN); trans = nft_trans_chain_add(ctx, NFT_MSG_DELCHAIN);
if (err < 0) if (IS_ERR(trans))
return err; return PTR_ERR(trans);
ctx->table->use--; ctx->table->use--;
nft_deactivate_next(ctx->net, ctx->chain); nft_deactivate_next(ctx->net, ctx->chain);
return err; return 0;
} }
static void nft_rule_expr_activate(const struct nft_ctx *ctx, static void nft_rule_expr_activate(const struct nft_ctx *ctx,
...@@ -1189,6 +1189,9 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) ...@@ -1189,6 +1189,9 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
u64 pkts, bytes; u64 pkts, bytes;
int cpu; int cpu;
if (!stats)
return 0;
memset(&total, 0, sizeof(total)); memset(&total, 0, sizeof(total));
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
cpu_stats = per_cpu_ptr(stats, cpu); cpu_stats = per_cpu_ptr(stats, cpu);
...@@ -1246,6 +1249,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, ...@@ -1246,6 +1249,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
if (nft_is_base_chain(chain)) { if (nft_is_base_chain(chain)) {
const struct nft_base_chain *basechain = nft_base_chain(chain); const struct nft_base_chain *basechain = nft_base_chain(chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops;
struct nft_stats __percpu *stats;
struct nlattr *nest; struct nlattr *nest;
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK); nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
...@@ -1267,8 +1271,9 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, ...@@ -1267,8 +1271,9 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name)) if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
goto nla_put_failure; goto nla_put_failure;
if (rcu_access_pointer(basechain->stats) && stats = rcu_dereference_check(basechain->stats,
nft_dump_stats(skb, rcu_dereference(basechain->stats))) lockdep_commit_lock_is_held(net));
if (nft_dump_stats(skb, stats))
goto nla_put_failure; goto nla_put_failure;
} }
...@@ -1615,6 +1620,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, ...@@ -1615,6 +1620,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
struct nft_base_chain *basechain; struct nft_base_chain *basechain;
struct nft_stats __percpu *stats; struct nft_stats __percpu *stats;
struct net *net = ctx->net; struct net *net = ctx->net;
struct nft_trans *trans;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule **rules; struct nft_rule **rules;
int err; int err;
...@@ -1662,7 +1668,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, ...@@ -1662,7 +1668,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
ops->dev = hook.dev; ops->dev = hook.dev;
chain->flags |= NFT_BASE_CHAIN; chain->flags |= NFT_BASE_CHAIN;
basechain->policy = policy; basechain->policy = NF_ACCEPT;
} else { } else {
chain = kzalloc(sizeof(*chain), GFP_KERNEL); chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL) if (chain == NULL)
...@@ -1698,13 +1704,18 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, ...@@ -1698,13 +1704,18 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
if (err) if (err)
goto err2; goto err2;
err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN); trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
if (err < 0) { if (IS_ERR(trans)) {
err = PTR_ERR(trans);
rhltable_remove(&table->chains_ht, &chain->rhlhead, rhltable_remove(&table->chains_ht, &chain->rhlhead,
nft_chain_ht_params); nft_chain_ht_params);
goto err2; goto err2;
} }
nft_trans_chain_policy(trans) = -1;
if (nft_is_base_chain(chain))
nft_trans_chain_policy(trans) = policy;
table->use++; table->use++;
list_add_tail_rcu(&chain->list, &table->chains); list_add_tail_rcu(&chain->list, &table->chains);
...@@ -6310,6 +6321,27 @@ static int nf_tables_validate(struct net *net) ...@@ -6310,6 +6321,27 @@ static int nf_tables_validate(struct net *net)
return 0; return 0;
} }
/* a drop policy has to be deferred until all rules have been activated,
* otherwise a large ruleset that contains a drop-policy base chain will
* cause all packets to get dropped until the full transaction has been
* processed.
*
* We defer the drop policy until the transaction has been finalized.
*/
static void nft_chain_commit_drop_policy(struct nft_trans *trans)
{
struct nft_base_chain *basechain;
if (nft_trans_chain_policy(trans) != NF_DROP)
return;
if (!nft_is_base_chain(trans->ctx.chain))
return;
basechain = nft_base_chain(trans->ctx.chain);
basechain->policy = NF_DROP;
}
static void nft_chain_commit_update(struct nft_trans *trans) static void nft_chain_commit_update(struct nft_trans *trans)
{ {
struct nft_base_chain *basechain; struct nft_base_chain *basechain;
...@@ -6631,6 +6663,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -6631,6 +6663,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN); nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
/* trans destroyed after rcu grace period */ /* trans destroyed after rcu grace period */
} else { } else {
nft_chain_commit_drop_policy(trans);
nft_clear(net, trans->ctx.chain); nft_clear(net, trans->ctx.chain);
nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN); nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
nft_trans_destroy(trans); nft_trans_destroy(trans);
......
...@@ -94,8 +94,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr, ...@@ -94,8 +94,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
if (help) if (help)
goto out; goto out;
if (ctinfo == IP_CT_NEW || if (!nf_ct_is_confirmed(ct))
ctinfo == IP_CT_RELATED)
goto out; goto out;
if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
...@@ -113,6 +112,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr, ...@@ -113,6 +112,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
if (ret < 0) if (ret < 0)
goto err_flow_add; goto err_flow_add;
dst_release(route.tuple[!dir].dst);
return; return;
err_flow_add: err_flow_add:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment