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(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;
return 0;
}
+static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
+ size_t len) {
+ dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
+ INADDR_BROADCAST, DHCP_PORT_SERVER, len);
+
+ return dhcp_network_send_raw_socket(client->fd, &client->link,
+ packet, len);
+}
+
static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
int err = 0;
_cleanup_free_ DHCPPacket *discover;
if (err < 0)
return err;
- dhcp_packet_append_ip_headers(discover, len);
-
- err = dhcp_network_send_raw_socket(client->fd, &client->link,
- discover, len);
+ err = dhcp_client_send_raw(client, discover, len);
+ if (err < 0)
+ return err;
log_dhcp_client(client, "DISCOVER");
- return err;
+ return 0;
}
static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
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);
&request->dhcp,
len - DHCP_IP_UDP_SIZE);
} else {
- dhcp_packet_append_ip_headers(request, len);
-
- err = dhcp_network_send_raw_socket(client->fd, &client->link,
- request, len);
+ err = dhcp_client_send_raw(client, request, len);
}
+ if (err < 0)
+ return err;
log_dhcp_client(client, "REQUEST");
- return err;
+ return 0;
}
static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
void *userdata) {
sd_dhcp_client *client = userdata;
usec_t next_timeout = 0;
+ uint64_t time_now;
uint32_t time_left;
- int r = 0;
+ int r;
assert(s);
assert(client);
assert(client->event);
+ r = sd_event_get_now_monotonic(client->event, &time_now);
+ if (r < 0)
+ goto error;
+
switch (client->state) {
case DHCP_STATE_RENEWING:
if (time_left < 60)
time_left = 60;
- next_timeout = usec + time_left * USEC_PER_SEC;
+ next_timeout = time_now + time_left * USEC_PER_SEC;
break;
if (time_left < 60)
time_left = 60;
- next_timeout = usec + time_left * USEC_PER_SEC;
+ 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:
if (client->attempt < 64)
client->attempt *= 2;
- next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
+ next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
break;
}
switch (client->state) {
case DHCP_STATE_INIT:
- client_update_secs(client, usec);
+ client_update_secs(client, time_now);
r = client_send_discover(client, client->secs);
if (r >= 0) {
break;
case DHCP_STATE_SELECTING:
- client_update_secs(client, usec);
+ client_update_secs(client, time_now);
r = client_send_discover(client, client->secs);
if (r < 0 && client->attempt >= 64)
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;
- client->request_sent = usec;
+ 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_initialize_events(sd_dhcp_client *client,
- sd_event_io_handler_t io_callback,
- usec_t usec) {
+ sd_event_io_handler_t io_callback) {
int r;
assert(client);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
r = sd_event_add_monotonic(client->event,
- &client->timeout_resend,
- usec, 0,
+ &client->timeout_resend, 0, 0,
client_timeout_resend, client);
if (r < 0)
goto error;
}
+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(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;
log_dhcp_client(client, "TIMEOUT T2");
- return client_initialize_events(client, client_receive_message_raw,
- usec);
+ return client_initialize_events(client, client_receive_message_raw);
}
static int client_timeout_t1(sd_event_source *s, uint64_t usec,
log_dhcp_client(client, "TIMEOUT T1");
- return client_initialize_events(client, client_receive_message_udp, usec);
+ return client_initialize_events(client, client_receive_message_udp);
}
static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
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;
return -EINVAL;
r = sd_event_add_monotonic(client->event,
- &client->timeout_t1,
- next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t1, client);
+ &client->timeout_t1,
+ next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t1, client);
if (r < 0)
return r;
return -EINVAL;
r = sd_event_add_monotonic(client->event,
- &client->timeout_t2,
- next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t2, client);
+ &client->timeout_t2,
+ next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t2, client);
if (r < 0)
return r;
return -EINVAL;
r = sd_event_add_monotonic(client->event,
- &client->timeout_expire, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_expire, client);
+ &client->timeout_expire, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_expire, client);
if (r < 0)
return r;
client->attempt = 1;
r = sd_event_add_monotonic(client->event,
- &client->timeout_resend,
- time_now, 0,
- client_timeout_resend,
+ &client->timeout_resend, 0,
+ 0, client_timeout_resend,
client);
if (r < 0)
goto error;
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(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;
-
- log_dhcp_client(client, "STARTED");
+ if (client->last_addr)
+ client->state = DHCP_STATE_INIT_REBOOT;
- return client_initialize_events(client, client_receive_message_raw,
- client->start_time);
+ return client_start(client);
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {