aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-03-02 01:06:22 -0500
committerDavid S. Miller <davem@davemloft.net>2011-03-02 01:07:37 -0500
commitb42835dbe83d725198c7ab0bbe726d6dfd92a634 (patch)
tree8728dccb3fa9bb54dba53a0a00c6e2818b843a91 /net/ipv6
parentf6d460cf0ed16d35aec48f823685e7a0e0283d84 (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.c117
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)
300static inline void mip6_addr_swap(struct sk_buff *skb) {} 300static inline void mip6_addr_swap(struct sk_buff *skb) {}
301#endif 301#endif
302 302
303static 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
361relookup_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:
455relookup_failed:
456 if (!dst)
457 goto out;
458 break;
459 }
460 476
461route_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