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_ */