diff options
author | David S. Miller <davem@davemloft.net> | 2011-03-01 18:49:55 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-01 18:49:55 -0500 |
commit | f6d460cf0ed16d35aec48f823685e7a0e0283d84 (patch) | |
tree | 56c311e236e3856220660afe7eaadf76106e58c6 /net/ipv4/icmp.c | |
parent | 2774c131b1d19920b4587db1cfbd6f0750ad1f15 (diff) |
ipv4: Make icmp route lookup code a bit clearer.
The route lookup code in icmp_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/ipv4/icmp.c')
-rw-r--r-- | net/ipv4/icmp.c | 175 |
1 files changed, 96 insertions, 79 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index ad2bcf1b69ae..2a86c8951dcd 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -369,6 +369,98 @@ out_unlock: | |||
369 | icmp_xmit_unlock(sk); | 369 | icmp_xmit_unlock(sk); |
370 | } | 370 | } |
371 | 371 | ||
372 | static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, | ||
373 | struct iphdr *iph, | ||
374 | __be32 saddr, u8 tos, | ||
375 | int type, int code, | ||
376 | struct icmp_bxm *param) | ||
377 | { | ||
378 | struct flowi fl = { | ||
379 | .fl4_dst = (param->replyopts.srr ? | ||
380 | param->replyopts.faddr : iph->saddr), | ||
381 | .fl4_src = saddr, | ||
382 | .fl4_tos = RT_TOS(tos), | ||
383 | .proto = IPPROTO_ICMP, | ||
384 | .fl_icmp_type = type, | ||
385 | .fl_icmp_code = code, | ||
386 | }; | ||
387 | struct rtable *rt, *rt2; | ||
388 | int err; | ||
389 | |||
390 | security_skb_classify_flow(skb_in, &fl); | ||
391 | err = __ip_route_output_key(net, &rt, &fl); | ||
392 | if (err) | ||
393 | return ERR_PTR(err); | ||
394 | |||
395 | /* No need to clone since we're just using its address. */ | ||
396 | rt2 = rt; | ||
397 | |||
398 | if (!fl.fl4_src) | ||
399 | fl.fl4_src = rt->rt_src; | ||
400 | |||
401 | err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0); | ||
402 | switch (err) { | ||
403 | case 0: | ||
404 | if (rt != rt2) | ||
405 | return rt; | ||
406 | break; | ||
407 | case -EPERM: | ||
408 | rt = NULL; | ||
409 | break; | ||
410 | default: | ||
411 | return ERR_PTR(err); | ||
412 | } | ||
413 | |||
414 | err = xfrm_decode_session_reverse(skb_in, &fl, AF_INET); | ||
415 | if (err) | ||
416 | goto relookup_failed; | ||
417 | |||
418 | if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) { | ||
419 | err = __ip_route_output_key(net, &rt2, &fl); | ||
420 | } else { | ||
421 | struct flowi fl2 = {}; | ||
422 | unsigned long orefdst; | ||
423 | |||
424 | fl2.fl4_dst = fl.fl4_src; | ||
425 | err = ip_route_output_key(net, &rt2, &fl2); | ||
426 | if (err) | ||
427 | goto relookup_failed; | ||
428 | /* Ugh! */ | ||
429 | orefdst = skb_in->_skb_refdst; /* save old refdst */ | ||
430 | err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, | ||
431 | RT_TOS(tos), rt2->dst.dev); | ||
432 | |||
433 | dst_release(&rt2->dst); | ||
434 | rt2 = skb_rtable(skb_in); | ||
435 | skb_in->_skb_refdst = orefdst; /* restore old refdst */ | ||
436 | } | ||
437 | |||
438 | if (err) | ||
439 | goto relookup_failed; | ||
440 | |||
441 | err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL, | ||
442 | XFRM_LOOKUP_ICMP); | ||
443 | switch (err) { | ||
444 | case 0: | ||
445 | dst_release(&rt->dst); | ||
446 | rt = rt2; | ||
447 | break; | ||
448 | case -EPERM: | ||
449 | return ERR_PTR(err); | ||
450 | default: | ||
451 | if (!rt) | ||
452 | return ERR_PTR(err); | ||
453 | break; | ||
454 | } | ||
455 | |||
456 | |||
457 | return rt; | ||
458 | |||
459 | relookup_failed: | ||
460 | if (rt) | ||
461 | return rt; | ||
462 | return ERR_PTR(err); | ||
463 | } | ||
372 | 464 | ||
373 | /* | 465 | /* |
374 | * Send an ICMP message in response to a situation | 466 | * Send an ICMP message in response to a situation |
@@ -506,86 +598,11 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
506 | ipc.opt = &icmp_param.replyopts; | 598 | ipc.opt = &icmp_param.replyopts; |
507 | ipc.tx_flags = 0; | 599 | ipc.tx_flags = 0; |
508 | 600 | ||
509 | { | 601 | rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, |
510 | struct flowi fl = { | 602 | type, code, &icmp_param); |
511 | .fl4_dst = icmp_param.replyopts.srr ? | 603 | if (IS_ERR(rt)) |
512 | icmp_param.replyopts.faddr : iph->saddr, | 604 | goto out_unlock; |
513 | .fl4_src = saddr, | ||
514 | .fl4_tos = RT_TOS(tos), | ||
515 | .proto = IPPROTO_ICMP, | ||
516 | .fl_icmp_type = type, | ||
517 | .fl_icmp_code = code, | ||
518 | }; | ||
519 | int err; | ||
520 | struct rtable *rt2; | ||
521 | |||
522 | security_skb_classify_flow(skb_in, &fl); | ||
523 | if (__ip_route_output_key(net, &rt, &fl)) | ||
524 | goto out_unlock; | ||
525 | |||
526 | /* No need to clone since we're just using its address. */ | ||
527 | rt2 = rt; | ||
528 | |||
529 | if (!fl.nl_u.ip4_u.saddr) | ||
530 | fl.nl_u.ip4_u.saddr = rt->rt_src; | ||
531 | |||
532 | err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0); | ||
533 | switch (err) { | ||
534 | case 0: | ||
535 | if (rt != rt2) | ||
536 | goto route_done; | ||
537 | break; | ||
538 | case -EPERM: | ||
539 | rt = NULL; | ||
540 | break; | ||
541 | default: | ||
542 | goto out_unlock; | ||
543 | } | ||
544 | |||
545 | if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET)) | ||
546 | goto relookup_failed; | ||
547 | |||
548 | if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) | ||
549 | err = __ip_route_output_key(net, &rt2, &fl); | ||
550 | else { | ||
551 | struct flowi fl2 = {}; | ||
552 | unsigned long orefdst; | ||
553 | |||
554 | fl2.fl4_dst = fl.fl4_src; | ||
555 | if (ip_route_output_key(net, &rt2, &fl2)) | ||
556 | goto relookup_failed; | ||
557 | |||
558 | /* Ugh! */ | ||
559 | orefdst = skb_in->_skb_refdst; /* save old refdst */ | ||
560 | err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, | ||
561 | RT_TOS(tos), rt2->dst.dev); | ||
562 | |||
563 | dst_release(&rt2->dst); | ||
564 | rt2 = skb_rtable(skb_in); | ||
565 | skb_in->_skb_refdst = orefdst; /* restore old refdst */ | ||
566 | } | ||
567 | |||
568 | if (err) | ||
569 | goto relookup_failed; | ||
570 | |||
571 | err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL, | ||
572 | XFRM_LOOKUP_ICMP); | ||
573 | switch (err) { | ||
574 | case 0: | ||
575 | dst_release(&rt->dst); | ||
576 | rt = rt2; | ||
577 | break; | ||
578 | case -EPERM: | ||
579 | goto ende; | ||
580 | default: | ||
581 | relookup_failed: | ||
582 | if (!rt) | ||
583 | goto out_unlock; | ||
584 | break; | ||
585 | } | ||
586 | } | ||
587 | 605 | ||
588 | route_done: | ||
589 | if (!icmpv4_xrlim_allow(net, rt, type, code)) | 606 | if (!icmpv4_xrlim_allow(net, rt, type, code)) |
590 | goto ende; | 607 | goto ende; |
591 | 608 | ||