summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/netinet6/in6.h50
-rw-r--r--sys/netinet6/in6_cksum.c7
-rw-r--r--sys/netinet6/ip6_output.c27
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) {