/* SPDX-License-Identifier: LGPL-2.1+ */
/***
- This file is part of systemd.
-
- Copyright (C) 2013 Intel Corporation. All rights reserved.
+ Copyright © 2013 Intel Corporation. All rights reserved.
***/
#include <errno.h>
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
+#include "event-util.h"
#include "hostname-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 */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
+ char **user_class;
uint32_t mtu;
uint32_t xid;
usec_t start_time;
- unsigned int attempt;
+ unsigned attempt;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-static int dhcp_client_set_iaid_duid(
+static int dhcp_client_set_iaid_duid_internal(
sd_dhcp_client *client,
uint32_t iaid,
bool append_iaid,
uint16_t duid_type,
const void *duid,
- size_t duid_len) {
+ size_t duid_len,
+ usec_t llt_time) {
DHCP_CLIENT_DONT_DESTROY(client);
int r;
if (iaid == 0) {
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;
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;
- } else if (duid_type == DUID_TYPE_EN) {
- r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
- if (r < 0)
- return r;
} else
- return -EOPNOTSUPP;
+ switch (duid_type) {
+ case DUID_TYPE_LLT:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_EN:
+ r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_LL:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_UUID:
+ r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ return -EINVAL;
+ }
client->client_id_len = sizeof(client->client_id.type) + len +
(append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Configured IAID+DUID, restarting.");
+ log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : "");
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
sd_dhcp_client_start(client);
}
uint16_t duid_type,
const void *duid,
size_t duid_len) {
- return dhcp_client_set_iaid_duid(client, iaid, true, duid_type, duid, duid_len);
+ return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0);
+}
+
+int sd_dhcp_client_set_iaid_duid_llt(
+ sd_dhcp_client *client,
+ uint32_t iaid,
+ usec_t llt_time) {
+ return dhcp_client_set_iaid_duid_internal(client, iaid, true, 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(client, 0, false, duid_type, duid, duid_len);
+ return dhcp_client_set_iaid_duid_internal(client, 0, false, 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);
}
int sd_dhcp_client_set_hostname(
return free_and_strdup(&client->vendor_class_identifier, vci);
}
+int sd_dhcp_client_set_user_class(
+ sd_dhcp_client *client,
+ const char* const* user_class) {
+
+ _cleanup_strv_free_ char **s = NULL;
+ char **p;
+
+ STRV_FOREACH(p, (char **) user_class)
+ if (strlen(*p) > 255)
+ return -ENAMETOOLONG;
+
+ s = strv_copy((char **) user_class);
+ if (!s)
+ return -ENOMEM;
+
+ client->user_class = TAKE_PTR(s);
+
+ return 0;
+}
+
int sd_dhcp_client_set_client_port(
sd_dhcp_client *client,
uint16_t port) {
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->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;
let the server know how large the server may make its DHCP messages.
Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
- than the defined default size unless the Maximum Messge Size option
+ than the defined default size unless the Maximum Message Size option
is explicitly set
RFC3442 "Requirements to Avoid Sizing Constraints":
return r;
}
+ if (client->user_class) {
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_USER_CLASS,
+ strv_length(client->user_class),
+ client->user_class);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
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;
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);
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;
-
- 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);
} 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);
if (r < 0)
return client->event;
}
-sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref++;
-
- return client;
-}
-
-sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref--;
-
- if (client->n_ref > 0)
- return NULL;
+static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
+ assert(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);
free(client->req_opts);
free(client->hostname);
free(client->vendor_class_identifier);
+ client->user_class = strv_free(client->user_class);
return mfree(client);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
+
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
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,
+ .attempt = 1,
+ .mtu = DHCP_DEFAULT_MIN_SIZE,
+ .port = DHCP_PORT_CLIENT,
+ .anonymize = !!anonymize,
+ };
/* NOTE: this could be moved to a function. */
if (anonymize) {
client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);