X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-dhcp%2Fsd-dhcp-client.c;h=01397cff398be59c3a4e1a1fee05109bf28e7688;hp=7e5c36a857b897a09b24b35e47605b976558dff7;hb=0a1b6da82109c3b08b1f966a1625a77cc312135a;hpb=377a218f876507fb8be9c21ef4121fa2576ec317 diff --git a/src/libsystemd-dhcp/sd-dhcp-client.c b/src/libsystemd-dhcp/sd-dhcp-client.c index 7e5c36a85..01397cff3 100644 --- a/src/libsystemd-dhcp/sd-dhcp-client.c +++ b/src/libsystemd-dhcp/sd-dhcp-client.c @@ -23,42 +23,16 @@ #include #include #include +#include #include "util.h" #include "list.h" #include "dhcp-protocol.h" +#include "dhcp-lease.h" #include "dhcp-internal.h" #include "sd-dhcp-client.h" -#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 - -#define client_state_machine_check(s, r) \ - do { \ - if (s != DHCP_STATE_BOUND && \ - s != DHCP_STATE_RENEWING && \ - s != DHCP_STATE_REBINDING) { \ - return (r); \ - } \ - } while (false) - -struct DHCPLease { - uint32_t t1; - uint32_t t2; - uint32_t lifetime; - be32_t address; - be32_t server_address; - be32_t subnet_mask; - be32_t router; - struct in_addr *dns; - size_t dns_size; - uint16_t mtu; - char *domainname; - char *hostname; -}; - -typedef struct DHCPLease DHCPLease; - struct sd_dhcp_client { DHCPState state; sd_event *event; @@ -75,6 +49,7 @@ struct sd_dhcp_client { struct ether_addr mac_addr; uint32_t xid; usec_t start_time; + uint16_t secs; unsigned int attempt; usec_t request_sent; sd_event_source *timeout_t1; @@ -82,7 +57,7 @@ struct sd_dhcp_client { sd_event_source *timeout_expire; sd_dhcp_client_cb_t cb; void *userdata; - DHCPLease *lease; + sd_dhcp_lease *lease; }; static const uint8_t default_req_opts[] = { @@ -94,8 +69,10 @@ static const uint8_t default_req_opts[] = { DHCP_OPTION_NTP_SERVER, }; -static int client_receive_message(sd_event_source *s, int fd, - uint32_t revents, void *userdata); +static int client_receive_message_raw(sd_event_source *s, int fd, + uint32_t revents, void *userdata); +static int client_receive_message_udp(sd_event_source *s, int fd, + uint32_t revents, void *userdata); int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, void *userdata) { @@ -171,93 +148,16 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, return 0; } -int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr) { - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - addr->s_addr = client->lease->address; - - return 0; -} - -int sd_dhcp_client_get_mtu(sd_dhcp_client *client, uint16_t *mtu) { - assert_return(client, -EINVAL); - assert_return(mtu, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - if (client->lease->mtu) - *mtu = client->lease->mtu; - else - return -ENOENT; - - return 0; -} - -int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr **addr, size_t *addr_size) { - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(addr_size, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - if (client->lease->dns_size) { - *addr_size = client->lease->dns_size; - *addr = client->lease->dns; - } else - return -ENOENT; - - return 0; -} - -int sd_dhcp_client_get_domainname(sd_dhcp_client *client, const char **domainname) { - assert_return(client, -EINVAL); - assert_return(domainname, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - if (client->lease->domainname) - *domainname = client->lease->domainname; - else - return -ENOENT; - - return 0; -} - -int sd_dhcp_client_get_hostname(sd_dhcp_client *client, const char **hostname) { +int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); - assert_return(hostname, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - if (client->lease->hostname) - *hostname = client->lease->hostname; - else - return -ENOENT; - - return 0; -} - -int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr) { - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); - - client_state_machine_check (client->state, -EADDRNOTAVAIL); - - addr->s_addr = client->lease->router; - - return 0; -} - -int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr) { - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); + assert_return(ret, -EINVAL); - client_state_machine_check (client->state, -EADDRNOTAVAIL); + if (client->state != DHCP_STATE_BOUND && + client->state != DHCP_STATE_RENEWING && + client->state != DHCP_STATE_REBINDING) + return -EADDRNOTAVAIL; - addr->s_addr = client->lease->subnet_mask; + *ret = sd_dhcp_lease_ref(client->lease); return 0; } @@ -269,19 +169,6 @@ static int client_notify(sd_dhcp_client *client, int event) { return 0; } -static void lease_free(DHCPLease *lease) { - if (!lease) - return; - - free(lease->hostname); - free(lease->domainname); - free(lease->dns); - free(lease); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, lease_free); -#define _cleanup_lease_free_ _cleanup_(lease_freep) - static int client_stop(sd_dhcp_client *client, int error) { assert_return(client, -EINVAL); @@ -302,152 +189,79 @@ static int client_stop(sd_dhcp_client *client, int error) { client_notify(client, error); - switch (client->state) { - - case DHCP_STATE_INIT: - case DHCP_STATE_SELECTING: - case DHCP_STATE_REQUESTING: - case DHCP_STATE_BOUND: - - client->start_time = 0; - client->state = DHCP_STATE_INIT; - break; - - case DHCP_STATE_INIT_REBOOT: - case DHCP_STATE_REBOOTING: - case DHCP_STATE_RENEWING: - case DHCP_STATE_REBINDING: + client->start_time = 0; + client->secs = 0; + client->state = DHCP_STATE_INIT; - break; - } + if (client->lease) + client->lease = sd_dhcp_lease_unref(client->lease); - if (client->lease) { - lease_free(client->lease); - client->lease = NULL; - } + log_dhcp_client(client, "STOPPED"); return 0; } -static int client_packet_init(sd_dhcp_client *client, uint8_t type, - DHCPMessage *message, uint16_t secs, - uint8_t **opt, size_t *optlen) { - int err; - be16_t max_size; - - *opt = (uint8_t *)(message + 1); +static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, + uint8_t type, uint16_t secs, uint8_t **opt, + size_t *optlen) { + int r; - if (*optlen < 4) - return -ENOBUFS; - *optlen -= 4; + assert(secs); - message->op = BOOTREQUEST; - message->htype = 1; - message->hlen = ETHER_ADDR_LEN; - message->xid = htobe32(client->xid); + r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt, + optlen); + if (r < 0) + return r; /* 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); + if (client->state == DHCP_STATE_RENEWING || client->state == DHCP_STATE_REBINDING) message->ciaddr = client->lease->address; - memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN); - (*opt)[0] = 0x63; - (*opt)[1] = 0x82; - (*opt)[2] = 0x53; - (*opt)[3] = 0x63; - - *opt += 4; - - err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1, - &type); - if (err < 0) - return err; - - /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient + /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ - err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER, - ETH_ALEN, &client->mac_addr); - if (err < 0) - return err; + r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER, + ETH_ALEN, &client->mac_addr); + if (r < 0) + return r; if (type == DHCP_DISCOVER || type == DHCP_REQUEST) { - err = dhcp_option_append(opt, optlen, - DHCP_OPTION_PARAMETER_REQUEST_LIST, - client->req_opts_size, - client->req_opts); - if (err < 0) - return err; + be16_t max_size; + + r = dhcp_option_append(opt, optlen, + DHCP_OPTION_PARAMETER_REQUEST_LIST, + client->req_opts_size, + client->req_opts); + if (r < 0) + return r; /* Some DHCP servers will send bigger DHCP packets than the defined default size unless the Maximum Messge Size option is explicitely set */ max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE + - DHCP_CLIENT_MIN_OPTIONS_SIZE); - err = dhcp_option_append(opt, optlen, - DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, - 2, &max_size); - if (err < 0) - return err; + DHCP_MIN_OPTIONS_SIZE); + r = dhcp_option_append(opt, optlen, + DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, + 2, &max_size); + if (r < 0) + return r; } return 0; } -static uint16_t client_checksum(void *buf, int len) { - uint32_t sum; - uint16_t *check; - int i; - uint8_t *odd; - - sum = 0; - check = buf; - - for (i = 0; i < len / 2 ; i++) - sum += check[i]; - - if (len & 0x01) { - odd = buf; - sum += odd[len - 1]; - } - - while (sum >> 16) - sum = (sum & 0xffff) + (sum >> 16); - - return ~sum; -} - -static void client_append_ip_headers(DHCPPacket *packet, uint16_t len) { - packet->ip.version = IPVERSION; - packet->ip.ihl = DHCP_IP_SIZE / 4; - packet->ip.tot_len = htobe16(len); - - packet->ip.protocol = IPPROTO_UDP; - packet->ip.saddr = INADDR_ANY; - packet->ip.daddr = INADDR_BROADCAST; - - packet->udp.source = htobe16(DHCP_PORT_CLIENT); - packet->udp.dest = htobe16(DHCP_PORT_SERVER); - packet->udp.len = htobe16(len - DHCP_IP_SIZE); - - packet->ip.check = packet->udp.len; - packet->udp.check = client_checksum(&packet->ip.ttl, len - 8); - - packet->ip.ttl = IPDEFTTL; - packet->ip.check = 0; - packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE); -} - static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { int err = 0; _cleanup_free_ DHCPPacket *discover; size_t optlen, len; uint8_t *opt; - optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE; + optlen = DHCP_MIN_OPTIONS_SIZE; len = sizeof(DHCPPacket) + optlen; discover = malloc0(len); @@ -455,8 +269,8 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { if (!discover) return -ENOMEM; - err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp, - secs, &opt, &optlen); + err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER, + secs, &opt, &optlen); if (err < 0) return err; @@ -472,11 +286,13 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { if (err < 0) return err; - client_append_ip_headers(discover, len); + dhcp_packet_append_ip_headers(discover, len); err = dhcp_network_send_raw_socket(client->fd, &client->link, discover, len); + log_dhcp_client(client, "DISCOVER"); + return err; } @@ -486,15 +302,15 @@ static int client_send_request(sd_dhcp_client *client, uint16_t secs) { int err; uint8_t *opt; - optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE; + optlen = DHCP_MIN_OPTIONS_SIZE; len = DHCP_MESSAGE_SIZE + optlen; request = malloc0(len); if (!request) return -ENOMEM; - err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs, - &opt, &optlen); + err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs, + &opt, &optlen); if (err < 0) return err; @@ -519,24 +335,33 @@ static int client_send_request(sd_dhcp_client *client, uint16_t secs) { if (client->state == DHCP_STATE_RENEWING) { err = dhcp_network_send_udp_socket(client->fd, client->lease->server_address, + DHCP_PORT_SERVER, &request->dhcp, len - DHCP_IP_UDP_SIZE); } else { - client_append_ip_headers(request, len); + dhcp_packet_append_ip_headers(request, len); err = dhcp_network_send_raw_socket(client->fd, &client->link, request, len); } + log_dhcp_client(client, "REQUEST"); + return err; } +static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now) +{ + client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; + + return client->secs; +} + static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; usec_t next_timeout = 0; uint32_t time_left; - uint16_t secs; int r = 0; assert(s); @@ -546,7 +371,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, switch (client->state) { case DHCP_STATE_RENEWING: - time_left = (client->lease->t2 - client->lease->t1)/2; + time_left = (client->lease->t2 - client->lease->t1) / 2; if (time_left < 60) time_left = 60; @@ -556,7 +381,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, case DHCP_STATE_REBINDING: - time_left = (client->lease->lifetime - client->lease->t2)/2; + time_left = (client->lease->lifetime - client->lease->t2) / 2; if (time_left < 60) time_left = 60; @@ -580,22 +405,27 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, next_timeout += (random_u32() & 0x1fffff); - r = sd_event_add_monotonic(client->event, next_timeout, + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + r = sd_event_add_monotonic(client->event, + &client->timeout_resend, + next_timeout, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - &client->timeout_resend); + client_timeout_resend, client); if (r < 0) goto error; - r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); if (r < 0) goto error; - secs = (usec - client->start_time) / USEC_PER_SEC; - switch (client->state) { case DHCP_STATE_INIT: - r = client_send_discover(client, secs); + + client_update_secs(client, usec); + + r = client_send_discover(client, client->secs); if (r >= 0) { client->state = DHCP_STATE_SELECTING; client->attempt = 1; @@ -607,7 +437,9 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, break; case DHCP_STATE_SELECTING: - r = client_send_discover(client, secs); + client_update_secs(client, usec); + + r = client_send_discover(client, client->secs); if (r < 0 && client->attempt >= 64) goto error; @@ -616,7 +448,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, case DHCP_STATE_REQUESTING: case DHCP_STATE_RENEWING: case DHCP_STATE_REBINDING: - r = client_send_request(client, secs); + r = client_send_request(client, client->secs); if (r < 0 && client->attempt >= 64) goto error; @@ -641,29 +473,36 @@ error: return 0; } -static int client_initialize_events(sd_dhcp_client *client, usec_t usec) { +static int client_initialize_events(sd_dhcp_client *client, + sd_event_io_handler_t io_callback, + usec_t usec) { int r; assert(client); assert(client->event); - r = sd_event_add_io(client->event, client->fd, EPOLLIN, - client_receive_message, client, - &client->receive_message); + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, io_callback, + client); if (r < 0) goto error; - r = sd_event_source_set_priority(client->receive_message, client->event_priority); + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); if (r < 0) goto error; - r = sd_event_add_monotonic(client->event, usec, 0, - client_timeout_resend, client, - &client->timeout_resend); + 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, client); if (r < 0) goto error; - r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); error: if (r < 0) @@ -677,6 +516,8 @@ 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); return 0; @@ -704,10 +545,14 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->fd = r; - return client_initialize_events(client, usec); + log_dhcp_client(client, "TIMEOUT T2"); + + return client_initialize_events(client, client_receive_message_raw, + usec); } -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { +static int client_timeout_t1(sd_event_source *s, uint64_t usec, + void *userdata) { sd_dhcp_client *client = userdata; int r; @@ -715,7 +560,8 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) client->attempt = 1; r = dhcp_network_bind_udp_socket(client->index, - client->lease->address); + client->lease->address, + DHCP_PORT_CLIENT); if (r < 0) { client_stop(client, r); return 0; @@ -723,165 +569,25 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) client->fd = r; - return client_initialize_events(client, usec); -} - -static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option, - void *user_data) { - DHCPLease *lease = user_data; - be32_t val; - - switch(code) { - - case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: - if (len == 4) { - memcpy(&val, option, 4); - lease->lifetime = be32toh(val); - } - - break; - - case DHCP_OPTION_SERVER_IDENTIFIER: - if (len >= 4) - memcpy(&lease->server_address, option, 4); - - break; - - case DHCP_OPTION_SUBNET_MASK: - if (len >= 4) - memcpy(&lease->subnet_mask, option, 4); - - break; - - case DHCP_OPTION_ROUTER: - if (len >= 4) - memcpy(&lease->router, option, 4); - - break; - - case DHCP_OPTION_DOMAIN_NAME_SERVER: - if (len >= 4) { - unsigned i; - - lease->dns_size = len / 4; - - free(lease->dns); - lease->dns = new0(struct in_addr, lease->dns_size); - if (!lease->dns) - return -ENOMEM; - - for (i = 0; i < lease->dns_size; i++) { - memcpy(&lease->dns[i].s_addr, option + 4 * i, 4); - } - } - - break; - - case DHCP_OPTION_INTERFACE_MTU: - if (len >= 2) { - be16_t mtu; - - memcpy(&mtu, option, 2); - lease->mtu = be16toh(mtu); - - if (lease->mtu < 68) - lease->mtu = 0; - } - - break; - - case DHCP_OPTION_DOMAIN_NAME: - if (len >= 1) { - free(lease->domainname); - lease->domainname = strndup((const char *)option, len); - } - - break; - - case DHCP_OPTION_HOST_NAME: - if (len >= 1) { - free(lease->hostname); - lease->hostname = strndup((const char *)option, len); - } - - break; - - case DHCP_OPTION_RENEWAL_T1_TIME: - if (len == 4) { - memcpy(&val, option, 4); - lease->t1 = be32toh(val); - } - - break; - - case DHCP_OPTION_REBINDING_T2_TIME: - if (len == 4) { - memcpy(&val, option, 4); - lease->t2 = be32toh(val); - } - - break; - } - - return 0; -} - -static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message, - size_t len) { - size_t hdrlen; - - if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE)) - return -EINVAL; - - hdrlen = message->ip.ihl * 4; - if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip, - hdrlen)) - return -EINVAL; - - message->ip.check = message->udp.len; - message->ip.ttl = 0; - - if (hdrlen + be16toh(message->udp.len) > len || - client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12)) - return -EINVAL; - - if (be16toh(message->udp.source) != DHCP_PORT_SERVER || - be16toh(message->udp.dest) != DHCP_PORT_CLIENT) - return -EINVAL; - - if (message->dhcp.op != BOOTREPLY) - return -EINVAL; - - if (be32toh(message->dhcp.xid) != client->xid) - return -EINVAL; + log_dhcp_client(client, "TIMEOUT T1"); - if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet, - ETHER_ADDR_LEN)) - return -EINVAL; - - return 0; + return client_initialize_events(client, client_receive_message_udp, usec); } -static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer, - size_t len) { - _cleanup_lease_free_ DHCPLease *lease = NULL; +static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, + size_t len) { + _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; int r; - r = client_verify_headers(client, offer, len); + r = dhcp_lease_new(&lease); if (r < 0) return r; - lease = new0(DHCPLease, 1); - if (!lease) - return -ENOMEM; - - len = len - DHCP_IP_UDP_SIZE; - r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, - lease); + r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); if (r != DHCP_OFFER) return -ENOMSG; - lease->address = offer->dhcp.yiaddr; + lease->address = offer->yiaddr; if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || @@ -892,41 +598,30 @@ static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer, client->lease = lease; lease = NULL; + log_dhcp_client(client, "OFFER"); + return 0; } -static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf, - size_t len) { - DHCPPacket *ack; - DHCPMessage *dhcp; - _cleanup_lease_free_ DHCPLease *lease = NULL; +static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, + size_t len) { + _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; int r; - if (client->state == DHCP_STATE_RENEWING) { - dhcp = (DHCPMessage *)buf; - } else { - ack = (DHCPPacket *)buf; - - r = client_verify_headers(client, ack, len); - if (r < 0) - return r; - - dhcp = &ack->dhcp; - len -= DHCP_IP_UDP_SIZE; - } - - lease = new0(DHCPLease, 1); - if (!lease) - return -ENOMEM; + r = dhcp_lease_new(&lease); + if (r < 0) + return r; - r = dhcp_option_parse(dhcp, len, client_parse_offer, lease); - if (r == DHCP_NAK) + r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease); + if (r == DHCP_NAK) { + log_dhcp_client(client, "NAK"); return DHCP_EVENT_NO_LEASE; + } if (r != DHCP_ACK) return -ENOMSG; - lease->address = dhcp->yiaddr; + lease->address = ack->yiaddr; if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || @@ -941,12 +636,14 @@ static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf, r = DHCP_EVENT_IP_CHANGE; } - lease_free(client->lease); + client->lease = sd_dhcp_lease_unref(client->lease); } client->lease = lease; lease = NULL; + log_dhcp_client(client, "ACK"); + return r; } @@ -978,14 +675,16 @@ static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) { if (next_timeout < usec) return -EINVAL; - r = sd_event_add_monotonic(client->event, next_timeout, + r = sd_event_add_monotonic(client->event, + &client->timeout_t1, + next_timeout, 10 * USEC_PER_MSEC, - client_timeout_t1, client, - &client->timeout_t1); + client_timeout_t1, client); if (r < 0) return r; - r = sd_event_source_set_priority(client->timeout_t1, client->event_priority); + r = sd_event_source_set_priority(client->timeout_t1, + client->event_priority); if (r < 0) return r; @@ -1003,14 +702,16 @@ static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) { if (next_timeout < usec) return -EINVAL; - r = sd_event_add_monotonic(client->event, next_timeout, + r = sd_event_add_monotonic(client->event, + &client->timeout_t2, + next_timeout, 10 * USEC_PER_MSEC, - client_timeout_t2, client, - &client->timeout_t2); + client_timeout_t2, client); if (r < 0) return r; - r = sd_event_source_set_priority(client->timeout_t2, client->event_priority); + r = sd_event_source_set_priority(client->timeout_t2, + client->event_priority); if (r < 0) return r; @@ -1019,47 +720,59 @@ static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) { if (next_timeout < usec) return -EINVAL; - r = sd_event_add_monotonic(client->event, next_timeout, + r = sd_event_add_monotonic(client->event, + &client->timeout_expire, next_timeout, 10 * USEC_PER_MSEC, - client_timeout_expire, client, - &client->timeout_expire); + client_timeout_expire, client); if (r < 0) return r; - r = sd_event_source_set_priority(client->timeout_expire, client->event_priority); + r = sd_event_source_set_priority(client->timeout_expire, + client->event_priority); if (r < 0) return r; return 0; } -static int client_receive_message(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { - sd_dhcp_client *client = userdata; - uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE]; - int buflen = sizeof(buf); - int len, r = 0, notify_event = 0; - DHCPPacket *message; - usec_t time_now; +static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, + int len, usec_t time_now) { + int r = 0, notify_event = 0; - assert(s); assert(client); assert(client->event); + assert(message); - len = read(fd, &buf, buflen); - if (len < 0) + if (len < DHCP_MESSAGE_SIZE) { + log_dhcp_client(client, "message too small (%d bytes): " + "ignoring", len); return 0; + } - r = sd_event_get_now_monotonic(client->event, &time_now); - if (r < 0) - goto error; + if (message->op != BOOTREPLY) { + log_dhcp_client(client, "not a BOOTREPLY message: ignoring"); + return 0; + } + + if (be32toh(message->xid) != client->xid) { + log_dhcp_client(client, "received xid (%u) does not match " + "expected (%u): ignoring", + be32toh(message->xid), client->xid); + return 0; + } + + if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet, + ETHER_ADDR_LEN)) { + log_dhcp_client(client, "received chaddr does not match " + "expected: ignoring"); + return 0; + } switch (client->state) { case DHCP_STATE_SELECTING: - message = (DHCPPacket *)&buf; - - if (client_receive_offer(client, message, len) >= 0) { + r = client_handle_offer(client, message, len); + if (r >= 0) { client->timeout_resend = sd_event_source_unref(client->timeout_resend); @@ -1067,14 +780,16 @@ static int client_receive_message(sd_event_source *s, int fd, client->state = DHCP_STATE_REQUESTING; client->attempt = 1; - r = sd_event_add_monotonic(client->event, time_now, 0, + r = sd_event_add_monotonic(client->event, + &client->timeout_resend, + time_now, 0, client_timeout_resend, - client, - &client->timeout_resend); + client); if (r < 0) goto error; - r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); if (r < 0) goto error; } @@ -1085,7 +800,7 @@ static int client_receive_message(sd_event_source *s, int fd, case DHCP_STATE_RENEWING: case DHCP_STATE_REBINDING: - r = client_receive_ack(client, buf, len); + r = client_handle_ack(client, message, len); if (r == DHCP_EVENT_NO_LEASE) goto error; @@ -1136,6 +851,73 @@ error: return 0; } +static int client_receive_message_udp(sd_event_source *s, int fd, + uint32_t revents, void *userdata) { + sd_dhcp_client *client = userdata; + _cleanup_free_ DHCPMessage *message = NULL; + int buflen = 0, len, r; + usec_t time_now; + + assert(s); + assert(client); + assert(client->event); + + r = ioctl(fd, FIONREAD, &buflen); + if (r < 0 || buflen <= 0) + buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE; + + message = malloc0(buflen); + if (!message) + return -ENOMEM; + + len = read(fd, message, buflen); + if (len < 0) + return 0; + + r = sd_event_get_now_monotonic(client->event, &time_now); + if (r < 0) + return client_stop(client, r); + + return client_handle_message(client, message, len, + time_now); +} + +static int client_receive_message_raw(sd_event_source *s, int fd, + uint32_t revents, void *userdata) { + sd_dhcp_client *client = userdata; + _cleanup_free_ DHCPPacket *packet = NULL; + int buflen = 0, len, r; + usec_t time_now; + + assert(s); + assert(client); + assert(client->event); + + r = ioctl(fd, FIONREAD, &buflen); + if (r < 0 || buflen <= 0) + buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE; + + packet = malloc0(buflen); + if (!packet) + return -ENOMEM; + + len = read(fd, packet, buflen); + if (len < 0) + return 0; + + r = dhcp_packet_verify_headers(packet, len); + if (r < 0) + return 0; + + len -= DHCP_IP_UDP_SIZE; + + r = sd_event_get_now_monotonic(client->event, &time_now); + if (r < 0) + return client_stop(client, r); + + return client_handle_message(client, &packet->dhcp, len, time_now); +} + int sd_dhcp_client_start(sd_dhcp_client *client) { int r; @@ -1156,15 +938,20 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { client->fd = r; client->start_time = now(CLOCK_MONOTONIC); + client->secs = 0; + + log_dhcp_client(client, "STARTED"); - return client_initialize_events(client, client->start_time); + return client_initialize_events(client, client_receive_message_raw, + client->start_time); } int sd_dhcp_client_stop(sd_dhcp_client *client) { return client_stop(client, DHCP_EVENT_STOP); } -int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority) { +int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, + int priority) { int r; assert_return(client, -EINVAL);