aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sctp/ipv6.c44
1 files changed, 43 insertions, 1 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 321f175055bf..3a571d6614f9 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -80,6 +80,9 @@
80 80
81#include <asm/uaccess.h> 81#include <asm/uaccess.h>
82 82
83static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
84 union sctp_addr *s2);
85
83/* Event handler for inet6 address addition/deletion events. 86/* Event handler for inet6 address addition/deletion events.
84 * The sctp_local_addr_list needs to be protocted by a spin lock since 87 * The sctp_local_addr_list needs to be protocted by a spin lock since
85 * multiple notifiers (say IPv4 and IPv6) may be running at the same 88 * multiple notifiers (say IPv4 and IPv6) may be running at the same
@@ -244,8 +247,14 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
244 union sctp_addr *daddr, 247 union sctp_addr *daddr,
245 union sctp_addr *saddr) 248 union sctp_addr *saddr)
246{ 249{
247 struct dst_entry *dst; 250 struct dst_entry *dst = NULL;
248 struct flowi6 fl6; 251 struct flowi6 fl6;
252 struct sctp_bind_addr *bp;
253 struct sctp_sockaddr_entry *laddr;
254 union sctp_addr *baddr = NULL;
255 __u8 matchlen = 0;
256 __u8 bmatchlen;
257 sctp_scope_t scope;
249 258
250 memset(&fl6, 0, sizeof(fl6)); 259 memset(&fl6, 0, sizeof(fl6));
251 ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr); 260 ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr);
@@ -261,6 +270,39 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
261 } 270 }
262 271
263 dst = ip6_route_output(&init_net, NULL, &fl6); 272 dst = ip6_route_output(&init_net, NULL, &fl6);
273 if (!asoc || saddr)
274 goto out;
275
276 if (dst->error) {
277 dst_release(dst);
278 dst = NULL;
279 bp = &asoc->base.bind_addr;
280 scope = sctp_scope(daddr);
281 /* Walk through the bind address list and try to get a dst that
282 * matches a bind address as the source address.
283 */
284 rcu_read_lock();
285 list_for_each_entry_rcu(laddr, &bp->address_list, list) {
286 if (!laddr->valid)
287 continue;
288 if ((laddr->state == SCTP_ADDR_SRC) &&
289 (laddr->a.sa.sa_family == AF_INET6) &&
290 (scope <= sctp_scope(&laddr->a))) {
291 bmatchlen = sctp_v6_addr_match_len(daddr,
292 &laddr->a);
293 if (!baddr || (matchlen < bmatchlen)) {
294 baddr = &laddr->a;
295 matchlen = bmatchlen;
296 }
297 }
298 }
299 rcu_read_unlock();
300 if (baddr) {
301 ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr);
302 dst = ip6_route_output(&init_net, NULL, &fl6);
303 }
304 }
305out:
264 if (!dst->error) { 306 if (!dst->error) {
265 struct rt6_info *rt; 307 struct rt6_info *rt;
266 rt = (struct rt6_info *)dst; 308 rt = (struct rt6_info *)dst;