From 8e0ef3ca5c0eaba74645b3d26ca623802d7d9165 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Fri, 23 Jan 2026 17:45:03 -0300 Subject: [PATCH] rewrite L4 checksum calculation --- mongoose.c | 36 ++++++++++++------ src/net_builtin.c | 36 ++++++++++++------ test/mip_test.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 24 deletions(-) diff --git a/mongoose.c b/mongoose.c index 4aaa238b..2a16b6f5 100644 --- a/mongoose.c +++ b/mongoose.c @@ -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, diff --git a/src/net_builtin.c b/src/net_builtin.c index 2c525625..a8103105 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -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, diff --git a/test/mip_test.c b/test/mip_test.c index 2a788180..608bf185 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -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;