diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2012-09-26 14:06:41 +0200 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2012-09-28 11:34:15 +0900 |
commit | 63dca2c0b0e7a92cb39d1b1ecefa32ffda201975 (patch) | |
tree | 4998cbf50a15ab5a939dc8cd722c668f601ba3cc /net/netfilter/ipvs/ip_vs_proto_sctp.c | |
parent | ipvs: Use config macro IS_ENABLED() (diff) | |
download | linux-dev-63dca2c0b0e7a92cb39d1b1ecefa32ffda201975.tar.xz linux-dev-63dca2c0b0e7a92cb39d1b1ecefa32ffda201975.zip |
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/ipvs/ip_vs_proto_sctp.c')
-rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_sctp.c | 22 |
1 files changed, 13 insertions, 9 deletions
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 9f3fb751c491..b903db60341e 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, sctp_sctphdr_t *sh, _sctph; struct ip_vs_iphdr iph; - ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); + ip_vs_fill_iph_skb(af, skb, &iph); sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph); if (sh == NULL) @@ -72,12 +72,14 @@ sctp_snat_handler(struct sk_buff *skb, struct sk_buff *iter; __be32 crc32; + struct ip_vs_iphdr iph; + ip_vs_fill_iph_skb(cp->af, skb, &iph); + sctphoff = iph.len; + #ifdef CONFIG_IP_VS_IPV6 - if (cp->af == AF_INET6) - sctphoff = sizeof(struct ipv6hdr); - else + if (cp->af == AF_INET6 && iph.fragoffs) + return 1; #endif - sctphoff = ip_hdrlen(skb); /* csum_check requires unshared skb */ if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) @@ -116,12 +118,14 @@ sctp_dnat_handler(struct sk_buff *skb, struct sk_buff *iter; __be32 crc32; + struct ip_vs_iphdr iph; + ip_vs_fill_iph_skb(cp->af, skb, &iph); + sctphoff = iph.len; + #ifdef CONFIG_IP_VS_IPV6 - if (cp->af == AF_INET6) - sctphoff = sizeof(struct ipv6hdr); - else + if (cp->af == AF_INET6 && iph.fragoffs) + return 1; #endif - sctphoff = ip_hdrlen(skb); /* csum_check requires unshared skb */ if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) |