Commit f41f0319 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter/nftables/IPVS fixes for net

The following patchset contains Netfilter/IPVS fixes, mostly nftables
fixes, most relevantly they are:

* Fix a crash in the h323 conntrack NAT helper due to expectation list
  corruption, from Alexey Dobriyan.

* A couple of RCU race fixes for conntrack, one manifests by hitting BUG_ON
  in nf_nat_setup_info() and the destroy path, patches from Andrey Vagin and
  me.

* Dump direction attribute in nft_ct only if it is set, from Arturo
  Borrero.

* Fix IPVS bug in its own connection tracking system that may lead to
  copying only 4 bytes of the IPv6 address when initializing the
  ip_vs_conn object, from Michal Kubecek.

* Fix -EBUSY errors in nftables when deleting the rules, chain and tables
  in a row due mixture of asynchronous and synchronous object releasing,
  from me.

* Three fixes for the nf_tables set infrastructure when using intervals and
  mappings, from me.

* Four patches to fixing the nf_tables log, reject and ct expressions from
  the new inet table, from Patrick McHardy.

* Fix memory overrun in the map that is used to dynamically allocate names
  from anonymous sets, also from Patrick.

* Fix a potential oops if you dump a set with NFPROTO_UNSPEC and a table
  name, from Patrick McHardy.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4a5ab4e2 6d8c00d5
...@@ -284,6 +284,8 @@ extern unsigned int nf_conntrack_max; ...@@ -284,6 +284,8 @@ extern unsigned int nf_conntrack_max;
extern unsigned int nf_conntrack_hash_rnd; extern unsigned int nf_conntrack_hash_rnd;
void init_nf_conntrack_hash_rnd(void); void init_nf_conntrack_hash_rnd(void);
void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl);
#define NF_CT_STAT_INC(net, count) __this_cpu_inc((net)->ct.stat->count) #define NF_CT_STAT_INC(net, count) __this_cpu_inc((net)->ct.stat->count)
#define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count) #define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count)
......
...@@ -252,6 +252,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -252,6 +252,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
* @owner: module reference * @owner: module reference
* @policy: netlink attribute policy * @policy: netlink attribute policy
* @maxattr: highest netlink attribute number * @maxattr: highest netlink attribute number
* @family: address family for AF-specific types
*/ */
struct nft_expr_type { struct nft_expr_type {
const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *, const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *,
...@@ -262,6 +263,7 @@ struct nft_expr_type { ...@@ -262,6 +263,7 @@ struct nft_expr_type {
struct module *owner; struct module *owner;
const struct nla_policy *policy; const struct nla_policy *policy;
unsigned int maxattr; unsigned int maxattr;
u8 family;
}; };
/** /**
...@@ -320,7 +322,6 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) ...@@ -320,7 +322,6 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
* struct nft_rule - nf_tables rule * struct nft_rule - nf_tables rule
* *
* @list: used internally * @list: used internally
* @rcu_head: used internally for rcu
* @handle: rule handle * @handle: rule handle
* @genmask: generation mask * @genmask: generation mask
* @dlen: length of expression data * @dlen: length of expression data
...@@ -328,7 +329,6 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) ...@@ -328,7 +329,6 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
*/ */
struct nft_rule { struct nft_rule {
struct list_head list; struct list_head list;
struct rcu_head rcu_head;
u64 handle:46, u64 handle:46,
genmask:2, genmask:2,
dlen:16; dlen:16;
...@@ -389,7 +389,6 @@ enum nft_chain_flags { ...@@ -389,7 +389,6 @@ enum nft_chain_flags {
* *
* @rules: list of rules in the chain * @rules: list of rules in the chain
* @list: used internally * @list: used internally
* @rcu_head: used internally
* @net: net namespace that this chain belongs to * @net: net namespace that this chain belongs to
* @table: table that this chain belongs to * @table: table that this chain belongs to
* @handle: chain handle * @handle: chain handle
...@@ -401,7 +400,6 @@ enum nft_chain_flags { ...@@ -401,7 +400,6 @@ enum nft_chain_flags {
struct nft_chain { struct nft_chain {
struct list_head rules; struct list_head rules;
struct list_head list; struct list_head list;
struct rcu_head rcu_head;
struct net *net; struct net *net;
struct nft_table *table; struct nft_table *table;
u64 handle; u64 handle;
...@@ -529,6 +527,9 @@ void nft_unregister_expr(struct nft_expr_type *); ...@@ -529,6 +527,9 @@ void nft_unregister_expr(struct nft_expr_type *);
#define MODULE_ALIAS_NFT_CHAIN(family, name) \ #define MODULE_ALIAS_NFT_CHAIN(family, name) \
MODULE_ALIAS("nft-chain-" __stringify(family) "-" name) MODULE_ALIAS("nft-chain-" __stringify(family) "-" name)
#define MODULE_ALIAS_NFT_AF_EXPR(family, name) \
MODULE_ALIAS("nft-expr-" __stringify(family) "-" name)
#define MODULE_ALIAS_NFT_EXPR(name) \ #define MODULE_ALIAS_NFT_EXPR(name) \
MODULE_ALIAS("nft-expr-" name) MODULE_ALIAS("nft-expr-" name)
......
#ifndef _NFT_REJECT_H_
#define _NFT_REJECT_H_
struct nft_reject {
enum nft_reject_types type:8;
u8 icmp_code;
};
extern const struct nla_policy nft_reject_policy[];
int nft_reject_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr);
void nft_reject_ipv4_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt);
void nft_reject_ipv6_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt);
#endif
...@@ -61,6 +61,11 @@ config NFT_CHAIN_NAT_IPV4 ...@@ -61,6 +61,11 @@ config NFT_CHAIN_NAT_IPV4
packet transformations such as the source, destination address and packet transformations such as the source, destination address and
source and destination ports. source and destination ports.
config NFT_REJECT_IPV4
depends on NF_TABLES_IPV4
default NFT_REJECT
tristate
config NF_TABLES_ARP config NF_TABLES_ARP
depends on NF_TABLES depends on NF_TABLES
tristate "ARP nf_tables support" tristate "ARP nf_tables support"
......
...@@ -30,6 +30,7 @@ obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o ...@@ -30,6 +30,7 @@ obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o
# generic IP tables # generic IP tables
......
...@@ -229,7 +229,10 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, ...@@ -229,7 +229,10 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
ret = nf_ct_expect_related(rtcp_exp); ret = nf_ct_expect_related(rtcp_exp);
if (ret == 0) if (ret == 0)
break; break;
else if (ret != -EBUSY) { else if (ret == -EBUSY) {
nf_ct_unexpect_related(rtp_exp);
continue;
} else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtp_exp);
nated_port = 0; nated_port = 0;
break; break;
......
/*
* Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2013 Eric Leblond <eric@regit.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/icmp.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <net/netfilter/nft_reject.h>
void nft_reject_ipv4_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
nf_send_unreach(pkt->skb, priv->icmp_code);
break;
case NFT_REJECT_TCP_RST:
nf_send_reset(pkt->skb, pkt->ops->hooknum);
break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
EXPORT_SYMBOL_GPL(nft_reject_ipv4_eval);
static struct nft_expr_type nft_reject_ipv4_type;
static const struct nft_expr_ops nft_reject_ipv4_ops = {
.type = &nft_reject_ipv4_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_ipv4_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_ipv4_type __read_mostly = {
.family = NFPROTO_IPV4,
.name = "reject",
.ops = &nft_reject_ipv4_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_ipv4_module_init(void)
{
return nft_register_expr(&nft_reject_ipv4_type);
}
static void __exit nft_reject_ipv4_module_exit(void)
{
nft_unregister_expr(&nft_reject_ipv4_type);
}
module_init(nft_reject_ipv4_module_init);
module_exit(nft_reject_ipv4_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "reject");
...@@ -50,6 +50,11 @@ config NFT_CHAIN_NAT_IPV6 ...@@ -50,6 +50,11 @@ config NFT_CHAIN_NAT_IPV6
packet transformations such as the source, destination address and packet transformations such as the source, destination address and
source and destination ports. source and destination ports.
config NFT_REJECT_IPV6
depends on NF_TABLES_IPV6
default NFT_REJECT
tristate
config IP6_NF_IPTABLES config IP6_NF_IPTABLES
tristate "IP6 tables support (required for filtering)" tristate "IP6 tables support (required for filtering)"
depends on INET && IPV6 depends on INET && IPV6
......
...@@ -27,6 +27,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o ...@@ -27,6 +27,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o
obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
# matches # matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
......
/*
* Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2013 Eric Leblond <eric@regit.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_reject.h>
#include <net/netfilter/ipv6/nf_reject.h>
void nft_reject_ipv6_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
nf_send_unreach6(net, pkt->skb, priv->icmp_code,
pkt->ops->hooknum);
break;
case NFT_REJECT_TCP_RST:
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
EXPORT_SYMBOL_GPL(nft_reject_ipv6_eval);
static struct nft_expr_type nft_reject_ipv6_type;
static const struct nft_expr_ops nft_reject_ipv6_ops = {
.type = &nft_reject_ipv6_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_ipv6_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_ipv6_type __read_mostly = {
.family = NFPROTO_IPV6,
.name = "reject",
.ops = &nft_reject_ipv6_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_ipv6_module_init(void)
{
return nft_register_expr(&nft_reject_ipv6_type);
}
static void __exit nft_reject_ipv6_module_exit(void)
{
nft_unregister_expr(&nft_reject_ipv6_type);
}
module_init(nft_reject_ipv6_module_init);
module_exit(nft_reject_ipv6_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "reject");
...@@ -513,7 +513,6 @@ config NFT_QUEUE ...@@ -513,7 +513,6 @@ config NFT_QUEUE
config NFT_REJECT config NFT_REJECT
depends on NF_TABLES depends on NF_TABLES
depends on NF_TABLES_IPV6 || !NF_TABLES_IPV6
default m if NETFILTER_ADVANCED=n default m if NETFILTER_ADVANCED=n
tristate "Netfilter nf_tables reject support" tristate "Netfilter nf_tables reject support"
help help
...@@ -521,6 +520,11 @@ config NFT_REJECT ...@@ -521,6 +520,11 @@ config NFT_REJECT
explicitly deny and notify via TCP reset/ICMP informational errors explicitly deny and notify via TCP reset/ICMP informational errors
unallowed traffic. unallowed traffic.
config NFT_REJECT_INET
depends on NF_TABLES_INET
default NFT_REJECT
tristate
config NFT_COMPAT config NFT_COMPAT
depends on NF_TABLES depends on NF_TABLES
depends on NETFILTER_XTABLES depends on NETFILTER_XTABLES
......
...@@ -79,6 +79,7 @@ obj-$(CONFIG_NFT_LIMIT) += nft_limit.o ...@@ -79,6 +79,7 @@ obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
obj-$(CONFIG_NFT_NAT) += nft_nat.o obj-$(CONFIG_NFT_NAT) += nft_nat.o
obj-$(CONFIG_NFT_QUEUE) += nft_queue.o obj-$(CONFIG_NFT_QUEUE) += nft_queue.o
obj-$(CONFIG_NFT_REJECT) += nft_reject.o obj-$(CONFIG_NFT_REJECT) += nft_reject.o
obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o
obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o
obj-$(CONFIG_NFT_HASH) += nft_hash.o obj-$(CONFIG_NFT_HASH) += nft_hash.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
......
...@@ -871,11 +871,11 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, ...@@ -871,11 +871,11 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
cp->protocol = p->protocol; cp->protocol = p->protocol;
ip_vs_addr_set(p->af, &cp->caddr, p->caddr); ip_vs_addr_set(p->af, &cp->caddr, p->caddr);
cp->cport = p->cport; cp->cport = p->cport;
ip_vs_addr_set(p->af, &cp->vaddr, p->vaddr); /* proto should only be IPPROTO_IP if p->vaddr is a fwmark */
cp->vport = p->vport;
/* proto should only be IPPROTO_IP if d_addr is a fwmark */
ip_vs_addr_set(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af, ip_vs_addr_set(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af,
&cp->daddr, daddr); &cp->vaddr, p->vaddr);
cp->vport = p->vport;
ip_vs_addr_set(p->af, &cp->daddr, daddr);
cp->dport = dport; cp->dport = dport;
cp->flags = flags; cp->flags = flags;
cp->fwmark = fwmark; cp->fwmark = fwmark;
......
...@@ -312,6 +312,21 @@ static void death_by_timeout(unsigned long ul_conntrack) ...@@ -312,6 +312,21 @@ static void death_by_timeout(unsigned long ul_conntrack)
nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0); nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0);
} }
static inline bool
nf_ct_key_equal(struct nf_conntrack_tuple_hash *h,
const struct nf_conntrack_tuple *tuple,
u16 zone)
{
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
/* A conntrack can be recreated with the equal tuple,
* so we need to check that the conntrack is confirmed
*/
return nf_ct_tuple_equal(tuple, &h->tuple) &&
nf_ct_zone(ct) == zone &&
nf_ct_is_confirmed(ct);
}
/* /*
* Warning : * Warning :
* - Caller must take a reference on returned object * - Caller must take a reference on returned object
...@@ -333,8 +348,7 @@ ____nf_conntrack_find(struct net *net, u16 zone, ...@@ -333,8 +348,7 @@ ____nf_conntrack_find(struct net *net, u16 zone,
local_bh_disable(); local_bh_disable();
begin: begin:
hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[bucket], hnnode) { hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[bucket], hnnode) {
if (nf_ct_tuple_equal(tuple, &h->tuple) && if (nf_ct_key_equal(h, tuple, zone)) {
nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)) == zone) {
NF_CT_STAT_INC(net, found); NF_CT_STAT_INC(net, found);
local_bh_enable(); local_bh_enable();
return h; return h;
...@@ -372,8 +386,7 @@ __nf_conntrack_find_get(struct net *net, u16 zone, ...@@ -372,8 +386,7 @@ __nf_conntrack_find_get(struct net *net, u16 zone,
!atomic_inc_not_zero(&ct->ct_general.use))) !atomic_inc_not_zero(&ct->ct_general.use)))
h = NULL; h = NULL;
else { else {
if (unlikely(!nf_ct_tuple_equal(tuple, &h->tuple) || if (unlikely(!nf_ct_key_equal(h, tuple, zone))) {
nf_ct_zone(ct) != zone)) {
nf_ct_put(ct); nf_ct_put(ct);
goto begin; goto begin;
} }
...@@ -435,7 +448,9 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) ...@@ -435,7 +448,9 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
goto out; goto out;
add_timer(&ct->timeout); add_timer(&ct->timeout);
nf_conntrack_get(&ct->ct_general); smp_wmb();
/* The caller holds a reference to this object */
atomic_set(&ct->ct_general.use, 2);
__nf_conntrack_hash_insert(ct, hash, repl_hash); __nf_conntrack_hash_insert(ct, hash, repl_hash);
NF_CT_STAT_INC(net, insert); NF_CT_STAT_INC(net, insert);
spin_unlock_bh(&nf_conntrack_lock); spin_unlock_bh(&nf_conntrack_lock);
...@@ -449,6 +464,21 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) ...@@ -449,6 +464,21 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
} }
EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
/* deletion from this larval template list happens via nf_ct_put() */
void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl)
{
__set_bit(IPS_TEMPLATE_BIT, &tmpl->status);
__set_bit(IPS_CONFIRMED_BIT, &tmpl->status);
nf_conntrack_get(&tmpl->ct_general);
spin_lock_bh(&nf_conntrack_lock);
/* Overload tuple linked list to put us in template list. */
hlist_nulls_add_head_rcu(&tmpl->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
&net->ct.tmpl);
spin_unlock_bh(&nf_conntrack_lock);
}
EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert);
/* Confirm a connection given skb; places it in hash table */ /* Confirm a connection given skb; places it in hash table */
int int
__nf_conntrack_confirm(struct sk_buff *skb) __nf_conntrack_confirm(struct sk_buff *skb)
...@@ -720,11 +750,10 @@ __nf_conntrack_alloc(struct net *net, u16 zone, ...@@ -720,11 +750,10 @@ __nf_conntrack_alloc(struct net *net, u16 zone,
nf_ct_zone->id = zone; nf_ct_zone->id = zone;
} }
#endif #endif
/* /* Because we use RCU lookups, we set ct_general.use to zero before
* changes to lookup keys must be done before setting refcnt to 1 * this is inserted in any list.
*/ */
smp_wmb(); atomic_set(&ct->ct_general.use, 0);
atomic_set(&ct->ct_general.use, 1);
return ct; return ct;
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
...@@ -748,6 +777,11 @@ void nf_conntrack_free(struct nf_conn *ct) ...@@ -748,6 +777,11 @@ void nf_conntrack_free(struct nf_conn *ct)
{ {
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
/* A freed object has refcnt == 0, that's
* the golden rule for SLAB_DESTROY_BY_RCU
*/
NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 0);
nf_ct_ext_destroy(ct); nf_ct_ext_destroy(ct);
nf_ct_ext_free(ct); nf_ct_ext_free(ct);
kmem_cache_free(net->ct.nf_conntrack_cachep, ct); kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
...@@ -843,6 +877,9 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, ...@@ -843,6 +877,9 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
NF_CT_STAT_INC(net, new); NF_CT_STAT_INC(net, new);
} }
/* Now it is inserted into the unconfirmed list, bump refcount */
nf_conntrack_get(&ct->ct_general);
/* Overload tuple linked list to put us in unconfirmed list. */ /* Overload tuple linked list to put us in unconfirmed list. */
hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
&net->ct.unconfirmed); &net->ct.unconfirmed);
......
...@@ -363,9 +363,8 @@ static int __net_init synproxy_net_init(struct net *net) ...@@ -363,9 +363,8 @@ static int __net_init synproxy_net_init(struct net *net)
goto err2; goto err2;
if (!nfct_synproxy_ext_add(ct)) if (!nfct_synproxy_ext_add(ct))
goto err2; goto err2;
__set_bit(IPS_TEMPLATE_BIT, &ct->status);
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
nf_conntrack_tmpl_insert(net, ct);
snet->tmpl = ct; snet->tmpl = ct;
snet->stats = alloc_percpu(struct synproxy_stats); snet->stats = alloc_percpu(struct synproxy_stats);
...@@ -390,7 +389,7 @@ static void __net_exit synproxy_net_exit(struct net *net) ...@@ -390,7 +389,7 @@ static void __net_exit synproxy_net_exit(struct net *net)
{ {
struct synproxy_net *snet = synproxy_pernet(net); struct synproxy_net *snet = synproxy_pernet(net);
nf_conntrack_free(snet->tmpl); nf_ct_put(snet->tmpl);
synproxy_proc_exit(net); synproxy_proc_exit(net);
free_percpu(snet->stats); free_percpu(snet->stats);
} }
......
...@@ -1008,10 +1008,8 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1008,10 +1008,8 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
return 0; return 0;
} }
static void nf_tables_rcu_chain_destroy(struct rcu_head *head) static void nf_tables_chain_destroy(struct nft_chain *chain)
{ {
struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
BUG_ON(chain->use > 0); BUG_ON(chain->use > 0);
if (chain->flags & NFT_BASE_CHAIN) { if (chain->flags & NFT_BASE_CHAIN) {
...@@ -1045,7 +1043,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1045,7 +1043,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
if (!list_empty(&chain->rules)) if (!list_empty(&chain->rules) || chain->use > 0)
return -EBUSY; return -EBUSY;
list_del(&chain->list); list_del(&chain->list);
...@@ -1059,7 +1057,9 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1059,7 +1057,9 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
family); family);
/* Make sure all rule references are gone before this is released */ /* Make sure all rule references are gone before this is released */
call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy); synchronize_rcu();
nf_tables_chain_destroy(chain);
return 0; return 0;
} }
...@@ -1114,35 +1114,45 @@ void nft_unregister_expr(struct nft_expr_type *type) ...@@ -1114,35 +1114,45 @@ void nft_unregister_expr(struct nft_expr_type *type)
} }
EXPORT_SYMBOL_GPL(nft_unregister_expr); EXPORT_SYMBOL_GPL(nft_unregister_expr);
static const struct nft_expr_type *__nft_expr_type_get(struct nlattr *nla) static const struct nft_expr_type *__nft_expr_type_get(u8 family,
struct nlattr *nla)
{ {
const struct nft_expr_type *type; const struct nft_expr_type *type;
list_for_each_entry(type, &nf_tables_expressions, list) { list_for_each_entry(type, &nf_tables_expressions, list) {
if (!nla_strcmp(nla, type->name)) if (!nla_strcmp(nla, type->name) &&
(!type->family || type->family == family))
return type; return type;
} }
return NULL; return NULL;
} }
static const struct nft_expr_type *nft_expr_type_get(struct nlattr *nla) static const struct nft_expr_type *nft_expr_type_get(u8 family,
struct nlattr *nla)
{ {
const struct nft_expr_type *type; const struct nft_expr_type *type;
if (nla == NULL) if (nla == NULL)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
type = __nft_expr_type_get(nla); type = __nft_expr_type_get(family, nla);
if (type != NULL && try_module_get(type->owner)) if (type != NULL && try_module_get(type->owner))
return type; return type;
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
if (type == NULL) { if (type == NULL) {
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
request_module("nft-expr-%u-%.*s", family,
nla_len(nla), (char *)nla_data(nla));
nfnl_lock(NFNL_SUBSYS_NFTABLES);
if (__nft_expr_type_get(family, nla))
return ERR_PTR(-EAGAIN);
nfnl_unlock(NFNL_SUBSYS_NFTABLES); nfnl_unlock(NFNL_SUBSYS_NFTABLES);
request_module("nft-expr-%.*s", request_module("nft-expr-%.*s",
nla_len(nla), (char *)nla_data(nla)); nla_len(nla), (char *)nla_data(nla));
nfnl_lock(NFNL_SUBSYS_NFTABLES); nfnl_lock(NFNL_SUBSYS_NFTABLES);
if (__nft_expr_type_get(nla)) if (__nft_expr_type_get(family, nla))
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
} }
#endif #endif
...@@ -1193,7 +1203,7 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx, ...@@ -1193,7 +1203,7 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
if (err < 0) if (err < 0)
return err; return err;
type = nft_expr_type_get(tb[NFTA_EXPR_NAME]); type = nft_expr_type_get(ctx->afi->family, tb[NFTA_EXPR_NAME]);
if (IS_ERR(type)) if (IS_ERR(type))
return PTR_ERR(type); return PTR_ERR(type);
...@@ -1521,9 +1531,8 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1521,9 +1531,8 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
return err; return err;
} }
static void nf_tables_rcu_rule_destroy(struct rcu_head *head) static void nf_tables_rule_destroy(struct nft_rule *rule)
{ {
struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head);
struct nft_expr *expr; struct nft_expr *expr;
/* /*
...@@ -1538,11 +1547,6 @@ static void nf_tables_rcu_rule_destroy(struct rcu_head *head) ...@@ -1538,11 +1547,6 @@ static void nf_tables_rcu_rule_destroy(struct rcu_head *head)
kfree(rule); kfree(rule);
} }
static void nf_tables_rule_destroy(struct nft_rule *rule)
{
call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy);
}
#define NFT_RULE_MAXEXPRS 128 #define NFT_RULE_MAXEXPRS 128
static struct nft_expr_info *info; static struct nft_expr_info *info;
...@@ -1809,9 +1813,6 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -1809,9 +1813,6 @@ static int nf_tables_commit(struct sk_buff *skb)
synchronize_rcu(); synchronize_rcu();
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
/* Delete this rule from the dirty list */
list_del(&rupd->list);
/* This rule was inactive in the past and just became active. /* This rule was inactive in the past and just became active.
* Clear the next bit of the genmask since its meaning has * Clear the next bit of the genmask since its meaning has
* changed, now it is the future. * changed, now it is the future.
...@@ -1822,6 +1823,7 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -1822,6 +1823,7 @@ static int nf_tables_commit(struct sk_buff *skb)
rupd->chain, rupd->rule, rupd->chain, rupd->rule,
NFT_MSG_NEWRULE, 0, NFT_MSG_NEWRULE, 0,
rupd->family); rupd->family);
list_del(&rupd->list);
kfree(rupd); kfree(rupd);
continue; continue;
} }
...@@ -1831,7 +1833,15 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -1831,7 +1833,15 @@ static int nf_tables_commit(struct sk_buff *skb)
nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain, nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain,
rupd->rule, NFT_MSG_DELRULE, 0, rupd->rule, NFT_MSG_DELRULE, 0,
rupd->family); rupd->family);
}
/* Make sure we don't see any packet traversing old rules */
synchronize_rcu();
/* Now we can safely release unused old rules */
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
nf_tables_rule_destroy(rupd->rule); nf_tables_rule_destroy(rupd->rule);
list_del(&rupd->list);
kfree(rupd); kfree(rupd);
} }
...@@ -1844,20 +1854,26 @@ static int nf_tables_abort(struct sk_buff *skb) ...@@ -1844,20 +1854,26 @@ static int nf_tables_abort(struct sk_buff *skb)
struct nft_rule_trans *rupd, *tmp; struct nft_rule_trans *rupd, *tmp;
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
/* Delete all rules from the dirty list */
list_del(&rupd->list);
if (!nft_rule_is_active_next(net, rupd->rule)) { if (!nft_rule_is_active_next(net, rupd->rule)) {
nft_rule_clear(net, rupd->rule); nft_rule_clear(net, rupd->rule);
list_del(&rupd->list);
kfree(rupd); kfree(rupd);
continue; continue;
} }
/* This rule is inactive, get rid of it */ /* This rule is inactive, get rid of it */
list_del_rcu(&rupd->rule->list); list_del_rcu(&rupd->rule->list);
}
/* Make sure we don't see any packet accessing aborted rules */
synchronize_rcu();
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
nf_tables_rule_destroy(rupd->rule); nf_tables_rule_destroy(rupd->rule);
list_del(&rupd->list);
kfree(rupd); kfree(rupd);
} }
return 0; return 0;
} }
...@@ -1943,6 +1959,9 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, ...@@ -1943,6 +1959,9 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
} }
if (nla[NFTA_SET_TABLE] != NULL) { if (nla[NFTA_SET_TABLE] != NULL) {
if (afi == NULL)
return -EAFNOSUPPORT;
table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
...@@ -1989,13 +2008,13 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, ...@@ -1989,13 +2008,13 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
if (!sscanf(i->name, name, &tmp)) if (!sscanf(i->name, name, &tmp))
continue; continue;
if (tmp < 0 || tmp > BITS_PER_LONG * PAGE_SIZE) if (tmp < 0 || tmp >= BITS_PER_BYTE * PAGE_SIZE)
continue; continue;
set_bit(tmp, inuse); set_bit(tmp, inuse);
} }
n = find_first_zero_bit(inuse, BITS_PER_LONG * PAGE_SIZE); n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
free_page((unsigned long)inuse); free_page((unsigned long)inuse);
} }
...@@ -2428,6 +2447,8 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2428,6 +2447,8 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
struct nft_ctx ctx; struct nft_ctx ctx;
int err; int err;
if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
return -EAFNOSUPPORT;
if (nla[NFTA_SET_TABLE] == NULL) if (nla[NFTA_SET_TABLE] == NULL)
return -EINVAL; return -EINVAL;
...@@ -2435,9 +2456,6 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2435,9 +2456,6 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
if (err < 0) if (err < 0)
return err; return err;
if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
return -EAFNOSUPPORT;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
...@@ -2723,6 +2741,9 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2723,6 +2741,9 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_DATA] == NULL && if (nla[NFTA_SET_ELEM_DATA] == NULL &&
!(elem.flags & NFT_SET_ELEM_INTERVAL_END)) !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
return -EINVAL; return -EINVAL;
if (nla[NFTA_SET_ELEM_DATA] != NULL &&
elem.flags & NFT_SET_ELEM_INTERVAL_END)
return -EINVAL;
} else { } else {
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
return -EINVAL; return -EINVAL;
...@@ -2977,6 +2998,9 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx, ...@@ -2977,6 +2998,9 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
const struct nft_set_iter *iter, const struct nft_set_iter *iter,
const struct nft_set_elem *elem) const struct nft_set_elem *elem)
{ {
if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
return 0;
switch (elem->data.verdict) { switch (elem->data.verdict) {
case NFT_JUMP: case NFT_JUMP:
case NFT_GOTO: case NFT_GOTO:
......
...@@ -103,7 +103,7 @@ static struct nf_loginfo trace_loginfo = { ...@@ -103,7 +103,7 @@ static struct nf_loginfo trace_loginfo = {
}, },
}; };
static inline void nft_trace_packet(const struct nft_pktinfo *pkt, static void nft_trace_packet(const struct nft_pktinfo *pkt,
const struct nft_chain *chain, const struct nft_chain *chain,
int rulenum, enum nft_trace type) int rulenum, enum nft_trace type)
{ {
......
...@@ -226,6 +226,7 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr, ...@@ -226,6 +226,7 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
if (tb[NFTA_CT_DIRECTION] != NULL) if (tb[NFTA_CT_DIRECTION] != NULL)
return -EINVAL; return -EINVAL;
break; break;
case NFT_CT_L3PROTOCOL:
case NFT_CT_PROTOCOL: case NFT_CT_PROTOCOL:
case NFT_CT_SRC: case NFT_CT_SRC:
case NFT_CT_DST: case NFT_CT_DST:
...@@ -311,8 +312,19 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -311,8 +312,19 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
goto nla_put_failure; goto nla_put_failure;
switch (priv->key) {
case NFT_CT_PROTOCOL:
case NFT_CT_SRC:
case NFT_CT_DST:
case NFT_CT_PROTO_SRC:
case NFT_CT_PROTO_DST:
if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
goto nla_put_failure; goto nla_put_failure;
default:
break;
}
return 0; return 0;
nla_put_failure: nla_put_failure:
......
...@@ -23,7 +23,6 @@ static const char *nft_log_null_prefix = ""; ...@@ -23,7 +23,6 @@ static const char *nft_log_null_prefix = "";
struct nft_log { struct nft_log {
struct nf_loginfo loginfo; struct nf_loginfo loginfo;
char *prefix; char *prefix;
int family;
}; };
static void nft_log_eval(const struct nft_expr *expr, static void nft_log_eval(const struct nft_expr *expr,
...@@ -33,7 +32,7 @@ static void nft_log_eval(const struct nft_expr *expr, ...@@ -33,7 +32,7 @@ static void nft_log_eval(const struct nft_expr *expr,
const struct nft_log *priv = nft_expr_priv(expr); const struct nft_log *priv = nft_expr_priv(expr);
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
nf_log_packet(net, priv->family, pkt->ops->hooknum, pkt->skb, pkt->in, nf_log_packet(net, pkt->ops->pf, pkt->ops->hooknum, pkt->skb, pkt->in,
pkt->out, &priv->loginfo, "%s", priv->prefix); pkt->out, &priv->loginfo, "%s", priv->prefix);
} }
...@@ -52,8 +51,6 @@ static int nft_log_init(const struct nft_ctx *ctx, ...@@ -52,8 +51,6 @@ static int nft_log_init(const struct nft_ctx *ctx,
struct nf_loginfo *li = &priv->loginfo; struct nf_loginfo *li = &priv->loginfo;
const struct nlattr *nla; const struct nlattr *nla;
priv->family = ctx->afi->family;
nla = tb[NFTA_LOG_PREFIX]; nla = tb[NFTA_LOG_PREFIX];
if (nla != NULL) { if (nla != NULL) {
priv->prefix = kmalloc(nla_len(nla) + 1, GFP_KERNEL); priv->prefix = kmalloc(nla_len(nla) + 1, GFP_KERNEL);
......
...@@ -25,7 +25,6 @@ struct nft_queue { ...@@ -25,7 +25,6 @@ struct nft_queue {
u16 queuenum; u16 queuenum;
u16 queues_total; u16 queues_total;
u16 flags; u16 flags;
u8 family;
}; };
static void nft_queue_eval(const struct nft_expr *expr, static void nft_queue_eval(const struct nft_expr *expr,
...@@ -43,7 +42,7 @@ static void nft_queue_eval(const struct nft_expr *expr, ...@@ -43,7 +42,7 @@ static void nft_queue_eval(const struct nft_expr *expr,
queue = priv->queuenum + cpu % priv->queues_total; queue = priv->queuenum + cpu % priv->queues_total;
} else { } else {
queue = nfqueue_hash(pkt->skb, queue, queue = nfqueue_hash(pkt->skb, queue,
priv->queues_total, priv->family, priv->queues_total, pkt->ops->pf,
jhash_initval); jhash_initval);
} }
} }
...@@ -71,7 +70,6 @@ static int nft_queue_init(const struct nft_ctx *ctx, ...@@ -71,7 +70,6 @@ static int nft_queue_init(const struct nft_ctx *ctx,
return -EINVAL; return -EINVAL;
init_hashrandom(&jhash_initval); init_hashrandom(&jhash_initval);
priv->family = ctx->afi->family;
priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM])); priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM]));
if (tb[NFTA_QUEUE_TOTAL] != NULL) if (tb[NFTA_QUEUE_TOTAL] != NULL)
......
...@@ -69,8 +69,10 @@ static void nft_rbtree_elem_destroy(const struct nft_set *set, ...@@ -69,8 +69,10 @@ static void nft_rbtree_elem_destroy(const struct nft_set *set,
struct nft_rbtree_elem *rbe) struct nft_rbtree_elem *rbe)
{ {
nft_data_uninit(&rbe->key, NFT_DATA_VALUE); nft_data_uninit(&rbe->key, NFT_DATA_VALUE);
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP &&
!(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_uninit(rbe->data, set->dtype); nft_data_uninit(rbe->data, set->dtype);
kfree(rbe); kfree(rbe);
} }
...@@ -108,7 +110,8 @@ static int nft_rbtree_insert(const struct nft_set *set, ...@@ -108,7 +110,8 @@ static int nft_rbtree_insert(const struct nft_set *set,
int err; int err;
size = sizeof(*rbe); size = sizeof(*rbe);
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP &&
!(elem->flags & NFT_SET_ELEM_INTERVAL_END))
size += sizeof(rbe->data[0]); size += sizeof(rbe->data[0]);
rbe = kzalloc(size, GFP_KERNEL); rbe = kzalloc(size, GFP_KERNEL);
...@@ -117,7 +120,8 @@ static int nft_rbtree_insert(const struct nft_set *set, ...@@ -117,7 +120,8 @@ static int nft_rbtree_insert(const struct nft_set *set,
rbe->flags = elem->flags; rbe->flags = elem->flags;
nft_data_copy(&rbe->key, &elem->key); nft_data_copy(&rbe->key, &elem->key);
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP &&
!(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_copy(rbe->data, &elem->data); nft_data_copy(rbe->data, &elem->data);
err = __nft_rbtree_insert(set, rbe); err = __nft_rbtree_insert(set, rbe);
...@@ -153,7 +157,8 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) ...@@ -153,7 +157,8 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
parent = parent->rb_right; parent = parent->rb_right;
else { else {
elem->cookie = rbe; elem->cookie = rbe;
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP &&
!(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_copy(&elem->data, rbe->data); nft_data_copy(&elem->data, rbe->data);
elem->flags = rbe->flags; elem->flags = rbe->flags;
return 0; return 0;
...@@ -177,7 +182,8 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, ...@@ -177,7 +182,8 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
rbe = rb_entry(node, struct nft_rbtree_elem, node); rbe = rb_entry(node, struct nft_rbtree_elem, node);
nft_data_copy(&elem.key, &rbe->key); nft_data_copy(&elem.key, &rbe->key);
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP &&
!(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_copy(&elem.data, rbe->data); nft_data_copy(&elem.data, rbe->data);
elem.flags = rbe->flags; elem.flags = rbe->flags;
......
...@@ -16,56 +16,15 @@ ...@@ -16,56 +16,15 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/icmp.h> #include <net/netfilter/nft_reject.h>
#include <net/netfilter/ipv4/nf_reject.h>
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = {
#include <net/netfilter/ipv6/nf_reject.h>
#endif
struct nft_reject {
enum nft_reject_types type:8;
u8 icmp_code;
u8 family;
};
static void nft_reject_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
#endif
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
if (priv->family == NFPROTO_IPV4)
nf_send_unreach(pkt->skb, priv->icmp_code);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
else if (priv->family == NFPROTO_IPV6)
nf_send_unreach6(net, pkt->skb, priv->icmp_code,
pkt->ops->hooknum);
#endif
break;
case NFT_REJECT_TCP_RST:
if (priv->family == NFPROTO_IPV4)
nf_send_reset(pkt->skb, pkt->ops->hooknum);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
else if (priv->family == NFPROTO_IPV6)
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
#endif
break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
static const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = {
[NFTA_REJECT_TYPE] = { .type = NLA_U32 }, [NFTA_REJECT_TYPE] = { .type = NLA_U32 },
[NFTA_REJECT_ICMP_CODE] = { .type = NLA_U8 }, [NFTA_REJECT_ICMP_CODE] = { .type = NLA_U8 },
}; };
EXPORT_SYMBOL_GPL(nft_reject_policy);
static int nft_reject_init(const struct nft_ctx *ctx, int nft_reject_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[])
{ {
...@@ -74,7 +33,6 @@ static int nft_reject_init(const struct nft_ctx *ctx, ...@@ -74,7 +33,6 @@ static int nft_reject_init(const struct nft_ctx *ctx,
if (tb[NFTA_REJECT_TYPE] == NULL) if (tb[NFTA_REJECT_TYPE] == NULL)
return -EINVAL; return -EINVAL;
priv->family = ctx->afi->family;
priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE])); priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
switch (priv->type) { switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH: case NFT_REJECT_ICMP_UNREACH:
...@@ -89,8 +47,9 @@ static int nft_reject_init(const struct nft_ctx *ctx, ...@@ -89,8 +47,9 @@ static int nft_reject_init(const struct nft_ctx *ctx,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nft_reject_init);
static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr) int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr)
{ {
const struct nft_reject *priv = nft_expr_priv(expr); const struct nft_reject *priv = nft_expr_priv(expr);
...@@ -109,37 +68,7 @@ static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -109,37 +68,7 @@ static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr)
nla_put_failure: nla_put_failure:
return -1; return -1;
} }
EXPORT_SYMBOL_GPL(nft_reject_dump);
static struct nft_expr_type nft_reject_type;
static const struct nft_expr_ops nft_reject_ops = {
.type = &nft_reject_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_type __read_mostly = {
.name = "reject",
.ops = &nft_reject_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_module_init(void)
{
return nft_register_expr(&nft_reject_type);
}
static void __exit nft_reject_module_exit(void)
{
nft_unregister_expr(&nft_reject_type);
}
module_init(nft_reject_module_init);
module_exit(nft_reject_module_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("reject");
/*
* Copyright (c) 2014 Patrick McHardy <kaber@trash.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_reject.h>
static void nft_reject_inet_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
switch (pkt->ops->pf) {
case NFPROTO_IPV4:
nft_reject_ipv4_eval(expr, data, pkt);
case NFPROTO_IPV6:
nft_reject_ipv6_eval(expr, data, pkt);
}
}
static struct nft_expr_type nft_reject_inet_type;
static const struct nft_expr_ops nft_reject_inet_ops = {
.type = &nft_reject_inet_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_inet_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_inet_type __read_mostly = {
.family = NFPROTO_INET,
.name = "reject",
.ops = &nft_reject_inet_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_inet_module_init(void)
{
return nft_register_expr(&nft_reject_inet_type);
}
static void __exit nft_reject_inet_module_exit(void)
{
nft_unregister_expr(&nft_reject_inet_type);
}
module_init(nft_reject_inet_module_init);
module_exit(nft_reject_inet_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_AF_EXPR(1, "reject");
...@@ -228,12 +228,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, ...@@ -228,12 +228,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par,
goto err3; goto err3;
} }
__set_bit(IPS_TEMPLATE_BIT, &ct->status); nf_conntrack_tmpl_insert(par->net, ct);
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
/* Overload tuple linked list to put us in template list. */
hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
&par->net->ct.tmpl);
out: out:
info->ct = ct; info->ct = ct;
return 0; return 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