return 0;
}
-static int client_stop(sd_dhcp_client *client, int error) {
+static int client_initialize(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
client->receive_message =
sd_event_source_unref(client->receive_message);
- if (client->fd >= 0)
- close_nointr_nofail(client->fd);
- client->fd = -1;
+ client->fd = safe_close(client->fd);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
client->attempt = 1;
- client_notify(client, error);
-
client->start_time = 0;
client->secs = 0;
client->state = DHCP_STATE_INIT;
+ client->xid = 0;
if (client->lease)
client->lease = sd_dhcp_lease_unref(client->lease);
+ return 0;
+}
+
+static int client_stop(sd_dhcp_client *client, int error) {
+ assert_return(client, -EINVAL);
+
+ client_notify(client, error);
+
+ client_initialize(client);
+
log_dhcp_client(client, "STOPPED");
return 0;
if (err < 0)
return err;
- if (client->state == DHCP_STATE_REQUESTING) {
+ switch (client->state) {
+
+ case DHCP_STATE_INIT_REBOOT:
+ err = dhcp_option_append(&opt, &optlen,
+ DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ 4, &client->last_addr);
+ if (err < 0)
+ return err;
+ break;
+
+ case DHCP_STATE_REQUESTING:
err = dhcp_option_append(&opt, &optlen,
DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->lease->address);
4, &client->lease->server_address);
if (err < 0)
return err;
+ break;
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ break;
}
err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
next_timeout = time_now + time_left * USEC_PER_SEC;
break;
+ case DHCP_STATE_REBOOTING:
+ /* start over as we did not receive a timely ack or nak */
+ client->state = DHCP_STATE_INIT;
+ client->attempt = 1;
+ client->xid = random_u32();
+
+ /* fall through */
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
case DHCP_STATE_SELECTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_BOUND:
break;
+ case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
if (r < 0 && client->attempt >= 64)
goto error;
+ if (client->state == DHCP_STATE_INIT_REBOOT)
+ client->state = DHCP_STATE_REBOOTING;
+
client->request_sent = time_now;
break;
- case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
}
+static int client_start(sd_dhcp_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->event, -EINVAL);
+ assert_return(client->index > 0, -EINVAL);
+ assert_return(client->fd < 0, -EBUSY);
+ assert_return(client->xid == 0, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT ||
+ client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+ client->xid = random_u32();
+
+ r = dhcp_network_bind_raw_socket(client->index, &client->link);
+
+ if (r < 0) {
+ client_stop(client, r);
+ return r;
+ }
+
+ client->fd = r;
+ client->start_time = now(CLOCK_MONOTONIC);
+ client->secs = 0;
+
+ log_dhcp_client(client, "STARTED");
+
+ return client_initialize_events(client, client_receive_message_raw);
+}
+
static int client_timeout_expire(sd_event_source *s, uint64_t usec,
void *userdata) {
sd_dhcp_client *client = userdata;
log_dhcp_client(client, "EXPIRED");
- client_stop(client, DHCP_EVENT_EXPIRED);
+ client_notify(client, DHCP_EVENT_EXPIRED);
+
+ /* start over as the lease was lost */
+ client_initialize(client);
+ client_start(client);
return 0;
}
sd_dhcp_client *client = userdata;
int r;
- if (client->fd >= 0) {
- client->receive_message =
- sd_event_source_unref(client->receive_message);
- close_nointr_nofail(client->fd);
- client->fd = -1;
- }
+ client->receive_message = sd_event_source_unref(client->receive_message);
+ client->fd = safe_close(client->fd);
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
assert(client);
assert(client->event);
+ /* don't set timers for infinite leases */
+ if (client->lease->lifetime == 0xffffffff)
+ return 0;
+
if (client->lease->lifetime < 10)
return -EINVAL;
break;
+ case DHCP_STATE_REBOOTING:
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
case DHCP_STATE_REBINDING:
r = client_handle_ack(client, message, len);
- if (r == DHCP_EVENT_NO_LEASE)
+ if (r == DHCP_EVENT_NO_LEASE) {
+
+ client->timeout_resend =
+ sd_event_source_unref(client->timeout_resend);
+
+ if (client->state == DHCP_STATE_REBOOTING) {
+ r = client_initialize(client);
+ if (r < 0)
+ goto error;
+
+ r = client_start(client);
+ if (r < 0)
+ goto error;
+ }
+
goto error;
+ }
if (r >= 0) {
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
- if (client->state == DHCP_STATE_REQUESTING)
+ if (IN_SET(client->state, DHCP_STATE_REQUESTING,
+ DHCP_STATE_REBOOTING))
notify_event = DHCP_EVENT_IP_ACQUIRE;
else if (r != DHCP_EVENT_IP_ACQUIRE)
notify_event = r;
client->receive_message =
sd_event_source_unref(client->receive_message);
- close_nointr_nofail(client->fd);
- client->fd = -1;
+ client->fd = safe_close(client->fd);
}
r = 0;
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
break;
int r;
assert_return(client, -EINVAL);
- assert_return(client->event, -EINVAL);
- assert_return(client->index > 0, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT ||
- client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
-
- client->xid = random_u32();
- r = dhcp_network_bind_raw_socket(client->index, &client->link);
-
- if (r < 0) {
- client_stop(client, r);
+ r = client_initialize(client);
+ if (r < 0)
return r;
- }
- client->fd = r;
- client->start_time = now(CLOCK_MONOTONIC);
- client->secs = 0;
+ if (client->last_addr)
+ client->state = DHCP_STATE_INIT_REBOOT;
- log_dhcp_client(client, "STARTED");
-
- return client_initialize_events(client, client_receive_message_raw);
+ return client_start(client);
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {