rewrite L4 checksum calculation

This commit is contained in:
Sergio R. Caprile
2026-01-27 10:32:50 -03:00
parent b74913ded5
commit 8e0ef3ca5c
3 changed files with 143 additions and 24 deletions
+24 -12
View File
@@ -5042,6 +5042,7 @@ static void mkpay(struct pkt *pkt, void *p) {
mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p));
}
// NOTE(): DOES NOT handle reentries after odd length, use last
static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
size_t i;
const uint8_t *p = (const uint8_t *) buf;
@@ -5192,18 +5193,25 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
udp->csum = 0;
cs = csumup(0, udp, sizeof(*udp));
cs = csumup(cs, buf, len);
#if MG_ENABLE_IPV6
if (ip_dst->is_ip6) {
uint32_t pseudo[2];
cs = csumup(cs, &ip6->src, sizeof(ip6->src));
cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
pseudo[0] = mg_htonl((uint32_t) (sizeof(*udp) + len));
pseudo[1] = mg_htonl(17);
cs = csumup(cs, pseudo, 2 * sizeof(uint32_t));
} else
#endif
{
uint16_t pseudo[2];
cs = csumup(cs, &ip->src, sizeof(ip->src));
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
pseudo[0] = mg_htons(17);
pseudo[1] = udp->len;
cs = csumup(cs, pseudo, 2 * sizeof(uint16_t));
}
cs += (uint32_t) (17 + sizeof(*udp) + len);
cs = csumup(cs, buf, len); // possibly odd length must be last
udp->csum = csumfin(cs);
memmove(udp + 1, buf, len);
l2_len = mg_l2_footer(ifp->l2type, l2_len, l2p);
@@ -5520,6 +5528,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
struct ip6 *ip6;
struct icmp6 *icmp6;
uint32_t cs;
uint16_t pseudo[2];
ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len);
icmp6 = (struct icmp6 *) (ip6 + 1);
memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
@@ -5528,10 +5537,12 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
memcpy(icmp6 + 1, buf, len); // Copy payload
icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
cs = csumup(0, icmp6, sizeof(*icmp6));
cs = csumup(cs, buf, len);
cs = csumup(cs, ip_src, 16);
cs = csumup(cs, ip_dst, 16);
cs += (uint32_t) (58 + sizeof(*icmp6) + len);
pseudo[0] = htons(58);
pseudo[1] = htons((uint16_t) (sizeof(*icmp6) + len));
cs = csumup(cs, pseudo, 4);
cs = csumup(cs, buf, len); // possibly odd length must be last
icmp6->csum = csumfin(cs);
driver_output(
ifp, mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len, l2p));
@@ -5922,24 +5933,25 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
{
uint32_t cs = 0;
uint16_t n = (uint16_t) (sizeof(*tcp) + len);
cs = csumup(cs, tcp, n);
#if MG_ENABLE_IPV6
if (ip_dst->is_ip6) {
uint32_t pseudo[2];
cs = csumup(cs, &ip6->src, sizeof(ip6->src));
cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
cs += (uint32_t) (6 + n);
pseudo[0] = mg_htonl((uint32_t) n);
pseudo[1] = mg_htonl(6);
cs = csumup(cs, pseudo, 2 * sizeof(uint32_t));
} else
#endif
{
uint8_t pseudo[4];
pseudo[0] = 0;
pseudo[1] = ip->proto;
pseudo[2] = (uint8_t) (n >> 8);
pseudo[3] = (uint8_t) (n & 255);
uint16_t pseudo[2];
cs = csumup(cs, &ip->src, sizeof(ip->src));
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
cs = csumup(cs, pseudo, sizeof(pseudo));
pseudo[0] = mg_htons(6);
pseudo[1] = mg_htons(n);
cs = csumup(cs, pseudo, 2 * sizeof(uint16_t));
}
cs = csumup(cs, tcp, n); // possibly odd length must be last
tcp->csum = csumfin(cs);
}
MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src,
+24 -12
View File
@@ -216,6 +216,7 @@ static void mkpay(struct pkt *pkt, void *p) {
mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p));
}
// NOTE(): DOES NOT handle reentries after odd length, use last
static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
size_t i;
const uint8_t *p = (const uint8_t *) buf;
@@ -366,18 +367,25 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
udp->csum = 0;
cs = csumup(0, udp, sizeof(*udp));
cs = csumup(cs, buf, len);
#if MG_ENABLE_IPV6
if (ip_dst->is_ip6) {
uint32_t pseudo[2];
cs = csumup(cs, &ip6->src, sizeof(ip6->src));
cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
pseudo[0] = mg_htonl((uint32_t) (sizeof(*udp) + len));
pseudo[1] = mg_htonl(17);
cs = csumup(cs, pseudo, 2 * sizeof(uint32_t));
} else
#endif
{
uint16_t pseudo[2];
cs = csumup(cs, &ip->src, sizeof(ip->src));
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
pseudo[0] = mg_htons(17);
pseudo[1] = udp->len;
cs = csumup(cs, pseudo, 2 * sizeof(uint16_t));
}
cs += (uint32_t) (17 + sizeof(*udp) + len);
cs = csumup(cs, buf, len); // possibly odd length must be last
udp->csum = csumfin(cs);
memmove(udp + 1, buf, len);
l2_len = mg_l2_footer(ifp->l2type, l2_len, l2p);
@@ -694,6 +702,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
struct ip6 *ip6;
struct icmp6 *icmp6;
uint32_t cs;
uint16_t pseudo[2];
ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len);
icmp6 = (struct icmp6 *) (ip6 + 1);
memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
@@ -702,10 +711,12 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
memcpy(icmp6 + 1, buf, len); // Copy payload
icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
cs = csumup(0, icmp6, sizeof(*icmp6));
cs = csumup(cs, buf, len);
cs = csumup(cs, ip_src, 16);
cs = csumup(cs, ip_dst, 16);
cs += (uint32_t) (58 + sizeof(*icmp6) + len);
pseudo[0] = htons(58);
pseudo[1] = htons((uint16_t) (sizeof(*icmp6) + len));
cs = csumup(cs, pseudo, 4);
cs = csumup(cs, buf, len); // possibly odd length must be last
icmp6->csum = csumfin(cs);
driver_output(
ifp, mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len, l2p));
@@ -1096,24 +1107,25 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
{
uint32_t cs = 0;
uint16_t n = (uint16_t) (sizeof(*tcp) + len);
cs = csumup(cs, tcp, n);
#if MG_ENABLE_IPV6
if (ip_dst->is_ip6) {
uint32_t pseudo[2];
cs = csumup(cs, &ip6->src, sizeof(ip6->src));
cs = csumup(cs, &ip6->dst, sizeof(ip6->dst));
cs += (uint32_t) (6 + n);
pseudo[0] = mg_htonl((uint32_t) n);
pseudo[1] = mg_htonl(6);
cs = csumup(cs, pseudo, 2 * sizeof(uint32_t));
} else
#endif
{
uint8_t pseudo[4];
pseudo[0] = 0;
pseudo[1] = ip->proto;
pseudo[2] = (uint8_t) (n >> 8);
pseudo[3] = (uint8_t) (n & 255);
uint16_t pseudo[2];
cs = csumup(cs, &ip->src, sizeof(ip->src));
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
cs = csumup(cs, pseudo, sizeof(pseudo));
pseudo[0] = mg_htons(6);
pseudo[1] = mg_htons(n);
cs = csumup(cs, pseudo, 2 * sizeof(uint16_t));
}
cs = csumup(cs, tcp, n); // possibly odd length must be last
tcp->csum = csumfin(cs);
}
MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src,
+95
View File
@@ -41,10 +41,100 @@ struct ipp {
static void test_csum(void) {
uint8_t ip[20] = {0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11,
0x94, 0xcf, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01};
uint8_t udp_odd[53] = {0xc0, 0xa8, 0x45, 0x58, 0x08, 0x08, 0x08, 0x08, 0x00,
0x11, 0x00, 0x29, 0xad, 0x67, 0x00, 0x35, 0x00, 0x29,
0xaf, 0xf8, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x69, 0x6d,
0x65, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01};
ASSERT(ipcsum(ip, 20) == 0);
ASSERT(ipcsum(udp_odd, 53) == 0);
// UDP and TCP checksum calc funcions use the same basic calls as ipcsum()
}
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct pseudoip {
uint32_t src; // Source IP
uint32_t dst; // Destination IP
uint8_t zero;
uint8_t proto; // Upper level protocol
uint16_t len; // Datagram length
};
struct pseudoip6 {
uint64_t src[2]; // Source IP
uint64_t dst[2]; // Destination IP
uint32_t plen; // Payload length
uint8_t zero[3];
uint8_t next; // Upper level protocol
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
static bool test_udp_csum(void *d, void *u) {
struct ip *ip = (struct ip *) d;
struct udp *udp = (struct udp *) u;
struct pseudoip pseudo, *pip;
pseudo.src = ip->src;
pseudo.dst = ip->dst;
pseudo.zero = 0;
pseudo.proto = ip->proto;
pseudo.len = udp->len;
pip = (struct pseudoip *)((size_t) u - sizeof(*pip));
memcpy(pip, &pseudo, sizeof(*pip));
return (ipcsum(pip, sizeof(*pip) + mg_ntohs(udp->len)) == 0);
}
static bool test_udp6_csum(void *d, void *u) {
struct ip6 *ip = (struct ip6 *) d;
struct udp *udp = (struct udp *) u;
struct pseudoip6 pseudo, *pip;
pseudo.src[0] = ip->src[0], pseudo.src[1] = ip->src[1];
pseudo.dst[0] = ip->dst[0], pseudo.dst[1] = ip->dst[1];
pseudo.zero[0] = 0, pseudo.zero[1] = 0, pseudo.zero[2] = 0;
pseudo.plen = mg_htonl((uint32_t) mg_ntohs(udp->len));
pseudo.next = ip->next;
pip = (struct pseudoip6 *) ((size_t) u - sizeof(*pip));
memcpy(pip, &pseudo, sizeof(*pip));
return (ipcsum(pip, sizeof(*pip) + mg_ntohs(udp->len)) == 0);
}
static bool test_tcp_csum(void *d, void *t) {
struct ip *ip = (struct ip *) d;
struct pseudoip pseudo, *pip;
pseudo.src = ip->src;
pseudo.dst = ip->dst;
pseudo.zero = 0;
pseudo.proto = ip->proto;
pseudo.len = mg_htons((uint16_t)(mg_ntohs(ip->len) - (ip->ver & 0x0F) * 4));
pip = (struct pseudoip *)((size_t) t - sizeof(*pip));
memcpy(pip, &pseudo, sizeof(*pip));
return (ipcsum(pip, sizeof(*pip) + mg_ntohs(pip->len)) == 0);
}
static bool test_tcp6_csum(void *d, void *t) {
struct ip6 *ip = (struct ip6 *) d;
struct pseudoip6 pseudo, *pip;
pseudo.src[0] = ip->src[0], pseudo.src[1] = ip->src[1];
pseudo.dst[0] = ip->dst[0], pseudo.dst[1] = ip->dst[1];
pseudo.zero[0] = 0, pseudo.zero[1] = 0, pseudo.zero[2] = 0;
pseudo.plen = mg_htonl((uint32_t) mg_ntohs(ip->plen));
pseudo.next = ip->next;
pip = (struct pseudoip6 *) ((size_t) t - sizeof(*pip));
memcpy(pip, &pseudo, sizeof(*pip));
return (ipcsum(pip, sizeof(*pip) + mg_ntohl(pip->plen)) == 0);
}
static bool executed = false;
static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
@@ -364,10 +454,13 @@ static void test_tcp_basics(bool ipv6) {
ASSERT(t->flags == (TH_RST | TH_ACK));
ASSERT(t->seq == mg_htonl(0));
ASSERT(t->ack == mg_htonl(1235));
mg_hexdump(s_driver_data.buf, s_driver_data.len);
if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2);
ASSERT(test_tcp6_csum(i6, t));
} else {
ASSERT(i->src == 1 && i->dst == 2);
ASSERT(test_tcp_csum(i, t));
}
// send SYN+ACK, expect RST
@@ -1068,8 +1161,10 @@ static void test_udp(bool ipv6) {
ASSERT(*((char *) (u + 1)) == 'P');
if (ipv6) {
ASSERT(i6->src[0] == 1 && i6->dst[0] == 2);
ASSERT(test_udp6_csum(i6, u));
} else {
ASSERT(i->src == 1 && i->dst == 2);
ASSERT(test_udp_csum(i, u));
}
s_driver_data.len = 0;