From: Yu Watanabe Date: Sun, 15 Mar 2026 04:30:38 +0000 (+0900) Subject: ip-util: introduce ip_checksum() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c87767d14ee2a26df8516357e8be56d882b66c6;p=thirdparty%2Fsystemd.git ip-util: introduce ip_checksum() It is equivalent to dhcp_packet_checksum(). It is generic and not specific to DHCP. Hence, renamed to ip_checksum(). --- diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 90eae88379a..3b17ad8ac42 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -4,10 +4,10 @@ ***/ #include -#include #include "dhcp-option.h" #include "dhcp-packet.h" +#include "ip-util.h" #include "log.h" #include "memory-util.h" @@ -79,41 +79,6 @@ int dhcp_message_init( return 0; } -uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) { - uint64_t *buf_64 = (uint64_t*)buf; - uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t)); - uint64_t sum = 0; - - /* See RFC1071 */ - - while (buf_64 < end_64) { - sum += *buf_64; - if (sum < *buf_64) - /* wrap around in one's complement */ - sum++; - - buf_64++; - } - - if (len % sizeof(uint64_t)) { - /* If the buffer is not aligned to 64-bit, we need - to zero-pad the last few bytes and add them in */ - uint64_t buf_tail = 0; - - memcpy(&buf_tail, buf_64, len % sizeof(uint64_t)); - - sum += buf_tail; - if (sum < buf_tail) - /* wrap around */ - sum++; - } - - while (sum >> 16) - sum = (sum & 0xffff) + (sum >> 16); - - return ~sum; -} - void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, uint16_t source_port, be32_t destination_addr, uint16_t destination_port, uint16_t len, int ip_service_type) { @@ -136,11 +101,11 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, packet->udp.len = htobe16(len - DHCP_IP_SIZE); packet->ip.check = packet->udp.len; - packet->udp.check = dhcp_packet_checksum(&packet->ip.ttl, len - 8); + packet->udp.check = ip_checksum(&packet->ip.ttl, len - 8); packet->ip.ttl = IPDEFTTL; packet->ip.check = 0; - packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE); + packet->ip.check = ip_checksum(&packet->ip, DHCP_IP_SIZE); } int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) { @@ -193,7 +158,7 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui if all the other checks have passed */ - if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) + if (ip_checksum(&packet->ip, hdrlen)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "ignoring packet: invalid IP checksum"); @@ -201,8 +166,7 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui packet->ip.check = packet->udp.len; packet->ip.ttl = 0; - if (dhcp_packet_checksum(&packet->ip.ttl, - be16toh(packet->udp.len) + 12)) + if (ip_checksum(&packet->ip.ttl, be16toh(packet->udp.len) + 12)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "ignoring packet: invalid UDP checksum"); } diff --git a/src/libsystemd-network/dhcp-packet.h b/src/libsystemd-network/dhcp-packet.h index 967bd5d89df..8a56383adda 100644 --- a/src/libsystemd-network/dhcp-packet.h +++ b/src/libsystemd-network/dhcp-packet.h @@ -23,8 +23,6 @@ int dhcp_message_init( size_t optlen, size_t *ret_optoffset); -uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); - void dhcp_packet_append_ip_headers( DHCPPacket *packet, be32_t source_addr, diff --git a/src/libsystemd-network/ip-util.c b/src/libsystemd-network/ip-util.c new file mode 100644 index 00000000000..dd7e872b2f0 --- /dev/null +++ b/src/libsystemd-network/ip-util.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "iovec-util.h" +#include "ip-util.h" + +static uint64_t complement_sum(uint64_t a, uint64_t b) { + /* This performs one's complement addition (end-around carry). See RFC1071. */ + if (a <= UINT64_MAX - b) + return a + b; + + return a - (UINT64_MAX - b); +} + +static uint64_t checksum_iov(uint64_t sum, const struct iovec *iov) { + assert(iov); + + for (struct iovec i = *iov; iovec_is_set(&i); iovec_inc(&i, sizeof(uint64_t))) { + uint64_t t = 0; + memcpy(&t, i.iov_base, MIN(i.iov_len, sizeof(uint64_t))); + sum = complement_sum(sum, t); + } + + return sum; +} + +static uint16_t checksum_finalize(uint64_t sum) { + while ((sum >> 16) != 0) + sum = (sum & 0xffffu) + (sum >> 16); + + return ~sum; +} + +uint16_t ip_checksum(const void *buf, size_t len) { + /* See RFC1071 */ + return checksum_finalize(checksum_iov(0, &IOVEC_MAKE(buf, len))); +} diff --git a/src/libsystemd-network/ip-util.h b/src/libsystemd-network/ip-util.h new file mode 100644 index 00000000000..dd11afbadac --- /dev/null +++ b/src/libsystemd-network/ip-util.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-forward.h" + +uint16_t ip_checksum(const void *buf, size_t len); diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index d1e13d99b53..b0443c36952 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -12,6 +12,7 @@ libsystemd_network_sources = files( 'dhcp6-protocol.c', 'icmp6-packet.c', 'icmp6-util.c', + 'ip-util.c', 'lldp-neighbor.c', 'lldp-network.c', 'ndisc-option.c', @@ -84,6 +85,9 @@ executables += [ network_test_template + { 'sources' : files('test-dhcp6-client.c'), }, + network_test_template + { + 'sources' : files('test-ip-util.c'), + }, network_test_template + { 'sources' : files('test-ipv4ll-manual.c'), 'type' : 'manual', diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 97802e2c164..b2cadc07e5b 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -17,9 +17,9 @@ #include "dhcp-duid-internal.h" #include "dhcp-network.h" #include "dhcp-option.h" -#include "dhcp-packet.h" #include "ether-addr-util.h" #include "fd-util.h" +#include "ip-util.h" #include "log.h" #include "tests.h" @@ -107,16 +107,6 @@ TEST(dhcp_client_anonymize) { ASSERT_OK_ZERO(sd_dhcp_client_set_request_option(client, 101)); } -TEST(dhcp_packet_checksum) { - uint8_t buf[20] = { - 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff - }; - - ASSERT_EQ(dhcp_packet_checksum(buf, 20), be16toh(0x78ae)); -} - TEST(dhcp_identifier_set_iaid) { uint32_t iaid_legacy; be32_t iaid; @@ -181,13 +171,13 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const discover->ip.ttl = 0; discover->ip.check = discover->udp.len; - udp_check = ~dhcp_packet_checksum(&discover->ip.ttl, len - 8); + udp_check = ~ip_checksum(&discover->ip.ttl, len - 8); ASSERT_EQ(udp_check, 0xffff); discover->ip.ttl = IPDEFTTL; discover->ip.check = ip_check; - ip_check = ~dhcp_packet_checksum((uint8_t*) &discover->ip, sizeof(discover->ip)); + ip_check = ~ip_checksum((uint8_t*) &discover->ip, sizeof(discover->ip)); ASSERT_EQ(ip_check, 0xffff); ASSERT_NE(discover->dhcp.xid, 0u); diff --git a/src/libsystemd-network/test-ip-util.c b/src/libsystemd-network/test-ip-util.c new file mode 100644 index 00000000000..58adb8f48ef --- /dev/null +++ b/src/libsystemd-network/test-ip-util.c @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "ip-util.h" +#include "tests.h" + +TEST(ip_checksum) { + uint8_t buf[20] = { + 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff + }; + + ASSERT_EQ(ip_checksum(buf, 20), be16toh(0x78ae)); +} + +DEFINE_TEST_MAIN(LOG_DEBUG);