#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
+#include "event-util.h"
#include "hostname-util.h"
+#include "io-util.h"
+#include "memory-util.h"
#include "random-util.h"
#include "string-util.h"
-#include "util.h"
#include "strv.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
uint32_t mtu;
uint32_t xid;
usec_t start_time;
- unsigned attempt;
+ uint64_t attempt;
+ uint64_t max_attempts;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
*/
/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
static const uint8_t default_req_opts_anonymize[] = {
- SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
- SD_DHCP_OPTION_ROUTER, /* 3 */
- SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
- SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
- SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
- SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
- SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
- SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
- SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
- SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
- SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
- SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
- SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
+ SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
+ SD_DHCP_OPTION_ROUTER, /* 3 */
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
+ SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
+ SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
+ SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
+ SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
+ SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
+ SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
+ SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
+ SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
};
static int client_receive_message_raw(
assert_return(data, -EINVAL);
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
- switch (type) {
-
- case ARPHRD_ETHER:
- if (data_len != ETH_ALEN)
- return -EINVAL;
- break;
-
- case ARPHRD_INFINIBAND:
- if (data_len != INFINIBAND_ALEN)
- return -EINVAL;
- break;
-
- default:
- break;
- }
-
if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
client->client_id.type == type &&
memcmp(&client->client_id.raw.data, data, data_len) == 0)
return 0;
+ /* For hardware types, log debug message about unexpected data length.
+ *
+ * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
+ * last last 8 bytes of the address are stable and suitable to put into
+ * the client-id. The caller is advised to account for that. */
+ if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
+ (type == ARPHRD_INFINIBAND && data_len != 8))
+ log_dhcp_client(client, "Changing client ID to hardware type %u with "
+ "unexpected address length %zu",
+ type, data_len);
+
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
log_dhcp_client(client, "Changing client ID on running DHCP "
"client, restarting");
*/
static int dhcp_client_set_iaid_duid_internal(
sd_dhcp_client *client,
+ bool iaid_append,
+ bool iaid_set,
uint32_t iaid,
- bool append_iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len,
size_t len;
assert_return(client, -EINVAL);
- assert_return(duid_len == 0 || duid != NULL, -EINVAL);
+ assert_return(duid_len == 0 || duid, -EINVAL);
- if (duid != NULL) {
- r = dhcp_validate_duid_len(duid_type, duid_len);
+ if (duid) {
+ r = dhcp_validate_duid_len(duid_type, duid_len, true);
if (r < 0)
return r;
}
zero(client->client_id);
client->client_id.type = 255;
- if (append_iaid) {
- /* If IAID is not configured, generate it. */
- if (iaid == 0) {
+ if (iaid_append) {
+ if (iaid_set)
+ client->client_id.ns.iaid = htobe32(iaid);
+ else {
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
client->mac_addr_len,
+ true,
&client->client_id.ns.iaid);
if (r < 0)
return r;
- } else
- client->client_id.ns.iaid = htobe32(iaid);
+ }
}
- if (duid != NULL) {
+ if (duid) {
client->client_id.ns.duid.type = htobe16(duid_type);
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
len = sizeof(client->client_id.ns.duid.type) + duid_len;
}
client->client_id_len = sizeof(client->client_id.type) + len +
- (append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
+ (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : "");
+ log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
sd_dhcp_client_start(client);
}
int sd_dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
+ bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len) {
- return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0);
+ return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
}
int sd_dhcp_client_set_iaid_duid_llt(
sd_dhcp_client *client,
+ bool iaid_set,
uint32_t iaid,
usec_t llt_time) {
- return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time);
+ return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp_client_set_duid(
uint16_t duid_type,
const void *duid,
size_t duid_len) {
- return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0);
+ return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
}
int sd_dhcp_client_set_duid_llt(
sd_dhcp_client *client,
usec_t llt_time) {
- return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time);
+ return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp_client_set_hostname(
return 0;
}
+int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
+ assert_return(client, -EINVAL);
+
+ client->max_attempts = max_attempts;
+
+ return 0;
+}
+
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);
- if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
+ if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
return -EADDRNOTAVAIL;
if (ret)
return 0;
}
-static void client_notify(sd_dhcp_client *client, int event) {
+static int client_notify(sd_dhcp_client *client, int event) {
assert(client);
if (client->callback)
- client->callback(client, event, client->userdata);
+ return client->callback(client, event, client->userdata);
+
+ return 0;
}
static int client_initialize(sd_dhcp_client *client) {
client->fd = asynchronous_close(client->fd);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+ (void) event_source_disable(client->timeout_resend);
+ (void) event_source_disable(client->timeout_t1);
+ (void) event_source_disable(client->timeout_t2);
+ (void) event_source_disable(client->timeout_expire);
- client->attempt = 1;
+ client->attempt = 0;
client->state = DHCP_STATE_INIT;
client->xid = 0;
assert(ret);
assert(_optlen);
assert(_optoffset);
- assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST));
+ assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE));
optlen = DHCP_MIN_OPTIONS_SIZE;
size = sizeof(DHCPPacket) + optlen;
client->client_id.type = 255;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
+ true, &client->client_id.ns.iaid);
if (r < 0)
return r;
MAY contain the Parameter Request List option. */
/* NOTE: in case that there would be an option to do not send
* any PRL at all, the size should be checked before sending */
- if (client->req_opts_size > 0) {
+ if (client->req_opts_size > 0 && type != DHCP_RELEASE) {
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
*/
/* RFC7844 section 3:
SHOULD NOT contain any other option. */
- if (!client->anonymize) {
+ if (!client->anonymize && type != DHCP_RELEASE) {
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
return 0;
}
+static int client_send_release(sd_dhcp_client *client) {
+ _cleanup_free_ DHCPPacket *release = NULL;
+ size_t optoffset, optlen;
+ int r;
+
+ assert(client);
+ assert(!IN_SET(client->state, DHCP_STATE_STOPPED));
+
+ r = client_message_init(client, &release, DHCP_RELEASE,
+ &optlen, &optoffset);
+ if (r < 0)
+ return r;
+
+ /* Fill up release IP and MAC */
+ release->dhcp.ciaddr = client->lease->address;
+ memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+
+ r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_END, 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = dhcp_network_send_udp_socket(client->fd,
+ client->lease->server_address,
+ DHCP_PORT_SERVER,
+ &release->dhcp,
+ sizeof(DHCPMessage) + optoffset);
+ if (r < 0)
+ return r;
+
+ log_dhcp_client(client, "RELEASE");
+
+ return 0;
+}
+
static int client_send_request(sd_dhcp_client *client) {
_cleanup_free_ DHCPPacket *request = NULL;
size_t optoffset, optlen;
case DHCP_STATE_REQUESTING:
case DHCP_STATE_BOUND:
- if (client->attempt < 64)
- client->attempt *= 2;
+ if (client->attempt < client->max_attempts)
+ client->attempt++;
+ else
+ goto error;
- next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
+ next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC;
break;
next_timeout += (random_u32() & 0x1fffff);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- next_timeout, 10 * USEC_PER_MSEC,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ next_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
goto error;
r = client_send_discover(client);
if (r >= 0) {
client->state = DHCP_STATE_SELECTING;
- client->attempt = 1;
- } else {
- if (client->attempt >= 64)
- goto error;
- }
+ client->attempt = 0;
+ } else if (client->attempt >= client->max_attempts)
+ goto error;
break;
case DHCP_STATE_SELECTING:
r = client_send_discover(client);
- if (r < 0 && client->attempt >= 64)
+ if (r < 0 && client->attempt >= client->max_attempts)
goto error;
break;
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_send_request(client);
- if (r < 0 && client->attempt >= 64)
+ if (r < 0 && client->attempt >= client->max_attempts)
goto error;
if (client->state == DHCP_STATE_INIT_REBOOT)
assert(client);
assert(client->event);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
if (client->start_delay) {
assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
usec += client->start_delay;
}
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- usec, 0,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
- if (r < 0)
- goto error;
-
-error:
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ usec, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
client_stop(client, r);
client->fd = asynchronous_close(client->fd);
client->state = DHCP_STATE_REBINDING;
- client->attempt = 1;
+ client->attempt = 0;
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
DHCP_CLIENT_DONT_DESTROY(client);
client->state = DHCP_STATE_RENEWING;
- client->attempt = 1;
+ client->attempt = 0;
return client_initialize_time_events(client);
}
sd_dhcp_lease_unref(client->lease);
client->lease = TAKE_PTR(lease);
+ if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
+ return -ENOMSG;
+
log_dhcp_client(client, "OFFER");
return 0;
return 0;
}
+static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
+ if (a->address != b->address)
+ return false;
+
+ if (a->subnet_mask != b->subnet_mask)
+ return false;
+
+ if (a->router_size != b->router_size)
+ return false;
+
+ for (size_t i = 0; i < a->router_size; i++)
+ if (a->router[i].s_addr != b->router[i].s_addr)
+ return false;
+
+ return true;
+}
+
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char *error_message = NULL;
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
- if (client->lease->address != lease->address ||
- client->lease->subnet_mask != lease->subnet_mask ||
- client->lease->router != lease->router) {
- r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
- } else
+ if (lease_equal(client->lease, lease))
r = SD_DHCP_CLIENT_EVENT_RENEW;
+ else
+ r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
client->lease = sd_dhcp_lease_unref(client->lease);
}
assert(client->lease);
assert(client->lease->lifetime);
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
/* don't set timers for infinite leases */
- if (client->lease->lifetime == 0xffffffff)
+ if (client->lease->lifetime == 0xffffffff) {
+ (void) event_source_disable(client->timeout_t1);
+ (void) event_source_disable(client->timeout_t2);
+ (void) event_source_disable(client->timeout_expire);
+
return 0;
+ }
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
}
/* arm lifetime timeout */
- r = sd_event_add_time(client->event, &client->timeout_expire,
- clock_boottime_or_monotonic(),
- lifetime_timeout, 10 * USEC_PER_MSEC,
- client_timeout_expire, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_expire,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
+ r = event_reset_time(client->event, &client->timeout_expire,
+ clock_boottime_or_monotonic(),
+ lifetime_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_expire, client,
+ client->event_priority, "dhcp4-lifetime", true);
if (r < 0)
return r;
return 0;
/* arm T2 timeout */
- r = sd_event_add_time(client->event,
- &client->timeout_t2,
- clock_boottime_or_monotonic(),
- t2_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t2, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t2,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
+ r = event_reset_time(client->event, &client->timeout_t2,
+ clock_boottime_or_monotonic(),
+ t2_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_t2, client,
+ client->event_priority, "dhcp4-t2-timeout", true);
if (r < 0)
return r;
return 0;
/* arm T1 timeout */
- r = sd_event_add_time(client->event,
- &client->timeout_t1,
- clock_boottime_or_monotonic(),
- t1_timeout, 10 * USEC_PER_MSEC,
- client_timeout_t1, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t1,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
+ r = event_reset_time(client->event, &client->timeout_t1,
+ clock_boottime_or_monotonic(),
+ t1_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_t1, client,
+ client->event_priority, "dhcp4-t1-timer", true);
if (r < 0)
return r;
r = client_handle_offer(client, message, len);
if (r >= 0) {
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
client->state = DHCP_STATE_REQUESTING;
- client->attempt = 1;
-
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- 0, 0,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
+ client->attempt = 0;
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
goto error;
} else if (r == -ENOMSG)
r = client_handle_ack(client, message, len);
if (r >= 0) {
client->start_delay = 0;
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
+ (void) event_source_disable(client->timeout_resend);
client->receive_message =
sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
notify_event = r;
client->state = DHCP_STATE_BOUND;
- client->attempt = 1;
+ client->attempt = 0;
client->last_addr = client->lease->address;
} else if (r == -EADDRNOTAVAIL) {
/* got a NAK, let's restart the client */
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
r = client_initialize(client);
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPMessage *message = NULL;
- const struct ether_addr zero_mac = {};
- const struct ether_addr *expected_chaddr = NULL;
+ const uint8_t *expected_chaddr = NULL;
uint8_t expected_hlen = 0;
ssize_t len, buflen;
assert(client);
buflen = next_datagram_size_fd(fd);
+ if (buflen == -ENETDOWN) {
+ /* the link is down. Don't return an error or the I/O event
+ source will be disconnected and we won't be able to receive
+ packets again when the link comes back. */
+ return 0;
+ }
if (buflen < 0)
return buflen;
len = recv(fd, message, buflen, 0);
if (len < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
+ /* see comment above for why we shouldn't error out on ENETDOWN. */
+ if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
return 0;
return log_dhcp_client_errno(client, errno,
if (client->arp_type == ARPHRD_ETHER) {
expected_hlen = ETH_ALEN;
- expected_chaddr = (const struct ether_addr *) &client->mac_addr;
- } else {
- /* Non-Ethernet links expect zero chaddr */
- expected_hlen = 0;
- expected_chaddr = &zero_mac;
+ expected_chaddr = &client->mac_addr[0];
}
if (message->hlen != expected_hlen) {
return 0;
}
- if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
+ 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;
}
assert(client);
buflen = next_datagram_size_fd(fd);
+ if (buflen == -ENETDOWN)
+ return 0;
if (buflen < 0)
return buflen;
if (!packet)
return -ENOMEM;
- iov.iov_base = packet;
- iov.iov_len = buflen;
+ iov = IOVEC_MAKE(packet, buflen);
len = recvmsg(fd, &msg, 0);
if (len < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
+ if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
return 0;
return log_dhcp_client_errno(client, errno,
} else if ((size_t)len < sizeof(DHCPPacket))
return 0;
- CMSG_FOREACH(cmsg, &msg) {
+ CMSG_FOREACH(cmsg, &msg)
if (cmsg->cmsg_level == SOL_PACKET &&
cmsg->cmsg_type == PACKET_AUXDATA &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
break;
}
- }
r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
if (r < 0)
return r;
}
+int sd_dhcp_client_send_release(sd_dhcp_client *client) {
+ assert_return(client, -EINVAL);
+
+ client_send_release(client);
+
+ return 0;
+}
+
int sd_dhcp_client_stop(sd_dhcp_client *client) {
DHCP_CLIENT_DONT_DESTROY(client);
log_dhcp_client(client, "FREE");
- client_initialize(client);
+ client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+ client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+ client->timeout_expire = sd_event_source_unref(client->timeout_expire);
- client->receive_message = sd_event_source_unref(client->receive_message);
+ client_initialize(client);
sd_dhcp_client_detach_event(client);
assert_return(ret, -EINVAL);
- client = new0(sd_dhcp_client, 1);
+ client = new(sd_dhcp_client, 1);
if (!client)
return -ENOMEM;
- client->n_ref = 1;
- client->state = DHCP_STATE_INIT;
- client->ifindex = -1;
- client->fd = -1;
- client->attempt = 1;
- client->mtu = DHCP_DEFAULT_MIN_SIZE;
- client->port = DHCP_PORT_CLIENT;
-
- client->anonymize = !!anonymize;
+ *client = (sd_dhcp_client) {
+ .n_ref = 1,
+ .state = DHCP_STATE_INIT,
+ .ifindex = -1,
+ .fd = -1,
+ .mtu = DHCP_DEFAULT_MIN_SIZE,
+ .port = DHCP_PORT_CLIENT,
+ .anonymize = !!anonymize,
+ .max_attempts = (uint64_t) -1,
+ };
/* NOTE: this could be moved to a function. */
if (anonymize) {
client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);