diff options
author | Christian Weisgerber <naddy@cvs.openbsd.org> | 2014-01-22 14:27:21 +0000 |
---|---|---|
committer | Christian Weisgerber <naddy@cvs.openbsd.org> | 2014-01-22 14:27:21 +0000 |
commit | d58a2a454a81c098056ad0bbe8955816c929c526 (patch) | |
tree | cd49cdaf2e24cca140c5eb02b83a8b3bf466a36d /sys/netinet6 | |
parent | 67dba4aa0297de97a87c6c57f9001fd41683c6f6 (diff) |
Split the checksum calculation for IPv6 like for IPv4:
Always calculate the pseudo-header checksum.
Complete the checksum if hardware offload is not available.
Parts originally from NetBSD; ok henning@
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6.h | 50 | ||||
-rw-r--r-- | sys/netinet6/in6_cksum.c | 7 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 27 |
3 files changed, 80 insertions, 4 deletions
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 83ba26eb5cd..7a17a3e9c8f 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.h,v 1.69 2013/10/28 21:02:35 deraadt Exp $ */ +/* $OpenBSD: in6.h,v 1.70 2014/01/22 14:27:20 naddy Exp $ */ /* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */ /* @@ -416,6 +416,54 @@ typedef __socklen_t socklen_t; /* length type for network syscalls */ #endif /* __BSD_VISIBLE */ #ifdef _KERNEL +/* + * 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); +} extern u_char inet6ctlerrmap[]; extern struct ifqueue ip6intrq; /* IP6 packet input queue */ diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c index a98b2004b6d..00e35629071 100644 --- a/sys/netinet6/in6_cksum.c +++ b/sys/netinet6/in6_cksum.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_cksum.c,v 1.15 2008/06/11 19:00:50 mcbride Exp $ */ +/* $OpenBSD: in6_cksum.c,v 1.16 2014/01/22 14:27:20 naddy Exp $ */ /* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ /* @@ -115,6 +115,10 @@ in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len) 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, u_int32_t off, u_int32_t len) 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. */ diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index f547e635db8..2efda0435f3 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_output.c,v 1.150 2014/01/21 10:18:26 mpi Exp $ */ +/* $OpenBSD: ip6_output.c,v 1.151 2014/01/22 14:27:20 naddy Exp $ */ /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ /* @@ -3211,7 +3211,7 @@ in6_delayed_cksum(struct mbuf *m, u_int8_t nxt) if (offset <= 0 || nxtp != nxt) /* If the desired next protocol isn't found, punt. */ return; - csum = (u_int16_t)(in6_cksum(m, nxt, offset, m->m_pkthdr.len - offset)); + csum = (u_int16_t)(in6_cksum(m, 0, offset, m->m_pkthdr.len - offset)); switch (nxt) { case IPPROTO_TCP: @@ -3238,6 +3238,29 @@ in6_delayed_cksum(struct mbuf *m, u_int8_t nxt) void in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp) { + /* some hw and in6_delayed_cksum need the pseudo header cksum */ + if (m->m_pkthdr.csum_flags & + (M_TCP_CSUM_OUT|M_UDP_CSUM_OUT|M_ICMP_CSUM_OUT)) { + struct ip6_hdr *ip6; + int nxt, offset; + u_int16_t csum; + + ip6 = mtod(m, struct ip6_hdr *); + offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst, + htonl(m->m_pkthdr.len - offset), htonl(nxt)); + if (nxt == IPPROTO_TCP) + offset += offsetof(struct tcphdr, th_sum); + else if (nxt == IPPROTO_UDP) + offset += offsetof(struct udphdr, uh_sum); + else if (nxt == IPPROTO_ICMPV6) + offset += offsetof(struct icmp6_hdr, icmp6_cksum); + 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; + } + if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) { if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) || ifp->if_bridgeport != NULL) { |