X-Git-Url: https://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=4f41b4c06ac425178e74b1df2fec1b19e0b352ac;hb=0a1b6da82109c3b08b1f966a1625a77cc312135a;hpb=cf597f650a178626258e67005dc917e504e9e5ea diff --git a/src/libsystemd-dhcp/sd-dhcp-client.c b/src/libsystemd-dhcp/sd-dhcp-client.c index 4f41b4c06..01397cff3 100644 --- a/src/libsystemd-dhcp/sd-dhcp-client.c +++ b/src/libsystemd-dhcp/sd-dhcp-client.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "util.h" #include "list.h" @@ -32,8 +33,6 @@ #include "dhcp-internal.h" #include "sd-dhcp-client.h" -#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 - struct sd_dhcp_client { DHCPState state; sd_event *event; @@ -70,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) { @@ -195,6 +196,8 @@ static int client_stop(sd_dhcp_client *client, int error) { if (client->lease) client->lease = sd_dhcp_lease_unref(client->lease); + log_dhcp_client(client, "STOPPED"); + return 0; } @@ -203,18 +206,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, size_t *optlen) { int r; - r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, - secs, opt, optlen); + assert(secs); + + 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; - /* 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 */ r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER, ETH_ALEN, &client->mac_addr); @@ -235,7 +244,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, 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); + DHCP_MIN_OPTIONS_SIZE); r = dhcp_option_append(opt, optlen, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, 2, &max_size); @@ -252,7 +261,7 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { 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); @@ -277,11 +286,13 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs) { if (err < 0) return err; - dhcp_packet_append_ip_headers(discover, BOOTREQUEST, 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; } @@ -291,7 +302,7 @@ 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); @@ -324,21 +335,24 @@ 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 { - dhcp_packet_append_ip_headers(request, BOOTREQUEST, 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; + client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; return client->secs; } @@ -357,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; @@ -367,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; @@ -391,14 +405,18 @@ 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; @@ -455,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) @@ -491,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; @@ -518,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; @@ -529,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; @@ -537,47 +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); -} + log_dhcp_client(client, "TIMEOUT T1"); -static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message, - size_t len) { - int r; - - r = dhcp_packet_verify_headers(message, BOOTREPLY, len); - if (r < 0) - return r; - - if (be32toh(message->dhcp.xid) != client->xid) - return -EINVAL; - - 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) { +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); - if (r < 0) - return r; - r = dhcp_lease_new(&lease); if (r < 0) return r; - len = len - DHCP_IP_UDP_SIZE; - r = dhcp_option_parse(&offer->dhcp, len, dhcp_lease_parse_options, - 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 || @@ -588,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; +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; - } - r = dhcp_lease_new(&lease); if (r < 0) return r; - r = dhcp_option_parse(dhcp, len, dhcp_lease_parse_options, 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 || @@ -643,6 +642,8 @@ static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf, client->lease = lease; lease = NULL; + log_dhcp_client(client, "ACK"); + return r; } @@ -674,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; @@ -699,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; @@ -715,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); @@ -763,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; } @@ -781,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; @@ -832,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; @@ -854,14 +940,18 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { client->start_time = now(CLOCK_MONOTONIC); client->secs = 0; - return client_initialize_events(client, client->start_time); + log_dhcp_client(client, "STARTED"); + + 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);