Skip to content

Commit

Permalink
ipv6: Separate tcp offload functionality
Browse files Browse the repository at this point in the history
Pull TCPv6 offload functionality into its won file in preparation
for moving it out of the module.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vlad Yasevich authored and David S. Miller committed Nov 15, 2012
1 parent d1da932 commit 8663e02
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 110 deletions.
35 changes: 35 additions & 0 deletions include/net/ip6_checksum.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion net/ipv6/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions net/ipv6/ip6_offload.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
113 changes: 4 additions & 109 deletions net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand All @@ -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 changes: 98 additions & 0 deletions net/ipv6/tcpv6_offload.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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);
}

0 comments on commit 8663e02

Please sign in to comment.