diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 95 | 
1 files changed, 4 insertions, 91 deletions
| 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) | 
