mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-11 05:17:44 +03:00
sd-dhcp: checksum - make endianess-neutral
For efficiency, we group bytes together before adding them up. This is guaranteed to always work (regardless of the byte order) as long as the i-th byte in each group lign up with the i-th byte in each other group. On big-endian machines this broke when handling the trailing few bytes which did not make up a full group of 4 bytes. This patch fixes the problem by explicitly creating a 4 byte zero-padded group out of the trailing bytes. Reported and tested by Thomas Ritter <th.ritter@gmx.at>.
This commit is contained in:
parent
735a1a2ea5
commit
0bbc2c1f3b
@ -48,7 +48,7 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
|
||||
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type,
|
||||
size_t optlen, size_t *optoffset);
|
||||
|
||||
uint16_t dhcp_packet_checksum(void *buf, size_t len);
|
||||
uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
|
||||
|
||||
void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
|
||||
uint16_t source, be32_t destination_addr,
|
||||
|
@ -60,47 +60,32 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
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 ++;
|
||||
}
|
||||
|
||||
buf_32 = (uint32_t*)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;
|
||||
|
||||
if (len & sizeof(uint32_t)) {
|
||||
sum += *buf_32;
|
||||
if (sum < *buf_32)
|
||||
sum++;
|
||||
memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
|
||||
|
||||
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 += buf_tail;
|
||||
if (sum < buf_tail)
|
||||
/* wrap around */
|
||||
sum++;
|
||||
}
|
||||
|
||||
@ -129,11 +114,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 = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
|
||||
|
||||
packet->ip.ttl = IPDEFTTL;
|
||||
packet->ip.check = 0;
|
||||
packet->ip.check = dhcp_packet_checksum(&packet->ip, DHCP_IP_SIZE);
|
||||
packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
|
||||
}
|
||||
|
||||
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
|
||||
@ -193,7 +178,7 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
|
||||
if all the other checks have passed
|
||||
*/
|
||||
|
||||
if (dhcp_packet_checksum(&packet->ip, hdrlen)) {
|
||||
if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
|
||||
log_debug("ignoring packet: invalid IP checksum");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -202,7 +187,7 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
|
||||
packet->ip.check = packet->udp.len;
|
||||
packet->ip.ttl = 0;
|
||||
|
||||
if (dhcp_packet_checksum(&packet->ip.ttl,
|
||||
if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
|
||||
be16toh(packet->udp.len) + 12)) {
|
||||
log_debug("ignoring packet: invalid UDP checksum");
|
||||
return -EINVAL;
|
||||
|
@ -128,7 +128,7 @@ static void test_checksum(void)
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
assert_se(dhcp_packet_checksum(&buf, 20) == be16toh(0x78ae));
|
||||
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
|
||||
}
|
||||
|
||||
static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
|
||||
@ -175,13 +175,13 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
|
||||
discover->ip.ttl = 0;
|
||||
discover->ip.check = discover->udp.len;
|
||||
|
||||
udp_check = ~dhcp_packet_checksum(&discover->ip.ttl, len - 8);
|
||||
udp_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip.ttl, len - 8);
|
||||
assert_se(udp_check == 0xffff);
|
||||
|
||||
discover->ip.ttl = IPDEFTTL;
|
||||
discover->ip.check = ip_check;
|
||||
|
||||
ip_check = ~dhcp_packet_checksum(&discover->ip, sizeof(discover->ip));
|
||||
ip_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip, sizeof(discover->ip));
|
||||
assert_se(ip_check == 0xffff);
|
||||
|
||||
assert_se(discover->dhcp.xid);
|
||||
|
Loading…
Reference in New Issue
Block a user