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

Commit 25030a7f authored by Gerrit Renker's avatar Gerrit Renker Committed by David S. Miller
Browse files

[UDP]: Unify UDPv4 and UDPv6 ->get_port()



This patch creates one common function which is called by
udp_v4_get_port() and udp_v6_get_port(). As a result,
  * duplicated code is removed
  * udp_port_rover and local port lookup can now be removed from udp.h
  * further savings follow since the same function will be used by UDP-Litev4
    and UDP-Litev6

In contrast to the patch sent in response to Yoshifujis comments
(fixed by this variant), the code below also removes the
EXPORT_SYMBOL(udp_port_rover), since udp_port_rover can now remain
local to net/ipv4/udp.c.

Signed-off-by: default avatarGerrit Renker <gerrit@erg.abdn.ac.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6a28ec8c
Loading
Loading
Loading
Loading
+2 −16
Original line number Diff line number Diff line
@@ -30,25 +30,9 @@

#define UDP_HTABLE_SIZE		128

/* udp.c: This needs to be shared by v4 and v6 because the lookup
 *        and hashing code needs to work with different AF's yet
 *        the port space is shared.
 */
extern struct hlist_head udp_hash[UDP_HTABLE_SIZE];
extern rwlock_t udp_hash_lock;

extern int udp_port_rover;

static inline int udp_lport_inuse(u16 num)
{
	struct sock *sk;
	struct hlist_node *node;

	sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
		if (inet_sk(sk)->num == num)
			return 1;
	return 0;
}

/* Note: this must match 'valbool' in sock_setsockopt */
#define UDP_CSUM_NOXMIT		1
@@ -63,6 +47,8 @@ extern struct proto udp_prot;

struct sk_buff;

extern int	udp_get_port(struct sock *sk, unsigned short snum,
			     int (*saddr_cmp)(struct sock *, struct sock *));
extern void	udp_err(struct sk_buff *, u32);

extern int	udp_sendmsg(struct kiocb *iocb, struct sock *sk,
+60 −36
Original line number Diff line number Diff line
@@ -118,14 +118,34 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly;
struct hlist_head udp_hash[UDP_HTABLE_SIZE];
DEFINE_RWLOCK(udp_hash_lock);

/* Shared by v4/v6 udp. */
/* Shared by v4/v6 udp_get_port */
int udp_port_rover;

static int udp_v4_get_port(struct sock *sk, unsigned short snum)
static inline int udp_lport_inuse(u16 num)
{
	struct sock *sk;
	struct hlist_node *node;

	sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
		if (inet_sk(sk)->num == num)
			return 1;
	return 0;
}

/**
 *  udp_get_port  -  common port lookup for IPv4 and IPv6
 *
 *  @sk:          socket struct in question
 *  @snum:        port number to look up
 *  @saddr_comp:  AF-dependent comparison of bound local IP addresses
 */
int udp_get_port(struct sock *sk, unsigned short snum,
		 int (*saddr_cmp)(struct sock *sk1, struct sock *sk2))
{
	struct hlist_node *node;
	struct hlist_head *head;
	struct sock *sk2;
	struct inet_sock *inet = inet_sk(sk);
	int    error = 1;

	write_lock_bh(&udp_hash_lock);
	if (snum == 0) {
@@ -137,11 +157,10 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
		best_size_so_far = 32767;
		best = result = udp_port_rover;
		for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
			struct hlist_head *list;
			int size;

			list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
			if (hlist_empty(list)) {
			head = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
			if (hlist_empty(head)) {
				if (result > sysctl_local_port_range[1])
					result = sysctl_local_port_range[0] +
						((result - sysctl_local_port_range[0]) &
@@ -149,12 +168,11 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
				goto gotit;
			}
			size = 0;
			sk_for_each(sk2, node, list)
				if (++size >= best_size_so_far)
					goto next;
			sk_for_each(sk2, node, head)
				if (++size < best_size_so_far) {
					best_size_so_far = size;
					best = result;
		next:;
				}
		}
		result = best;
		for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
@@ -170,38 +188,44 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
gotit:
		udp_port_rover = snum = result;
	} else {
		sk_for_each(sk2, node,
			    &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) {
			struct inet_sock *inet2 = inet_sk(sk2);
		head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];

			if (inet2->num == snum &&
		sk_for_each(sk2, node, head)
			if (inet_sk(sk2)->num == snum                        &&
			    sk2 != sk                                        &&
			    !ipv6_only_sock(sk2) &&
			    (!sk2->sk_bound_dev_if ||
			     !sk->sk_bound_dev_if ||
			     sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
			    (!inet2->rcv_saddr ||
			     !inet->rcv_saddr ||
			     inet2->rcv_saddr == inet->rcv_saddr) &&
			    (!sk2->sk_reuse || !sk->sk_reuse))
			    (!sk2->sk_reuse        || !sk->sk_reuse)         &&
			    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
			     || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
			    (*saddr_cmp)(sk, sk2)                              )
				goto fail;
	}
	}
	inet->num = snum;
	inet_sk(sk)->num = snum;
	if (sk_unhashed(sk)) {
		struct hlist_head *h = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];

		sk_add_node(sk, h);
		head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
		sk_add_node(sk, head);
		sock_prot_inc_use(sk->sk_prot);
	}
	write_unlock_bh(&udp_hash_lock);
	return 0;

	error = 0;
fail:
	write_unlock_bh(&udp_hash_lock);
	return 1;
	return error;
}

static inline int  ipv4_rcv_saddr_equal(struct sock *sk1, struct sock *sk2)
{
	struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);

	return 	( !ipv6_only_sock(sk2)  &&
		  (!inet1->rcv_saddr || !inet2->rcv_saddr ||
		   inet1->rcv_saddr == inet2->rcv_saddr      ));
}

static inline int udp_v4_get_port(struct sock *sk, unsigned short snum)
{
	return udp_get_port(sk, snum, ipv4_rcv_saddr_equal);
}


static void udp_v4_hash(struct sock *sk)
{
	BUG();
@@ -1596,7 +1620,7 @@ EXPORT_SYMBOL(udp_disconnect);
EXPORT_SYMBOL(udp_hash);
EXPORT_SYMBOL(udp_hash_lock);
EXPORT_SYMBOL(udp_ioctl);
EXPORT_SYMBOL(udp_port_rover);
EXPORT_SYMBOL(udp_get_port);
EXPORT_SYMBOL(udp_prot);
EXPORT_SYMBOL(udp_sendmsg);
EXPORT_SYMBOL(udp_poll);
+2 −74
Original line number Diff line number Diff line
@@ -61,81 +61,9 @@

DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly;

/* Grrr, addr_type already calculated by caller, but I don't want
 * to add some silly "cookie" argument to this method just for that.
 */
static int udp_v6_get_port(struct sock *sk, unsigned short snum)
static inline int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
	struct sock *sk2;
	struct hlist_node *node;

	write_lock_bh(&udp_hash_lock);
	if (snum == 0) {
		int best_size_so_far, best, result, i;

		if (udp_port_rover > sysctl_local_port_range[1] ||
		    udp_port_rover < sysctl_local_port_range[0])
			udp_port_rover = sysctl_local_port_range[0];
		best_size_so_far = 32767;
		best = result = udp_port_rover;
		for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
			int size;
			struct hlist_head *list;

			list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
			if (hlist_empty(list)) {
				if (result > sysctl_local_port_range[1])
					result = sysctl_local_port_range[0] +
						((result - sysctl_local_port_range[0]) &
						 (UDP_HTABLE_SIZE - 1));
				goto gotit;
			}
			size = 0;
			sk_for_each(sk2, node, list)
				if (++size >= best_size_so_far)
					goto next;
			best_size_so_far = size;
			best = result;
		next:;
		}
		result = best;
		for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
			if (result > sysctl_local_port_range[1])
				result = sysctl_local_port_range[0]
					+ ((result - sysctl_local_port_range[0]) &
					   (UDP_HTABLE_SIZE - 1));
			if (!udp_lport_inuse(result))
				break;
		}
		if (i >= (1 << 16) / UDP_HTABLE_SIZE)
			goto fail;
gotit:
		udp_port_rover = snum = result;
	} else {
		sk_for_each(sk2, node,
			    &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) {
			if (inet_sk(sk2)->num == snum &&
			    sk2 != sk &&
			    (!sk2->sk_bound_dev_if ||
			     !sk->sk_bound_dev_if ||
			     sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
			    (!sk2->sk_reuse || !sk->sk_reuse) &&
			    ipv6_rcv_saddr_equal(sk, sk2))
				goto fail;
		}
	}

	inet_sk(sk)->num = snum;
	if (sk_unhashed(sk)) {
		sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]);
		sock_prot_inc_use(sk->sk_prot);
	}
	write_unlock_bh(&udp_hash_lock);
	return 0;

fail:
	write_unlock_bh(&udp_hash_lock);
	return 1;
	return udp_get_port(sk, snum, ipv6_rcv_saddr_equal);
}

static void udp_v6_hash(struct sock *sk)