Dear systemd-networkd developers,

I think there is an issue with the generation of the UDP checksums on big-endian systems, which I already posted as an issue to the bugtracker (https://bugs.freedesktop.org/show_bug.cgi?id=78082) but since there was not reaction I'll give it a try in this List.

I run the latest systemd on x86 (LE) and powerpc (BE) machines, and the powerpc never aquired a lease - checking with wireshark it complains on incorrect UDP checksums.

Changing the checksum calculation to the code it borrowed from busybox (see attachment) fixed this issue for me. Since the code from busybox is less efficient I guess it is not a direct replacement, but it would be great having systemd-networkd also running on BE systems.

Best regards,
Thomas Ritter

diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index d72d7a6..de63217 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -60,53 +60,33 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
 }
 
 uint16_t dhcp_packet_checksum(void *buf, size_t len) {
-        uint64_t *buf_64 = buf;
-        uint64_t *end_64 = (uint64_t*)buf + (len / sizeof(uint64_t));
-        uint32_t *buf_32;
-        uint16_t *buf_16;
-        uint8_t *buf_8;
-        uint64_t sum = 0;
-
-        while (buf_64 < end_64) {
-                sum += *buf_64;
-                if (sum < *buf_64)
-                        sum++;
-
-                buf_64 ++;
-        }
-
-        buf_32 = (uint32_t*)buf_64;
-
-        if (len & sizeof(uint32_t)) {
-                sum += *buf_32;
-                if (sum < *buf_32)
-                        sum++;
-
-                buf_32 ++;
-        }
-
-        buf_16 = (uint16_t*)buf_32;
-
-        if (len & sizeof(uint16_t)) {
-                sum += *buf_16;
-                if (sum < *buf_16)
-                        sum ++;
-
-                buf_16 ++;
-        }
-
-        buf_8 = (uint8_t*)buf_16;
-
-        if (len & sizeof(uint8_t)) {
-                sum += *buf_8;
-                if (sum < *buf_8)
-                        sum++;
-        }
-
-        while (sum >> 16)
-                sum = (sum & 0xffff) + (sum >> 16);
-
-        return ~sum;
+    /*
+     * Our algorithm is simple, using a 32 bit accumulator,
+     * we add sequential 16 bit words to it, and at the end, fold
+     * back all the carry bits from the top 16 bits into the lower
+     * 16 bits.
+     */
+    uint16_t *addr = (uint16_t*)buf;
+    unsigned sum = 0;
+    while (nleft > 1) {
+        sum += *addr++;
+        nleft -= 2;
+    }
+
+    /* Mop up an odd byte, if necessary */
+    if (nleft == 1) {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+            sum += *(uint8_t*)addr;
+#else
+            sum += *(uint8_t*)addr << 8;
+#endif
+    }
+
+    /* Add back carry outs from top 16 bits to low 16 bits */
+    sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
+    sum += (sum >> 16);                     /* add carry */
+
+    return (uint16_t)~sum;
 }
 
 void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
_______________________________________________
systemd-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to