Commit 4f6b15c3 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/IPVS fixes for net

The following patchset contains Netfilter/IPVS fixes for your net tree,
they are:

1) Fix handling of simultaneous open TCP connection in conntrack,
   from Jozsef Kadlecsik.

2) Insufficient sanitify check of xtables extension names, from
   Florian Westphal.

3) Skip unnecessary synchronize_rcu() call when transaction log
   is already empty, from Florian Westphal.

4) Incorrect destination mac validation in ebt_stp, from Stephen
   Hemminger.

5) xtables module reference counter leak in nft_compat, from
   Florian Westphal.

6) Incorrect connection reference counting logic in IPVS
   one-packet scheduler, from Julian Anastasov.

7) Wrong stats for 32-bits CPU in IPVS, also from Julian.

8) Calm down sparse error in netfilter core, also from Florian.

9) Use nla_strlcpy to fix compilation warning in nfnetlink_acct
   and nfnetlink_cthelper, again from Florian.

10) Missing module alias in icmp and icmp6 xtables extensions,
    from Florian Westphal.

11) Base chain statistics in nf_tables may be unset/null, from Florian.

12) Fix handling of large matchinfo size in nft_compat, this includes
    one preparation for before this fix. From Florian.

13) Fix bogus EBUSY error when deleting chains due to incorrect reference
    counting from the preparation phase of the two-phase commit protocol.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 91dfd02b f0dfd7a2
...@@ -170,6 +170,7 @@ struct nft_data_desc { ...@@ -170,6 +170,7 @@ struct nft_data_desc {
int nft_data_init(const struct nft_ctx *ctx, int nft_data_init(const struct nft_ctx *ctx,
struct nft_data *data, unsigned int size, struct nft_data *data, unsigned int size,
struct nft_data_desc *desc, const struct nlattr *nla); struct nft_data_desc *desc, const struct nlattr *nla);
void nft_data_hold(const struct nft_data *data, enum nft_data_types type);
void nft_data_release(const struct nft_data *data, enum nft_data_types type); void nft_data_release(const struct nft_data *data, enum nft_data_types type);
int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
enum nft_data_types type, unsigned int len); enum nft_data_types type, unsigned int len);
...@@ -736,6 +737,10 @@ struct nft_expr_ops { ...@@ -736,6 +737,10 @@ struct nft_expr_ops {
int (*init)(const struct nft_ctx *ctx, int (*init)(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]); const struct nlattr * const tb[]);
void (*activate)(const struct nft_ctx *ctx,
const struct nft_expr *expr);
void (*deactivate)(const struct nft_ctx *ctx,
const struct nft_expr *expr);
void (*destroy)(const struct nft_ctx *ctx, void (*destroy)(const struct nft_ctx *ctx,
const struct nft_expr *expr); const struct nft_expr *expr);
int (*dump)(struct sk_buff *skb, int (*dump)(struct sk_buff *skb,
......
...@@ -46,6 +46,9 @@ enum tcp_conntrack { ...@@ -46,6 +46,9 @@ enum tcp_conntrack {
/* Marks possibility for expected RFC5961 challenge ACK */ /* Marks possibility for expected RFC5961 challenge ACK */
#define IP_CT_EXP_CHALLENGE_ACK 0x40 #define IP_CT_EXP_CHALLENGE_ACK 0x40
/* Simultaneous open initialized */
#define IP_CT_TCP_SIMULTANEOUS_OPEN 0x80
struct nf_ct_tcp_flags { struct nf_ct_tcp_flags {
__u8 flags; __u8 flags;
__u8 mask; __u8 mask;
......
...@@ -161,8 +161,8 @@ static int ebt_stp_mt_check(const struct xt_mtchk_param *par) ...@@ -161,8 +161,8 @@ static int ebt_stp_mt_check(const struct xt_mtchk_param *par)
/* Make sure the match only receives stp frames */ /* Make sure the match only receives stp frames */
if (!par->nft_compat && if (!par->nft_compat &&
(!ether_addr_equal(e->destmac, eth_stp_addr) || (!ether_addr_equal(e->destmac, eth_stp_addr) ||
!is_broadcast_ether_addr(e->destmsk) || !(e->bitmask & EBT_DESTMAC) ||
!(e->bitmask & EBT_DESTMAC))) !is_broadcast_ether_addr(e->destmsk)))
return -EINVAL; return -EINVAL;
return 0; return 0;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("IPv4 packet filter"); MODULE_DESCRIPTION("IPv4 packet filter");
MODULE_ALIAS("ipt_icmp");
void *ipt_alloc_initial_table(const struct xt_table *info) void *ipt_alloc_initial_table(const struct xt_table *info)
{ {
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("IPv6 packet filter"); MODULE_DESCRIPTION("IPv6 packet filter");
MODULE_ALIAS("ip6t_icmp6");
void *ip6t_alloc_initial_table(const struct xt_table *info) void *ip6t_alloc_initial_table(const struct xt_table *info)
{ {
......
...@@ -585,7 +585,8 @@ void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *); ...@@ -585,7 +585,8 @@ void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *);
EXPORT_SYMBOL(nf_nat_decode_session_hook); EXPORT_SYMBOL(nf_nat_decode_session_hook);
#endif #endif
static void __net_init __netfilter_net_init(struct nf_hook_entries **e, int max) static void __net_init
__netfilter_net_init(struct nf_hook_entries __rcu **e, int max)
{ {
int h; int h;
......
...@@ -232,7 +232,10 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) ...@@ -232,7 +232,10 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
{ {
unsigned int hash; unsigned int hash;
bool ret; bool ret = false;
if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
return refcount_dec_if_one(&cp->refcnt);
hash = ip_vs_conn_hashkey_conn(cp); hash = ip_vs_conn_hashkey_conn(cp);
...@@ -240,15 +243,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) ...@@ -240,15 +243,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
spin_lock(&cp->lock); spin_lock(&cp->lock);
if (cp->flags & IP_VS_CONN_F_HASHED) { if (cp->flags & IP_VS_CONN_F_HASHED) {
ret = false;
/* Decrease refcnt and unlink conn only if we are last user */ /* Decrease refcnt and unlink conn only if we are last user */
if (refcount_dec_if_one(&cp->refcnt)) { if (refcount_dec_if_one(&cp->refcnt)) {
hlist_del_rcu(&cp->c_list); hlist_del_rcu(&cp->c_list);
cp->flags &= ~IP_VS_CONN_F_HASHED; cp->flags &= ~IP_VS_CONN_F_HASHED;
ret = true; ret = true;
} }
} else }
ret = refcount_read(&cp->refcnt) ? false : true;
spin_unlock(&cp->lock); spin_unlock(&cp->lock);
ct_write_unlock_bh(hash); ct_write_unlock_bh(hash);
...@@ -454,12 +455,6 @@ ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af, ...@@ -454,12 +455,6 @@ ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af,
} }
EXPORT_SYMBOL_GPL(ip_vs_conn_out_get_proto); EXPORT_SYMBOL_GPL(ip_vs_conn_out_get_proto);
static void __ip_vs_conn_put_notimer(struct ip_vs_conn *cp)
{
__ip_vs_conn_put(cp);
ip_vs_conn_expire(&cp->timer);
}
/* /*
* Put back the conn and restart its timer with its timeout * Put back the conn and restart its timer with its timeout
*/ */
...@@ -478,7 +473,7 @@ void ip_vs_conn_put(struct ip_vs_conn *cp) ...@@ -478,7 +473,7 @@ void ip_vs_conn_put(struct ip_vs_conn *cp)
(refcount_read(&cp->refcnt) == 1) && (refcount_read(&cp->refcnt) == 1) &&
!timer_pending(&cp->timer)) !timer_pending(&cp->timer))
/* expire connection immediately */ /* expire connection immediately */
__ip_vs_conn_put_notimer(cp); ip_vs_conn_expire(&cp->timer);
else else
__ip_vs_conn_put_timer(cp); __ip_vs_conn_put_timer(cp);
} }
......
...@@ -119,6 +119,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -119,6 +119,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_cpu_stats *s; struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc; struct ip_vs_service *svc;
local_bh_disable();
s = this_cpu_ptr(dest->stats.cpustats); s = this_cpu_ptr(dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.inpkts++; s->cnt.inpkts++;
...@@ -137,6 +139,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -137,6 +139,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
s->cnt.inpkts++; s->cnt.inpkts++;
s->cnt.inbytes += skb->len; s->cnt.inbytes += skb->len;
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable();
} }
} }
...@@ -151,6 +155,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -151,6 +155,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_cpu_stats *s; struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc; struct ip_vs_service *svc;
local_bh_disable();
s = this_cpu_ptr(dest->stats.cpustats); s = this_cpu_ptr(dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.outpkts++; s->cnt.outpkts++;
...@@ -169,6 +175,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -169,6 +175,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
s->cnt.outpkts++; s->cnt.outpkts++;
s->cnt.outbytes += skb->len; s->cnt.outbytes += skb->len;
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable();
} }
} }
...@@ -179,6 +187,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) ...@@ -179,6 +187,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
struct netns_ipvs *ipvs = svc->ipvs; struct netns_ipvs *ipvs = svc->ipvs;
struct ip_vs_cpu_stats *s; struct ip_vs_cpu_stats *s;
local_bh_disable();
s = this_cpu_ptr(cp->dest->stats.cpustats); s = this_cpu_ptr(cp->dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.conns++; s->cnt.conns++;
...@@ -193,6 +203,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) ...@@ -193,6 +203,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.conns++; s->cnt.conns++;
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable();
} }
......
...@@ -981,6 +981,17 @@ static int tcp_packet(struct nf_conn *ct, ...@@ -981,6 +981,17 @@ static int tcp_packet(struct nf_conn *ct,
return NF_ACCEPT; /* Don't change state */ return NF_ACCEPT; /* Don't change state */
} }
break; break;
case TCP_CONNTRACK_SYN_SENT2:
/* tcp_conntracks table is not smart enough to handle
* simultaneous open.
*/
ct->proto.tcp.last_flags |= IP_CT_TCP_SIMULTANEOUS_OPEN;
break;
case TCP_CONNTRACK_SYN_RECV:
if (dir == IP_CT_DIR_REPLY && index == TCP_ACK_SET &&
ct->proto.tcp.last_flags & IP_CT_TCP_SIMULTANEOUS_OPEN)
new_state = TCP_CONNTRACK_ESTABLISHED;
break;
case TCP_CONNTRACK_CLOSE: case TCP_CONNTRACK_CLOSE:
if (index == TCP_RST_SET if (index == TCP_RST_SET
&& (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET)
......
...@@ -214,6 +214,34 @@ static int nft_delchain(struct nft_ctx *ctx) ...@@ -214,6 +214,34 @@ static int nft_delchain(struct nft_ctx *ctx)
return err; return err;
} }
static void nft_rule_expr_activate(const struct nft_ctx *ctx,
struct nft_rule *rule)
{
struct nft_expr *expr;
expr = nft_expr_first(rule);
while (expr != nft_expr_last(rule) && expr->ops) {
if (expr->ops->activate)
expr->ops->activate(ctx, expr);
expr = nft_expr_next(expr);
}
}
static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
struct nft_rule *rule)
{
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 = nft_expr_next(expr);
}
}
static int static int
nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule) nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
{ {
...@@ -259,6 +287,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule) ...@@ -259,6 +287,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
nft_trans_destroy(trans); nft_trans_destroy(trans);
return err; return err;
} }
nft_rule_expr_deactivate(ctx, rule);
return 0; return 0;
} }
...@@ -2238,6 +2267,13 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, ...@@ -2238,6 +2267,13 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
kfree(rule); kfree(rule);
} }
static void nf_tables_rule_release(const struct nft_ctx *ctx,
struct nft_rule *rule)
{
nft_rule_expr_deactivate(ctx, rule);
nf_tables_rule_destroy(ctx, rule);
}
#define NFT_RULE_MAXEXPRS 128 #define NFT_RULE_MAXEXPRS 128
static struct nft_expr_info *info; static struct nft_expr_info *info;
...@@ -2402,7 +2438,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, ...@@ -2402,7 +2438,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
return 0; return 0;
err2: err2:
nf_tables_rule_destroy(&ctx, rule); nf_tables_rule_release(&ctx, rule);
err1: err1:
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (info[i].ops != NULL) if (info[i].ops != NULL)
...@@ -4044,8 +4080,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -4044,8 +4080,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^ if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) || nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) ||
nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^ nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
return -EBUSY; err = -EBUSY;
goto err5;
}
if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) && nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
memcmp(nft_set_ext_data(ext), memcmp(nft_set_ext_data(ext),
...@@ -4130,7 +4168,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk, ...@@ -4130,7 +4168,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
* NFT_GOTO verdicts. This function must be called on active data objects * NFT_GOTO verdicts. This function must be called on active data objects
* from the second phase of the commit protocol. * from the second phase of the commit protocol.
*/ */
static void nft_data_hold(const struct nft_data *data, enum nft_data_types type) void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
{ {
if (type == NFT_DATA_VERDICT) { if (type == NFT_DATA_VERDICT) {
switch (data->verdict.code) { switch (data->verdict.code) {
...@@ -5761,7 +5799,7 @@ static void nft_chain_commit_update(struct nft_trans *trans) ...@@ -5761,7 +5799,7 @@ static void nft_chain_commit_update(struct nft_trans *trans)
} }
} }
static void nf_tables_commit_release(struct nft_trans *trans) static void nft_commit_release(struct nft_trans *trans)
{ {
switch (trans->msg_type) { switch (trans->msg_type) {
case NFT_MSG_DELTABLE: case NFT_MSG_DELTABLE:
...@@ -5790,6 +5828,21 @@ static void nf_tables_commit_release(struct nft_trans *trans) ...@@ -5790,6 +5828,21 @@ static void nf_tables_commit_release(struct nft_trans *trans)
kfree(trans); kfree(trans);
} }
static void nf_tables_commit_release(struct net *net)
{
struct nft_trans *trans, *next;
if (list_empty(&net->nft.commit_list))
return;
synchronize_rcu();
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
nft_commit_release(trans);
}
}
static int nf_tables_commit(struct net *net, struct sk_buff *skb) static int nf_tables_commit(struct net *net, struct sk_buff *skb)
{ {
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
...@@ -5920,13 +5973,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -5920,13 +5973,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
} }
} }
synchronize_rcu(); nf_tables_commit_release(net);
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
nf_tables_commit_release(trans);
}
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
return 0; return 0;
...@@ -6006,10 +6053,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb) ...@@ -6006,10 +6053,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
case NFT_MSG_NEWRULE: case NFT_MSG_NEWRULE:
trans->ctx.chain->use--; trans->ctx.chain->use--;
list_del_rcu(&nft_trans_rule(trans)->list); list_del_rcu(&nft_trans_rule(trans)->list);
nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans));
break; break;
case NFT_MSG_DELRULE: case NFT_MSG_DELRULE:
trans->ctx.chain->use++; trans->ctx.chain->use++;
nft_clear(trans->ctx.net, nft_trans_rule(trans)); nft_clear(trans->ctx.net, nft_trans_rule(trans));
nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
...@@ -6585,7 +6634,7 @@ int __nft_release_basechain(struct nft_ctx *ctx) ...@@ -6585,7 +6634,7 @@ int __nft_release_basechain(struct nft_ctx *ctx)
list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) { list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
list_del(&rule->list); list_del(&rule->list);
ctx->chain->use--; ctx->chain->use--;
nf_tables_rule_destroy(ctx, rule); nf_tables_rule_release(ctx, rule);
} }
list_del(&ctx->chain->list); list_del(&ctx->chain->list);
ctx->table->use--; ctx->table->use--;
...@@ -6623,7 +6672,7 @@ static void __nft_release_tables(struct net *net) ...@@ -6623,7 +6672,7 @@ static void __nft_release_tables(struct net *net)
list_for_each_entry_safe(rule, nr, &chain->rules, list) { list_for_each_entry_safe(rule, nr, &chain->rules, list) {
list_del(&rule->list); list_del(&rule->list);
chain->use--; chain->use--;
nf_tables_rule_destroy(&ctx, rule); nf_tables_rule_release(&ctx, rule);
} }
} }
list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
......
...@@ -119,15 +119,22 @@ DEFINE_STATIC_KEY_FALSE(nft_counters_enabled); ...@@ -119,15 +119,22 @@ DEFINE_STATIC_KEY_FALSE(nft_counters_enabled);
static noinline void nft_update_chain_stats(const struct nft_chain *chain, static noinline void nft_update_chain_stats(const struct nft_chain *chain,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
{ {
struct nft_base_chain *base_chain;
struct nft_stats *stats; struct nft_stats *stats;
base_chain = nft_base_chain(chain);
if (!base_chain->stats)
return;
stats = this_cpu_ptr(rcu_dereference(base_chain->stats));
if (stats) {
local_bh_disable(); local_bh_disable();
stats = this_cpu_ptr(rcu_dereference(nft_base_chain(chain)->stats));
u64_stats_update_begin(&stats->syncp); u64_stats_update_begin(&stats->syncp);
stats->pkts++; stats->pkts++;
stats->bytes += pkt->skb->len; stats->bytes += pkt->skb->len;
u64_stats_update_end(&stats->syncp); u64_stats_update_end(&stats->syncp);
local_bh_enable(); local_bh_enable();
}
} }
struct nft_jumpstack { struct nft_jumpstack {
......
...@@ -115,7 +115,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, ...@@ -115,7 +115,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
nfacct->flags = flags; nfacct->flags = flags;
} }
strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); nla_strlcpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
if (tb[NFACCT_BYTES]) { if (tb[NFACCT_BYTES]) {
atomic64_set(&nfacct->bytes, atomic64_set(&nfacct->bytes,
......
...@@ -149,7 +149,7 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, ...@@ -149,7 +149,7 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
!tb[NFCTH_POLICY_EXPECT_TIMEOUT]) !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
return -EINVAL; return -EINVAL;
strncpy(expect_policy->name, nla_strlcpy(expect_policy->name,
nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN); nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
expect_policy->max_expected = expect_policy->max_expected =
ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
...@@ -234,7 +234,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[], ...@@ -234,7 +234,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
if (ret < 0) if (ret < 0)
goto err1; goto err1;
strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); nla_strlcpy(helper->name,
nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
if (size > FIELD_SIZEOF(struct nf_conn_help, data)) { if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
ret = -ENOMEM; ret = -ENOMEM;
......
...@@ -27,14 +27,31 @@ struct nft_xt { ...@@ -27,14 +27,31 @@ struct nft_xt {
struct list_head head; struct list_head head;
struct nft_expr_ops ops; struct nft_expr_ops ops;
unsigned int refcnt; unsigned int refcnt;
/* Unlike other expressions, ops doesn't have static storage duration.
* nft core assumes they do. We use kfree_rcu so that nft core can
* can check expr->ops->size even after nft_compat->destroy() frees
* the nft_xt struct that holds the ops structure.
*/
struct rcu_head rcu_head;
}; };
static void nft_xt_put(struct nft_xt *xt) /* Used for matches where *info is larger than X byte */
#define NFT_MATCH_LARGE_THRESH 192
struct nft_xt_match_priv {
void *info;
};
static bool nft_xt_put(struct nft_xt *xt)
{ {
if (--xt->refcnt == 0) { if (--xt->refcnt == 0) {
list_del(&xt->head); list_del(&xt->head);
kfree(xt); kfree_rcu(xt, rcu_head);
return true;
} }
return false;
} }
static int nft_compat_chain_validate_dependency(const char *tablename, static int nft_compat_chain_validate_dependency(const char *tablename,
...@@ -226,6 +243,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -226,6 +243,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
struct xt_target *target = expr->ops->data; struct xt_target *target = expr->ops->data;
struct xt_tgchk_param par; struct xt_tgchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
struct nft_xt *nft_xt;
u16 proto = 0; u16 proto = 0;
bool inv = false; bool inv = false;
union nft_entry e = {}; union nft_entry e = {};
...@@ -236,25 +254,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -236,25 +254,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
if (ctx->nla[NFTA_RULE_COMPAT]) { if (ctx->nla[NFTA_RULE_COMPAT]) {
ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
if (ret < 0) if (ret < 0)
goto err; return ret;
} }
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
ret = xt_check_target(&par, size, proto, inv); ret = xt_check_target(&par, size, proto, inv);
if (ret < 0) if (ret < 0)
goto err; return ret;
/* The standard target cannot be used */ /* The standard target cannot be used */
if (target->target == NULL) { if (!target->target)
ret = -EINVAL; return -EINVAL;
goto err;
}
nft_xt = container_of(expr->ops, struct nft_xt, ops);
nft_xt->refcnt++;
return 0; return 0;
err:
module_put(target->me);
return ret;
} }
static void static void
...@@ -271,7 +286,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ...@@ -271,7 +286,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
if (par.target->destroy != NULL) if (par.target->destroy != NULL)
par.target->destroy(&par); par.target->destroy(&par);
nft_xt_put(container_of(expr->ops, struct nft_xt, ops)); if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
module_put(target->me); module_put(target->me);
} }
...@@ -316,11 +331,11 @@ static int nft_target_validate(const struct nft_ctx *ctx, ...@@ -316,11 +331,11 @@ static int nft_target_validate(const struct nft_ctx *ctx,
return 0; return 0;
} }
static void nft_match_eval(const struct nft_expr *expr, static void __nft_match_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt,
void *info)
{ {
void *info = nft_expr_priv(expr);
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
struct sk_buff *skb = pkt->skb; struct sk_buff *skb = pkt->skb;
bool ret; bool ret;
...@@ -344,6 +359,22 @@ static void nft_match_eval(const struct nft_expr *expr, ...@@ -344,6 +359,22 @@ static void nft_match_eval(const struct nft_expr *expr,
} }
} }
static void nft_match_large_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_xt_match_priv *priv = nft_expr_priv(expr);
__nft_match_eval(expr, regs, pkt, priv->info);
}
static void nft_match_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
__nft_match_eval(expr, regs, pkt, nft_expr_priv(expr));
}
static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = {
[NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING },
[NFTA_MATCH_REV] = { .type = NLA_U32 }, [NFTA_MATCH_REV] = { .type = NLA_U32 },
...@@ -404,13 +435,14 @@ static void match_compat_from_user(struct xt_match *m, void *in, void *out) ...@@ -404,13 +435,14 @@ static void match_compat_from_user(struct xt_match *m, void *in, void *out)
} }
static int static int
nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[],
void *info)
{ {
void *info = nft_expr_priv(expr);
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
struct xt_mtchk_param par; struct xt_mtchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
struct nft_xt *nft_xt;
u16 proto = 0; u16 proto = 0;
bool inv = false; bool inv = false;
union nft_entry e = {}; union nft_entry e = {};
...@@ -421,26 +453,50 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -421,26 +453,50 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
if (ctx->nla[NFTA_RULE_COMPAT]) { if (ctx->nla[NFTA_RULE_COMPAT]) {
ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
if (ret < 0) if (ret < 0)
goto err; return ret;
} }
nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
ret = xt_check_match(&par, size, proto, inv); ret = xt_check_match(&par, size, proto, inv);
if (ret < 0) if (ret < 0)
goto err; return ret;
nft_xt = container_of(expr->ops, struct nft_xt, ops);
nft_xt->refcnt++;
return 0; return 0;
err: }
module_put(match->me);
static int
nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr));
}
static int
nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_xt_match_priv *priv = nft_expr_priv(expr);
struct xt_match *m = expr->ops->data;
int ret;
priv->info = kmalloc(XT_ALIGN(m->matchsize), GFP_KERNEL);
if (!priv->info)
return -ENOMEM;
ret = __nft_match_init(ctx, expr, tb, priv->info);
if (ret)
kfree(priv->info);
return ret; return ret;
} }
static void static void
nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
void *info)
{ {
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
void *info = nft_expr_priv(expr);
struct xt_mtdtor_param par; struct xt_mtdtor_param par;
par.net = ctx->net; par.net = ctx->net;
...@@ -450,13 +506,28 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ...@@ -450,13 +506,28 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
if (par.match->destroy != NULL) if (par.match->destroy != NULL)
par.match->destroy(&par); par.match->destroy(&par);
nft_xt_put(container_of(expr->ops, struct nft_xt, ops)); if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
module_put(match->me); module_put(match->me);
} }
static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) static void
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_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
{
struct nft_xt_match_priv *priv = nft_expr_priv(expr);
__nft_match_destroy(ctx, expr, priv->info);
kfree(priv->info);
}
static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr,
void *info)
{ {
void *info = nft_expr_priv(expr);
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) ||
...@@ -470,6 +541,18 @@ static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -470,6 +541,18 @@ static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1; return -1;
} }
static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
return __nft_match_dump(skb, expr, nft_expr_priv(expr));
}
static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e)
{
struct nft_xt_match_priv *priv = nft_expr_priv(e);
return __nft_match_dump(skb, e, priv->info);
}
static int nft_match_validate(const struct nft_ctx *ctx, static int nft_match_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nft_data **data) const struct nft_data **data)
...@@ -637,6 +720,7 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -637,6 +720,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
{ {
struct nft_xt *nft_match; struct nft_xt *nft_match;
struct xt_match *match; struct xt_match *match;
unsigned int matchsize;
char *mt_name; char *mt_name;
u32 rev, family; u32 rev, family;
int err; int err;
...@@ -654,14 +738,9 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -654,14 +738,9 @@ nft_match_select_ops(const struct nft_ctx *ctx,
list_for_each_entry(nft_match, &nft_match_list, head) { list_for_each_entry(nft_match, &nft_match_list, head) {
struct xt_match *match = nft_match->ops.data; struct xt_match *match = nft_match->ops.data;
if (nft_match_cmp(match, mt_name, rev, family)) { if (nft_match_cmp(match, mt_name, rev, family))
if (!try_module_get(match->me))
return ERR_PTR(-ENOENT);
nft_match->refcnt++;
return &nft_match->ops; return &nft_match->ops;
} }
}
match = xt_request_find_match(family, mt_name, rev); match = xt_request_find_match(family, mt_name, rev);
if (IS_ERR(match)) if (IS_ERR(match))
...@@ -679,9 +758,8 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -679,9 +758,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
goto err; goto err;
} }
nft_match->refcnt = 1; nft_match->refcnt = 0;
nft_match->ops.type = &nft_match_type; nft_match->ops.type = &nft_match_type;
nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
nft_match->ops.eval = nft_match_eval; nft_match->ops.eval = nft_match_eval;
nft_match->ops.init = nft_match_init; nft_match->ops.init = nft_match_init;
nft_match->ops.destroy = nft_match_destroy; nft_match->ops.destroy = nft_match_destroy;
...@@ -689,6 +767,18 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -689,6 +767,18 @@ nft_match_select_ops(const struct nft_ctx *ctx,
nft_match->ops.validate = nft_match_validate; nft_match->ops.validate = nft_match_validate;
nft_match->ops.data = match; nft_match->ops.data = match;
matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
if (matchsize > NFT_MATCH_LARGE_THRESH) {
matchsize = NFT_EXPR_SIZE(sizeof(struct nft_xt_match_priv));
nft_match->ops.eval = nft_match_large_eval;
nft_match->ops.init = nft_match_large_init;
nft_match->ops.destroy = nft_match_large_destroy;
nft_match->ops.dump = nft_match_large_dump;
}
nft_match->ops.size = matchsize;
list_add(&nft_match->head, &nft_match_list); list_add(&nft_match->head, &nft_match_list);
return &nft_match->ops; return &nft_match->ops;
...@@ -739,14 +829,9 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -739,14 +829,9 @@ nft_target_select_ops(const struct nft_ctx *ctx,
list_for_each_entry(nft_target, &nft_target_list, head) { list_for_each_entry(nft_target, &nft_target_list, head) {
struct xt_target *target = nft_target->ops.data; struct xt_target *target = nft_target->ops.data;
if (nft_target_cmp(target, tg_name, rev, family)) { if (nft_target_cmp(target, tg_name, rev, family))
if (!try_module_get(target->me))
return ERR_PTR(-ENOENT);
nft_target->refcnt++;
return &nft_target->ops; return &nft_target->ops;
} }
}
target = xt_request_find_target(family, tg_name, rev); target = xt_request_find_target(family, tg_name, rev);
if (IS_ERR(target)) if (IS_ERR(target))
...@@ -764,7 +849,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -764,7 +849,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
goto err; goto err;
} }
nft_target->refcnt = 1; nft_target->refcnt = 0;
nft_target->ops.type = &nft_target_type; nft_target->ops.type = &nft_target_type;
nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
nft_target->ops.init = nft_target_init; nft_target->ops.init = nft_target_init;
...@@ -823,6 +908,32 @@ static int __init nft_compat_module_init(void) ...@@ -823,6 +908,32 @@ static int __init nft_compat_module_init(void)
static void __exit nft_compat_module_exit(void) static void __exit nft_compat_module_exit(void)
{ {
struct nft_xt *xt, *next;
/* list should be empty here, it can be non-empty only in case there
* was an error that caused nft_xt expr to not be initialized fully
* and noone else requested the same expression later.
*
* In this case, the lists contain 0-refcount entries that still
* hold module reference.
*/
list_for_each_entry_safe(xt, next, &nft_target_list, head) {
struct xt_target *target = xt->ops.data;
if (WARN_ON_ONCE(xt->refcnt))
continue;
module_put(target->me);
kfree(xt);
}
list_for_each_entry_safe(xt, next, &nft_match_list, head) {
struct xt_match *match = xt->ops.data;
if (WARN_ON_ONCE(xt->refcnt))
continue;
module_put(match->me);
kfree(xt);
}
nfnetlink_subsys_unregister(&nfnl_compat_subsys); nfnetlink_subsys_unregister(&nfnl_compat_subsys);
nft_unregister_expr(&nft_target_type); nft_unregister_expr(&nft_target_type);
nft_unregister_expr(&nft_match_type); nft_unregister_expr(&nft_match_type);
......
...@@ -69,7 +69,15 @@ static int nft_immediate_init(const struct nft_ctx *ctx, ...@@ -69,7 +69,15 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
return err; return err;
} }
static void nft_immediate_destroy(const struct nft_ctx *ctx, static void nft_immediate_activate(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg));
}
static void nft_immediate_deactivate(const struct nft_ctx *ctx,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_immediate_expr *priv = nft_expr_priv(expr); const struct nft_immediate_expr *priv = nft_expr_priv(expr);
...@@ -108,7 +116,8 @@ static const struct nft_expr_ops nft_imm_ops = { ...@@ -108,7 +116,8 @@ static const struct nft_expr_ops nft_imm_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
.eval = nft_immediate_eval, .eval = nft_immediate_eval,
.init = nft_immediate_init, .init = nft_immediate_init,
.destroy = nft_immediate_destroy, .activate = nft_immediate_activate,
.deactivate = nft_immediate_deactivate,
.dump = nft_immediate_dump, .dump = nft_immediate_dump,
.validate = nft_immediate_validate, .validate = nft_immediate_validate,
}; };
......
...@@ -183,6 +183,9 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) ...@@ -183,6 +183,9 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision)
struct xt_match *m; struct xt_match *m;
int err = -ENOENT; int err = -ENOENT;
if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
return ERR_PTR(-EINVAL);
mutex_lock(&xt[af].mutex); mutex_lock(&xt[af].mutex);
list_for_each_entry(m, &xt[af].match, list) { list_for_each_entry(m, &xt[af].match, list) {
if (strcmp(m->name, name) == 0) { if (strcmp(m->name, name) == 0) {
...@@ -229,6 +232,9 @@ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) ...@@ -229,6 +232,9 @@ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision)
struct xt_target *t; struct xt_target *t;
int err = -ENOENT; int err = -ENOENT;
if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
return ERR_PTR(-EINVAL);
mutex_lock(&xt[af].mutex); mutex_lock(&xt[af].mutex);
list_for_each_entry(t, &xt[af].target, list) { list_for_each_entry(t, &xt[af].target, list) {
if (strcmp(t->name, name) == 0) { if (strcmp(t->name, name) == 0) {
......
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