Commit 05cfe986 authored by Julian Anastasov's avatar Julian Anastasov Committed by Florian Westphal
Browse files

ipvs: skip ipv6 extension headers for csum checks



Protocol checksum validation fails for IPv6 if there are extension
headers before the protocol header. iph->len already contains its
offset, so use it to fix the problem.

Fixes: 2906f66a ("ipvs: SCTP Trasport Loadbalancing Support")
Fixes: 0bbdd42b ("IPVS: Extend protocol DNAT/SNAT and state handlers")
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
parent 4edd4ba7
Loading
Loading
Loading
Loading
+6 −12
Original line number Diff line number Diff line
@@ -10,7 +10,8 @@
#include <net/ip_vs.h>

static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		unsigned int sctphoff);

static int
sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -108,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!sctp_csum_check(cp->af, skb, pp))
		if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
			return 0;

		/* Call application helper if needed */
@@ -156,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!sctp_csum_check(cp->af, skb, pp))
		if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
			return 0;

		/* Call application helper if needed */
@@ -185,19 +186,12 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
}

static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		unsigned int sctphoff)
{
	unsigned int sctphoff;
	struct sctphdr *sh;
	__le32 cmp, val;

#ifdef CONFIG_IP_VS_IPV6
	if (af == AF_INET6)
		sctphoff = sizeof(struct ipv6hdr);
	else
#endif
		sctphoff = ip_hdrlen(skb);

	sh = (struct sctphdr *)(skb->data + sctphoff);
	cmp = sh->checksum;
	val = sctp_compute_cksum(skb, sctphoff);
+7 −14
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@
#include <net/ip_vs.h>

static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
	       unsigned int tcphoff);

static int
tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -165,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!tcp_csum_check(cp->af, skb, pp))
		if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
			return 0;

		/* Call application helper if needed */
@@ -243,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!tcp_csum_check(cp->af, skb, pp))
		if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
			return 0;

		/*
@@ -300,17 +301,9 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,


static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
	       unsigned int tcphoff)
{
	unsigned int tcphoff;

#ifdef CONFIG_IP_VS_IPV6
	if (af == AF_INET6)
		tcphoff = sizeof(struct ipv6hdr);
	else
#endif
		tcphoff = ip_hdrlen(skb);

	switch (skb->ip_summed) {
	case CHECKSUM_NONE:
		skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
@@ -321,7 +314,7 @@ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
			if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
					    &ipv6_hdr(skb)->daddr,
					    skb->len - tcphoff,
					    ipv6_hdr(skb)->nexthdr,
					    IPPROTO_TCP,
					    skb->csum)) {
				IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
						 "Failed checksum for");
+7 −13
Original line number Diff line number Diff line
@@ -24,7 +24,8 @@
#include <net/ip6_checksum.h>

static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
	       unsigned int udphoff);

static int
udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -154,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!udp_csum_check(cp->af, skb, pp))
		if (!udp_csum_check(cp->af, skb, pp, udphoff))
			return 0;

		/*
@@ -237,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
		int ret;

		/* Some checks before mangling */
		if (!udp_csum_check(cp->af, skb, pp))
		if (!udp_csum_check(cp->af, skb, pp, udphoff))
			return 0;

		/*
@@ -296,17 +297,10 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,


static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
	       unsigned int udphoff)
{
	struct udphdr _udph, *uh;
	unsigned int udphoff;

#ifdef CONFIG_IP_VS_IPV6
	if (af == AF_INET6)
		udphoff = sizeof(struct ipv6hdr);
	else
#endif
		udphoff = ip_hdrlen(skb);

	uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
	if (uh == NULL)
@@ -324,7 +318,7 @@ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
				if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
						    &ipv6_hdr(skb)->daddr,
						    skb->len - udphoff,
						    ipv6_hdr(skb)->nexthdr,
						    IPPROTO_UDP,
						    skb->csum)) {
					IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
							 "Failed checksum for");