diff options
Diffstat (limited to 'net/netfilter')
| -rw-r--r-- | net/netfilter/nf_conntrack_core.c | 46 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_expect.c | 4 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_extend.c | 2 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_proto_tcp.c | 6 | ||||
| -rw-r--r-- | net/netfilter/nf_log.c | 16 | ||||
| -rw-r--r-- | net/netfilter/xt_NFQUEUE.c | 8 | ||||
| -rw-r--r-- | net/netfilter/xt_RATEEST.c | 2 | ||||
| -rw-r--r-- | net/netfilter/xt_cluster.c | 8 | ||||
| -rw-r--r-- | net/netfilter/xt_conntrack.c | 66 | ||||
| -rw-r--r-- | net/netfilter/xt_osf.c | 5 | ||||
| -rw-r--r-- | net/netfilter/xt_quota.c | 3 | ||||
| -rw-r--r-- | net/netfilter/xt_rateest.c | 2 | 
12 files changed, 131 insertions, 37 deletions
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5f72b94b4918..b5869b9574b0 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -335,7 +335,8 @@ begin:  	h = __nf_conntrack_find(net, tuple);  	if (h) {  		ct = nf_ct_tuplehash_to_ctrack(h); -		if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) +		if (unlikely(nf_ct_is_dying(ct) || +			     !atomic_inc_not_zero(&ct->ct_general.use)))  			h = NULL;  		else {  			if (unlikely(!nf_ct_tuple_equal(tuple, &h->tuple))) { @@ -425,7 +426,6 @@ __nf_conntrack_confirm(struct sk_buff *skb)  	/* Remove from unconfirmed list */  	hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); -	__nf_conntrack_hash_insert(ct, hash, repl_hash);  	/* Timer relative to confirmation time, not original  	   setting time, otherwise we'd get timer wrap in  	   weird delay cases. */ @@ -433,8 +433,16 @@ __nf_conntrack_confirm(struct sk_buff *skb)  	add_timer(&ct->timeout);  	atomic_inc(&ct->ct_general.use);  	set_bit(IPS_CONFIRMED_BIT, &ct->status); + +	/* Since the lookup is lockless, hash insertion must be done after +	 * starting the timer and setting the CONFIRMED bit. The RCU barriers +	 * guarantee that no other CPU can find the conntrack before the above +	 * stores are visible. +	 */ +	__nf_conntrack_hash_insert(ct, hash, repl_hash);  	NF_CT_STAT_INC(net, insert);  	spin_unlock_bh(&nf_conntrack_lock); +  	help = nfct_help(ct);  	if (help && help->helper)  		nf_conntrack_event_cache(IPCT_HELPER, ct); @@ -503,7 +511,8 @@ static noinline int early_drop(struct net *net, unsigned int hash)  			cnt++;  		} -		if (ct && unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) +		if (ct && unlikely(nf_ct_is_dying(ct) || +				   !atomic_inc_not_zero(&ct->ct_general.use)))  			ct = NULL;  		if (ct || cnt >= NF_CT_EVICTION_RANGE)  			break; @@ -552,23 +561,38 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,  		}  	} -	ct = kmem_cache_zalloc(nf_conntrack_cachep, gfp); +	/* +	 * Do not use kmem_cache_zalloc(), as this cache uses +	 * SLAB_DESTROY_BY_RCU. +	 */ +	ct = kmem_cache_alloc(nf_conntrack_cachep, gfp);  	if (ct == NULL) {  		pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");  		atomic_dec(&net->ct.count);  		return ERR_PTR(-ENOMEM);  	} - +	/* +	 * Let ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.next +	 * and ct->tuplehash[IP_CT_DIR_REPLY].hnnode.next unchanged. +	 */ +	memset(&ct->tuplehash[IP_CT_DIR_MAX], 0, +	       sizeof(*ct) - offsetof(struct nf_conn, tuplehash[IP_CT_DIR_MAX]));  	spin_lock_init(&ct->lock); -	atomic_set(&ct->ct_general.use, 1);  	ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; +	ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;  	ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; +	ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev = NULL;  	/* Don't set timer yet: wait for confirmation */  	setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);  #ifdef CONFIG_NET_NS  	ct->ct_net = net;  #endif +	/* +	 * changes to lookup keys must be done before setting refcnt to 1 +	 */ +	smp_wmb(); +	atomic_set(&ct->ct_general.use, 1);  	return ct;  }  EXPORT_SYMBOL_GPL(nf_conntrack_alloc); @@ -1267,13 +1291,19 @@ err_cache:  	return ret;  } +/* + * We need to use special "null" values, not used in hash table + */ +#define UNCONFIRMED_NULLS_VAL	((1<<30)+0) +#define DYING_NULLS_VAL		((1<<30)+1) +  static int nf_conntrack_init_net(struct net *net)  {  	int ret;  	atomic_set(&net->ct.count, 0); -	INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, 0); -	INIT_HLIST_NULLS_HEAD(&net->ct.dying, 0); +	INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL); +	INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL);  	net->ct.stat = alloc_percpu(struct ip_conntrack_stat);  	if (!net->ct.stat) {  		ret = -ENOMEM; diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index afde8f991646..2032dfe25ca8 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -617,8 +617,10 @@ err1:  void nf_conntrack_expect_fini(struct net *net)  {  	exp_proc_remove(net); -	if (net_eq(net, &init_net)) +	if (net_eq(net, &init_net)) { +		rcu_barrier(); /* Wait for call_rcu() before destroy */  		kmem_cache_destroy(nf_ct_expect_cachep); +	}  	nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,  			     nf_ct_expect_hsize);  } diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 4b2c769d555f..fef95be334bd 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -186,6 +186,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type)  	rcu_assign_pointer(nf_ct_ext_types[type->id], NULL);  	update_alloc_size(type);  	mutex_unlock(&nf_ct_ext_type_mutex); -	synchronize_rcu(); +	rcu_barrier(); /* Wait for completion of call_rcu()'s */  }  EXPORT_SYMBOL_GPL(nf_ct_extend_unregister); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 33fc0a443f3d..97a82ba75376 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -720,8 +720,8 @@ static bool tcp_in_window(const struct nf_conn *ct,  /* Caller must linearize skb at tcp header. */  void nf_conntrack_tcp_update(const struct sk_buff *skb,  			     unsigned int dataoff, -			     struct nf_conn *ct, -			     int dir) +			     struct nf_conn *ct, int dir, +			     s16 offset)  {  	const struct tcphdr *tcph = (const void *)skb->data + dataoff;  	const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[dir]; @@ -734,7 +734,7 @@ void nf_conntrack_tcp_update(const struct sk_buff *skb,  	/*  	 * We have to worry for the ack in the reply packet only...  	 */ -	if (after(end, ct->proto.tcp.seen[dir].td_end)) +	if (ct->proto.tcp.seen[dir].td_end + offset == end)  		ct->proto.tcp.seen[dir].td_end = end;  	ct->proto.tcp.last_end = end;  	spin_unlock_bh(&ct->lock); diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 2fefe147750a..4e620305f28c 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -47,7 +47,6 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger)  	mutex_lock(&nf_log_mutex);  	if (pf == NFPROTO_UNSPEC) { -		int i;  		for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)  			list_add_tail(&(logger->list[i]), &(nf_loggers_l[i]));  	} else { @@ -216,7 +215,7 @@ static const struct file_operations nflog_file_ops = {  #endif /* PROC_FS */  #ifdef CONFIG_SYSCTL -struct ctl_path nf_log_sysctl_path[] = { +static struct ctl_path nf_log_sysctl_path[] = {  	{ .procname = "net", .ctl_name = CTL_NET, },  	{ .procname = "netfilter", .ctl_name = NET_NETFILTER, },  	{ .procname = "nf_log", .ctl_name = CTL_UNNUMBERED, }, @@ -228,19 +227,26 @@ static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];  static struct ctl_table_header *nf_log_dir_header;  static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp, -			 void *buffer, size_t *lenp, loff_t *ppos) +			 void __user *buffer, size_t *lenp, loff_t *ppos)  {  	const struct nf_logger *logger; +	char buf[NFLOGGER_NAME_LEN]; +	size_t size = *lenp;  	int r = 0;  	int tindex = (unsigned long)table->extra1;  	if (write) { -		if (!strcmp(buffer, "NONE")) { +		if (size > sizeof(buf)) +			size = sizeof(buf); +		if (copy_from_user(buf, buffer, size)) +			return -EFAULT; + +		if (!strcmp(buf, "NONE")) {  			nf_log_unbind_pf(tindex);  			return 0;  		}  		mutex_lock(&nf_log_mutex); -		logger = __find_logger(tindex, buffer); +		logger = __find_logger(tindex, buf);  		if (logger == NULL) {  			mutex_unlock(&nf_log_mutex);  			return -ENOENT; diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 498b45101df7..f28f6a5fc02d 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -40,12 +40,12 @@ nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par)  static u32 hash_v4(const struct sk_buff *skb)  {  	const struct iphdr *iph = ip_hdr(skb); -	u32 ipaddr; +	__be32 ipaddr;  	/* packets in either direction go into same queue */  	ipaddr = iph->saddr ^ iph->daddr; -	return jhash_2words(ipaddr, iph->protocol, jhash_initval); +	return jhash_2words((__force u32)ipaddr, iph->protocol, jhash_initval);  }  static unsigned int @@ -63,14 +63,14 @@ nfqueue_tg4_v1(struct sk_buff *skb, const struct xt_target_param *par)  static u32 hash_v6(const struct sk_buff *skb)  {  	const struct ipv6hdr *ip6h = ipv6_hdr(skb); -	u32 addr[4]; +	__be32 addr[4];  	addr[0] = ip6h->saddr.s6_addr32[0] ^ ip6h->daddr.s6_addr32[0];  	addr[1] = ip6h->saddr.s6_addr32[1] ^ ip6h->daddr.s6_addr32[1];  	addr[2] = ip6h->saddr.s6_addr32[2] ^ ip6h->daddr.s6_addr32[2];  	addr[3] = ip6h->saddr.s6_addr32[3] ^ ip6h->daddr.s6_addr32[3]; -	return jhash2(addr, ARRAY_SIZE(addr), jhash_initval); +	return jhash2((__force u32 *)addr, ARRAY_SIZE(addr), jhash_initval);  }  static unsigned int diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index 43f5676b1af4..d80b8192e0d4 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -74,7 +74,7 @@ static unsigned int  xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par)  {  	const struct xt_rateest_target_info *info = par->targinfo; -	struct gnet_stats_basic *stats = &info->est->bstats; +	struct gnet_stats_basic_packed *stats = &info->est->bstats;  	spin_lock_bh(&info->est->lock);  	stats->bytes += skb->len; diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 69a639f35403..225ee3ecd69d 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -15,14 +15,14 @@  #include <net/netfilter/nf_conntrack.h>  #include <linux/netfilter/xt_cluster.h> -static inline u_int32_t nf_ct_orig_ipv4_src(const struct nf_conn *ct) +static inline u32 nf_ct_orig_ipv4_src(const struct nf_conn *ct)  { -	return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; +	return (__force u32)ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;  } -static inline const void *nf_ct_orig_ipv6_src(const struct nf_conn *ct) +static inline const u32 *nf_ct_orig_ipv6_src(const struct nf_conn *ct)  { -	return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6; +	return (__force u32 *)ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6;  }  static inline u_int32_t diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 0b7139f3dd78..fc581800698e 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -129,7 +129,7 @@ conntrack_addrcmp(const union nf_inet_addr *kaddr,  static inline bool  conntrack_mt_origsrc(const struct nf_conn *ct, -                     const struct xt_conntrack_mtinfo1 *info, +                     const struct xt_conntrack_mtinfo2 *info,  		     u_int8_t family)  {  	return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, @@ -138,7 +138,7 @@ conntrack_mt_origsrc(const struct nf_conn *ct,  static inline bool  conntrack_mt_origdst(const struct nf_conn *ct, -                     const struct xt_conntrack_mtinfo1 *info, +                     const struct xt_conntrack_mtinfo2 *info,  		     u_int8_t family)  {  	return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3, @@ -147,7 +147,7 @@ conntrack_mt_origdst(const struct nf_conn *ct,  static inline bool  conntrack_mt_replsrc(const struct nf_conn *ct, -                     const struct xt_conntrack_mtinfo1 *info, +                     const struct xt_conntrack_mtinfo2 *info,  		     u_int8_t family)  {  	return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3, @@ -156,7 +156,7 @@ conntrack_mt_replsrc(const struct nf_conn *ct,  static inline bool  conntrack_mt_repldst(const struct nf_conn *ct, -                     const struct xt_conntrack_mtinfo1 *info, +                     const struct xt_conntrack_mtinfo2 *info,  		     u_int8_t family)  {  	return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3, @@ -164,7 +164,7 @@ conntrack_mt_repldst(const struct nf_conn *ct,  }  static inline bool -ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info, +ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info,                      const struct nf_conn *ct)  {  	const struct nf_conntrack_tuple *tuple; @@ -204,7 +204,7 @@ ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info,  static bool  conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par)  { -	const struct xt_conntrack_mtinfo1 *info = par->matchinfo; +	const struct xt_conntrack_mtinfo2 *info = par->matchinfo;  	enum ip_conntrack_info ctinfo;  	const struct nf_conn *ct;  	unsigned int statebit; @@ -278,6 +278,16 @@ conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par)  	return true;  } +static bool +conntrack_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) +{ +	const struct xt_conntrack_mtinfo2 *const *info = par->matchinfo; +	struct xt_match_param newpar = *par; + +	newpar.matchinfo = *info; +	return conntrack_mt(skb, &newpar); +} +  static bool conntrack_mt_check(const struct xt_mtchk_param *par)  {  	if (nf_ct_l3proto_try_module_get(par->family) < 0) { @@ -288,11 +298,45 @@ static bool conntrack_mt_check(const struct xt_mtchk_param *par)  	return true;  } +static bool conntrack_mt_check_v1(const struct xt_mtchk_param *par) +{ +	struct xt_conntrack_mtinfo1 *info = par->matchinfo; +	struct xt_conntrack_mtinfo2 *up; +	int ret = conntrack_mt_check(par); + +	if (ret < 0) +		return ret; + +	up = kmalloc(sizeof(*up), GFP_KERNEL); +	if (up == NULL) { +		nf_ct_l3proto_module_put(par->family); +		return -ENOMEM; +	} + +	/* +	 * The strategy here is to minimize the overhead of v1 matching, +	 * by prebuilding a v2 struct and putting the pointer into the +	 * v1 dataspace. +	 */ +	memcpy(up, info, offsetof(typeof(*info), state_mask)); +	up->state_mask  = info->state_mask; +	up->status_mask = info->status_mask; +	*(void **)info  = up; +	return true; +} +  static void conntrack_mt_destroy(const struct xt_mtdtor_param *par)  {  	nf_ct_l3proto_module_put(par->family);  } +static void conntrack_mt_destroy_v1(const struct xt_mtdtor_param *par) +{ +	struct xt_conntrack_mtinfo2 **info = par->matchinfo; +	kfree(*info); +	conntrack_mt_destroy(par); +} +  #ifdef CONFIG_COMPAT  struct compat_xt_conntrack_info  { @@ -363,6 +407,16 @@ static struct xt_match conntrack_mt_reg[] __read_mostly = {  		.revision   = 1,  		.family     = NFPROTO_UNSPEC,  		.matchsize  = sizeof(struct xt_conntrack_mtinfo1), +		.match      = conntrack_mt_v1, +		.checkentry = conntrack_mt_check_v1, +		.destroy    = conntrack_mt_destroy_v1, +		.me         = THIS_MODULE, +	}, +	{ +		.name       = "conntrack", +		.revision   = 2, +		.family     = NFPROTO_UNSPEC, +		.matchsize  = sizeof(struct xt_conntrack_mtinfo2),  		.match      = conntrack_mt,  		.checkentry = conntrack_mt_check,  		.destroy    = conntrack_mt_destroy, diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 863e40977a4d..0f482e2440b4 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -330,7 +330,8 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,  			fcount++;  			if (info->flags & XT_OSF_LOG) -				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL, +				nf_log_packet(p->family, p->hooknum, skb, +					p->in, p->out, NULL,  					"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",  					f->genre, f->version, f->subtype,  					&ip->saddr, ntohs(tcp->source), @@ -345,7 +346,7 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,  	rcu_read_unlock();  	if (!fcount && (info->flags & XT_OSF_LOG)) -		nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL, +		nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,  			"Remote OS is not known: %pi4:%u -> %pi4:%u\n",  				&ip->saddr, ntohs(tcp->source),  				&ip->daddr, ntohs(tcp->dest)); diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index 01dd07b764ec..390b7d09fe51 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -52,8 +52,9 @@ static bool quota_mt_check(const struct xt_mtchk_param *par)  	q->master = kmalloc(sizeof(*q->master), GFP_KERNEL);  	if (q->master == NULL) -		return -ENOMEM; +		return false; +	q->master->quota = q->quota;  	return true;  } diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 220a1d588ee0..4fc6a917f6de 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -66,7 +66,7 @@ xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par)  		if (info->flags & XT_RATEEST_MATCH_BPS)  			ret &= bps1 == bps2;  		if (info->flags & XT_RATEEST_MATCH_PPS) -			ret &= pps2 == pps2; +			ret &= pps1 == pps2;  		break;  	}  | 
