UDPv4 and UDPv6 use an almost identical version of the get_port function, which is unnecessary since the (long) code differs in only one if-statement.
By disentangling the if statement and adding v4/v6 checks appropriately, this code duplication can be removed and further * udp_port_rover can stay in net/ipv4/udp.c * udp_lport_inuse can become static in net/ipv4/udp.c (only called by udp_get_port The text below discusses the re-arrangement of the if-statement. This is implemented by enclosed patch (works both on stable and Torvalds' release). The patch also dispenses with a goto statement whose jump label is referenced only once. D i s c u s s i o n The following compares the statements for udp_v{4,6}_get_port. A) In udp_v4_get_port(): ========================= if (inet2->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) ) goto fail; This function is called from IPv4 context, hence sk->sk_family == PF_INET. B) In udp_v6_get_port(): ========================= 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; This function is called from IPv6 context, hence sk->sk_family == PF_INET6. Common denominator: =================== By re-ordering some of the last literals, both functions share the following conjunction of conditions: 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) ) To make the function applicable to both v4 and v6 contexts, a second if statement is added, which branches according to sk's sk_family. Signed-off-by: Gerrit Renker <[EMAIL PROTECTED]> --- include/net/udp.h | 17 +---------- net/ipv4/udp.c | 57 ++++++++++++++++++++++++-------------- net/ipv6/udp.c | 79 +---------------------------------------------------- 3 files changed, 38 insertions(+), 115 deletions(-) diff --git a/include/net/udp.h b/include/net/udp.h index 766fba1..69d4288 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -30,25 +30,9 @@ #include <linux/seq_file.h> #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,7 @@ extern struct proto udp_prot; struct sk_buff; +extern int udp_get_port(struct sock *sk, unsigned short snum); extern void udp_err(struct sk_buff *, u32); extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 3f93292..eb3aa82 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -102,6 +102,7 @@ #include <net/protocol.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <net/addrconf.h> #include <net/sock.h> #include <net/udp.h> #include <net/icmp.h> @@ -119,10 +120,20 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_sta struct hlist_head udp_hash[UDP_HTABLE_SIZE]; DEFINE_RWLOCK(udp_hash_lock); -/* Shared by v4/v6 udp. */ 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; +} + +int udp_get_port(struct sock *sk, unsigned short snum) { struct hlist_node *node; struct sock *sk2; @@ -151,11 +162,10 @@ static int udp_v4_get_port(struct sock * } size = 0; sk_for_each(sk2, node, list) - if (++size >= best_size_so_far) - goto next; - best_size_so_far = size; - best = result; - next:; + if (++size < best_size_so_far) { + best_size_so_far = size; + best = result; + } } result = best; for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { @@ -175,24 +185,29 @@ gotit: &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) { struct inet_sock *inet2 = inet_sk(sk2); - if (inet2->num == snum && - sk2 != sk && - !ipv6_only_sock(sk2) && + if (inet2->num == snum && + sk2 != sk && + (!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) && - (!inet2->rcv_saddr || - !inet->rcv_saddr || - inet2->rcv_saddr == inet->rcv_saddr) && - (!sk2->sk_reuse || !sk->sk_reuse)) - goto fail; + !sk->sk_bound_dev_if || + sk2->sk_bound_dev_if == sk->sk_bound_dev_if) ) { + if (sk->sk_family == PF_INET && + !ipv6_only_sock(sk2) && + (!inet2->rcv_saddr || + !inet->rcv_saddr || + inet2->rcv_saddr == inet->rcv_saddr) ) + goto fail; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if(sk->sk_family == PF_INET6 && + ipv6_rcv_saddr_equal(sk, sk2) ) + goto fail; + } +#endif } } inet->num = snum; if (sk_unhashed(sk)) { - struct hlist_head *h = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; - - sk_add_node(sk, h); + sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]); sock_prot_inc_use(sk->sk_prot); } write_unlock_bh(&udp_hash_lock); @@ -1385,7 +1400,7 @@ struct proto udp_prot = { .backlog_rcv = udp_queue_rcv_skb, .hash = udp_v4_hash, .unhash = udp_v4_unhash, - .get_port = udp_v4_get_port, + .get_port = udp_get_port, .obj_size = sizeof(struct udp_sock), #ifdef CONFIG_COMPAT .compat_setsockopt = compat_udp_setsockopt, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 8d3432a..0d8b739 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -62,83 +62,6 @@ #include <linux/seq_file.h> 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) -{ - 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; -} - static void udp_v6_hash(struct sock *sk) { BUG(); @@ -1083,7 +1006,7 @@ struct proto udpv6_prot = { .backlog_rcv = udpv6_queue_rcv_skb, .hash = udp_v6_hash, .unhash = udp_v6_unhash, - .get_port = udp_v6_get_port, + .get_port = udp_get_port, .obj_size = sizeof(struct udp6_sock), #ifdef CONFIG_COMPAT .compat_setsockopt = compat_udpv6_setsockopt, - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html