aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/xfrm.h3
-rw-r--r--include/net/dst.h1
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/icmp.c82
-rw-r--r--net/ipv6/icmp.c60
-rw-r--r--net/xfrm/xfrm_policy.c17
6 files changed, 154 insertions, 10 deletions
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index c0e41e02234f..1131eabfaa2a 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -329,6 +329,7 @@ struct xfrm_usersa_info {
329#define XFRM_STATE_DECAP_DSCP 2 329#define XFRM_STATE_DECAP_DSCP 2
330#define XFRM_STATE_NOPMTUDISC 4 330#define XFRM_STATE_NOPMTUDISC 4
331#define XFRM_STATE_WILDRECV 8 331#define XFRM_STATE_WILDRECV 8
332#define XFRM_STATE_ICMP 16
332}; 333};
333 334
334struct xfrm_usersa_id { 335struct xfrm_usersa_id {
@@ -363,6 +364,8 @@ struct xfrm_userpolicy_info {
363#define XFRM_POLICY_BLOCK 1 364#define XFRM_POLICY_BLOCK 1
364 __u8 flags; 365 __u8 flags;
365#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ 366#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
367 /* Automatically expand selector to include matching ICMP payloads. */
368#define XFRM_POLICY_ICMP 2
366 __u8 share; 369 __u8 share;
367}; 370};
368 371
diff --git a/include/net/dst.h b/include/net/dst.h
index aaa2dbb50179..31468c9aa877 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -268,6 +268,7 @@ extern void dst_init(void);
268/* Flags for xfrm_lookup flags argument. */ 268/* Flags for xfrm_lookup flags argument. */
269enum { 269enum {
270 XFRM_LOOKUP_WAIT = 1 << 0, 270 XFRM_LOOKUP_WAIT = 1 << 0,
271 XFRM_LOOKUP_ICMP = 1 << 1,
271}; 272};
272 273
273struct flowi; 274struct flowi;
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
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 93c96cfd5ee1..c0bea7bfaa8a 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -63,6 +63,7 @@
63#include <net/ip6_route.h> 63#include <net/ip6_route.h>
64#include <net/addrconf.h> 64#include <net/addrconf.h>
65#include <net/icmp.h> 65#include <net/icmp.h>
66#include <net/xfrm.h>
66 67
67#include <asm/uaccess.h> 68#include <asm/uaccess.h>
68#include <asm/system.h> 69#include <asm/system.h>
@@ -86,7 +87,7 @@ static int icmpv6_rcv(struct sk_buff *skb);
86 87
87static struct inet6_protocol icmpv6_protocol = { 88static struct inet6_protocol icmpv6_protocol = {
88 .handler = icmpv6_rcv, 89 .handler = icmpv6_rcv,
89 .flags = INET6_PROTO_FINAL, 90 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
90}; 91};
91 92
92static __inline__ int icmpv6_xmit_lock(void) 93static __inline__ int icmpv6_xmit_lock(void)
@@ -310,8 +311,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
310 struct ipv6_pinfo *np; 311 struct ipv6_pinfo *np;
311 struct in6_addr *saddr = NULL; 312 struct in6_addr *saddr = NULL;
312 struct dst_entry *dst; 313 struct dst_entry *dst;
314 struct dst_entry *dst2;
313 struct icmp6hdr tmp_hdr; 315 struct icmp6hdr tmp_hdr;
314 struct flowi fl; 316 struct flowi fl;
317 struct flowi fl2;
315 struct icmpv6_msg msg; 318 struct icmpv6_msg msg;
316 int iif = 0; 319 int iif = 0;
317 int addr_type = 0; 320 int addr_type = 0;
@@ -418,9 +421,42 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
418 goto out_dst_release; 421 goto out_dst_release;
419 } 422 }
420 423
421 if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) 424 /* No need to clone since we're just using its address. */
425 dst2 = dst;
426
427 err = xfrm_lookup(&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;
438 }
439
440 if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6))
441 goto out;
442
443 if (ip6_dst_lookup(sk, &dst2, &fl))
422 goto out; 444 goto out;
423 445
446 err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP);
447 if (err == -ENOENT) {
448 if (!dst)
449 goto out;
450 goto route_done;
451 }
452
453 dst_release(dst);
454 dst = dst2;
455
456 if (err)
457 goto out;
458
459route_done:
424 if (ipv6_addr_is_multicast(&fl.fl6_dst)) 460 if (ipv6_addr_is_multicast(&fl.fl6_dst))
425 hlimit = np->mcast_hops; 461 hlimit = np->mcast_hops;
426 else 462 else
@@ -608,6 +644,22 @@ static int icmpv6_rcv(struct sk_buff *skb)
608 struct icmp6hdr *hdr; 644 struct icmp6hdr *hdr;
609 int type; 645 int type;
610 646
647 if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb) &&
648 skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
649 int nh;
650
651 if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr)))
652 goto drop_no_count;
653
654 nh = skb_network_offset(skb);
655 skb_set_network_header(skb, sizeof(*hdr));
656
657 if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
658 goto drop_no_count;
659
660 skb_set_network_header(skb, nh);
661 }
662
611 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); 663 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS);
612 664
613 saddr = &ipv6_hdr(skb)->saddr; 665 saddr = &ipv6_hdr(skb)->saddr;
@@ -630,8 +682,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
630 } 682 }
631 } 683 }
632 684
633 if (!pskb_pull(skb, sizeof(struct icmp6hdr))) 685 __skb_pull(skb, sizeof(*hdr));
634 goto discard_it;
635 686
636 hdr = icmp6_hdr(skb); 687 hdr = icmp6_hdr(skb);
637 688
@@ -717,6 +768,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
717 768
718discard_it: 769discard_it:
719 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); 770 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);
771drop_no_count:
720 kfree_skb(skb); 772 kfree_skb(skb);
721 return 0; 773 return 0;
722} 774}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 2e10d46c0e8c..a83b5e1349ed 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1469,11 +1469,13 @@ restart:
1469 goto dropdst; 1469 goto dropdst;
1470 } 1470 }
1471 1471
1472 err = -ENOENT;
1473
1472 if (!policy) { 1474 if (!policy) {
1473 /* To accelerate a bit... */ 1475 /* To accelerate a bit... */
1474 if ((dst_orig->flags & DST_NOXFRM) || 1476 if ((dst_orig->flags & DST_NOXFRM) ||
1475 !xfrm_policy_count[XFRM_POLICY_OUT]) 1477 !xfrm_policy_count[XFRM_POLICY_OUT])
1476 return 0; 1478 goto nopol;
1477 1479
1478 policy = flow_cache_lookup(fl, dst_orig->ops->family, 1480 policy = flow_cache_lookup(fl, dst_orig->ops->family,
1479 dir, xfrm_policy_lookup); 1481 dir, xfrm_policy_lookup);
@@ -1483,14 +1485,18 @@ restart:
1483 } 1485 }
1484 1486
1485 if (!policy) 1487 if (!policy)
1486 return 0; 1488 goto nopol;
1487 1489
1488 family = dst_orig->ops->family; 1490 family = dst_orig->ops->family;
1489 policy->curlft.use_time = get_seconds();
1490 pols[0] = policy; 1491 pols[0] = policy;
1491 npols ++; 1492 npols ++;
1492 xfrm_nr += pols[0]->xfrm_nr; 1493 xfrm_nr += pols[0]->xfrm_nr;
1493 1494
1495 if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP))
1496 goto error;
1497
1498 policy->curlft.use_time = get_seconds();
1499
1494 switch (policy->action) { 1500 switch (policy->action) {
1495 default: 1501 default:
1496 case XFRM_POLICY_BLOCK: 1502 case XFRM_POLICY_BLOCK:
@@ -1649,6 +1655,11 @@ dropdst:
1649 dst_release(dst_orig); 1655 dst_release(dst_orig);
1650 *dst_p = NULL; 1656 *dst_p = NULL;
1651 return err; 1657 return err;
1658
1659nopol:
1660 if (flags & XFRM_LOOKUP_ICMP)
1661 goto dropdst;
1662 return 0;
1652} 1663}
1653EXPORT_SYMBOL(__xfrm_lookup); 1664EXPORT_SYMBOL(__xfrm_lookup);
1654 1665