diff options
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r-- | net/sctp/ipv6.c | 213 |
1 files changed, 121 insertions, 92 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 95e0c8eda1a0..0bb0d7cb9f10 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c | |||
@@ -80,6 +80,13 @@ | |||
80 | 80 | ||
81 | #include <asm/uaccess.h> | 81 | #include <asm/uaccess.h> |
82 | 82 | ||
83 | static inline int sctp_v6_addr_match_len(union sctp_addr *s1, | ||
84 | union sctp_addr *s2); | ||
85 | static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, | ||
86 | __be16 port); | ||
87 | static int sctp_v6_cmp_addr(const union sctp_addr *addr1, | ||
88 | const union sctp_addr *addr2); | ||
89 | |||
83 | /* Event handler for inet6 address addition/deletion events. | 90 | /* Event handler for inet6 address addition/deletion events. |
84 | * 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 |
85 | * multiple notifiers (say IPv4 and IPv6) may be running at the same | 92 | * multiple notifiers (say IPv4 and IPv6) may be running at the same |
@@ -123,7 +130,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, | |||
123 | } | 130 | } |
124 | spin_unlock_bh(&sctp_local_addr_lock); | 131 | spin_unlock_bh(&sctp_local_addr_lock); |
125 | if (found) | 132 | if (found) |
126 | call_rcu(&addr->rcu, sctp_local_addr_free); | 133 | kfree_rcu(addr, rcu); |
127 | break; | 134 | break; |
128 | } | 135 | } |
129 | 136 | ||
@@ -201,76 +208,146 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) | |||
201 | { | 208 | { |
202 | struct sock *sk = skb->sk; | 209 | struct sock *sk = skb->sk; |
203 | struct ipv6_pinfo *np = inet6_sk(sk); | 210 | struct ipv6_pinfo *np = inet6_sk(sk); |
204 | struct flowi fl; | 211 | struct flowi6 fl6; |
205 | 212 | ||
206 | memset(&fl, 0, sizeof(fl)); | 213 | memset(&fl6, 0, sizeof(fl6)); |
207 | 214 | ||
208 | fl.proto = sk->sk_protocol; | 215 | fl6.flowi6_proto = sk->sk_protocol; |
209 | 216 | ||
210 | /* Fill in the dest address from the route entry passed with the skb | 217 | /* Fill in the dest address from the route entry passed with the skb |
211 | * and the source address from the transport. | 218 | * and the source address from the transport. |
212 | */ | 219 | */ |
213 | ipv6_addr_copy(&fl.fl6_dst, &transport->ipaddr.v6.sin6_addr); | 220 | ipv6_addr_copy(&fl6.daddr, &transport->ipaddr.v6.sin6_addr); |
214 | ipv6_addr_copy(&fl.fl6_src, &transport->saddr.v6.sin6_addr); | 221 | ipv6_addr_copy(&fl6.saddr, &transport->saddr.v6.sin6_addr); |
215 | 222 | ||
216 | fl.fl6_flowlabel = np->flow_label; | 223 | fl6.flowlabel = np->flow_label; |
217 | IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); | 224 | IP6_ECN_flow_xmit(sk, fl6.flowlabel); |
218 | if (ipv6_addr_type(&fl.fl6_src) & IPV6_ADDR_LINKLOCAL) | 225 | if (ipv6_addr_type(&fl6.saddr) & IPV6_ADDR_LINKLOCAL) |
219 | fl.oif = transport->saddr.v6.sin6_scope_id; | 226 | fl6.flowi6_oif = transport->saddr.v6.sin6_scope_id; |
220 | else | 227 | else |
221 | fl.oif = sk->sk_bound_dev_if; | 228 | fl6.flowi6_oif = sk->sk_bound_dev_if; |
222 | 229 | ||
223 | if (np->opt && np->opt->srcrt) { | 230 | if (np->opt && np->opt->srcrt) { |
224 | struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; | 231 | struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; |
225 | ipv6_addr_copy(&fl.fl6_dst, rt0->addr); | 232 | ipv6_addr_copy(&fl6.daddr, rt0->addr); |
226 | } | 233 | } |
227 | 234 | ||
228 | SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", | 235 | SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", |
229 | __func__, skb, skb->len, | 236 | __func__, skb, skb->len, |
230 | &fl.fl6_src, &fl.fl6_dst); | 237 | &fl6.saddr, &fl6.daddr); |
231 | 238 | ||
232 | SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); | 239 | SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); |
233 | 240 | ||
234 | if (!(transport->param_flags & SPP_PMTUD_ENABLE)) | 241 | if (!(transport->param_flags & SPP_PMTUD_ENABLE)) |
235 | skb->local_df = 1; | 242 | skb->local_df = 1; |
236 | 243 | ||
237 | return ip6_xmit(sk, skb, &fl, np->opt); | 244 | return ip6_xmit(sk, skb, &fl6, np->opt); |
238 | } | 245 | } |
239 | 246 | ||
240 | /* Returns the dst cache entry for the given source and destination ip | 247 | /* Returns the dst cache entry for the given source and destination ip |
241 | * addresses. | 248 | * addresses. |
242 | */ | 249 | */ |
243 | static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, | 250 | static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, |
244 | union sctp_addr *daddr, | 251 | struct flowi *fl, struct sock *sk) |
245 | union sctp_addr *saddr) | ||
246 | { | 252 | { |
247 | struct dst_entry *dst; | 253 | struct sctp_association *asoc = t->asoc; |
248 | struct flowi fl; | 254 | struct dst_entry *dst = NULL; |
255 | struct flowi6 *fl6 = &fl->u.ip6; | ||
256 | struct sctp_bind_addr *bp; | ||
257 | struct sctp_sockaddr_entry *laddr; | ||
258 | union sctp_addr *baddr = NULL; | ||
259 | union sctp_addr *daddr = &t->ipaddr; | ||
260 | union sctp_addr dst_saddr; | ||
261 | __u8 matchlen = 0; | ||
262 | __u8 bmatchlen; | ||
263 | sctp_scope_t scope; | ||
249 | 264 | ||
250 | memset(&fl, 0, sizeof(fl)); | 265 | memset(fl6, 0, sizeof(struct flowi6)); |
251 | ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr); | 266 | ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr); |
267 | fl6->fl6_dport = daddr->v6.sin6_port; | ||
268 | fl6->flowi6_proto = IPPROTO_SCTP; | ||
252 | if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) | 269 | if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) |
253 | fl.oif = daddr->v6.sin6_scope_id; | 270 | fl6->flowi6_oif = daddr->v6.sin6_scope_id; |
254 | 271 | ||
272 | SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr); | ||
255 | 273 | ||
256 | SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl.fl6_dst); | 274 | if (asoc) |
275 | fl6->fl6_sport = htons(asoc->base.bind_addr.port); | ||
257 | 276 | ||
258 | if (saddr) { | 277 | if (saddr) { |
259 | ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr); | 278 | ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr); |
260 | SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl.fl6_src); | 279 | fl6->fl6_sport = saddr->v6.sin6_port; |
280 | SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); | ||
261 | } | 281 | } |
262 | 282 | ||
263 | dst = ip6_route_output(&init_net, NULL, &fl); | 283 | dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); |
264 | if (!dst->error) { | 284 | if (!asoc || saddr) |
285 | goto out; | ||
286 | |||
287 | bp = &asoc->base.bind_addr; | ||
288 | scope = sctp_scope(daddr); | ||
289 | /* ip6_dst_lookup has filled in the fl6->saddr for us. Check | ||
290 | * to see if we can use it. | ||
291 | */ | ||
292 | if (!IS_ERR(dst)) { | ||
293 | /* Walk through the bind address list and look for a bind | ||
294 | * address that matches the source address of the returned dst. | ||
295 | */ | ||
296 | sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port)); | ||
297 | rcu_read_lock(); | ||
298 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { | ||
299 | if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) | ||
300 | continue; | ||
301 | |||
302 | /* Do not compare against v4 addrs */ | ||
303 | if ((laddr->a.sa.sa_family == AF_INET6) && | ||
304 | (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { | ||
305 | rcu_read_unlock(); | ||
306 | goto out; | ||
307 | } | ||
308 | } | ||
309 | rcu_read_unlock(); | ||
310 | /* None of the bound addresses match the source address of the | ||
311 | * dst. So release it. | ||
312 | */ | ||
313 | dst_release(dst); | ||
314 | dst = NULL; | ||
315 | } | ||
316 | |||
317 | /* Walk through the bind address list and try to get the | ||
318 | * best source address for a given destination. | ||
319 | */ | ||
320 | rcu_read_lock(); | ||
321 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { | ||
322 | if (!laddr->valid && laddr->state != SCTP_ADDR_SRC) | ||
323 | continue; | ||
324 | if ((laddr->a.sa.sa_family == AF_INET6) && | ||
325 | (scope <= sctp_scope(&laddr->a))) { | ||
326 | bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); | ||
327 | if (!baddr || (matchlen < bmatchlen)) { | ||
328 | baddr = &laddr->a; | ||
329 | matchlen = bmatchlen; | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | rcu_read_unlock(); | ||
334 | if (baddr) { | ||
335 | ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr); | ||
336 | fl6->fl6_sport = baddr->v6.sin6_port; | ||
337 | dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); | ||
338 | } | ||
339 | |||
340 | out: | ||
341 | if (!IS_ERR(dst)) { | ||
265 | struct rt6_info *rt; | 342 | struct rt6_info *rt; |
266 | rt = (struct rt6_info *)dst; | 343 | rt = (struct rt6_info *)dst; |
344 | t->dst = dst; | ||
267 | SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", | 345 | SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", |
268 | &rt->rt6i_dst.addr, &rt->rt6i_src.addr); | 346 | &rt->rt6i_dst.addr, &fl6->saddr); |
269 | return dst; | 347 | } else { |
348 | t->dst = NULL; | ||
349 | SCTP_DEBUG_PRINTK("NO ROUTE\n"); | ||
270 | } | 350 | } |
271 | SCTP_DEBUG_PRINTK("NO ROUTE\n"); | ||
272 | dst_release(dst); | ||
273 | return NULL; | ||
274 | } | 351 | } |
275 | 352 | ||
276 | /* Returns the number of consecutive initial bits that match in the 2 ipv6 | 353 | /* Returns the number of consecutive initial bits that match in the 2 ipv6 |
@@ -286,64 +363,18 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, | |||
286 | * and asoc's bind address list. | 363 | * and asoc's bind address list. |
287 | */ | 364 | */ |
288 | static void sctp_v6_get_saddr(struct sctp_sock *sk, | 365 | static void sctp_v6_get_saddr(struct sctp_sock *sk, |
289 | struct sctp_association *asoc, | 366 | struct sctp_transport *t, |
290 | struct dst_entry *dst, | 367 | struct flowi *fl) |
291 | union sctp_addr *daddr, | ||
292 | union sctp_addr *saddr) | ||
293 | { | 368 | { |
294 | struct sctp_bind_addr *bp; | 369 | struct flowi6 *fl6 = &fl->u.ip6; |
295 | struct sctp_sockaddr_entry *laddr; | 370 | union sctp_addr *saddr = &t->saddr; |
296 | sctp_scope_t scope; | ||
297 | union sctp_addr *baddr = NULL; | ||
298 | __u8 matchlen = 0; | ||
299 | __u8 bmatchlen; | ||
300 | |||
301 | SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", | ||
302 | __func__, asoc, dst, &daddr->v6.sin6_addr); | ||
303 | |||
304 | if (!asoc) { | ||
305 | ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)), | ||
306 | dst ? ip6_dst_idev(dst)->dev : NULL, | ||
307 | &daddr->v6.sin6_addr, | ||
308 | inet6_sk(&sk->inet.sk)->srcprefs, | ||
309 | &saddr->v6.sin6_addr); | ||
310 | SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n", | ||
311 | &saddr->v6.sin6_addr); | ||
312 | return; | ||
313 | } | ||
314 | |||
315 | scope = sctp_scope(daddr); | ||
316 | |||
317 | bp = &asoc->base.bind_addr; | ||
318 | 371 | ||
319 | /* Go through the bind address list and find the best source address | 372 | SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); |
320 | * that matches the scope of the destination address. | ||
321 | */ | ||
322 | rcu_read_lock(); | ||
323 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { | ||
324 | if (!laddr->valid) | ||
325 | continue; | ||
326 | if ((laddr->state == SCTP_ADDR_SRC) && | ||
327 | (laddr->a.sa.sa_family == AF_INET6) && | ||
328 | (scope <= sctp_scope(&laddr->a))) { | ||
329 | bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); | ||
330 | if (!baddr || (matchlen < bmatchlen)) { | ||
331 | baddr = &laddr->a; | ||
332 | matchlen = bmatchlen; | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | 373 | ||
337 | if (baddr) { | 374 | if (t->dst) { |
338 | memcpy(saddr, baddr, sizeof(union sctp_addr)); | 375 | saddr->v6.sin6_family = AF_INET6; |
339 | SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); | 376 | ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr); |
340 | } else { | ||
341 | pr_err("%s: asoc:%p Could not find a valid source " | ||
342 | "address for the dest:%pI6\n", | ||
343 | __func__, asoc, &daddr->v6.sin6_addr); | ||
344 | } | 377 | } |
345 | |||
346 | rcu_read_unlock(); | ||
347 | } | 378 | } |
348 | 379 | ||
349 | /* Make a copy of all potential local addresses. */ | 380 | /* Make a copy of all potential local addresses. */ |
@@ -465,14 +496,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr, | |||
465 | return length; | 496 | return length; |
466 | } | 497 | } |
467 | 498 | ||
468 | /* Initialize a sctp_addr from a dst_entry. */ | 499 | /* Initialize a sctp_addr from struct in6_addr. */ |
469 | static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, | 500 | static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, |
470 | __be16 port) | 501 | __be16 port) |
471 | { | 502 | { |
472 | struct rt6_info *rt = (struct rt6_info *)dst; | ||
473 | addr->sa.sa_family = AF_INET6; | 503 | addr->sa.sa_family = AF_INET6; |
474 | addr->v6.sin6_port = port; | 504 | addr->v6.sin6_port = port; |
475 | ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); | 505 | ipv6_addr_copy(&addr->v6.sin6_addr, saddr); |
476 | } | 506 | } |
477 | 507 | ||
478 | /* Compare addresses exactly. | 508 | /* Compare addresses exactly. |
@@ -531,7 +561,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr) | |||
531 | static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) | 561 | static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) |
532 | { | 562 | { |
533 | int type; | 563 | int type; |
534 | struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; | 564 | const struct in6_addr *in6 = (const struct in6_addr *)&addr->v6.sin6_addr; |
535 | 565 | ||
536 | type = ipv6_addr_type(in6); | 566 | type = ipv6_addr_type(in6); |
537 | if (IPV6_ADDR_ANY == type) | 567 | if (IPV6_ADDR_ANY == type) |
@@ -959,7 +989,6 @@ static struct sctp_af sctp_af_inet6 = { | |||
959 | .to_sk_daddr = sctp_v6_to_sk_daddr, | 989 | .to_sk_daddr = sctp_v6_to_sk_daddr, |
960 | .from_addr_param = sctp_v6_from_addr_param, | 990 | .from_addr_param = sctp_v6_from_addr_param, |
961 | .to_addr_param = sctp_v6_to_addr_param, | 991 | .to_addr_param = sctp_v6_to_addr_param, |
962 | .dst_saddr = sctp_v6_dst_saddr, | ||
963 | .cmp_addr = sctp_v6_cmp_addr, | 992 | .cmp_addr = sctp_v6_cmp_addr, |
964 | .scope = sctp_v6_scope, | 993 | .scope = sctp_v6_scope, |
965 | .addr_valid = sctp_v6_addr_valid, | 994 | .addr_valid = sctp_v6_addr_valid, |