X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-dhcp%2Fdhcp-client.c;h=e9c4700081c0168b16bb28329193d2d705876767;hp=4d1722f5b36f56d1cc01c435630b5fee537cc26f;hb=d3d8ac2f2bac721d99f893c0a0128d21db636d4c;hpb=46a66b794a00e0f34981ac25dbf2eae2423b6215 diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c index 4d1722f5b..e9c470008 100644 --- a/src/libsystemd-dhcp/dhcp-client.c +++ b/src/libsystemd-dhcp/dhcp-client.c @@ -34,12 +34,15 @@ struct sd_dhcp_client { DHCPState state; + sd_event *event; + sd_event_source *timeout_resend; int index; uint8_t *req_opts; size_t req_opts_size; uint32_t last_addr; struct ether_addr mac_addr; uint32_t xid; + usec_t start_time; }; static const uint8_t default_req_opts[] = { @@ -119,9 +122,38 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, return 0; } +static int client_stop(sd_dhcp_client *client, int error) +{ + assert_return(client, -EINVAL); + assert_return(client->state != DHCP_STATE_INIT && + client->state != DHCP_STATE_INIT_REBOOT, -EALREADY); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + switch (client->state) { + + case DHCP_STATE_INIT: + case DHCP_STATE_SELECTING: + + client->state = DHCP_STATE_INIT; + break; + + case DHCP_STATE_INIT_REBOOT: + case DHCP_STATE_REBOOTING: + case DHCP_STATE_REQUESTING: + case DHCP_STATE_BOUND: + case DHCP_STATE_RENEWING: + case DHCP_STATE_REBINDING: + + break; + } + + return 0; +} + static int client_packet_init(sd_dhcp_client *client, uint8_t type, - DHCPMessage *message, uint8_t **opt, - size_t *optlen) + DHCPMessage *message, uint16_t secs, + uint8_t **opt, size_t *optlen) { int err; @@ -136,6 +168,10 @@ static int client_packet_init(sd_dhcp_client *client, uint8_t type, message->hlen = ETHER_ADDR_LEN; message->xid = htobe32(client->xid); + /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers + refuse to issue an DHCP lease if 'secs' is set to zero */ + message->secs = htobe16(secs); + memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN); (*opt)[0] = 0x63; (*opt)[1] = 0x82; @@ -189,7 +225,7 @@ static uint16_t client_checksum(void *buf, int len) return ~((sum & 0xffff) + (sum >> 16)); } -static int client_send_discover(sd_dhcp_client *client) +static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { int err = 0; _cleanup_free_ DHCPPacket *discover; @@ -205,7 +241,7 @@ static int client_send_discover(sd_dhcp_client *client) return -ENOMEM; err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp, - &opt, &optlen); + secs, &opt, &optlen); if (err < 0) return err; @@ -247,8 +283,61 @@ static int client_send_discover(sd_dhcp_client *client) return 0; } +static int client_timeout_resend(sd_event_source *s, uint64_t usec, + void *userdata) +{ + sd_dhcp_client *client = userdata; + usec_t next_timeout; + uint16_t secs; + int err = 0; + + switch (client->state) { + case DHCP_STATE_INIT: + case DHCP_STATE_SELECTING: + + if (!client->start_time) + client->start_time = usec; + + secs = (usec - client->start_time) / USEC_PER_SEC; + + next_timeout = usec + 2 * USEC_PER_SEC + (random() & 0x1fffff); + + err = sd_event_add_monotonic(client->event, next_timeout, + 10 * USEC_PER_MSEC, + client_timeout_resend, client, + &client->timeout_resend); + if (err < 0) + goto error; + + if (client_send_discover(client, secs) >= 0) + client->state = DHCP_STATE_SELECTING; + + break; + + case DHCP_STATE_INIT_REBOOT: + case DHCP_STATE_REBOOTING: + case DHCP_STATE_REQUESTING: + case DHCP_STATE_BOUND: + case DHCP_STATE_RENEWING: + case DHCP_STATE_REBINDING: + + break; + } + + return 0; + +error: + client_stop(client, err); + + /* Errors were dealt with when stopping the client, don't spill + errors into the event loop handler */ + return 0; +} + int sd_dhcp_client_start(sd_dhcp_client *client) { + int err; + assert_return(client, -EINVAL); assert_return(client->index >= 0, -EINVAL); assert_return(client->state == DHCP_STATE_INIT || @@ -256,17 +345,36 @@ int sd_dhcp_client_start(sd_dhcp_client *client) client->xid = random_u(); - return client_send_discover(client); + err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0, + client_timeout_resend, client, + &client->timeout_resend); + if (err < 0) + goto error; + + return 0; + +error: + client_stop(client, err); + + return err; +} + +int sd_dhcp_client_stop(sd_dhcp_client *client) +{ + return client_stop(client, 0); } -sd_dhcp_client *sd_dhcp_client_new(void) +sd_dhcp_client *sd_dhcp_client_new(sd_event *event) { sd_dhcp_client *client; + assert_return(event, NULL); + client = new0(sd_dhcp_client, 1); if (!client) return NULL; + client->event = sd_event_ref(event); client->state = DHCP_STATE_INIT; client->index = -1;