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 */
}
}