|
@@ -152,177 +152,6 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_twsk_unique);
|
|
EXPORT_SYMBOL_GPL(tcp_twsk_unique);
|
|
|
|
|
|
-/* called with local bh disabled */
|
|
|
|
-static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
|
|
|
|
- struct inet_timewait_sock **twp)
|
|
|
|
-{
|
|
|
|
- struct inet_sock *inet = inet_sk(sk);
|
|
|
|
- u32 daddr = inet->rcv_saddr;
|
|
|
|
- u32 saddr = inet->daddr;
|
|
|
|
- int dif = sk->sk_bound_dev_if;
|
|
|
|
- INET_ADDR_COOKIE(acookie, saddr, daddr)
|
|
|
|
- const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
|
|
|
|
- unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
|
|
|
|
- struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
|
|
|
|
- struct sock *sk2;
|
|
|
|
- const struct hlist_node *node;
|
|
|
|
- struct inet_timewait_sock *tw;
|
|
|
|
-
|
|
|
|
- prefetch(head->chain.first);
|
|
|
|
- write_lock(&head->lock);
|
|
|
|
-
|
|
|
|
- /* Check TIME-WAIT sockets first. */
|
|
|
|
- sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
|
|
|
|
- tw = inet_twsk(sk2);
|
|
|
|
-
|
|
|
|
- if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
|
|
|
|
- if (twsk_unique(sk, sk2, twp))
|
|
|
|
- goto unique;
|
|
|
|
- else
|
|
|
|
- goto not_unique;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- tw = NULL;
|
|
|
|
-
|
|
|
|
- /* And established part... */
|
|
|
|
- sk_for_each(sk2, node, &head->chain) {
|
|
|
|
- if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
|
|
|
|
- goto not_unique;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-unique:
|
|
|
|
- /* Must record num and sport now. Otherwise we will see
|
|
|
|
- * in hash table socket with a funny identity. */
|
|
|
|
- inet->num = lport;
|
|
|
|
- inet->sport = htons(lport);
|
|
|
|
- sk->sk_hash = hash;
|
|
|
|
- BUG_TRAP(sk_unhashed(sk));
|
|
|
|
- __sk_add_node(sk, &head->chain);
|
|
|
|
- sock_prot_inc_use(sk->sk_prot);
|
|
|
|
- write_unlock(&head->lock);
|
|
|
|
-
|
|
|
|
- if (twp) {
|
|
|
|
- *twp = tw;
|
|
|
|
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
|
|
|
|
- } else if (tw) {
|
|
|
|
- /* Silly. Should hash-dance instead... */
|
|
|
|
- inet_twsk_deschedule(tw, &tcp_death_row);
|
|
|
|
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
|
|
|
|
-
|
|
|
|
- inet_twsk_put(tw);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
-not_unique:
|
|
|
|
- write_unlock(&head->lock);
|
|
|
|
- return -EADDRNOTAVAIL;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static inline u32 connect_port_offset(const struct sock *sk)
|
|
|
|
-{
|
|
|
|
- const struct inet_sock *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 inline int tcp_v4_hash_connect(struct sock *sk)
|
|
|
|
-{
|
|
|
|
- const unsigned short snum = inet_sk(sk)->num;
|
|
|
|
- struct inet_bind_hashbucket *head;
|
|
|
|
- struct inet_bind_bucket *tb;
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- if (!snum) {
|
|
|
|
- int low = sysctl_local_port_range[0];
|
|
|
|
- int high = sysctl_local_port_range[1];
|
|
|
|
- int range = high - low;
|
|
|
|
- int i;
|
|
|
|
- int port;
|
|
|
|
- static u32 hint;
|
|
|
|
- u32 offset = hint + connect_port_offset(sk);
|
|
|
|
- struct hlist_node *node;
|
|
|
|
- struct inet_timewait_sock *tw = NULL;
|
|
|
|
-
|
|
|
|
- local_bh_disable();
|
|
|
|
- for (i = 1; i <= range; i++) {
|
|
|
|
- port = low + (i + offset) % range;
|
|
|
|
- head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
|
|
|
|
- spin_lock(&head->lock);
|
|
|
|
-
|
|
|
|
- /* Does not bother with rcv_saddr checks,
|
|
|
|
- * because the established check is already
|
|
|
|
- * unique enough.
|
|
|
|
- */
|
|
|
|
- inet_bind_bucket_for_each(tb, node, &head->chain) {
|
|
|
|
- if (tb->port == port) {
|
|
|
|
- BUG_TRAP(!hlist_empty(&tb->owners));
|
|
|
|
- if (tb->fastreuse >= 0)
|
|
|
|
- goto next_port;
|
|
|
|
- if (!__tcp_v4_check_established(sk,
|
|
|
|
- port,
|
|
|
|
- &tw))
|
|
|
|
- goto ok;
|
|
|
|
- goto next_port;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
|
|
|
|
- if (!tb) {
|
|
|
|
- spin_unlock(&head->lock);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- tb->fastreuse = -1;
|
|
|
|
- goto ok;
|
|
|
|
-
|
|
|
|
- next_port:
|
|
|
|
- spin_unlock(&head->lock);
|
|
|
|
- }
|
|
|
|
- local_bh_enable();
|
|
|
|
-
|
|
|
|
- return -EADDRNOTAVAIL;
|
|
|
|
-
|
|
|
|
-ok:
|
|
|
|
- hint += i;
|
|
|
|
-
|
|
|
|
- /* Head lock still held and bh's disabled */
|
|
|
|
- inet_bind_hash(sk, tb, port);
|
|
|
|
- if (sk_unhashed(sk)) {
|
|
|
|
- inet_sk(sk)->sport = htons(port);
|
|
|
|
- __inet_hash(&tcp_hashinfo, sk, 0);
|
|
|
|
- }
|
|
|
|
- spin_unlock(&head->lock);
|
|
|
|
-
|
|
|
|
- if (tw) {
|
|
|
|
- inet_twsk_deschedule(tw, &tcp_death_row);;
|
|
|
|
- inet_twsk_put(tw);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ret = 0;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
|
|
|
|
- tb = inet_csk(sk)->icsk_bind_hash;
|
|
|
|
- spin_lock_bh(&head->lock);
|
|
|
|
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
|
|
|
|
- __inet_hash(&tcp_hashinfo, sk, 0);
|
|
|
|
- spin_unlock_bh(&head->lock);
|
|
|
|
- return 0;
|
|
|
|
- } else {
|
|
|
|
- spin_unlock(&head->lock);
|
|
|
|
- /* No definite answer... Walk to established hash table */
|
|
|
|
- ret = __tcp_v4_check_established(sk, snum, NULL);
|
|
|
|
-out:
|
|
|
|
- local_bh_enable();
|
|
|
|
- return ret;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/* This will initiate an outgoing connection. */
|
|
/* This will initiate an outgoing connection. */
|
|
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
{
|
|
{
|
|
@@ -403,7 +232,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
* complete initialization after this.
|
|
* complete initialization after this.
|
|
*/
|
|
*/
|
|
tcp_set_state(sk, TCP_SYN_SENT);
|
|
tcp_set_state(sk, TCP_SYN_SENT);
|
|
- err = tcp_v4_hash_connect(sk);
|
|
|
|
|
|
+ err = inet_hash_connect(&tcp_death_row, sk);
|
|
if (err)
|
|
if (err)
|
|
goto failure;
|
|
goto failure;
|
|
|
|
|