Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[INET]: Generalise tcp_v4_hash_connect

Renaming it to inet_hash_connect, making it possible to ditch
dccp_v4_hash_connect and share the same code with TCP instead.

Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Arnaldo Carvalho de Melo and committed by
David S. Miller
a7f5e7f1 6d6ee43e

+186 -336
+2 -4
drivers/char/random.c
··· 1554 1554 1555 1555 EXPORT_SYMBOL(secure_tcp_sequence_number); 1556 1556 1557 - 1558 - 1559 - /* Generate secure starting point for ephemeral TCP port search */ 1560 - u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport) 1557 + /* Generate secure starting point for ephemeral IPV4 transport port search */ 1558 + u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport) 1561 1559 { 1562 1560 struct keydata *keyptr = get_keyptr(); 1563 1561 u32 hash[4];
+1 -1
include/linux/random.h
··· 52 52 void generate_random_uuid(unsigned char uuid_out[16]); 53 53 54 54 extern __u32 secure_ip_id(__u32 daddr); 55 - extern u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport); 55 + extern u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport); 56 56 extern u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, 57 57 __u16 dport); 58 58 extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
+3
include/net/inet_hashtables.h
··· 434 434 435 435 return sk; 436 436 } 437 + 438 + extern int inet_hash_connect(struct inet_timewait_death_row *death_row, 439 + struct sock *sk); 437 440 #endif /* _INET_HASHTABLES_H */
+1 -159
net/dccp/ipv4.c
··· 54 54 55 55 EXPORT_SYMBOL_GPL(dccp_unhash); 56 56 57 - /* called with local bh disabled */ 58 - static int __dccp_v4_check_established(struct sock *sk, const __u16 lport, 59 - struct inet_timewait_sock **twp) 60 - { 61 - struct inet_sock *inet = inet_sk(sk); 62 - const u32 daddr = inet->rcv_saddr; 63 - const u32 saddr = inet->daddr; 64 - const int dif = sk->sk_bound_dev_if; 65 - INET_ADDR_COOKIE(acookie, saddr, daddr) 66 - const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport); 67 - unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport); 68 - struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash); 69 - const struct sock *sk2; 70 - const struct hlist_node *node; 71 - struct inet_timewait_sock *tw; 72 - 73 - prefetch(head->chain.first); 74 - write_lock(&head->lock); 75 - 76 - /* Check TIME-WAIT sockets first. */ 77 - sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) { 78 - tw = inet_twsk(sk2); 79 - 80 - if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) 81 - goto not_unique; 82 - } 83 - tw = NULL; 84 - 85 - /* And established part... */ 86 - sk_for_each(sk2, node, &head->chain) { 87 - if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) 88 - goto not_unique; 89 - } 90 - 91 - /* Must record num and sport now. Otherwise we will see 92 - * in hash table socket with a funny identity. */ 93 - inet->num = lport; 94 - inet->sport = htons(lport); 95 - sk->sk_hash = hash; 96 - BUG_TRAP(sk_unhashed(sk)); 97 - __sk_add_node(sk, &head->chain); 98 - sock_prot_inc_use(sk->sk_prot); 99 - write_unlock(&head->lock); 100 - 101 - if (twp != NULL) { 102 - *twp = tw; 103 - NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 104 - } else if (tw != NULL) { 105 - /* Silly. Should hash-dance instead... */ 106 - inet_twsk_deschedule(tw, &dccp_death_row); 107 - NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 108 - 109 - inet_twsk_put(tw); 110 - } 111 - 112 - return 0; 113 - 114 - not_unique: 115 - write_unlock(&head->lock); 116 - return -EADDRNOTAVAIL; 117 - } 118 - 119 - /* 120 - * Bind a port for a connect operation and hash it. 121 - */ 122 - static int dccp_v4_hash_connect(struct sock *sk) 123 - { 124 - const unsigned short snum = inet_sk(sk)->num; 125 - struct inet_bind_hashbucket *head; 126 - struct inet_bind_bucket *tb; 127 - int ret; 128 - 129 - if (snum == 0) { 130 - int low = sysctl_local_port_range[0]; 131 - int high = sysctl_local_port_range[1]; 132 - int remaining = (high - low) + 1; 133 - int rover = net_random() % (high - low) + low; 134 - struct hlist_node *node; 135 - struct inet_timewait_sock *tw = NULL; 136 - 137 - local_bh_disable(); 138 - do { 139 - head = &dccp_hashinfo.bhash[inet_bhashfn(rover, 140 - dccp_hashinfo.bhash_size)]; 141 - spin_lock(&head->lock); 142 - 143 - /* Does not bother with rcv_saddr checks, 144 - * because the established check is already 145 - * unique enough. 146 - */ 147 - inet_bind_bucket_for_each(tb, node, &head->chain) { 148 - if (tb->port == rover) { 149 - BUG_TRAP(!hlist_empty(&tb->owners)); 150 - if (tb->fastreuse >= 0) 151 - goto next_port; 152 - if (!__dccp_v4_check_established(sk, 153 - rover, 154 - &tw)) 155 - goto ok; 156 - goto next_port; 157 - } 158 - } 159 - 160 - tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep, 161 - head, rover); 162 - if (tb == NULL) { 163 - spin_unlock(&head->lock); 164 - break; 165 - } 166 - tb->fastreuse = -1; 167 - goto ok; 168 - 169 - next_port: 170 - spin_unlock(&head->lock); 171 - if (++rover > high) 172 - rover = low; 173 - } while (--remaining > 0); 174 - 175 - local_bh_enable(); 176 - 177 - return -EADDRNOTAVAIL; 178 - 179 - ok: 180 - /* All locks still held and bhs disabled */ 181 - inet_bind_hash(sk, tb, rover); 182 - if (sk_unhashed(sk)) { 183 - inet_sk(sk)->sport = htons(rover); 184 - __inet_hash(&dccp_hashinfo, sk, 0); 185 - } 186 - spin_unlock(&head->lock); 187 - 188 - if (tw != NULL) { 189 - inet_twsk_deschedule(tw, &dccp_death_row); 190 - inet_twsk_put(tw); 191 - } 192 - 193 - ret = 0; 194 - goto out; 195 - } 196 - 197 - head = &dccp_hashinfo.bhash[inet_bhashfn(snum, 198 - dccp_hashinfo.bhash_size)]; 199 - tb = inet_csk(sk)->icsk_bind_hash; 200 - spin_lock_bh(&head->lock); 201 - if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) { 202 - __inet_hash(&dccp_hashinfo, sk, 0); 203 - spin_unlock_bh(&head->lock); 204 - return 0; 205 - } else { 206 - spin_unlock(&head->lock); 207 - /* No definite answer... Walk to established hash table */ 208 - ret = __dccp_v4_check_established(sk, snum, NULL); 209 - out: 210 - local_bh_enable(); 211 - return ret; 212 - } 213 - } 214 - 215 57 int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 216 58 { 217 59 struct inet_sock *inet = inet_sk(sk); ··· 114 272 * complete initialization after this. 115 273 */ 116 274 dccp_set_state(sk, DCCP_REQUESTING); 117 - err = dccp_v4_hash_connect(sk); 275 + err = inet_hash_connect(&dccp_death_row, sk); 118 276 if (err != 0) 119 277 goto failure; 120 278
+178
net/ipv4/inet_hashtables.c
··· 15 15 16 16 #include <linux/config.h> 17 17 #include <linux/module.h> 18 + #include <linux/random.h> 18 19 #include <linux/sched.h> 19 20 #include <linux/slab.h> 20 21 #include <linux/wait.h> 21 22 22 23 #include <net/inet_connection_sock.h> 23 24 #include <net/inet_hashtables.h> 25 + #include <net/ip.h> 24 26 25 27 /* 26 28 * Allocate and initialize a new local port bind bucket. ··· 165 163 } 166 164 167 165 EXPORT_SYMBOL_GPL(__inet_lookup_listener); 166 + 167 + /* called with local bh disabled */ 168 + static int __inet_check_established(struct inet_timewait_death_row *death_row, 169 + struct sock *sk, __u16 lport, 170 + struct inet_timewait_sock **twp) 171 + { 172 + struct inet_hashinfo *hinfo = death_row->hashinfo; 173 + struct inet_sock *inet = inet_sk(sk); 174 + u32 daddr = inet->rcv_saddr; 175 + u32 saddr = inet->daddr; 176 + int dif = sk->sk_bound_dev_if; 177 + INET_ADDR_COOKIE(acookie, saddr, daddr) 178 + const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport); 179 + unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport); 180 + struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash); 181 + struct sock *sk2; 182 + const struct hlist_node *node; 183 + struct inet_timewait_sock *tw; 184 + 185 + prefetch(head->chain.first); 186 + write_lock(&head->lock); 187 + 188 + /* Check TIME-WAIT sockets first. */ 189 + sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) { 190 + tw = inet_twsk(sk2); 191 + 192 + if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) { 193 + if (twsk_unique(sk, sk2, twp)) 194 + goto unique; 195 + else 196 + goto not_unique; 197 + } 198 + } 199 + tw = NULL; 200 + 201 + /* And established part... */ 202 + sk_for_each(sk2, node, &head->chain) { 203 + if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) 204 + goto not_unique; 205 + } 206 + 207 + unique: 208 + /* Must record num and sport now. Otherwise we will see 209 + * in hash table socket with a funny identity. */ 210 + inet->num = lport; 211 + inet->sport = htons(lport); 212 + sk->sk_hash = hash; 213 + BUG_TRAP(sk_unhashed(sk)); 214 + __sk_add_node(sk, &head->chain); 215 + sock_prot_inc_use(sk->sk_prot); 216 + write_unlock(&head->lock); 217 + 218 + if (twp) { 219 + *twp = tw; 220 + NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 221 + } else if (tw) { 222 + /* Silly. Should hash-dance instead... */ 223 + inet_twsk_deschedule(tw, death_row); 224 + NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 225 + 226 + inet_twsk_put(tw); 227 + } 228 + 229 + return 0; 230 + 231 + not_unique: 232 + write_unlock(&head->lock); 233 + return -EADDRNOTAVAIL; 234 + } 235 + 236 + static inline u32 inet_sk_port_offset(const struct sock *sk) 237 + { 238 + const struct inet_sock *inet = inet_sk(sk); 239 + return secure_ipv4_port_ephemeral(inet->rcv_saddr, inet->daddr, 240 + inet->dport); 241 + } 242 + 243 + /* 244 + * Bind a port for a connect operation and hash it. 245 + */ 246 + int inet_hash_connect(struct inet_timewait_death_row *death_row, 247 + struct sock *sk) 248 + { 249 + struct inet_hashinfo *hinfo = death_row->hashinfo; 250 + const unsigned short snum = inet_sk(sk)->num; 251 + struct inet_bind_hashbucket *head; 252 + struct inet_bind_bucket *tb; 253 + int ret; 254 + 255 + if (!snum) { 256 + int low = sysctl_local_port_range[0]; 257 + int high = sysctl_local_port_range[1]; 258 + int range = high - low; 259 + int i; 260 + int port; 261 + static u32 hint; 262 + u32 offset = hint + inet_sk_port_offset(sk); 263 + struct hlist_node *node; 264 + struct inet_timewait_sock *tw = NULL; 265 + 266 + local_bh_disable(); 267 + for (i = 1; i <= range; i++) { 268 + port = low + (i + offset) % range; 269 + head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; 270 + spin_lock(&head->lock); 271 + 272 + /* Does not bother with rcv_saddr checks, 273 + * because the established check is already 274 + * unique enough. 275 + */ 276 + inet_bind_bucket_for_each(tb, node, &head->chain) { 277 + if (tb->port == port) { 278 + BUG_TRAP(!hlist_empty(&tb->owners)); 279 + if (tb->fastreuse >= 0) 280 + goto next_port; 281 + if (!__inet_check_established(death_row, 282 + sk, port, 283 + &tw)) 284 + goto ok; 285 + goto next_port; 286 + } 287 + } 288 + 289 + tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port); 290 + if (!tb) { 291 + spin_unlock(&head->lock); 292 + break; 293 + } 294 + tb->fastreuse = -1; 295 + goto ok; 296 + 297 + next_port: 298 + spin_unlock(&head->lock); 299 + } 300 + local_bh_enable(); 301 + 302 + return -EADDRNOTAVAIL; 303 + 304 + ok: 305 + hint += i; 306 + 307 + /* Head lock still held and bh's disabled */ 308 + inet_bind_hash(sk, tb, port); 309 + if (sk_unhashed(sk)) { 310 + inet_sk(sk)->sport = htons(port); 311 + __inet_hash(hinfo, sk, 0); 312 + } 313 + spin_unlock(&head->lock); 314 + 315 + if (tw) { 316 + inet_twsk_deschedule(tw, death_row);; 317 + inet_twsk_put(tw); 318 + } 319 + 320 + ret = 0; 321 + goto out; 322 + } 323 + 324 + head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)]; 325 + tb = inet_csk(sk)->icsk_bind_hash; 326 + spin_lock_bh(&head->lock); 327 + if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) { 328 + __inet_hash(hinfo, sk, 0); 329 + spin_unlock_bh(&head->lock); 330 + return 0; 331 + } else { 332 + spin_unlock(&head->lock); 333 + /* No definite answer... Walk to established hash table */ 334 + ret = __inet_check_established(death_row, sk, snum, NULL); 335 + out: 336 + local_bh_enable(); 337 + return ret; 338 + } 339 + } 340 + 341 + EXPORT_SYMBOL_GPL(inet_hash_connect);
+1 -172
net/ipv4/tcp_ipv4.c
··· 152 152 153 153 EXPORT_SYMBOL_GPL(tcp_twsk_unique); 154 154 155 - /* called with local bh disabled */ 156 - static int __tcp_v4_check_established(struct sock *sk, __u16 lport, 157 - struct inet_timewait_sock **twp) 158 - { 159 - struct inet_sock *inet = inet_sk(sk); 160 - u32 daddr = inet->rcv_saddr; 161 - u32 saddr = inet->daddr; 162 - int dif = sk->sk_bound_dev_if; 163 - INET_ADDR_COOKIE(acookie, saddr, daddr) 164 - const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport); 165 - unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport); 166 - struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash); 167 - struct sock *sk2; 168 - const struct hlist_node *node; 169 - struct inet_timewait_sock *tw; 170 - 171 - prefetch(head->chain.first); 172 - write_lock(&head->lock); 173 - 174 - /* Check TIME-WAIT sockets first. */ 175 - sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) { 176 - tw = inet_twsk(sk2); 177 - 178 - if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) { 179 - if (twsk_unique(sk, sk2, twp)) 180 - goto unique; 181 - else 182 - goto not_unique; 183 - } 184 - } 185 - tw = NULL; 186 - 187 - /* And established part... */ 188 - sk_for_each(sk2, node, &head->chain) { 189 - if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) 190 - goto not_unique; 191 - } 192 - 193 - unique: 194 - /* Must record num and sport now. Otherwise we will see 195 - * in hash table socket with a funny identity. */ 196 - inet->num = lport; 197 - inet->sport = htons(lport); 198 - sk->sk_hash = hash; 199 - BUG_TRAP(sk_unhashed(sk)); 200 - __sk_add_node(sk, &head->chain); 201 - sock_prot_inc_use(sk->sk_prot); 202 - write_unlock(&head->lock); 203 - 204 - if (twp) { 205 - *twp = tw; 206 - NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 207 - } else if (tw) { 208 - /* Silly. Should hash-dance instead... */ 209 - inet_twsk_deschedule(tw, &tcp_death_row); 210 - NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED); 211 - 212 - inet_twsk_put(tw); 213 - } 214 - 215 - return 0; 216 - 217 - not_unique: 218 - write_unlock(&head->lock); 219 - return -EADDRNOTAVAIL; 220 - } 221 - 222 - static inline u32 connect_port_offset(const struct sock *sk) 223 - { 224 - const struct inet_sock *inet = inet_sk(sk); 225 - 226 - return secure_tcp_port_ephemeral(inet->rcv_saddr, inet->daddr, 227 - inet->dport); 228 - } 229 - 230 - /* 231 - * Bind a port for a connect operation and hash it. 232 - */ 233 - static inline int tcp_v4_hash_connect(struct sock *sk) 234 - { 235 - const unsigned short snum = inet_sk(sk)->num; 236 - struct inet_bind_hashbucket *head; 237 - struct inet_bind_bucket *tb; 238 - int ret; 239 - 240 - if (!snum) { 241 - int low = sysctl_local_port_range[0]; 242 - int high = sysctl_local_port_range[1]; 243 - int range = high - low; 244 - int i; 245 - int port; 246 - static u32 hint; 247 - u32 offset = hint + connect_port_offset(sk); 248 - struct hlist_node *node; 249 - struct inet_timewait_sock *tw = NULL; 250 - 251 - local_bh_disable(); 252 - for (i = 1; i <= range; i++) { 253 - port = low + (i + offset) % range; 254 - head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)]; 255 - spin_lock(&head->lock); 256 - 257 - /* Does not bother with rcv_saddr checks, 258 - * because the established check is already 259 - * unique enough. 260 - */ 261 - inet_bind_bucket_for_each(tb, node, &head->chain) { 262 - if (tb->port == port) { 263 - BUG_TRAP(!hlist_empty(&tb->owners)); 264 - if (tb->fastreuse >= 0) 265 - goto next_port; 266 - if (!__tcp_v4_check_established(sk, 267 - port, 268 - &tw)) 269 - goto ok; 270 - goto next_port; 271 - } 272 - } 273 - 274 - tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port); 275 - if (!tb) { 276 - spin_unlock(&head->lock); 277 - break; 278 - } 279 - tb->fastreuse = -1; 280 - goto ok; 281 - 282 - next_port: 283 - spin_unlock(&head->lock); 284 - } 285 - local_bh_enable(); 286 - 287 - return -EADDRNOTAVAIL; 288 - 289 - ok: 290 - hint += i; 291 - 292 - /* Head lock still held and bh's disabled */ 293 - inet_bind_hash(sk, tb, port); 294 - if (sk_unhashed(sk)) { 295 - inet_sk(sk)->sport = htons(port); 296 - __inet_hash(&tcp_hashinfo, sk, 0); 297 - } 298 - spin_unlock(&head->lock); 299 - 300 - if (tw) { 301 - inet_twsk_deschedule(tw, &tcp_death_row);; 302 - inet_twsk_put(tw); 303 - } 304 - 305 - ret = 0; 306 - goto out; 307 - } 308 - 309 - head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)]; 310 - tb = inet_csk(sk)->icsk_bind_hash; 311 - spin_lock_bh(&head->lock); 312 - if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) { 313 - __inet_hash(&tcp_hashinfo, sk, 0); 314 - spin_unlock_bh(&head->lock); 315 - return 0; 316 - } else { 317 - spin_unlock(&head->lock); 318 - /* No definite answer... Walk to established hash table */ 319 - ret = __tcp_v4_check_established(sk, snum, NULL); 320 - out: 321 - local_bh_enable(); 322 - return ret; 323 - } 324 - } 325 - 326 155 /* This will initiate an outgoing connection. */ 327 156 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 328 157 { ··· 232 403 * complete initialization after this. 233 404 */ 234 405 tcp_set_state(sk, TCP_SYN_SENT); 235 - err = tcp_v4_hash_connect(sk); 406 + err = inet_hash_connect(&tcp_death_row, sk); 236 407 if (err) 237 408 goto failure; 238 409