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) \
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)
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)
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);
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);
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;
}
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;
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:
if (client->state == DHCP_STATE_INIT_REBOOT)
client_set_state(client, DHCP_STATE_REBOOTING);
-
- client->request_sent = time_now;
break;
case DHCP_STATE_REBOOTING:
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;
if (r < 0)
return r;
+ dhcp_lease_set_timestamp(lease, timestamp);
+
dhcp_lease_unref_and_replace(client->lease, lease);
if (client->lease->rapid_commit) {
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;
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))
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) {
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)
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".
return 0;
}
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
+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;
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))
return r;
if (r == -EADDRNOTAVAIL)
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))
return r;
if (r == -EADDRNOTAVAIL)
sd_dhcp_client *client = ASSERT_PTR(userdata);
_cleanup_free_ DHCPMessage *message = NULL;
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);
if (!message)
return -ENOMEM;
- len = recv(fd, message, buflen, 0);
- if (len < 0) {
- if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
- return 0;
+ iov = IOVEC_MAKE(message, buflen);
- log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
+ len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
+ if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
+ return 0;
+ if (len < 0) {
+ log_dhcp_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
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.");
- r = client_handle_message(client, message, len);
+ r = client_handle_message(client, message, len, &t);
if (r < 0)
client_stop(client, r);
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,
};
struct cmsghdr *cmsg;
bool checksum = true;
+ triple_timestamp t = {};
ssize_t buflen, len;
int r;
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);
+
+ } 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.");
- r = client_handle_message(client, &packet->dhcp, len);
+ r = client_handle_message(client, &packet->dhcp, len, &t);
if (r < 0)
client_stop(client, r);
#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);
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);
}
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);
#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;
if (r < 0)
return r;
+ request_set_timestamp(req, timestamp);
+
r = dhcp_server_cleanup_expired_leases(server);
if (r < 0)
return r;
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 = {
.msg_controllen = sizeof(control),
};
ssize_t datagram_size, len;
+ struct cmsghdr *cmsg;
+ triple_timestamp t = {};
int r;
datagram_size = next_datagram_size_fd(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");
}
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;
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]) {