aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/icmp.c')
-rw-r--r--net/ipv4/icmp.c82
1 files changed, 79 insertions, 3 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 13d74598d3e4..3c41a6f7e6ec 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -92,6 +92,7 @@
92#include <asm/system.h> 92#include <asm/system.h>
93#include <asm/uaccess.h> 93#include <asm/uaccess.h>
94#include <net/checksum.h> 94#include <net/checksum.h>
95#include <net/xfrm.h>
95 96
96/* 97/*
97 * Build xmit assembly blocks 98 * Build xmit assembly blocks
@@ -563,11 +564,71 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
563 } 564 }
564 } 565 }
565 }; 566 };
567 int err;
568 struct rtable *rt2;
569
566 security_skb_classify_flow(skb_in, &fl); 570 security_skb_classify_flow(skb_in, &fl);
567 if (ip_route_output_key(&rt, &fl)) 571 if (__ip_route_output_key(&rt, &fl))
572 goto out_unlock;
573
574 /* No need to clone since we're just using its address. */
575 rt2 = rt;
576
577 err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
578 switch (err) {
579 case 0:
580 if (rt != rt2)
581 goto route_done;
582 break;
583 case -EPERM:
584 rt = NULL;
585 break;
586 default:
587 goto out_unlock;
588 }
589
590 if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET))
591 goto out_unlock;
592
593 if (inet_addr_type(fl.fl4_src) == RTN_LOCAL)
594 err = __ip_route_output_key(&rt2, &fl);
595 else {
596 struct flowi fl2 = {};
597 struct dst_entry *odst;
598
599 fl2.fl4_dst = fl.fl4_src;
600 if (ip_route_output_key(&rt2, &fl2))
601 goto out_unlock;
602
603 /* Ugh! */
604 odst = skb_in->dst;
605 err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
606 RT_TOS(tos), rt2->u.dst.dev);
607
608 dst_release(&rt2->u.dst);
609 rt2 = (struct rtable *)skb_in->dst;
610 skb_in->dst = odst;
611 }
612
613 if (err)
614 goto out_unlock;
615
616 err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
617 XFRM_LOOKUP_ICMP);
618 if (err == -ENOENT) {
619 if (!rt)
620 goto out_unlock;
621 goto route_done;
622 }
623
624 dst_release(&rt->u.dst);
625 rt = rt2;
626
627 if (err)
568 goto out_unlock; 628 goto out_unlock;
569 } 629 }
570 630
631route_done:
571 if (!icmpv4_xrlim_allow(rt, type, code)) 632 if (!icmpv4_xrlim_allow(rt, type, code))
572 goto ende; 633 goto ende;
573 634
@@ -916,6 +977,22 @@ int icmp_rcv(struct sk_buff *skb)
916 struct icmphdr *icmph; 977 struct icmphdr *icmph;
917 struct rtable *rt = (struct rtable *)skb->dst; 978 struct rtable *rt = (struct rtable *)skb->dst;
918 979
980 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb) &&
981 skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
982 int nh;
983
984 if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
985 goto drop;
986
987 nh = skb_network_offset(skb);
988 skb_set_network_header(skb, sizeof(*icmph));
989
990 if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
991 goto drop;
992
993 skb_set_network_header(skb, nh);
994 }
995
919 ICMP_INC_STATS_BH(ICMP_MIB_INMSGS); 996 ICMP_INC_STATS_BH(ICMP_MIB_INMSGS);
920 997
921 switch (skb->ip_summed) { 998 switch (skb->ip_summed) {
@@ -929,8 +1006,7 @@ int icmp_rcv(struct sk_buff *skb)
929 goto error; 1006 goto error;
930 } 1007 }
931 1008
932 if (!pskb_pull(skb, sizeof(struct icmphdr))) 1009 __skb_pull(skb, sizeof(*icmph));
933 goto error;
934 1010
935 icmph = icmp_hdr(skb); 1011 icmph = icmp_hdr(skb);
936 1012