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

Commit 8663e02a authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller
Browse files

ipv6: Separate tcp offload functionality



Pull TCPv6 offload functionality into its won file in preparation
for moving it out of the module.

Signed-off-by: default avatarVlad Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d1da932e
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@
#include <net/ip.h>
#include <asm/checksum.h>
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/ipv6.h>

#ifndef _HAVE_ARCH_IPV6_CSUM

@@ -91,4 +93,37 @@ static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
}

#endif

static __inline__ __sum16 tcp_v6_check(int len,
				   const struct in6_addr *saddr,
				   const struct in6_addr *daddr,
				   __wsum base)
{
	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
}

static inline void __tcp_v6_send_check(struct sk_buff *skb,
				       const struct in6_addr *saddr,
				       const struct in6_addr *daddr)
{
	struct tcphdr *th = tcp_hdr(skb);

	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
		skb->csum_start = skb_transport_header(skb) - skb->head;
		skb->csum_offset = offsetof(struct tcphdr, check);
	} else {
		th->check = tcp_v6_check(skb->len, saddr, daddr,
					 csum_partial(th, th->doff << 2,
						      skb->csum));
	}
}

static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
{
	struct ipv6_pinfo *np = inet6_sk(sk);

	__tcp_v6_send_check(skb, &np->saddr, &np->daddr);
}

#endif
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o

ipv6-offload :=	ip6_offload.o
ipv6-offload :=	ip6_offload.o tcpv6_offload.o

ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
+3 −0
Original line number Diff line number Diff line
@@ -11,6 +11,9 @@
#ifndef __ip6_offload_h
#define __ip6_offload_h

int tcpv6_offload_init(void);
void tcpv6_offload_cleanup(void);

extern void ipv6_offload_init(void);
extern void ipv6_offload_cleanup(void);

+4 −109
Original line number Diff line number Diff line
@@ -71,15 +71,13 @@

#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include "ip6_offload.h"

static void	tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);
static void	tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
				      struct request_sock *req);

static int	tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
static void	__tcp_v6_send_check(struct sk_buff *skb,
				    const struct in6_addr *saddr,
				    const struct in6_addr *daddr);

static const struct inet_connection_sock_af_ops ipv6_mapped;
static const struct inet_connection_sock_af_ops ipv6_specific;
@@ -119,14 +117,6 @@ static void tcp_v6_hash(struct sock *sk)
	}
}

static __inline__ __sum16 tcp_v6_check(int len,
				   const struct in6_addr *saddr,
				   const struct in6_addr *daddr,
				   __wsum base)
{
	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
}

static __u32 tcp_v6_init_sequence(const struct sk_buff *skb)
{
	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
@@ -722,94 +712,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
};
#endif

static void __tcp_v6_send_check(struct sk_buff *skb,
				const struct in6_addr *saddr, const struct in6_addr *daddr)
{
	struct tcphdr *th = tcp_hdr(skb);

	if (skb->ip_summed == CHECKSUM_PARTIAL) {
		th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
		skb->csum_start = skb_transport_header(skb) - skb->head;
		skb->csum_offset = offsetof(struct tcphdr, check);
	} else {
		th->check = tcp_v6_check(skb->len, saddr, daddr,
					 csum_partial(th, th->doff << 2,
						      skb->csum));
	}
}

static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
{
	struct ipv6_pinfo *np = inet6_sk(sk);

	__tcp_v6_send_check(skb, &np->saddr, &np->daddr);
}

static int tcp_v6_gso_send_check(struct sk_buff *skb)
{
	const struct ipv6hdr *ipv6h;
	struct tcphdr *th;

	if (!pskb_may_pull(skb, sizeof(*th)))
		return -EINVAL;

	ipv6h = ipv6_hdr(skb);
	th = tcp_hdr(skb);

	th->check = 0;
	skb->ip_summed = CHECKSUM_PARTIAL;
	__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
	return 0;
}

static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
					 struct sk_buff *skb)
{
	const struct ipv6hdr *iph = skb_gro_network_header(skb);
	__wsum wsum;
	__sum16 sum;

	switch (skb->ip_summed) {
	case CHECKSUM_COMPLETE:
		if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
				  skb->csum)) {
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			break;
		}
flush:
		NAPI_GRO_CB(skb)->flush = 1;
		return NULL;

	case CHECKSUM_NONE:
		wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
						    skb_gro_len(skb),
						    IPPROTO_TCP, 0));
		sum = csum_fold(skb_checksum(skb,
					     skb_gro_offset(skb),
					     skb_gro_len(skb),
					     wsum));
		if (sum)
			goto flush;

		skb->ip_summed = CHECKSUM_UNNECESSARY;
		break;
	}

	return tcp_gro_receive(head, skb);
}

static int tcp6_gro_complete(struct sk_buff *skb)
{
	const struct ipv6hdr *iph = ipv6_hdr(skb);
	struct tcphdr *th = tcp_hdr(skb);

	th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
				  &iph->saddr, &iph->daddr, 0);
	skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;

	return tcp_gro_complete(skb);
}

static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
				 u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass)
{
@@ -2069,13 +1971,6 @@ static const struct inet6_protocol tcpv6_protocol = {
	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};

static const struct net_offload tcpv6_offload = {
	.gso_send_check	=	tcp_v6_gso_send_check,
	.gso_segment	=	tcp_tso_segment,
	.gro_receive	=	tcp6_gro_receive,
	.gro_complete	=	tcp6_gro_complete,
};

static struct inet_protosw tcpv6_protosw = {
	.type		=	SOCK_STREAM,
	.protocol	=	IPPROTO_TCP,
@@ -2112,7 +2007,7 @@ int __init tcpv6_init(void)
{
	int ret;

	ret = inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
	ret = tcpv6_offload_init();
	if (ret)
		goto out;

@@ -2136,7 +2031,7 @@ int __init tcpv6_init(void)
out_tcpv6_protocol:
	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
out_offload:
	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
	tcpv6_offload_cleanup();
	goto out;
}

@@ -2145,5 +2040,5 @@ void tcpv6_exit(void)
	unregister_pernet_subsys(&tcpv6_net_ops);
	inet6_unregister_protosw(&tcpv6_protosw);
	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
	tcpv6_offload_cleanup();
}
+98 −0
Original line number Diff line number Diff line
/*
 *	IPV6 GSO/GRO offload support
 *	Linux INET6 implementation
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      TCPv6 GSO/GRO support
 */
#include <linux/skbuff.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ip6_checksum.h>
#include "ip6_offload.h"

static int tcp_v6_gso_send_check(struct sk_buff *skb)
{
	const struct ipv6hdr *ipv6h;
	struct tcphdr *th;

	if (!pskb_may_pull(skb, sizeof(*th)))
		return -EINVAL;

	ipv6h = ipv6_hdr(skb);
	th = tcp_hdr(skb);

	th->check = 0;
	skb->ip_summed = CHECKSUM_PARTIAL;
	__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
	return 0;
}

static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
					 struct sk_buff *skb)
{
	const struct ipv6hdr *iph = skb_gro_network_header(skb);
	__wsum wsum;
	__sum16 sum;

	switch (skb->ip_summed) {
	case CHECKSUM_COMPLETE:
		if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
				  skb->csum)) {
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			break;
		}
flush:
		NAPI_GRO_CB(skb)->flush = 1;
		return NULL;

	case CHECKSUM_NONE:
		wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
						    skb_gro_len(skb),
						    IPPROTO_TCP, 0));
		sum = csum_fold(skb_checksum(skb,
					     skb_gro_offset(skb),
					     skb_gro_len(skb),
					     wsum));
		if (sum)
			goto flush;

		skb->ip_summed = CHECKSUM_UNNECESSARY;
		break;
	}

	return tcp_gro_receive(head, skb);
}

static int tcp6_gro_complete(struct sk_buff *skb)
{
	const struct ipv6hdr *iph = ipv6_hdr(skb);
	struct tcphdr *th = tcp_hdr(skb);

	th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
				  &iph->saddr, &iph->daddr, 0);
	skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;

	return tcp_gro_complete(skb);
}

static const struct net_offload tcpv6_offload = {
	.gso_send_check	=	tcp_v6_gso_send_check,
	.gso_segment	=	tcp_tso_segment,
	.gro_receive	=	tcp6_gro_receive,
	.gro_complete	=	tcp6_gro_complete,
};

int __init tcpv6_offload_init(void)
{
	return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
}

void tcpv6_offload_cleanup(void)
{
	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
}