X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-client.c;h=1f7f238ca0002b2d58e14adccd4654ece87ea160;hp=2a46624f2e8c328e41c520e3377c96dd15f1c92d;hb=affaa94fc38a980a70534f70f6a6c58a4129b062;hpb=8186d9dda0d3f8a853afe6fdbd605f42151fe165 diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 2a46624f2..1f7f238ca 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include "dhcp-lease-internal.h" #include "sd-dhcp-client.h" +#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN + struct sd_dhcp_client { RefCount n_ref; @@ -48,6 +51,7 @@ struct sd_dhcp_client { int fd; union sockaddr_union link; sd_event_source *receive_message; + bool request_broadcast; uint8_t *req_opts; size_t req_opts_allocated; size_t req_opts_size; @@ -56,6 +60,12 @@ struct sd_dhcp_client { uint8_t type; struct ether_addr mac_addr; } _packed_ client_id; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; + char *hostname; + char *vendor_class_identifier; + uint32_t mtu; uint32_t xid; usec_t start_time; uint16_t secs; @@ -82,7 +92,7 @@ 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); -static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error); +static void client_stop(sd_dhcp_client *client, int error); int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, void *userdata) { @@ -94,6 +104,14 @@ int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, return 0; } +int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) { + assert_return(client, -EINVAL); + + client->request_broadcast = !!broadcast; + + return 0; +} + int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { size_t i; @@ -151,25 +169,37 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) { return 0; } -int sd_dhcp_client_set_mac(sd_dhcp_client *client, - const struct ether_addr *addr) { +int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { + DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; assert_return(client, -EINVAL); assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); - if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0) + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) return 0; if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Changing MAC address on running DHCP " "client, restarting"); need_restart = true; - client = client_stop(client, DHCP_EVENT_STOP); + client_stop(client, DHCP_EVENT_STOP); } - if (!client) - return 0; + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; memcpy(&client->client_id.mac_addr, addr, ETH_ALEN); client->client_id.type = 0x01; @@ -180,6 +210,53 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, return 0; } +int sd_dhcp_client_set_hostname(sd_dhcp_client *client, + const char *hostname) { + char *new_hostname = NULL; + + assert_return(client, -EINVAL); + + if (streq_ptr(client->hostname, hostname)) + return 0; + + if (hostname) { + new_hostname = strdup(hostname); + if (!new_hostname) + return -ENOMEM; + } + + free(client->hostname); + client->hostname = new_hostname; + + return 0; +} + +int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, + const char *vci) { + char *new_vci = NULL; + + assert_return(client, -EINVAL); + + new_vci = strdup(vci); + if (!new_vci) + return -ENOMEM; + + free(client->vendor_class_identifier); + + client->vendor_class_identifier = new_vci; + + return 0; +} + +int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { + assert_return(client, -EINVAL); + assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE); + + client->mtu = mtu; + + return 0; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); assert_return(ret, -EINVAL); @@ -194,14 +271,9 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { return 0; } -static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) { - if (client->cb) { - client = sd_dhcp_client_ref(client); +static void client_notify(sd_dhcp_client *client, int event) { + if (client->cb) client->cb(client, event, client->userdata); - client = sd_dhcp_client_unref(client); - } - - return client; } static int client_initialize(sd_dhcp_client *client) { @@ -229,31 +301,19 @@ static int client_initialize(sd_dhcp_client *client) { return 0; } -static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) { - assert_return(client, NULL); +static void client_stop(sd_dhcp_client *client, int error) { + assert(client); if (error < 0) log_dhcp_client(client, "STOPPED: %s", strerror(-error)); - else { - switch(error) { - case DHCP_EVENT_STOP: - log_dhcp_client(client, "STOPPED"); - break; - case DHCP_EVENT_NO_LEASE: - log_dhcp_client(client, "STOPPED: No lease"); - break; - default: - log_dhcp_client(client, "STOPPED: Unknown reason"); - break; - } - } - - client = client_notify(client, error); + else if (error == DHCP_EVENT_STOP) + log_dhcp_client(client, "STOPPED"); + else + log_dhcp_client(client, "STOPPED: Unknown event"); - if (client) - client_initialize(client); + client_notify(client, error); - return client; + client_initialize(client); } static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, @@ -278,7 +338,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, - optlen, &optoffset); + client->arp_type, optlen, &optoffset); if (r < 0) return r; @@ -292,14 +352,22 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or DHCPREQUEST messages that client sends. The BROADCAST bit will provide a hint to the DHCP server and BOOTP relay agent to broadcast - any messages to the client on the client's subnet. */ - packet->dhcp.flags = htobe16(0x8000); + any messages to the client on the client's subnet. + + Note: some interfaces needs this to be enabled, but some networks + needs this to be disabled as broadcasts are filteretd, so this + needs to be configurable */ + if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) + packet->dhcp.flags = htobe16(0x8000); /* RFC2132 section 4.1.1: The client MUST include its hardware address in the ’chaddr’ field, if - necessary for delivery of DHCP reply messages. + necessary for delivery of DHCP reply messages. Non-Ethernet + interfaces will leave 'chaddr' empty and use the client identifier + instead (eg, RFC 4390 section 2.1). */ - memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN); + if (client->arp_type == ARPHRD_ETHER) + memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ @@ -331,9 +399,23 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, Note (from ConnMan): Some DHCP servers will send bigger DHCP packets than the defined default size unless the Maximum Messge Size option is explicitely set + + RFC3442 "Requirements to Avoid Sizing Constraints": + Because a full routing table can be quite large, the standard 576 + octet maximum size for a DHCP message may be too short to contain + some legitimate Classless Static Route options. Because of this, + clients implementing the Classless Static Route option SHOULD send a + Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP + stack is capable of receiving larger IP datagrams. In this case, the + client SHOULD set the value of this option to at least the MTU of the + interface that the client is configuring. The client MAY set the + value of this option higher, up to the size of the largest UDP packet + it is prepared to accept. (Note that the value specified in the + Maximum DHCP Message Size option is the total maximum packet size, + including IP and UDP headers.) */ max_size = htobe16(size); - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, 2, &max_size); if (r < 0) @@ -368,7 +450,7 @@ static int client_send_discover(sd_dhcp_client *client) { /* See RFC2131 section 4.4.1 */ - r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now); + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) return r; assert(time_now >= client->start_time); @@ -396,8 +478,30 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + /* it is unclear from RFC 2131 if client should send hostname in + DHCPDISCOVER but dhclient does and so we do as well + */ + if (client->hostname) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + if (r < 0) + return r; + } + + if (client->vendor_class_identifier) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, + strlen(client->vendor_class_identifier), + client->vendor_class_identifier); + if (r < 0) + return r; + } + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; /* We currently ignore: The client SHOULD wait a random time between one and ten seconds to @@ -485,6 +589,14 @@ static int client_send_request(sd_dhcp_client *client) { return -EINVAL; } + if (client->hostname) { + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + if (r < 0) + return r; + } + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -528,6 +640,7 @@ static int client_start(sd_dhcp_client *client); static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); usec_t next_timeout = 0; uint64_t time_now; uint32_t time_left; @@ -537,7 +650,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, assert(client); assert(client->event); - r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now); + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) goto error; @@ -599,7 +712,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, r = sd_event_add_time(client->event, &client->timeout_resend, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), next_timeout, 10 * USEC_PER_MSEC, client_timeout_resend, client); if (r < 0) @@ -610,6 +723,11 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, if (r < 0) goto error; + r = sd_event_source_set_name(client->timeout_resend, + "dhcp4-resend-timer"); + if (r < 0) + goto error; + switch (client->state) { case DHCP_STATE_INIT: r = client_send_discover(client); @@ -665,8 +783,8 @@ error: return 0; } -static int client_initialize_events(sd_dhcp_client *client, - sd_event_io_handler_t io_callback) { +static int client_initialize_io_events(sd_dhcp_client *client, + sd_event_io_handler_t io_callback) { int r; assert(client); @@ -683,11 +801,29 @@ static int client_initialize_events(sd_dhcp_client *client, if (r < 0) goto error; + r = sd_event_source_set_name(client->receive_message, + "dhcp4-receive-message"); + if (r < 0) + goto error; + +error: + if (r < 0) + client_stop(client, r); + + return 0; +} + +static int client_initialize_time_events(sd_dhcp_client *client) { + int r; + + assert(client); + assert(client->event); + client->timeout_resend = sd_event_source_unref(client->timeout_resend); r = sd_event_add_time(client->event, &client->timeout_resend, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, client); if (r < 0) @@ -696,6 +832,11 @@ static int client_initialize_events(sd_dhcp_client *client, r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); + r = sd_event_source_set_name(client->timeout_resend, + "dhcp4-resend-timer"); + if (r < 0) + goto error; + error: if (r < 0) client_stop(client, r); @@ -704,6 +845,14 @@ error: } +static int client_initialize_events(sd_dhcp_client *client, + sd_event_io_handler_t io_callback) { + client_initialize_io_events(client, io_callback); + client_initialize_time_events(client); + + return 0; +} + static int client_start(sd_dhcp_client *client) { int r; @@ -717,7 +866,9 @@ static int client_start(sd_dhcp_client *client) { client->xid = random_u32(); - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return r; @@ -725,7 +876,7 @@ static int client_start(sd_dhcp_client *client) { client->fd = r; if (client->state == DHCP_STATE_INIT) { - client->start_time = now(CLOCK_MONOTONIC); + client->start_time = now(clock_boottime_or_monotonic()); client->secs = 0; } @@ -735,13 +886,14 @@ static int client_start(sd_dhcp_client *client) { static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); log_dhcp_client(client, "EXPIRED"); - client = client_notify(client, DHCP_EVENT_EXPIRED); + client_notify(client, DHCP_EVENT_EXPIRED); /* lease was lost, start over if not freed or stopped in callback */ - if (client && client->state != DHCP_STATE_STOPPED) { + if (client->state != DHCP_STATE_STOPPED) { client_initialize(client); client_start(client); } @@ -751,6 +903,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec, static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); int r; client->receive_message = sd_event_source_unref(client->receive_message); @@ -759,7 +912,9 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->state = DHCP_STATE_REBINDING; client->attempt = 1; - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return 0; @@ -772,21 +927,12 @@ static int client_timeout_t2(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; + DHCP_CLIENT_DONT_DESTROY(client); client->state = DHCP_STATE_RENEWING; client->attempt = 1; - r = dhcp_network_bind_udp_socket(client->lease->address, - DHCP_PORT_CLIENT); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client->fd = r; - - return client_initialize_events(client, client_receive_message_udp); + return client_initialize_time_events(client); } static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, @@ -800,7 +946,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); if (r != DHCP_OFFER) { - log_dhcp_client(client, "receieved message was not an OFFER, ignoring"); + log_dhcp_client(client, "received message was not an OFFER, ignoring"); return -ENOMSG; } @@ -811,7 +957,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || lease->lifetime == 0) { - log_dhcp_client(client, "receieved lease lacks address, server " + log_dhcp_client(client, "received lease lacks address, server " "address or lease lifetime, ignoring"); return -ENOMSG; } @@ -819,7 +965,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (lease->subnet_mask == INADDR_ANY) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "receieved lease lacks subnet " + log_dhcp_client(client, "received lease lacks subnet " "mask, and a fallback one can not be " "generated, ignoring"); return -ENOMSG; @@ -835,6 +981,19 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, return 0; } +static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, + size_t len) { + int r; + + r = dhcp_option_parse(force, len, NULL, NULL); + if (r != DHCP_FORCERENEW) + return -ENOMSG; + + log_dhcp_client(client, "FORCERENEW"); + + return 0; +} + static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; @@ -847,11 +1006,11 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, 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; + return -EADDRNOTAVAIL; } if (r != DHCP_ACK) { - log_dhcp_client(client, "receieved message was not an ACK, ignoring"); + log_dhcp_client(client, "received message was not an ACK, ignoring"); return -ENOMSG; } @@ -862,7 +1021,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (lease->address == INADDR_ANY || lease->server_address == INADDR_ANY || lease->lifetime == 0) { - log_dhcp_client(client, "receieved lease lacks address, server " + log_dhcp_client(client, "received lease lacks address, server " "address or lease lifetime, ignoring"); return -ENOMSG; } @@ -870,7 +1029,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (lease->subnet_mask == INADDR_ANY) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "receieved lease lacks subnet " + log_dhcp_client(client, "received lease lacks subnet " "mask, and a fallback one can not be " "generated, ignoring"); return -ENOMSG; @@ -883,7 +1042,8 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, client->lease->subnet_mask != lease->subnet_mask || client->lease->router != lease->router) { r = DHCP_EVENT_IP_CHANGE; - } + } else + r = DHCP_EVENT_RENEW; client->lease = sd_dhcp_lease_unref(client->lease); } @@ -927,7 +1087,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (client->lease->lifetime == 0xffffffff) return 0; - r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now); + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) return r; assert(client->request_sent <= time_now); @@ -978,7 +1138,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm lifetime timeout */ r = sd_event_add_time(client->event, &client->timeout_expire, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), lifetime_timeout, 10 * USEC_PER_MSEC, client_timeout_expire, client); if (r < 0) @@ -989,6 +1149,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_name(client->timeout_expire, + "dhcp4-lifetime"); + if (r < 0) + return r; + log_dhcp_client(client, "lease expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, 0)); @@ -1000,7 +1165,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T2 timeout */ r = sd_event_add_time(client->event, &client->timeout_t2, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), t2_timeout, 10 * USEC_PER_MSEC, client_timeout_t2, client); @@ -1012,6 +1177,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_name(client->timeout_t2, + "dhcp4-t2-timeout"); + if (r < 0) + return r; + log_dhcp_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, 0)); @@ -1023,7 +1193,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T1 timeout */ r = sd_event_add_time(client->event, &client->timeout_t1, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), t1_timeout, 10 * USEC_PER_MSEC, client_timeout_t1, client); if (r < 0) @@ -1034,6 +1204,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; + r = sd_event_source_set_name(client->timeout_t1, + "dhcp4-t1-timer"); + if (r < 0) + return r; + log_dhcp_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, 0)); @@ -1043,41 +1218,13 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) { + DHCP_CLIENT_DONT_DESTROY(client); int r = 0, notify_event = 0; assert(client); assert(client->event); assert(message); - if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) { - log_dhcp_client(client, "not a DHCP message: ignoring"); - return 0; - } - - 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 (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) { - log_dhcp_client(client, "not an ethernet packet"); - return 0; - } - - if (memcmp(&message->chaddr[0], &client->client_id.mac_addr, - ETH_ALEN)) { - log_dhcp_client(client, "received chaddr does not match " - "expected: ignoring"); - return 0; - } - switch (client->state) { case DHCP_STATE_SELECTING: @@ -1092,7 +1239,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, r = sd_event_add_time(client->event, &client->timeout_resend, - CLOCK_MONOTONIC, + clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, client); if (r < 0) @@ -1102,6 +1249,11 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, client->event_priority); if (r < 0) goto error; + + r = sd_event_source_set_name(client->timeout_resend, + "dhcp4-resend-timer"); + if (r < 0) + goto error; } else if (r == -ENOMSG) /* invalid message, let's ignore it */ return 0; @@ -1114,27 +1266,12 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, case DHCP_STATE_REBINDING: r = client_handle_ack(client, message, len); - 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; - - log_dhcp_client(client, "REBOOTED"); - } - - goto error; - } else if (r >= 0) { + if (r >= 0) { client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->receive_message = + sd_event_source_unref(client->receive_message); + client->fd = asynchronous_close(client->fd); if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING)) @@ -1151,16 +1288,51 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, if (r < 0) goto error; + r = dhcp_network_bind_udp_socket(client->lease->address, + DHCP_PORT_CLIENT); + if (r < 0) { + log_dhcp_client(client, "could not bind UDP socket"); + goto error; + } + + client->fd = r; + + client_initialize_io_events(client, client_receive_message_udp); + if (notify_event) { - client = client_notify(client, notify_event); - if (!client || - client->state == DHCP_STATE_STOPPED) + client_notify(client, notify_event); + if (client->state == DHCP_STATE_STOPPED) return 0; } - client->receive_message = - sd_event_source_unref(client->receive_message); - client->fd = asynchronous_close(client->fd); + } else if (r == -EADDRNOTAVAIL) { + /* got a NAK, let's restart the client */ + client->timeout_resend = + sd_event_source_unref(client->timeout_resend); + + r = client_initialize(client); + if (r < 0) + goto error; + + r = client_start(client); + if (r < 0) + goto error; + + log_dhcp_client(client, "REBOOTED"); + + return 0; + } else if (r == -ENOMSG) + /* invalid message, let's ignore it */ + return 0; + + break; + + case DHCP_STATE_BOUND: + r = client_handle_forcerenew(client, message, len); + if (r >= 0) { + r = client_timeout_t1(NULL, 0, client); + if (r < 0) + goto error; } else if (r == -ENOMSG) /* invalid message, let's ignore it */ return 0; @@ -1169,7 +1341,6 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, case DHCP_STATE_INIT: case DHCP_STATE_INIT_REBOOT: - case DHCP_STATE_BOUND: break; @@ -1179,7 +1350,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, } error: - if (r < 0 || r == DHCP_EVENT_NO_LEASE) + if (r < 0) client_stop(client, r); return r; @@ -1190,6 +1361,9 @@ static int client_receive_message_udp(sd_event_source *s, int fd, sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; int buflen = 0, len, r; + const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } }; + const struct ether_addr *expected_chaddr = NULL; + uint8_t expected_hlen = 0; assert(s); assert(client); @@ -1211,8 +1385,55 @@ static int client_receive_message_udp(sd_event_source *s, int fd, log_dhcp_client(client, "could not receive message from UDP " "socket: %m"); return 0; - } else if ((size_t)len < sizeof(DHCPMessage)) + } else if ((size_t)len < sizeof(DHCPMessage)) { + log_dhcp_client(client, "too small to be a DHCP message: ignoring"); return 0; + } + + if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) { + log_dhcp_client(client, "not a DHCP message: ignoring"); + return 0; + } + + if (message->op != BOOTREPLY) { + log_dhcp_client(client, "not a BOOTREPLY message: ignoring"); + return 0; + } + + if (message->htype != client->arp_type) { + log_dhcp_client(client, "packet type does not match client type"); + return 0; + } + + if (client->arp_type == ARPHRD_ETHER) { + expected_hlen = ETH_ALEN; + expected_chaddr = (const struct ether_addr *) &client->mac_addr; + } else { + /* Non-ethernet links expect zero chaddr */ + expected_hlen = 0; + expected_chaddr = &zero_mac; + } + + if (message->hlen != expected_hlen) { + log_dhcp_client(client, "unexpected packet hlen %d", message->hlen); + return 0; + } + + if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { + log_dhcp_client(client, "received chaddr does not match " + "expected: ignoring"); + return 0; + } + + if (client->state != DHCP_STATE_BOUND && + be32toh(message->xid) != client->xid) { + /* in BOUND state, we may receive FORCERENEW with xid set by server, + so ignore the xid in this case */ + log_dhcp_client(client, "received xid (%u) does not match " + "expected (%u): ignoring", + be32toh(message->xid), client->xid); + return 0; + } return client_handle_message(client, message, len); } @@ -1293,18 +1514,18 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { r = client_start(client); if (r >= 0) - log_dhcp_client(client, "STARTED on ifindex %u with address %s", - client->index, - ether_ntoa(&client->client_id.mac_addr)); + log_dhcp_client(client, "STARTED on ifindex %u", client->index); return r; } int sd_dhcp_client_stop(sd_dhcp_client *client) { + DHCP_CLIENT_DONT_DESTROY(client); + assert_return(client, -EINVAL); - if (client_stop(client, DHCP_EVENT_STOP)) - client->state = DHCP_STATE_STOPPED; + client_stop(client, DHCP_EVENT_STOP); + client->state = DHCP_STATE_STOPPED; return 0; } @@ -1353,7 +1574,7 @@ sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { if (client && REFCNT_DEC(client->n_ref) <= 0) { - log_dhcp_client(client, "UNREF"); + log_dhcp_client(client, "FREE"); client_initialize(client); @@ -1365,19 +1586,16 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { sd_dhcp_lease_unref(client->lease); free(client->req_opts); + free(client->hostname); + free(client->vendor_class_identifier); free(client); - - return NULL; } - return client; + return NULL; } -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref); -#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp) - int sd_dhcp_client_new(sd_dhcp_client **ret) { - _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL; + _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL; assert_return(ret, -EINVAL); @@ -1390,6 +1608,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) { client->index = -1; client->fd = -1; client->attempt = 1; + client->mtu = DHCP_DEFAULT_MIN_SIZE; client->req_opts_size = ELEMENTSOF(default_req_opts);