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().
This commit is contained in:
Yu Watanabe
2026-04-25 05:23:37 +09:00
parent 37adb410a2
commit 7c87767d14
7 changed files with 72 additions and 56 deletions
+5 -41
View File
@@ -4,10 +4,10 @@
***/
#include <net/if_arp.h>
#include <string.h>
#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");
}
-2
View File
@@ -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,
+38
View File
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <string.h>
#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)));
}
+6
View File
@@ -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);
+4
View File
@@ -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',
+3 -13
View File
@@ -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);
+16
View File
@@ -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);