Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
Browse files Browse the repository at this point in the history
Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

The following patchset contains Netfilter updates for your net tree,
they are:

1) Fix missing initialization of the range structure (allocated in the
   stack) in nft_masq_{ipv4, ipv6}_eval, from Daniel Borkmann.

2) Make sure the data we receive from userspace contains the req_version
   structure, otherwise return an error incomplete on truncated input.
   From Dan Carpenter.

3) Fix handling og skb->sk which may cause incorrect handling
   of connections from a local process. Via Simon Horman, patch from
   Calvin Owens.

4) Fix wrong netns in nft_compat when setting target and match params
   structure.

5) Relax chain type validation in nft_compat that was recently included,
   this broke the matches that need to be run from the route chain type.
   Now iptables-test.py automated regression tests report success again
   and we avoid the only possible problematic case, which is the use of
   nat targets out of nat chain type.

6) Use match->table to validate the tablename, instead of the match->name.
   Again patch for nft_compat.

7) Restore the synchronous release of objects from the commit and abort
   path in nf_tables. This is causing two major problems: splats when using
   nft_compat, given that matches and targets may sleep and call_rcu is
   invoked from softirq context. Moreover Patrick reported possible event
   notification reordering when rules refer to anonymous sets.

8) Fix race condition in between packets that are being confirmed by
   conntrack and the ctnetlink flush operation. This happens since the
   removal of the central spinlock. Thanks to Jesper D. Brouer to looking
   into this.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Nov 16, 2014
2 parents 35717d8 + 5195c14 commit f1227c5
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 58 deletions.
2 changes: 0 additions & 2 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,12 @@ struct nft_rule {
/**
* struct nft_trans - nf_tables object update in transaction
*
* @rcu_head: rcu head to defer release of transaction data
* @list: used internally
* @msg_type: message type
* @ctx: transaction context
* @data: internal information related to the transaction
*/
struct nft_trans {
struct rcu_head rcu_head;
struct list_head list;
int msg_type;
struct nft_ctx ctx;
Expand Down
1 change: 1 addition & 0 deletions net/ipv4/netfilter/nft_masq_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
struct nf_nat_range range;
unsigned int verdict;

memset(&range, 0, sizeof(range));
range.flags = priv->flags;

verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
Expand Down
1 change: 1 addition & 0 deletions net/ipv6/netfilter/nft_masq_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
struct nf_nat_range range;
unsigned int verdict;

memset(&range, 0, sizeof(range));
range.flags = priv->flags;

verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
Expand Down
6 changes: 6 additions & 0 deletions net/netfilter/ipset/ip_set_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
if (*op < IP_SET_OP_VERSION) {
/* Check the version at the beginning of operations */
struct ip_set_req_version *req_version = data;

if (*len < sizeof(struct ip_set_req_version)) {
ret = -EINVAL;
goto done;
}

if (req_version->version != IPSET_PROTOCOL) {
ret = -EPROTO;
goto done;
Expand Down
2 changes: 2 additions & 0 deletions net/netfilter/ipvs/ip_vs_xmit.c
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
new_skb = skb_realloc_headroom(skb, max_headroom);
if (!new_skb)
goto error;
if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
consume_skb(skb);
skb = new_skb;
}
Expand Down
14 changes: 8 additions & 6 deletions net/netfilter/nf_conntrack_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,12 +611,16 @@ __nf_conntrack_confirm(struct sk_buff *skb)
*/
NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
pr_debug("Confirming conntrack %p\n", ct);
/* We have to check the DYING flag inside the lock to prevent
a race against nf_ct_get_next_corpse() possibly called from
user context, else we insert an already 'dead' hash, blocking
further use of that particular connection -JM */

/* We have to check the DYING flag after unlink to prevent
* a race against nf_ct_get_next_corpse() possibly called from
* user context, else we insert an already 'dead' hash, blocking
* further use of that particular connection -JM.
*/
nf_ct_del_from_dying_or_unconfirmed_list(ct);

if (unlikely(nf_ct_is_dying(ct))) {
nf_ct_add_to_dying_list(ct);
nf_conntrack_double_unlock(hash, reply_hash);
local_bh_enable();
return NF_ACCEPT;
Expand All @@ -636,8 +640,6 @@ __nf_conntrack_confirm(struct sk_buff *skb)
zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
goto out;

nf_ct_del_from_dying_or_unconfirmed_list(ct);

/* Timer relative to confirmation time, not original
setting time, otherwise we'd get timer wrap in
weird delay cases. */
Expand Down
24 changes: 8 additions & 16 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans)
}
}

/* Schedule objects for release via rcu to make sure no packets are accesing
* removed rules.
*/
static void nf_tables_commit_release_rcu(struct rcu_head *rt)
static void nf_tables_commit_release(struct nft_trans *trans)
{
struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);

switch (trans->msg_type) {
case NFT_MSG_DELTABLE:
nf_tables_table_destroy(&trans->ctx);
Expand Down Expand Up @@ -3612,24 +3607,20 @@ static int nf_tables_commit(struct sk_buff *skb)
}
}

synchronize_rcu();

list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
trans->ctx.nla = NULL;
call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
nf_tables_commit_release(trans);
}

nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);

return 0;
}

/* Schedule objects for release via rcu to make sure no packets are accesing
* aborted rules.
*/
static void nf_tables_abort_release_rcu(struct rcu_head *rt)
static void nf_tables_abort_release(struct nft_trans *trans)
{
struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);

switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
nf_tables_table_destroy(&trans->ctx);
Expand Down Expand Up @@ -3725,11 +3716,12 @@ static int nf_tables_abort(struct sk_buff *skb)
}
}

synchronize_rcu();

list_for_each_entry_safe_reverse(trans, next,
&net->nft.commit_list, list) {
list_del(&trans->list);
trans->ctx.nla = NULL;
call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
nf_tables_abort_release(trans);
}

return 0;
Expand Down
40 changes: 6 additions & 34 deletions net/netfilter/nft_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,17 @@
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/netfilter/nf_tables.h>

static const struct {
const char *name;
u8 type;
} table_to_chaintype[] = {
{ "filter", NFT_CHAIN_T_DEFAULT },
{ "raw", NFT_CHAIN_T_DEFAULT },
{ "security", NFT_CHAIN_T_DEFAULT },
{ "mangle", NFT_CHAIN_T_ROUTE },
{ "nat", NFT_CHAIN_T_NAT },
{ },
};

static int nft_compat_table_to_chaintype(const char *table)
{
int i;

for (i = 0; table_to_chaintype[i].name != NULL; i++) {
if (strcmp(table_to_chaintype[i].name, table) == 0)
return table_to_chaintype[i].type;
}

return -1;
}

static int nft_compat_chain_validate_dependency(const char *tablename,
const struct nft_chain *chain)
{
enum nft_chain_type type;
const struct nft_base_chain *basechain;

if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
return 0;

type = nft_compat_table_to_chaintype(tablename);
if (type < 0)
return -EINVAL;

basechain = nft_base_chain(chain);
if (basechain->type->type != type)
if (strcmp(tablename, "nat") == 0 &&
basechain->type->type != NFT_CHAIN_T_NAT)
return -EINVAL;

return 0;
Expand Down Expand Up @@ -117,7 +89,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
struct xt_target *target, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
par->net = &init_net;
par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
Expand Down Expand Up @@ -324,7 +296,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
struct xt_match *match, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
par->net = &init_net;
par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
Expand Down Expand Up @@ -374,7 +346,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
union nft_entry e = {};
int ret;

ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
ret = nft_compat_chain_validate_dependency(match->table, ctx->chain);
if (ret < 0)
goto err;

Expand Down Expand Up @@ -448,7 +420,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
if (!(hook_mask & match->hooks))
return -EINVAL;

ret = nft_compat_chain_validate_dependency(match->name,
ret = nft_compat_chain_validate_dependency(match->table,
ctx->chain);
if (ret < 0)
return ret;
Expand Down

0 comments on commit f1227c5

Please sign in to comment.