+void
+in_delayed_cksum(struct mbuf *m)
+{
+ in_delayed_cksum_offset(m, 0);
+}
+
+void
+in_cksum_offset(struct mbuf* m, size_t ip_offset)
+{
+ struct ip* ip = NULL;
+ int hlen = 0;
+ unsigned char buf[sizeof(struct ip)];
+ int swapped = 0;
+
+ /* Save copy of first mbuf pointer and the ip_offset before modifying */
+ struct mbuf* m0 = m;
+ size_t ip_offset_copy = ip_offset;
+
+ while (ip_offset >= m->m_len) {
+ ip_offset -= m->m_len;
+ m = m->m_next;
+ if (m == NULL) {
+ printf("in_cksum_offset failed - ip_offset wasn't "
+ "in the packet\n");
+ return;
+ }
+ }
+
+ /*
+ * In case the IP header is not contiguous, or not 32-bit
+ * aligned, copy it to a local buffer.
+ */
+ if ((ip_offset + sizeof(struct ip) > m->m_len) ||
+ !IP_HDR_ALIGNED_P(mtod(m, caddr_t) + ip_offset)) {
+#if DEBUG
+ printf("in_cksum_offset - delayed m_pullup, m->len: %d "
+ "off: %lu\n", m->m_len, ip_offset);
+#endif
+ m_copydata(m, ip_offset, sizeof(struct ip), (caddr_t) buf);
+
+ ip = (struct ip *)(void *)buf;
+ ip->ip_sum = 0;
+ m_copyback(m, ip_offset + offsetof(struct ip, ip_sum), 2,
+ (caddr_t)&ip->ip_sum);
+ } else {
+ ip = (struct ip*)(void *)(m->m_data + ip_offset);
+ ip->ip_sum = 0;
+ }
+
+ /* Gross */
+ if (ip_offset) {
+ m->m_len -= ip_offset;
+ m->m_data += ip_offset;
+ }
+
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+ /*
+ * We could be in the context of an IP or interface filter; in the
+ * former case, ip_len would be in host order while for the latter
+ * it would be in network (correct) order. Because of this, we
+ * attempt to interpret the length field by comparing it against
+ * the actual packet length. If the comparison fails, byte swap
+ * the length and check again. If it still fails, then the packet
+ * is bogus and we give up.
+ */
+ if (ntohs(ip->ip_len) != (m0->m_pkthdr.len - ip_offset_copy)) {
+ ip->ip_len = SWAP16(ip->ip_len);
+ swapped = 1;
+ if (ntohs(ip->ip_len) != (m0->m_pkthdr.len - ip_offset_copy)) {
+ ip->ip_len = SWAP16(ip->ip_len);
+ printf("in_cksum_offset: ip_len %d (%d) "
+ "doesn't match actual length %lu\n",
+ ip->ip_len, SWAP16(ip->ip_len),
+ (m0->m_pkthdr.len - ip_offset_copy));
+ return;
+ }
+ }
+
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, hlen);
+ if (swapped)
+ ip->ip_len = SWAP16(ip->ip_len);
+
+ /* Gross */
+ if (ip_offset) {
+ if (M_LEADINGSPACE(m) < ip_offset)
+ panic("in_cksum_offset - chain modified!\n");
+ m->m_len += ip_offset;
+ m->m_data -= ip_offset;
+ }
+
+ /*
+ * Insert the checksum in the existing chain if IP header not
+ * contiguous, or if it's not 32-bit aligned, i.e. all the cases
+ * where it was copied to a local buffer.
+ */
+ if (ip_offset + sizeof(struct ip) > m->m_len) {
+ char tmp[2];
+
+#if DEBUG
+ printf("in_cksum_offset m_copyback, m->len: %u off: %lu "
+ "p: %d\n", m->m_len,
+ ip_offset + offsetof(struct ip, ip_sum), ip->ip_p);
+#endif
+ *(u_short *)(void *)tmp = ip->ip_sum;
+ m_copyback(m, ip_offset + offsetof(struct ip, ip_sum), 2, tmp);
+ } else if (!IP_HDR_ALIGNED_P(mtod(m, caddr_t) + ip_offset)) {
+ bcopy(&ip->ip_sum,
+ (m->m_data + ip_offset + offsetof(struct ip, ip_sum)),
+ sizeof (u_short));
+ }
+}
+
+/*
+ * Insert IP options into preformed packet.
+ * Adjust IP destination as required for IP source routing,
+ * as indicated by a non-zero in_addr at the start of the options.