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;