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 fixes for net

The following patchset contains Netfilter fixes for net:

1) Use CONFIG_NF_TABLES_INET from seltests, not NF_TABLES_INET.
   From Naresh Kamboju.

2) Add a test to cover masquerading and redirect case, from Florian
   Westphal.

3) Two packets coming from the same socket may race to set up NAT,
   ending up with different tuples and the packet losing race being
   dropped. Update nf_conntrack_tuple_taken() to exercise clash
   resolution for this case. From Martynas Pumputis and Florian
   Westphal.

4) Unbind anonymous sets from the commit and abort path, this fixes
   a splat due to double set list removal/release in case that the
   transaction needs to be aborted.

5) Do not preserve original output interface for packets that are
   redirected in the output chain when ip6_route_me_harder() is
   called. Otherwise packets end up going not going to the loopback
   device. From Eli Cooper.

6) Fix bogus splat in nft_compat with CONFIG_REFCOUNT_FULL=y, this
   also simplifies the existing logic to deal with the list insertions
   of the xtables extensions. From Florian Westphal.

Diffstat look rather larger than usual because of the new selftest, but
Florian and I consider that having tests soon into the tree is good to
improve coverage. If there's a different policy in this regard, please,
let me know.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 5, 2019
2 parents c8101f7 + 947e492 commit f09bef6
Show file tree
Hide file tree
Showing 12 changed files with 888 additions and 122 deletions.
17 changes: 13 additions & 4 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,7 @@ struct nft_set_binding {
int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_binding *binding);
void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_binding *binding);
void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_binding *binding);
struct nft_set_binding *binding, bool commit);
void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);

/**
Expand Down Expand Up @@ -721,6 +719,13 @@ struct nft_expr_type {
#define NFT_EXPR_STATEFUL 0x1
#define NFT_EXPR_GC 0x2

enum nft_trans_phase {
NFT_TRANS_PREPARE,
NFT_TRANS_ABORT,
NFT_TRANS_COMMIT,
NFT_TRANS_RELEASE
};

/**
* struct nft_expr_ops - nf_tables expression operations
*
Expand Down Expand Up @@ -750,7 +755,8 @@ struct nft_expr_ops {
void (*activate)(const struct nft_ctx *ctx,
const struct nft_expr *expr);
void (*deactivate)(const struct nft_ctx *ctx,
const struct nft_expr *expr);
const struct nft_expr *expr,
enum nft_trans_phase phase);
void (*destroy)(const struct nft_ctx *ctx,
const struct nft_expr *expr);
void (*destroy_clone)(const struct nft_ctx *ctx,
Expand Down Expand Up @@ -1323,12 +1329,15 @@ struct nft_trans_rule {
struct nft_trans_set {
struct nft_set *set;
u32 set_id;
bool bound;
};

#define nft_trans_set(trans) \
(((struct nft_trans_set *)trans->data)->set)
#define nft_trans_set_id(trans) \
(((struct nft_trans_set *)trans->data)->set_id)
#define nft_trans_set_bound(trans) \
(((struct nft_trans_set *)trans->data)->bound)

struct nft_trans_chain {
bool update;
Expand Down
4 changes: 3 additions & 1 deletion net/ipv6/netfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
struct sock *sk = sk_to_full_sk(skb->sk);
unsigned int hh_len;
struct dst_entry *dst;
int strict = (ipv6_addr_type(&iph->daddr) &
(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
struct flowi6 fl6 = {
.flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0,
strict ? skb_dst(skb)->dev->ifindex : 0,
.flowi6_mark = skb->mark,
.flowi6_uid = sock_net_uid(net, sk),
.daddr = iph->daddr,
Expand Down
16 changes: 16 additions & 0 deletions net/netfilter/nf_conntrack_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,22 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
}

if (nf_ct_key_equal(h, tuple, zone, net)) {
/* Tuple is taken already, so caller will need to find
* a new source port to use.
*
* Only exception:
* If the *original tuples* are identical, then both
* conntracks refer to the same flow.
* This is a rare situation, it can occur e.g. when
* more than one UDP packet is sent from same socket
* in different threads.
*
* Let nf_ct_resolve_clash() deal with this later.
*/
if (nf_ct_tuple_equal(&ignored_conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple))
continue;

NF_CT_STAT_INC_ATOMIC(net, found);
rcu_read_unlock();
return 1;
Expand Down
85 changes: 41 additions & 44 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ static void nft_trans_destroy(struct nft_trans *trans)
kfree(trans);
}

static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
{
struct net *net = ctx->net;
struct nft_trans *trans;

if (!nft_set_is_anonymous(set))
return;

list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
if (trans->msg_type == NFT_MSG_NEWSET &&
nft_trans_set(trans) == set) {
nft_trans_set_bound(trans) = true;
break;
}
}
}

static int nf_tables_register_hook(struct net *net,
const struct nft_table *table,
struct nft_chain *chain)
Expand Down Expand Up @@ -211,18 +228,6 @@ static int nft_delchain(struct nft_ctx *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)
{
Expand All @@ -238,14 +243,15 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
}

static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
struct nft_rule *rule)
struct nft_rule *rule,
enum nft_trans_phase phase)
{
struct nft_expr *expr;

expr = nft_expr_first(rule);
while (expr != nft_expr_last(rule) && expr->ops) {
if (expr->ops->deactivate)
expr->ops->deactivate(ctx, expr);
expr->ops->deactivate(ctx, expr, phase);

expr = nft_expr_next(expr);
}
Expand Down Expand Up @@ -296,7 +302,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
nft_trans_destroy(trans);
return err;
}
nft_rule_expr_deactivate(ctx, rule);
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_PREPARE);

return 0;
}
Expand Down Expand Up @@ -1929,9 +1935,6 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
*/
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);
Expand Down Expand Up @@ -2079,10 +2082,6 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
err = PTR_ERR(ops);
goto err1;
}
if (!nft_expr_check_ops(ops)) {
err = -EINVAL;
goto err1;
}
} else
ops = type->ops;

Expand Down Expand Up @@ -2511,7 +2510,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
static void nf_tables_rule_release(const struct nft_ctx *ctx,
struct nft_rule *rule)
{
nft_rule_expr_deactivate(ctx, rule);
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
nf_tables_rule_destroy(ctx, rule);
}

Expand Down Expand Up @@ -3708,39 +3707,30 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
bind:
binding->chain = ctx->chain;
list_add_tail_rcu(&binding->list, &set->bindings);
nft_set_trans_bind(ctx, set);

return 0;
}
EXPORT_SYMBOL_GPL(nf_tables_bind_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)
struct nft_set_binding *binding, bool event)
{
list_del_rcu(&binding->list);

if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
nft_is_active(ctx->net, set))
if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) {
list_del_rcu(&set->list);
if (event)
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET,
GFP_KERNEL);
}
}
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);
if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
nft_set_destroy(set);
}
}
EXPORT_SYMBOL_GPL(nf_tables_destroy_set);

Expand Down Expand Up @@ -6535,6 +6525,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_rule_notify(&trans->ctx,
nft_trans_rule(trans),
NFT_MSG_DELRULE);
nft_rule_expr_deactivate(&trans->ctx,
nft_trans_rule(trans),
NFT_TRANS_COMMIT);
break;
case NFT_MSG_NEWSET:
nft_clear(net, nft_trans_set(trans));
Expand Down Expand Up @@ -6621,7 +6614,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
break;
case NFT_MSG_NEWSET:
nft_set_destroy(nft_trans_set(trans));
if (!nft_trans_set_bound(trans))
nft_set_destroy(nft_trans_set(trans));
break;
case NFT_MSG_NEWSETELEM:
nft_set_elem_destroy(nft_trans_elem_set(trans),
Expand Down Expand Up @@ -6682,7 +6676,9 @@ static int __nf_tables_abort(struct net *net)
case NFT_MSG_NEWRULE:
trans->ctx.chain->use--;
list_del_rcu(&nft_trans_rule(trans)->list);
nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans));
nft_rule_expr_deactivate(&trans->ctx,
nft_trans_rule(trans),
NFT_TRANS_ABORT);
break;
case NFT_MSG_DELRULE:
trans->ctx.chain->use++;
Expand All @@ -6692,7 +6688,8 @@ static int __nf_tables_abort(struct net *net)
break;
case NFT_MSG_NEWSET:
trans->ctx.table->use--;
list_del_rcu(&nft_trans_set(trans)->list);
if (!nft_trans_set_bound(trans))
list_del_rcu(&nft_trans_set(trans)->list);
break;
case NFT_MSG_DELSET:
trans->ctx.table->use++;
Expand Down
62 changes: 25 additions & 37 deletions net/netfilter/nft_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ static struct nft_compat_net *nft_compat_pernet(struct net *net)
return net_generic(net, nft_compat_net_id);
}

static void nft_xt_get(struct nft_xt *xt)
{
/* refcount_inc() warns on 0 -> 1 transition, but we can't
* init the reference count to 1 in .select_ops -- we can't
* undo such an increase when another expression inside the same
* rule fails afterwards.
*/
if (xt->listcnt == 0)
refcount_set(&xt->refcnt, 1);
else
refcount_inc(&xt->refcnt);

xt->listcnt++;
}

static bool nft_xt_put(struct nft_xt *xt)
{
if (refcount_dec_and_test(&xt->refcnt)) {
Expand Down Expand Up @@ -291,7 +306,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return -EINVAL;

nft_xt = container_of(expr->ops, struct nft_xt, ops);
refcount_inc(&nft_xt->refcnt);
nft_xt_get(nft_xt);
return 0;
}

Expand Down Expand Up @@ -504,7 +519,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return ret;

nft_xt = container_of(expr->ops, struct nft_xt, ops);
refcount_inc(&nft_xt->refcnt);
nft_xt_get(nft_xt);
return 0;
}

Expand Down Expand Up @@ -558,41 +573,16 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
__nft_match_destroy(ctx, expr, nft_expr_priv(expr));
}

static void nft_compat_activate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
struct list_head *h)
{
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);

if (xt->listcnt == 0)
list_add(&xt->head, h);

xt->listcnt++;
}

static void nft_compat_activate_mt(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);

nft_compat_activate(ctx, expr, &cn->nft_match_list);
}

static void nft_compat_activate_tg(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);

nft_compat_activate(ctx, expr, &cn->nft_target_list);
}

static void nft_compat_deactivate(const struct nft_ctx *ctx,
const struct nft_expr *expr)
const struct nft_expr *expr,
enum nft_trans_phase phase)
{
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);

if (--xt->listcnt == 0)
list_del_init(&xt->head);
if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) {
if (--xt->listcnt == 0)
list_del_init(&xt->head);
}
}

static void
Expand Down Expand Up @@ -848,7 +838,6 @@ nft_match_select_ops(const struct nft_ctx *ctx,
nft_match->ops.eval = nft_match_eval;
nft_match->ops.init = nft_match_init;
nft_match->ops.destroy = nft_match_destroy;
nft_match->ops.activate = nft_compat_activate_mt;
nft_match->ops.deactivate = nft_compat_deactivate;
nft_match->ops.dump = nft_match_dump;
nft_match->ops.validate = nft_match_validate;
Expand All @@ -866,7 +855,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,

nft_match->ops.size = matchsize;

nft_match->listcnt = 1;
nft_match->listcnt = 0;
list_add(&nft_match->head, &cn->nft_match_list);

return &nft_match->ops;
Expand Down Expand Up @@ -953,7 +942,6 @@ nft_target_select_ops(const struct nft_ctx *ctx,
nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
nft_target->ops.init = nft_target_init;
nft_target->ops.destroy = nft_target_destroy;
nft_target->ops.activate = nft_compat_activate_tg;
nft_target->ops.deactivate = nft_compat_deactivate;
nft_target->ops.dump = nft_target_dump;
nft_target->ops.validate = nft_target_validate;
Expand All @@ -964,7 +952,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
else
nft_target->ops.eval = nft_target_eval_xt;

nft_target->listcnt = 1;
nft_target->listcnt = 0;
list_add(&nft_target->head, &cn->nft_target_list);

return &nft_target->ops;
Expand Down
Loading

0 comments on commit f09bef6

Please sign in to comment.