aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp
diff options
context:
space:
mode:
Diffstat (limited to 'net/dccp')
-rw-r--r--net/dccp/ipv6.c171
1 files changed, 1 insertions, 170 deletions
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 4d078f5b911b..71bf04eb21e1 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -84,175 +84,6 @@ static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
84 dh->dccph_sport); 84 dh->dccph_sport);
85} 85}
86 86
87static int __dccp_v6_check_established(struct sock *sk, const __u16 lport,
88 struct inet_timewait_sock **twp)
89{
90 struct inet_sock *inet = inet_sk(sk);
91 const struct ipv6_pinfo *np = inet6_sk(sk);
92 const struct in6_addr *daddr = &np->rcv_saddr;
93 const struct in6_addr *saddr = &np->daddr;
94 const int dif = sk->sk_bound_dev_if;
95 const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
96 const unsigned int hash = inet6_ehashfn(daddr, inet->num,
97 saddr, inet->dport);
98 struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
99 struct sock *sk2;
100 const struct hlist_node *node;
101 struct inet_timewait_sock *tw;
102
103 prefetch(head->chain.first);
104 write_lock(&head->lock);
105
106 /* Check TIME-WAIT sockets first. */
107 sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
108 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
109
110 tw = inet_twsk(sk2);
111
112 if(*((__u32 *)&(tw->tw_dport)) == ports &&
113 sk2->sk_family == PF_INET6 &&
114 ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
115 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
116 sk2->sk_bound_dev_if == sk->sk_bound_dev_if)
117 goto not_unique;
118 }
119 tw = NULL;
120
121 /* And established part... */
122 sk_for_each(sk2, node, &head->chain) {
123 if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
124 goto not_unique;
125 }
126
127 BUG_TRAP(sk_unhashed(sk));
128 __sk_add_node(sk, &head->chain);
129 sk->sk_hash = hash;
130 sock_prot_inc_use(sk->sk_prot);
131 write_unlock(&head->lock);
132
133 if (twp) {
134 *twp = tw;
135 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
136 } else if (tw) {
137 /* Silly. Should hash-dance instead... */
138 inet_twsk_deschedule(tw, &dccp_death_row);
139 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
140
141 inet_twsk_put(tw);
142 }
143 return 0;
144
145not_unique:
146 write_unlock(&head->lock);
147 return -EADDRNOTAVAIL;
148}
149
150static inline u32 dccp_v6_port_offset(const struct sock *sk)
151{
152 const struct inet_sock *inet = inet_sk(sk);
153 const struct ipv6_pinfo *np = inet6_sk(sk);
154
155 return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
156 np->daddr.s6_addr32,
157 inet->dport);
158}
159
160static int dccp_v6_hash_connect(struct sock *sk)
161{
162 const unsigned short snum = inet_sk(sk)->num;
163 struct inet_bind_hashbucket *head;
164 struct inet_bind_bucket *tb;
165 int ret;
166
167 if (snum == 0) {
168 int low = sysctl_local_port_range[0];
169 int high = sysctl_local_port_range[1];
170 int range = high - low;
171 int i;
172 int port;
173 static u32 hint;
174 u32 offset = hint + dccp_v6_port_offset(sk);
175 struct hlist_node *node;
176 struct inet_timewait_sock *tw = NULL;
177
178 local_bh_disable();
179 for (i = 1; i <= range; i++) {
180 port = low + (i + offset) % range;
181 head = &dccp_hashinfo.bhash[inet_bhashfn(port,
182 dccp_hashinfo.bhash_size)];
183 spin_lock(&head->lock);
184
185 /* Does not bother with rcv_saddr checks,
186 * because the established check is already
187 * unique enough.
188 */
189 inet_bind_bucket_for_each(tb, node, &head->chain) {
190 if (tb->port == port) {
191 BUG_TRAP(!hlist_empty(&tb->owners));
192 if (tb->fastreuse >= 0)
193 goto next_port;
194 if (!__dccp_v6_check_established(sk,
195 port,
196 &tw))
197 goto ok;
198 goto next_port;
199 }
200 }
201
202 tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
203 head, port);
204 if (!tb) {
205 spin_unlock(&head->lock);
206 break;
207 }
208 tb->fastreuse = -1;
209 goto ok;
210
211 next_port:
212 spin_unlock(&head->lock);
213 }
214 local_bh_enable();
215
216 return -EADDRNOTAVAIL;
217ok:
218 hint += i;
219
220 /* Head lock still held and bh's disabled */
221 inet_bind_hash(sk, tb, port);
222 if (sk_unhashed(sk)) {
223 inet_sk(sk)->sport = htons(port);
224 __inet6_hash(&dccp_hashinfo, sk);
225 }
226 spin_unlock(&head->lock);
227
228 if (tw) {
229 inet_twsk_deschedule(tw, &dccp_death_row);
230 inet_twsk_put(tw);
231 }
232
233 ret = 0;
234 goto out;
235 }
236
237 head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
238 dccp_hashinfo.bhash_size)];
239 tb = inet_csk(sk)->icsk_bind_hash;
240 spin_lock_bh(&head->lock);
241
242 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
243 __inet6_hash(&dccp_hashinfo, sk);
244 spin_unlock_bh(&head->lock);
245 return 0;
246 } else {
247 spin_unlock(&head->lock);
248 /* No definite answer... Walk to established hash table */
249 ret = __dccp_v6_check_established(sk, snum, NULL);
250out:
251 local_bh_enable();
252 return ret;
253 }
254}
255
256static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 87static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
257 int addr_len) 88 int addr_len)
258{ 89{
@@ -403,7 +234,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
403 inet->dport = usin->sin6_port; 234 inet->dport = usin->sin6_port;
404 235
405 dccp_set_state(sk, DCCP_REQUESTING); 236 dccp_set_state(sk, DCCP_REQUESTING);
406 err = dccp_v6_hash_connect(sk); 237 err = inet6_hash_connect(&dccp_death_row, sk);
407 if (err) 238 if (err)
408 goto late_failure; 239 goto late_failure;
409 /* FIXME */ 240 /* FIXME */