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 patches for your net tree: 1) Fix NULL pointer dereference from nf_nat_decode_session() if NAT is not loaded, from Prashant Bhole. 2) Fix socket extension module autoload. 3) Don't bogusly reject sets with the NFT_SET_EVAL flag set on from the dynset extension. 4) Fix races with nf_tables module removal and netns exit path, patches from Florian Westphal. 5) Don't hit BUG_ON if jumpstack goes too deep, instead hit WARN_ON_ONCE, from Taehee Yoo. 6) Another NULL pointer dereference from ctnetlink, again if NAT is not loaded, from Florian Westphal. 7) Fix x_tables match list corruption in xt_connmark module removal path, also from Florian. 8) nf_conncount doesn't properly deal with conntrack zones, hence garbage collector may get rid of entries in a different zone. From Yi-Hung Wei. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
60d061e347
|
@ -345,7 +345,7 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
nat_hook = rcu_dereference(nf_nat_hook);
|
nat_hook = rcu_dereference(nf_nat_hook);
|
||||||
if (nat_hook->decode_session)
|
if (nat_hook && nat_hook->decode_session)
|
||||||
nat_hook->decode_session(skb, fl);
|
nat_hook->decode_session(skb, fl);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,7 +20,8 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
|
||||||
bool *addit);
|
bool *addit);
|
||||||
|
|
||||||
bool nf_conncount_add(struct hlist_head *head,
|
bool nf_conncount_add(struct hlist_head *head,
|
||||||
const struct nf_conntrack_tuple *tuple);
|
const struct nf_conntrack_tuple *tuple,
|
||||||
|
const struct nf_conntrack_zone *zone);
|
||||||
|
|
||||||
void nf_conncount_cache_free(struct hlist_head *hhead);
|
void nf_conncount_cache_free(struct hlist_head *hhead);
|
||||||
|
|
||||||
|
|
|
@ -266,7 +266,7 @@ enum nft_rule_compat_attributes {
|
||||||
* @NFT_SET_INTERVAL: set contains intervals
|
* @NFT_SET_INTERVAL: set contains intervals
|
||||||
* @NFT_SET_MAP: set is used as a dictionary
|
* @NFT_SET_MAP: set is used as a dictionary
|
||||||
* @NFT_SET_TIMEOUT: set uses timeouts
|
* @NFT_SET_TIMEOUT: set uses timeouts
|
||||||
* @NFT_SET_EVAL: set contains expressions for evaluation
|
* @NFT_SET_EVAL: set can be updated from the evaluation path
|
||||||
* @NFT_SET_OBJECT: set contains stateful objects
|
* @NFT_SET_OBJECT: set contains stateful objects
|
||||||
*/
|
*/
|
||||||
enum nft_set_flags {
|
enum nft_set_flags {
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
struct nf_conncount_tuple {
|
struct nf_conncount_tuple {
|
||||||
struct hlist_node node;
|
struct hlist_node node;
|
||||||
struct nf_conntrack_tuple tuple;
|
struct nf_conntrack_tuple tuple;
|
||||||
|
struct nf_conntrack_zone zone;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nf_conncount_rb {
|
struct nf_conncount_rb {
|
||||||
|
@ -80,7 +81,8 @@ static int key_diff(const u32 *a, const u32 *b, unsigned int klen)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nf_conncount_add(struct hlist_head *head,
|
bool nf_conncount_add(struct hlist_head *head,
|
||||||
const struct nf_conntrack_tuple *tuple)
|
const struct nf_conntrack_tuple *tuple,
|
||||||
|
const struct nf_conntrack_zone *zone)
|
||||||
{
|
{
|
||||||
struct nf_conncount_tuple *conn;
|
struct nf_conncount_tuple *conn;
|
||||||
|
|
||||||
|
@ -88,6 +90,7 @@ bool nf_conncount_add(struct hlist_head *head,
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return false;
|
return false;
|
||||||
conn->tuple = *tuple;
|
conn->tuple = *tuple;
|
||||||
|
conn->zone = *zone;
|
||||||
hlist_add_head(&conn->node, head);
|
hlist_add_head(&conn->node, head);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +111,7 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
|
||||||
|
|
||||||
/* check the saved connections */
|
/* check the saved connections */
|
||||||
hlist_for_each_entry_safe(conn, n, head, node) {
|
hlist_for_each_entry_safe(conn, n, head, node) {
|
||||||
found = nf_conntrack_find_get(net, zone, &conn->tuple);
|
found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple);
|
||||||
if (found == NULL) {
|
if (found == NULL) {
|
||||||
hlist_del(&conn->node);
|
hlist_del(&conn->node);
|
||||||
kmem_cache_free(conncount_conn_cachep, conn);
|
kmem_cache_free(conncount_conn_cachep, conn);
|
||||||
|
@ -117,7 +120,8 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
|
||||||
|
|
||||||
found_ct = nf_ct_tuplehash_to_ctrack(found);
|
found_ct = nf_ct_tuplehash_to_ctrack(found);
|
||||||
|
|
||||||
if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple)) {
|
if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple) &&
|
||||||
|
nf_ct_zone_equal(found_ct, zone, zone->dir)) {
|
||||||
/*
|
/*
|
||||||
* Just to be sure we have it only once in the list.
|
* Just to be sure we have it only once in the list.
|
||||||
* We should not see tuples twice unless someone hooks
|
* We should not see tuples twice unless someone hooks
|
||||||
|
@ -196,7 +200,7 @@ count_tree(struct net *net, struct rb_root *root,
|
||||||
if (!addit)
|
if (!addit)
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
if (!nf_conncount_add(&rbconn->hhead, tuple))
|
if (!nf_conncount_add(&rbconn->hhead, tuple, zone))
|
||||||
return 0; /* hotdrop */
|
return 0; /* hotdrop */
|
||||||
|
|
||||||
return count + 1;
|
return count + 1;
|
||||||
|
@ -238,6 +242,7 @@ count_tree(struct net *net, struct rb_root *root,
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->tuple = *tuple;
|
conn->tuple = *tuple;
|
||||||
|
conn->zone = *zone;
|
||||||
memcpy(rbconn->key, key, sizeof(u32) * keylen);
|
memcpy(rbconn->key, key, sizeof(u32) * keylen);
|
||||||
|
|
||||||
INIT_HLIST_HEAD(&rbconn->hhead);
|
INIT_HLIST_HEAD(&rbconn->hhead);
|
||||||
|
|
|
@ -1446,7 +1446,8 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
|
||||||
}
|
}
|
||||||
nfnl_lock(NFNL_SUBSYS_CTNETLINK);
|
nfnl_lock(NFNL_SUBSYS_CTNETLINK);
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (nat_hook->parse_nat_setup)
|
nat_hook = rcu_dereference(nf_nat_hook);
|
||||||
|
if (nat_hook)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
#endif
|
#endif
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
|
@ -5837,18 +5837,23 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
|
||||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||||
struct nft_flowtable *flowtable;
|
struct nft_flowtable *flowtable;
|
||||||
struct nft_table *table;
|
struct nft_table *table;
|
||||||
|
struct net *net;
|
||||||
|
|
||||||
if (event != NETDEV_UNREGISTER)
|
if (event != NETDEV_UNREGISTER)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
net = maybe_get_net(dev_net(dev));
|
||||||
|
if (!net)
|
||||||
|
return 0;
|
||||||
|
|
||||||
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
||||||
list_for_each_entry(table, &dev_net(dev)->nft.tables, list) {
|
list_for_each_entry(table, &net->nft.tables, list) {
|
||||||
list_for_each_entry(flowtable, &table->flowtables, list) {
|
list_for_each_entry(flowtable, &table->flowtables, list) {
|
||||||
nft_flowtable_event(event, dev, flowtable);
|
nft_flowtable_event(event, dev, flowtable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
||||||
|
put_net(net);
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6439,7 +6444,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
|
||||||
kfree(trans);
|
kfree(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nf_tables_abort(struct net *net, struct sk_buff *skb)
|
static int __nf_tables_abort(struct net *net)
|
||||||
{
|
{
|
||||||
struct nft_trans *trans, *next;
|
struct nft_trans *trans, *next;
|
||||||
struct nft_trans_elem *te;
|
struct nft_trans_elem *te;
|
||||||
|
@ -6555,6 +6560,11 @@ static void nf_tables_cleanup(struct net *net)
|
||||||
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
|
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nf_tables_abort(struct net *net, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return __nf_tables_abort(net);
|
||||||
|
}
|
||||||
|
|
||||||
static bool nf_tables_valid_genid(struct net *net, u32 genid)
|
static bool nf_tables_valid_genid(struct net *net, u32 genid)
|
||||||
{
|
{
|
||||||
return net->nft.base_seq == genid;
|
return net->nft.base_seq == genid;
|
||||||
|
@ -7149,9 +7159,12 @@ static int __net_init nf_tables_init_net(struct net *net)
|
||||||
|
|
||||||
static void __net_exit nf_tables_exit_net(struct net *net)
|
static void __net_exit nf_tables_exit_net(struct net *net)
|
||||||
{
|
{
|
||||||
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
||||||
|
if (!list_empty(&net->nft.commit_list))
|
||||||
|
__nf_tables_abort(net);
|
||||||
__nft_release_tables(net);
|
__nft_release_tables(net);
|
||||||
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
||||||
WARN_ON_ONCE(!list_empty(&net->nft.tables));
|
WARN_ON_ONCE(!list_empty(&net->nft.tables));
|
||||||
WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pernet_operations nf_tables_net_ops = {
|
static struct pernet_operations nf_tables_net_ops = {
|
||||||
|
@ -7193,13 +7206,13 @@ err1:
|
||||||
|
|
||||||
static void __exit nf_tables_module_exit(void)
|
static void __exit nf_tables_module_exit(void)
|
||||||
{
|
{
|
||||||
unregister_pernet_subsys(&nf_tables_net_ops);
|
|
||||||
nfnetlink_subsys_unregister(&nf_tables_subsys);
|
nfnetlink_subsys_unregister(&nf_tables_subsys);
|
||||||
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
|
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
|
||||||
|
nft_chain_filter_fini();
|
||||||
|
unregister_pernet_subsys(&nf_tables_net_ops);
|
||||||
rcu_barrier();
|
rcu_barrier();
|
||||||
nf_tables_core_module_exit();
|
nf_tables_core_module_exit();
|
||||||
kfree(info);
|
kfree(info);
|
||||||
nft_chain_filter_fini();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(nf_tables_module_init);
|
module_init(nf_tables_module_init);
|
||||||
|
|
|
@ -183,7 +183,8 @@ next_rule:
|
||||||
|
|
||||||
switch (regs.verdict.code) {
|
switch (regs.verdict.code) {
|
||||||
case NFT_JUMP:
|
case NFT_JUMP:
|
||||||
BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
|
if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE))
|
||||||
|
return NF_DROP;
|
||||||
jumpstack[stackptr].chain = chain;
|
jumpstack[stackptr].chain = chain;
|
||||||
jumpstack[stackptr].rules = rules + 1;
|
jumpstack[stackptr].rules = rules + 1;
|
||||||
stackptr++;
|
stackptr++;
|
||||||
|
|
|
@ -429,7 +429,7 @@ replay:
|
||||||
*/
|
*/
|
||||||
if (err == -EAGAIN) {
|
if (err == -EAGAIN) {
|
||||||
status |= NFNL_BATCH_REPLAY;
|
status |= NFNL_BATCH_REPLAY;
|
||||||
goto next;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ack:
|
ack:
|
||||||
|
@ -456,7 +456,7 @@ ack:
|
||||||
if (err)
|
if (err)
|
||||||
status |= NFNL_BATCH_FAILURE;
|
status |= NFNL_BATCH_FAILURE;
|
||||||
}
|
}
|
||||||
next:
|
|
||||||
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
|
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
|
||||||
if (msglen > skb->len)
|
if (msglen > skb->len)
|
||||||
msglen = skb->len;
|
msglen = skb->len;
|
||||||
|
@ -464,7 +464,11 @@ next:
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
if (status & NFNL_BATCH_REPLAY) {
|
if (status & NFNL_BATCH_REPLAY) {
|
||||||
ss->abort(net, oskb);
|
const struct nfnetlink_subsystem *ss2;
|
||||||
|
|
||||||
|
ss2 = nfnl_dereference_protected(subsys_id);
|
||||||
|
if (ss2 == ss)
|
||||||
|
ss->abort(net, oskb);
|
||||||
nfnl_err_reset(&err_list);
|
nfnl_err_reset(&err_list);
|
||||||
nfnl_unlock(subsys_id);
|
nfnl_unlock(subsys_id);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
|
@ -318,6 +318,10 @@ static int nf_tables_netdev_event(struct notifier_block *this,
|
||||||
event != NETDEV_CHANGENAME)
|
event != NETDEV_CHANGENAME)
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
ctx.net = maybe_get_net(ctx.net);
|
||||||
|
if (!ctx.net)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
||||||
list_for_each_entry(table, &ctx.net->nft.tables, list) {
|
list_for_each_entry(table, &ctx.net->nft.tables, list) {
|
||||||
if (table->family != NFPROTO_NETDEV)
|
if (table->family != NFPROTO_NETDEV)
|
||||||
|
@ -334,6 +338,7 @@ static int nf_tables_netdev_event(struct notifier_block *this,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
||||||
|
put_net(ctx.net);
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
|
||||||
if (!addit)
|
if (!addit)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!nf_conncount_add(&priv->hhead, tuple_ptr)) {
|
if (!nf_conncount_add(&priv->hhead, tuple_ptr, zone)) {
|
||||||
regs->verdict.code = NF_DROP;
|
regs->verdict.code = NF_DROP;
|
||||||
spin_unlock_bh(&priv->lock);
|
spin_unlock_bh(&priv->lock);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -203,9 +203,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
||||||
goto err1;
|
goto err1;
|
||||||
set->ops->gc_init(set);
|
set->ops->gc_init(set);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (set->flags & NFT_SET_EVAL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
nft_set_ext_prepare(&priv->tmpl);
|
nft_set_ext_prepare(&priv->tmpl);
|
||||||
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
|
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
|
||||||
|
|
|
@ -142,3 +142,4 @@ module_exit(nft_socket_module_exit);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Máté Eckl");
|
MODULE_AUTHOR("Máté Eckl");
|
||||||
MODULE_DESCRIPTION("nf_tables socket match module");
|
MODULE_DESCRIPTION("nf_tables socket match module");
|
||||||
|
MODULE_ALIAS_NFT_EXPR("socket");
|
||||||
|
|
|
@ -211,7 +211,7 @@ static int __init connmark_mt_init(void)
|
||||||
static void __exit connmark_mt_exit(void)
|
static void __exit connmark_mt_exit(void)
|
||||||
{
|
{
|
||||||
xt_unregister_match(&connmark_mt_reg);
|
xt_unregister_match(&connmark_mt_reg);
|
||||||
xt_unregister_target(connmark_tg_reg);
|
xt_unregister_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(connmark_mt_init);
|
module_init(connmark_mt_init);
|
||||||
|
|
Loading…
Reference in New Issue