aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-12-12 13:44:43 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:57:23 -0500
commit8b7817f3a959ed99d7443afc12f78a7e1fcc2063 (patch)
tree7e315dfbf5c77e67f6e7ad56f14eaddca621212b /net/ipv4
parentd5422efe680fc55010c6ddca2370ca9548a96355 (diff)
[IPSEC]: Add ICMP host relookup support
RFC 4301 requires us to relookup ICMP traffic that does not match any policies using the reverse of its payload. This patch implements this for ICMP traffic that originates from or terminates on localhost. This is activated on outbound with the new policy flag XFRM_POLICY_ICMP, and on inbound by the new state flag XFRM_STATE_ICMP. On inbound the policy check is now performed by the ICMP protocol so that it can repeat the policy check where necessary. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/icmp.c82
2 files changed, 80 insertions, 3 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 7f8b27ff94ff..5089a369e99c 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1291,6 +1291,7 @@ static struct net_protocol udp_protocol = {
1291 1291
1292static struct net_protocol icmp_protocol = { 1292static struct net_protocol icmp_protocol = {
1293 .handler = icmp_rcv, 1293 .handler = icmp_rcv,
1294 .no_policy = 1,
1294}; 1295};
1295 1296
1296static int __init init_ipv4_mibs(void) 1297static int __init init_ipv4_mibs(void)
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