aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/icmp.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-03-01 18:49:55 -0500
committerDavid S. Miller <davem@davemloft.net>2011-03-01 18:49:55 -0500
commitf6d460cf0ed16d35aec48f823685e7a0e0283d84 (patch)
tree56c311e236e3856220660afe7eaadf76106e58c6 /net/ipv4/icmp.c
parent2774c131b1d19920b4587db1cfbd6f0750ad1f15 (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.c175
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
372static 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
459relookup_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:
581relookup_failed:
582 if (!rt)
583 goto out_unlock;
584 break;
585 }
586 }
587 605
588route_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