diff options
author | Julius Volz <juliusv@google.com> | 2008-09-02 09:55:47 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2008-09-04 21:17:09 -0400 |
commit | 2a3b791e6e1169f374224d164738e9f7be703d77 (patch) | |
tree | 2dcbe488eeb00a4fae70b209aea5fe6a3ecb5ae9 /net/ipv4/ipvs | |
parent | cd17f9ed099ed27e9b0d298253e5c05e335ac656 (diff) |
IPVS: Add/adjust Netfilter hook functions and helpers for v6
Add Netfilter hook functions or modify existing ones, if possible, to
process IPv6 packets. Some support functions are also added/modified for
this. ip_vs_nat_icmp_v6() was already added in the patch that added the v6
xmit functions, as it is called from one of them.
Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net/ipv4/ipvs')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_core.c | 365 |
1 files changed, 329 insertions, 36 deletions
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 8bfd7c2f0ebe..035a511e12f7 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c | |||
@@ -39,6 +39,11 @@ | |||
39 | #include <linux/netfilter.h> | 39 | #include <linux/netfilter.h> |
40 | #include <linux/netfilter_ipv4.h> | 40 | #include <linux/netfilter_ipv4.h> |
41 | 41 | ||
42 | #ifdef CONFIG_IP_VS_IPV6 | ||
43 | #include <net/ipv6.h> | ||
44 | #include <linux/netfilter_ipv6.h> | ||
45 | #endif | ||
46 | |||
42 | #include <net/ip_vs.h> | 47 | #include <net/ip_vs.h> |
43 | 48 | ||
44 | 49 | ||
@@ -60,6 +65,7 @@ EXPORT_SYMBOL(ip_vs_get_debug_level); | |||
60 | 65 | ||
61 | /* ID used in ICMP lookups */ | 66 | /* ID used in ICMP lookups */ |
62 | #define icmp_id(icmph) (((icmph)->un).echo.id) | 67 | #define icmp_id(icmph) (((icmph)->un).echo.id) |
68 | #define icmpv6_id(icmph) (icmph->icmp6_dataun.u_echo.identifier) | ||
63 | 69 | ||
64 | const char *ip_vs_proto_name(unsigned proto) | 70 | const char *ip_vs_proto_name(unsigned proto) |
65 | { | 71 | { |
@@ -74,6 +80,10 @@ const char *ip_vs_proto_name(unsigned proto) | |||
74 | return "TCP"; | 80 | return "TCP"; |
75 | case IPPROTO_ICMP: | 81 | case IPPROTO_ICMP: |
76 | return "ICMP"; | 82 | return "ICMP"; |
83 | #ifdef CONFIG_IP_VS_IPV6 | ||
84 | case IPPROTO_ICMPV6: | ||
85 | return "ICMPv6"; | ||
86 | #endif | ||
77 | default: | 87 | default: |
78 | sprintf(buf, "IP_%d", proto); | 88 | sprintf(buf, "IP_%d", proto); |
79 | return buf; | 89 | return buf; |
@@ -425,7 +435,8 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
425 | { | 435 | { |
426 | __be16 _ports[2], *pptr; | 436 | __be16 _ports[2], *pptr; |
427 | struct ip_vs_iphdr iph; | 437 | struct ip_vs_iphdr iph; |
428 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 438 | int unicast; |
439 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | ||
429 | 440 | ||
430 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); | 441 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); |
431 | if (pptr == NULL) { | 442 | if (pptr == NULL) { |
@@ -433,11 +444,17 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
433 | return NF_DROP; | 444 | return NF_DROP; |
434 | } | 445 | } |
435 | 446 | ||
447 | #ifdef CONFIG_IP_VS_IPV6 | ||
448 | if (svc->af == AF_INET6) | ||
449 | unicast = ipv6_addr_type(&iph.daddr.in6) & IPV6_ADDR_UNICAST; | ||
450 | else | ||
451 | #endif | ||
452 | unicast = (inet_addr_type(&init_net, iph.daddr.ip) == RTN_UNICAST); | ||
453 | |||
436 | /* if it is fwmark-based service, the cache_bypass sysctl is up | 454 | /* if it is fwmark-based service, the cache_bypass sysctl is up |
437 | and the destination is RTN_UNICAST (and not local), then create | 455 | and the destination is a non-local unicast, then create |
438 | a cache_bypass connection entry */ | 456 | a cache_bypass connection entry */ |
439 | if (sysctl_ip_vs_cache_bypass && svc->fwmark | 457 | if (sysctl_ip_vs_cache_bypass && svc->fwmark && unicast) { |
440 | && (inet_addr_type(&init_net, iph.daddr.ip) == RTN_UNICAST)) { | ||
441 | int ret, cs; | 458 | int ret, cs; |
442 | struct ip_vs_conn *cp; | 459 | struct ip_vs_conn *cp; |
443 | 460 | ||
@@ -445,7 +462,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
445 | 462 | ||
446 | /* create a new connection entry */ | 463 | /* create a new connection entry */ |
447 | IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n"); | 464 | IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n"); |
448 | cp = ip_vs_conn_new(AF_INET, iph.protocol, | 465 | cp = ip_vs_conn_new(svc->af, iph.protocol, |
449 | &iph.saddr, pptr[0], | 466 | &iph.saddr, pptr[0], |
450 | &iph.daddr, pptr[1], | 467 | &iph.daddr, pptr[1], |
451 | 0, 0, | 468 | 0, 0, |
@@ -489,7 +506,14 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
489 | * created, the TCP RST packet cannot be sent, instead that | 506 | * created, the TCP RST packet cannot be sent, instead that |
490 | * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ | 507 | * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ |
491 | */ | 508 | */ |
492 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | 509 | #ifdef CONFIG_IP_VS_IPV6 |
510 | if (svc->af == AF_INET6) | ||
511 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, | ||
512 | skb->dev); | ||
513 | else | ||
514 | #endif | ||
515 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
516 | |||
493 | return NF_DROP; | 517 | return NF_DROP; |
494 | } | 518 | } |
495 | 519 | ||
@@ -528,6 +552,14 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user) | |||
528 | return err; | 552 | return err; |
529 | } | 553 | } |
530 | 554 | ||
555 | #ifdef CONFIG_IP_VS_IPV6 | ||
556 | static inline int ip_vs_gather_frags_v6(struct sk_buff *skb, u_int32_t user) | ||
557 | { | ||
558 | /* TODO IPv6: Find out what to do here for IPv6 */ | ||
559 | return 0; | ||
560 | } | ||
561 | #endif | ||
562 | |||
531 | /* | 563 | /* |
532 | * Packet has been made sufficiently writable in caller | 564 | * Packet has been made sufficiently writable in caller |
533 | * - inout: 1=in->out, 0=out->in | 565 | * - inout: 1=in->out, 0=out->in |
@@ -727,11 +759,117 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related) | |||
727 | return verdict; | 759 | return verdict; |
728 | } | 760 | } |
729 | 761 | ||
730 | static inline int is_tcp_reset(const struct sk_buff *skb) | 762 | #ifdef CONFIG_IP_VS_IPV6 |
763 | static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related) | ||
764 | { | ||
765 | struct ipv6hdr *iph; | ||
766 | struct icmp6hdr _icmph, *ic; | ||
767 | struct ipv6hdr _ciph, *cih; /* The ip header contained | ||
768 | within the ICMP */ | ||
769 | struct ip_vs_iphdr ciph; | ||
770 | struct ip_vs_conn *cp; | ||
771 | struct ip_vs_protocol *pp; | ||
772 | unsigned int offset, verdict; | ||
773 | |||
774 | *related = 1; | ||
775 | |||
776 | /* reassemble IP fragments */ | ||
777 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
778 | if (ip_vs_gather_frags_v6(skb, IP_DEFRAG_VS_OUT)) | ||
779 | return NF_STOLEN; | ||
780 | } | ||
781 | |||
782 | iph = ipv6_hdr(skb); | ||
783 | offset = sizeof(struct ipv6hdr); | ||
784 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); | ||
785 | if (ic == NULL) | ||
786 | return NF_DROP; | ||
787 | |||
788 | IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n", | ||
789 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
790 | NIP6(iph->saddr), NIP6(iph->daddr)); | ||
791 | |||
792 | /* | ||
793 | * Work through seeing if this is for us. | ||
794 | * These checks are supposed to be in an order that means easy | ||
795 | * things are checked first to speed up processing.... however | ||
796 | * this means that some packets will manage to get a long way | ||
797 | * down this stack and then be rejected, but that's life. | ||
798 | */ | ||
799 | if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) && | ||
800 | (ic->icmp6_type != ICMPV6_PKT_TOOBIG) && | ||
801 | (ic->icmp6_type != ICMPV6_TIME_EXCEED)) { | ||
802 | *related = 0; | ||
803 | return NF_ACCEPT; | ||
804 | } | ||
805 | |||
806 | /* Now find the contained IP header */ | ||
807 | offset += sizeof(_icmph); | ||
808 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); | ||
809 | if (cih == NULL) | ||
810 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | ||
811 | |||
812 | pp = ip_vs_proto_get(cih->nexthdr); | ||
813 | if (!pp) | ||
814 | return NF_ACCEPT; | ||
815 | |||
816 | /* Is the embedded protocol header present? */ | ||
817 | /* TODO: we don't support fragmentation at the moment anyways */ | ||
818 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
819 | return NF_ACCEPT; | ||
820 | |||
821 | IP_VS_DBG_PKT(11, pp, skb, offset, "Checking outgoing ICMPv6 for"); | ||
822 | |||
823 | offset += sizeof(struct ipv6hdr); | ||
824 | |||
825 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); | ||
826 | /* The embedded headers contain source and dest in reverse order */ | ||
827 | cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); | ||
828 | if (!cp) | ||
829 | return NF_ACCEPT; | ||
830 | |||
831 | verdict = NF_DROP; | ||
832 | |||
833 | if (IP_VS_FWD_METHOD(cp) != 0) { | ||
834 | IP_VS_ERR("shouldn't reach here, because the box is on the " | ||
835 | "half connection in the tun/dr module.\n"); | ||
836 | } | ||
837 | |||
838 | /* Ensure the checksum is correct */ | ||
839 | if (!skb_csum_unnecessary(skb) | ||
840 | && ip_vs_checksum_complete(skb, sizeof(struct ipv6hdr))) { | ||
841 | /* Failed checksum! */ | ||
842 | IP_VS_DBG(1, "Forward ICMPv6: failed checksum from " | ||
843 | NIP6_FMT "!\n", | ||
844 | NIP6(iph->saddr)); | ||
845 | goto out; | ||
846 | } | ||
847 | |||
848 | if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr) | ||
849 | offset += 2 * sizeof(__u16); | ||
850 | if (!skb_make_writable(skb, offset)) | ||
851 | goto out; | ||
852 | |||
853 | ip_vs_nat_icmp_v6(skb, pp, cp, 1); | ||
854 | |||
855 | /* do the statistics and put it back */ | ||
856 | ip_vs_out_stats(cp, skb); | ||
857 | |||
858 | skb->ipvs_property = 1; | ||
859 | verdict = NF_ACCEPT; | ||
860 | |||
861 | out: | ||
862 | __ip_vs_conn_put(cp); | ||
863 | |||
864 | return verdict; | ||
865 | } | ||
866 | #endif | ||
867 | |||
868 | static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) | ||
731 | { | 869 | { |
732 | struct tcphdr _tcph, *th; | 870 | struct tcphdr _tcph, *th; |
733 | 871 | ||
734 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | 872 | th = skb_header_pointer(skb, nh_len, sizeof(_tcph), &_tcph); |
735 | if (th == NULL) | 873 | if (th == NULL) |
736 | return 0; | 874 | return 0; |
737 | return th->rst; | 875 | return th->rst; |
@@ -750,38 +888,64 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
750 | struct ip_vs_iphdr iph; | 888 | struct ip_vs_iphdr iph; |
751 | struct ip_vs_protocol *pp; | 889 | struct ip_vs_protocol *pp; |
752 | struct ip_vs_conn *cp; | 890 | struct ip_vs_conn *cp; |
891 | int af; | ||
753 | 892 | ||
754 | EnterFunction(11); | 893 | EnterFunction(11); |
755 | 894 | ||
895 | af = (skb->protocol == __constant_htons(ETH_P_IP)) ? AF_INET : AF_INET6; | ||
896 | |||
756 | if (skb->ipvs_property) | 897 | if (skb->ipvs_property) |
757 | return NF_ACCEPT; | 898 | return NF_ACCEPT; |
758 | 899 | ||
759 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 900 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
760 | if (unlikely(iph.protocol == IPPROTO_ICMP)) { | 901 | #ifdef CONFIG_IP_VS_IPV6 |
761 | int related, verdict = ip_vs_out_icmp(skb, &related); | 902 | if (af == AF_INET6) { |
903 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { | ||
904 | int related, verdict = ip_vs_out_icmp_v6(skb, &related); | ||
762 | 905 | ||
763 | if (related) | 906 | if (related) |
764 | return verdict; | 907 | return verdict; |
765 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 908 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
766 | } | 909 | } |
910 | } else | ||
911 | #endif | ||
912 | if (unlikely(iph.protocol == IPPROTO_ICMP)) { | ||
913 | int related, verdict = ip_vs_out_icmp(skb, &related); | ||
914 | |||
915 | if (related) | ||
916 | return verdict; | ||
917 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
918 | } | ||
767 | 919 | ||
768 | pp = ip_vs_proto_get(iph.protocol); | 920 | pp = ip_vs_proto_get(iph.protocol); |
769 | if (unlikely(!pp)) | 921 | if (unlikely(!pp)) |
770 | return NF_ACCEPT; | 922 | return NF_ACCEPT; |
771 | 923 | ||
772 | /* reassemble IP fragments */ | 924 | /* reassemble IP fragments */ |
773 | if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) && | 925 | #ifdef CONFIG_IP_VS_IPV6 |
774 | !pp->dont_defrag)) { | 926 | if (af == AF_INET6) { |
775 | if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT)) | 927 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { |
776 | return NF_STOLEN; | 928 | int related, verdict = ip_vs_out_icmp_v6(skb, &related); |
777 | 929 | ||
778 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 930 | if (related) |
779 | } | 931 | return verdict; |
932 | |||
933 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
934 | } | ||
935 | } else | ||
936 | #endif | ||
937 | if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) && | ||
938 | !pp->dont_defrag)) { | ||
939 | if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT)) | ||
940 | return NF_STOLEN; | ||
941 | |||
942 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
943 | } | ||
780 | 944 | ||
781 | /* | 945 | /* |
782 | * Check if the packet belongs to an existing entry | 946 | * Check if the packet belongs to an existing entry |
783 | */ | 947 | */ |
784 | cp = pp->conn_out_get(AF_INET, skb, pp, &iph, iph.len, 0); | 948 | cp = pp->conn_out_get(af, skb, pp, &iph, iph.len, 0); |
785 | 949 | ||
786 | if (unlikely(!cp)) { | 950 | if (unlikely(!cp)) { |
787 | if (sysctl_ip_vs_nat_icmp_send && | 951 | if (sysctl_ip_vs_nat_icmp_send && |
@@ -794,16 +958,26 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
794 | if (pptr == NULL) | 958 | if (pptr == NULL) |
795 | return NF_ACCEPT; /* Not for me */ | 959 | return NF_ACCEPT; /* Not for me */ |
796 | if (ip_vs_lookup_real_service(iph.protocol, | 960 | if (ip_vs_lookup_real_service(iph.protocol, |
797 | iph.saddr.ip, pptr[0])) { | 961 | iph.saddr.ip, |
962 | pptr[0])) { | ||
798 | /* | 963 | /* |
799 | * Notify the real server: there is no | 964 | * Notify the real server: there is no |
800 | * existing entry if it is not RST | 965 | * existing entry if it is not RST |
801 | * packet or not TCP packet. | 966 | * packet or not TCP packet. |
802 | */ | 967 | */ |
803 | if (iph.protocol != IPPROTO_TCP | 968 | if (iph.protocol != IPPROTO_TCP |
804 | || !is_tcp_reset(skb)) { | 969 | || !is_tcp_reset(skb, iph.len)) { |
805 | icmp_send(skb,ICMP_DEST_UNREACH, | 970 | #ifdef CONFIG_IP_VS_IPV6 |
806 | ICMP_PORT_UNREACH, 0); | 971 | if (af == AF_INET6) |
972 | icmpv6_send(skb, | ||
973 | ICMPV6_DEST_UNREACH, | ||
974 | ICMPV6_PORT_UNREACH, | ||
975 | 0, skb->dev); | ||
976 | else | ||
977 | #endif | ||
978 | icmp_send(skb, | ||
979 | ICMP_DEST_UNREACH, | ||
980 | ICMP_PORT_UNREACH, 0); | ||
807 | return NF_DROP; | 981 | return NF_DROP; |
808 | } | 982 | } |
809 | } | 983 | } |
@@ -821,8 +995,16 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
821 | /* mangle the packet */ | 995 | /* mangle the packet */ |
822 | if (pp->snat_handler && !pp->snat_handler(skb, pp, cp)) | 996 | if (pp->snat_handler && !pp->snat_handler(skb, pp, cp)) |
823 | goto drop; | 997 | goto drop; |
824 | ip_hdr(skb)->saddr = cp->vaddr.ip; | 998 | |
825 | ip_send_check(ip_hdr(skb)); | 999 | #ifdef CONFIG_IP_VS_IPV6 |
1000 | if (af == AF_INET6) | ||
1001 | ipv6_hdr(skb)->saddr = cp->vaddr.in6; | ||
1002 | else | ||
1003 | #endif | ||
1004 | { | ||
1005 | ip_hdr(skb)->saddr = cp->vaddr.ip; | ||
1006 | ip_send_check(ip_hdr(skb)); | ||
1007 | } | ||
826 | 1008 | ||
827 | /* For policy routing, packets originating from this | 1009 | /* For policy routing, packets originating from this |
828 | * machine itself may be routed differently to packets | 1010 | * machine itself may be routed differently to packets |
@@ -830,8 +1012,14 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
830 | * if it came from this machine itself. So re-compute | 1012 | * if it came from this machine itself. So re-compute |
831 | * the routing information. | 1013 | * the routing information. |
832 | */ | 1014 | */ |
833 | if (ip_route_me_harder(skb, RTN_LOCAL) != 0) | 1015 | #ifdef CONFIG_IP_VS_IPV6 |
834 | goto drop; | 1016 | if (af == AF_INET6) { |
1017 | if (ip6_route_me_harder(skb) != 0) | ||
1018 | goto drop; | ||
1019 | } else | ||
1020 | #endif | ||
1021 | if (ip_route_me_harder(skb, RTN_LOCAL) != 0) | ||
1022 | goto drop; | ||
835 | 1023 | ||
836 | IP_VS_DBG_PKT(10, pp, skb, 0, "After SNAT"); | 1024 | IP_VS_DBG_PKT(10, pp, skb, 0, "After SNAT"); |
837 | 1025 | ||
@@ -949,6 +1137,94 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
949 | return verdict; | 1137 | return verdict; |
950 | } | 1138 | } |
951 | 1139 | ||
1140 | #ifdef CONFIG_IP_VS_IPV6 | ||
1141 | static int | ||
1142 | ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | ||
1143 | { | ||
1144 | struct ipv6hdr *iph; | ||
1145 | struct icmp6hdr _icmph, *ic; | ||
1146 | struct ipv6hdr _ciph, *cih; /* The ip header contained | ||
1147 | within the ICMP */ | ||
1148 | struct ip_vs_iphdr ciph; | ||
1149 | struct ip_vs_conn *cp; | ||
1150 | struct ip_vs_protocol *pp; | ||
1151 | unsigned int offset, verdict; | ||
1152 | |||
1153 | *related = 1; | ||
1154 | |||
1155 | /* reassemble IP fragments */ | ||
1156 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
1157 | if (ip_vs_gather_frags_v6(skb, hooknum == NF_INET_LOCAL_IN ? | ||
1158 | IP_DEFRAG_VS_IN : | ||
1159 | IP_DEFRAG_VS_FWD)) | ||
1160 | return NF_STOLEN; | ||
1161 | } | ||
1162 | |||
1163 | iph = ipv6_hdr(skb); | ||
1164 | offset = sizeof(struct ipv6hdr); | ||
1165 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); | ||
1166 | if (ic == NULL) | ||
1167 | return NF_DROP; | ||
1168 | |||
1169 | IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n", | ||
1170 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
1171 | NIP6(iph->saddr), NIP6(iph->daddr)); | ||
1172 | |||
1173 | /* | ||
1174 | * Work through seeing if this is for us. | ||
1175 | * These checks are supposed to be in an order that means easy | ||
1176 | * things are checked first to speed up processing.... however | ||
1177 | * this means that some packets will manage to get a long way | ||
1178 | * down this stack and then be rejected, but that's life. | ||
1179 | */ | ||
1180 | if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) && | ||
1181 | (ic->icmp6_type != ICMPV6_PKT_TOOBIG) && | ||
1182 | (ic->icmp6_type != ICMPV6_TIME_EXCEED)) { | ||
1183 | *related = 0; | ||
1184 | return NF_ACCEPT; | ||
1185 | } | ||
1186 | |||
1187 | /* Now find the contained IP header */ | ||
1188 | offset += sizeof(_icmph); | ||
1189 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); | ||
1190 | if (cih == NULL) | ||
1191 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | ||
1192 | |||
1193 | pp = ip_vs_proto_get(cih->nexthdr); | ||
1194 | if (!pp) | ||
1195 | return NF_ACCEPT; | ||
1196 | |||
1197 | /* Is the embedded protocol header present? */ | ||
1198 | /* TODO: we don't support fragmentation at the moment anyways */ | ||
1199 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
1200 | return NF_ACCEPT; | ||
1201 | |||
1202 | IP_VS_DBG_PKT(11, pp, skb, offset, "Checking incoming ICMPv6 for"); | ||
1203 | |||
1204 | offset += sizeof(struct ipv6hdr); | ||
1205 | |||
1206 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); | ||
1207 | /* The embedded headers contain source and dest in reverse order */ | ||
1208 | cp = pp->conn_in_get(AF_INET6, skb, pp, &ciph, offset, 1); | ||
1209 | if (!cp) | ||
1210 | return NF_ACCEPT; | ||
1211 | |||
1212 | verdict = NF_DROP; | ||
1213 | |||
1214 | /* do the statistics and put it back */ | ||
1215 | ip_vs_in_stats(cp, skb); | ||
1216 | if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr) | ||
1217 | offset += 2 * sizeof(__u16); | ||
1218 | verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset); | ||
1219 | /* do not touch skb anymore */ | ||
1220 | |||
1221 | __ip_vs_conn_put(cp); | ||
1222 | |||
1223 | return verdict; | ||
1224 | } | ||
1225 | #endif | ||
1226 | |||
1227 | |||
952 | /* | 1228 | /* |
953 | * Check if it's for virtual services, look it up, | 1229 | * Check if it's for virtual services, look it up, |
954 | * and send it on its way... | 1230 | * and send it on its way... |
@@ -961,9 +1237,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
961 | struct ip_vs_iphdr iph; | 1237 | struct ip_vs_iphdr iph; |
962 | struct ip_vs_protocol *pp; | 1238 | struct ip_vs_protocol *pp; |
963 | struct ip_vs_conn *cp; | 1239 | struct ip_vs_conn *cp; |
964 | int ret, restart; | 1240 | int ret, restart, af; |
1241 | |||
1242 | af = (skb->protocol == __constant_htons(ETH_P_IP)) ? AF_INET : AF_INET6; | ||
965 | 1243 | ||
966 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 1244 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
967 | 1245 | ||
968 | /* | 1246 | /* |
969 | * Big tappo: only PACKET_HOST (neither loopback nor mcasts) | 1247 | * Big tappo: only PACKET_HOST (neither loopback nor mcasts) |
@@ -974,7 +1252,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
974 | IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n", | 1252 | IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n", |
975 | skb->pkt_type, | 1253 | skb->pkt_type, |
976 | iph.protocol, | 1254 | iph.protocol, |
977 | IP_VS_DBG_ADDR(AF_INET, &iph.daddr)); | 1255 | IP_VS_DBG_ADDR(af, &iph.daddr)); |
978 | return NF_ACCEPT; | 1256 | return NF_ACCEPT; |
979 | } | 1257 | } |
980 | 1258 | ||
@@ -983,7 +1261,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
983 | 1261 | ||
984 | if (related) | 1262 | if (related) |
985 | return verdict; | 1263 | return verdict; |
986 | ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph); | 1264 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
987 | } | 1265 | } |
988 | 1266 | ||
989 | /* Protocol supported? */ | 1267 | /* Protocol supported? */ |
@@ -994,12 +1272,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
994 | /* | 1272 | /* |
995 | * Check if the packet belongs to an existing connection entry | 1273 | * Check if the packet belongs to an existing connection entry |
996 | */ | 1274 | */ |
997 | cp = pp->conn_in_get(AF_INET, skb, pp, &iph, iph.len, 0); | 1275 | cp = pp->conn_in_get(af, skb, pp, &iph, iph.len, 0); |
998 | 1276 | ||
999 | if (unlikely(!cp)) { | 1277 | if (unlikely(!cp)) { |
1000 | int v; | 1278 | int v; |
1001 | 1279 | ||
1002 | if (!pp->conn_schedule(AF_INET, skb, pp, &v, &cp)) | 1280 | if (!pp->conn_schedule(af, skb, pp, &v, &cp)) |
1003 | return v; | 1281 | return v; |
1004 | } | 1282 | } |
1005 | 1283 | ||
@@ -1082,6 +1360,21 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, | |||
1082 | return ip_vs_in_icmp(skb, &r, hooknum); | 1360 | return ip_vs_in_icmp(skb, &r, hooknum); |
1083 | } | 1361 | } |
1084 | 1362 | ||
1363 | #ifdef CONFIG_IP_VS_IPV6 | ||
1364 | static unsigned int | ||
1365 | ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, | ||
1366 | const struct net_device *in, const struct net_device *out, | ||
1367 | int (*okfn)(struct sk_buff *)) | ||
1368 | { | ||
1369 | int r; | ||
1370 | |||
1371 | if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) | ||
1372 | return NF_ACCEPT; | ||
1373 | |||
1374 | return ip_vs_in_icmp_v6(skb, &r, hooknum); | ||
1375 | } | ||
1376 | #endif | ||
1377 | |||
1085 | 1378 | ||
1086 | static struct nf_hook_ops ip_vs_ops[] __read_mostly = { | 1379 | static struct nf_hook_ops ip_vs_ops[] __read_mostly = { |
1087 | /* After packet filtering, forward packet through VS/DR, VS/TUN, | 1380 | /* After packet filtering, forward packet through VS/DR, VS/TUN, |