+static void
+ip_input_adjust(struct mbuf *m, struct ip *ip, struct ifnet *inifp)
+{
+ boolean_t adjust = TRUE;
+
+ ASSERT(m_pktlen(m) > ip->ip_len);
+
+ /*
+ * Invalidate hardware checksum info if ip_adj_clear_hwcksum
+ * is set; useful to handle buggy drivers. Note that this
+ * should not be enabled by default, as we may get here due
+ * to link-layer padding.
+ */
+ if (ip_adj_clear_hwcksum &&
+ (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) &&
+ !(inifp->if_flags & IFF_LOOPBACK) &&
+ !(m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
+ m->m_pkthdr.csum_flags &= ~CSUM_DATA_VALID;
+ m->m_pkthdr.csum_data = 0;
+ ipstat.ips_adj_hwcsum_clr++;
+ }
+
+ /*
+ * If partial checksum information is available, subtract
+ * out the partial sum of postpended extraneous bytes, and
+ * update the checksum metadata accordingly. By doing it
+ * here, the upper layer transport only needs to adjust any
+ * prepended extraneous bytes (else it will do both.)
+ */
+ if (ip_adj_partial_sum &&
+ (m->m_pkthdr.csum_flags & (CSUM_DATA_VALID|CSUM_PARTIAL)) ==
+ (CSUM_DATA_VALID|CSUM_PARTIAL)) {
+ m->m_pkthdr.csum_rx_val = m_adj_sum16(m,
+ m->m_pkthdr.csum_rx_start, m->m_pkthdr.csum_rx_start,
+ (ip->ip_len - m->m_pkthdr.csum_rx_start),
+ m->m_pkthdr.csum_rx_val);
+ } else if ((m->m_pkthdr.csum_flags &
+ (CSUM_DATA_VALID|CSUM_PARTIAL)) ==
+ (CSUM_DATA_VALID|CSUM_PARTIAL)) {
+ /*
+ * If packet has partial checksum info and we decided not
+ * to subtract the partial sum of postpended extraneous
+ * bytes here (not the default case), leave that work to
+ * be handled by the other layers. For now, only TCP, UDP
+ * layers are capable of dealing with this. For all other
+ * protocols (including fragments), trim and ditch the
+ * partial sum as those layers might not implement partial
+ * checksumming (or adjustment) at all.
+ */
+ if ((ip->ip_off & (IP_MF | IP_OFFMASK)) == 0 &&
+ (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
+ adjust = FALSE;
+ } else {
+ m->m_pkthdr.csum_flags &= ~CSUM_DATA_VALID;
+ m->m_pkthdr.csum_data = 0;
+ ipstat.ips_adj_hwcsum_clr++;
+ }
+ }
+
+ if (adjust) {
+ ipstat.ips_adj++;
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = ip->ip_len;
+ m->m_pkthdr.len = ip->ip_len;
+ } else {
+ m_adj(m, ip->ip_len - m->m_pkthdr.len);
+ }
+ }
+}
+