+ _CASSERT(sizeof (csum) == sizeof (uint16_t));
+ VERIFY(m->m_flags & M_PKTHDR);
+
+ sw_csum = (csum_flags & m->m_pkthdr.csum_flags);
+
+ if ((sw_csum &= CSUM_DELAY_IPV6_DATA) == 0)
+ goto done;
+
+ mlen = m->m_pkthdr.len; /* total mbuf len */
+ hlen = sizeof (*ip6); /* IPv6 header len */
+
+ /* sanity check (need at least IPv6 header) */
+ if (mlen < (hoff + hlen)) {
+ panic("%s: mbuf %p pkt len (%u) < hoff+ip6_hdr "
+ "(%u+%u)\n", __func__, m, mlen, hoff, hlen);
+ /* NOTREACHED */
+ }
+
+ /*
+ * In case the IPv6 header is not contiguous, or not 32-bit
+ * aligned, copy it to a local buffer.
+ */
+ if ((hoff + hlen) > m->m_len ||
+ !IP6_HDR_ALIGNED_P(mtod(m, caddr_t) + hoff)) {
+ m_copydata(m, hoff, hlen, (caddr_t)buf);
+ ip6 = (struct ip6_hdr *)(void *)buf;
+ } else {
+ ip6 = (struct ip6_hdr *)(void *)(m->m_data + hoff);
+ }
+
+ nxt = ip6->ip6_nxt;
+ plen = ntohs(ip6->ip6_plen);
+ if (plen != (mlen - (hoff + hlen))) {
+ plen = OSSwapInt16(plen);
+ if (plen != (mlen - (hoff + hlen))) {
+ /* Don't complain for jumbograms */
+ if (plen != 0 || nxt != IPPROTO_HOPOPTS) {
+ printf("%s: mbuf 0x%llx proto %d IPv6 "
+ "plen %d (%x) [swapped %d (%x)] doesn't "
+ "match actual packet length; %d is used "
+ "instead\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(m), nxt,
+ ip6->ip6_plen, ip6->ip6_plen, plen, plen,
+ (mlen - (hoff + hlen)));
+ }
+ plen = mlen - (hoff + hlen);
+ }
+ }
+
+ if (optlen < 0) {
+ /* next header isn't TCP/UDP and we don't know optlen, bail */
+ if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
+ sw_csum = 0;
+ goto done;
+ }
+ olen = 0;
+ } else {
+ /* caller supplied the original transport number; use it */
+ if (nxt0 >= 0)
+ nxt = nxt0;
+ olen = optlen;
+ }
+
+ offset = hoff + hlen + olen; /* ULP header */
+
+ /* sanity check */
+ if (mlen < offset) {
+ panic("%s: mbuf %p pkt len (%u) < hoff+ip6_hdr+ext_hdr "
+ "(%u+%u+%u)\n", __func__, m, mlen, hoff, hlen, olen);
+ /* NOTREACHED */
+ }
+
+ /*
+ * offset is added to the lower 16-bit value of csum_data,
+ * which is expected to contain the ULP offset; therefore
+ * CSUM_PARTIAL offset adjustment must be undone.
+ */
+ if ((m->m_pkthdr.csum_flags & (CSUM_PARTIAL|CSUM_DATA_VALID)) ==
+ (CSUM_PARTIAL|CSUM_DATA_VALID)) {
+ /*
+ * Get back the original ULP offset (this will
+ * undo the CSUM_PARTIAL logic in ip6_output.)
+ */
+ m->m_pkthdr.csum_data = (m->m_pkthdr.csum_tx_stuff -
+ m->m_pkthdr.csum_tx_start);
+ }
+
+ ulpoff = (m->m_pkthdr.csum_data & 0xffff); /* ULP csum offset */
+
+ if (mlen < (ulpoff + sizeof (csum))) {
+ panic("%s: mbuf %p pkt len (%u) proto %d invalid ULP "
+ "cksum offset (%u) cksum flags 0x%x\n", __func__,
+ m, mlen, nxt, ulpoff, m->m_pkthdr.csum_flags);
+ /* NOTREACHED */