[UDP]: Randomize port selection.
This patch causes UDP port allocation to be randomized like TCP. The earlier code would always choose same port (ie first empty list). Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
356f89e12e
commit
32c1da7081
|
@ -113,9 +113,8 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly;
|
||||||
struct hlist_head udp_hash[UDP_HTABLE_SIZE];
|
struct hlist_head udp_hash[UDP_HTABLE_SIZE];
|
||||||
DEFINE_RWLOCK(udp_hash_lock);
|
DEFINE_RWLOCK(udp_hash_lock);
|
||||||
|
|
||||||
static int udp_port_rover;
|
static inline int __udp_lib_lport_inuse(__u16 num,
|
||||||
|
const struct hlist_head udptable[])
|
||||||
static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
|
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct hlist_node *node;
|
struct hlist_node *node;
|
||||||
|
@ -132,11 +131,10 @@ static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
|
||||||
* @sk: socket struct in question
|
* @sk: socket struct in question
|
||||||
* @snum: port number to look up
|
* @snum: port number to look up
|
||||||
* @udptable: hash list table, must be of UDP_HTABLE_SIZE
|
* @udptable: hash list table, must be of UDP_HTABLE_SIZE
|
||||||
* @port_rover: pointer to record of last unallocated port
|
|
||||||
* @saddr_comp: AF-dependent comparison of bound local IP addresses
|
* @saddr_comp: AF-dependent comparison of bound local IP addresses
|
||||||
*/
|
*/
|
||||||
int __udp_lib_get_port(struct sock *sk, unsigned short snum,
|
int __udp_lib_get_port(struct sock *sk, unsigned short snum,
|
||||||
struct hlist_head udptable[], int *port_rover,
|
struct hlist_head udptable[],
|
||||||
int (*saddr_comp)(const struct sock *sk1,
|
int (*saddr_comp)(const struct sock *sk1,
|
||||||
const struct sock *sk2 ) )
|
const struct sock *sk2 ) )
|
||||||
{
|
{
|
||||||
|
@ -146,49 +144,56 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
|
||||||
int error = 1;
|
int error = 1;
|
||||||
|
|
||||||
write_lock_bh(&udp_hash_lock);
|
write_lock_bh(&udp_hash_lock);
|
||||||
if (snum == 0) {
|
|
||||||
int best_size_so_far, best, result, i;
|
|
||||||
|
|
||||||
if (*port_rover > sysctl_local_port_range[1] ||
|
if (!snum) {
|
||||||
*port_rover < sysctl_local_port_range[0])
|
int i;
|
||||||
*port_rover = sysctl_local_port_range[0];
|
int low = sysctl_local_port_range[0];
|
||||||
best_size_so_far = 32767;
|
int high = sysctl_local_port_range[1];
|
||||||
best = result = *port_rover;
|
unsigned rover, best, best_size_so_far;
|
||||||
for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
|
|
||||||
int size;
|
|
||||||
|
|
||||||
head = &udptable[result & (UDP_HTABLE_SIZE - 1)];
|
best_size_so_far = UINT_MAX;
|
||||||
if (hlist_empty(head)) {
|
best = rover = net_random() % (high - low) + low;
|
||||||
if (result > sysctl_local_port_range[1])
|
|
||||||
result = sysctl_local_port_range[0] +
|
/* 1st pass: look for empty (or shortest) hash chain */
|
||||||
((result - sysctl_local_port_range[0]) &
|
for (i = 0; i < UDP_HTABLE_SIZE; i++) {
|
||||||
(UDP_HTABLE_SIZE - 1));
|
int size = 0;
|
||||||
|
|
||||||
|
head = &udptable[rover & (UDP_HTABLE_SIZE - 1)];
|
||||||
|
if (hlist_empty(head))
|
||||||
goto gotit;
|
goto gotit;
|
||||||
}
|
|
||||||
size = 0;
|
|
||||||
sk_for_each(sk2, node, head) {
|
sk_for_each(sk2, node, head) {
|
||||||
if (++size >= best_size_so_far)
|
if (++size >= best_size_so_far)
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
best_size_so_far = size;
|
best_size_so_far = size;
|
||||||
best = result;
|
best = rover;
|
||||||
next:
|
next:
|
||||||
;
|
/* fold back if end of range */
|
||||||
|
if (++rover > high)
|
||||||
|
rover = low + ((rover - low)
|
||||||
|
& (UDP_HTABLE_SIZE - 1));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
result = best;
|
|
||||||
for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE;
|
/* 2nd pass: find hole in shortest hash chain */
|
||||||
i++, result += UDP_HTABLE_SIZE) {
|
rover = best;
|
||||||
if (result > sysctl_local_port_range[1])
|
for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
|
||||||
result = sysctl_local_port_range[0]
|
if (! __udp_lib_lport_inuse(rover, udptable))
|
||||||
+ ((result - sysctl_local_port_range[0]) &
|
goto gotit;
|
||||||
(UDP_HTABLE_SIZE - 1));
|
rover += UDP_HTABLE_SIZE;
|
||||||
if (! __udp_lib_lport_inuse(result, udptable))
|
if (rover > high)
|
||||||
break;
|
rover = low + ((rover - low)
|
||||||
|
& (UDP_HTABLE_SIZE - 1));
|
||||||
}
|
}
|
||||||
if (i >= (1 << 16) / UDP_HTABLE_SIZE)
|
|
||||||
goto fail;
|
|
||||||
|
/* All ports in use! */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
gotit:
|
gotit:
|
||||||
*port_rover = snum = result;
|
snum = rover;
|
||||||
} else {
|
} else {
|
||||||
head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
|
head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
|
||||||
|
|
||||||
|
@ -201,6 +206,7 @@ gotit:
|
||||||
(*saddr_comp)(sk, sk2) )
|
(*saddr_comp)(sk, sk2) )
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
inet_sk(sk)->num = snum;
|
inet_sk(sk)->num = snum;
|
||||||
sk->sk_hash = snum;
|
sk->sk_hash = snum;
|
||||||
if (sk_unhashed(sk)) {
|
if (sk_unhashed(sk)) {
|
||||||
|
@ -217,7 +223,7 @@ fail:
|
||||||
int udp_get_port(struct sock *sk, unsigned short snum,
|
int udp_get_port(struct sock *sk, unsigned short snum,
|
||||||
int (*scmp)(const struct sock *, const struct sock *))
|
int (*scmp)(const struct sock *, const struct sock *))
|
||||||
{
|
{
|
||||||
return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp);
|
return __udp_lib_get_port(sk, snum, udp_hash, scmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
|
int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
|
||||||
|
|
|
@ -9,7 +9,7 @@ extern int __udp4_lib_rcv(struct sk_buff *, struct hlist_head [], int );
|
||||||
extern void __udp4_lib_err(struct sk_buff *, u32, struct hlist_head []);
|
extern void __udp4_lib_err(struct sk_buff *, u32, struct hlist_head []);
|
||||||
|
|
||||||
extern int __udp_lib_get_port(struct sock *sk, unsigned short snum,
|
extern int __udp_lib_get_port(struct sock *sk, unsigned short snum,
|
||||||
struct hlist_head udptable[], int *port_rover,
|
struct hlist_head udptable[],
|
||||||
int (*)(const struct sock*,const struct sock*));
|
int (*)(const struct sock*,const struct sock*));
|
||||||
extern int ipv4_rcv_saddr_equal(const struct sock *, const struct sock *);
|
extern int ipv4_rcv_saddr_equal(const struct sock *, const struct sock *);
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
DEFINE_SNMP_STAT(struct udp_mib, udplite_statistics) __read_mostly;
|
DEFINE_SNMP_STAT(struct udp_mib, udplite_statistics) __read_mostly;
|
||||||
|
|
||||||
struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
|
struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
|
||||||
static int udplite_port_rover;
|
|
||||||
|
|
||||||
int udplite_get_port(struct sock *sk, unsigned short p,
|
int udplite_get_port(struct sock *sk, unsigned short p,
|
||||||
int (*c)(const struct sock *, const struct sock *))
|
int (*c)(const struct sock *, const struct sock *))
|
||||||
{
|
{
|
||||||
return __udp_lib_get_port(sk, p, udplite_hash, &udplite_port_rover, c);
|
return __udp_lib_get_port(sk, p, udplite_hash, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int udplite_v4_get_port(struct sock *sk, unsigned short snum)
|
static int udplite_v4_get_port(struct sock *sk, unsigned short snum)
|
||||||
|
|
Loading…
Reference in a new issue