aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2004-12-19 23:17:24 -0800
committerDavid S. Miller <davem@nuts.davemloft.net>2004-12-19 23:17:24 -0800
commitcceae0d365b18b685cc564071aae22599ebf52a9 (patch)
tree4749fd15e33e528e8fc0e48e9d7799e9668c43b3 /net
parent712bc31381a3327859442ba1ec026d77e728fbbb (diff)
downloadhistory-cceae0d365b18b685cc564071aae22599ebf52a9.tar.gz
[TCP]: Efficient port randomization (rev 3)
okay, here is the revised version. Testing shows that it is more consistent, and just as fast as existing code, probably because of the getting rid of portalloc_lock and better distribution. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/tcp_ipv4.c62
1 files changed, 25 insertions, 37 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 844b9fa6fe9196..3062579ef720cf 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -636,10 +636,18 @@ not_unique:
return -EADDRNOTAVAIL;
}
+static inline u32 connect_port_offset(const struct sock *sk)
+{
+ const struct inet_opt *inet = inet_sk(sk);
+
+ return secure_tcp_port_ephemeral(inet->rcv_saddr, inet->daddr,
+ inet->dport);
+}
+
/*
* Bind a port for a connect operation and hash it.
*/
-static int tcp_v4_hash_connect(struct sock *sk)
+static inline int tcp_v4_hash_connect(struct sock *sk)
{
unsigned short snum = inet_sk(sk)->num;
struct tcp_bind_hashbucket *head;
@@ -647,36 +655,20 @@ static int tcp_v4_hash_connect(struct sock *sk)
int ret;
if (!snum) {
- int rover;
int low = sysctl_local_port_range[0];
int high = sysctl_local_port_range[1];
- int remaining = (high - low) + 1;
+ int range = high - low;
+ int i;
+ int port;
+ static u32 hint;
+ u32 offset = hint + connect_port_offset(sk);
struct hlist_node *node;
struct tcp_tw_bucket *tw = NULL;
local_bh_disable();
-
- /* TODO. Actually it is not so bad idea to remove
- * tcp_portalloc_lock before next submission to Linus.
- * As soon as we touch this place at all it is time to think.
- *
- * Now it protects single _advisory_ variable tcp_port_rover,
- * hence it is mostly useless.
- * Code will work nicely if we just delete it, but
- * I am afraid in contented case it will work not better or
- * even worse: another cpu just will hit the same bucket
- * and spin there.
- * So some cpu salt could remove both contention and
- * memory pingpong. Any ideas how to do this in a nice way?
- */
- spin_lock(&tcp_portalloc_lock);
- rover = tcp_port_rover;
-
- do {
- rover++;
- if ((rover < low) || (rover > high))
- rover = low;
- head = &tcp_bhash[tcp_bhashfn(rover)];
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &tcp_bhash[tcp_bhashfn(port)];
spin_lock(&head->lock);
/* Does not bother with rcv_saddr checks,
@@ -684,19 +676,19 @@ static int tcp_v4_hash_connect(struct sock *sk)
* unique enough.
*/
tb_for_each(tb, node, &head->chain) {
- if (tb->port == rover) {
+ if (tb->port == port) {
BUG_TRAP(!hlist_empty(&tb->owners));
if (tb->fastreuse >= 0)
goto next_port;
if (!__tcp_v4_check_established(sk,
- rover,
+ port,
&tw))
goto ok;
goto next_port;
}
}
- tb = tcp_bucket_create(head, rover);
+ tb = tcp_bucket_create(head, port);
if (!tb) {
spin_unlock(&head->lock);
break;
@@ -706,22 +698,18 @@ static int tcp_v4_hash_connect(struct sock *sk)
next_port:
spin_unlock(&head->lock);
- } while (--remaining > 0);
- tcp_port_rover = rover;
- spin_unlock(&tcp_portalloc_lock);
-
+ }
local_bh_enable();
return -EADDRNOTAVAIL;
ok:
- /* All locks still held and bhs disabled */
- tcp_port_rover = rover;
- spin_unlock(&tcp_portalloc_lock);
+ hint += i;
- tcp_bind_hash(sk, tb, rover);
+ /* Head lock still held and bh's disabled */
+ tcp_bind_hash(sk, tb, port);
if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(rover);
+ inet_sk(sk)->sport = htons(port);
__tcp_v4_hash(sk, 0);
}
spin_unlock(&head->lock);