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

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter fixes for net:

1) Disable BH while holding list spinlock in nf_conncount, from
   Taehee Yoo.

2) List corruption in nf_conncount, also from Taehee.

3) Fix race that results in leaving around an empty list node in
   nf_conncount, from Taehee Yoo.

4) Proper chain handling for inactive chains from the commit path,
   from Florian Westphal. This includes a selftest for this.

5) Do duplicate rule handles when replacing rules, also from Florian.

6) Remove net_exit path in xt_RATEEST that results in splat, from Taehee.

7) Possible use-after-free in nft_compat when releasing extensions.
   From Florian.

8) Memory leak in xt_hashlimit, from Taehee.

9) Call ip_vs_dst_notifier after ipv6_dev_notf, from Xin Long.

10) Fix cttimeout with udplite and gre, from Florian.

11) Preserve oif for IPv6 link-local generated traffic from mangle
    table, from Alin Nastac.

12) Missing error handling in masquerade notifiers, from Taehee Yoo.

13) Use mutex to protect registration/unregistration of masquerade
    extensions in order to prevent a race, from Taehee.

14) Incorrect condition check in tree_nodes_free(), also from Taehee.

15) Fix chain counter leak in rule replacement path, from Taehee.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-11-28 11:02:45 -08:00
commit e9d8faf93d
23 changed files with 260 additions and 108 deletions

View File

@ -21,6 +21,19 @@ struct nf_ct_gre_keymap {
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
}; };
enum grep_conntrack {
GRE_CT_UNREPLIED,
GRE_CT_REPLIED,
GRE_CT_MAX
};
struct netns_proto_gre {
struct nf_proto_net nf;
rwlock_t keymap_lock;
struct list_head keymap_list;
unsigned int gre_timeouts[GRE_CT_MAX];
};
/* add new tuple->key_reply pair to keymap */ /* add new tuple->key_reply pair to keymap */
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
struct nf_conntrack_tuple *t); struct nf_conntrack_tuple *t);

View File

@ -9,7 +9,7 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,
const struct nf_nat_range2 *range, const struct nf_nat_range2 *range,
const struct net_device *out); const struct net_device *out);
void nf_nat_masquerade_ipv4_register_notifier(void); int nf_nat_masquerade_ipv4_register_notifier(void);
void nf_nat_masquerade_ipv4_unregister_notifier(void); void nf_nat_masquerade_ipv4_unregister_notifier(void);
#endif /*_NF_NAT_MASQUERADE_IPV4_H_ */ #endif /*_NF_NAT_MASQUERADE_IPV4_H_ */

View File

@ -5,7 +5,7 @@
unsigned int unsigned int
nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range, nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
const struct net_device *out); const struct net_device *out);
void nf_nat_masquerade_ipv6_register_notifier(void); int nf_nat_masquerade_ipv6_register_notifier(void);
void nf_nat_masquerade_ipv6_unregister_notifier(void); void nf_nat_masquerade_ipv6_unregister_notifier(void);
#endif /* _NF_NAT_MASQUERADE_IPV6_H_ */ #endif /* _NF_NAT_MASQUERADE_IPV6_H_ */

View File

@ -81,9 +81,12 @@ static int __init masquerade_tg_init(void)
int ret; int ret;
ret = xt_register_target(&masquerade_tg_reg); ret = xt_register_target(&masquerade_tg_reg);
if (ret)
return ret;
if (ret == 0) ret = nf_nat_masquerade_ipv4_register_notifier();
nf_nat_masquerade_ipv4_register_notifier(); if (ret)
xt_unregister_target(&masquerade_tg_reg);
return ret; return ret;
} }

View File

@ -147,28 +147,50 @@ static struct notifier_block masq_inet_notifier = {
.notifier_call = masq_inet_event, .notifier_call = masq_inet_event,
}; };
static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0); static int masq_refcnt;
static DEFINE_MUTEX(masq_mutex);
void nf_nat_masquerade_ipv4_register_notifier(void) int nf_nat_masquerade_ipv4_register_notifier(void)
{ {
int ret = 0;
mutex_lock(&masq_mutex);
/* check if the notifier was already set */ /* check if the notifier was already set */
if (atomic_inc_return(&masquerade_notifier_refcount) > 1) if (++masq_refcnt > 1)
return; goto out_unlock;
/* Register for device down reports */ /* Register for device down reports */
register_netdevice_notifier(&masq_dev_notifier); ret = register_netdevice_notifier(&masq_dev_notifier);
if (ret)
goto err_dec;
/* Register IP address change reports */ /* Register IP address change reports */
register_inetaddr_notifier(&masq_inet_notifier); ret = register_inetaddr_notifier(&masq_inet_notifier);
if (ret)
goto err_unregister;
mutex_unlock(&masq_mutex);
return ret;
err_unregister:
unregister_netdevice_notifier(&masq_dev_notifier);
err_dec:
masq_refcnt--;
out_unlock:
mutex_unlock(&masq_mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier); EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier);
void nf_nat_masquerade_ipv4_unregister_notifier(void) void nf_nat_masquerade_ipv4_unregister_notifier(void)
{ {
mutex_lock(&masq_mutex);
/* check if the notifier still has clients */ /* check if the notifier still has clients */
if (atomic_dec_return(&masquerade_notifier_refcount) > 0) if (--masq_refcnt > 0)
return; goto out_unlock;
unregister_netdevice_notifier(&masq_dev_notifier); unregister_netdevice_notifier(&masq_dev_notifier);
unregister_inetaddr_notifier(&masq_inet_notifier); unregister_inetaddr_notifier(&masq_inet_notifier);
out_unlock:
mutex_unlock(&masq_mutex);
} }
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier); EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier);

View File

@ -69,7 +69,9 @@ static int __init nft_masq_ipv4_module_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
nf_nat_masquerade_ipv4_register_notifier(); ret = nf_nat_masquerade_ipv4_register_notifier();
if (ret)
nft_unregister_expr(&nft_masq_ipv4_type);
return ret; return ret;
} }

View File

@ -24,7 +24,8 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
unsigned int hh_len; unsigned int hh_len;
struct dst_entry *dst; struct dst_entry *dst;
struct flowi6 fl6 = { struct flowi6 fl6 = {
.flowi6_oif = sk ? sk->sk_bound_dev_if : 0, .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0,
.flowi6_mark = skb->mark, .flowi6_mark = skb->mark,
.flowi6_uid = sock_net_uid(net, sk), .flowi6_uid = sock_net_uid(net, sk),
.daddr = iph->daddr, .daddr = iph->daddr,

View File

@ -58,8 +58,12 @@ static int __init masquerade_tg6_init(void)
int err; int err;
err = xt_register_target(&masquerade_tg6_reg); err = xt_register_target(&masquerade_tg6_reg);
if (err == 0) if (err)
nf_nat_masquerade_ipv6_register_notifier(); return err;
err = nf_nat_masquerade_ipv6_register_notifier();
if (err)
xt_unregister_target(&masquerade_tg6_reg);
return err; return err;
} }

View File

@ -132,8 +132,8 @@ static void iterate_cleanup_work(struct work_struct *work)
* of ipv6 addresses being deleted), we also need to add an upper * of ipv6 addresses being deleted), we also need to add an upper
* limit to the number of queued work items. * limit to the number of queued work items.
*/ */
static int masq_inet_event(struct notifier_block *this, static int masq_inet6_event(struct notifier_block *this,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct inet6_ifaddr *ifa = ptr; struct inet6_ifaddr *ifa = ptr;
const struct net_device *dev; const struct net_device *dev;
@ -171,30 +171,53 @@ static int masq_inet_event(struct notifier_block *this,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static struct notifier_block masq_inet_notifier = { static struct notifier_block masq_inet6_notifier = {
.notifier_call = masq_inet_event, .notifier_call = masq_inet6_event,
}; };
static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0); static int masq_refcnt;
static DEFINE_MUTEX(masq_mutex);
void nf_nat_masquerade_ipv6_register_notifier(void) int nf_nat_masquerade_ipv6_register_notifier(void)
{ {
/* check if the notifier is already set */ int ret = 0;
if (atomic_inc_return(&masquerade_notifier_refcount) > 1)
return;
register_netdevice_notifier(&masq_dev_notifier); mutex_lock(&masq_mutex);
register_inet6addr_notifier(&masq_inet_notifier); /* check if the notifier is already set */
if (++masq_refcnt > 1)
goto out_unlock;
ret = register_netdevice_notifier(&masq_dev_notifier);
if (ret)
goto err_dec;
ret = register_inet6addr_notifier(&masq_inet6_notifier);
if (ret)
goto err_unregister;
mutex_unlock(&masq_mutex);
return ret;
err_unregister:
unregister_netdevice_notifier(&masq_dev_notifier);
err_dec:
masq_refcnt--;
out_unlock:
mutex_unlock(&masq_mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier); EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier);
void nf_nat_masquerade_ipv6_unregister_notifier(void) void nf_nat_masquerade_ipv6_unregister_notifier(void)
{ {
mutex_lock(&masq_mutex);
/* check if the notifier still has clients */ /* check if the notifier still has clients */
if (atomic_dec_return(&masquerade_notifier_refcount) > 0) if (--masq_refcnt > 0)
return; goto out_unlock;
unregister_inet6addr_notifier(&masq_inet_notifier); unregister_inet6addr_notifier(&masq_inet6_notifier);
unregister_netdevice_notifier(&masq_dev_notifier); unregister_netdevice_notifier(&masq_dev_notifier);
out_unlock:
mutex_unlock(&masq_mutex);
} }
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier); EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier);

View File

@ -70,7 +70,9 @@ static int __init nft_masq_ipv6_module_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
nf_nat_masquerade_ipv6_register_notifier(); ret = nf_nat_masquerade_ipv6_register_notifier();
if (ret)
nft_unregister_expr(&nft_masq_ipv6_type);
return ret; return ret;
} }

View File

@ -3980,6 +3980,9 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs)
static struct notifier_block ip_vs_dst_notifier = { static struct notifier_block ip_vs_dst_notifier = {
.notifier_call = ip_vs_dst_event, .notifier_call = ip_vs_dst_event,
#ifdef CONFIG_IP_VS_IPV6
.priority = ADDRCONF_NOTIFY_PRIORITY + 5,
#endif
}; };
int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)

View File

@ -49,6 +49,7 @@ struct nf_conncount_tuple {
struct nf_conntrack_zone zone; struct nf_conntrack_zone zone;
int cpu; int cpu;
u32 jiffies32; u32 jiffies32;
bool dead;
struct rcu_head rcu_head; struct rcu_head rcu_head;
}; };
@ -106,15 +107,16 @@ nf_conncount_add(struct nf_conncount_list *list,
conn->zone = *zone; conn->zone = *zone;
conn->cpu = raw_smp_processor_id(); conn->cpu = raw_smp_processor_id();
conn->jiffies32 = (u32)jiffies; conn->jiffies32 = (u32)jiffies;
spin_lock(&list->list_lock); conn->dead = false;
spin_lock_bh(&list->list_lock);
if (list->dead == true) { if (list->dead == true) {
kmem_cache_free(conncount_conn_cachep, conn); kmem_cache_free(conncount_conn_cachep, conn);
spin_unlock(&list->list_lock); spin_unlock_bh(&list->list_lock);
return NF_CONNCOUNT_SKIP; return NF_CONNCOUNT_SKIP;
} }
list_add_tail(&conn->node, &list->head); list_add_tail(&conn->node, &list->head);
list->count++; list->count++;
spin_unlock(&list->list_lock); spin_unlock_bh(&list->list_lock);
return NF_CONNCOUNT_ADDED; return NF_CONNCOUNT_ADDED;
} }
EXPORT_SYMBOL_GPL(nf_conncount_add); EXPORT_SYMBOL_GPL(nf_conncount_add);
@ -132,19 +134,22 @@ static bool conn_free(struct nf_conncount_list *list,
{ {
bool free_entry = false; bool free_entry = false;
spin_lock(&list->list_lock); spin_lock_bh(&list->list_lock);
if (list->count == 0) { if (conn->dead) {
spin_unlock(&list->list_lock); spin_unlock_bh(&list->list_lock);
return free_entry; return free_entry;
} }
list->count--; list->count--;
conn->dead = true;
list_del_rcu(&conn->node); list_del_rcu(&conn->node);
if (list->count == 0) if (list->count == 0) {
list->dead = true;
free_entry = true; free_entry = true;
}
spin_unlock(&list->list_lock); spin_unlock_bh(&list->list_lock);
call_rcu(&conn->rcu_head, __conn_free); call_rcu(&conn->rcu_head, __conn_free);
return free_entry; return free_entry;
} }
@ -245,7 +250,7 @@ void nf_conncount_list_init(struct nf_conncount_list *list)
{ {
spin_lock_init(&list->list_lock); spin_lock_init(&list->list_lock);
INIT_LIST_HEAD(&list->head); INIT_LIST_HEAD(&list->head);
list->count = 1; list->count = 0;
list->dead = false; list->dead = false;
} }
EXPORT_SYMBOL_GPL(nf_conncount_list_init); EXPORT_SYMBOL_GPL(nf_conncount_list_init);
@ -259,6 +264,7 @@ bool nf_conncount_gc_list(struct net *net,
struct nf_conn *found_ct; struct nf_conn *found_ct;
unsigned int collected = 0; unsigned int collected = 0;
bool free_entry = false; bool free_entry = false;
bool ret = false;
list_for_each_entry_safe(conn, conn_n, &list->head, node) { list_for_each_entry_safe(conn, conn_n, &list->head, node) {
found = find_or_evict(net, list, conn, &free_entry); found = find_or_evict(net, list, conn, &free_entry);
@ -288,7 +294,15 @@ bool nf_conncount_gc_list(struct net *net,
if (collected > CONNCOUNT_GC_MAX_NODES) if (collected > CONNCOUNT_GC_MAX_NODES)
return false; return false;
} }
return false;
spin_lock_bh(&list->list_lock);
if (!list->count) {
list->dead = true;
ret = true;
}
spin_unlock_bh(&list->list_lock);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_conncount_gc_list); EXPORT_SYMBOL_GPL(nf_conncount_gc_list);
@ -309,11 +323,8 @@ static void tree_nodes_free(struct rb_root *root,
while (gc_count) { while (gc_count) {
rbconn = gc_nodes[--gc_count]; rbconn = gc_nodes[--gc_count];
spin_lock(&rbconn->list.list_lock); spin_lock(&rbconn->list.list_lock);
if (rbconn->list.count == 0 && rbconn->list.dead == false) { rb_erase(&rbconn->node, root);
rbconn->list.dead = true; call_rcu(&rbconn->rcu_head, __tree_nodes_free);
rb_erase(&rbconn->node, root);
call_rcu(&rbconn->rcu_head, __tree_nodes_free);
}
spin_unlock(&rbconn->list.list_lock); spin_unlock(&rbconn->list.list_lock);
} }
} }
@ -414,6 +425,7 @@ insert_tree(struct net *net,
nf_conncount_list_init(&rbconn->list); nf_conncount_list_init(&rbconn->list);
list_add(&conn->node, &rbconn->list.head); list_add(&conn->node, &rbconn->list.head);
count = 1; count = 1;
rbconn->list.count = count;
rb_link_node(&rbconn->node, parent, rbnode); rb_link_node(&rbconn->node, parent, rbnode);
rb_insert_color(&rbconn->node, root); rb_insert_color(&rbconn->node, root);

View File

@ -43,24 +43,12 @@
#include <linux/netfilter/nf_conntrack_proto_gre.h> #include <linux/netfilter/nf_conntrack_proto_gre.h>
#include <linux/netfilter/nf_conntrack_pptp.h> #include <linux/netfilter/nf_conntrack_pptp.h>
enum grep_conntrack {
GRE_CT_UNREPLIED,
GRE_CT_REPLIED,
GRE_CT_MAX
};
static const unsigned int gre_timeouts[GRE_CT_MAX] = { static const unsigned int gre_timeouts[GRE_CT_MAX] = {
[GRE_CT_UNREPLIED] = 30*HZ, [GRE_CT_UNREPLIED] = 30*HZ,
[GRE_CT_REPLIED] = 180*HZ, [GRE_CT_REPLIED] = 180*HZ,
}; };
static unsigned int proto_gre_net_id __read_mostly; static unsigned int proto_gre_net_id __read_mostly;
struct netns_proto_gre {
struct nf_proto_net nf;
rwlock_t keymap_lock;
struct list_head keymap_list;
unsigned int gre_timeouts[GRE_CT_MAX];
};
static inline struct netns_proto_gre *gre_pernet(struct net *net) static inline struct netns_proto_gre *gre_pernet(struct net *net)
{ {
@ -402,6 +390,8 @@ static int __init nf_ct_proto_gre_init(void)
{ {
int ret; int ret;
BUILD_BUG_ON(offsetof(struct netns_proto_gre, nf) != 0);
ret = register_pernet_subsys(&proto_gre_net_ops); ret = register_pernet_subsys(&proto_gre_net_ops);
if (ret < 0) if (ret < 0)
goto out_pernet; goto out_pernet;

View File

@ -2457,7 +2457,7 @@ err:
static void nf_tables_rule_destroy(const struct nft_ctx *ctx, static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
struct nft_rule *rule) struct nft_rule *rule)
{ {
struct nft_expr *expr; struct nft_expr *expr, *next;
/* /*
* Careful: some expressions might not be initialized in case this * Careful: some expressions might not be initialized in case this
@ -2465,8 +2465,9 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
*/ */
expr = nft_expr_first(rule); expr = nft_expr_first(rule);
while (expr != nft_expr_last(rule) && expr->ops) { while (expr != nft_expr_last(rule) && expr->ops) {
next = nft_expr_next(expr);
nf_tables_expr_destroy(ctx, expr); nf_tables_expr_destroy(ctx, expr);
expr = nft_expr_next(expr); expr = next;
} }
kfree(rule); kfree(rule);
} }
@ -2589,17 +2590,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
if (chain->use == UINT_MAX) if (chain->use == UINT_MAX)
return -EOVERFLOW; return -EOVERFLOW;
}
if (nla[NFTA_RULE_POSITION]) { if (nla[NFTA_RULE_POSITION]) {
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
return -EOPNOTSUPP; old_rule = __nft_rule_lookup(chain, pos_handle);
if (IS_ERR(old_rule)) {
pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION])); NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
old_rule = __nft_rule_lookup(chain, pos_handle); return PTR_ERR(old_rule);
if (IS_ERR(old_rule)) { }
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION]);
return PTR_ERR(old_rule);
} }
} }
@ -2669,21 +2667,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
} }
if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nlh->nlmsg_flags & NLM_F_REPLACE) {
if (!nft_is_active_next(net, old_rule)) { trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
err = -ENOENT;
goto err2;
}
trans = nft_trans_rule_add(&ctx, NFT_MSG_DELRULE,
old_rule);
if (trans == NULL) { if (trans == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err2;
} }
nft_deactivate_next(net, old_rule); err = nft_delrule(&ctx, old_rule);
chain->use--; if (err < 0) {
nft_trans_destroy(trans);
if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
err = -ENOMEM;
goto err2; goto err2;
} }
@ -6324,7 +6315,7 @@ static void nf_tables_commit_chain_free_rules_old(struct nft_rule **rules)
call_rcu(&old->h, __nf_tables_commit_chain_free_rules_old); call_rcu(&old->h, __nf_tables_commit_chain_free_rules_old);
} }
static void nf_tables_commit_chain_active(struct net *net, struct nft_chain *chain) static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
{ {
struct nft_rule **g0, **g1; struct nft_rule **g0, **g1;
bool next_genbit; bool next_genbit;
@ -6441,11 +6432,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
/* step 2. Make rules_gen_X visible to packet path */ /* step 2. Make rules_gen_X visible to packet path */
list_for_each_entry(table, &net->nft.tables, list) { list_for_each_entry(table, &net->nft.tables, list) {
list_for_each_entry(chain, &table->chains, list) { list_for_each_entry(chain, &table->chains, list)
if (!nft_is_active_next(net, chain)) nf_tables_commit_chain(net, chain);
continue;
nf_tables_commit_chain_active(net, chain);
}
} }
/* /*

View File

@ -455,7 +455,8 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl,
case IPPROTO_TCP: case IPPROTO_TCP:
timeouts = nf_tcp_pernet(net)->timeouts; timeouts = nf_tcp_pernet(net)->timeouts;
break; break;
case IPPROTO_UDP: case IPPROTO_UDP: /* fallthrough */
case IPPROTO_UDPLITE:
timeouts = nf_udp_pernet(net)->timeouts; timeouts = nf_udp_pernet(net)->timeouts;
break; break;
case IPPROTO_DCCP: case IPPROTO_DCCP:
@ -469,13 +470,23 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl,
case IPPROTO_SCTP: case IPPROTO_SCTP:
#ifdef CONFIG_NF_CT_PROTO_SCTP #ifdef CONFIG_NF_CT_PROTO_SCTP
timeouts = nf_sctp_pernet(net)->timeouts; timeouts = nf_sctp_pernet(net)->timeouts;
#endif
break;
case IPPROTO_GRE:
#ifdef CONFIG_NF_CT_PROTO_GRE
if (l4proto->net_id) {
struct netns_proto_gre *net_gre;
net_gre = net_generic(net, *l4proto->net_id);
timeouts = net_gre->gre_timeouts;
}
#endif #endif
break; break;
case 255: case 255:
timeouts = &nf_generic_pernet(net)->timeout; timeouts = &nf_generic_pernet(net)->timeout;
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ONCE(1, "Missing timeouts for proto %d", l4proto->l4proto);
break; break;
} }

View File

@ -520,6 +520,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
void *info) void *info)
{ {
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
struct module *me = match->me;
struct xt_mtdtor_param par; struct xt_mtdtor_param par;
par.net = ctx->net; par.net = ctx->net;
@ -530,7 +531,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
par.match->destroy(&par); par.match->destroy(&par);
if (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(me);
} }
static void static void

View File

@ -214,7 +214,9 @@ static int __init nft_flow_offload_module_init(void)
{ {
int err; int err;
register_netdevice_notifier(&flow_offload_netdev_notifier); err = register_netdevice_notifier(&flow_offload_netdev_notifier);
if (err)
goto err;
err = nft_register_expr(&nft_flow_offload_type); err = nft_register_expr(&nft_flow_offload_type);
if (err < 0) if (err < 0)
@ -224,6 +226,7 @@ static int __init nft_flow_offload_module_init(void)
register_expr: register_expr:
unregister_netdevice_notifier(&flow_offload_netdev_notifier); unregister_netdevice_notifier(&flow_offload_netdev_notifier);
err:
return err; return err;
} }

View File

@ -201,18 +201,8 @@ static __net_init int xt_rateest_net_init(struct net *net)
return 0; return 0;
} }
static void __net_exit xt_rateest_net_exit(struct net *net)
{
struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
int i;
for (i = 0; i < ARRAY_SIZE(xn->hash); i++)
WARN_ON_ONCE(!hlist_empty(&xn->hash[i]));
}
static struct pernet_operations xt_rateest_net_ops = { static struct pernet_operations xt_rateest_net_ops = {
.init = xt_rateest_net_init, .init = xt_rateest_net_init,
.exit = xt_rateest_net_exit,
.id = &xt_rateest_id, .id = &xt_rateest_id,
.size = sizeof(struct xt_rateest_net), .size = sizeof(struct xt_rateest_net),
}; };

View File

@ -295,9 +295,10 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
/* copy match config into hashtable config */ /* copy match config into hashtable config */
ret = cfg_copy(&hinfo->cfg, (void *)cfg, 3); ret = cfg_copy(&hinfo->cfg, (void *)cfg, 3);
if (ret) {
if (ret) vfree(hinfo);
return ret; return ret;
}
hinfo->cfg.size = size; hinfo->cfg.size = size;
if (hinfo->cfg.max == 0) if (hinfo->cfg.max == 0)
@ -814,7 +815,6 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
int ret; int ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 1); ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
if (ret) if (ret)
return ret; return ret;
@ -830,7 +830,6 @@ hashlimit_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
int ret; int ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 2); ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
if (ret) if (ret)
return ret; return ret;
@ -921,7 +920,6 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)
return ret; return ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 1); ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
if (ret) if (ret)
return ret; return ret;
@ -940,7 +938,6 @@ static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)
return ret; return ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 2); ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
if (ret) if (ret)
return ret; return ret;

View File

@ -24,6 +24,7 @@ TARGETS += memory-hotplug
TARGETS += mount TARGETS += mount
TARGETS += mqueue TARGETS += mqueue
TARGETS += net TARGETS += net
TARGETS += netfilter
TARGETS += nsfs TARGETS += nsfs
TARGETS += powerpc TARGETS += powerpc
TARGETS += proc TARGETS += proc

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for netfilter selftests
TEST_PROGS := nft_trans_stress.sh
include ../lib.mk

View File

@ -0,0 +1,2 @@
CONFIG_NET_NS=y
NF_TABLES_INET=y

View File

@ -0,0 +1,78 @@
#!/bin/bash
#
# This test is for stress-testing the nf_tables config plane path vs.
# packet path processing: Make sure we never release rules that are
# still visible to other cpus.
#
# set -e
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
testns=testns1
tables="foo bar baz quux"
nft --version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without nft tool"
exit $ksft_skip
fi
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without ip tool"
exit $ksft_skip
fi
tmp=$(mktemp)
for table in $tables; do
echo add table inet "$table" >> "$tmp"
echo flush table inet "$table" >> "$tmp"
echo "add chain inet $table INPUT { type filter hook input priority 0; }" >> "$tmp"
echo "add chain inet $table OUTPUT { type filter hook output priority 0; }" >> "$tmp"
for c in $(seq 1 400); do
chain=$(printf "chain%03u" "$c")
echo "add chain inet $table $chain" >> "$tmp"
done
for c in $(seq 1 400); do
chain=$(printf "chain%03u" "$c")
for BASE in INPUT OUTPUT; do
echo "add rule inet $table $BASE counter jump $chain" >> "$tmp"
done
echo "add rule inet $table $chain counter return" >> "$tmp"
done
done
ip netns add "$testns"
ip -netns "$testns" link set lo up
lscpu | grep ^CPU\(s\): | ( read cpu cpunum ;
cpunum=$((cpunum-1))
for i in $(seq 0 $cpunum);do
mask=$(printf 0x%x $((1<<$i)))
ip netns exec "$testns" taskset $mask ping -4 127.0.0.1 -fq > /dev/null &
ip netns exec "$testns" taskset $mask ping -6 ::1 -fq > /dev/null &
done)
sleep 1
for i in $(seq 1 10) ; do ip netns exec "$testns" nft -f "$tmp" & done
for table in $tables;do
randsleep=$((RANDOM%10))
sleep $randsleep
ip netns exec "$testns" nft delete table inet $table 2>/dev/null
done
randsleep=$((RANDOM%10))
sleep $randsleep
pkill -9 ping
wait
rm -f "$tmp"
ip netns del "$testns"