diff options
author | David S. Miller <davem@davemloft.net> | 2011-03-02 01:06:22 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-02 01:07:37 -0500 |
commit | b42835dbe83d725198c7ab0bbe726d6dfd92a634 (patch) | |
tree | 8728dccb3fa9bb54dba53a0a00c6e2818b843a91 /net/ipv6 | |
parent | f6d460cf0ed16d35aec48f823685e7a0e0283d84 (diff) |
ipv6: Make icmp route lookup code a bit clearer.
The route lookup code in icmpv6_send() is slightly tricky as a result of
having to handle all of the requirements of RFC 4301 host relookups.
Pull the route resolution into a seperate function, so that the error
handling and route reference counting is hopefully easier to see and
contained wholly within this new routine.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/icmp.c | 117 |
1 files changed, 66 insertions, 51 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index a31d91b04c87..e332bae104ee 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -300,6 +300,70 @@ static void mip6_addr_swap(struct sk_buff *skb) | |||
300 | static inline void mip6_addr_swap(struct sk_buff *skb) {} | 300 | static inline void mip6_addr_swap(struct sk_buff *skb) {} |
301 | #endif | 301 | #endif |
302 | 302 | ||
303 | static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, | ||
304 | struct sock *sk, struct flowi *fl) | ||
305 | { | ||
306 | struct dst_entry *dst, *dst2; | ||
307 | struct flowi fl2; | ||
308 | int err; | ||
309 | |||
310 | err = ip6_dst_lookup(sk, &dst, fl); | ||
311 | if (err) | ||
312 | return ERR_PTR(err); | ||
313 | |||
314 | /* | ||
315 | * We won't send icmp if the destination is known | ||
316 | * anycast. | ||
317 | */ | ||
318 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { | ||
319 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); | ||
320 | dst_release(dst); | ||
321 | return ERR_PTR(-EINVAL); | ||
322 | } | ||
323 | |||
324 | /* No need to clone since we're just using its address. */ | ||
325 | dst2 = dst; | ||
326 | |||
327 | err = xfrm_lookup(net, &dst, fl, sk, 0); | ||
328 | switch (err) { | ||
329 | case 0: | ||
330 | if (dst != dst2) | ||
331 | return dst; | ||
332 | break; | ||
333 | case -EPERM: | ||
334 | dst = NULL; | ||
335 | break; | ||
336 | default: | ||
337 | return ERR_PTR(err); | ||
338 | } | ||
339 | |||
340 | err = xfrm_decode_session_reverse(skb, &fl2, AF_INET6); | ||
341 | if (err) | ||
342 | goto relookup_failed; | ||
343 | |||
344 | err = ip6_dst_lookup(sk, &dst2, &fl2); | ||
345 | if (err) | ||
346 | goto relookup_failed; | ||
347 | |||
348 | err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); | ||
349 | switch (err) { | ||
350 | case 0: | ||
351 | dst_release(dst); | ||
352 | dst = dst2; | ||
353 | break; | ||
354 | case -EPERM: | ||
355 | dst_release(dst); | ||
356 | return ERR_PTR(err); | ||
357 | default: | ||
358 | goto relookup_failed; | ||
359 | } | ||
360 | |||
361 | relookup_failed: | ||
362 | if (dst) | ||
363 | return dst; | ||
364 | return ERR_PTR(err); | ||
365 | } | ||
366 | |||
303 | /* | 367 | /* |
304 | * Send an ICMP message in response to a packet in error | 368 | * Send an ICMP message in response to a packet in error |
305 | */ | 369 | */ |
@@ -312,10 +376,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
312 | struct ipv6_pinfo *np; | 376 | struct ipv6_pinfo *np; |
313 | struct in6_addr *saddr = NULL; | 377 | struct in6_addr *saddr = NULL; |
314 | struct dst_entry *dst; | 378 | struct dst_entry *dst; |
315 | struct dst_entry *dst2; | ||
316 | struct icmp6hdr tmp_hdr; | 379 | struct icmp6hdr tmp_hdr; |
317 | struct flowi fl; | 380 | struct flowi fl; |
318 | struct flowi fl2; | ||
319 | struct icmpv6_msg msg; | 381 | struct icmpv6_msg msg; |
320 | int iif = 0; | 382 | int iif = 0; |
321 | int addr_type = 0; | 383 | int addr_type = 0; |
@@ -408,57 +470,10 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
408 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) | 470 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) |
409 | fl.oif = np->mcast_oif; | 471 | fl.oif = np->mcast_oif; |
410 | 472 | ||
411 | err = ip6_dst_lookup(sk, &dst, &fl); | 473 | dst = icmpv6_route_lookup(net, skb, sk, &fl); |
412 | if (err) | 474 | if (IS_ERR(dst)) |
413 | goto out; | ||
414 | |||
415 | /* | ||
416 | * We won't send icmp if the destination is known | ||
417 | * anycast. | ||
418 | */ | ||
419 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { | ||
420 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); | ||
421 | goto out_dst_release; | ||
422 | } | ||
423 | |||
424 | /* No need to clone since we're just using its address. */ | ||
425 | dst2 = dst; | ||
426 | |||
427 | err = xfrm_lookup(net, &dst, &fl, sk, 0); | ||
428 | switch (err) { | ||
429 | case 0: | ||
430 | if (dst != dst2) | ||
431 | goto route_done; | ||
432 | break; | ||
433 | case -EPERM: | ||
434 | dst = NULL; | ||
435 | break; | ||
436 | default: | ||
437 | goto out; | 475 | goto out; |
438 | } | ||
439 | |||
440 | if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) | ||
441 | goto relookup_failed; | ||
442 | |||
443 | if (ip6_dst_lookup(sk, &dst2, &fl2)) | ||
444 | goto relookup_failed; | ||
445 | |||
446 | err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); | ||
447 | switch (err) { | ||
448 | case 0: | ||
449 | dst_release(dst); | ||
450 | dst = dst2; | ||
451 | break; | ||
452 | case -EPERM: | ||
453 | goto out_dst_release; | ||
454 | default: | ||
455 | relookup_failed: | ||
456 | if (!dst) | ||
457 | goto out; | ||
458 | break; | ||
459 | } | ||
460 | 476 | ||
461 | route_done: | ||
462 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) | 477 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) |
463 | hlimit = np->mcast_hops; | 478 | hlimit = np->mcast_hops; |
464 | else | 479 | else |