aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/ipv6.c
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2011-04-26 17:51:31 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-27 16:14:04 -0400
commit9914ae3ca770389a3bec3114d0a07532a7f235dd (patch)
tree269120740aa1afdb0dd792284341aaee93024f28 /net/sctp/ipv6.c
parent625034113bd45c71fb9e329f52f25fef9e6993a3 (diff)
sctp: cache the ipv6 source after route lookup
The ipv6 routing lookup does give us a source address, but instead of filling it into the dst, it's stored in the flowi. We can use that instead of going through the entire source address selection again. Also the useless ->dst_saddr member of sctp_pf is removed. And sctp_v6_dst_saddr() is removed, instead by introduce sctp_v6_to_addr(), which can be reused to cleanup some dup code. Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r--net/sctp/ipv6.c161
1 files changed, 73 insertions, 88 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 3a571d6614f9..51c048d256f5 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -82,6 +82,10 @@
82 82
83static inline int sctp_v6_addr_match_len(union sctp_addr *s1, 83static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
84 union sctp_addr *s2); 84 union sctp_addr *s2);
85static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,
86 __be16 port);
87static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
88 const union sctp_addr *addr2);
85 89
86/* Event handler for inet6 address addition/deletion events. 90/* Event handler for inet6 address addition/deletion events.
87 * The sctp_local_addr_list needs to be protocted by a spin lock since 91 * The sctp_local_addr_list needs to be protocted by a spin lock since
@@ -245,73 +249,99 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
245 */ 249 */
246static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, 250static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
247 union sctp_addr *daddr, 251 union sctp_addr *daddr,
248 union sctp_addr *saddr) 252 union sctp_addr *saddr,
253 struct flowi *fl,
254 struct sock *sk)
249{ 255{
250 struct dst_entry *dst = NULL; 256 struct dst_entry *dst = NULL;
251 struct flowi6 fl6; 257 struct flowi6 *fl6 = &fl->u.ip6;
252 struct sctp_bind_addr *bp; 258 struct sctp_bind_addr *bp;
253 struct sctp_sockaddr_entry *laddr; 259 struct sctp_sockaddr_entry *laddr;
254 union sctp_addr *baddr = NULL; 260 union sctp_addr *baddr = NULL;
261 union sctp_addr dst_saddr;
255 __u8 matchlen = 0; 262 __u8 matchlen = 0;
256 __u8 bmatchlen; 263 __u8 bmatchlen;
257 sctp_scope_t scope; 264 sctp_scope_t scope;
265 int err = 0;
258 266
259 memset(&fl6, 0, sizeof(fl6)); 267 memset(fl6, 0, sizeof(struct flowi6));
260 ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr); 268 ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
261 if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) 269 if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
262 fl6.flowi6_oif = daddr->v6.sin6_scope_id; 270 fl6->flowi6_oif = daddr->v6.sin6_scope_id;
263 271
264 272
265 SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6.daddr); 273 SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
266 274
267 if (saddr) { 275 if (saddr) {
268 ipv6_addr_copy(&fl6.saddr, &saddr->v6.sin6_addr); 276 ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr);
269 SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6.saddr); 277 SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
270 } 278 }
271 279
272 dst = ip6_route_output(&init_net, NULL, &fl6); 280 err = ip6_dst_lookup(sk, &dst, fl6);
273 if (!asoc || saddr) 281 if (!asoc || saddr)
274 goto out; 282 goto out;
275 283
276 if (dst->error) { 284 bp = &asoc->base.bind_addr;
277 dst_release(dst); 285 scope = sctp_scope(daddr);
278 dst = NULL; 286 /* ip6_dst_lookup has filled in the fl6->saddr for us. Check
279 bp = &asoc->base.bind_addr; 287 * to see if we can use it.
280 scope = sctp_scope(daddr); 288 */
281 /* Walk through the bind address list and try to get a dst that 289 if (!err) {
282 * matches a bind address as the source address. 290 /* Walk through the bind address list and look for a bind
291 * address that matches the source address of the returned dst.
283 */ 292 */
293 sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port));
284 rcu_read_lock(); 294 rcu_read_lock();
285 list_for_each_entry_rcu(laddr, &bp->address_list, list) { 295 list_for_each_entry_rcu(laddr, &bp->address_list, list) {
286 if (!laddr->valid) 296 if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
287 continue; 297 continue;
288 if ((laddr->state == SCTP_ADDR_SRC) && 298
289 (laddr->a.sa.sa_family == AF_INET6) && 299 /* Do not compare against v4 addrs */
290 (scope <= sctp_scope(&laddr->a))) { 300 if ((laddr->a.sa.sa_family == AF_INET6) &&
291 bmatchlen = sctp_v6_addr_match_len(daddr, 301 (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) {
292 &laddr->a); 302 rcu_read_unlock();
293 if (!baddr || (matchlen < bmatchlen)) { 303 goto out;
294 baddr = &laddr->a;
295 matchlen = bmatchlen;
296 }
297 } 304 }
298 } 305 }
299 rcu_read_unlock(); 306 rcu_read_unlock();
300 if (baddr) { 307 /* None of the bound addresses match the source address of the
301 ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr); 308 * dst. So release it.
302 dst = ip6_route_output(&init_net, NULL, &fl6); 309 */
310 dst_release(dst);
311 dst = NULL;
312 }
313
314 /* Walk through the bind address list and try to get the
315 * best source address for a given destination.
316 */
317 rcu_read_lock();
318 list_for_each_entry_rcu(laddr, &bp->address_list, list) {
319 if (!laddr->valid && laddr->state != SCTP_ADDR_SRC)
320 continue;
321 if ((laddr->a.sa.sa_family == AF_INET6) &&
322 (scope <= sctp_scope(&laddr->a))) {
323 bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
324 if (!baddr || (matchlen < bmatchlen)) {
325 baddr = &laddr->a;
326 matchlen = bmatchlen;
327 }
303 } 328 }
304 } 329 }
330 rcu_read_unlock();
331 if (baddr) {
332 ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
333 err = ip6_dst_lookup(sk, &dst, fl6);
334 }
335
305out: 336out:
306 if (!dst->error) { 337 if (!err) {
307 struct rt6_info *rt; 338 struct rt6_info *rt;
308 rt = (struct rt6_info *)dst; 339 rt = (struct rt6_info *)dst;
309 SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", 340 SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
310 &rt->rt6i_dst.addr, &rt->rt6i_src.addr); 341 &rt->rt6i_dst.addr, &fl6->saddr);
311 return dst; 342 return dst;
312 } 343 }
313 SCTP_DEBUG_PRINTK("NO ROUTE\n"); 344 SCTP_DEBUG_PRINTK("NO ROUTE\n");
314 dst_release(dst);
315 return NULL; 345 return NULL;
316} 346}
317 347
@@ -328,64 +358,21 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
328 * and asoc's bind address list. 358 * and asoc's bind address list.
329 */ 359 */
330static void sctp_v6_get_saddr(struct sctp_sock *sk, 360static void sctp_v6_get_saddr(struct sctp_sock *sk,
331 struct sctp_association *asoc, 361 struct sctp_transport *t,
332 struct dst_entry *dst,
333 union sctp_addr *daddr, 362 union sctp_addr *daddr,
334 union sctp_addr *saddr) 363 struct flowi *fl)
335{ 364{
336 struct sctp_bind_addr *bp; 365 struct flowi6 *fl6 = &fl->u.ip6;
337 struct sctp_sockaddr_entry *laddr; 366 union sctp_addr *saddr = &t->saddr;
338 sctp_scope_t scope;
339 union sctp_addr *baddr = NULL;
340 __u8 matchlen = 0;
341 __u8 bmatchlen;
342 367
343 SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", 368 SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ",
344 __func__, asoc, dst, &daddr->v6.sin6_addr); 369 __func__, t->asoc, t->dst, &daddr->v6.sin6_addr);
345
346 if (!asoc) {
347 ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)),
348 dst ? ip6_dst_idev(dst)->dev : NULL,
349 &daddr->v6.sin6_addr,
350 inet6_sk(&sk->inet.sk)->srcprefs,
351 &saddr->v6.sin6_addr);
352 SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n",
353 &saddr->v6.sin6_addr);
354 return;
355 }
356
357 scope = sctp_scope(daddr);
358 370
359 bp = &asoc->base.bind_addr;
360
361 /* Go through the bind address list and find the best source address
362 * that matches the scope of the destination address.
363 */
364 rcu_read_lock();
365 list_for_each_entry_rcu(laddr, &bp->address_list, list) {
366 if (!laddr->valid)
367 continue;
368 if ((laddr->state == SCTP_ADDR_SRC) &&
369 (laddr->a.sa.sa_family == AF_INET6) &&
370 (scope <= sctp_scope(&laddr->a))) {
371 bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
372 if (!baddr || (matchlen < bmatchlen)) {
373 baddr = &laddr->a;
374 matchlen = bmatchlen;
375 }
376 }
377 }
378 371
379 if (baddr) { 372 if (t->dst) {
380 memcpy(saddr, baddr, sizeof(union sctp_addr)); 373 saddr->v6.sin6_family = AF_INET6;
381 SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); 374 ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr);
382 } else {
383 pr_err("%s: asoc:%p Could not find a valid source "
384 "address for the dest:%pI6\n",
385 __func__, asoc, &daddr->v6.sin6_addr);
386 } 375 }
387
388 rcu_read_unlock();
389} 376}
390 377
391/* Make a copy of all potential local addresses. */ 378/* Make a copy of all potential local addresses. */
@@ -507,14 +494,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr,
507 return length; 494 return length;
508} 495}
509 496
510/* Initialize a sctp_addr from a dst_entry. */ 497/* Initialize a sctp_addr from struct in6_addr. */
511static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, 498static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr,
512 __be16 port) 499 __be16 port)
513{ 500{
514 struct rt6_info *rt = (struct rt6_info *)dst;
515 addr->sa.sa_family = AF_INET6; 501 addr->sa.sa_family = AF_INET6;
516 addr->v6.sin6_port = port; 502 addr->v6.sin6_port = port;
517 ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); 503 ipv6_addr_copy(&addr->v6.sin6_addr, saddr);
518} 504}
519 505
520/* Compare addresses exactly. 506/* Compare addresses exactly.
@@ -1001,7 +987,6 @@ static struct sctp_af sctp_af_inet6 = {
1001 .to_sk_daddr = sctp_v6_to_sk_daddr, 987 .to_sk_daddr = sctp_v6_to_sk_daddr,
1002 .from_addr_param = sctp_v6_from_addr_param, 988 .from_addr_param = sctp_v6_from_addr_param,
1003 .to_addr_param = sctp_v6_to_addr_param, 989 .to_addr_param = sctp_v6_to_addr_param,
1004 .dst_saddr = sctp_v6_dst_saddr,
1005 .cmp_addr = sctp_v6_cmp_addr, 990 .cmp_addr = sctp_v6_cmp_addr,
1006 .scope = sctp_v6_scope, 991 .scope = sctp_v6_scope,
1007 .addr_valid = sctp_v6_addr_valid, 992 .addr_valid = sctp_v6_addr_valid,