Merge pull request #29921 from yuwata/sd-dhcp-client-timestamp
dhcp: further cleanups for timestamp
This commit is contained in:
@@ -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) \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
Reference in New Issue
Block a user