mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 15:01:13 +07:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4f6b15c3a6
@ -170,6 +170,7 @@ struct nft_data_desc {
|
||||
int nft_data_init(const struct nft_ctx *ctx,
|
||||
struct nft_data *data, unsigned int size,
|
||||
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);
|
||||
int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
|
||||
enum nft_data_types type, unsigned int len);
|
||||
@ -736,6 +737,10 @@ struct nft_expr_ops {
|
||||
int (*init)(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
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,
|
||||
const struct nft_expr *expr);
|
||||
int (*dump)(struct sk_buff *skb,
|
||||
|
@ -46,6 +46,9 @@ enum tcp_conntrack {
|
||||
/* Marks possibility for expected RFC5961 challenge ACK */
|
||||
#define IP_CT_EXP_CHALLENGE_ACK 0x40
|
||||
|
||||
/* Simultaneous open initialized */
|
||||
#define IP_CT_TCP_SIMULTANEOUS_OPEN 0x80
|
||||
|
||||
struct nf_ct_tcp_flags {
|
||||
__u8 flags;
|
||||
__u8 mask;
|
||||
|
@ -161,8 +161,8 @@ static int ebt_stp_mt_check(const struct xt_mtchk_param *par)
|
||||
/* Make sure the match only receives stp frames */
|
||||
if (!par->nft_compat &&
|
||||
(!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 0;
|
||||
|
@ -34,6 +34,7 @@
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
MODULE_DESCRIPTION("IPv4 packet filter");
|
||||
MODULE_ALIAS("ipt_icmp");
|
||||
|
||||
void *ipt_alloc_initial_table(const struct xt_table *info)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
MODULE_DESCRIPTION("IPv6 packet filter");
|
||||
MODULE_ALIAS("ip6t_icmp6");
|
||||
|
||||
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 *);
|
||||
EXPORT_SYMBOL(nf_nat_decode_session_hook);
|
||||
#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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -240,15 +243,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
|
||||
spin_lock(&cp->lock);
|
||||
|
||||
if (cp->flags & IP_VS_CONN_F_HASHED) {
|
||||
ret = false;
|
||||
/* Decrease refcnt and unlink conn only if we are last user */
|
||||
if (refcount_dec_if_one(&cp->refcnt)) {
|
||||
hlist_del_rcu(&cp->c_list);
|
||||
cp->flags &= ~IP_VS_CONN_F_HASHED;
|
||||
ret = true;
|
||||
}
|
||||
} else
|
||||
ret = refcount_read(&cp->refcnt) ? false : true;
|
||||
}
|
||||
|
||||
spin_unlock(&cp->lock);
|
||||
ct_write_unlock_bh(hash);
|
||||
@ -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);
|
||||
|
||||
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
|
||||
*/
|
||||
@ -478,7 +473,7 @@ void ip_vs_conn_put(struct ip_vs_conn *cp)
|
||||
(refcount_read(&cp->refcnt) == 1) &&
|
||||
!timer_pending(&cp->timer))
|
||||
/* expire connection immediately */
|
||||
__ip_vs_conn_put_notimer(cp);
|
||||
ip_vs_conn_expire(&cp->timer);
|
||||
else
|
||||
__ip_vs_conn_put_timer(cp);
|
||||
}
|
||||
|
@ -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_service *svc;
|
||||
|
||||
local_bh_disable();
|
||||
|
||||
s = this_cpu_ptr(dest->stats.cpustats);
|
||||
u64_stats_update_begin(&s->syncp);
|
||||
s->cnt.inpkts++;
|
||||
@ -137,6 +139,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
|
||||
s->cnt.inpkts++;
|
||||
s->cnt.inbytes += skb->len;
|
||||
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)
|
||||
struct ip_vs_cpu_stats *s;
|
||||
struct ip_vs_service *svc;
|
||||
|
||||
local_bh_disable();
|
||||
|
||||
s = this_cpu_ptr(dest->stats.cpustats);
|
||||
u64_stats_update_begin(&s->syncp);
|
||||
s->cnt.outpkts++;
|
||||
@ -169,6 +175,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
|
||||
s->cnt.outpkts++;
|
||||
s->cnt.outbytes += skb->len;
|
||||
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)
|
||||
struct netns_ipvs *ipvs = svc->ipvs;
|
||||
struct ip_vs_cpu_stats *s;
|
||||
|
||||
local_bh_disable();
|
||||
|
||||
s = this_cpu_ptr(cp->dest->stats.cpustats);
|
||||
u64_stats_update_begin(&s->syncp);
|
||||
s->cnt.conns++;
|
||||
@ -193,6 +203,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
|
||||
u64_stats_update_begin(&s->syncp);
|
||||
s->cnt.conns++;
|
||||
u64_stats_update_end(&s->syncp);
|
||||
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
|
||||
|
@ -981,6 +981,17 @@ static int tcp_packet(struct nf_conn *ct,
|
||||
return NF_ACCEPT; /* Don't change state */
|
||||
}
|
||||
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:
|
||||
if (index == TCP_RST_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)
|
||||
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
|
||||
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)
|
||||
nft_trans_destroy(trans);
|
||||
return err;
|
||||
}
|
||||
nft_rule_expr_deactivate(ctx, rule);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2238,6 +2267,13 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
|
||||
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
|
||||
|
||||
static struct nft_expr_info *info;
|
||||
@ -2402,7 +2438,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
nf_tables_rule_destroy(&ctx, rule);
|
||||
nf_tables_rule_release(&ctx, rule);
|
||||
err1:
|
||||
for (i = 0; i < n; i++) {
|
||||
if (info[i].ops != NULL)
|
||||
@ -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) ^
|
||||
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) ||
|
||||
nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
|
||||
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF))
|
||||
return -EBUSY;
|
||||
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
|
||||
err = -EBUSY;
|
||||
goto err5;
|
||||
}
|
||||
if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
|
||||
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
|
||||
memcmp(nft_set_ext_data(ext),
|
||||
@ -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
|
||||
* 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) {
|
||||
switch (data->verdict.code) {
|
||||
@ -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) {
|
||||
case NFT_MSG_DELTABLE:
|
||||
@ -5790,6 +5828,21 @@ static void nf_tables_commit_release(struct nft_trans *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)
|
||||
{
|
||||
struct nft_trans *trans, *next;
|
||||
@ -5920,13 +5973,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
|
||||
list_del(&trans->list);
|
||||
nf_tables_commit_release(trans);
|
||||
}
|
||||
|
||||
nf_tables_commit_release(net);
|
||||
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
|
||||
|
||||
return 0;
|
||||
@ -6006,10 +6053,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
|
||||
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));
|
||||
break;
|
||||
case NFT_MSG_DELRULE:
|
||||
trans->ctx.chain->use++;
|
||||
nft_clear(trans->ctx.net, nft_trans_rule(trans));
|
||||
nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
|
||||
nft_trans_destroy(trans);
|
||||
break;
|
||||
case NFT_MSG_NEWSET:
|
||||
@ -6585,7 +6634,7 @@ int __nft_release_basechain(struct nft_ctx *ctx)
|
||||
list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
|
||||
list_del(&rule->list);
|
||||
ctx->chain->use--;
|
||||
nf_tables_rule_destroy(ctx, rule);
|
||||
nf_tables_rule_release(ctx, rule);
|
||||
}
|
||||
list_del(&ctx->chain->list);
|
||||
ctx->table->use--;
|
||||
@ -6623,7 +6672,7 @@ static void __nft_release_tables(struct net *net)
|
||||
list_for_each_entry_safe(rule, nr, &chain->rules, list) {
|
||||
list_del(&rule->list);
|
||||
chain->use--;
|
||||
nf_tables_rule_destroy(&ctx, rule);
|
||||
nf_tables_rule_release(&ctx, rule);
|
||||
}
|
||||
}
|
||||
list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
|
||||
|
@ -119,15 +119,22 @@ DEFINE_STATIC_KEY_FALSE(nft_counters_enabled);
|
||||
static noinline void nft_update_chain_stats(const struct nft_chain *chain,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
struct nft_base_chain *base_chain;
|
||||
struct nft_stats *stats;
|
||||
|
||||
local_bh_disable();
|
||||
stats = this_cpu_ptr(rcu_dereference(nft_base_chain(chain)->stats));
|
||||
u64_stats_update_begin(&stats->syncp);
|
||||
stats->pkts++;
|
||||
stats->bytes += pkt->skb->len;
|
||||
u64_stats_update_end(&stats->syncp);
|
||||
local_bh_enable();
|
||||
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();
|
||||
u64_stats_update_begin(&stats->syncp);
|
||||
stats->pkts++;
|
||||
stats->bytes += pkt->skb->len;
|
||||
u64_stats_update_end(&stats->syncp);
|
||||
local_bh_enable();
|
||||
}
|
||||
}
|
||||
|
||||
struct nft_jumpstack {
|
||||
|
@ -115,7 +115,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
|
||||
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]) {
|
||||
atomic64_set(&nfacct->bytes,
|
||||
|
@ -149,8 +149,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
|
||||
!tb[NFCTH_POLICY_EXPECT_TIMEOUT])
|
||||
return -EINVAL;
|
||||
|
||||
strncpy(expect_policy->name,
|
||||
nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
|
||||
nla_strlcpy(expect_policy->name,
|
||||
nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
|
||||
expect_policy->max_expected =
|
||||
ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
|
||||
if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
|
||||
@ -234,7 +234,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
|
||||
if (ret < 0)
|
||||
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]));
|
||||
if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -27,14 +27,31 @@ struct nft_xt {
|
||||
struct list_head head;
|
||||
struct nft_expr_ops ops;
|
||||
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) {
|
||||
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,
|
||||
@ -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_tgchk_param par;
|
||||
size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
|
||||
struct nft_xt *nft_xt;
|
||||
u16 proto = 0;
|
||||
bool inv = false;
|
||||
union nft_entry e = {};
|
||||
@ -236,25 +254,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
if (ctx->nla[NFTA_RULE_COMPAT]) {
|
||||
ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
|
||||
|
||||
ret = xt_check_target(&par, size, proto, inv);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
/* The standard target cannot be used */
|
||||
if (target->target == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (!target->target)
|
||||
return -EINVAL;
|
||||
|
||||
nft_xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
nft_xt->refcnt++;
|
||||
return 0;
|
||||
err:
|
||||
module_put(target->me);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -271,8 +286,8 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||
if (par.target->destroy != NULL)
|
||||
par.target->destroy(&par);
|
||||
|
||||
nft_xt_put(container_of(expr->ops, struct nft_xt, ops));
|
||||
module_put(target->me);
|
||||
if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
|
||||
module_put(target->me);
|
||||
}
|
||||
|
||||
static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
||||
@ -316,11 +331,11 @@ static int nft_target_validate(const struct nft_ctx *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nft_match_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
static void __nft_match_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt,
|
||||
void *info)
|
||||
{
|
||||
void *info = nft_expr_priv(expr);
|
||||
struct xt_match *match = expr->ops->data;
|
||||
struct sk_buff *skb = pkt->skb;
|
||||
bool ret;
|
||||
@ -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] = {
|
||||
[NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING },
|
||||
[NFTA_MATCH_REV] = { .type = NLA_U32 },
|
||||
@ -404,13 +435,14 @@ static void match_compat_from_user(struct xt_match *m, void *in, void *out)
|
||||
}
|
||||
|
||||
static int
|
||||
nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[])
|
||||
__nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[],
|
||||
void *info)
|
||||
{
|
||||
void *info = nft_expr_priv(expr);
|
||||
struct xt_match *match = expr->ops->data;
|
||||
struct xt_mtchk_param par;
|
||||
size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
|
||||
struct nft_xt *nft_xt;
|
||||
u16 proto = 0;
|
||||
bool inv = false;
|
||||
union nft_entry e = {};
|
||||
@ -421,26 +453,50 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
if (ctx->nla[NFTA_RULE_COMPAT]) {
|
||||
ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
|
||||
|
||||
ret = xt_check_match(&par, size, proto, inv);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
nft_xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
nft_xt->refcnt++;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
void *info = nft_expr_priv(expr);
|
||||
struct xt_mtdtor_param par;
|
||||
|
||||
par.net = ctx->net;
|
||||
@ -450,13 +506,28 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||
if (par.match->destroy != NULL)
|
||||
par.match->destroy(&par);
|
||||
|
||||
nft_xt_put(container_of(expr->ops, struct nft_xt, ops));
|
||||
module_put(match->me);
|
||||
if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
|
||||
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;
|
||||
|
||||
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)
|
||||
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,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
@ -637,6 +720,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
{
|
||||
struct nft_xt *nft_match;
|
||||
struct xt_match *match;
|
||||
unsigned int matchsize;
|
||||
char *mt_name;
|
||||
u32 rev, family;
|
||||
int err;
|
||||
@ -654,13 +738,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
list_for_each_entry(nft_match, &nft_match_list, head) {
|
||||
struct xt_match *match = nft_match->ops.data;
|
||||
|
||||
if (nft_match_cmp(match, mt_name, rev, family)) {
|
||||
if (!try_module_get(match->me))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
nft_match->refcnt++;
|
||||
if (nft_match_cmp(match, mt_name, rev, family))
|
||||
return &nft_match->ops;
|
||||
}
|
||||
}
|
||||
|
||||
match = xt_request_find_match(family, mt_name, rev);
|
||||
@ -679,9 +758,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
goto err;
|
||||
}
|
||||
|
||||
nft_match->refcnt = 1;
|
||||
nft_match->refcnt = 0;
|
||||
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.init = nft_match_init;
|
||||
nft_match->ops.destroy = nft_match_destroy;
|
||||
@ -689,6 +767,18 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
nft_match->ops.validate = nft_match_validate;
|
||||
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);
|
||||
|
||||
return &nft_match->ops;
|
||||
@ -739,13 +829,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
|
||||
list_for_each_entry(nft_target, &nft_target_list, head) {
|
||||
struct xt_target *target = nft_target->ops.data;
|
||||
|
||||
if (nft_target_cmp(target, tg_name, rev, family)) {
|
||||
if (!try_module_get(target->me))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
nft_target->refcnt++;
|
||||
if (nft_target_cmp(target, tg_name, rev, family))
|
||||
return &nft_target->ops;
|
||||
}
|
||||
}
|
||||
|
||||
target = xt_request_find_target(family, tg_name, rev);
|
||||
@ -764,7 +849,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
|
||||
goto err;
|
||||
}
|
||||
|
||||
nft_target->refcnt = 1;
|
||||
nft_target->refcnt = 0;
|
||||
nft_target->ops.type = &nft_target_type;
|
||||
nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
|
||||
nft_target->ops.init = nft_target_init;
|
||||
@ -823,6 +908,32 @@ static int __init nft_compat_module_init(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);
|
||||
nft_unregister_expr(&nft_target_type);
|
||||
nft_unregister_expr(&nft_match_type);
|
||||
|
@ -69,8 +69,16 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nft_immediate_destroy(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
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_immediate_expr *priv = nft_expr_priv(expr);
|
||||
|
||||
@ -108,7 +116,8 @@ static const struct nft_expr_ops nft_imm_ops = {
|
||||
.size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
|
||||
.eval = nft_immediate_eval,
|
||||
.init = nft_immediate_init,
|
||||
.destroy = nft_immediate_destroy,
|
||||
.activate = nft_immediate_activate,
|
||||
.deactivate = nft_immediate_deactivate,
|
||||
.dump = nft_immediate_dump,
|
||||
.validate = nft_immediate_validate,
|
||||
};
|
||||
|
@ -183,6 +183,9 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision)
|
||||
struct xt_match *m;
|
||||
int err = -ENOENT;
|
||||
|
||||
if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mutex_lock(&xt[af].mutex);
|
||||
list_for_each_entry(m, &xt[af].match, list) {
|
||||
if (strcmp(m->name, name) == 0) {
|
||||
@ -229,6 +232,9 @@ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision)
|
||||
struct xt_target *t;
|
||||
int err = -ENOENT;
|
||||
|
||||
if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mutex_lock(&xt[af].mutex);
|
||||
list_for_each_entry(t, &xt[af].target, list) {
|
||||
if (strcmp(t->name, name) == 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user