Christian Weisgerber <[email protected]> wrote:
> Currently this is only implemented for IPv4. The diff below adds
> the missing pieces for IPv6, so that we will later be able to profit
> from checksum offload over IPv6.
Nobody? Nothing?
> Works for me for basic TCP/UDP use over IPv6.
> bridge(4) and pf(4) are completely untested.
IPv6 NAT works.
> Index: sys/mbuf.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/mbuf.h,v
> retrieving revision 1.159
> diff -u -p -r1.159 mbuf.h
> --- sys/mbuf.h 5 Oct 2012 12:27:02 -0000 1.159
> +++ sys/mbuf.h 12 Nov 2012 18:28:30 -0000
> @@ -182,7 +182,7 @@ struct mbuf {
>
> /* Checksumming flags */
> #define M_IPV4_CSUM_OUT 0x0001 /* IPv4 checksum needed */
> -#define M_TCP_CSUM_OUT 0x0002 /* TCP checksum needed */
> +#define M_TCP_CSUM_OUT 0x0002 /* TCP checksum needed */
> #define M_UDP_CSUM_OUT 0x0004 /* UDP checksum needed */
> #define M_IPV4_CSUM_IN_OK 0x0008 /* IPv4 checksum verified */
> #define M_IPV4_CSUM_IN_BAD 0x0010 /* IPv4 checksum bad */
> Index: net/pf.c
> ===================================================================
> RCS file: /cvs/src/sys/net/pf.c,v
> retrieving revision 1.816
> diff -u -p -r1.816 pf.c
> --- net/pf.c 6 Nov 2012 12:32:41 -0000 1.816
> +++ net/pf.c 12 Nov 2012 20:37:03 -0000
> @@ -6108,6 +6108,7 @@ pf_route6(struct mbuf **m, struct pf_rul
> if (IN6_IS_SCOPE_EMBED(&dst->sin6_addr))
> dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
> if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
> + in6_proto_cksum_out(m0, ifp);
> nd6_output(ifp, ifp, m0, dst, NULL);
> } else {
> in6_ifstat_inc(ifp, ifs6_in_toobig);
> Index: netinet/tcp_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/tcp_output.c,v
> retrieving revision 1.97
> diff -u -p -r1.97 tcp_output.c
> --- netinet/tcp_output.c 20 Sep 2012 10:25:03 -0000 1.97
> +++ netinet/tcp_output.c 12 Nov 2012 18:47:48 -0000
> @@ -961,8 +961,11 @@ send:
> #endif /* INET */
> #ifdef INET6
> case AF_INET6:
> - th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
> - hdrlen - sizeof(struct ip6_hdr) + len);
> + /* Defer checksumming until later (ip6_output() or hardware) */
> + m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
> + if (len + optlen)
> + th->th_sum = in_cksum_addword(th->th_sum,
> + htons((u_int16_t)(len + optlen)));
> break;
> #endif /* INET6 */
> }
> Index: netinet/tcp_subr.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/tcp_subr.c,v
> retrieving revision 1.113
> diff -u -p -r1.113 tcp_subr.c
> --- netinet/tcp_subr.c 10 Mar 2012 12:03:29 -0000 1.113
> +++ netinet/tcp_subr.c 12 Nov 2012 18:49:19 -0000
> @@ -283,7 +283,9 @@ tcp_template(tp)
>
> th = (struct tcphdr *)(mtod(m, caddr_t) +
> sizeof(struct ip6_hdr));
> - th->th_sum = 0;
> + th->th_sum = in6_cksum_phdr(&inp->inp_laddr6,
> + &inp->inp_faddr6, htonl(sizeof(struct tcphdr)),
> + htonl(IPPROTO_TCP));
> }
> break;
> #endif /* INET6 */
> Index: netinet6/in6.h
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/in6.h,v
> retrieving revision 1.61
> diff -u -p -r1.61 in6.h
> --- netinet6/in6.h 6 Nov 2012 12:32:42 -0000 1.61
> +++ netinet6/in6.h 12 Nov 2012 18:41:19 -0000
> @@ -757,7 +757,57 @@ struct ip6_mtuinfo {
> #ifdef _KERNEL
> struct cmsghdr;
>
> +/*
> + * in6_cksum_phdr:
> + *
> + * Compute significant parts of the IPv6 checksum pseudo-header
> + * for use in a delayed TCP/UDP checksum calculation.
> + *
> + * Args:
> + *
> + * src Source IPv6 address
> + * dst Destination IPv6 address
> + * len htonl(proto-hdr-len)
> + * nxt htonl(next-proto-number)
> + *
> + * NOTE: We expect the src and dst addresses to be 16-bit
> + * aligned!
> + */
> +static __inline u_int16_t __attribute__((__unused__))
> +in6_cksum_phdr(const struct in6_addr *src, const struct in6_addr *dst,
> + u_int32_t len, u_int32_t nxt)
> +{
> + u_int32_t sum = 0;
> + const u_int16_t *w;
> +
> + w = (const u_int16_t *) src;
> + sum += w[0];
> + if (!IN6_IS_SCOPE_EMBED(src))
> + sum += w[1];
> + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
> + sum += w[6]; sum += w[7];
> +
> + w = (const u_int16_t *) dst;
> + sum += w[0];
> + if (!IN6_IS_SCOPE_EMBED(dst))
> + sum += w[1];
> + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
> + sum += w[6]; sum += w[7];
> +
> + sum += (u_int16_t)(len >> 16) + (u_int16_t)(len /*& 0xffff*/);
> +
> + sum += (u_int16_t)(nxt >> 16) + (u_int16_t)(nxt /*& 0xffff*/);
> +
> + sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/);
> +
> + if (sum > 0xffff)
> + sum -= 0xffff;
> +
> + return (sum);
> +}
> +
> int in6_cksum(struct mbuf *, u_int8_t, u_int32_t, u_int32_t);
> +extern void in6_proto_cksum_out(struct mbuf *, struct ifnet *);
> int in6_localaddr(struct in6_addr *);
> int in6_addrscope(struct in6_addr *);
> struct in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *,
> u_int);
> Index: netinet6/in6_cksum.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/in6_cksum.c,v
> retrieving revision 1.15
> diff -u -p -r1.15 in6_cksum.c
> --- netinet6/in6_cksum.c 11 Jun 2008 19:00:50 -0000 1.15
> +++ netinet6/in6_cksum.c 12 Nov 2012 20:22:30 -0000
> @@ -115,6 +115,10 @@ in6_cksum(struct mbuf *m, u_int8_t nxt,
> m->m_pkthdr.len, off, len);
> }
>
> + /* Skip pseudo-header if nxt == 0. */
> + if (nxt == 0)
> + goto skip_phdr;
> +
> bzero(&uph, sizeof(uph));
>
> /*
> @@ -141,6 +145,7 @@ in6_cksum(struct mbuf *m, u_int8_t nxt,
> sum += uph.phs[0]; sum += uph.phs[1];
> sum += uph.phs[2]; sum += uph.phs[3];
>
> +skip_phdr:
> /*
> * Secondly calculate a summary of the first mbuf excluding offset.
> */
> Index: netinet6/ip6_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/ip6_output.c,v
> retrieving revision 1.132
> diff -u -p -r1.132 ip6_output.c
> --- netinet6/ip6_output.c 6 Nov 2012 12:32:42 -0000 1.132
> +++ netinet6/ip6_output.c 12 Nov 2012 20:23:14 -0000
> @@ -134,6 +134,8 @@ int ip6_splithdr(struct mbuf *, struct i
> int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
> struct ifnet *, struct in6_addr *, u_long *, int *);
> int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
> +void in6_delayed_cksum(struct mbuf *, u_int8_t);
> +void in6_proto_cksum_out(struct mbuf *, struct ifnet *);
>
> /* Context for non-repeating IDs */
> struct idgen32_ctx ip6_id_ctx;
> @@ -297,6 +299,12 @@ ip6_output(struct mbuf *m0, struct ip6_p
> sspi = tdb->tdb_spi;
> sproto = tdb->tdb_sproto;
> splx(s);
> +
> + /*
> + * If it needs TCP/UDP hardware-checksumming, do the
> + * computation now.
> + */
> + in6_proto_cksum_out(m, NULL);
> }
>
> /* Fall through to the routing/multicast handling code */
> @@ -875,6 +883,7 @@ reroute:
> * transmit packet without fragmentation
> */
> if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
> + in6_proto_cksum_out(m, ifp);
> error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
> goto done;
> }
> @@ -935,6 +944,8 @@ reroute:
> ip6->ip6_nxt = IPPROTO_FRAGMENT;
> }
>
> + in6_proto_cksum_out(m, NULL);
> +
> m0 = m;
> error = ip6_fragment(m0, hlen, nextproto, mtu);
>
> @@ -3213,4 +3224,63 @@ void
> ip6_randomid_init(void)
> {
> idgen32_init(&ip6_id_ctx);
> +}
> +
> +/*
> + * Process a delayed payload checksum calculation.
> + */
> +void
> +in6_delayed_cksum(struct mbuf *m, u_int8_t nxt)
> +{
> + int nxtp, offset;
> + u_int16_t csum;
> +
> + offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxtp);
> + if (offset <= 0 || nxtp != nxt)
> + /* If the desired next protocol isn't found, punt. */
> + return;
> +
> + csum = (u_int16_t)(in6_cksum(m, 0, offset, m->m_pkthdr.len - offset));
> +
> + switch (nxt) {
> + case IPPROTO_TCP:
> + offset += offsetof(struct tcphdr, th_sum);
> + break;
> +
> + case IPPROTO_UDP:
> + offset += offsetof(struct udphdr, uh_sum);
> + if (csum == 0)
> + csum = 0xffff;
> + break;
> +
> + case IPPROTO_ICMPV6:
> + offset += offsetof(struct icmp6_hdr, icmp6_cksum);
> + break;
> + }
> +
> + if ((offset + sizeof(u_int16_t)) > m->m_len)
> + m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
> + else
> + *(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
> +}
> +
> +void
> +in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
> +{
> + if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
> + if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) ||
> + ifp->if_bridgeport != NULL) {
> + in6_delayed_cksum(m, IPPROTO_TCP);
> + m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
> + }
> + } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
> + if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) ||
> + ifp->if_bridgeport != NULL) {
> + in6_delayed_cksum(m, IPPROTO_UDP);
> + m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
> + }
> + } else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) {
> + in6_delayed_cksum(m, IPPROTO_ICMPV6);
> + m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */
> + }
> }
> Index: netinet6/udp6_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/udp6_output.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 udp6_output.c
> --- netinet6/udp6_output.c 24 Nov 2011 17:39:55 -0000 1.17
> +++ netinet6/udp6_output.c 12 Nov 2012 20:21:23 -0000
> @@ -259,10 +259,9 @@ udp6_output(struct in6pcb *in6p, struct
> ip6->ip6_src = *laddr;
> ip6->ip6_dst = *faddr;
>
> - if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
> - sizeof(struct ip6_hdr), plen)) == 0) {
> - udp6->uh_sum = 0xffff;
> - }
> + udp6->uh_sum = in6_cksum_phdr(laddr, faddr,
> + htonl(plen), htonl(IPPROTO_UDP));
> + m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
>
> flags = 0;
> if (in6p->in6p_flags & IN6P_MINMTU)
--
Christian "naddy" Weisgerber [email protected]