Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51ef348b authored by Julius Volz's avatar Julius Volz Committed by Simon Horman
Browse files

IPVS: Add 'af' args to protocol handler functions



Add 'af' arguments to conn_schedule(), conn_in_get(), conn_out_get() and
csum_check() function pointers in struct ip_vs_protocol. Extend the
respective functions for TCP, UDP, AH and ESP and adjust the callers.

The changes in the callers need to be somewhat extensive, since they now
need to pass a filled out struct ip_vs_iphdr * to the modified functions
instead of a struct iphdr *.

Signed-off-by: default avatarJulius Volz <juliusv@google.com>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
parent b14198f6
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -296,21 +296,23 @@ struct ip_vs_protocol {

	void (*exit)(struct ip_vs_protocol *pp);

	int (*conn_schedule)(struct sk_buff *skb,
	int (*conn_schedule)(int af, struct sk_buff *skb,
			     struct ip_vs_protocol *pp,
			     int *verdict, struct ip_vs_conn **cpp);

	struct ip_vs_conn *
	(*conn_in_get)(const struct sk_buff *skb,
	(*conn_in_get)(int af,
		       const struct sk_buff *skb,
		       struct ip_vs_protocol *pp,
		       const struct iphdr *iph,
		       const struct ip_vs_iphdr *iph,
		       unsigned int proto_off,
		       int inverse);

	struct ip_vs_conn *
	(*conn_out_get)(const struct sk_buff *skb,
	(*conn_out_get)(int af,
			const struct sk_buff *skb,
			struct ip_vs_protocol *pp,
			const struct iphdr *iph,
			const struct ip_vs_iphdr *iph,
			unsigned int proto_off,
			int inverse);

@@ -320,7 +322,8 @@ struct ip_vs_protocol {
	int (*dnat_handler)(struct sk_buff *skb,
			    struct ip_vs_protocol *pp, struct ip_vs_conn *cp);

	int (*csum_check)(struct sk_buff *skb, struct ip_vs_protocol *pp);
	int (*csum_check)(int af, struct sk_buff *skb,
			  struct ip_vs_protocol *pp);

	const char *(*state_name)(int state);

+32 −32
Original line number Diff line number Diff line
@@ -572,6 +572,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
	struct iphdr *iph;
	struct icmphdr	_icmph, *ic;
	struct iphdr	_ciph, *cih;	/* The ip header contained within the ICMP */
	struct ip_vs_iphdr ciph;
	struct ip_vs_conn *cp;
	struct ip_vs_protocol *pp;
	unsigned int offset, ihl, verdict;
@@ -627,8 +628,9 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)

	offset += cih->ihl * 4;

	ip_vs_fill_iphdr(AF_INET, cih, &ciph);
	/* The embedded headers contain source and dest in reverse order */
	cp = pp->conn_out_get(skb, pp, cih, offset, 1);
	cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1);
	if (!cp)
		return NF_ACCEPT;

@@ -686,43 +688,41 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
	  const struct net_device *in, const struct net_device *out,
	  int (*okfn)(struct sk_buff *))
{
	struct iphdr	*iph;
	struct ip_vs_iphdr iph;
	struct ip_vs_protocol *pp;
	struct ip_vs_conn *cp;
	int ihl;

	EnterFunction(11);

	if (skb->ipvs_property)
		return NF_ACCEPT;

	iph = ip_hdr(skb);
	if (unlikely(iph->protocol == IPPROTO_ICMP)) {
	ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	if (unlikely(iph.protocol == IPPROTO_ICMP)) {
		int related, verdict = ip_vs_out_icmp(skb, &related);

		if (related)
			return verdict;
		iph = ip_hdr(skb);
		ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	}

	pp = ip_vs_proto_get(iph->protocol);
	pp = ip_vs_proto_get(iph.protocol);
	if (unlikely(!pp))
		return NF_ACCEPT;

	/* reassemble IP fragments */
	if (unlikely(iph->frag_off & htons(IP_MF|IP_OFFSET) &&
	if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) &&
		     !pp->dont_defrag)) {
		if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT))
			return NF_STOLEN;
		iph = ip_hdr(skb);
	}

	ihl = iph->ihl << 2;
		ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	}

	/*
	 * Check if the packet belongs to an existing entry
	 */
	cp = pp->conn_out_get(skb, pp, iph, ihl, 0);
	cp = pp->conn_out_get(AF_INET, skb, pp, &iph, iph.len, 0);

	if (unlikely(!cp)) {
		if (sysctl_ip_vs_nat_icmp_send &&
@@ -730,18 +730,18 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
		     pp->protocol == IPPROTO_UDP)) {
			__be16 _ports[2], *pptr;

			pptr = skb_header_pointer(skb, ihl,
			pptr = skb_header_pointer(skb, iph.len,
						  sizeof(_ports), _ports);
			if (pptr == NULL)
				return NF_ACCEPT;	/* Not for me */
			if (ip_vs_lookup_real_service(iph->protocol,
						      iph->saddr, pptr[0])) {
			if (ip_vs_lookup_real_service(iph.protocol,
						      iph.saddr.ip, pptr[0])) {
				/*
				 * Notify the real server: there is no
				 * existing entry if it is not RST
				 * packet or not TCP packet.
				 */
				if (iph->protocol != IPPROTO_TCP
				if (iph.protocol != IPPROTO_TCP
				    || !is_tcp_reset(skb)) {
					icmp_send(skb,ICMP_DEST_UNREACH,
						  ICMP_PORT_UNREACH, 0);
@@ -756,7 +756,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,

	IP_VS_DBG_PKT(11, pp, skb, 0, "Outgoing packet");

	if (!skb_make_writable(skb, ihl))
	if (!skb_make_writable(skb, iph.len))
		goto drop;

	/* mangle the packet */
@@ -804,6 +804,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
	struct iphdr *iph;
	struct icmphdr	_icmph, *ic;
	struct iphdr	_ciph, *cih;	/* The ip header contained within the ICMP */
	struct ip_vs_iphdr ciph;
	struct ip_vs_conn *cp;
	struct ip_vs_protocol *pp;
	unsigned int offset, ihl, verdict;
@@ -860,8 +861,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)

	offset += cih->ihl * 4;

	ip_vs_fill_iphdr(AF_INET, cih, &ciph);
	/* The embedded headers contain source and dest in reverse order */
	cp = pp->conn_in_get(skb, pp, cih, offset, 1);
	cp = pp->conn_in_get(AF_INET, skb, pp, &ciph, offset, 1);
	if (!cp)
		return NF_ACCEPT;

@@ -897,11 +899,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
	 const struct net_device *in, const struct net_device *out,
	 int (*okfn)(struct sk_buff *))
{
	struct iphdr	*iph;
	struct ip_vs_iphdr iph;
	struct ip_vs_protocol *pp;
	struct ip_vs_conn *cp;
	int ret, restart;
	int ihl;

	ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);

	/*
	 *	Big tappo: only PACKET_HOST (neither loopback nor mcasts)
@@ -909,38 +912,35 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
	 */
	if (unlikely(skb->pkt_type != PACKET_HOST
		     || skb->dev->flags & IFF_LOOPBACK || skb->sk)) {
		IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
		IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n",
			      skb->pkt_type,
			  ip_hdr(skb)->protocol,
			  NIPQUAD(ip_hdr(skb)->daddr));
			      iph.protocol,
			      IP_VS_DBG_ADDR(AF_INET, &iph.daddr));
		return NF_ACCEPT;
	}

	iph = ip_hdr(skb);
	if (unlikely(iph->protocol == IPPROTO_ICMP)) {
	if (unlikely(iph.protocol == IPPROTO_ICMP)) {
		int related, verdict = ip_vs_in_icmp(skb, &related, hooknum);

		if (related)
			return verdict;
		iph = ip_hdr(skb);
		ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	}

	/* Protocol supported? */
	pp = ip_vs_proto_get(iph->protocol);
	pp = ip_vs_proto_get(iph.protocol);
	if (unlikely(!pp))
		return NF_ACCEPT;

	ihl = iph->ihl << 2;

	/*
	 * Check if the packet belongs to an existing connection entry
	 */
	cp = pp->conn_in_get(skb, pp, iph, ihl, 0);
	cp = pp->conn_in_get(AF_INET, skb, pp, &iph, iph.len, 0);

	if (unlikely(!cp)) {
		int v;

		if (!pp->conn_schedule(skb, pp, &v, &cp))
		if (!pp->conn_schedule(AF_INET, skb, pp, &v, &cp))
			return v;
	}

+28 −28
Original line number Diff line number Diff line
@@ -39,25 +39,23 @@ struct isakmp_hdr {


static struct ip_vs_conn *
ah_esp_conn_in_get(const struct sk_buff *skb,
		   struct ip_vs_protocol *pp,
		   const struct iphdr *iph,
		   unsigned int proto_off,
ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
		   const struct ip_vs_iphdr *iph, unsigned int proto_off,
		   int inverse)
{
	struct ip_vs_conn *cp;

	if (likely(!inverse)) {
		cp = ip_vs_conn_in_get(IPPROTO_UDP,
				       iph->saddr,
				       iph->saddr.ip,
				       htons(PORT_ISAKMP),
				       iph->daddr,
				       iph->daddr.ip,
				       htons(PORT_ISAKMP));
	} else {
		cp = ip_vs_conn_in_get(IPPROTO_UDP,
				       iph->daddr,
				       iph->daddr.ip,
				       htons(PORT_ISAKMP),
				       iph->saddr,
				       iph->saddr.ip,
				       htons(PORT_ISAKMP));
	}

@@ -66,12 +64,12 @@ ah_esp_conn_in_get(const struct sk_buff *skb,
		 * We are not sure if the packet is from our
		 * service, so our conn_schedule hook should return NF_ACCEPT
		 */
		IP_VS_DBG(12, "Unknown ISAKMP entry for outin packet "
			  "%s%s %u.%u.%u.%u->%u.%u.%u.%u\n",
		IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet "
			      "%s%s %s->%s\n",
			      inverse ? "ICMP+" : "",
			      pp->name,
			  NIPQUAD(iph->saddr),
			  NIPQUAD(iph->daddr));
			      IP_VS_DBG_ADDR(af, &iph->saddr),
			      IP_VS_DBG_ADDR(af, &iph->daddr));
	}

	return cp;
@@ -79,32 +77,35 @@ ah_esp_conn_in_get(const struct sk_buff *skb,


static struct ip_vs_conn *
ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
		    const struct iphdr *iph, unsigned int proto_off, int inverse)
ah_esp_conn_out_get(int af, const struct sk_buff *skb,
		    struct ip_vs_protocol *pp,
		    const struct ip_vs_iphdr *iph,
		    unsigned int proto_off,
		    int inverse)
{
	struct ip_vs_conn *cp;

	if (likely(!inverse)) {
		cp = ip_vs_conn_out_get(IPPROTO_UDP,
					iph->saddr,
					iph->saddr.ip,
					htons(PORT_ISAKMP),
					iph->daddr,
					iph->daddr.ip,
					htons(PORT_ISAKMP));
	} else {
		cp = ip_vs_conn_out_get(IPPROTO_UDP,
					iph->daddr,
					iph->daddr.ip,
					htons(PORT_ISAKMP),
					iph->saddr,
					iph->saddr.ip,
					htons(PORT_ISAKMP));
	}

	if (!cp) {
		IP_VS_DBG(12, "Unknown ISAKMP entry for inout packet "
			  "%s%s %u.%u.%u.%u->%u.%u.%u.%u\n",
		IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet "
			      "%s%s %s->%s\n",
			      inverse ? "ICMP+" : "",
			      pp->name,
			  NIPQUAD(iph->saddr),
			  NIPQUAD(iph->daddr));
			      IP_VS_DBG_ADDR(af, &iph->saddr),
			      IP_VS_DBG_ADDR(af, &iph->daddr));
	}

	return cp;
@@ -112,8 +113,7 @@ ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,


static int
ah_esp_conn_schedule(struct sk_buff *skb,
		     struct ip_vs_protocol *pp,
ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		     int *verdict, struct ip_vs_conn **cpp)
{
	/*
+51 −28
Original line number Diff line number Diff line
@@ -25,8 +25,9 @@


static struct ip_vs_conn *
tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
		const struct iphdr *iph, unsigned int proto_off, int inverse)
tcp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
		const struct ip_vs_iphdr *iph, unsigned int proto_off,
		int inverse)
{
	__be16 _ports[2], *pptr;

@@ -36,18 +37,19 @@ tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,

	if (likely(!inverse)) {
		return ip_vs_conn_in_get(iph->protocol,
					 iph->saddr, pptr[0],
					 iph->daddr, pptr[1]);
					 iph->saddr.ip, pptr[0],
					 iph->daddr.ip, pptr[1]);
	} else {
		return ip_vs_conn_in_get(iph->protocol,
					 iph->daddr, pptr[1],
					 iph->saddr, pptr[0]);
					 iph->daddr.ip, pptr[1],
					 iph->saddr.ip, pptr[0]);
	}
}

static struct ip_vs_conn *
tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
		 const struct iphdr *iph, unsigned int proto_off, int inverse)
tcp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
		 const struct ip_vs_iphdr *iph, unsigned int proto_off,
		 int inverse)
{
	__be16 _ports[2], *pptr;

@@ -57,26 +59,25 @@ tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,

	if (likely(!inverse)) {
		return ip_vs_conn_out_get(iph->protocol,
					  iph->saddr, pptr[0],
					  iph->daddr, pptr[1]);
					  iph->saddr.ip, pptr[0],
					  iph->daddr.ip, pptr[1]);
	} else {
		return ip_vs_conn_out_get(iph->protocol,
					  iph->daddr, pptr[1],
					  iph->saddr, pptr[0]);
					  iph->daddr.ip, pptr[1],
					  iph->saddr.ip, pptr[0]);
	}
}


static int
tcp_conn_schedule(struct sk_buff *skb,
		  struct ip_vs_protocol *pp,
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		  int *verdict, struct ip_vs_conn **cpp)
{
	struct ip_vs_service *svc;
	struct tcphdr _tcph, *th;
	struct ip_vs_iphdr iph;

	ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);

	th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
	if (th == NULL) {
@@ -85,8 +86,8 @@ tcp_conn_schedule(struct sk_buff *skb,
	}

	if (th->syn &&
	    (svc = ip_vs_service_get(AF_INET, skb->mark, iph.protocol,
				     &iph.daddr, th->dest))) {
	    (svc = ip_vs_service_get(af, skb->mark, iph.protocol, &iph.daddr,
				     th->dest))) {
		if (ip_vs_todrop()) {
			/*
			 * It seems that we are very loaded.
@@ -136,7 +137,7 @@ tcp_snat_handler(struct sk_buff *skb,

	if (unlikely(cp->app != NULL)) {
		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(skb, pp))
		if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
			return 0;

		/* Call application helper if needed */
@@ -182,7 +183,7 @@ tcp_dnat_handler(struct sk_buff *skb,

	if (unlikely(cp->app != NULL)) {
		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(skb, pp))
		if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
			return 0;

		/*
@@ -219,17 +220,39 @@ tcp_dnat_handler(struct sk_buff *skb,


static int
tcp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
{
	const unsigned int tcphoff = ip_hdrlen(skb);
	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);
	case CHECKSUM_COMPLETE:
		if (csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
#ifdef CONFIG_IP_VS_IPV6
		if (af == AF_INET6) {
			if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
					    &ipv6_hdr(skb)->daddr,
					    skb->len - tcphoff,
					    ipv6_hdr(skb)->nexthdr,
					    skb->csum)) {
				IP_VS_DBG_RL_PKT(0, pp, skb, 0,
						 "Failed checksum for");
				return 0;
			}
		} else
#endif
			if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
					      ip_hdr(skb)->daddr,
					      skb->len - tcphoff,
				      ip_hdr(skb)->protocol, skb->csum)) {
					      ip_hdr(skb)->protocol,
					      skb->csum)) {
				IP_VS_DBG_RL_PKT(0, pp, skb, 0,
						 "Failed checksum for");
				return 0;
+51 −30
Original line number Diff line number Diff line
@@ -24,8 +24,9 @@
#include <net/ip.h>

static struct ip_vs_conn *
udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
		const struct iphdr *iph, unsigned int proto_off, int inverse)
udp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
		const struct ip_vs_iphdr *iph, unsigned int proto_off,
		int inverse)
{
	struct ip_vs_conn *cp;
	__be16 _ports[2], *pptr;
@@ -36,12 +37,12 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,

	if (likely(!inverse)) {
		cp = ip_vs_conn_in_get(iph->protocol,
				       iph->saddr, pptr[0],
				       iph->daddr, pptr[1]);
				       iph->saddr.ip, pptr[0],
				       iph->daddr.ip, pptr[1]);
	} else {
		cp = ip_vs_conn_in_get(iph->protocol,
				       iph->daddr, pptr[1],
				       iph->saddr, pptr[0]);
				       iph->daddr.ip, pptr[1],
				       iph->saddr.ip, pptr[0]);
	}

	return cp;
@@ -49,25 +50,25 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,


static struct ip_vs_conn *
udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
		 const struct iphdr *iph, unsigned int proto_off, int inverse)
udp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
		 const struct ip_vs_iphdr *iph, unsigned int proto_off,
		 int inverse)
{
	struct ip_vs_conn *cp;
	__be16 _ports[2], *pptr;

	pptr = skb_header_pointer(skb, ip_hdrlen(skb),
				  sizeof(_ports), _ports);
	pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
	if (pptr == NULL)
		return NULL;

	if (likely(!inverse)) {
		cp = ip_vs_conn_out_get(iph->protocol,
					iph->saddr, pptr[0],
					iph->daddr, pptr[1]);
					iph->saddr.ip, pptr[0],
					iph->daddr.ip, pptr[1]);
	} else {
		cp = ip_vs_conn_out_get(iph->protocol,
					iph->daddr, pptr[1],
					iph->saddr, pptr[0]);
					iph->daddr.ip, pptr[1],
					iph->saddr.ip, pptr[0]);
	}

	return cp;
@@ -75,14 +76,14 @@ udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,


static int
udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
		  int *verdict, struct ip_vs_conn **cpp)
{
	struct ip_vs_service *svc;
	struct udphdr _udph, *uh;
	struct ip_vs_iphdr iph;

	ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
	ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);

	uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
	if (uh == NULL) {
@@ -90,7 +91,7 @@ udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
		return 0;
	}

	svc = ip_vs_service_get(AF_INET, skb->mark, iph.protocol,
	svc = ip_vs_service_get(af, skb->mark, iph.protocol,
				&iph.daddr, uh->dest);
	if (svc) {
		if (ip_vs_todrop()) {
@@ -143,7 +144,7 @@ udp_snat_handler(struct sk_buff *skb,

	if (unlikely(cp->app != NULL)) {
		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(skb, pp))
		if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
			return 0;

		/*
@@ -195,7 +196,7 @@ udp_dnat_handler(struct sk_buff *skb,

	if (unlikely(cp->app != NULL)) {
		/* Some checks before mangling */
		if (pp->csum_check && !pp->csum_check(skb, pp))
		if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
			return 0;

		/*
@@ -234,10 +235,17 @@ udp_dnat_handler(struct sk_buff *skb,


static int
udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
{
	struct udphdr _udph, *uh;
	const unsigned int udphoff = ip_hdrlen(skb);
	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)
@@ -249,6 +257,19 @@ udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
			skb->csum = skb_checksum(skb, udphoff,
						 skb->len - udphoff, 0);
		case CHECKSUM_COMPLETE:
#ifdef CONFIG_IP_VS_IPV6
			if (af == AF_INET6) {
				if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
						    &ipv6_hdr(skb)->daddr,
						    skb->len - udphoff,
						    ipv6_hdr(skb)->nexthdr,
						    skb->csum)) {
					IP_VS_DBG_RL_PKT(0, pp, skb, 0,
							 "Failed checksum for");
					return 0;
				}
			} else
#endif
				if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
						      ip_hdr(skb)->daddr,
						      skb->len - udphoff,