Here is an imperfect patch that calculates the UDP checksum if none was present.

This allows the 4to6 machinery to do its thing, the aforementioned DNS responses go through successfully, and now I can access the ESA website.  (Hooray!)

The imperfection is that I had to add an asm("":::"memory") memory barrier, after the newly assigned checksum gets written to the checksum field in the UDP header.  Without this memory barrier, it wasn't working for me (arm64, gcc 14.2.0).  When I temporarily changed -O2 to -O1, the problem went away.  The memory barrier gets it working with -O2.  I guess it's an aliasing problem, the compiler reordered something it shouldn't have, but I don't fully understand it.  Here's hoping this patch can serve as a starting point for something cleaner.

commit 80ccad0a8abe62520ad24cc1af0ecfba4e42490b
Author: Mark Glines <m...@glines.org>
Date:   Tue Nov 26 14:27:33 2024 -0500

    Handle null UDP v4 checksums

diff --git a/nat64.c b/nat64.c
index a88f81d..5cfe425 100644
--- a/nat64.c
+++ b/nat64.c
@@ -190,8 +190,23 @@ static int xlate_payload_4to6(struct pkt *p, struct ip6 *ip6)
 		if (p->data_len < 8)
 			return -1;
 		tck = (uint16_t *)(p->data + 6);
-		if (!*tck)
-			return -1; /* drop UDP packets with no checksum */
+		if (!*tck) {
+			/* UDP checksums are optional in ipv4, but mandatory in ipv6.
+			 * This v4 packet doesn't have one.
+			 * Give it one, so that it can be converted to v6.
+			 */
+			struct udp_v4_pseudoheader phdr = {
+				.src = p->ip4->src,
+				.dest = p->ip4->dest,
+				.zero = 0,
+				.proto = 17,
+				.udplen = htons(p->data_len)
+			};
+			cksum = ip_checksum(p->data, p->data_len);
+			cksum = ones_add(ip_checksum(&phdr, sizeof(phdr)), cksum);
+			*tck = cksum;
+			asm volatile("":::"memory");
+		}
 		break;
 	case 6:
 		if (p->data_len < 20)
diff --git a/tayga.h b/tayga.h
index c8aadb8..a2b1838 100644
--- a/tayga.h
+++ b/tayga.h
@@ -93,6 +93,14 @@ struct ip6_frag {
 #define IP6_F_MF	0x0001
 #define IP6_F_MASK	0xfff8
 
+struct udp_v4_pseudoheader {
+	struct in_addr src;
+	struct in_addr dest;
+	uint8_t zero;
+	uint8_t proto;
+	uint16_t udplen;
+} __attribute__ ((__packed__));
+
 struct icmp {
 	uint8_t type;
 	uint8_t code;

Reply via email to