Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8313f5c authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller
Browse files

[INET6]: Generalise tcp_v6_hash_connect



Renaming it to inet6_hash_connect, making it possible to ditch
dccp_v6_hash_connect and share the same code with TCP instead.

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a7f5e7f1
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1573,7 +1573,7 @@ u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
}

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
{
	struct keydata *keyptr = get_keyptr();
	u32 hash[12];
@@ -1584,7 +1584,7 @@ u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dp

	return twothirdsMD4Transform(daddr, hash);
}
EXPORT_SYMBOL(secure_tcpv6_port_ephemeral);
EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
#endif

#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+2 −2
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ void generate_random_uuid(unsigned char uuid_out[16]);

extern __u32 secure_ip_id(__u32 daddr);
extern u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
extern u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, 
extern u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, 
				      __u16 dport);
extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
					__u16 sport, __u16 dport);
+3 −0
Original line number Diff line number Diff line
@@ -527,6 +527,9 @@ extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
extern int inet6_ioctl(struct socket *sock, unsigned int cmd, 
		       unsigned long arg);

extern int inet6_hash_connect(struct inet_timewait_death_row *death_row,
			      struct sock *sk);

/*
 * reassembly.c
 */
+1 −170
Original line number Diff line number Diff line
@@ -84,175 +84,6 @@ static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
						   dh->dccph_sport);
}

static int __dccp_v6_check_established(struct sock *sk, const __u16 lport,
				       struct inet_timewait_sock **twp)
{
	struct inet_sock *inet = inet_sk(sk);
	const struct ipv6_pinfo *np = inet6_sk(sk);
	const struct in6_addr *daddr = &np->rcv_saddr;
	const struct in6_addr *saddr = &np->daddr;
	const int dif = sk->sk_bound_dev_if;
	const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
	const unsigned int hash = inet6_ehashfn(daddr, inet->num,
						saddr, inet->dport);
	struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
	struct sock *sk2;
	const struct hlist_node *node;
	struct inet_timewait_sock *tw;

	prefetch(head->chain.first);
	write_lock(&head->lock);

	/* Check TIME-WAIT sockets first. */
	sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
		const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);

		tw = inet_twsk(sk2);

		if(*((__u32 *)&(tw->tw_dport))	== ports	 &&
		   sk2->sk_family		== PF_INET6	 &&
		   ipv6_addr_equal(&tw6->tw_v6_daddr, saddr)	 &&
		   ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
		   sk2->sk_bound_dev_if == sk->sk_bound_dev_if)
			goto not_unique;
	}
	tw = NULL;

	/* And established part... */
	sk_for_each(sk2, node, &head->chain) {
		if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
			goto not_unique;
	}

	BUG_TRAP(sk_unhashed(sk));
	__sk_add_node(sk, &head->chain);
	sk->sk_hash = hash;
	sock_prot_inc_use(sk->sk_prot);
	write_unlock(&head->lock);

	if (twp) {
		*twp = tw;
		NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
	} else if (tw) {
		/* Silly. Should hash-dance instead... */
		inet_twsk_deschedule(tw, &dccp_death_row);
		NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);

		inet_twsk_put(tw);
	}
	return 0;

not_unique:
	write_unlock(&head->lock);
	return -EADDRNOTAVAIL;
}

static inline u32 dccp_v6_port_offset(const struct sock *sk)
{
	const struct inet_sock *inet = inet_sk(sk);
	const struct ipv6_pinfo *np = inet6_sk(sk);

	return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
					   np->daddr.s6_addr32,
					   inet->dport);
}

static int dccp_v6_hash_connect(struct sock *sk)
{
	const unsigned short snum = inet_sk(sk)->num;
 	struct inet_bind_hashbucket *head;
 	struct inet_bind_bucket *tb;
	int ret;

 	if (snum == 0) {
 		int low = sysctl_local_port_range[0];
 		int high = sysctl_local_port_range[1];
		int range = high - low;
 		int i;
		int port;
		static u32 hint;
		u32 offset = hint + dccp_v6_port_offset(sk);
		struct hlist_node *node;
 		struct inet_timewait_sock *tw = NULL;

 		local_bh_disable();
		for (i = 1; i <= range; i++) {
			port = low + (i + offset) % range;
 			head = &dccp_hashinfo.bhash[inet_bhashfn(port,
						    dccp_hashinfo.bhash_size)];
 			spin_lock(&head->lock);

 			/* Does not bother with rcv_saddr checks,
 			 * because the established check is already
 			 * unique enough.
 			 */
			inet_bind_bucket_for_each(tb, node, &head->chain) {
 				if (tb->port == port) {
 					BUG_TRAP(!hlist_empty(&tb->owners));
 					if (tb->fastreuse >= 0)
 						goto next_port;
 					if (!__dccp_v6_check_established(sk,
									 port,
									 &tw))
 						goto ok;
 					goto next_port;
 				}
 			}

 			tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
						     head, port);
 			if (!tb) {
 				spin_unlock(&head->lock);
 				break;
 			}
 			tb->fastreuse = -1;
 			goto ok;

 		next_port:
 			spin_unlock(&head->lock);
 		}
 		local_bh_enable();

 		return -EADDRNOTAVAIL;
ok:
		hint += i;

 		/* Head lock still held and bh's disabled */
 		inet_bind_hash(sk, tb, port);
		if (sk_unhashed(sk)) {
 			inet_sk(sk)->sport = htons(port);
 			__inet6_hash(&dccp_hashinfo, sk);
 		}
 		spin_unlock(&head->lock);

 		if (tw) {
 			inet_twsk_deschedule(tw, &dccp_death_row);
 			inet_twsk_put(tw);
 		}

		ret = 0;
		goto out;
 	}

 	head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
						 dccp_hashinfo.bhash_size)];
 	tb   = inet_csk(sk)->icsk_bind_hash;
	spin_lock_bh(&head->lock);

	if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
		__inet6_hash(&dccp_hashinfo, sk);
		spin_unlock_bh(&head->lock);
		return 0;
	} else {
		spin_unlock(&head->lock);
		/* No definite answer... Walk to established hash table */
		ret = __dccp_v6_check_established(sk, snum, NULL);
out:
		local_bh_enable();
		return ret;
	}
}

static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 
			   int addr_len)
{
@@ -403,7 +234,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
	inet->dport = usin->sin6_port;

	dccp_set_state(sk, DCCP_REQUESTING);
	err = dccp_v6_hash_connect(sk);
	err = inet6_hash_connect(&dccp_death_row, sk);
	if (err)
		goto late_failure;
	/* FIXME */
+181 −2
Original line number Diff line number Diff line
@@ -5,7 +5,8 @@
 *
 *		Generic INET6 transport hashtables
 *
 * Authors:	Lotsa people, from code originally in tcp
 * Authors:	Lotsa people, from code originally in tcp, generalised here
 * 		by Arnaldo Carvalho de Melo <acme@mandriva.com>
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
@@ -14,12 +15,13 @@
 */

#include <linux/config.h>

#include <linux/module.h>
#include <linux/random.h>

#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
#include <net/ip.h>

struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
				   const struct in6_addr *daddr,
@@ -79,3 +81,180 @@ struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
}

EXPORT_SYMBOL_GPL(inet6_lookup);

static int __inet6_check_established(struct inet_timewait_death_row *death_row,
				     struct sock *sk, const __u16 lport,
				     struct inet_timewait_sock **twp)
{
	struct inet_hashinfo *hinfo = death_row->hashinfo;
	const struct inet_sock *inet = inet_sk(sk);
	const struct ipv6_pinfo *np = inet6_sk(sk);
	const struct in6_addr *daddr = &np->rcv_saddr;
	const struct in6_addr *saddr = &np->daddr;
	const int dif = sk->sk_bound_dev_if;
	const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
	const unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr,
						inet->dport);
	struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
	struct sock *sk2;
	const struct hlist_node *node;
	struct inet_timewait_sock *tw;

	prefetch(head->chain.first);
	write_lock(&head->lock);

	/* Check TIME-WAIT sockets first. */
	sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
		const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);

		tw = inet_twsk(sk2);

		if(*((__u32 *)&(tw->tw_dport)) == ports		 &&
		   sk2->sk_family	       == PF_INET6	 &&
		   ipv6_addr_equal(&tw6->tw_v6_daddr, saddr)	 &&
		   ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
		   sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
			if (twsk_unique(sk, sk2, twp))
				goto unique;
			else
				goto not_unique;
		}
	}
	tw = NULL;

	/* And established part... */
	sk_for_each(sk2, node, &head->chain) {
		if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
			goto not_unique;
	}

unique:
	BUG_TRAP(sk_unhashed(sk));
	__sk_add_node(sk, &head->chain);
	sk->sk_hash = hash;
	sock_prot_inc_use(sk->sk_prot);
	write_unlock(&head->lock);

	if (twp != NULL) {
		*twp = tw;
		NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
	} else if (tw != NULL) {
		/* Silly. Should hash-dance instead... */
		inet_twsk_deschedule(tw, death_row);
		NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);

		inet_twsk_put(tw);
	}
	return 0;

not_unique:
	write_unlock(&head->lock);
	return -EADDRNOTAVAIL;
}

static inline u32 inet6_sk_port_offset(const struct sock *sk)
{
	const struct inet_sock *inet = inet_sk(sk);
	const struct ipv6_pinfo *np = inet6_sk(sk);
	return secure_ipv6_port_ephemeral(np->rcv_saddr.s6_addr32,
					  np->daddr.s6_addr32,
					  inet->dport);
}

int inet6_hash_connect(struct inet_timewait_death_row *death_row,
		       struct sock *sk)
{
	struct inet_hashinfo *hinfo = death_row->hashinfo;
	const unsigned short snum = inet_sk(sk)->num;
 	struct inet_bind_hashbucket *head;
 	struct inet_bind_bucket *tb;
	int ret;

 	if (snum == 0) {
 		const int low = sysctl_local_port_range[0];
 		const int high = sysctl_local_port_range[1];
		const int range = high - low;
 		int i, port;
		static u32 hint;
		const u32 offset = hint + inet6_sk_port_offset(sk);
		struct hlist_node *node;
 		struct inet_timewait_sock *tw = NULL;

 		local_bh_disable();
		for (i = 1; i <= range; i++) {
			port = low + (i + offset) % range;
 			head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
 			spin_lock(&head->lock);

 			/* Does not bother with rcv_saddr checks,
 			 * because the established check is already
 			 * unique enough.
 			 */
			inet_bind_bucket_for_each(tb, node, &head->chain) {
 				if (tb->port == port) {
 					BUG_TRAP(!hlist_empty(&tb->owners));
 					if (tb->fastreuse >= 0)
 						goto next_port;
 					if (!__inet6_check_established(death_row,
								       sk, port,
								       &tw))
 						goto ok;
 					goto next_port;
 				}
 			}

 			tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
						     head, port);
 			if (!tb) {
 				spin_unlock(&head->lock);
 				break;
 			}
 			tb->fastreuse = -1;
 			goto ok;

 		next_port:
 			spin_unlock(&head->lock);
 		}
 		local_bh_enable();

 		return -EADDRNOTAVAIL;

ok:
		hint += i;

 		/* Head lock still held and bh's disabled */
 		inet_bind_hash(sk, tb, port);
		if (sk_unhashed(sk)) {
 			inet_sk(sk)->sport = htons(port);
 			__inet6_hash(hinfo, sk);
 		}
 		spin_unlock(&head->lock);

 		if (tw) {
 			inet_twsk_deschedule(tw, death_row);
 			inet_twsk_put(tw);
 		}

		ret = 0;
		goto out;
 	}

 	head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
 	tb   = inet_csk(sk)->icsk_bind_hash;
	spin_lock_bh(&head->lock);

	if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
		__inet6_hash(hinfo, sk);
		spin_unlock_bh(&head->lock);
		return 0;
	} else {
		spin_unlock(&head->lock);
		/* No definite answer... Walk to established hash table */
		ret = __inet6_check_established(death_row, sk, snum, NULL);
out:
		local_bh_enable();
		return ret;
	}
}

EXPORT_SYMBOL_GPL(inet6_hash_connect);
Loading