Currently, ICMP(v4) checksums are calculated using in_cksum(), which requires the following m_data/m_len dance:
hlen = ip->ip_hl << 2; m->m_data += hlen; m->m_len -= hlen; icp = mtod(m, struct icmp *); icp->icmp_cksum = 0; icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen); m->m_data -= hlen; m->m_len += hlen; blambert@ and I found that the ICMP checksum can be calculated using in4_cksum() instead, which avoids that dance and shortens the code: hlen = ip->ip_hl << 2; icp = (struct icmp *)(mtod(m, caddr_t) + hlen); icp->icmp_cksum = 0; icp->icmp_cksum = in4_cksum(m, 0, hlen, ntohs(ip->ip_len) - hlen); Note that the implementation of in_cksum() and in4_cksum() may be architecture-specific on various architectures. I have tested this diff on amd64, hppa, i386, loongson, macppc, sgi, and sparc64. abieber@ has also tested it independently on macppc. Comments? OK? Index: ip_icmp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_icmp.c,v retrieving revision 1.99 diff -u -p -U7 -p -r1.99 ip_icmp.c --- ip_icmp.c 3 May 2013 09:35:20 -0000 1.99 +++ ip_icmp.c 21 May 2013 21:52:59 -0000 @@ -343,24 +343,20 @@ icmp_input(struct mbuf *m, ...) } i = hlen + min(icmplen, ICMP_ADVLENMIN); if (m->m_len < i && (m = m_pullup(m, i)) == NULL) { icmpstat.icps_tooshort++; return; } ip = mtod(m, struct ip *); - m->m_len -= hlen; - m->m_data += hlen; - icp = mtod(m, struct icmp *); - if (in_cksum(m, icmplen)) { + if (in4_cksum(m, 0, hlen, icmplen)) { icmpstat.icps_checksum++; goto freeit; } - m->m_len += hlen; - m->m_data -= hlen; + icp = (struct icmp *)(mtod(m, caddr_t) + hlen); #ifdef ICMPPRINTFS /* * Message type specific processing. */ if (icmpprintfs) printf("icmp_input, type %d code %d\n", icp->icmp_type, icp->icmp_code); @@ -803,21 +799,17 @@ void icmp_send(struct mbuf *m, struct mbuf *opts) { struct ip *ip = mtod(m, struct ip *); int hlen; struct icmp *icp; hlen = ip->ip_hl << 2; - m->m_data += hlen; - m->m_len -= hlen; - icp = mtod(m, struct icmp *); + icp = (struct icmp *)(mtod(m, caddr_t) + hlen); icp->icmp_cksum = 0; - icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen); - m->m_data -= hlen; - m->m_len += hlen; + icp->icmp_cksum = in4_cksum(m, 0, hlen, ntohs(ip->ip_len) - hlen); #ifdef ICMPPRINTFS if (icmpprintfs) { char buf[4 * sizeof("123")]; strlcpy(buf, inet_ntoa(ip->ip_dst), sizeof buf); printf("icmp_send dst %s src %s\n", buf, inet_ntoa(ip->ip_src)); Index: ip_output.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_output.c,v retrieving revision 1.239 diff -u -p -U7 -p -r1.239 ip_output.c --- ip_output.c 24 Apr 2013 12:34:15 -0000 1.239 +++ ip_output.c 21 May 2013 21:52:59 -0000 @@ -2122,17 +2122,13 @@ in_proto_cksum_out(struct mbuf *m, struc } } else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) { struct ip *ip = mtod(m, struct ip *); int hlen; struct icmp *icp; hlen = ip->ip_hl << 2; - m->m_data += hlen; - m->m_len -= hlen; - icp = mtod(m, struct icmp *); + icp = (struct icmp *)(mtod(m, caddr_t) + hlen); icp->icmp_cksum = 0; - icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen); - m->m_data -= hlen; - m->m_len += hlen; + icp->icmp_cksum = in4_cksum(m, 0, hlen, ntohs(ip->ip_len) - hlen); m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */ } }