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: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d1da932ed4
commit
8663e02aba
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 @@ out_tcpv6_protosw:
|
||||
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
net/ipv6/tcpv6_offload.c
Normal file
98
net/ipv6/tcpv6_offload.c
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user