Merge pull request #29921 from yuwata/sd-dhcp-client-timestamp

dhcp: further cleanups for timestamp
This commit is contained in:
Luca Boccassi
2023-11-08 10:55:11 +00:00
committed by GitHub
10 changed files with 222 additions and 156 deletions
+1 -1
View File
@@ -90,8 +90,8 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains);
int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
#define dhcp_lease_unref_and_replace(a, b) \
+8
View File
@@ -115,6 +115,10 @@ static int _bind_raw_socket(
if (r < 0)
return -errno;
r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
return r;
if (so_priority_set) {
r = setsockopt_int(s, SOL_SOCKET, SO_PRIORITY, so_priority);
if (r < 0)
@@ -206,6 +210,10 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
if (r < 0)
return r;
r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
return r;
if (ifindex > 0) {
r = socket_bind_to_ifindex(s, ifindex);
if (r < 0)
+3 -1
View File
@@ -129,7 +129,9 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui
size_t hdrlen;
assert(packet);
assert(len >= sizeof(DHCPPacket));
if (len < sizeof(DHCPPacket))
return 0;
/* IP */
@@ -110,12 +110,13 @@ typedef struct DHCPRequest {
const uint8_t *parameter_request_list;
size_t parameter_request_list_len;
bool rapid_commit;
triple_timestamp timestamp;
} DHCPRequest;
extern const struct hash_ops dhcp_lease_hash_ops;
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length);
size_t length, const triple_timestamp *timestamp);
int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
+1 -1
View File
@@ -75,7 +75,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
client->xid = 2;
client->state = DHCP_STATE_SELECTING;
(void) client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size);
(void) client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size, NULL);
assert_se(sd_dhcp_client_stop(client) >= 0);
+1 -1
View File
@@ -96,7 +96,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(add_static_lease(server, 3) >= 0);
assert_se(add_static_lease(server, 4) >= 0);
(void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size);
(void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size, NULL);
return 0;
}
+129 -118
View File
@@ -116,7 +116,6 @@ struct sd_dhcp_client {
uint64_t max_attempts;
OrderedHashmap *extra_options;
OrderedHashmap *vendor_options;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
sd_event_source *timeout_expire;
@@ -1366,16 +1365,12 @@ static int client_timeout_resend(
client->attempt = 0;
} else if (client->attempt >= client->max_attempts)
goto error;
client->request_sent = time_now;
break;
case DHCP_STATE_SELECTING:
r = client_send_discover(client);
if (r < 0 && client->attempt >= client->max_attempts)
goto error;
client->request_sent = time_now;
break;
case DHCP_STATE_INIT_REBOOT:
@@ -1388,8 +1383,6 @@ static int client_timeout_resend(
if (client->state == DHCP_STATE_INIT_REBOOT)
client_set_state(client, DHCP_STATE_REBOOTING);
client->request_sent = time_now;
break;
case DHCP_STATE_REBOOTING:
@@ -1672,7 +1665,7 @@ static int client_parse_message(
return 0;
}
static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len, const triple_timestamp *timestamp) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
int r;
@@ -1683,6 +1676,8 @@ static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage
if (r < 0)
return r;
dhcp_lease_set_timestamp(lease, timestamp);
dhcp_lease_unref_and_replace(client->lease, lease);
if (client->lease->rapid_commit) {
@@ -1786,7 +1781,7 @@ static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
return true;
}
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len, const triple_timestamp *timestamp) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
int r;
@@ -1797,6 +1792,8 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *message, size_
if (r < 0)
return r;
dhcp_lease_set_timestamp(lease, timestamp);
if (!client->lease)
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
else if (lease_equal(client->lease, lease))
@@ -1818,8 +1815,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
assert(client->event);
assert(client->lease);
assert(client->lease->lifetime > 0);
triple_timestamp_from_boottime(&client->lease->timestamp, client->request_sent);
assert(triple_timestamp_is_set(&client->lease->timestamp));
/* don't set timers for infinite leases */
if (client->lease->lifetime == USEC_INFINITY) {
@@ -1833,7 +1829,6 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
return r;
assert(client->request_sent <= time_now);
/* verify that 0 < t2 < lifetime */
if (client->lease->t2 == 0 || client->lease->t2 >= client->lease->lifetime)
@@ -1851,9 +1846,15 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
assert(client->lease->t1 < client->lease->t2);
assert(client->lease->t2 < client->lease->lifetime);
client->expire_time = usec_add(client->request_sent, client->lease->lifetime);
client->t1_time = usec_add(client->request_sent, client->lease->t1);
client->t2_time = usec_add(client->request_sent, client->lease->t2);
r = sd_dhcp_lease_get_lifetime_timestamp(client->lease, CLOCK_BOOTTIME, &client->expire_time);
if (r < 0)
return r;
r = sd_dhcp_lease_get_t1_timestamp(client->lease, CLOCK_BOOTTIME, &client->t1_time);
if (r < 0)
return r;
r = sd_dhcp_lease_get_t2_timestamp(client->lease, CLOCK_BOOTTIME, &client->t2_time);
if (r < 0)
return r;
/* RFC2131 section 4.4.5:
* Times T1 and T2 SHOULD be chosen with some random "fuzz".
@@ -2012,88 +2013,117 @@ static int client_restart(sd_dhcp_client *client) {
return 0;
}
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
static int client_verify_message_header(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
const uint8_t *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
assert(client);
assert(message);
if (len < sizeof(DHCPMessage))
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Too small to be a DHCP message, ignoring.");
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Not a DHCP message, ignoring.");
if (message->op != BOOTREPLY)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Not a BOOTREPLY message, ignoring.");
if (message->htype != client->arp_type)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Packet type does not match client type, ignoring.");
if (client->arp_type == ARPHRD_ETHER) {
expected_hlen = ETH_ALEN;
expected_chaddr = client->hw_addr.bytes;
}
if (message->hlen != expected_hlen)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Received packet hlen (%u) does not match expected (%u), ignoring.",
message->hlen, expected_hlen);
if (memcmp_safe(message->chaddr, expected_chaddr, expected_hlen))
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Received chaddr does not match expected, ignoring.");
if (client->state != DHCP_STATE_BOUND &&
be32toh(message->xid) != client->xid)
/* in BOUND state, we may receive FORCERENEW with xid set by server,
so ignore the xid in this case */
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
"Received xid (%u) does not match expected (%u), ignoring.",
be32toh(message->xid), client->xid);
return 0;
}
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, size_t len, const triple_timestamp *timestamp) {
DHCP_CLIENT_DONT_DESTROY(client);
int r;
assert(client);
assert(client->event);
assert(message);
if (client_verify_message_header(client, message, len) < 0)
return 0;
switch (client->state) {
case DHCP_STATE_SELECTING:
r = client_handle_offer_or_rapid_ack(client, message, len);
r = client_handle_offer_or_rapid_ack(client, message, len, timestamp);
if (ERRNO_IS_NEG_RESOURCE(r))
goto error;
if (r == -EADDRNOTAVAIL) {
return r;
if (r == -EADDRNOTAVAIL)
/* got a rapid NAK, let's restart the client */
r = client_restart(client);
if (r < 0)
goto error;
return 0;
}
return client_restart(client);
if (r < 0)
return 0; /* invalid message, let's ignore it */
if (client->lease->rapid_commit)
/* got a successful rapid commit */
r = client_enter_bound(client, r);
else
r = client_enter_requesting(client);
break;
return client_enter_bound(client, r);
return client_enter_requesting(client);
case DHCP_STATE_REBOOTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_handle_ack(client, message, len);
r = client_handle_ack(client, message, len, timestamp);
if (ERRNO_IS_NEG_RESOURCE(r))
goto error;
if (r == -EADDRNOTAVAIL) {
return r;
if (r == -EADDRNOTAVAIL)
/* got a NAK, let's restart the client */
r = client_restart(client);
if (r < 0)
goto error;
return 0;
}
return client_restart(client);
if (r < 0)
return 0; /* invalid message, let's ignore it */
r = client_enter_bound(client, r);
break;
return client_enter_bound(client, r);
case DHCP_STATE_BOUND:
r = client_handle_forcerenew(client, message, len);
if (r == -ENOMSG)
return 0; /* invalid message, let's ignore it */
if (ERRNO_IS_NEG_RESOURCE(r))
return r;
if (r < 0)
goto error;
return 0; /* invalid message, let's ignore it */
r = client_timeout_t1(NULL, 0, client);
break;
return client_timeout_t1(NULL, 0, client);
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
r = 0;
break;
log_dhcp_client(client, "Unexpectedly receive message without sending any requests, ignoring.");
return 0;
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
default:
assert_not_reached();
}
error:
if (r < 0)
client_stop(client, r);
return r;
return 0;
}
static int client_receive_message_udp(
@@ -2104,9 +2134,18 @@ static int client_receive_message_udp(
sd_dhcp_client *client = ASSERT_PTR(userdata);
_cleanup_free_ DHCPMessage *message = NULL;
const uint8_t *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
ssize_t len, buflen;
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
struct iovec iov;
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
triple_timestamp t = {};
int r;
assert(s);
@@ -2122,60 +2161,25 @@ static int client_receive_message_udp(
if (!message)
return -ENOMEM;
len = recv(fd, message, buflen, 0);
iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
return 0;
if (len < 0) {
if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
return 0;
log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCPMessage)) {
log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
log_dhcp_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
log_dhcp_client(client, "Not a DHCP message: ignoring");
return 0;
}
if (message->op != BOOTREPLY) {
log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
return 0;
}
if (message->htype != client->arp_type) {
log_dhcp_client(client, "Packet type does not match client type");
return 0;
}
if (client->arp_type == ARPHRD_ETHER) {
expected_hlen = ETH_ALEN;
expected_chaddr = client->hw_addr.bytes;
}
if (message->hlen != expected_hlen) {
log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
return 0;
}
if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) {
log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
return 0;
}
if (client->state != DHCP_STATE_BOUND &&
be32toh(message->xid) != client->xid) {
/* in BOUND state, we may receive FORCERENEW with xid set by server,
so ignore the xid in this case */
log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
be32toh(message->xid), client->xid);
return 0;
}
struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
if (tv)
triple_timestamp_from_realtime(&t, timeval_load(tv));
log_dhcp_client(client, "Received message from UDP socket, processing.");
(void) client_handle_message(client, message, len);
r = client_handle_message(client, message, len, &t);
if (r < 0)
client_stop(client, r);
return 0;
}
@@ -2187,7 +2191,9 @@ static int client_receive_message_raw(
sd_dhcp_client *client = ASSERT_PTR(userdata);
_cleanup_free_ DHCPPacket *packet = NULL;
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL +
CMSG_SPACE(sizeof(struct tpacket_auxdata))) control = {};
struct iovec iov = {};
struct msghdr msg = {
.msg_iov = &iov,
@@ -2197,6 +2203,7 @@ static int client_receive_message_raw(
};
struct cmsghdr *cmsg;
bool checksum = true;
triple_timestamp t = {};
ssize_t buflen, len;
int r;
@@ -2223,23 +2230,27 @@ static int client_receive_message_raw(
log_dhcp_client_errno(client, len, "Could not receive message from raw socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCPPacket))
return 0;
cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
if (cmsg) {
struct tpacket_auxdata *aux = CMSG_TYPED_DATA(cmsg, struct tpacket_auxdata);
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
}
CMSG_FOREACH(cmsg, &msg)
if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
struct tpacket_auxdata *aux = CMSG_TYPED_DATA(cmsg, struct tpacket_auxdata);
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
if (r < 0)
} else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval *tv = CMSG_TYPED_DATA(cmsg, struct timeval);
triple_timestamp_from_realtime(&t, timeval_load(tv));
}
if (dhcp_packet_verify_headers(packet, len, checksum, client->port) < 0)
return 0;
len -= DHCP_IP_UDP_SIZE;
log_dhcp_client(client, "Received message from RAW socket, processing.");
(void) client_handle_message(client, &packet->dhcp, len);
r = client_handle_message(client, &packet->dhcp, len, &t);
if (r < 0)
client_stop(client, r);
return 0;
}
+9
View File
@@ -33,6 +33,15 @@
#include "tmpfile-util.h"
#include "unaligned.h"
void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp) {
assert(lease);
if (timestamp && triple_timestamp_is_set(timestamp))
lease->timestamp = *timestamp;
else
triple_timestamp_get(&lease->timestamp);
}
int sd_dhcp_lease_get_timestamp(sd_dhcp_lease *lease, clockid_t clock, uint64_t *ret) {
assert_return(lease, -EINVAL);
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
+45 -10
View File
@@ -897,6 +897,31 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
return 0;
}
static void request_set_timestamp(DHCPRequest *req, const triple_timestamp *timestamp) {
assert(req);
if (timestamp && triple_timestamp_is_set(timestamp))
req->timestamp = *timestamp;
else
triple_timestamp_get(&req->timestamp);
}
static int request_get_lifetime_timestamp(DHCPRequest *req, clockid_t clock, usec_t *ret) {
assert(req);
assert(TRIPLE_TIMESTAMP_HAS_CLOCK(clock));
assert(clock_supported(clock));
assert(ret);
if (req->lifetime <= 0)
return -ENODATA;
if (!triple_timestamp_is_set(&req->timestamp))
return -ENODATA;
*ret = usec_add(triple_timestamp_by_clock(&req->timestamp, clock), req->lifetime);
return 0;
}
static bool address_is_in_pool(sd_dhcp_server *server, be32_t address) {
assert(server);
@@ -1031,19 +1056,17 @@ static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest
}
static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
usec_t time_now, expiration;
usec_t expiration;
int r;
assert(server);
assert(req);
assert(address != 0);
r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
r = request_get_lifetime_timestamp(req, CLOCK_BOOTTIME, &expiration);
if (r < 0)
return r;
expiration = usec_add(req->lifetime, time_now);
if (existing_lease) {
assert(existing_lease->server);
assert(existing_lease->address == address);
@@ -1148,7 +1171,7 @@ static int server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *re
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp) {
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
_cleanup_free_ char *error_message = NULL;
DHCPLease *existing_lease, *static_lease;
@@ -1172,6 +1195,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
if (r < 0)
return r;
request_set_timestamp(req, timestamp);
r = dhcp_server_cleanup_expired_leases(server);
if (r < 0)
return r;
@@ -1354,7 +1379,9 @@ static size_t relay_agent_information_length(const char* agent_circuit_id, const
static int server_receive_message(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
_cleanup_free_ DHCPMessage *message = NULL;
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control;
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL +
CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
sd_dhcp_server *server = ASSERT_PTR(userdata);
struct iovec iov = {};
struct msghdr msg = {
@@ -1364,6 +1391,8 @@ static int server_receive_message(sd_event_source *s, int fd,
.msg_controllen = sizeof(control),
};
ssize_t datagram_size, len;
struct cmsghdr *cmsg;
triple_timestamp t = {};
int r;
datagram_size = next_datagram_size_fd(fd);
@@ -1397,16 +1426,22 @@ static int server_receive_message(sd_event_source *s, int fd,
return 0;
/* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
struct in_pktinfo *info = CMSG_FIND_DATA(&msg, IPPROTO_IP, IP_PKTINFO, struct in_pktinfo);
if (info && info->ipi_ifindex != server->ifindex)
return 0;
CMSG_FOREACH(cmsg, &msg)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *info = CMSG_TYPED_DATA(cmsg, struct in_pktinfo);
if (info->ipi_ifindex != server->ifindex)
return 0;
} else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval *tv = CMSG_TYPED_DATA(cmsg, struct timeval);
triple_timestamp_from_realtime(&t, timeval_load(tv));
}
if (sd_dhcp_server_is_in_relay_mode(server)) {
r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen);
if (r < 0)
log_dhcp_server_errno(server, r, "Couldn't relay message, ignoring: %m");
} else {
r = dhcp_server_handle_message(server, message, (size_t) len);
r = dhcp_server_handle_message(server, message, (size_t) len, &t);
if (r < 0)
log_dhcp_server_errno(server, r, "Couldn't process incoming message, ignoring: %m");
}
+23 -23
View File
@@ -135,58 +135,58 @@ static void test_message_handler(void) {
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_start(server) >= 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.end = 0;
/* TODO, shouldn't this fail? */
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.end = SD_DHCP_OPTION_END;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.option_type.code = 0;
test.option_type.length = 0;
test.option_type.type = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == -ENOMSG);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == -ENOMSG);
test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE;
test.option_type.length = 1;
test.option_type.type = DHCP_DISCOVER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.message.op = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
test.message.op = BOOTREQUEST;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.message.htype = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.message.htype = ARPHRD_ETHER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.message.hlen = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == -EBADMSG);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == -EBADMSG);
test.message.hlen = ETHER_ADDR_LEN;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER);
test.option_type.type = DHCP_REQUEST;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS;
test.option_requested_ip.length = 4;
test.option_requested_ip.address = htobe32(0x12345678);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_NAK);
test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER;
test.option_server_id.length = 4;
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
test.option_server_id.address = htobe32(0x12345678);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER;
test.option_client_id.length = 7;
@@ -197,30 +197,30 @@ static void test_message_handler(void) {
test.option_client_id.id[4] = 'D';
test.option_client_id.id[5] = 'E';
test.option_client_id.id[6] = 'F';
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
/* request address reserved for static lease (unmatching client ID) */
test.option_client_id.id[6] = 'H';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
/* request unmatching address */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 41);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
/* request matching address */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
/* try again */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
}
static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {