Module Name:    src
Committed By:   rin
Date:           Wed Dec 12 01:40:21 UTC 2018

Modified Files:
        src/sys/net: files.net
        src/sys/netinet: in_offload.c in_offload.h
        src/sys/netinet6: in6_offload.c in6_offload.h
        src/sys/rump/net/lib/libnet: Makefile
Added Files:
        src/sys/net: ether_sw_offload.c ether_sw_offload.h

Log Message:
PR kern/53562

Add ether_sw_offload_[tr]x: handle TX/RX offload options in software.
Since this violates separation b/w L2 and L3/L4, new files are added
rather than having the routines in sys/net/if_ethersubr.c.

OK msaitoh thorpej


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/net/ether_sw_offload.c \
    src/sys/net/ether_sw_offload.h
cvs rdiff -u -r1.19 -r1.20 src/sys/net/files.net
cvs rdiff -u -r1.12 -r1.13 src/sys/netinet/in_offload.c
cvs rdiff -u -r1.11 -r1.12 src/sys/netinet/in_offload.h
cvs rdiff -u -r1.11 -r1.12 src/sys/netinet6/in6_offload.c
cvs rdiff -u -r1.9 -r1.10 src/sys/netinet6/in6_offload.h
cvs rdiff -u -r1.30 -r1.31 src/sys/rump/net/lib/libnet/Makefile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/files.net
diff -u src/sys/net/files.net:1.19 src/sys/net/files.net:1.20
--- src/sys/net/files.net:1.19	Sun Sep 23 09:21:03 2018
+++ src/sys/net/files.net	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files.net,v 1.19 2018/09/23 09:21:03 maxv Exp $
+#	$NetBSD: files.net,v 1.20 2018/12/12 01:40:20 rin Exp $
 
 # XXX CLEANUP
 define	net
@@ -7,6 +7,7 @@ file	net/bpf_filter.c		bpf_filter
 file	net/bpf_stub.c			net
 file	net/bsd-comp.c			ppp & ppp_bsdcomp
 file	net/dl_print.c
+file	net/ether_sw_offload.c		bridge
 file	net/if.c			net
 file	net/if_arcsubr.c		arcnet			needs-flag
 file	net/if_bridge.c			bridge			needs-flag

Index: src/sys/netinet/in_offload.c
diff -u src/sys/netinet/in_offload.c:1.12 src/sys/netinet/in_offload.c:1.13
--- src/sys/netinet/in_offload.c:1.12	Wed Sep 19 07:54:11 2018
+++ src/sys/netinet/in_offload.c	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_offload.c,v 1.12 2018/09/19 07:54:11 rin Exp $	*/
+/*	$NetBSD: in_offload.c,v 1.13 2018/12/12 01:40:20 rin Exp $	*/
 
 /*
  * Copyright (c)2005, 2006 YAMAMOTO Takashi,
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_offload.c,v 1.12 2018/09/19 07:54:11 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_offload.c,v 1.13 2018/12/12 01:40:20 rin Exp $");
 
 #include <sys/param.h>
 #include <sys/mbuf.h>
@@ -43,22 +43,23 @@ __KERNEL_RCSID(0, "$NetBSD: in_offload.c
 
 /*
  * Handle M_CSUM_TSOv4 in software. Split the TCP payload in chunks of
- * size MSS, and send them.
+ * size MSS, and return mbuf chain consists of them.
  */
-static int
-tcp4_segment(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa,
-    struct rtentry *rt)
+struct mbuf *
+tcp4_segment(struct mbuf *m, int off)
 {
 	int mss;
 	int iphlen, thlen;
 	int hlen, len;
 	struct ip *ip;
 	struct tcphdr *th;
-	uint16_t ipid;
+	uint16_t ipid, phsum;
 	uint32_t tcpseq;
 	struct mbuf *hdr = NULL;
-	struct mbuf *t;
-	int error = 0;
+	struct mbuf *m0 = NULL;
+	struct mbuf *prev = NULL;
+	struct mbuf *n, *t;
+	int nsegs;
 
 	KASSERT((m->m_flags & M_PKTHDR) != 0);
 	KASSERT((m->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0);
@@ -66,107 +67,129 @@ tcp4_segment(struct ifnet *ifp, struct m
 	m->m_pkthdr.csum_flags = 0;
 
 	len = m->m_pkthdr.len;
-	KASSERT(len >= sizeof(*ip) + sizeof(*th));
+	KASSERT(len >= off + sizeof(*ip) + sizeof(*th));
 
-	if (m->m_len < sizeof(*ip)) {
-		m = m_pullup(m, sizeof(*ip));
-		if (m == NULL) {
-			error = ENOMEM;
+	hlen = off + sizeof(*ip);
+	if (m->m_len < hlen) {
+		m = m_pullup(m, hlen);
+		if (m == NULL)
 			goto quit;
-		}
 	}
-	ip = mtod(m, struct ip *);
+	ip = (void *)(mtod(m, char *) + off);
 	iphlen = ip->ip_hl * 4;
 	KASSERT(ip->ip_v == IPVERSION);
 	KASSERT(iphlen >= sizeof(*ip));
 	KASSERT(ip->ip_p == IPPROTO_TCP);
 	ipid = ntohs(ip->ip_id);
 
-	hlen = iphlen + sizeof(*th);
+	hlen = off + iphlen + sizeof(*th);
 	if (m->m_len < hlen) {
 		m = m_pullup(m, hlen);
-		if (m == NULL) {
-			error = ENOMEM;
+		if (m == NULL)
 			goto quit;
-		}
 	}
-	th = (void *)(mtod(m, char *) + iphlen);
+	th = (void *)(mtod(m, char *) + off + iphlen);
 	tcpseq = ntohl(th->th_seq);
 	thlen = th->th_off * 4;
-	hlen = iphlen + thlen;
+	hlen = off + iphlen + thlen;
 
 	mss = m->m_pkthdr.segsz;
 	KASSERT(mss != 0);
 	KASSERT(len > hlen);
 
 	t = m_split(m, hlen, M_NOWAIT);
-	if (t == NULL) {
-		error = ENOMEM;
+	if (t == NULL)
 		goto quit;
-	}
 	hdr = m;
 	m = t;
+
 	len -= hlen;
 	KASSERT(len % mss == 0);
-	while (len > 0) {
-		struct mbuf *n;
 
-		n = m_dup(hdr, 0, hlen, M_NOWAIT);
-		if (n == NULL) {
-			error = ENOMEM;
-			goto quit;
-		}
+	ip = (void *)(mtod(hdr, char *) + off);
+	ip->ip_len = htons(iphlen + thlen + mss);
+	phsum = in_cksum_phdr(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+	    htons((uint16_t)(thlen + mss) + IPPROTO_TCP));
+
+	for (nsegs = len / mss; nsegs > 0; nsegs--) {
+		if (nsegs > 1) {
+			n = m_dup(hdr, 0, hlen, M_NOWAIT);
+			if (n == NULL)
+				goto quit;
+		} else
+			n = hdr;
 		KASSERT(n->m_len == hlen); /* XXX */
 
-		t = m_split(m, mss, M_NOWAIT);
-		if (t == NULL) {
-			m_freem(n);
-			error = ENOMEM;
-			goto quit;
-		}
+		if (nsegs > 1) {
+			t = m_split(m, mss, M_NOWAIT);
+			if (t == NULL) {
+				m_freem(n);
+				goto quit;
+			}
+		} else
+			t = m;
 		m_cat(n, m);
 		m = t;
 
 		KASSERT(n->m_len >= hlen); /* XXX */
 
+		if (m0 == NULL)
+			m0 = n;
+
+		if (prev != NULL)
+			prev->m_nextpkt = n;
+
 		n->m_pkthdr.len = hlen + mss;
-		ip = mtod(n, struct ip *);
-		KASSERT(ip->ip_v == IPVERSION);
-		ip->ip_len = htons(n->m_pkthdr.len);
+		n->m_nextpkt = NULL;	/* XXX */
+
+		ip = (void *)(mtod(n, char *) + off);
 		ip->ip_id = htons(ipid);
-		th = (void *)(mtod(n, char *) + iphlen);
-		th->th_seq = htonl(tcpseq);
 		ip->ip_sum = 0;
-		ip->ip_sum = in_cksum(n, iphlen);
-		th->th_sum = 0;
-		th->th_sum = in4_cksum(n, IPPROTO_TCP, iphlen, thlen + mss);
+		ip->ip_sum = in4_cksum(n, 0, off, iphlen);
 
-		error = ip_if_output(ifp, n, sa, rt);
-		if (error) {
-			goto quit;
-		}
+		th = (void *)(mtod(n, char *) + off + iphlen);
+		th->th_seq = htonl(tcpseq);
+		th->th_sum = phsum;
+		th->th_sum = in4_cksum(n, 0, off + iphlen, thlen + mss);
 
 		tcpseq += mss;
 		ipid++;
-		len -= mss;
+		prev = n;
 	}
+	return m0;
 
 quit:
-	if (hdr != NULL) {
+	if (hdr != NULL)
 		m_freem(hdr);
-	}
-	if (m != NULL) {
+	if (m != NULL)
+		m_freem(m);
+	for (m = m0; m != NULL; m = n) {
+		n = m->m_nextpkt;
 		m_freem(m);
 	}
 
-	return error;
+	return NULL;
 }
 
 int
 ip_tso_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa,
     struct rtentry *rt)
 {
-	return tcp4_segment(ifp, m, sa, rt);
+	struct mbuf *n;
+	int error = 0;
+
+	m = tcp4_segment(m, 0);
+	if (m == NULL)
+		return ENOMEM;
+	do {
+		n = m->m_nextpkt;
+		if (error == 0)
+			error = ip_if_output(ifp, m, sa, rt);
+		else
+			m_freem(m);
+		m = n;
+	} while (m != NULL);
+	return error;
 }
 
 /*

Index: src/sys/netinet/in_offload.h
diff -u src/sys/netinet/in_offload.h:1.11 src/sys/netinet/in_offload.h:1.12
--- src/sys/netinet/in_offload.h:1.11	Wed Jul 11 06:00:34 2018
+++ src/sys/netinet/in_offload.h	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_offload.h,v 1.11 2018/07/11 06:00:34 maxv Exp $	*/
+/*	$NetBSD: in_offload.h,v 1.12 2018/12/12 01:40:20 rin Exp $	*/
 
 /*
  * Copyright (c)2005, 2006 YAMAMOTO Takashi,
@@ -32,6 +32,7 @@
 /*
  * Subroutines to do software-only equivalent of h/w offloading.
  */
+struct mbuf *tcp4_segment(struct mbuf *, int);
 int ip_tso_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
     struct rtentry *);
 void in_undefer_cksum(struct mbuf *, size_t, int);

Index: src/sys/netinet6/in6_offload.c
diff -u src/sys/netinet6/in6_offload.c:1.11 src/sys/netinet6/in6_offload.c:1.12
--- src/sys/netinet6/in6_offload.c:1.11	Wed Sep 19 07:54:11 2018
+++ src/sys/netinet6/in6_offload.c	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_offload.c,v 1.11 2018/09/19 07:54:11 rin Exp $	*/
+/*	$NetBSD: in6_offload.c,v 1.12 2018/12/12 01:40:20 rin Exp $	*/
 
 /*
  * Copyright (c)2006 YAMAMOTO Takashi,
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_offload.c,v 1.11 2018/09/19 07:54:11 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_offload.c,v 1.12 2018/12/12 01:40:20 rin Exp $");
 
 #include <sys/param.h>
 #include <sys/mbuf.h>
@@ -45,11 +45,10 @@ __KERNEL_RCSID(0, "$NetBSD: in6_offload.
 
 /*
  * Handle M_CSUM_TSOv6 in software. Split the TCP payload in chunks of
- * size MSS, and send them.
+ * size MSS, and return mbuf chain consists of them.
  */
-static int
-tcp6_segment(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
-    const struct sockaddr_in6 *dst, struct rtentry *rt)
+struct mbuf *
+tcp6_segment(struct mbuf *m, int off)
 {
 	int mss;
 	int iphlen;
@@ -59,9 +58,12 @@ tcp6_segment(struct ifnet *ifp, struct i
 	struct ip6_hdr *iph;
 	struct tcphdr *th;
 	uint32_t tcpseq;
+	uint16_t phsum;
 	struct mbuf *hdr = NULL;
-	struct mbuf *t;
-	int error = 0;
+	struct mbuf *m0 = NULL;
+	struct mbuf *prev = NULL;
+	struct mbuf *n, *t;
+	int nsegs;
 
 	KASSERT((m->m_flags & M_PKTHDR) != 0);
 	KASSERT((m->m_pkthdr.csum_flags & M_CSUM_TSOv6) != 0);
@@ -69,101 +71,121 @@ tcp6_segment(struct ifnet *ifp, struct i
 	m->m_pkthdr.csum_flags = 0;
 
 	len = m->m_pkthdr.len;
-	KASSERT(len >= sizeof(*iph) + sizeof(*th));
+	KASSERT(len >= off + sizeof(*iph) + sizeof(*th));
 
-	if (m->m_len < sizeof(*iph)) {
-		m = m_pullup(m, sizeof(*iph));
-		if (m == NULL) {
-			error = ENOMEM;
+	hlen = off + sizeof(*iph);
+	if (m->m_len < hlen) {
+		m = m_pullup(m, hlen);
+		if (m == NULL)
 			goto quit;
-		}
 	}
-	iph = mtod(m, struct ip6_hdr *);
+	iph = (void *)(mtod(m, char *) + off);
 	iphlen = sizeof(*iph);
 	KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
 	KASSERT(iph->ip6_nxt == IPPROTO_TCP);
 
-	hlen = iphlen + sizeof(*th);
+	hlen = off + iphlen + sizeof(*th);
 	if (m->m_len < hlen) {
 		m = m_pullup(m, hlen);
-		if (m == NULL) {
-			error = ENOMEM;
+		if (m == NULL)
 			goto quit;
-		}
 	}
-	th = (void *)(mtod(m, char *) + iphlen);
+	th = (void *)(mtod(m, char *) + off + iphlen);
 	tcpseq = ntohl(th->th_seq);
 	thlen = th->th_off * 4;
-	hlen = iphlen + thlen;
+	hlen = off + iphlen + thlen;
 
 	mss = m->m_pkthdr.segsz;
 	KASSERT(mss != 0);
 	KASSERT(len > hlen);
 
 	t = m_split(m, hlen, M_NOWAIT);
-	if (t == NULL) {
-		error = ENOMEM;
+	if (t == NULL)
 		goto quit;
-	}
 	hdr = m;
 	m = t;
+
 	len -= hlen;
 	KASSERT(len % mss == 0);
-	while (len > 0) {
-		struct mbuf *n;
 
-		n = m_dup(hdr, 0, hlen, M_NOWAIT);
-		if (n == NULL) {
-			error = ENOMEM;
-			goto quit;
-		}
+	iph = (void *)(mtod(hdr, char *) + off);
+	iph->ip6_plen = htons(thlen + mss);
+	phsum = in6_cksum_phdr(&iph->ip6_src, &iph->ip6_dst, htonl(thlen + mss),
+	    htonl(IPPROTO_TCP));
+
+	for (nsegs = len / mss; nsegs > 0; nsegs--) {
+		if (nsegs > 1) {
+			n = m_dup(hdr, 0, hlen, M_NOWAIT);
+			if (n == NULL)
+				goto quit;
+		} else
+			n = hdr;
 		KASSERT(n->m_len == hlen); /* XXX */
 
-		t = m_split(m, mss, M_NOWAIT);
-		if (t == NULL) {
-			m_freem(n);
-			error = ENOMEM;
-			goto quit;
-		}
+		if (nsegs > 1) {
+			t = m_split(m, mss, M_NOWAIT);
+			if (t == NULL) {
+				m_freem(n);
+				goto quit;
+			}
+		} else
+			t = m;
 		m_cat(n, m);
 		m = t;
 
 		KASSERT(n->m_len >= hlen); /* XXX */
 
+		if (m0 == NULL)
+			m0 = n;
+
+		if (prev != NULL)
+			prev->m_nextpkt = n;
+
 		n->m_pkthdr.len = hlen + mss;
-		iph = mtod(n, struct ip6_hdr *);
-		KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
-		iph->ip6_plen = htons(thlen + mss);
-		th = (void *)(mtod(n, char *) + iphlen);
-		th->th_seq = htonl(tcpseq);
-		th->th_sum = 0;
-		th->th_sum = in6_cksum(n, IPPROTO_TCP, iphlen, thlen + mss);
+		n->m_nextpkt = NULL;	/* XXX */
 
-		error = ip6_if_output(ifp, origifp, n, dst, rt);
-		if (error) {
-			goto quit;
-		}
+		th = (void *)(mtod(n, char *) + off + iphlen);
+		th->th_seq = htonl(tcpseq);
+		th->th_sum = phsum;
+		th->th_sum = in6_cksum(n, 0, off + iphlen, thlen + mss);
 
 		tcpseq += mss;
-		len -= mss;
+		prev = n;
 	}
+	return m0;
 
 quit:
-	if (hdr != NULL) {
+	if (hdr != NULL)
 		m_freem(hdr);
-	}
-	if (m != NULL) {
+	if (m != NULL)
+		m_freem(m);
+	for (m = m0; m != NULL; m = n) {
+		n = m->m_nextpkt;
 		m_freem(m);
 	}
 
-	return error;
+	return NULL;
 }
 
 int
 ip6_tso_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
     const struct sockaddr_in6 *dst, struct rtentry *rt)
 {
-	return tcp6_segment(ifp, origifp, m, dst, rt);
+	struct mbuf *n;
+	int error = 0;
+
+	m = tcp6_segment(m, 0);
+	if (m == NULL)
+		return ENOMEM;
+	do {
+		n = m->m_nextpkt;
+		if (error == 0)
+			error = ip6_if_output(ifp, origifp, m, dst, rt);
+		else
+			m_freem(m);
+		m = n;
+	} while (m != NULL);
+	return error;
 }
 
 /*

Index: src/sys/netinet6/in6_offload.h
diff -u src/sys/netinet6/in6_offload.h:1.9 src/sys/netinet6/in6_offload.h:1.10
--- src/sys/netinet6/in6_offload.h:1.9	Fri Aug 10 06:55:04 2018
+++ src/sys/netinet6/in6_offload.h	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_offload.h,v 1.9 2018/08/10 06:55:04 maxv Exp $	*/
+/*	$NetBSD: in6_offload.h,v 1.10 2018/12/12 01:40:20 rin Exp $	*/
 
 /*
  * Copyright (c)2005, 2006 YAMAMOTO Takashi,
@@ -32,6 +32,7 @@
 /*
  * Subroutines to do software-only equivalent of h/w offloading.
  */
+struct mbuf *tcp6_segment(struct mbuf *, int);
 int ip6_tso_output(struct ifnet *, struct ifnet *, struct mbuf *,
     const struct sockaddr_in6 *, struct rtentry *);
 void in6_undefer_cksum(struct mbuf *, size_t, int);

Index: src/sys/rump/net/lib/libnet/Makefile
diff -u src/sys/rump/net/lib/libnet/Makefile:1.30 src/sys/rump/net/lib/libnet/Makefile:1.31
--- src/sys/rump/net/lib/libnet/Makefile:1.30	Tue Jan 17 08:10:37 2017
+++ src/sys/rump/net/lib/libnet/Makefile	Wed Dec 12 01:40:20 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.30 2017/01/17 08:10:37 ozaki-r Exp $
+#	$NetBSD: Makefile,v 1.31 2018/12/12 01:40:20 rin Exp $
 #
 
 
@@ -15,6 +15,7 @@ SRCS=	if.c if_loop.c route.c rtsock.c ra
 SRCS+=	if_43.c
 SRCS+=	if_llatbl.c
 SRCS+=	net_component.c
+SRCS+=	ether_sw_offload.c
 
 .include <bsd.init.mk>
 

Added files:

Index: src/sys/net/ether_sw_offload.c
diff -u /dev/null src/sys/net/ether_sw_offload.c:1.1
--- /dev/null	Wed Dec 12 01:40:21 2018
+++ src/sys/net/ether_sw_offload.c	Wed Dec 12 01:40:20 2018
@@ -0,0 +1,312 @@
+/*	$NetBSD: ether_sw_offload.c,v 1.1 2018/12/12 01:40:20 rin Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Rin Okuyama.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ether_sw_offload.c,v 1.1 2018/12/12 01:40:20 rin Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/ether_sw_offload.h>
+
+#include <netinet/in.h>
+#include <netinet/in_offload.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_offload.h>
+
+/*
+ * Handle TX offload in software. For TSO, split the packet into
+ * chanks with payloads of size MSS. For chekcsum offload, update
+ * required checksum fields. The results are more than one packet
+ * in general. Return a mbuf chain consists of them.
+ */
+
+struct mbuf *
+ether_sw_offload_tx(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ether_header *ep;
+	int flags, ehlen;
+	bool v4;
+
+	KASSERT(m->m_flags & M_PKTHDR);
+	flags = m->m_pkthdr.csum_flags;
+	if (flags == 0)
+		goto done;
+
+	/* Sanity check */
+	if (!TX_OFFLOAD_SUPPORTED(ifp->if_csum_flags_tx, flags))
+		goto quit;
+
+	KASSERT(m->m_pkthdr.len >= sizeof(*ep));
+	if (m->m_len < sizeof(*ep)) {
+		m = m_pullup(m, sizeof(*ep));
+		if (m == NULL)
+			return NULL;
+	}
+	ep = mtod(m, struct ether_header *);
+	switch (ntohs(ep->ether_type)) {
+	case ETHERTYPE_IP:
+	case ETHERTYPE_IPV6:
+		ehlen = ETHER_HDR_LEN;
+		break;
+	case ETHERTYPE_VLAN:
+		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+		break;
+	default:
+		goto quit;
+	}
+	KASSERT(m->m_pkthdr.len >= ehlen);
+
+	v4 = flags & (M_CSUM_TSOv4 | M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4);
+
+	if (flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) {
+		/*
+		 * tcp[46]_segment() assume that size of payloads is
+		 * a multiple of MSS. Further, tcp6_segment() assumes
+		 * no extention headers.
+		 *
+		 * XXX Do we need some KASSERT's?
+		 */
+		if (v4)
+			return tcp4_segment(m, ehlen);
+		else
+			return tcp6_segment(m, ehlen);
+	}
+
+	if (v4)
+		in_undefer_cksum(m, ehlen, flags);
+	else
+		in6_undefer_cksum(m, ehlen, flags);
+done:
+	m->m_pkthdr.csum_flags = 0;
+	m->m_nextpkt = NULL;
+	return m;
+quit:
+	m_freem(m);
+	return NULL;
+}
+
+/*
+ * Handle RX offload in software.
+ *
+ * XXX Fragmented packets or packets with IPv6 extension headers
+ * are not currently supported.
+ */
+
+struct mbuf *
+ether_sw_offload_rx(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ether_header *eh;
+	struct ip *ip;
+	struct ip6_hdr *ip6;
+	struct tcphdr *th;
+	struct udphdr *uh;
+	uint16_t sum, osum;
+	uint8_t proto;
+	int flags, enabled, len, ehlen, iphlen, l4offset;
+	bool v4;
+
+	flags = 0;
+
+	enabled = ifp->if_csum_flags_rx;
+	if (!(enabled & (M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4 |
+	    M_CSUM_TCPv6 | M_CSUM_UDPv6)))
+		goto done;
+
+	KASSERT(m->m_flags & M_PKTHDR);
+	len = m->m_pkthdr.len;
+
+	KASSERT(len >= sizeof(*eh));
+	if (m->m_len < sizeof(*eh)) {
+		m = m_pullup(m, sizeof(*eh));
+		if (m == NULL)
+			return NULL;
+	}
+	eh = mtod(m, struct ether_header *);
+	switch (htons(eh->ether_type)) {
+	case ETHERTYPE_IP:
+	case ETHERTYPE_IPV6:
+		ehlen = ETHER_HDR_LEN;
+		break;
+	case ETHERTYPE_VLAN:
+		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+		break;
+	default:
+		goto done;
+	}
+
+	KASSERT(len >= ehlen);
+	len = m->m_pkthdr.len - ehlen;
+
+	KASSERT(len >= sizeof(*ip));
+	if (m->m_len < ehlen + sizeof(*ip)) {
+		m = m_pullup(m, ehlen + sizeof(*ip));
+		if (m == NULL)
+			return NULL;
+	}
+	ip = (void *)(mtod(m, char *) + ehlen);
+	v4 = (ip->ip_v == IPVERSION);
+
+	if (v4) {
+		if (enabled & M_CSUM_IPv4)
+			flags |= M_CSUM_IPv4;
+
+		iphlen = ip->ip_hl << 2;
+		KASSERT(iphlen >= sizeof(*ip));
+
+		len -= iphlen;
+		KASSERT(len >= 0);
+
+		if (in4_cksum(m, 0, ehlen, iphlen) != 0) {
+			if (enabled & M_CSUM_IPv4)
+				flags |= M_CSUM_IPv4_BAD;
+			/* Broken. Do not check further. */
+			goto done;
+		}
+
+		/* Check if fragmented. */
+		if (ntohs(ip->ip_off) & ~(IP_DF | IP_RF))
+			goto done;
+
+		proto = ip->ip_p;
+		switch (proto) {
+		case IPPROTO_TCP:
+			if (!(enabled & M_CSUM_TCPv4))
+				goto done;
+			break;
+		case IPPROTO_UDP:
+			if (!(enabled & M_CSUM_UDPv4))
+				goto done;
+			break;
+		default:
+			goto done;
+		}
+
+		sum = in_cksum_phdr(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+		    htons((uint16_t)len + proto));
+	} else {
+		KASSERT(len >= sizeof(*ip6));
+		if (m->m_len < ehlen + sizeof(*ip6)) {
+			m = m_pullup(m, ehlen + sizeof(*ip6));
+			if (m == NULL)
+				return NULL;
+		}
+		ip6 = (void *)(mtod(m, char *) + ehlen);
+		KASSERT((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
+
+		iphlen = sizeof(*ip6);
+
+		len -= iphlen;
+
+		proto = ip6->ip6_nxt;
+		switch (proto) {
+		case IPPROTO_TCP:
+			if (!(enabled & M_CSUM_TCPv6))
+				goto done;
+			break;
+		case IPPROTO_UDP:
+			if (!(enabled & M_CSUM_UDPv6))
+				goto done;
+			break;
+		default:
+			/* XXX Extension headers are not supported. */
+			goto done;
+		}
+
+		sum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst, htonl(len),
+		    htonl(proto));
+	}
+
+	l4offset = ehlen + iphlen;
+	switch (proto) {
+	case IPPROTO_TCP:
+		KASSERT(len >= sizeof(*th));
+		if (m->m_len < l4offset + sizeof(*th)) {
+			m = m_pullup(m, l4offset + sizeof(*th));
+			if (m == NULL)
+				return NULL;
+		}
+		th = (void *)(mtod(m, char *) + l4offset);
+		osum = th->th_sum;
+		th->th_sum = sum;
+		if (v4) {
+			flags |= M_CSUM_TCPv4;
+			sum = in4_cksum(m, 0, l4offset, len);
+		} else {
+			flags |= M_CSUM_TCPv6;
+			sum = in6_cksum(m, 0, l4offset, len);
+		}
+		if (sum != osum)
+			flags |= M_CSUM_TCP_UDP_BAD;
+		th->th_sum = osum;
+		break;
+	case IPPROTO_UDP:
+		KASSERT(len >= sizeof(*uh));
+		if (m->m_len < l4offset + sizeof(*uh)) {
+			m = m_pullup(m, l4offset + sizeof(*uh));
+			if (m == NULL)
+				return NULL;
+		}
+		uh = (void *)(mtod(m, char *) + l4offset);
+		osum = uh->uh_sum;
+		if (osum == 0)
+			break;
+		uh->uh_sum = sum;
+		if (v4) {
+			flags |= M_CSUM_UDPv4;
+			sum = in4_cksum(m, 0, l4offset, len);
+		} else {
+			flags |= M_CSUM_UDPv6;
+			sum = in6_cksum(m, 0, l4offset, len);
+		}
+		if (sum == 0)
+			sum = 0xffff;
+		if (sum != osum)
+			flags |= M_CSUM_TCP_UDP_BAD;
+		uh->uh_sum = osum;
+		break;
+	default:
+		panic("%s: impossible", __func__);
+		break;
+	}
+
+done:
+	m->m_pkthdr.csum_flags = flags;
+	return m;
+}
Index: src/sys/net/ether_sw_offload.h
diff -u /dev/null src/sys/net/ether_sw_offload.h:1.1
--- /dev/null	Wed Dec 12 01:40:21 2018
+++ src/sys/net/ether_sw_offload.h	Wed Dec 12 01:40:20 2018
@@ -0,0 +1,55 @@
+/*	$NetBSD: ether_sw_offload.h,v 1.1 2018/12/12 01:40:20 rin Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Rin Okuyama.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NET_ETHER_SW_OFFLOAD_H_
+#define _NET_ETHER_SW_OFFLOAD_H_
+
+#ifdef _KERNEL
+#include <sys/mbuf.h>
+#include <net/if.h>
+
+/*
+ * Whether given TX offload options are supported by the interface.
+ * We need to check TSO bits first; M_CSUM_IPv4 is turned on even if
+ * TSOv4 is enabled (and some drivers depend on this behavior).
+ * Therefore we cannot test
+ *	m->m_pkthdr.csum_flags & ~ifp->if_csum_flags_tx
+ * for interfaces that do not support hw IPv4 checksum.
+ */
+#define TX_OFFLOAD_SUPPORTED(if_flags, mbuf_flags)			\
+    ((((mbuf_flags) & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) & (if_flags)) ||	\
+     !((mbuf_flags) & ~(if_flags)))
+
+struct mbuf *ether_sw_offload_tx(struct ifnet *, struct mbuf *);
+struct mbuf *ether_sw_offload_rx(struct ifnet *, struct mbuf *);
+#endif /* _KERNEL */
+
+#endif /* !_NET_ETHER_SW_OFFLOAD_H_ */

Reply via email to