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

Commit 684f2176 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller
Browse files

tcp6: Add GRO support



This patch adds GRO support for TCP over IPv6.  The code is exactly
the same as the IPv4 version except for the pseudo-header checksum
computation.

Note that I've removed the unused tcphdr argument from tcp_v6_check
rather than invent a bogus value for GRO.

Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 787e9208
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -2542,6 +2542,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)

	return pp;
}
EXPORT_SYMBOL(tcp_gro_receive);

int tcp_gro_complete(struct sk_buff *skb)
{
@@ -2558,6 +2559,7 @@ int tcp_gro_complete(struct sk_buff *skb)

	return 0;
}
EXPORT_SYMBOL(tcp_gro_complete);

#ifdef CONFIG_TCP_MD5SIG
static unsigned long tcp_md5sig_users;
+41 −4
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@ static void tcp_v6_hash(struct sock *sk)
	}
}

static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len,
static __inline__ __sum16 tcp_v6_check(int len,
				   struct in6_addr *saddr,
				   struct in6_addr *daddr,
				   __wsum base)
@@ -501,7 +501,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
	if (skb) {
		struct tcphdr *th = tcp_hdr(skb);

		th->check = tcp_v6_check(th, skb->len,
		th->check = tcp_v6_check(skb->len,
					 &treq->loc_addr, &treq->rmt_addr,
					 csum_partial(th, skb->len, skb->csum));

@@ -942,6 +942,41 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
	return 0;
}

struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb)
{
	struct ipv6hdr *iph = ipv6_hdr(skb);

	switch (skb->ip_summed) {
	case CHECKSUM_COMPLETE:
		if (!tcp_v6_check(skb->len, &iph->saddr, &iph->daddr,
				  skb->csum)) {
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			break;
		}

		/* fall through */
	case CHECKSUM_NONE:
		NAPI_GRO_CB(skb)->flush = 1;
		return NULL;
	}

	return tcp_gro_receive(head, skb);
}
EXPORT_SYMBOL(tcp6_gro_receive);

int tcp6_gro_complete(struct sk_buff *skb)
{
	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);
}
EXPORT_SYMBOL(tcp6_gro_complete);

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)
{
@@ -1429,14 +1464,14 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
{
	if (skb->ip_summed == CHECKSUM_COMPLETE) {
		if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr,
		if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr,
				  &ipv6_hdr(skb)->daddr, skb->csum)) {
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			return 0;
		}
	}

	skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len,
	skb->csum = ~csum_unfold(tcp_v6_check(skb->len,
					      &ipv6_hdr(skb)->saddr,
					      &ipv6_hdr(skb)->daddr, 0));

@@ -2062,6 +2097,8 @@ static struct inet6_protocol tcpv6_protocol = {
	.err_handler	=	tcp_v6_err,
	.gso_send_check	=	tcp_v6_gso_send_check,
	.gso_segment	=	tcp_tso_segment,
	.gro_receive	=	tcp6_gro_receive,
	.gro_complete	=	tcp6_gro_complete,
	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};