diff options
-rw-r--r-- | include/linux/xfrm.h | 3 | ||||
-rw-r--r-- | include/net/dst.h | 1 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 1 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 82 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 60 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 17 |
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 | ||
334 | struct xfrm_usersa_id { | 335 | struct 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. */ |
269 | enum { | 269 | enum { |
270 | XFRM_LOOKUP_WAIT = 1 << 0, | 270 | XFRM_LOOKUP_WAIT = 1 << 0, |
271 | XFRM_LOOKUP_ICMP = 1 << 1, | ||
271 | }; | 272 | }; |
272 | 273 | ||
273 | struct flowi; | 274 | struct 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 | ||
1292 | static struct net_protocol icmp_protocol = { | 1292 | static struct net_protocol icmp_protocol = { |
1293 | .handler = icmp_rcv, | 1293 | .handler = icmp_rcv, |
1294 | .no_policy = 1, | ||
1294 | }; | 1295 | }; |
1295 | 1296 | ||
1296 | static int __init init_ipv4_mibs(void) | 1297 | static 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 | ||
631 | route_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 | ||
87 | static struct inet6_protocol icmpv6_protocol = { | 88 | static 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 | ||
92 | static __inline__ int icmpv6_xmit_lock(void) | 93 | static __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 | |||
459 | route_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 | ||
718 | discard_it: | 769 | discard_it: |
719 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); | 770 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); |
771 | drop_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 | |||
1659 | nopol: | ||
1660 | if (flags & XFRM_LOOKUP_ICMP) | ||
1661 | goto dropdst; | ||
1662 | return 0; | ||
1652 | } | 1663 | } |
1653 | EXPORT_SYMBOL(__xfrm_lookup); | 1664 | EXPORT_SYMBOL(__xfrm_lookup); |
1654 | 1665 | ||