aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
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
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')
-rw-r--r--net/sctp/ipv6.c161
-rw-r--r--net/sctp/protocol.c47
-rw-r--r--net/sctp/socket.c2
-rw-r--r--net/sctp/transport.c15
4 files changed, 106 insertions, 119 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,
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d04f63..34216458ded1 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -465,33 +465,35 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
465 */ 465 */
466static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, 466static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
467 union sctp_addr *daddr, 467 union sctp_addr *daddr,
468 union sctp_addr *saddr) 468 union sctp_addr *saddr,
469 struct flowi *fl,
470 struct sock *sk)
469{ 471{
470 struct rtable *rt; 472 struct rtable *rt;
471 struct flowi4 fl4; 473 struct flowi4 *fl4 = &fl->u.ip4;
472 struct sctp_bind_addr *bp; 474 struct sctp_bind_addr *bp;
473 struct sctp_sockaddr_entry *laddr; 475 struct sctp_sockaddr_entry *laddr;
474 struct dst_entry *dst = NULL; 476 struct dst_entry *dst = NULL;
475 union sctp_addr dst_saddr; 477 union sctp_addr dst_saddr;
476 478
477 memset(&fl4, 0x0, sizeof(struct flowi4)); 479 memset(fl4, 0x0, sizeof(struct flowi4));
478 fl4.daddr = daddr->v4.sin_addr.s_addr; 480 fl4->daddr = daddr->v4.sin_addr.s_addr;
479 fl4.fl4_dport = daddr->v4.sin_port; 481 fl4->fl4_dport = daddr->v4.sin_port;
480 fl4.flowi4_proto = IPPROTO_SCTP; 482 fl4->flowi4_proto = IPPROTO_SCTP;
481 if (asoc) { 483 if (asoc) {
482 fl4.flowi4_tos = RT_CONN_FLAGS(asoc->base.sk); 484 fl4->flowi4_tos = RT_CONN_FLAGS(asoc->base.sk);
483 fl4.flowi4_oif = asoc->base.sk->sk_bound_dev_if; 485 fl4->flowi4_oif = asoc->base.sk->sk_bound_dev_if;
484 fl4.fl4_sport = htons(asoc->base.bind_addr.port); 486 fl4->fl4_sport = htons(asoc->base.bind_addr.port);
485 } 487 }
486 if (saddr) { 488 if (saddr) {
487 fl4.saddr = saddr->v4.sin_addr.s_addr; 489 fl4->saddr = saddr->v4.sin_addr.s_addr;
488 fl4.fl4_sport = saddr->v4.sin_port; 490 fl4->fl4_sport = saddr->v4.sin_port;
489 } 491 }
490 492
491 SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", 493 SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
492 __func__, &fl4.daddr, &fl4.saddr); 494 __func__, &fl4->daddr, &fl4->saddr);
493 495
494 rt = ip_route_output_key(&init_net, &fl4); 496 rt = ip_route_output_key(&init_net, fl4);
495 if (!IS_ERR(rt)) 497 if (!IS_ERR(rt))
496 dst = &rt->dst; 498 dst = &rt->dst;
497 499
@@ -533,9 +535,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
533 continue; 535 continue;
534 if ((laddr->state == SCTP_ADDR_SRC) && 536 if ((laddr->state == SCTP_ADDR_SRC) &&
535 (AF_INET == laddr->a.sa.sa_family)) { 537 (AF_INET == laddr->a.sa.sa_family)) {
536 fl4.saddr = laddr->a.v4.sin_addr.s_addr; 538 fl4->saddr = laddr->a.v4.sin_addr.s_addr;
537 fl4.fl4_sport = laddr->a.v4.sin_port; 539 fl4->fl4_sport = laddr->a.v4.sin_port;
538 rt = ip_route_output_key(&init_net, &fl4); 540 rt = ip_route_output_key(&init_net, fl4);
539 if (!IS_ERR(rt)) { 541 if (!IS_ERR(rt)) {
540 dst = &rt->dst; 542 dst = &rt->dst;
541 goto out_unlock; 543 goto out_unlock;
@@ -559,19 +561,15 @@ out:
559 * to cache it separately and hence this is an empty routine. 561 * to cache it separately and hence this is an empty routine.
560 */ 562 */
561static void sctp_v4_get_saddr(struct sctp_sock *sk, 563static void sctp_v4_get_saddr(struct sctp_sock *sk,
562 struct sctp_association *asoc, 564 struct sctp_transport *t,
563 struct dst_entry *dst,
564 union sctp_addr *daddr, 565 union sctp_addr *daddr,
565 union sctp_addr *saddr) 566 struct flowi *fl)
566{ 567{
567 struct rtable *rt = (struct rtable *)dst; 568 union sctp_addr *saddr = &t->saddr;
568 569 struct rtable *rt = (struct rtable *)t->dst;
569 if (!asoc)
570 return;
571 570
572 if (rt) { 571 if (rt) {
573 saddr->v4.sin_family = AF_INET; 572 saddr->v4.sin_family = AF_INET;
574 saddr->v4.sin_port = htons(asoc->base.bind_addr.port);
575 saddr->v4.sin_addr.s_addr = rt->rt_src; 573 saddr->v4.sin_addr.s_addr = rt->rt_src;
576 } 574 }
577} 575}
@@ -950,7 +948,6 @@ static struct sctp_af sctp_af_inet = {
950 .to_sk_daddr = sctp_v4_to_sk_daddr, 948 .to_sk_daddr = sctp_v4_to_sk_daddr,
951 .from_addr_param = sctp_v4_from_addr_param, 949 .from_addr_param = sctp_v4_from_addr_param,
952 .to_addr_param = sctp_v4_to_addr_param, 950 .to_addr_param = sctp_v4_to_addr_param,
953 .dst_saddr = sctp_v4_dst_saddr,
954 .cmp_addr = sctp_v4_cmp_addr, 951 .cmp_addr = sctp_v4_cmp_addr,
955 .addr_valid = sctp_v4_addr_valid, 952 .addr_valid = sctp_v4_addr_valid,
956 .inaddr_any = sctp_v4_inaddr_any, 953 .inaddr_any = sctp_v4_inaddr_any,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee116746..33d9ee629b4e 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2287,7 +2287,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
2287 trans->param_flags = 2287 trans->param_flags =
2288 (trans->param_flags & ~SPP_PMTUD) | pmtud_change; 2288 (trans->param_flags & ~SPP_PMTUD) | pmtud_change;
2289 if (update) { 2289 if (update) {
2290 sctp_transport_pmtu(trans); 2290 sctp_transport_pmtu(trans, sctp_opt2sk(sp));
2291 sctp_assoc_sync_pmtu(asoc); 2291 sctp_assoc_sync_pmtu(asoc);
2292 } 2292 }
2293 } else if (asoc) { 2293 } else if (asoc) {
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index d3ae493d234a..2544b9b21f86 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -211,11 +211,15 @@ void sctp_transport_set_owner(struct sctp_transport *transport,
211} 211}
212 212
213/* Initialize the pmtu of a transport. */ 213/* Initialize the pmtu of a transport. */
214void sctp_transport_pmtu(struct sctp_transport *transport) 214void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
215{ 215{
216 struct dst_entry *dst; 216 struct dst_entry *dst;
217 struct flowi fl;
217 218
218 dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL); 219 dst = transport->af_specific->get_dst(transport->asoc,
220 &transport->ipaddr,
221 &transport->saddr,
222 &fl, sk);
219 223
220 if (dst) { 224 if (dst) {
221 transport->pathmtu = dst_mtu(dst); 225 transport->pathmtu = dst_mtu(dst);
@@ -272,15 +276,16 @@ void sctp_transport_route(struct sctp_transport *transport,
272 struct sctp_af *af = transport->af_specific; 276 struct sctp_af *af = transport->af_specific;
273 union sctp_addr *daddr = &transport->ipaddr; 277 union sctp_addr *daddr = &transport->ipaddr;
274 struct dst_entry *dst; 278 struct dst_entry *dst;
279 struct flowi fl;
275 280
276 dst = af->get_dst(asoc, daddr, saddr); 281 dst = af->get_dst(asoc, daddr, saddr, &fl, sctp_opt2sk(opt));
282 transport->dst = dst;
277 283
278 if (saddr) 284 if (saddr)
279 memcpy(&transport->saddr, saddr, sizeof(union sctp_addr)); 285 memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
280 else 286 else
281 af->get_saddr(opt, asoc, dst, daddr, &transport->saddr); 287 af->get_saddr(opt, transport, daddr, &fl);
282 288
283 transport->dst = dst;
284 if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) { 289 if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
285 return; 290 return;
286 } 291 }