ip-util: introduce several functions for IP packet handling (#41801)

This commit is contained in:
Yu Watanabe
2026-04-25 10:15:54 +09:00
committed by GitHub
9 changed files with 517 additions and 144 deletions
+3 -1
View File
@@ -98,7 +98,7 @@ int dhcp_client_send_raw(
fd_close = fd;
}
dhcp_packet_append_ip_headers(
r = dhcp_packet_append_ip_headers(
packet,
INADDR_ANY,
client->port,
@@ -106,6 +106,8 @@ int dhcp_client_send_raw(
client->server_port,
sizeof(DHCPPacket) + optoffset,
client->ip_service_type);
if (r < 0)
return r;
r = dhcp_network_send_raw_socket(
fd,
+34 -124
View File
@@ -4,11 +4,12 @@
***/
#include <net/if_arp.h>
#include <string.h>
#include "dhcp-option.h"
#include "dhcp-packet.h"
#include "log.h"
#include "iovec-util.h"
#include "iovec-wrapper.h"
#include "ip-util.h"
#include "memory-util.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
@@ -79,133 +80,42 @@ 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;
int 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) {
/* See RFC1071 */
struct iphdr ip;
struct udphdr udp;
int r;
while (buf_64 < end_64) {
sum += *buf_64;
if (sum < *buf_64)
/* wrap around in one's complement */
sum++;
assert(packet);
assert(len > offsetof(DHCPPacket, dhcp));
buf_64++;
}
r = udp_packet_build(
source_addr,
source_port,
destination_addr,
destination_port,
ip_service_type,
&(struct iovec_wrapper) {
.iovec = &IOVEC_MAKE(&packet->dhcp, len - offsetof(DHCPPacket, dhcp)),
.count = 1,
},
&ip,
&udp);
if (r < 0)
return r;
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) {
packet->ip.version = IPVERSION;
packet->ip.ihl = DHCP_IP_SIZE / 4;
packet->ip.tot_len = htobe16(len);
if (ip_service_type >= 0)
packet->ip.tos = ip_service_type;
else
packet->ip.tos = IPTOS_CLASS_CS6;
packet->ip.protocol = IPPROTO_UDP;
packet->ip.saddr = source_addr;
packet->ip.daddr = destination_addr;
packet->udp.source = htobe16(source_port);
packet->udp.dest = htobe16(destination_port);
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->ip.ttl = IPDEFTTL;
packet->ip.check = 0;
packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
packet->ip = ip;
packet->udp = udp;
return 0;
}
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
size_t hdrlen;
assert(packet);
if (len < sizeof(DHCPPacket))
return 0;
/* IP */
if (packet->ip.version != IPVERSION)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: not IPv4");
if (packet->ip.ihl < 5)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: IPv4 IHL (%i words) invalid",
packet->ip.ihl);
hdrlen = packet->ip.ihl * 4;
if (hdrlen < 20)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: IPv4 IHL (%zu bytes) smaller than minimum (20 bytes)",
hdrlen);
if (len < hdrlen)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: packet (%zu bytes) smaller than expected (%zu) by IP header",
len, hdrlen);
/* UDP */
if (packet->ip.protocol != IPPROTO_UDP)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: not UDP");
if (len < hdrlen + be16toh(packet->udp.len))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: packet (%zu bytes) smaller than expected (%zu) by UDP header",
len, hdrlen + be16toh(packet->udp.len));
if (be16toh(packet->udp.dest) != port)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: to port %u, which is not the DHCP client port (%u)",
be16toh(packet->udp.dest), port);
/* checksums - computing these is relatively expensive, so only do it
if all the other checks have passed
*/
if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: invalid IP checksum");
if (checksum && packet->udp.check) {
packet->ip.check = packet->udp.len;
packet->ip.ttl = 0;
if (dhcp_packet_checksum(&packet->ip.ttl,
be16toh(packet->udp.len) + 12))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"ignoring packet: invalid UDP checksum");
}
return 0;
return udp_packet_verify(&IOVEC_MAKE(packet, len), port, checksum, /* ret_payload= */ NULL);
}
+1 -3
View File
@@ -23,9 +23,7 @@ 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(
int dhcp_packet_append_ip_headers(
DHCPPacket *packet,
be32_t source_addr,
uint16_t source,
+251
View File
@@ -0,0 +1,251 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <string.h>
#include "iovec-util.h"
#include "iovec-wrapper.h"
#include "ip-util.h"
#include "log.h"
union iphdr_union {
struct iphdr ip;
uint8_t buf[15 * 4]; /* ip->ihl is 4 bits, hence max length is 15 * 4 */
};
struct udp_pseudo_header {
be32_t saddr;
be32_t daddr;
uint8_t unused;
uint8_t protocol;
be16_t len;
} _packed_;
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)));
}
static uint16_t iphdr_checksum(const union iphdr_union *ip) {
assert(ip);
return ip_checksum(ip, ip->ip.ihl * 4);
}
static uint16_t udphdr_checksum(
be32_t saddr,
be32_t daddr,
const struct udphdr *udp,
const struct iovec_wrapper *payload) {
assert(udp);
assert(payload);
/* RFC 768 */
struct udp_pseudo_header pseudo = {
.saddr = saddr,
.daddr = daddr,
.protocol = IPPROTO_UDP,
.len = udp->len,
};
uint64_t sum = 0;
sum = checksum_iov(sum, &IOVEC_MAKE(&pseudo, sizeof(struct udp_pseudo_header)));
sum = checksum_iov(sum, &IOVEC_MAKE(udp, sizeof(struct udphdr)));
uint8_t buf[2] = {};
bool odd = false;
FOREACH_ARRAY(i, payload->iovec, payload->count) {
if (!iovec_is_set(i))
continue;
struct iovec v = *i;
if (odd) {
buf[1] = *(uint8_t*) v.iov_base;
sum = checksum_iov(sum, &IOVEC_MAKE(buf, 2));
iovec_inc(&v, 1);
}
odd = v.iov_len % 2;
if (odd) {
buf[0] = ((uint8_t*) v.iov_base)[v.iov_len - 1];
v.iov_len--;
}
sum = checksum_iov(sum, &v);
}
if (odd) {
buf[1] = 0;
sum = checksum_iov(sum, &IOVEC_MAKE(buf, 2));
}
return checksum_finalize(sum);
}
int udp_packet_build(
be32_t source_addr,
uint16_t source_port,
be32_t destination_addr,
uint16_t destination_port,
int ip_service_type,
const struct iovec_wrapper *payload,
struct iphdr *ret_iphdr,
struct udphdr *ret_udphdr) {
assert(payload);
assert(ret_iphdr);
assert(ret_udphdr);
/* When ip_service_type is negative, IPTOS_CLASS_CS6 will be used. Otherwise, it must be a valid TOS,
* hence must be in 0…255. Here, we only check its range. */
if (ip_service_type > UINT8_MAX)
return -EINVAL;
/* iphdr.tot_len is uint16_t, hence the total length must be <= UINT16_MAX. */
size_t len = iovw_size(payload);
if (len > UDP_PAYLOAD_MAX_SIZE)
return -E2BIG;
union iphdr_union ip = {
.ip.version = IPVERSION,
.ip.ihl = sizeof(struct iphdr) / 4,
.ip.tos = ip_service_type >= 0 ? ip_service_type : IPTOS_CLASS_CS6,
.ip.tot_len = htobe16(sizeof(struct iphdr) + sizeof(struct udphdr) + len),
.ip.ttl = IPDEFTTL,
.ip.protocol = IPPROTO_UDP,
.ip.saddr = source_addr,
.ip.daddr = destination_addr,
};
ip.ip.check = iphdr_checksum(&ip);
struct udphdr udp = {
.source = htobe16(source_port),
.dest = htobe16(destination_port),
.len = htobe16(sizeof(struct udphdr) + len),
};
udp.check = udphdr_checksum(source_addr, destination_addr, &udp, payload);
*ret_iphdr = ip.ip;
*ret_udphdr = udp;
return 0;
}
int udp_packet_verify(
const struct iovec *packet,
uint16_t port,
bool checksum,
struct iovec *ret_payload) {
assert(packet);
/* This verifies IP and UDP packet headers and optionally returns the UDP payload. */
/* IP */
if (packet->iov_len < sizeof(struct iphdr))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: packet (%zu bytes) smaller than minimum IP header (%zu bytes), ignoring packet.",
packet->iov_len, sizeof(struct iphdr));
const union iphdr_union *ip = (const union iphdr_union*) packet->iov_base;
if (ip->ip.version != IPVERSION)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: packet is not IPv4, ignoring packet.");
size_t iphdrlen = ip->ip.ihl * 4;
if (iphdrlen < sizeof(struct iphdr))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: IP header size (%zu bytes) smaller than minimum (%zu bytes), ignoring packet.",
iphdrlen, sizeof(struct iphdr));
if (packet->iov_len < iphdrlen)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: packet (%zu bytes) smaller than IP header size (%zu bytes), ignoring packet.",
packet->iov_len, iphdrlen);
size_t totlen = be16toh(ip->ip.tot_len);
if (totlen < iphdrlen)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: packet size (%zu bytes) by IP header is smaller than the IP header size (%zu), ignoring packet.",
totlen, iphdrlen);
if (packet->iov_len < totlen)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: packet (%zu bytes) smaller than expected (%zu) by IP header, ignoring packet.",
packet->iov_len, totlen);
if (ip->ip.protocol != IPPROTO_UDP)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: not UDP, ignoring packet.");
if (iphdr_checksum(ip) != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"IPv4: invalid IP checksum, ignoring packet.");
/* UDP */
if (totlen < iphdrlen + sizeof(struct udphdr))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"UDP: packet (%zu bytes) smaller than IP header + UDP header, ignoring packet.",
totlen);
const struct udphdr *udp = (const struct udphdr*) ((const uint8_t*) packet->iov_base + iphdrlen);
size_t udplen = be16toh(udp->len);
if (udplen < sizeof(struct udphdr))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"UDP: UDP datagram (%zu bytes) smaller than UDP header (%zu bytes), ignoring packet.",
udplen, sizeof(struct udphdr));
if (totlen != iphdrlen + udplen)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"UDP: packet length by IP header (%zu bytes) does not match with the one by UDP header "
"(IP header %zu bytes + UDP %zu bytes = %zu bytes), ignoring packet.",
totlen, iphdrlen, udplen, iphdrlen + udplen);
if (be16toh(udp->dest) != port)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"UDP: to port %u, which is not the expected port (%u), ignoring packet.",
be16toh(udp->dest), port);
/* Calculate the UDP payload length from the UDP header (udplen), rather than the input packet length
* (len). The packet may contain garbage at the end. */
struct iovec payload = IOVEC_MAKE(
(const uint8_t*) packet->iov_base + iphdrlen + sizeof(struct udphdr),
udplen - sizeof(struct udphdr));
if (checksum && udp->check != 0 &&
udphdr_checksum(ip->ip.saddr, ip->ip.daddr, udp,
&(struct iovec_wrapper) {
.iovec = &payload,
.count = 1,
}) != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"UDP: invalid UDP checksum, ignoring packet.");
if (ret_payload)
*ret_payload = payload;
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <netinet/ip.h>
#include <netinet/udp.h>
#include "sd-forward.h"
#include "sparse-endian.h"
/* RFC 791
* Fragmentation and Reassembly.
* Every internet destination must be able to receive a datagram of 576 octets either in one piece or in
* fragments to be reassembled. */
#define IPV4_MIN_REASSEMBLY_SIZE 576u
/* This is a maximal UDP payload size in a packet when its IP header does not contain options. When a packet
* contains some IP options, then of course the allowed UDP payload size in the packet becomes smaller. */
#define UDP_PAYLOAD_MAX_SIZE (UINT16_MAX - sizeof(struct iphdr) - sizeof(struct udphdr))
uint16_t ip_checksum(const void *buf, size_t len);
int udp_packet_build(
be32_t source_addr,
uint16_t source_port,
be32_t destination_addr,
uint16_t destination_port,
int ip_service_type,
const struct iovec_wrapper *payload,
struct iphdr *ret_iphdr,
struct udphdr *ret_udphdr);
int udp_packet_verify(
const struct iovec *packet,
uint16_t port,
bool checksum,
struct iovec *ret_payload);
+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',
+11 -3
View File
@@ -322,6 +322,7 @@ static int dhcp_server_send_unicast_raw(
.ll.sll_ifindex = server->ifindex,
.ll.sll_halen = hlen,
};
int r;
assert(server);
assert(server->ifindex > 0);
@@ -336,9 +337,16 @@ static int dhcp_server_send_unicast_raw(
if (len > UINT16_MAX)
return -EOVERFLOW;
dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
packet->dhcp.yiaddr,
DHCP_PORT_CLIENT, len, -1);
r = dhcp_packet_append_ip_headers(
packet,
server->address,
DHCP_PORT_SERVER,
packet->dhcp.yiaddr,
DHCP_PORT_CLIENT,
len,
/* ip_service_type= */ -1);
if (r < 0)
return r;
return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
}
+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);
+173
View File
@@ -0,0 +1,173 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "iovec-util.h"
#include "iovec-wrapper.h"
#include "ip-util.h"
#include "random-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));
}
static void create_packet(struct iphdr *ip, struct udphdr *udp, struct iovec_wrapper *payload, struct iovec *ret) {
_cleanup_(iovw_done) struct iovec_wrapper iovw = {};
ASSERT_OK(iovw_put(&iovw, ip, sizeof(struct iphdr)));
ASSERT_OK(iovw_put(&iovw, udp, sizeof(struct udphdr)));
ASSERT_OK(iovw_put_iovw(&iovw, payload));
ASSERT_OK(iovw_concat(&iovw, ret));
}
TEST(udp_packet_build_and_verify) {
size_t n = random_u64_range(20) + 20;
_cleanup_(iovw_done_free) struct iovec_wrapper payload = {};
size_t i;
FOREACH_ARGUMENT(i, 1, 0, 1, 1, 3, 1, 2, 1, n, n, n + 1, n + 1, n + 2, n + 3, n + 4, n + 5, n + 6) {
struct iovec tmp = {};
ASSERT_OK(random_bytes_allocate_iovec(i, &tmp));
ASSERT_OK(iovw_consume_iov(&payload, &tmp));
}
struct iphdr ip;
struct udphdr udp;
ASSERT_OK(udp_packet_build(
/* source_addr= */ htobe32(0xC0020001),
/* source_port= */ 42,
/* destination_addr= */ htobe32(0xC0020002),
/* destination_port= */ 43,
/* ip_service_type= */ 7,
&payload,
&ip,
&udp));
_cleanup_(iovec_done) struct iovec joined = {};
ASSERT_OK(iovw_concat(&payload, &joined));
struct iphdr ip2;
struct udphdr udp2;
ASSERT_OK(udp_packet_build(
/* source_addr= */ htobe32(0xC0020001),
/* source_port= */ 42,
/* destination_addr= */ htobe32(0xC0020002),
/* destination_port= */ 43,
/* ip_service_type= */ 7,
&(struct iovec_wrapper) {
.iovec = &joined,
.count = 1,
},
&ip2,
&udp2));
ASSERT_EQ(memcmp(&ip, &ip2, sizeof(struct iphdr)), 0);
ASSERT_EQ(memcmp(&udp, &udp2, sizeof(struct udphdr)), 0);
_cleanup_(iovec_done) struct iovec packet = {};
create_packet(&ip, &udp, &payload, &packet);
struct iovec iov;
ASSERT_OK(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, &iov));
ASSERT_TRUE(iovec_equal(&iov, &joined));
ASSERT_OK(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ true, &iov));
ASSERT_TRUE(iovec_equal(&iov, &joined));
/* UDP port mismatch */
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 42, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* truncated packet */
ASSERT_ERROR(udp_packet_verify(&IOVEC_MAKE(packet.iov_base, packet.iov_len - 1),
/* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* bad IP version */
struct iphdr badip = ip;
badip.version = 6;
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* bad IP header size */
badip = ip;
badip.ihl = 1;
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* packet size in IP header is smaller than IP header size */
badip = ip;
badip.tot_len = htobe16(1);
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* packet size in IP header is larger than the packet size */
badip = ip;
badip.tot_len = htobe16(be16toh(ip.tot_len) + 1);
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* IP protocol mismatch */
badip = ip;
badip.protocol = IPPROTO_TCP;
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* bad IP header checksum */
badip = ip;
badip.check = ~ip.check;
iovec_done(&packet);
create_packet(&badip, &udp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* UDP length is smaller than the UDP header size */
struct udphdr badudp = udp;
badudp.len = htobe16(1);
iovec_done(&packet);
create_packet(&ip, &badudp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* UDP length is smaller than the packet size */
badudp = udp;
badudp.len = htobe16(be16toh(udp.len) - 1);
iovec_done(&packet);
create_packet(&ip, &badudp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* UDP length is larger than the packet size */
badudp = udp;
badudp.len = htobe16(be16toh(udp.len) + 1);
iovec_done(&packet);
create_packet(&ip, &badudp, &payload, &packet);
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, /* ret_payload= */ NULL), EBADMSG);
/* bad UDP checksum */
badudp = udp;
if (udp.check != UINT16_MAX)
badudp.check = ~udp.check;
else
badudp.check = 0xdeadu;
iovec_done(&packet);
create_packet(&ip, &badudp, &payload, &packet);
ASSERT_OK(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, &iov));
ASSERT_TRUE(iovec_equal(&iov, &joined));
ASSERT_ERROR(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ true, /* ret_payload= */ NULL), EBADMSG);
/* missing UDP checksum */
badudp = udp;
badudp.check = 0;
iovec_done(&packet);
create_packet(&ip, &badudp, &payload, &packet);
ASSERT_OK(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ false, &iov));
ASSERT_TRUE(iovec_equal(&iov, &joined));
ASSERT_OK(udp_packet_verify(&packet, /* port= */ 43, /* checksum= */ true, &iov));
ASSERT_TRUE(iovec_equal(&iov, &joined));
}
DEFINE_TEST_MAIN(LOG_DEBUG);