ipv6: Separate out UDP offload functionality
Pull UDP GSO code into a separate file in preparation for moving the code 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
8663e02aba
commit
5edbb07dc9
4 changed files with 130 additions and 101 deletions
|
@ -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 \
|
raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
|
||||||
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
|
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
|
||||||
|
|
||||||
ipv6-offload := ip6_offload.o tcpv6_offload.o
|
ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o
|
||||||
|
|
||||||
ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
|
ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
|
||||||
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
|
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
#ifndef __ip6_offload_h
|
#ifndef __ip6_offload_h
|
||||||
#define __ip6_offload_h
|
#define __ip6_offload_h
|
||||||
|
|
||||||
|
int udp_offload_init(void);
|
||||||
|
void udp_offload_cleanup(void);
|
||||||
|
|
||||||
int tcpv6_offload_init(void);
|
int tcpv6_offload_init(void);
|
||||||
void tcpv6_offload_cleanup(void);
|
void tcpv6_offload_cleanup(void);
|
||||||
|
|
||||||
|
|
104
net/ipv6/udp.c
104
net/ipv6/udp.c
|
@ -50,6 +50,7 @@
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <trace/events/skb.h>
|
#include <trace/events/skb.h>
|
||||||
#include "udp_impl.h"
|
#include "udp_impl.h"
|
||||||
|
#include "ip6_offload.h"
|
||||||
|
|
||||||
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
|
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
|
||||||
{
|
{
|
||||||
|
@ -1343,109 +1344,12 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int udp6_ufo_send_check(struct sk_buff *skb)
|
|
||||||
{
|
|
||||||
const struct ipv6hdr *ipv6h;
|
|
||||||
struct udphdr *uh;
|
|
||||||
|
|
||||||
if (!pskb_may_pull(skb, sizeof(*uh)))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
ipv6h = ipv6_hdr(skb);
|
|
||||||
uh = udp_hdr(skb);
|
|
||||||
|
|
||||||
uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
|
|
||||||
IPPROTO_UDP, 0);
|
|
||||||
skb->csum_start = skb_transport_header(skb) - skb->head;
|
|
||||||
skb->csum_offset = offsetof(struct udphdr, check);
|
|
||||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
|
|
||||||
netdev_features_t features)
|
|
||||||
{
|
|
||||||
struct sk_buff *segs = ERR_PTR(-EINVAL);
|
|
||||||
unsigned int mss;
|
|
||||||
unsigned int unfrag_ip6hlen, unfrag_len;
|
|
||||||
struct frag_hdr *fptr;
|
|
||||||
u8 *mac_start, *prevhdr;
|
|
||||||
u8 nexthdr;
|
|
||||||
u8 frag_hdr_sz = sizeof(struct frag_hdr);
|
|
||||||
int offset;
|
|
||||||
__wsum csum;
|
|
||||||
|
|
||||||
mss = skb_shinfo(skb)->gso_size;
|
|
||||||
if (unlikely(skb->len <= mss))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
|
|
||||||
/* Packet is from an untrusted source, reset gso_segs. */
|
|
||||||
int type = skb_shinfo(skb)->gso_type;
|
|
||||||
|
|
||||||
if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
|
|
||||||
!(type & (SKB_GSO_UDP))))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
|
|
||||||
|
|
||||||
segs = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
|
|
||||||
* do checksum of UDP packets sent as multiple IP fragments.
|
|
||||||
*/
|
|
||||||
offset = skb_checksum_start_offset(skb);
|
|
||||||
csum = skb_checksum(skb, offset, skb->len - offset, 0);
|
|
||||||
offset += skb->csum_offset;
|
|
||||||
*(__sum16 *)(skb->data + offset) = csum_fold(csum);
|
|
||||||
skb->ip_summed = CHECKSUM_NONE;
|
|
||||||
|
|
||||||
/* Check if there is enough headroom to insert fragment header. */
|
|
||||||
if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
|
|
||||||
pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Find the unfragmentable header and shift it left by frag_hdr_sz
|
|
||||||
* bytes to insert fragment header.
|
|
||||||
*/
|
|
||||||
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
|
|
||||||
nexthdr = *prevhdr;
|
|
||||||
*prevhdr = NEXTHDR_FRAGMENT;
|
|
||||||
unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
|
|
||||||
unfrag_ip6hlen;
|
|
||||||
mac_start = skb_mac_header(skb);
|
|
||||||
memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
|
|
||||||
|
|
||||||
skb->mac_header -= frag_hdr_sz;
|
|
||||||
skb->network_header -= frag_hdr_sz;
|
|
||||||
|
|
||||||
fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
|
|
||||||
fptr->nexthdr = nexthdr;
|
|
||||||
fptr->reserved = 0;
|
|
||||||
ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
|
|
||||||
|
|
||||||
/* Fragment the skb. ipv6 header and the remaining fields of the
|
|
||||||
* fragment header are updated in ipv6_gso_segment()
|
|
||||||
*/
|
|
||||||
segs = skb_segment(skb, features);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return segs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct inet6_protocol udpv6_protocol = {
|
static const struct inet6_protocol udpv6_protocol = {
|
||||||
.handler = udpv6_rcv,
|
.handler = udpv6_rcv,
|
||||||
.err_handler = udpv6_err,
|
.err_handler = udpv6_err,
|
||||||
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
|
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct net_offload udpv6_offload = {
|
|
||||||
.gso_send_check = udp6_ufo_send_check,
|
|
||||||
.gso_segment = udp6_ufo_fragment,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
|
|
||||||
|
@ -1568,7 +1472,7 @@ int __init udpv6_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
|
ret = udp_offload_init();
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1585,7 +1489,7 @@ out:
|
||||||
out_udpv6_protocol:
|
out_udpv6_protocol:
|
||||||
inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
|
inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
|
||||||
out_offload:
|
out_offload:
|
||||||
inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
|
udp_offload_cleanup();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1593,5 +1497,5 @@ void udpv6_exit(void)
|
||||||
{
|
{
|
||||||
inet6_unregister_protosw(&udpv6_protosw);
|
inet6_unregister_protosw(&udpv6_protosw);
|
||||||
inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
|
inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
|
||||||
inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
|
udp_offload_cleanup();
|
||||||
}
|
}
|
||||||
|
|
122
net/ipv6/udp_offload.c
Normal file
122
net/ipv6/udp_offload.c
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* UDPv6 GSO support
|
||||||
|
*/
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <net/protocol.h>
|
||||||
|
#include <net/ipv6.h>
|
||||||
|
#include <net/udp.h>
|
||||||
|
#include "ip6_offload.h"
|
||||||
|
|
||||||
|
static int udp6_ufo_send_check(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
const struct ipv6hdr *ipv6h;
|
||||||
|
struct udphdr *uh;
|
||||||
|
|
||||||
|
if (!pskb_may_pull(skb, sizeof(*uh)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ipv6h = ipv6_hdr(skb);
|
||||||
|
uh = udp_hdr(skb);
|
||||||
|
|
||||||
|
uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
|
||||||
|
IPPROTO_UDP, 0);
|
||||||
|
skb->csum_start = skb_transport_header(skb) - skb->head;
|
||||||
|
skb->csum_offset = offsetof(struct udphdr, check);
|
||||||
|
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
|
||||||
|
netdev_features_t features)
|
||||||
|
{
|
||||||
|
struct sk_buff *segs = ERR_PTR(-EINVAL);
|
||||||
|
unsigned int mss;
|
||||||
|
unsigned int unfrag_ip6hlen, unfrag_len;
|
||||||
|
struct frag_hdr *fptr;
|
||||||
|
u8 *mac_start, *prevhdr;
|
||||||
|
u8 nexthdr;
|
||||||
|
u8 frag_hdr_sz = sizeof(struct frag_hdr);
|
||||||
|
int offset;
|
||||||
|
__wsum csum;
|
||||||
|
|
||||||
|
mss = skb_shinfo(skb)->gso_size;
|
||||||
|
if (unlikely(skb->len <= mss))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
|
||||||
|
/* Packet is from an untrusted source, reset gso_segs. */
|
||||||
|
int type = skb_shinfo(skb)->gso_type;
|
||||||
|
|
||||||
|
if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
|
||||||
|
!(type & (SKB_GSO_UDP))))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
|
||||||
|
|
||||||
|
segs = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
|
||||||
|
* do checksum of UDP packets sent as multiple IP fragments.
|
||||||
|
*/
|
||||||
|
offset = skb_checksum_start_offset(skb);
|
||||||
|
csum = skb_checksum(skb, offset, skb->len - offset, 0);
|
||||||
|
offset += skb->csum_offset;
|
||||||
|
*(__sum16 *)(skb->data + offset) = csum_fold(csum);
|
||||||
|
skb->ip_summed = CHECKSUM_NONE;
|
||||||
|
|
||||||
|
/* Check if there is enough headroom to insert fragment header. */
|
||||||
|
if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
|
||||||
|
pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Find the unfragmentable header and shift it left by frag_hdr_sz
|
||||||
|
* bytes to insert fragment header.
|
||||||
|
*/
|
||||||
|
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
|
||||||
|
nexthdr = *prevhdr;
|
||||||
|
*prevhdr = NEXTHDR_FRAGMENT;
|
||||||
|
unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
|
||||||
|
unfrag_ip6hlen;
|
||||||
|
mac_start = skb_mac_header(skb);
|
||||||
|
memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
|
||||||
|
|
||||||
|
skb->mac_header -= frag_hdr_sz;
|
||||||
|
skb->network_header -= frag_hdr_sz;
|
||||||
|
|
||||||
|
fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
|
||||||
|
fptr->nexthdr = nexthdr;
|
||||||
|
fptr->reserved = 0;
|
||||||
|
ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
|
||||||
|
|
||||||
|
/* Fragment the skb. ipv6 header and the remaining fields of the
|
||||||
|
* fragment header are updated in ipv6_gso_segment()
|
||||||
|
*/
|
||||||
|
segs = skb_segment(skb, features);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return segs;
|
||||||
|
}
|
||||||
|
static const struct net_offload udpv6_offload = {
|
||||||
|
.gso_send_check = udp6_ufo_send_check,
|
||||||
|
.gso_segment = udp6_ufo_fragment,
|
||||||
|
};
|
||||||
|
|
||||||
|
int __init udp_offload_init(void)
|
||||||
|
{
|
||||||
|
return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void udp_offload_cleanup(void)
|
||||||
|
{
|
||||||
|
inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue