diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2012-09-26 08:06:41 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2012-09-27 22:34:15 -0400 |
commit | 63dca2c0b0e7a92cb39d1b1ecefa32ffda201975 (patch) | |
tree | 4998cbf50a15ab5a939dc8cd722c668f601ba3cc /net/netfilter | |
parent | a638e51437f5efd00c4579df56cfd4d497ed51a8 (diff) |
ipvs: Fix faulty IPv6 extension header handling in IPVS
IPv6 packets can contain extension headers, thus its wrong to assume
that the transport/upper-layer header, starts right after (struct
ipv6hdr) the IPv6 header. IPVS uses this false assumption, and will
write SNAT & DNAT modifications at a fixed pos which will corrupt the
message.
To fix this, proper header position must be found before modifying
packets. Introducing ip_vs_fill_iph_skb(), which uses ipv6_find_hdr()
to skip the exthdrs. It finds (1) the transport header offset, (2) the
protocol, and (3) detects if the packet is a fragment.
Note, that fragments in IPv6 is represented via an exthdr. Thus, this
is detected while skipping through the exthdrs.
This patch depends on commit 84018f55a:
"netfilter: ip6_tables: add flags parameter to ipv6_find_hdr()"
This also adds a dependency to ip6_tables.
Originally based on patch from: Hans Schillstrom
kABI notes:
Changing struct ip_vs_iphdr is a potential minor kABI breaker,
because external modules can be compiled with another version of
this struct. This should not matter, as they would most-likely
be using a compiled-in version of ip_vs_fill_iphdr(). When
recompiled, they will notice ip_vs_fill_iphdr() no longer exists,
and they have to used ip_vs_fill_iph_skb() instead.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/ipvs/Kconfig | 1 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_core.c | 211 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_dh.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_lblc.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_lblcr.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_pe_sip.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_sctp.c | 22 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_tcp.c | 22 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_udp.c | 22 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_sh.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_xmit.c | 5 | ||||
-rw-r--r-- | net/netfilter/xt_ipvs.c | 2 |
12 files changed, 152 insertions, 143 deletions
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index 8b2cffdfdd9..a97ae5328ae 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig | |||
@@ -28,6 +28,7 @@ if IP_VS | |||
28 | config IP_VS_IPV6 | 28 | config IP_VS_IPV6 |
29 | bool "IPv6 support for IPVS" | 29 | bool "IPv6 support for IPVS" |
30 | depends on IPV6 = y || IP_VS = IPV6 | 30 | depends on IPV6 = y || IP_VS = IPV6 |
31 | select IP6_NF_IPTABLES | ||
31 | ---help--- | 32 | ---help--- |
32 | Add IPv6 support to IPVS. This is incomplete and might be dangerous. | 33 | Add IPv6 support to IPVS. This is incomplete and might be dangerous. |
33 | 34 | ||
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index ebd105c4e0c..19c08425e13 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c | |||
@@ -236,7 +236,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
236 | union nf_inet_addr snet; /* source network of the client, | 236 | union nf_inet_addr snet; /* source network of the client, |
237 | after masking */ | 237 | after masking */ |
238 | 238 | ||
239 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 239 | ip_vs_fill_iph_skb(svc->af, skb, &iph); |
240 | 240 | ||
241 | /* Mask saddr with the netmask to adjust template granularity */ | 241 | /* Mask saddr with the netmask to adjust template granularity */ |
242 | #ifdef CONFIG_IP_VS_IPV6 | 242 | #ifdef CONFIG_IP_VS_IPV6 |
@@ -402,7 +402,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, | |||
402 | unsigned int flags; | 402 | unsigned int flags; |
403 | 403 | ||
404 | *ignored = 1; | 404 | *ignored = 1; |
405 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 405 | ip_vs_fill_iph_skb(svc->af, skb, &iph); |
406 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); | 406 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); |
407 | if (pptr == NULL) | 407 | if (pptr == NULL) |
408 | return NULL; | 408 | return NULL; |
@@ -506,7 +506,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
506 | int unicast; | 506 | int unicast; |
507 | #endif | 507 | #endif |
508 | 508 | ||
509 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 509 | ip_vs_fill_iph_skb(svc->af, skb, &iph); |
510 | 510 | ||
511 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); | 511 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); |
512 | if (pptr == NULL) { | 512 | if (pptr == NULL) { |
@@ -732,10 +732,19 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
732 | struct ip_vs_conn *cp, int inout) | 732 | struct ip_vs_conn *cp, int inout) |
733 | { | 733 | { |
734 | struct ipv6hdr *iph = ipv6_hdr(skb); | 734 | struct ipv6hdr *iph = ipv6_hdr(skb); |
735 | unsigned int icmp_offset = sizeof(struct ipv6hdr); | 735 | unsigned int icmp_offset = 0; |
736 | struct icmp6hdr *icmph = (struct icmp6hdr *)(skb_network_header(skb) + | 736 | unsigned int offs = 0; /* header offset*/ |
737 | icmp_offset); | 737 | int protocol; |
738 | struct ipv6hdr *ciph = (struct ipv6hdr *)(icmph + 1); | 738 | struct icmp6hdr *icmph; |
739 | struct ipv6hdr *ciph; | ||
740 | unsigned short fragoffs; | ||
741 | |||
742 | ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); | ||
743 | icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); | ||
744 | offs = icmp_offset + sizeof(struct icmp6hdr); | ||
745 | ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); | ||
746 | |||
747 | protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); | ||
739 | 748 | ||
740 | if (inout) { | 749 | if (inout) { |
741 | iph->saddr = cp->vaddr.in6; | 750 | iph->saddr = cp->vaddr.in6; |
@@ -746,10 +755,13 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
746 | } | 755 | } |
747 | 756 | ||
748 | /* the TCP/UDP/SCTP port */ | 757 | /* the TCP/UDP/SCTP port */ |
749 | if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr || | 758 | if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || |
750 | IPPROTO_SCTP == ciph->nexthdr) { | 759 | IPPROTO_SCTP == protocol)) { |
751 | __be16 *ports = (void *)ciph + sizeof(struct ipv6hdr); | 760 | __be16 *ports = (void *)(skb_network_header(skb) + offs); |
752 | 761 | ||
762 | IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, | ||
763 | ntohs(inout ? ports[1] : ports[0]), | ||
764 | ntohs(inout ? cp->vport : cp->dport)); | ||
753 | if (inout) | 765 | if (inout) |
754 | ports[1] = cp->vport; | 766 | ports[1] = cp->vport; |
755 | else | 767 | else |
@@ -898,9 +910,8 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, | |||
898 | IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, | 910 | IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, |
899 | "Checking outgoing ICMP for"); | 911 | "Checking outgoing ICMP for"); |
900 | 912 | ||
901 | offset += cih->ihl * 4; | 913 | ip_vs_fill_ip4hdr(cih, &ciph); |
902 | 914 | ciph.len += offset; | |
903 | ip_vs_fill_iphdr(AF_INET, cih, &ciph); | ||
904 | /* The embedded headers contain source and dest in reverse order */ | 915 | /* The embedded headers contain source and dest in reverse order */ |
905 | cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); | 916 | cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); |
906 | if (!cp) | 917 | if (!cp) |
@@ -908,41 +919,31 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, | |||
908 | 919 | ||
909 | snet.ip = iph->saddr; | 920 | snet.ip = iph->saddr; |
910 | return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, | 921 | return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, |
911 | pp, offset, ihl); | 922 | pp, ciph.len, ihl); |
912 | } | 923 | } |
913 | 924 | ||
914 | #ifdef CONFIG_IP_VS_IPV6 | 925 | #ifdef CONFIG_IP_VS_IPV6 |
915 | static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, | 926 | static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, |
916 | unsigned int hooknum) | 927 | unsigned int hooknum) |
917 | { | 928 | { |
918 | struct ipv6hdr *iph; | ||
919 | struct icmp6hdr _icmph, *ic; | 929 | struct icmp6hdr _icmph, *ic; |
920 | struct ipv6hdr _ciph, *cih; /* The ip header contained | 930 | struct ipv6hdr _ip6h, *ip6h; /* The ip header contained within ICMP */ |
921 | within the ICMP */ | 931 | struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */ |
922 | struct ip_vs_iphdr ciph; | ||
923 | struct ip_vs_conn *cp; | 932 | struct ip_vs_conn *cp; |
924 | struct ip_vs_protocol *pp; | 933 | struct ip_vs_protocol *pp; |
925 | unsigned int offset; | ||
926 | union nf_inet_addr snet; | 934 | union nf_inet_addr snet; |
935 | unsigned int writable; | ||
927 | 936 | ||
928 | *related = 1; | 937 | struct ip_vs_iphdr ipvsh_stack; |
938 | struct ip_vs_iphdr *ipvsh = &ipvsh_stack; | ||
939 | ip_vs_fill_iph_skb(AF_INET6, skb, ipvsh); | ||
929 | 940 | ||
930 | /* reassemble IP fragments */ | 941 | *related = 1; |
931 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
932 | if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum))) | ||
933 | return NF_STOLEN; | ||
934 | } | ||
935 | 942 | ||
936 | iph = ipv6_hdr(skb); | 943 | ic = skb_header_pointer(skb, ipvsh->len, sizeof(_icmph), &_icmph); |
937 | offset = sizeof(struct ipv6hdr); | ||
938 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); | ||
939 | if (ic == NULL) | 944 | if (ic == NULL) |
940 | return NF_DROP; | 945 | return NF_DROP; |
941 | 946 | ||
942 | IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) %pI6->%pI6\n", | ||
943 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
944 | &iph->saddr, &iph->daddr); | ||
945 | |||
946 | /* | 947 | /* |
947 | * Work through seeing if this is for us. | 948 | * Work through seeing if this is for us. |
948 | * These checks are supposed to be in an order that means easy | 949 | * These checks are supposed to be in an order that means easy |
@@ -955,35 +956,35 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, | |||
955 | return NF_ACCEPT; | 956 | return NF_ACCEPT; |
956 | } | 957 | } |
957 | 958 | ||
959 | IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n", | ||
960 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
961 | &ipvsh->saddr, &ipvsh->daddr); | ||
962 | |||
958 | /* Now find the contained IP header */ | 963 | /* Now find the contained IP header */ |
959 | offset += sizeof(_icmph); | 964 | ciph.len = ipvsh->len + sizeof(_icmph); |
960 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); | 965 | ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h); |
961 | if (cih == NULL) | 966 | if (ip6h == NULL) |
962 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | 967 | return NF_ACCEPT; /* The packet looks wrong, ignore */ |
963 | 968 | ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */ | |
964 | pp = ip_vs_proto_get(cih->nexthdr); | 969 | ciph.daddr.in6 = ip6h->daddr; |
970 | /* skip possible IPv6 exthdrs of contained IPv6 packet */ | ||
971 | ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL); | ||
972 | if (ciph.protocol < 0) | ||
973 | return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */ | ||
974 | |||
975 | pp = ip_vs_proto_get(ciph.protocol); | ||
965 | if (!pp) | 976 | if (!pp) |
966 | return NF_ACCEPT; | 977 | return NF_ACCEPT; |
967 | 978 | ||
968 | /* Is the embedded protocol header present? */ | ||
969 | /* TODO: we don't support fragmentation at the moment anyways */ | ||
970 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
971 | return NF_ACCEPT; | ||
972 | |||
973 | IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, | ||
974 | "Checking outgoing ICMPv6 for"); | ||
975 | |||
976 | offset += sizeof(struct ipv6hdr); | ||
977 | |||
978 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); | ||
979 | /* The embedded headers contain source and dest in reverse order */ | 979 | /* The embedded headers contain source and dest in reverse order */ |
980 | cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1); | 980 | cp = pp->conn_out_get(AF_INET6, skb, &ciph, ciph.len, 1); |
981 | if (!cp) | 981 | if (!cp) |
982 | return NF_ACCEPT; | 982 | return NF_ACCEPT; |
983 | 983 | ||
984 | snet.in6 = iph->saddr; | 984 | snet.in6 = ciph.saddr.in6; |
985 | return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp, | 985 | writable = ciph.len; |
986 | pp, offset, sizeof(struct ipv6hdr)); | 986 | return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, |
987 | pp, writable, sizeof(struct ipv6hdr)); | ||
987 | } | 988 | } |
988 | #endif | 989 | #endif |
989 | 990 | ||
@@ -1113,7 +1114,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1113 | if (!net_ipvs(net)->enable) | 1114 | if (!net_ipvs(net)->enable) |
1114 | return NF_ACCEPT; | 1115 | return NF_ACCEPT; |
1115 | 1116 | ||
1116 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1117 | ip_vs_fill_iph_skb(af, skb, &iph); |
1117 | #ifdef CONFIG_IP_VS_IPV6 | 1118 | #ifdef CONFIG_IP_VS_IPV6 |
1118 | if (af == AF_INET6) { | 1119 | if (af == AF_INET6) { |
1119 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { | 1120 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { |
@@ -1123,7 +1124,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1123 | 1124 | ||
1124 | if (related) | 1125 | if (related) |
1125 | return verdict; | 1126 | return verdict; |
1126 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1127 | ip_vs_fill_iph_skb(af, skb, &iph); |
1127 | } | 1128 | } |
1128 | } else | 1129 | } else |
1129 | #endif | 1130 | #endif |
@@ -1133,7 +1134,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1133 | 1134 | ||
1134 | if (related) | 1135 | if (related) |
1135 | return verdict; | 1136 | return verdict; |
1136 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1137 | ip_vs_fill_ip4hdr(skb_network_header(skb), &iph); |
1137 | } | 1138 | } |
1138 | 1139 | ||
1139 | pd = ip_vs_proto_data_get(net, iph.protocol); | 1140 | pd = ip_vs_proto_data_get(net, iph.protocol); |
@@ -1143,22 +1144,14 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1143 | 1144 | ||
1144 | /* reassemble IP fragments */ | 1145 | /* reassemble IP fragments */ |
1145 | #ifdef CONFIG_IP_VS_IPV6 | 1146 | #ifdef CONFIG_IP_VS_IPV6 |
1146 | if (af == AF_INET6) { | 1147 | if (af == AF_INET) |
1147 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
1148 | if (ip_vs_gather_frags_v6(skb, | ||
1149 | ip_vs_defrag_user(hooknum))) | ||
1150 | return NF_STOLEN; | ||
1151 | } | ||
1152 | |||
1153 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
1154 | } else | ||
1155 | #endif | 1148 | #endif |
1156 | if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) { | 1149 | if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) { |
1157 | if (ip_vs_gather_frags(skb, | 1150 | if (ip_vs_gather_frags(skb, |
1158 | ip_vs_defrag_user(hooknum))) | 1151 | ip_vs_defrag_user(hooknum))) |
1159 | return NF_STOLEN; | 1152 | return NF_STOLEN; |
1160 | 1153 | ||
1161 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1154 | ip_vs_fill_ip4hdr(skb_network_header(skb), &iph); |
1162 | } | 1155 | } |
1163 | 1156 | ||
1164 | /* | 1157 | /* |
@@ -1373,9 +1366,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
1373 | "Checking incoming ICMP for"); | 1366 | "Checking incoming ICMP for"); |
1374 | 1367 | ||
1375 | offset2 = offset; | 1368 | offset2 = offset; |
1376 | offset += cih->ihl * 4; | 1369 | ip_vs_fill_ip4hdr(cih, &ciph); |
1377 | 1370 | ciph.len += offset; | |
1378 | ip_vs_fill_iphdr(AF_INET, cih, &ciph); | 1371 | offset = ciph.len; |
1379 | /* The embedded headers contain source and dest in reverse order. | 1372 | /* The embedded headers contain source and dest in reverse order. |
1380 | * For IPIP this is error for request, not for reply. | 1373 | * For IPIP this is error for request, not for reply. |
1381 | */ | 1374 | */ |
@@ -1461,34 +1454,24 @@ static int | |||
1461 | ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | 1454 | ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) |
1462 | { | 1455 | { |
1463 | struct net *net = NULL; | 1456 | struct net *net = NULL; |
1464 | struct ipv6hdr *iph; | 1457 | struct ipv6hdr _ip6h, *ip6h; |
1465 | struct icmp6hdr _icmph, *ic; | 1458 | struct icmp6hdr _icmph, *ic; |
1466 | struct ipv6hdr _ciph, *cih; /* The ip header contained | 1459 | struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */ |
1467 | within the ICMP */ | ||
1468 | struct ip_vs_iphdr ciph; | ||
1469 | struct ip_vs_conn *cp; | 1460 | struct ip_vs_conn *cp; |
1470 | struct ip_vs_protocol *pp; | 1461 | struct ip_vs_protocol *pp; |
1471 | struct ip_vs_proto_data *pd; | 1462 | struct ip_vs_proto_data *pd; |
1472 | unsigned int offset, verdict; | 1463 | unsigned int offs_ciph, writable, verdict; |
1473 | 1464 | ||
1474 | *related = 1; | 1465 | struct ip_vs_iphdr iph_stack; |
1466 | struct ip_vs_iphdr *iph = &iph_stack; | ||
1467 | ip_vs_fill_iph_skb(AF_INET6, skb, iph); | ||
1475 | 1468 | ||
1476 | /* reassemble IP fragments */ | 1469 | *related = 1; |
1477 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
1478 | if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum))) | ||
1479 | return NF_STOLEN; | ||
1480 | } | ||
1481 | 1470 | ||
1482 | iph = ipv6_hdr(skb); | 1471 | ic = skb_header_pointer(skb, iph->len, sizeof(_icmph), &_icmph); |
1483 | offset = sizeof(struct ipv6hdr); | ||
1484 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); | ||
1485 | if (ic == NULL) | 1472 | if (ic == NULL) |
1486 | return NF_DROP; | 1473 | return NF_DROP; |
1487 | 1474 | ||
1488 | IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", | ||
1489 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
1490 | &iph->saddr, &iph->daddr); | ||
1491 | |||
1492 | /* | 1475 | /* |
1493 | * Work through seeing if this is for us. | 1476 | * Work through seeing if this is for us. |
1494 | * These checks are supposed to be in an order that means easy | 1477 | * These checks are supposed to be in an order that means easy |
@@ -1501,40 +1484,51 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
1501 | return NF_ACCEPT; | 1484 | return NF_ACCEPT; |
1502 | } | 1485 | } |
1503 | 1486 | ||
1487 | IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", | ||
1488 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
1489 | &iph->saddr, &iph->daddr); | ||
1490 | |||
1504 | /* Now find the contained IP header */ | 1491 | /* Now find the contained IP header */ |
1505 | offset += sizeof(_icmph); | 1492 | ciph.len = iph->len + sizeof(_icmph); |
1506 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); | 1493 | offs_ciph = ciph.len; /* Save ip header offset */ |
1507 | if (cih == NULL) | 1494 | ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h); |
1495 | if (ip6h == NULL) | ||
1508 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | 1496 | return NF_ACCEPT; /* The packet looks wrong, ignore */ |
1497 | ciph.saddr.in6 = ip6h->saddr; /* conn_in_get() handles reverse order */ | ||
1498 | ciph.daddr.in6 = ip6h->daddr; | ||
1499 | /* skip possible IPv6 exthdrs of contained IPv6 packet */ | ||
1500 | ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL); | ||
1501 | if (ciph.protocol < 0) | ||
1502 | return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */ | ||
1509 | 1503 | ||
1510 | net = skb_net(skb); | 1504 | net = skb_net(skb); |
1511 | pd = ip_vs_proto_data_get(net, cih->nexthdr); | 1505 | pd = ip_vs_proto_data_get(net, ciph.protocol); |
1512 | if (!pd) | 1506 | if (!pd) |
1513 | return NF_ACCEPT; | 1507 | return NF_ACCEPT; |
1514 | pp = pd->pp; | 1508 | pp = pd->pp; |
1515 | 1509 | ||
1516 | /* Is the embedded protocol header present? */ | 1510 | /* Cannot handle fragmented embedded protocol */ |
1517 | /* TODO: we don't support fragmentation at the moment anyways */ | 1511 | if (ciph.fragoffs) |
1518 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
1519 | return NF_ACCEPT; | 1512 | return NF_ACCEPT; |
1520 | 1513 | ||
1521 | IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, | 1514 | IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph, |
1522 | "Checking incoming ICMPv6 for"); | 1515 | "Checking incoming ICMPv6 for"); |
1523 | 1516 | ||
1524 | offset += sizeof(struct ipv6hdr); | ||
1525 | |||
1526 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); | ||
1527 | /* The embedded headers contain source and dest in reverse order */ | 1517 | /* The embedded headers contain source and dest in reverse order */ |
1528 | cp = pp->conn_in_get(AF_INET6, skb, &ciph, offset, 1); | 1518 | cp = pp->conn_in_get(AF_INET6, skb, &ciph, ciph.len, 1); |
1529 | if (!cp) | 1519 | if (!cp) |
1530 | return NF_ACCEPT; | 1520 | return NF_ACCEPT; |
1531 | 1521 | ||
1532 | /* do the statistics and put it back */ | 1522 | /* do the statistics and put it back */ |
1533 | ip_vs_in_stats(cp, skb); | 1523 | ip_vs_in_stats(cp, skb); |
1534 | if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr || | 1524 | |
1535 | IPPROTO_SCTP == cih->nexthdr) | 1525 | /* Need to mangle contained IPv6 header in ICMPv6 packet */ |
1536 | offset += 2 * sizeof(__u16); | 1526 | writable = ciph.len; |
1537 | verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum); | 1527 | if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || |
1528 | IPPROTO_SCTP == ciph.protocol) | ||
1529 | writable += 2 * sizeof(__u16); /* Also mangle ports */ | ||
1530 | |||
1531 | verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, writable, hooknum); | ||
1538 | 1532 | ||
1539 | __ip_vs_conn_put(cp); | 1533 | __ip_vs_conn_put(cp); |
1540 | 1534 | ||
@@ -1570,7 +1564,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1570 | if (unlikely((skb->pkt_type != PACKET_HOST && | 1564 | if (unlikely((skb->pkt_type != PACKET_HOST && |
1571 | hooknum != NF_INET_LOCAL_OUT) || | 1565 | hooknum != NF_INET_LOCAL_OUT) || |
1572 | !skb_dst(skb))) { | 1566 | !skb_dst(skb))) { |
1573 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1567 | ip_vs_fill_iph_skb(af, skb, &iph); |
1574 | IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s" | 1568 | IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s" |
1575 | " ignored in hook %u\n", | 1569 | " ignored in hook %u\n", |
1576 | skb->pkt_type, iph.protocol, | 1570 | skb->pkt_type, iph.protocol, |
@@ -1582,7 +1576,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1582 | if (!net_ipvs(net)->enable) | 1576 | if (!net_ipvs(net)->enable) |
1583 | return NF_ACCEPT; | 1577 | return NF_ACCEPT; |
1584 | 1578 | ||
1585 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 1579 | ip_vs_fill_iph_skb(af, skb, &iph); |
1586 | 1580 | ||
1587 | /* Bad... Do not break raw sockets */ | 1581 | /* Bad... Do not break raw sockets */ |
1588 | if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT && | 1582 | if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT && |
@@ -1602,7 +1596,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1602 | 1596 | ||
1603 | if (related) | 1597 | if (related) |
1604 | return verdict; | 1598 | return verdict; |
1605 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
1606 | } | 1599 | } |
1607 | } else | 1600 | } else |
1608 | #endif | 1601 | #endif |
@@ -1612,7 +1605,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1612 | 1605 | ||
1613 | if (related) | 1606 | if (related) |
1614 | return verdict; | 1607 | return verdict; |
1615 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
1616 | } | 1608 | } |
1617 | 1609 | ||
1618 | /* Protocol supported? */ | 1610 | /* Protocol supported? */ |
@@ -1622,10 +1614,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1622 | pp = pd->pp; | 1614 | pp = pd->pp; |
1623 | /* | 1615 | /* |
1624 | * Check if the packet belongs to an existing connection entry | 1616 | * Check if the packet belongs to an existing connection entry |
1617 | * Only sched first IPv6 fragment. | ||
1625 | */ | 1618 | */ |
1626 | cp = pp->conn_in_get(af, skb, &iph, iph.len, 0); | 1619 | cp = pp->conn_in_get(af, skb, &iph, iph.len, 0); |
1627 | 1620 | ||
1628 | if (unlikely(!cp)) { | 1621 | if (unlikely(!cp) && !iph.fragoffs) { |
1629 | int v; | 1622 | int v; |
1630 | 1623 | ||
1631 | if (!pp->conn_schedule(af, skb, pd, &v, &cp)) | 1624 | if (!pp->conn_schedule(af, skb, pd, &v, &cp)) |
@@ -1789,8 +1782,10 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, | |||
1789 | { | 1782 | { |
1790 | int r; | 1783 | int r; |
1791 | struct net *net; | 1784 | struct net *net; |
1785 | struct ip_vs_iphdr iphdr; | ||
1792 | 1786 | ||
1793 | if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) | 1787 | ip_vs_fill_iph_skb(AF_INET6, skb, &iphdr); |
1788 | if (iphdr.protocol != IPPROTO_ICMPV6) | ||
1794 | return NF_ACCEPT; | 1789 | return NF_ACCEPT; |
1795 | 1790 | ||
1796 | /* ipvs enabled in this netns ? */ | 1791 | /* ipvs enabled in this netns ? */ |
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 8b7dca9ea42..7f3b0cc00b7 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c | |||
@@ -215,7 +215,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
215 | struct ip_vs_dh_bucket *tbl; | 215 | struct ip_vs_dh_bucket *tbl; |
216 | struct ip_vs_iphdr iph; | 216 | struct ip_vs_iphdr iph; |
217 | 217 | ||
218 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 218 | ip_vs_fill_iph_addr_only(svc->af, skb, &iph); |
219 | 219 | ||
220 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); | 220 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); |
221 | 221 | ||
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index df646ccf08a..cbd37489ac7 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c | |||
@@ -479,7 +479,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
479 | struct ip_vs_dest *dest = NULL; | 479 | struct ip_vs_dest *dest = NULL; |
480 | struct ip_vs_lblc_entry *en; | 480 | struct ip_vs_lblc_entry *en; |
481 | 481 | ||
482 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 482 | ip_vs_fill_iph_addr_only(svc->af, skb, &iph); |
483 | 483 | ||
484 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); | 484 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); |
485 | 485 | ||
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 570e31ea427..161b67972e3 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c | |||
@@ -649,7 +649,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
649 | struct ip_vs_dest *dest = NULL; | 649 | struct ip_vs_dest *dest = NULL; |
650 | struct ip_vs_lblcr_entry *en; | 650 | struct ip_vs_lblcr_entry *en; |
651 | 651 | ||
652 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 652 | ip_vs_fill_iph_addr_only(svc->af, skb, &iph); |
653 | 653 | ||
654 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); | 654 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); |
655 | 655 | ||
diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c index 1aa5cac748c..ee4e2e3cc08 100644 --- a/net/netfilter/ipvs/ip_vs_pe_sip.c +++ b/net/netfilter/ipvs/ip_vs_pe_sip.c | |||
@@ -73,7 +73,7 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) | |||
73 | const char *dptr; | 73 | const char *dptr; |
74 | int retc; | 74 | int retc; |
75 | 75 | ||
76 | ip_vs_fill_iphdr(p->af, skb_network_header(skb), &iph); | 76 | ip_vs_fill_iph_skb(p->af, skb, &iph); |
77 | 77 | ||
78 | /* Only useful with UDP */ | 78 | /* Only useful with UDP */ |
79 | if (iph.protocol != IPPROTO_UDP) | 79 | if (iph.protocol != IPPROTO_UDP) |
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 9f3fb751c49..b903db60341 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c | |||
@@ -18,7 +18,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, | |||
18 | sctp_sctphdr_t *sh, _sctph; | 18 | sctp_sctphdr_t *sh, _sctph; |
19 | struct ip_vs_iphdr iph; | 19 | struct ip_vs_iphdr iph; |
20 | 20 | ||
21 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 21 | ip_vs_fill_iph_skb(af, skb, &iph); |
22 | 22 | ||
23 | sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph); | 23 | sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph); |
24 | if (sh == NULL) | 24 | if (sh == NULL) |
@@ -72,12 +72,14 @@ sctp_snat_handler(struct sk_buff *skb, | |||
72 | struct sk_buff *iter; | 72 | struct sk_buff *iter; |
73 | __be32 crc32; | 73 | __be32 crc32; |
74 | 74 | ||
75 | struct ip_vs_iphdr iph; | ||
76 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
77 | sctphoff = iph.len; | ||
78 | |||
75 | #ifdef CONFIG_IP_VS_IPV6 | 79 | #ifdef CONFIG_IP_VS_IPV6 |
76 | if (cp->af == AF_INET6) | 80 | if (cp->af == AF_INET6 && iph.fragoffs) |
77 | sctphoff = sizeof(struct ipv6hdr); | 81 | return 1; |
78 | else | ||
79 | #endif | 82 | #endif |
80 | sctphoff = ip_hdrlen(skb); | ||
81 | 83 | ||
82 | /* csum_check requires unshared skb */ | 84 | /* csum_check requires unshared skb */ |
83 | if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) | 85 | if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) |
@@ -116,12 +118,14 @@ sctp_dnat_handler(struct sk_buff *skb, | |||
116 | struct sk_buff *iter; | 118 | struct sk_buff *iter; |
117 | __be32 crc32; | 119 | __be32 crc32; |
118 | 120 | ||
121 | struct ip_vs_iphdr iph; | ||
122 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
123 | sctphoff = iph.len; | ||
124 | |||
119 | #ifdef CONFIG_IP_VS_IPV6 | 125 | #ifdef CONFIG_IP_VS_IPV6 |
120 | if (cp->af == AF_INET6) | 126 | if (cp->af == AF_INET6 && iph.fragoffs) |
121 | sctphoff = sizeof(struct ipv6hdr); | 127 | return 1; |
122 | else | ||
123 | #endif | 128 | #endif |
124 | sctphoff = ip_hdrlen(skb); | ||
125 | 129 | ||
126 | /* csum_check requires unshared skb */ | 130 | /* csum_check requires unshared skb */ |
127 | if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) | 131 | if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) |
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index cd609cc6272..8a96069c002 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c | |||
@@ -40,7 +40,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, | |||
40 | struct tcphdr _tcph, *th; | 40 | struct tcphdr _tcph, *th; |
41 | struct ip_vs_iphdr iph; | 41 | struct ip_vs_iphdr iph; |
42 | 42 | ||
43 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 43 | ip_vs_fill_iph_skb(af, skb, &iph); |
44 | 44 | ||
45 | th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph); | 45 | th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph); |
46 | if (th == NULL) { | 46 | if (th == NULL) { |
@@ -136,12 +136,14 @@ tcp_snat_handler(struct sk_buff *skb, | |||
136 | int oldlen; | 136 | int oldlen; |
137 | int payload_csum = 0; | 137 | int payload_csum = 0; |
138 | 138 | ||
139 | struct ip_vs_iphdr iph; | ||
140 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
141 | tcphoff = iph.len; | ||
142 | |||
139 | #ifdef CONFIG_IP_VS_IPV6 | 143 | #ifdef CONFIG_IP_VS_IPV6 |
140 | if (cp->af == AF_INET6) | 144 | if (cp->af == AF_INET6 && iph.fragoffs) |
141 | tcphoff = sizeof(struct ipv6hdr); | 145 | return 1; |
142 | else | ||
143 | #endif | 146 | #endif |
144 | tcphoff = ip_hdrlen(skb); | ||
145 | oldlen = skb->len - tcphoff; | 147 | oldlen = skb->len - tcphoff; |
146 | 148 | ||
147 | /* csum_check requires unshared skb */ | 149 | /* csum_check requires unshared skb */ |
@@ -216,12 +218,14 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
216 | int oldlen; | 218 | int oldlen; |
217 | int payload_csum = 0; | 219 | int payload_csum = 0; |
218 | 220 | ||
221 | struct ip_vs_iphdr iph; | ||
222 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
223 | tcphoff = iph.len; | ||
224 | |||
219 | #ifdef CONFIG_IP_VS_IPV6 | 225 | #ifdef CONFIG_IP_VS_IPV6 |
220 | if (cp->af == AF_INET6) | 226 | if (cp->af == AF_INET6 && iph.fragoffs) |
221 | tcphoff = sizeof(struct ipv6hdr); | 227 | return 1; |
222 | else | ||
223 | #endif | 228 | #endif |
224 | tcphoff = ip_hdrlen(skb); | ||
225 | oldlen = skb->len - tcphoff; | 229 | oldlen = skb->len - tcphoff; |
226 | 230 | ||
227 | /* csum_check requires unshared skb */ | 231 | /* csum_check requires unshared skb */ |
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 2fedb2dcb3d..d6f4eeec8b7 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c | |||
@@ -37,7 +37,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, | |||
37 | struct udphdr _udph, *uh; | 37 | struct udphdr _udph, *uh; |
38 | struct ip_vs_iphdr iph; | 38 | struct ip_vs_iphdr iph; |
39 | 39 | ||
40 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | 40 | ip_vs_fill_iph_skb(af, skb, &iph); |
41 | 41 | ||
42 | uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph); | 42 | uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph); |
43 | if (uh == NULL) { | 43 | if (uh == NULL) { |
@@ -133,12 +133,14 @@ udp_snat_handler(struct sk_buff *skb, | |||
133 | int oldlen; | 133 | int oldlen; |
134 | int payload_csum = 0; | 134 | int payload_csum = 0; |
135 | 135 | ||
136 | struct ip_vs_iphdr iph; | ||
137 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
138 | udphoff = iph.len; | ||
139 | |||
136 | #ifdef CONFIG_IP_VS_IPV6 | 140 | #ifdef CONFIG_IP_VS_IPV6 |
137 | if (cp->af == AF_INET6) | 141 | if (cp->af == AF_INET6 && iph.fragoffs) |
138 | udphoff = sizeof(struct ipv6hdr); | 142 | return 1; |
139 | else | ||
140 | #endif | 143 | #endif |
141 | udphoff = ip_hdrlen(skb); | ||
142 | oldlen = skb->len - udphoff; | 144 | oldlen = skb->len - udphoff; |
143 | 145 | ||
144 | /* csum_check requires unshared skb */ | 146 | /* csum_check requires unshared skb */ |
@@ -218,12 +220,14 @@ udp_dnat_handler(struct sk_buff *skb, | |||
218 | int oldlen; | 220 | int oldlen; |
219 | int payload_csum = 0; | 221 | int payload_csum = 0; |
220 | 222 | ||
223 | struct ip_vs_iphdr iph; | ||
224 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
225 | udphoff = iph.len; | ||
226 | |||
221 | #ifdef CONFIG_IP_VS_IPV6 | 227 | #ifdef CONFIG_IP_VS_IPV6 |
222 | if (cp->af == AF_INET6) | 228 | if (cp->af == AF_INET6 && iph.fragoffs) |
223 | udphoff = sizeof(struct ipv6hdr); | 229 | return 1; |
224 | else | ||
225 | #endif | 230 | #endif |
226 | udphoff = ip_hdrlen(skb); | ||
227 | oldlen = skb->len - udphoff; | 231 | oldlen = skb->len - udphoff; |
228 | 232 | ||
229 | /* csum_check requires unshared skb */ | 233 | /* csum_check requires unshared skb */ |
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index 05126521743..e3312699462 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c | |||
@@ -228,7 +228,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
228 | struct ip_vs_sh_bucket *tbl; | 228 | struct ip_vs_sh_bucket *tbl; |
229 | struct ip_vs_iphdr iph; | 229 | struct ip_vs_iphdr iph; |
230 | 230 | ||
231 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | 231 | ip_vs_fill_iph_addr_only(svc->af, skb, &iph); |
232 | 232 | ||
233 | IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n"); | 233 | IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n"); |
234 | 234 | ||
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 1060bd52a16..428de757957 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c | |||
@@ -679,14 +679,15 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
679 | struct rt6_info *rt; /* Route to the other host */ | 679 | struct rt6_info *rt; /* Route to the other host */ |
680 | int mtu; | 680 | int mtu; |
681 | int local; | 681 | int local; |
682 | struct ip_vs_iphdr iph; | ||
682 | 683 | ||
683 | EnterFunction(10); | 684 | EnterFunction(10); |
685 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
684 | 686 | ||
685 | /* check if it is a connection of no-client-port */ | 687 | /* check if it is a connection of no-client-port */ |
686 | if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { | 688 | if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { |
687 | __be16 _pt, *p; | 689 | __be16 _pt, *p; |
688 | p = skb_header_pointer(skb, sizeof(struct ipv6hdr), | 690 | p = skb_header_pointer(skb, iph.len, sizeof(_pt), &_pt); |
689 | sizeof(_pt), &_pt); | ||
690 | if (p == NULL) | 691 | if (p == NULL) |
691 | goto tx_error; | 692 | goto tx_error; |
692 | ip_vs_conn_fill_cport(cp, *p); | 693 | ip_vs_conn_fill_cport(cp, *p); |
diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c index bb10b0717f1..3f9b8cde245 100644 --- a/net/netfilter/xt_ipvs.c +++ b/net/netfilter/xt_ipvs.c | |||
@@ -67,7 +67,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
67 | goto out; | 67 | goto out; |
68 | } | 68 | } |
69 | 69 | ||
70 | ip_vs_fill_iphdr(family, skb_network_header(skb), &iph); | 70 | ip_vs_fill_iph_skb(family, skb, &iph); |
71 | 71 | ||
72 | if (data->bitmask & XT_IPVS_PROTO) | 72 | if (data->bitmask & XT_IPVS_PROTO) |
73 | if ((iph.protocol == data->l4proto) ^ | 73 | if ((iph.protocol == data->l4proto) ^ |