diff options
Diffstat (limited to 'net/sctp')
| -rw-r--r-- | net/sctp/ipv6.c | 49 |
1 files changed, 32 insertions, 17 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 961ee59f696a..f5b45b8b8b16 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c | |||
| @@ -240,12 +240,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, | |||
| 240 | struct sctp_bind_addr *bp; | 240 | struct sctp_bind_addr *bp; |
| 241 | struct ipv6_pinfo *np = inet6_sk(sk); | 241 | struct ipv6_pinfo *np = inet6_sk(sk); |
| 242 | struct sctp_sockaddr_entry *laddr; | 242 | struct sctp_sockaddr_entry *laddr; |
| 243 | union sctp_addr *baddr = NULL; | ||
| 244 | union sctp_addr *daddr = &t->ipaddr; | 243 | union sctp_addr *daddr = &t->ipaddr; |
| 245 | union sctp_addr dst_saddr; | 244 | union sctp_addr dst_saddr; |
| 246 | struct in6_addr *final_p, final; | 245 | struct in6_addr *final_p, final; |
| 247 | __u8 matchlen = 0; | 246 | __u8 matchlen = 0; |
| 248 | __u8 bmatchlen; | ||
| 249 | sctp_scope_t scope; | 247 | sctp_scope_t scope; |
| 250 | 248 | ||
| 251 | memset(fl6, 0, sizeof(struct flowi6)); | 249 | memset(fl6, 0, sizeof(struct flowi6)); |
| @@ -312,23 +310,37 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, | |||
| 312 | */ | 310 | */ |
| 313 | rcu_read_lock(); | 311 | rcu_read_lock(); |
| 314 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { | 312 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 315 | if (!laddr->valid) | 313 | struct dst_entry *bdst; |
| 314 | __u8 bmatchlen; | ||
| 315 | |||
| 316 | if (!laddr->valid || | ||
| 317 | laddr->state != SCTP_ADDR_SRC || | ||
| 318 | laddr->a.sa.sa_family != AF_INET6 || | ||
| 319 | scope > sctp_scope(&laddr->a)) | ||
| 316 | continue; | 320 | continue; |
| 317 | if ((laddr->state == SCTP_ADDR_SRC) && | 321 | |
| 318 | (laddr->a.sa.sa_family == AF_INET6) && | 322 | fl6->saddr = laddr->a.v6.sin6_addr; |
| 319 | (scope <= sctp_scope(&laddr->a))) { | 323 | fl6->fl6_sport = laddr->a.v6.sin6_port; |
| 320 | bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); | ||
| 321 | if (!baddr || (matchlen < bmatchlen)) { | ||
| 322 | baddr = &laddr->a; | ||
| 323 | matchlen = bmatchlen; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | if (baddr) { | ||
| 328 | fl6->saddr = baddr->v6.sin6_addr; | ||
| 329 | fl6->fl6_sport = baddr->v6.sin6_port; | ||
| 330 | final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); | 324 | final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); |
| 331 | dst = ip6_dst_lookup_flow(sk, fl6, final_p); | 325 | bdst = ip6_dst_lookup_flow(sk, fl6, final_p); |
| 326 | |||
| 327 | if (!IS_ERR(bdst) && | ||
| 328 | ipv6_chk_addr(dev_net(bdst->dev), | ||
| 329 | &laddr->a.v6.sin6_addr, bdst->dev, 1)) { | ||
| 330 | if (!IS_ERR_OR_NULL(dst)) | ||
| 331 | dst_release(dst); | ||
| 332 | dst = bdst; | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | |||
| 336 | bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); | ||
| 337 | if (matchlen > bmatchlen) | ||
| 338 | continue; | ||
| 339 | |||
| 340 | if (!IS_ERR_OR_NULL(dst)) | ||
| 341 | dst_release(dst); | ||
| 342 | dst = bdst; | ||
| 343 | matchlen = bmatchlen; | ||
| 332 | } | 344 | } |
| 333 | rcu_read_unlock(); | 345 | rcu_read_unlock(); |
| 334 | 346 | ||
| @@ -665,6 +677,9 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, | |||
| 665 | newnp = inet6_sk(newsk); | 677 | newnp = inet6_sk(newsk); |
| 666 | 678 | ||
| 667 | memcpy(newnp, np, sizeof(struct ipv6_pinfo)); | 679 | memcpy(newnp, np, sizeof(struct ipv6_pinfo)); |
| 680 | newnp->ipv6_mc_list = NULL; | ||
| 681 | newnp->ipv6_ac_list = NULL; | ||
| 682 | newnp->ipv6_fl_list = NULL; | ||
| 668 | 683 | ||
| 669 | rcu_read_lock(); | 684 | rcu_read_lock(); |
| 670 | opt = rcu_dereference(np->opt); | 685 | opt = rcu_dereference(np->opt); |
