2 This file is part of systemd.
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <net/ethernet.h>
25 #include <net/if_arp.h>
26 #include <netinet/ether.h>
27 #include <sys/param.h>
28 #include <sys/ioctl.h>
35 #include "dhcp-protocol.h"
36 #include "dhcp-internal.h"
37 #include "dhcp-lease-internal.h"
38 #include "sd-dhcp-client.h"
40 struct sd_dhcp_client {
46 sd_event_source *timeout_resend;
49 union sockaddr_union link;
50 sd_event_source *receive_message;
51 bool request_broadcast;
53 size_t req_opts_allocated;
58 struct ether_addr mac_addr;
61 char *vendor_class_identifier;
67 sd_event_source *timeout_t1;
68 sd_event_source *timeout_t2;
69 sd_event_source *timeout_expire;
70 sd_dhcp_client_cb_t cb;
75 static const uint8_t default_req_opts[] = {
76 DHCP_OPTION_SUBNET_MASK,
78 DHCP_OPTION_HOST_NAME,
79 DHCP_OPTION_DOMAIN_NAME,
80 DHCP_OPTION_DOMAIN_NAME_SERVER,
81 DHCP_OPTION_NTP_SERVER,
84 static int client_receive_message_raw(sd_event_source *s, int fd,
85 uint32_t revents, void *userdata);
86 static int client_receive_message_udp(sd_event_source *s, int fd,
87 uint32_t revents, void *userdata);
88 static void client_stop(sd_dhcp_client *client, int error);
90 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
92 assert_return(client, -EINVAL);
95 client->userdata = userdata;
100 int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
101 assert_return(client, -EINVAL);
103 client->request_broadcast = !!broadcast;
108 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
111 assert_return(client, -EINVAL);
112 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
113 DHCP_STATE_STOPPED), -EBUSY);
116 case DHCP_OPTION_PAD:
117 case DHCP_OPTION_OVERLOAD:
118 case DHCP_OPTION_MESSAGE_TYPE:
119 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
120 case DHCP_OPTION_END:
127 for (i = 0; i < client->req_opts_size; i++)
128 if (client->req_opts[i] == option)
131 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
132 client->req_opts_size + 1))
135 client->req_opts[client->req_opts_size++] = option;
140 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
141 const struct in_addr *last_addr) {
142 assert_return(client, -EINVAL);
143 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
144 DHCP_STATE_STOPPED), -EBUSY);
147 client->last_addr = last_addr->s_addr;
149 client->last_addr = INADDR_ANY;
154 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
155 assert_return(client, -EINVAL);
156 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
157 DHCP_STATE_STOPPED), -EBUSY);
158 assert_return(interface_index > 0, -EINVAL);
160 client->index = interface_index;
165 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
166 const struct ether_addr *addr) {
167 DHCP_CLIENT_DONT_DESTROY(client);
168 bool need_restart = false;
170 assert_return(client, -EINVAL);
171 assert_return(addr, -EINVAL);
173 if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
176 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
177 log_dhcp_client(client, "Changing MAC address on running DHCP "
178 "client, restarting");
180 client_stop(client, DHCP_EVENT_STOP);
183 memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
184 client->client_id.type = 0x01;
186 if (need_restart && client->state != DHCP_STATE_STOPPED)
187 sd_dhcp_client_start(client);
192 int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
193 const char *hostname) {
194 char *new_hostname = NULL;
196 assert_return(client, -EINVAL);
198 if (streq_ptr(client->hostname, hostname))
202 new_hostname = strdup(hostname);
207 free(client->hostname);
208 client->hostname = new_hostname;
213 int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
215 char *new_vci = NULL;
217 assert_return(client, -EINVAL);
219 new_vci = strdup(vci);
223 free(client->vendor_class_identifier);
225 client->vendor_class_identifier = new_vci;
230 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
231 assert_return(client, -EINVAL);
232 assert_return(ret, -EINVAL);
234 if (client->state != DHCP_STATE_BOUND &&
235 client->state != DHCP_STATE_RENEWING &&
236 client->state != DHCP_STATE_REBINDING)
237 return -EADDRNOTAVAIL;
239 *ret = sd_dhcp_lease_ref(client->lease);
244 static void client_notify(sd_dhcp_client *client, int event) {
246 client->cb(client, event, client->userdata);
249 static int client_initialize(sd_dhcp_client *client) {
250 assert_return(client, -EINVAL);
252 client->receive_message =
253 sd_event_source_unref(client->receive_message);
255 client->fd = asynchronous_close(client->fd);
257 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
259 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
260 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
261 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
265 client->state = DHCP_STATE_INIT;
269 client->lease = sd_dhcp_lease_unref(client->lease);
274 static void client_stop(sd_dhcp_client *client, int error) {
278 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
279 else if (error == DHCP_EVENT_STOP)
280 log_dhcp_client(client, "STOPPED");
282 log_dhcp_client(client, "STOPPED: Unknown event");
284 client_notify(client, error);
286 client_initialize(client);
289 static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
290 uint8_t type, size_t *_optlen, size_t *_optoffset) {
291 _cleanup_free_ DHCPPacket *packet;
292 size_t optlen, optoffset, size;
297 assert(client->secs);
301 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
303 optlen = DHCP_MIN_OPTIONS_SIZE;
304 size = sizeof(DHCPPacket) + optlen;
306 packet = malloc0(size);
310 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
315 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
316 refuse to issue an DHCP lease if 'secs' is set to zero */
317 packet->dhcp.secs = htobe16(client->secs);
319 /* RFC2132 section 4.1
320 A client that cannot receive unicast IP datagrams until its protocol
321 software has been configured with an IP address SHOULD set the
322 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
323 DHCPREQUEST messages that client sends. The BROADCAST bit will
324 provide a hint to the DHCP server and BOOTP relay agent to broadcast
325 any messages to the client on the client's subnet.
327 Note: some interfaces needs this to be enabled, but some networks
328 needs this to be disabled as broadcasts are filteretd, so this
329 needs to be configurable */
330 if (client->request_broadcast)
331 packet->dhcp.flags = htobe16(0x8000);
333 /* RFC2132 section 4.1.1:
334 The client MUST include its hardware address in the ’chaddr’ field, if
335 necessary for delivery of DHCP reply messages.
337 memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
339 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
340 Identifier option is not set */
341 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
342 DHCP_OPTION_CLIENT_IDENTIFIER,
343 sizeof(client->client_id), &client->client_id);
348 /* RFC2131 section 3.5:
349 in its initial DHCPDISCOVER or DHCPREQUEST message, a
350 client may provide the server with a list of specific
351 parameters the client is interested in. If the client
352 includes a list of parameters in a DHCPDISCOVER message,
353 it MUST include that list in any subsequent DHCPREQUEST
356 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
357 DHCP_OPTION_PARAMETER_REQUEST_LIST,
358 client->req_opts_size, client->req_opts);
362 /* RFC2131 section 3.5:
363 The client SHOULD include the ’maximum DHCP message size’ option to
364 let the server know how large the server may make its DHCP messages.
366 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
367 than the defined default size unless the Maximum Messge Size option
370 max_size = htobe16(size);
371 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
372 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
378 *_optoffset = optoffset;
385 static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
387 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
388 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
390 return dhcp_network_send_raw_socket(client->fd, &client->link,
394 static int client_send_discover(sd_dhcp_client *client) {
395 _cleanup_free_ DHCPPacket *discover = NULL;
396 size_t optoffset, optlen;
401 assert(client->state == DHCP_STATE_INIT ||
402 client->state == DHCP_STATE_SELECTING);
404 /* See RFC2131 section 4.4.1 */
406 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
409 assert(time_now >= client->start_time);
411 /* seconds between sending first and last DISCOVER
412 * must always be strictly positive to deal with broken servers */
413 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
415 r = client_message_init(client, &discover, DHCP_DISCOVER,
416 &optlen, &optoffset);
420 /* the client may suggest values for the network address
421 and lease time in the DHCPDISCOVER message. The client may include
422 the ’requested IP address’ option to suggest that a particular IP
423 address be assigned, and may include the ’IP address lease time’
424 option to suggest the lease time it would like.
426 if (client->last_addr != INADDR_ANY) {
427 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
428 DHCP_OPTION_REQUESTED_IP_ADDRESS,
429 4, &client->last_addr);
434 /* it is unclear from RFC 2131 if client should send hostname in
435 DHCPDISCOVER but dhclient does and so we do as well
437 if (client->hostname) {
438 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
439 DHCP_OPTION_HOST_NAME,
440 strlen(client->hostname), client->hostname);
445 if (client->vendor_class_identifier) {
446 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
447 DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
448 strlen(client->vendor_class_identifier),
449 client->vendor_class_identifier);
454 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
455 DHCP_OPTION_END, 0, NULL);
459 /* We currently ignore:
460 The client SHOULD wait a random time between one and ten seconds to
461 desynchronize the use of DHCP at startup.
463 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
467 log_dhcp_client(client, "DISCOVER");
472 static int client_send_request(sd_dhcp_client *client) {
473 _cleanup_free_ DHCPPacket *request = NULL;
474 size_t optoffset, optlen;
477 r = client_message_init(client, &request, DHCP_REQUEST,
478 &optlen, &optoffset);
482 switch (client->state) {
483 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
484 SELECTING should be REQUESTING)
487 case DHCP_STATE_REQUESTING:
488 /* Client inserts the address of the selected server in ’server
489 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
490 filled in with the yiaddr value from the chosen DHCPOFFER.
493 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
494 DHCP_OPTION_SERVER_IDENTIFIER,
495 4, &client->lease->server_address);
499 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
500 DHCP_OPTION_REQUESTED_IP_ADDRESS,
501 4, &client->lease->address);
507 case DHCP_STATE_INIT_REBOOT:
508 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
509 option MUST be filled in with client’s notion of its previously
510 assigned address. ’ciaddr’ MUST be zero.
512 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
513 DHCP_OPTION_REQUESTED_IP_ADDRESS,
514 4, &client->last_addr);
519 case DHCP_STATE_RENEWING:
520 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
521 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
522 client’s IP address.
526 case DHCP_STATE_REBINDING:
527 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
528 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
529 client’s IP address.
531 This message MUST be broadcast to the 0xffffffff IP broadcast address.
533 request->dhcp.ciaddr = client->lease->address;
537 case DHCP_STATE_INIT:
538 case DHCP_STATE_SELECTING:
539 case DHCP_STATE_REBOOTING:
540 case DHCP_STATE_BOUND:
541 case DHCP_STATE_STOPPED:
545 if (client->hostname) {
546 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
547 DHCP_OPTION_HOST_NAME,
548 strlen(client->hostname), client->hostname);
553 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
554 DHCP_OPTION_END, 0, NULL);
558 if (client->state == DHCP_STATE_RENEWING) {
559 r = dhcp_network_send_udp_socket(client->fd,
560 client->lease->server_address,
563 sizeof(DHCPMessage) + optoffset);
565 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
570 switch (client->state) {
571 case DHCP_STATE_REQUESTING:
572 log_dhcp_client(client, "REQUEST (requesting)");
574 case DHCP_STATE_INIT_REBOOT:
575 log_dhcp_client(client, "REQUEST (init-reboot)");
577 case DHCP_STATE_RENEWING:
578 log_dhcp_client(client, "REQUEST (renewing)");
580 case DHCP_STATE_REBINDING:
581 log_dhcp_client(client, "REQUEST (rebinding)");
584 log_dhcp_client(client, "REQUEST (invalid)");
591 static int client_start(sd_dhcp_client *client);
593 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
595 sd_dhcp_client *client = userdata;
596 DHCP_CLIENT_DONT_DESTROY(client);
597 usec_t next_timeout = 0;
604 assert(client->event);
606 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
610 switch (client->state) {
611 case DHCP_STATE_RENEWING:
613 time_left = (client->lease->t2 - client->lease->t1) / 2;
617 next_timeout = time_now + time_left * USEC_PER_SEC;
621 case DHCP_STATE_REBINDING:
623 time_left = (client->lease->lifetime - client->lease->t2) / 2;
627 next_timeout = time_now + time_left * USEC_PER_SEC;
630 case DHCP_STATE_REBOOTING:
631 /* start over as we did not receive a timely ack or nak */
632 r = client_initialize(client);
636 r = client_start(client);
640 log_dhcp_client(client, "REBOOTED");
644 case DHCP_STATE_INIT:
645 case DHCP_STATE_INIT_REBOOT:
646 case DHCP_STATE_SELECTING:
647 case DHCP_STATE_REQUESTING:
648 case DHCP_STATE_BOUND:
650 if (client->attempt < 64)
651 client->attempt *= 2;
653 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
657 case DHCP_STATE_STOPPED:
662 next_timeout += (random_u32() & 0x1fffff);
664 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
666 r = sd_event_add_time(client->event,
667 &client->timeout_resend,
668 clock_boottime_or_monotonic(),
669 next_timeout, 10 * USEC_PER_MSEC,
670 client_timeout_resend, client);
674 r = sd_event_source_set_priority(client->timeout_resend,
675 client->event_priority);
679 switch (client->state) {
680 case DHCP_STATE_INIT:
681 r = client_send_discover(client);
683 client->state = DHCP_STATE_SELECTING;
686 if (client->attempt >= 64)
692 case DHCP_STATE_SELECTING:
693 r = client_send_discover(client);
694 if (r < 0 && client->attempt >= 64)
699 case DHCP_STATE_INIT_REBOOT:
700 case DHCP_STATE_REQUESTING:
701 case DHCP_STATE_RENEWING:
702 case DHCP_STATE_REBINDING:
703 r = client_send_request(client);
704 if (r < 0 && client->attempt >= 64)
707 if (client->state == DHCP_STATE_INIT_REBOOT)
708 client->state = DHCP_STATE_REBOOTING;
710 client->request_sent = time_now;
714 case DHCP_STATE_REBOOTING:
715 case DHCP_STATE_BOUND:
719 case DHCP_STATE_STOPPED:
727 client_stop(client, r);
729 /* Errors were dealt with when stopping the client, don't spill
730 errors into the event loop handler */
734 static int client_initialize_events(sd_dhcp_client *client,
735 sd_event_io_handler_t io_callback) {
739 assert(client->event);
741 r = sd_event_add_io(client->event, &client->receive_message,
742 client->fd, EPOLLIN, io_callback,
747 r = sd_event_source_set_priority(client->receive_message,
748 client->event_priority);
752 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
754 r = sd_event_add_time(client->event,
755 &client->timeout_resend,
756 clock_boottime_or_monotonic(),
758 client_timeout_resend, client);
762 r = sd_event_source_set_priority(client->timeout_resend,
763 client->event_priority);
767 client_stop(client, r);
773 static int client_start(sd_dhcp_client *client) {
776 assert_return(client, -EINVAL);
777 assert_return(client->event, -EINVAL);
778 assert_return(client->index > 0, -EINVAL);
779 assert_return(client->fd < 0, -EBUSY);
780 assert_return(client->xid == 0, -EINVAL);
781 assert_return(client->state == DHCP_STATE_INIT ||
782 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
784 client->xid = random_u32();
786 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
788 client_stop(client, r);
793 if (client->state == DHCP_STATE_INIT) {
794 client->start_time = now(clock_boottime_or_monotonic());
798 return client_initialize_events(client, client_receive_message_raw);
801 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
803 sd_dhcp_client *client = userdata;
804 DHCP_CLIENT_DONT_DESTROY(client);
806 log_dhcp_client(client, "EXPIRED");
808 client_notify(client, DHCP_EVENT_EXPIRED);
810 /* lease was lost, start over if not freed or stopped in callback */
811 if (client->state != DHCP_STATE_STOPPED) {
812 client_initialize(client);
813 client_start(client);
819 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
820 sd_dhcp_client *client = userdata;
821 DHCP_CLIENT_DONT_DESTROY(client);
824 client->receive_message = sd_event_source_unref(client->receive_message);
825 client->fd = asynchronous_close(client->fd);
827 client->state = DHCP_STATE_REBINDING;
830 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr);
832 client_stop(client, r);
837 return client_initialize_events(client, client_receive_message_raw);
840 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
842 sd_dhcp_client *client = userdata;
843 DHCP_CLIENT_DONT_DESTROY(client);
846 client->state = DHCP_STATE_RENEWING;
849 r = dhcp_network_bind_udp_socket(client->lease->address,
852 log_dhcp_client(client, "could not bind UDP socket");
858 return client_initialize_events(client, client_receive_message_udp);
861 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
863 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
866 r = dhcp_lease_new(&lease);
870 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
871 if (r != DHCP_OFFER) {
872 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
876 lease->next_server = offer->siaddr;
878 lease->address = offer->yiaddr;
880 if (lease->address == INADDR_ANY ||
881 lease->server_address == INADDR_ANY ||
882 lease->lifetime == 0) {
883 log_dhcp_client(client, "receieved lease lacks address, server "
884 "address or lease lifetime, ignoring");
888 if (lease->subnet_mask == INADDR_ANY) {
889 r = dhcp_lease_set_default_subnet_mask(lease);
891 log_dhcp_client(client, "receieved lease lacks subnet "
892 "mask, and a fallback one can not be "
893 "generated, ignoring");
898 sd_dhcp_lease_unref(client->lease);
899 client->lease = lease;
902 log_dhcp_client(client, "OFFER");
907 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
909 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
912 r = dhcp_lease_new(&lease);
916 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
918 log_dhcp_client(client, "NAK");
919 return -EADDRNOTAVAIL;
923 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
927 lease->next_server = ack->siaddr;
929 lease->address = ack->yiaddr;
931 if (lease->address == INADDR_ANY ||
932 lease->server_address == INADDR_ANY ||
933 lease->lifetime == 0) {
934 log_dhcp_client(client, "receieved lease lacks address, server "
935 "address or lease lifetime, ignoring");
939 if (lease->subnet_mask == INADDR_ANY) {
940 r = dhcp_lease_set_default_subnet_mask(lease);
942 log_dhcp_client(client, "receieved lease lacks subnet "
943 "mask, and a fallback one can not be "
944 "generated, ignoring");
949 r = DHCP_EVENT_IP_ACQUIRE;
951 if (client->lease->address != lease->address ||
952 client->lease->subnet_mask != lease->subnet_mask ||
953 client->lease->router != lease->router) {
954 r = DHCP_EVENT_IP_CHANGE;
956 r = DHCP_EVENT_RENEW;
958 client->lease = sd_dhcp_lease_unref(client->lease);
961 client->lease = lease;
964 log_dhcp_client(client, "ACK");
969 static uint64_t client_compute_timeout(sd_dhcp_client *client,
970 uint32_t lifetime, double factor) {
972 assert(client->request_sent);
975 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
976 + (random_u32() & 0x1fffff);
979 static int client_set_lease_timeouts(sd_dhcp_client *client) {
981 uint64_t lifetime_timeout;
984 char time_string[FORMAT_TIMESPAN_MAX];
988 assert(client->event);
989 assert(client->lease);
990 assert(client->lease->lifetime);
992 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
993 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
994 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
996 /* don't set timers for infinite leases */
997 if (client->lease->lifetime == 0xffffffff)
1000 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1003 assert(client->request_sent <= time_now);
1005 /* convert the various timeouts from relative (secs) to absolute (usecs) */
1006 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
1007 if (client->lease->t1 && client->lease->t2) {
1008 /* both T1 and T2 are given */
1009 if (client->lease->t1 < client->lease->t2 &&
1010 client->lease->t2 < client->lease->lifetime) {
1011 /* they are both valid */
1012 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1013 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1016 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1017 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1018 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1019 client->lease->t1 = client->lease->lifetime / 2;
1021 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
1022 /* only T2 is given, and it is valid */
1023 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1024 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1025 client->lease->t1 = client->lease->lifetime / 2;
1026 if (t2_timeout <= t1_timeout) {
1027 /* the computed T1 would be invalid, so discard T2 */
1028 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1029 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1031 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
1032 /* only T1 is given, and it is valid */
1033 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1034 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1035 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1036 if (t2_timeout <= t1_timeout) {
1037 /* the computed T2 would be invalid, so discard T1 */
1038 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1039 client->lease->t2 = client->lease->lifetime / 2;
1042 /* fall back to the default timeouts */
1043 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1044 client->lease->t1 = client->lease->lifetime / 2;
1045 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1046 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1049 /* arm lifetime timeout */
1050 r = sd_event_add_time(client->event, &client->timeout_expire,
1051 clock_boottime_or_monotonic(),
1052 lifetime_timeout, 10 * USEC_PER_MSEC,
1053 client_timeout_expire, client);
1057 r = sd_event_source_set_priority(client->timeout_expire,
1058 client->event_priority);
1062 log_dhcp_client(client, "lease expires in %s",
1063 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1064 lifetime_timeout - time_now, 0));
1066 /* don't arm earlier timeouts if this has already expired */
1067 if (lifetime_timeout <= time_now)
1070 /* arm T2 timeout */
1071 r = sd_event_add_time(client->event,
1072 &client->timeout_t2,
1073 clock_boottime_or_monotonic(),
1076 client_timeout_t2, client);
1080 r = sd_event_source_set_priority(client->timeout_t2,
1081 client->event_priority);
1085 log_dhcp_client(client, "T2 expires in %s",
1086 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1087 t2_timeout - time_now, 0));
1089 /* don't arm earlier timeout if this has already expired */
1090 if (t2_timeout <= time_now)
1093 /* arm T1 timeout */
1094 r = sd_event_add_time(client->event,
1095 &client->timeout_t1,
1096 clock_boottime_or_monotonic(),
1097 t1_timeout, 10 * USEC_PER_MSEC,
1098 client_timeout_t1, client);
1102 r = sd_event_source_set_priority(client->timeout_t1,
1103 client->event_priority);
1107 log_dhcp_client(client, "T1 expires in %s",
1108 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1109 t1_timeout - time_now, 0));
1114 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1116 DHCP_CLIENT_DONT_DESTROY(client);
1117 int r = 0, notify_event = 0;
1120 assert(client->event);
1123 switch (client->state) {
1124 case DHCP_STATE_SELECTING:
1126 r = client_handle_offer(client, message, len);
1129 client->timeout_resend =
1130 sd_event_source_unref(client->timeout_resend);
1132 client->state = DHCP_STATE_REQUESTING;
1133 client->attempt = 1;
1135 r = sd_event_add_time(client->event,
1136 &client->timeout_resend,
1137 clock_boottime_or_monotonic(),
1139 client_timeout_resend, client);
1143 r = sd_event_source_set_priority(client->timeout_resend,
1144 client->event_priority);
1147 } else if (r == -ENOMSG)
1148 /* invalid message, let's ignore it */
1153 case DHCP_STATE_REBOOTING:
1154 case DHCP_STATE_REQUESTING:
1155 case DHCP_STATE_RENEWING:
1156 case DHCP_STATE_REBINDING:
1158 r = client_handle_ack(client, message, len);
1160 client->timeout_resend =
1161 sd_event_source_unref(client->timeout_resend);
1163 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1164 DHCP_STATE_REBOOTING))
1165 notify_event = DHCP_EVENT_IP_ACQUIRE;
1166 else if (r != DHCP_EVENT_IP_ACQUIRE)
1169 client->state = DHCP_STATE_BOUND;
1170 client->attempt = 1;
1172 client->last_addr = client->lease->address;
1174 r = client_set_lease_timeouts(client);
1179 client_notify(client, notify_event);
1180 if (client->state == DHCP_STATE_STOPPED)
1184 client->receive_message =
1185 sd_event_source_unref(client->receive_message);
1186 client->fd = asynchronous_close(client->fd);
1187 } else if (r == -EADDRNOTAVAIL) {
1188 /* got a NAK, let's restart the client */
1189 client->timeout_resend =
1190 sd_event_source_unref(client->timeout_resend);
1192 r = client_initialize(client);
1196 r = client_start(client);
1200 log_dhcp_client(client, "REBOOTED");
1203 } else if (r == -ENOMSG)
1204 /* invalid message, let's ignore it */
1209 case DHCP_STATE_INIT:
1210 case DHCP_STATE_INIT_REBOOT:
1211 case DHCP_STATE_BOUND:
1215 case DHCP_STATE_STOPPED:
1222 client_stop(client, r);
1227 static int client_receive_message_udp(sd_event_source *s, int fd,
1228 uint32_t revents, void *userdata) {
1229 sd_dhcp_client *client = userdata;
1230 _cleanup_free_ DHCPMessage *message = NULL;
1231 int buflen = 0, len, r;
1236 r = ioctl(fd, FIONREAD, &buflen);
1241 /* this can't be right */
1244 message = malloc0(buflen);
1248 len = read(fd, message, buflen);
1250 log_dhcp_client(client, "could not receive message from UDP "
1253 } else if ((size_t)len < sizeof(DHCPMessage)) {
1254 log_dhcp_client(client, "too small to be a DHCP message: ignoring");
1258 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1259 log_dhcp_client(client, "not a DHCP message: ignoring");
1263 if (message->op != BOOTREPLY) {
1264 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1268 if (be32toh(message->xid) != client->xid) {
1269 log_dhcp_client(client, "received xid (%u) does not match "
1270 "expected (%u): ignoring",
1271 be32toh(message->xid), client->xid);
1275 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1276 log_dhcp_client(client, "not an ethernet packet");
1280 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1282 log_dhcp_client(client, "received chaddr does not match "
1283 "expected: ignoring");
1287 return client_handle_message(client, message, len);
1290 static int client_receive_message_raw(sd_event_source *s, int fd,
1291 uint32_t revents, void *userdata) {
1292 sd_dhcp_client *client = userdata;
1293 _cleanup_free_ DHCPPacket *packet = NULL;
1294 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1295 struct iovec iov = {};
1296 struct msghdr msg = {
1299 .msg_control = cmsgbuf,
1300 .msg_controllen = sizeof(cmsgbuf),
1302 struct cmsghdr *cmsg;
1303 bool checksum = true;
1304 int buflen = 0, len, r;
1309 r = ioctl(fd, FIONREAD, &buflen);
1314 /* this can't be right */
1317 packet = malloc0(buflen);
1321 iov.iov_base = packet;
1322 iov.iov_len = buflen;
1324 len = recvmsg(fd, &msg, 0);
1326 log_dhcp_client(client, "could not receive message from raw "
1329 } else if ((size_t)len < sizeof(DHCPPacket))
1332 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1333 if (cmsg->cmsg_level == SOL_PACKET &&
1334 cmsg->cmsg_type == PACKET_AUXDATA &&
1335 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1336 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1338 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1343 r = dhcp_packet_verify_headers(packet, len, checksum);
1347 len -= DHCP_IP_UDP_SIZE;
1349 return client_handle_message(client, &packet->dhcp, len);
1352 int sd_dhcp_client_start(sd_dhcp_client *client) {
1355 assert_return(client, -EINVAL);
1357 r = client_initialize(client);
1361 if (client->last_addr)
1362 client->state = DHCP_STATE_INIT_REBOOT;
1364 r = client_start(client);
1366 log_dhcp_client(client, "STARTED on ifindex %u with address %s",
1368 ether_ntoa(&client->client_id.mac_addr));
1373 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1374 DHCP_CLIENT_DONT_DESTROY(client);
1376 assert_return(client, -EINVAL);
1378 client_stop(client, DHCP_EVENT_STOP);
1379 client->state = DHCP_STATE_STOPPED;
1384 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1388 assert_return(client, -EINVAL);
1389 assert_return(!client->event, -EBUSY);
1392 client->event = sd_event_ref(event);
1394 r = sd_event_default(&client->event);
1399 client->event_priority = priority;
1404 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1405 assert_return(client, -EINVAL);
1407 client->event = sd_event_unref(client->event);
1412 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1416 return client->event;
1419 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1421 assert_se(REFCNT_INC(client->n_ref) >= 2);
1426 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1427 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1428 log_dhcp_client(client, "FREE");
1430 client_initialize(client);
1432 client->receive_message =
1433 sd_event_source_unref(client->receive_message);
1435 sd_dhcp_client_detach_event(client);
1437 sd_dhcp_lease_unref(client->lease);
1439 free(client->req_opts);
1440 free(client->hostname);
1441 free(client->vendor_class_identifier);
1448 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1449 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
1451 assert_return(ret, -EINVAL);
1453 client = new0(sd_dhcp_client, 1);
1457 client->n_ref = REFCNT_INIT;
1458 client->state = DHCP_STATE_INIT;
1461 client->attempt = 1;
1463 client->req_opts_size = ELEMENTSOF(default_req_opts);
1465 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1466 if (!client->req_opts)