int fd;
union sockaddr_union link;
sd_event_source *receive_message;
+ bool request_broadcast;
uint8_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_size;
uint8_t type;
struct ether_addr mac_addr;
} _packed_ client_id;
+ char *hostname;
+ char *vendor_class_identifier;
uint32_t xid;
usec_t start_time;
uint16_t secs;
uint32_t revents, void *userdata);
static int client_receive_message_udp(sd_event_source *s, int fd,
uint32_t revents, void *userdata);
-static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error);
+static void client_stop(sd_dhcp_client *client, int error);
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
void *userdata) {
return 0;
}
+int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
+ assert_return(client, -EINVAL);
+
+ client->request_broadcast = !!broadcast;
+
+ return 0;
+}
+
int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
size_t i;
int sd_dhcp_client_set_mac(sd_dhcp_client *client,
const struct ether_addr *addr) {
+ DHCP_CLIENT_DONT_DESTROY(client);
bool need_restart = false;
assert_return(client, -EINVAL);
log_dhcp_client(client, "Changing MAC address on running DHCP "
"client, restarting");
need_restart = true;
- client = client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, DHCP_EVENT_STOP);
}
- if (!client)
- return 0;
-
memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
client->client_id.type = 0x01;
return 0;
}
+int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
+ const char *hostname) {
+ char *new_hostname = NULL;
+
+ assert_return(client, -EINVAL);
+
+ if (streq_ptr(client->hostname, hostname))
+ return 0;
+
+ if (hostname) {
+ new_hostname = strdup(hostname);
+ if (!new_hostname)
+ return -ENOMEM;
+ }
+
+ free(client->hostname);
+ client->hostname = new_hostname;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
+ const char *vci) {
+ char *new_vci = NULL;
+
+ assert_return(client, -EINVAL);
+
+ new_vci = strdup(vci);
+ if (!new_vci)
+ return -ENOMEM;
+
+ free(client->vendor_class_identifier);
+
+ client->vendor_class_identifier = new_vci;
+
+ return 0;
+}
+
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
return 0;
}
-static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) {
- if (client->cb) {
- client = sd_dhcp_client_ref(client);
+static void client_notify(sd_dhcp_client *client, int event) {
+ if (client->cb)
client->cb(client, event, client->userdata);
- client = sd_dhcp_client_unref(client);
- }
-
- return client;
}
static int client_initialize(sd_dhcp_client *client) {
return 0;
}
-static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
- assert_return(client, NULL);
+static void client_stop(sd_dhcp_client *client, int error) {
+ assert(client);
if (error < 0)
log_dhcp_client(client, "STOPPED: %s", strerror(-error));
}
}
- client = client_notify(client, error);
-
- if (client)
- client_initialize(client);
+ client_notify(client, error);
- return client;
+ client_initialize(client);
}
static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
DHCPREQUEST messages that client sends. The BROADCAST bit will
provide a hint to the DHCP server and BOOTP relay agent to broadcast
- any messages to the client on the client's subnet. */
- packet->dhcp.flags = htobe16(0x8000);
+ any messages to the client on the client's subnet.
+
+ Note: some interfaces needs this to be enabled, but some networks
+ needs this to be disabled as broadcasts are filteretd, so this
+ needs to be configurable */
+ if (client->request_broadcast)
+ packet->dhcp.flags = htobe16(0x8000);
/* RFC2132 section 4.1.1:
The client MUST include its hardware address in the ’chaddr’ field, if
/* See RFC2131 section 4.4.1 */
- r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
assert(time_now >= client->start_time);
return r;
}
+ /* it is unclear from RFC 2131 if client should send hostname in
+ DHCPDISCOVER but dhclient does and so we do as well
+ */
+ if (client->hostname) {
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ if (r < 0)
+ return r;
+ }
+
+ if (client->vendor_class_identifier) {
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+ strlen(client->vendor_class_identifier),
+ client->vendor_class_identifier);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_END, 0, NULL);
+ if (r < 0)
+ return r;
/* We currently ignore:
The client SHOULD wait a random time between one and ten seconds to
}
static int client_send_request(sd_dhcp_client *client) {
- _cleanup_free_ DHCPPacket *request;
+ _cleanup_free_ DHCPPacket *request = NULL;
size_t optoffset, optlen;
int r;
return -EINVAL;
}
+ if (client->hostname) {
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_END, 0, NULL);
if (r < 0)
static int client_timeout_resend(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
+ DHCP_CLIENT_DONT_DESTROY(client);
usec_t next_timeout = 0;
uint64_t time_now;
uint32_t time_left;
assert(client);
assert(client->event);
- r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
goto error;
r = sd_event_add_time(client->event,
&client->timeout_resend,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
next_timeout, 10 * USEC_PER_MSEC,
client_timeout_resend, client);
if (r < 0)
r = sd_event_add_time(client->event,
&client->timeout_resend,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
0, 0,
client_timeout_resend, client);
if (r < 0)
client->xid = random_u32();
- r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
+ r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
if (r < 0) {
client_stop(client, r);
return r;
client->fd = r;
if (client->state == DHCP_STATE_INIT) {
- client->start_time = now(CLOCK_MONOTONIC);
+ client->start_time = now(clock_boottime_or_monotonic());
client->secs = 0;
}
static int client_timeout_expire(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
+ DHCP_CLIENT_DONT_DESTROY(client);
log_dhcp_client(client, "EXPIRED");
- client = client_notify(client, DHCP_EVENT_EXPIRED);
+ client_notify(client, DHCP_EVENT_EXPIRED);
/* lease was lost, start over if not freed or stopped in callback */
- if (client && client->state != DHCP_STATE_STOPPED) {
+ if (client->state != DHCP_STATE_STOPPED) {
client_initialize(client);
client_start(client);
}
static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = userdata;
+ DHCP_CLIENT_DONT_DESTROY(client);
int r;
client->receive_message = sd_event_source_unref(client->receive_message);
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
- r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
+ r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
if (r < 0) {
client_stop(client, r);
return 0;
static int client_timeout_t1(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
+ DHCP_CLIENT_DONT_DESTROY(client);
int r;
client->state = DHCP_STATE_RENEWING;
r = dhcp_network_bind_udp_socket(client->lease->address,
DHCP_PORT_CLIENT);
if (r < 0) {
- client_stop(client, r);
+ log_dhcp_client(client, "could not bind UDP socket");
return 0;
}
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
r = DHCP_EVENT_IP_CHANGE;
- }
+ } else
+ r = DHCP_EVENT_RENEW;
client->lease = sd_dhcp_lease_unref(client->lease);
}
if (client->lease->lifetime == 0xffffffff)
return 0;
- r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
assert(client->request_sent <= time_now);
/* arm lifetime timeout */
r = sd_event_add_time(client->event, &client->timeout_expire,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
lifetime_timeout, 10 * USEC_PER_MSEC,
client_timeout_expire, client);
if (r < 0)
/* arm T2 timeout */
r = sd_event_add_time(client->event,
&client->timeout_t2,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
t2_timeout,
10 * USEC_PER_MSEC,
client_timeout_t2, client);
/* arm T1 timeout */
r = sd_event_add_time(client->event,
&client->timeout_t1,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
t1_timeout, 10 * USEC_PER_MSEC,
client_timeout_t1, client);
if (r < 0)
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
int len) {
+ DHCP_CLIENT_DONT_DESTROY(client);
int r = 0, notify_event = 0;
assert(client);
r = sd_event_add_time(client->event,
&client->timeout_resend,
- CLOCK_MONOTONIC,
+ clock_boottime_or_monotonic(),
0, 0,
client_timeout_resend, client);
if (r < 0)
goto error;
if (notify_event) {
- client = client_notify(client, notify_event);
- if (!client ||
- client->state == DHCP_STATE_STOPPED)
+ client_notify(client, notify_event);
+ if (client->state == DHCP_STATE_STOPPED)
return 0;
}
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
+ DHCP_CLIENT_DONT_DESTROY(client);
+
assert_return(client, -EINVAL);
- if (client_stop(client, DHCP_EVENT_STOP))
- client->state = DHCP_STATE_STOPPED;
+ client_stop(client, DHCP_EVENT_STOP);
+ client->state = DHCP_STATE_STOPPED;
return 0;
}
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
if (client && REFCNT_DEC(client->n_ref) <= 0) {
- log_dhcp_client(client, "UNREF");
+ log_dhcp_client(client, "FREE");
client_initialize(client);
sd_dhcp_lease_unref(client->lease);
free(client->req_opts);
+ free(client->hostname);
+ free(client->vendor_class_identifier);
free(client);
-
- return NULL;
}
- return client;
+ return NULL;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
-#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
-
int sd_dhcp_client_new(sd_dhcp_client **ret) {
- _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
+ _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);