aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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