diff options
-rw-r--r-- | include/net/inet_connection_sock.h | 6 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 3 | ||||
-rw-r--r-- | net/ipv4/inet_connection_sock.c | 11 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 3 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 95 |
5 files changed, 21 insertions, 97 deletions
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index b0c99060b78d..edc68e858d51 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h | |||
@@ -192,8 +192,12 @@ extern struct request_sock *inet_csk_search_req(const struct sock *sk, | |||
192 | const __u16 rport, | 192 | const __u16 rport, |
193 | const __u32 raddr, | 193 | const __u32 raddr, |
194 | const __u32 laddr); | 194 | const __u32 laddr); |
195 | extern int inet_csk_bind_conflict(const struct sock *sk, | ||
196 | const struct inet_bind_bucket *tb); | ||
195 | extern int inet_csk_get_port(struct inet_hashinfo *hashinfo, | 197 | extern int inet_csk_get_port(struct inet_hashinfo *hashinfo, |
196 | struct sock *sk, unsigned short snum); | 198 | struct sock *sk, unsigned short snum, |
199 | int (*bind_conflict)(const struct sock *sk, | ||
200 | const struct inet_bind_bucket *tb)); | ||
197 | 201 | ||
198 | extern struct dst_entry* inet_csk_route_req(struct sock *sk, | 202 | extern struct dst_entry* inet_csk_route_req(struct sock *sk, |
199 | const struct request_sock *req); | 203 | const struct request_sock *req); |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 656e13e38cfb..1ac3e30ae791 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -37,7 +37,8 @@ EXPORT_SYMBOL_GPL(dccp_hashinfo); | |||
37 | 37 | ||
38 | static int dccp_v4_get_port(struct sock *sk, const unsigned short snum) | 38 | static int dccp_v4_get_port(struct sock *sk, const unsigned short snum) |
39 | { | 39 | { |
40 | return inet_csk_get_port(&dccp_hashinfo, sk, snum); | 40 | return inet_csk_get_port(&dccp_hashinfo, sk, snum, |
41 | inet_csk_bind_conflict); | ||
41 | } | 42 | } |
42 | 43 | ||
43 | static void dccp_v4_hash(struct sock *sk) | 44 | static void dccp_v4_hash(struct sock *sk) |
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 3fe021f1a566..f05b6e761102 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c | |||
@@ -37,7 +37,8 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); | |||
37 | */ | 37 | */ |
38 | int sysctl_local_port_range[2] = { 1024, 4999 }; | 38 | int sysctl_local_port_range[2] = { 1024, 4999 }; |
39 | 39 | ||
40 | static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucket *tb) | 40 | int inet_csk_bind_conflict(const struct sock *sk, |
41 | const struct inet_bind_bucket *tb) | ||
41 | { | 42 | { |
42 | const u32 sk_rcv_saddr = inet_rcv_saddr(sk); | 43 | const u32 sk_rcv_saddr = inet_rcv_saddr(sk); |
43 | struct sock *sk2; | 44 | struct sock *sk2; |
@@ -62,11 +63,15 @@ static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucke | |||
62 | return node != NULL; | 63 | return node != NULL; |
63 | } | 64 | } |
64 | 65 | ||
66 | EXPORT_SYMBOL_GPL(inet_csk_bind_conflict); | ||
67 | |||
65 | /* Obtain a reference to a local port for the given sock, | 68 | /* Obtain a reference to a local port for the given sock, |
66 | * if snum is zero it means select any available local port. | 69 | * if snum is zero it means select any available local port. |
67 | */ | 70 | */ |
68 | int inet_csk_get_port(struct inet_hashinfo *hashinfo, | 71 | int inet_csk_get_port(struct inet_hashinfo *hashinfo, |
69 | struct sock *sk, unsigned short snum) | 72 | struct sock *sk, unsigned short snum, |
73 | int (*bind_conflict)(const struct sock *sk, | ||
74 | const struct inet_bind_bucket *tb)) | ||
70 | { | 75 | { |
71 | struct inet_bind_hashbucket *head; | 76 | struct inet_bind_hashbucket *head; |
72 | struct hlist_node *node; | 77 | struct hlist_node *node; |
@@ -125,7 +130,7 @@ tb_found: | |||
125 | goto success; | 130 | goto success; |
126 | } else { | 131 | } else { |
127 | ret = 1; | 132 | ret = 1; |
128 | if (inet_csk_bind_conflict(sk, tb)) | 133 | if (bind_conflict(sk, tb)) |
129 | goto fail_unlock; | 134 | goto fail_unlock; |
130 | } | 135 | } |
131 | } | 136 | } |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4d5021e1929b..2aa19c89a94a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -97,7 +97,8 @@ struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { | |||
97 | 97 | ||
98 | static int tcp_v4_get_port(struct sock *sk, unsigned short snum) | 98 | static int tcp_v4_get_port(struct sock *sk, unsigned short snum) |
99 | { | 99 | { |
100 | return inet_csk_get_port(&tcp_hashinfo, sk, snum); | 100 | return inet_csk_get_port(&tcp_hashinfo, sk, snum, |
101 | inet_csk_bind_conflict); | ||
101 | } | 102 | } |
102 | 103 | ||
103 | static void tcp_v4_hash(struct sock *sk) | 104 | static void tcp_v4_hash(struct sock *sk) |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8827389abaf7..76c8f5a2f7f3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -76,8 +76,8 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); | |||
76 | static struct tcp_func ipv6_mapped; | 76 | static struct tcp_func ipv6_mapped; |
77 | static struct tcp_func ipv6_specific; | 77 | static struct tcp_func ipv6_specific; |
78 | 78 | ||
79 | static inline int tcp_v6_bind_conflict(const struct sock *sk, | 79 | int inet6_csk_bind_conflict(const struct sock *sk, |
80 | const struct inet_bind_bucket *tb) | 80 | const struct inet_bind_bucket *tb) |
81 | { | 81 | { |
82 | const struct sock *sk2; | 82 | const struct sock *sk2; |
83 | const struct hlist_node *node; | 83 | const struct hlist_node *node; |
@@ -97,97 +97,10 @@ static inline int tcp_v6_bind_conflict(const struct sock *sk, | |||
97 | return node != NULL; | 97 | return node != NULL; |
98 | } | 98 | } |
99 | 99 | ||
100 | /* Grrr, addr_type already calculated by caller, but I don't want | ||
101 | * to add some silly "cookie" argument to this method just for that. | ||
102 | * But it doesn't matter, the recalculation is in the rarest path | ||
103 | * this function ever takes. | ||
104 | */ | ||
105 | static int tcp_v6_get_port(struct sock *sk, unsigned short snum) | 100 | static int tcp_v6_get_port(struct sock *sk, unsigned short snum) |
106 | { | 101 | { |
107 | struct inet_bind_hashbucket *head; | 102 | return inet_csk_get_port(&tcp_hashinfo, sk, snum, |
108 | struct inet_bind_bucket *tb; | 103 | inet6_csk_bind_conflict); |
109 | struct hlist_node *node; | ||
110 | int ret; | ||
111 | |||
112 | local_bh_disable(); | ||
113 | if (snum == 0) { | ||
114 | int low = sysctl_local_port_range[0]; | ||
115 | int high = sysctl_local_port_range[1]; | ||
116 | int remaining = (high - low) + 1; | ||
117 | int rover = net_random() % (high - low) + low; | ||
118 | |||
119 | do { | ||
120 | head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)]; | ||
121 | spin_lock(&head->lock); | ||
122 | inet_bind_bucket_for_each(tb, node, &head->chain) | ||
123 | if (tb->port == rover) | ||
124 | goto next; | ||
125 | break; | ||
126 | next: | ||
127 | spin_unlock(&head->lock); | ||
128 | if (++rover > high) | ||
129 | rover = low; | ||
130 | } while (--remaining > 0); | ||
131 | |||
132 | /* Exhausted local port range during search? It is not | ||
133 | * possible for us to be holding one of the bind hash | ||
134 | * locks if this test triggers, because if 'remaining' | ||
135 | * drops to zero, we broke out of the do/while loop at | ||
136 | * the top level, not from the 'break;' statement. | ||
137 | */ | ||
138 | ret = 1; | ||
139 | if (unlikely(remaining <= 0)) | ||
140 | goto fail; | ||
141 | |||
142 | /* OK, here is the one we will use. */ | ||
143 | snum = rover; | ||
144 | } else { | ||
145 | head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)]; | ||
146 | spin_lock(&head->lock); | ||
147 | inet_bind_bucket_for_each(tb, node, &head->chain) | ||
148 | if (tb->port == snum) | ||
149 | goto tb_found; | ||
150 | } | ||
151 | tb = NULL; | ||
152 | goto tb_not_found; | ||
153 | tb_found: | ||
154 | if (tb && !hlist_empty(&tb->owners)) { | ||
155 | if (tb->fastreuse > 0 && sk->sk_reuse && | ||
156 | sk->sk_state != TCP_LISTEN) { | ||
157 | goto success; | ||
158 | } else { | ||
159 | ret = 1; | ||
160 | if (tcp_v6_bind_conflict(sk, tb)) | ||
161 | goto fail_unlock; | ||
162 | } | ||
163 | } | ||
164 | tb_not_found: | ||
165 | ret = 1; | ||
166 | if (tb == NULL) { | ||
167 | tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, snum); | ||
168 | if (tb == NULL) | ||
169 | goto fail_unlock; | ||
170 | } | ||
171 | if (hlist_empty(&tb->owners)) { | ||
172 | if (sk->sk_reuse && sk->sk_state != TCP_LISTEN) | ||
173 | tb->fastreuse = 1; | ||
174 | else | ||
175 | tb->fastreuse = 0; | ||
176 | } else if (tb->fastreuse && | ||
177 | (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) | ||
178 | tb->fastreuse = 0; | ||
179 | |||
180 | success: | ||
181 | if (!inet_csk(sk)->icsk_bind_hash) | ||
182 | inet_bind_hash(sk, tb, snum); | ||
183 | BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb); | ||
184 | ret = 0; | ||
185 | |||
186 | fail_unlock: | ||
187 | spin_unlock(&head->lock); | ||
188 | fail: | ||
189 | local_bh_enable(); | ||
190 | return ret; | ||
191 | } | 104 | } |
192 | 105 | ||
193 | static __inline__ void __tcp_v6_hash(struct sock *sk) | 106 | static __inline__ void __tcp_v6_hash(struct sock *sk) |