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 <sys/param.h>
27 #include <sys/ioctl.h>
33 #include "dhcp-protocol.h"
34 #include "dhcp-internal.h"
35 #include "dhcp-lease-internal.h"
36 #include "sd-dhcp-client.h"
38 struct sd_dhcp_client {
44 sd_event_source *timeout_resend;
47 union sockaddr_union link;
48 sd_event_source *receive_message;
50 size_t req_opts_allocated;
55 struct ether_addr mac_addr;
62 sd_event_source *timeout_t1;
63 sd_event_source *timeout_t2;
64 sd_event_source *timeout_expire;
65 sd_dhcp_client_cb_t cb;
70 static const uint8_t default_req_opts[] = {
71 DHCP_OPTION_SUBNET_MASK,
73 DHCP_OPTION_HOST_NAME,
74 DHCP_OPTION_DOMAIN_NAME,
75 DHCP_OPTION_DOMAIN_NAME_SERVER,
76 DHCP_OPTION_NTP_SERVER,
79 static int client_receive_message_raw(sd_event_source *s, int fd,
80 uint32_t revents, void *userdata);
81 static int client_receive_message_udp(sd_event_source *s, int fd,
82 uint32_t revents, void *userdata);
83 static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error);
85 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
87 assert_return(client, -EINVAL);
90 client->userdata = userdata;
95 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
98 assert_return(client, -EINVAL);
99 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
100 DHCP_STATE_STOPPED), -EBUSY);
103 case DHCP_OPTION_PAD:
104 case DHCP_OPTION_OVERLOAD:
105 case DHCP_OPTION_MESSAGE_TYPE:
106 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
107 case DHCP_OPTION_END:
114 for (i = 0; i < client->req_opts_size; i++)
115 if (client->req_opts[i] == option)
118 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
119 client->req_opts_size + 1))
122 client->req_opts[client->req_opts_size++] = option;
127 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
128 const struct in_addr *last_addr) {
129 assert_return(client, -EINVAL);
130 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
131 DHCP_STATE_STOPPED), -EBUSY);
134 client->last_addr = last_addr->s_addr;
136 client->last_addr = INADDR_ANY;
141 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
142 assert_return(client, -EINVAL);
143 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
144 DHCP_STATE_STOPPED), -EBUSY);
145 assert_return(interface_index >= -1, -EINVAL);
147 client->index = interface_index;
152 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
153 const struct ether_addr *addr) {
154 bool need_restart = false;
156 assert_return(client, -EINVAL);
157 assert_return(addr, -EINVAL);
159 if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
162 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
163 log_dhcp_client(client, "Changing MAC address on running DHCP "
164 "client, restarting");
166 client = client_stop(client, DHCP_EVENT_STOP);
172 memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
173 client->client_id.type = 0x01;
175 if (need_restart && client->state != DHCP_STATE_STOPPED)
176 sd_dhcp_client_start(client);
181 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
182 assert_return(client, -EINVAL);
183 assert_return(ret, -EINVAL);
185 if (client->state != DHCP_STATE_BOUND &&
186 client->state != DHCP_STATE_RENEWING &&
187 client->state != DHCP_STATE_REBINDING)
188 return -EADDRNOTAVAIL;
190 *ret = sd_dhcp_lease_ref(client->lease);
195 static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) {
197 client = sd_dhcp_client_ref(client);
198 client->cb(client, event, client->userdata);
199 client = sd_dhcp_client_unref(client);
205 static int client_initialize(sd_dhcp_client *client) {
206 assert_return(client, -EINVAL);
208 client->receive_message =
209 sd_event_source_unref(client->receive_message);
211 client->fd = safe_close(client->fd);
213 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
216 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
217 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
221 client->state = DHCP_STATE_INIT;
225 client->lease = sd_dhcp_lease_unref(client->lease);
230 static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
231 assert_return(client, NULL);
233 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
235 client = client_notify(client, error);
238 client_initialize(client);
243 static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
244 uint8_t type, uint8_t **opt, size_t *optlen) {
249 assert(client->secs);
253 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
255 r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
260 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
261 refuse to issue an DHCP lease if 'secs' is set to zero */
262 message->secs = htobe16(client->secs);
264 /* RFC2132 section 4.1.1:
265 The client MUST include its hardware address in the ’chaddr’ field, if
266 necessary for delivery of DHCP reply messages.
268 memcpy(&message->chaddr, &client->client_id.mac_addr, ETH_ALEN);
270 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
271 Identifier option is not set */
272 r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
273 sizeof(client->client_id), &client->client_id);
278 /* RFC2131 section 3.5:
279 in its initial DHCPDISCOVER or DHCPREQUEST message, a
280 client may provide the server with a list of specific
281 parameters the client is interested in. If the client
282 includes a list of parameters in a DHCPDISCOVER message,
283 it MUST include that list in any subsequent DHCPREQUEST
286 r = dhcp_option_append(opt, optlen,
287 DHCP_OPTION_PARAMETER_REQUEST_LIST,
288 client->req_opts_size,
293 /* RFC2131 section 3.5:
294 The client SHOULD include the ’maximum DHCP message size’ option to
295 let the server know how large the server may make its DHCP messages.
297 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
298 than the defined default size unless the Maximum Messge Size option
301 max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
302 DHCP_MIN_OPTIONS_SIZE);
303 r = dhcp_option_append(opt, optlen,
304 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
312 static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
314 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
315 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
317 return dhcp_network_send_raw_socket(client->fd, &client->link,
321 static int client_send_discover(sd_dhcp_client *client) {
322 _cleanup_free_ DHCPPacket *discover = NULL;
329 assert(client->state == DHCP_STATE_INIT ||
330 client->state == DHCP_STATE_SELECTING);
332 /* See RFC2131 section 4.4.1 */
334 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
337 assert(time_now >= client->start_time);
339 /* seconds between sending first and last DISCOVER
340 * must always be strictly positive to deal with broken servers */
341 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
343 optlen = DHCP_MIN_OPTIONS_SIZE;
344 len = sizeof(DHCPPacket) + optlen;
346 discover = malloc0(len);
350 r = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
355 /* the client may suggest values for the network address
356 and lease time in the DHCPDISCOVER message. The client may include
357 the ’requested IP address’ option to suggest that a particular IP
358 address be assigned, and may include the ’IP address lease time’
359 option to suggest the lease time it would like.
361 if (client->last_addr != INADDR_ANY) {
362 r = dhcp_option_append(&opt, &optlen,
363 DHCP_OPTION_REQUESTED_IP_ADDRESS,
364 4, &client->last_addr);
369 r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
373 /* We currently ignore:
374 The client SHOULD wait a random time between one and ten seconds to
375 desynchronize the use of DHCP at startup.
377 r = dhcp_client_send_raw(client, discover, len - optlen);
381 log_dhcp_client(client, "DISCOVER");
386 static int client_send_request(sd_dhcp_client *client) {
387 _cleanup_free_ DHCPPacket *request;
392 optlen = DHCP_MIN_OPTIONS_SIZE;
393 len = sizeof(DHCPPacket) + optlen;
395 request = malloc0(len);
399 r = client_message_init(client, &request->dhcp, DHCP_REQUEST, &opt,
404 switch (client->state) {
405 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
406 SELECTING should be REQUESTING)
409 case DHCP_STATE_REQUESTING:
410 /* Client inserts the address of the selected server in ’server
411 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
412 filled in with the yiaddr value from the chosen DHCPOFFER.
415 r = dhcp_option_append(&opt, &optlen,
416 DHCP_OPTION_SERVER_IDENTIFIER,
417 4, &client->lease->server_address);
421 r = dhcp_option_append(&opt, &optlen,
422 DHCP_OPTION_REQUESTED_IP_ADDRESS,
423 4, &client->lease->address);
429 case DHCP_STATE_INIT_REBOOT:
430 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
431 option MUST be filled in with client’s notion of its previously
432 assigned address. ’ciaddr’ MUST be zero.
434 r = dhcp_option_append(&opt, &optlen,
435 DHCP_OPTION_REQUESTED_IP_ADDRESS,
436 4, &client->last_addr);
441 case DHCP_STATE_RENEWING:
442 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
443 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
444 client’s IP address.
448 case DHCP_STATE_REBINDING:
449 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
450 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
451 client’s IP address.
453 This message MUST be broadcast to the 0xffffffff IP broadcast address.
455 request->dhcp.ciaddr = client->lease->address;
459 case DHCP_STATE_INIT:
460 case DHCP_STATE_SELECTING:
461 case DHCP_STATE_REBOOTING:
462 case DHCP_STATE_BOUND:
463 case DHCP_STATE_STOPPED:
467 r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
471 if (client->state == DHCP_STATE_RENEWING) {
472 r = dhcp_network_send_udp_socket(client->fd,
473 client->lease->server_address,
476 len - optlen - DHCP_IP_UDP_SIZE);
478 r = dhcp_client_send_raw(client, request, len - optlen);
483 switch (client->state) {
484 case DHCP_STATE_REQUESTING:
485 log_dhcp_client(client, "REQUEST (requesting)");
487 case DHCP_STATE_INIT_REBOOT:
488 log_dhcp_client(client, "REQUEST (init-reboot)");
490 case DHCP_STATE_RENEWING:
491 log_dhcp_client(client, "REQUEST (renewing)");
493 case DHCP_STATE_REBINDING:
494 log_dhcp_client(client, "REQUEST (rebinding)");
497 log_dhcp_client(client, "REQUEST (invalid)");
504 static int client_start(sd_dhcp_client *client);
506 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
508 sd_dhcp_client *client = userdata;
509 usec_t next_timeout = 0;
516 assert(client->event);
518 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
522 switch (client->state) {
523 case DHCP_STATE_RENEWING:
525 time_left = (client->lease->t2 - client->lease->t1) / 2;
529 next_timeout = time_now + time_left * USEC_PER_SEC;
533 case DHCP_STATE_REBINDING:
535 time_left = (client->lease->lifetime - client->lease->t2) / 2;
539 next_timeout = time_now + time_left * USEC_PER_SEC;
542 case DHCP_STATE_REBOOTING:
543 /* start over as we did not receive a timely ack or nak */
544 r = client_initialize(client);
548 r = client_start(client);
552 log_dhcp_client(client, "REBOOTED");
556 case DHCP_STATE_INIT:
557 case DHCP_STATE_INIT_REBOOT:
558 case DHCP_STATE_SELECTING:
559 case DHCP_STATE_REQUESTING:
560 case DHCP_STATE_BOUND:
562 if (client->attempt < 64)
563 client->attempt *= 2;
565 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
569 case DHCP_STATE_STOPPED:
574 next_timeout += (random_u32() & 0x1fffff);
576 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
578 r = sd_event_add_time(client->event,
579 &client->timeout_resend,
581 next_timeout, 10 * USEC_PER_MSEC,
582 client_timeout_resend, client);
586 r = sd_event_source_set_priority(client->timeout_resend,
587 client->event_priority);
591 switch (client->state) {
592 case DHCP_STATE_INIT:
593 r = client_send_discover(client);
595 client->state = DHCP_STATE_SELECTING;
598 if (client->attempt >= 64)
604 case DHCP_STATE_SELECTING:
605 r = client_send_discover(client);
606 if (r < 0 && client->attempt >= 64)
611 case DHCP_STATE_INIT_REBOOT:
612 case DHCP_STATE_REQUESTING:
613 case DHCP_STATE_RENEWING:
614 case DHCP_STATE_REBINDING:
615 r = client_send_request(client);
616 if (r < 0 && client->attempt >= 64)
619 if (client->state == DHCP_STATE_INIT_REBOOT)
620 client->state = DHCP_STATE_REBOOTING;
622 client->request_sent = time_now;
626 case DHCP_STATE_REBOOTING:
627 case DHCP_STATE_BOUND:
631 case DHCP_STATE_STOPPED:
639 client_stop(client, r);
641 /* Errors were dealt with when stopping the client, don't spill
642 errors into the event loop handler */
646 static int client_initialize_events(sd_dhcp_client *client,
647 sd_event_io_handler_t io_callback) {
651 assert(client->event);
653 r = sd_event_add_io(client->event, &client->receive_message,
654 client->fd, EPOLLIN, io_callback,
659 r = sd_event_source_set_priority(client->receive_message,
660 client->event_priority);
664 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
666 r = sd_event_add_time(client->event,
667 &client->timeout_resend,
670 client_timeout_resend, client);
674 r = sd_event_source_set_priority(client->timeout_resend,
675 client->event_priority);
679 client_stop(client, r);
685 static int client_start(sd_dhcp_client *client) {
688 assert_return(client, -EINVAL);
689 assert_return(client->event, -EINVAL);
690 assert_return(client->index > 0, -EINVAL);
691 assert_return(client->fd < 0, -EBUSY);
692 assert_return(client->xid == 0, -EINVAL);
693 assert_return(client->state == DHCP_STATE_INIT ||
694 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
696 client->xid = random_u32();
698 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
700 client_stop(client, r);
705 if (client->state == DHCP_STATE_INIT) {
706 client->start_time = now(CLOCK_MONOTONIC);
710 return client_initialize_events(client, client_receive_message_raw);
713 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
715 sd_dhcp_client *client = userdata;
717 log_dhcp_client(client, "EXPIRED");
719 client = client_notify(client, DHCP_EVENT_EXPIRED);
721 /* lease was lost, start over if not freed or stopped in callback */
722 if (client && client->state != DHCP_STATE_STOPPED) {
723 client_initialize(client);
724 client_start(client);
730 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
731 sd_dhcp_client *client = userdata;
734 client->receive_message = sd_event_source_unref(client->receive_message);
735 client->fd = safe_close(client->fd);
737 client->state = DHCP_STATE_REBINDING;
740 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
742 client_stop(client, r);
747 log_dhcp_client(client, "TIMEOUT T2");
749 return client_initialize_events(client, client_receive_message_raw);
752 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
754 sd_dhcp_client *client = userdata;
757 client->state = DHCP_STATE_RENEWING;
760 r = dhcp_network_bind_udp_socket(client->index,
761 client->lease->address,
764 client_stop(client, r);
770 log_dhcp_client(client, "TIMEOUT T1");
772 return client_initialize_events(client, client_receive_message_udp);
775 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
777 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
780 r = dhcp_lease_new(&lease);
784 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
785 if (r != DHCP_OFFER) {
786 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
790 lease->next_server = offer->siaddr;
792 lease->address = offer->yiaddr;
794 if (lease->address == INADDR_ANY ||
795 lease->server_address == INADDR_ANY ||
796 lease->lifetime == 0) {
797 log_dhcp_client(client, "receieved lease lacks address, server "
798 "address or lease lifetime, ignoring");
802 if (lease->subnet_mask == INADDR_ANY) {
803 r = dhcp_lease_set_default_subnet_mask(lease);
805 log_dhcp_client(client, "receieved lease lacks subnet "
806 "mask, and a fallback one can not be "
807 "generated, ignoring");
812 client->lease = lease;
815 log_dhcp_client(client, "OFFER");
820 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
822 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
825 r = dhcp_lease_new(&lease);
829 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
831 log_dhcp_client(client, "NAK");
832 return DHCP_EVENT_NO_LEASE;
836 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
840 lease->next_server = ack->siaddr;
842 lease->address = ack->yiaddr;
844 if (lease->address == INADDR_ANY ||
845 lease->server_address == INADDR_ANY ||
846 lease->lifetime == 0) {
847 log_dhcp_client(client, "receieved lease lacks address, server "
848 "address or lease lifetime, ignoring");
852 if (lease->subnet_mask == INADDR_ANY) {
853 r = dhcp_lease_set_default_subnet_mask(lease);
855 log_dhcp_client(client, "receieved lease lacks subnet "
856 "mask, and a fallback one can not be "
857 "generated, ignoring");
862 r = DHCP_EVENT_IP_ACQUIRE;
864 if (client->lease->address != lease->address ||
865 client->lease->subnet_mask != lease->subnet_mask ||
866 client->lease->router != lease->router) {
867 r = DHCP_EVENT_IP_CHANGE;
870 client->lease = sd_dhcp_lease_unref(client->lease);
873 client->lease = lease;
876 log_dhcp_client(client, "ACK");
881 static uint64_t client_compute_timeout(sd_dhcp_client *client,
882 uint32_t lifetime, double factor) {
884 assert(client->request_sent);
887 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
888 + (random_u32() & 0x1fffff);
891 static int client_set_lease_timeouts(sd_dhcp_client *client) {
893 uint64_t lifetime_timeout;
896 char time_string[FORMAT_TIMESPAN_MAX];
900 assert(client->event);
901 assert(client->lease);
902 assert(client->lease->lifetime);
904 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
905 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
906 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
908 /* don't set timers for infinite leases */
909 if (client->lease->lifetime == 0xffffffff)
912 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
915 assert(client->request_sent <= time_now);
917 /* convert the various timeouts from relative (secs) to absolute (usecs) */
918 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
919 if (client->lease->t1 && client->lease->t2) {
920 /* both T1 and T2 are given */
921 if (client->lease->t1 < client->lease->t2 &&
922 client->lease->t2 < client->lease->lifetime) {
923 /* they are both valid */
924 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
925 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
928 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
929 client->lease->t2 = (client->lease->lifetime * 7) / 8;
930 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
931 client->lease->t1 = client->lease->lifetime / 2;
933 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
934 /* only T2 is given, and it is valid */
935 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
936 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
937 client->lease->t1 = client->lease->lifetime / 2;
938 if (t2_timeout <= t1_timeout) {
939 /* the computed T1 would be invalid, so discard T2 */
940 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
941 client->lease->t2 = (client->lease->lifetime * 7) / 8;
943 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
944 /* only T1 is given, and it is valid */
945 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
946 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
947 client->lease->t2 = (client->lease->lifetime * 7) / 8;
948 if (t2_timeout <= t1_timeout) {
949 /* the computed T2 would be invalid, so discard T1 */
950 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
951 client->lease->t2 = client->lease->lifetime / 2;
954 /* fall back to the default timeouts */
955 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
956 client->lease->t1 = client->lease->lifetime / 2;
957 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
958 client->lease->t2 = (client->lease->lifetime * 7) / 8;
961 /* arm lifetime timeout */
962 r = sd_event_add_time(client->event, &client->timeout_expire,
964 lifetime_timeout, 10 * USEC_PER_MSEC,
965 client_timeout_expire, client);
969 r = sd_event_source_set_priority(client->timeout_expire,
970 client->event_priority);
974 log_dhcp_client(client, "lease expires in %s",
975 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
976 lifetime_timeout - time_now, 0));
978 /* don't arm earlier timeouts if this has already expired */
979 if (lifetime_timeout <= time_now)
983 r = sd_event_add_time(client->event,
988 client_timeout_t2, client);
992 r = sd_event_source_set_priority(client->timeout_t2,
993 client->event_priority);
997 log_dhcp_client(client, "T2 expires in %s",
998 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
999 t2_timeout - time_now, 0));
1001 /* don't arm earlier timeout if this has already expired */
1002 if (t2_timeout <= time_now)
1005 /* arm T1 timeout */
1006 r = sd_event_add_time(client->event,
1007 &client->timeout_t1,
1009 t1_timeout, 10 * USEC_PER_MSEC,
1010 client_timeout_t1, client);
1014 r = sd_event_source_set_priority(client->timeout_t1,
1015 client->event_priority);
1019 log_dhcp_client(client, "T1 expires in %s",
1020 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1021 t1_timeout - time_now, 0));
1026 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1028 int r = 0, notify_event = 0;
1031 assert(client->event);
1034 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1035 log_dhcp_client(client, "not a DHCP message: ignoring");
1039 if (message->op != BOOTREPLY) {
1040 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1044 if (be32toh(message->xid) != client->xid) {
1045 log_dhcp_client(client, "received xid (%u) does not match "
1046 "expected (%u): ignoring",
1047 be32toh(message->xid), client->xid);
1051 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1052 log_dhcp_client(client, "not an ethernet packet");
1056 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1058 log_dhcp_client(client, "received chaddr does not match "
1059 "expected: ignoring");
1063 switch (client->state) {
1064 case DHCP_STATE_SELECTING:
1066 r = client_handle_offer(client, message, len);
1069 client->timeout_resend =
1070 sd_event_source_unref(client->timeout_resend);
1072 client->state = DHCP_STATE_REQUESTING;
1073 client->attempt = 1;
1075 r = sd_event_add_time(client->event,
1076 &client->timeout_resend,
1079 client_timeout_resend, client);
1083 r = sd_event_source_set_priority(client->timeout_resend,
1084 client->event_priority);
1087 } else if (r == -ENOMSG)
1088 /* invalid message, let's ignore it */
1093 case DHCP_STATE_REBOOTING:
1094 case DHCP_STATE_REQUESTING:
1095 case DHCP_STATE_RENEWING:
1096 case DHCP_STATE_REBINDING:
1098 r = client_handle_ack(client, message, len);
1099 if (r == DHCP_EVENT_NO_LEASE) {
1101 client->timeout_resend =
1102 sd_event_source_unref(client->timeout_resend);
1104 if (client->state == DHCP_STATE_REBOOTING) {
1105 r = client_initialize(client);
1109 r = client_start(client);
1113 log_dhcp_client(client, "REBOOTED");
1117 } else if (r >= 0) {
1118 client->timeout_resend =
1119 sd_event_source_unref(client->timeout_resend);
1121 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1122 DHCP_STATE_REBOOTING))
1123 notify_event = DHCP_EVENT_IP_ACQUIRE;
1124 else if (r != DHCP_EVENT_IP_ACQUIRE)
1127 client->state = DHCP_STATE_BOUND;
1128 client->attempt = 1;
1130 client->last_addr = client->lease->address;
1132 r = client_set_lease_timeouts(client);
1137 client = client_notify(client, notify_event);
1139 client->state == DHCP_STATE_STOPPED)
1143 client->receive_message =
1144 sd_event_source_unref(client->receive_message);
1145 client->fd = safe_close(client->fd);
1146 } else if (r == -ENOMSG)
1147 /* invalid message, let's ignore it */
1152 case DHCP_STATE_INIT:
1153 case DHCP_STATE_INIT_REBOOT:
1154 case DHCP_STATE_BOUND:
1158 case DHCP_STATE_STOPPED:
1164 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1165 client_stop(client, r);
1170 static int client_receive_message_udp(sd_event_source *s, int fd,
1171 uint32_t revents, void *userdata) {
1172 sd_dhcp_client *client = userdata;
1173 _cleanup_free_ DHCPMessage *message = NULL;
1174 int buflen = 0, len, r;
1179 r = ioctl(fd, FIONREAD, &buflen);
1180 if (r < 0 || buflen <= 0)
1181 buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
1183 message = malloc0(buflen);
1187 len = read(fd, message, buflen);
1189 log_dhcp_client(client, "could not receive message from UDP "
1190 "socket: %s", strerror(errno));
1192 } else if ((size_t)len < sizeof(DHCPMessage))
1195 return client_handle_message(client, message, len);
1198 static int client_receive_message_raw(sd_event_source *s, int fd,
1199 uint32_t revents, void *userdata) {
1200 sd_dhcp_client *client = userdata;
1201 _cleanup_free_ DHCPPacket *packet = NULL;
1202 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1203 struct iovec iov = {};
1204 struct msghdr msg = {
1207 .msg_control = cmsgbuf,
1208 .msg_controllen = sizeof(cmsgbuf),
1210 struct cmsghdr *cmsg;
1211 bool checksum = true;
1212 int buflen = 0, len, r;
1217 r = ioctl(fd, FIONREAD, &buflen);
1218 if (r < 0 || buflen <= 0)
1219 buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
1221 packet = malloc0(buflen);
1225 iov.iov_base = packet;
1226 iov.iov_len = buflen;
1228 len = recvmsg(fd, &msg, 0);
1230 log_dhcp_client(client, "could not receive message from raw "
1231 "socket: %s", strerror(errno));
1233 } else if ((size_t)len < sizeof(DHCPPacket))
1236 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1237 if (cmsg->cmsg_level == SOL_PACKET &&
1238 cmsg->cmsg_type == PACKET_AUXDATA &&
1239 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1240 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1242 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1247 r = dhcp_packet_verify_headers(packet, len, checksum);
1251 len -= DHCP_IP_UDP_SIZE;
1253 return client_handle_message(client, &packet->dhcp, len);
1256 int sd_dhcp_client_start(sd_dhcp_client *client) {
1259 assert_return(client, -EINVAL);
1261 r = client_initialize(client);
1265 if (client->last_addr)
1266 client->state = DHCP_STATE_INIT_REBOOT;
1268 r = client_start(client);
1270 log_dhcp_client(client, "STARTED");
1275 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1276 assert_return(client, -EINVAL);
1278 if (client_stop(client, DHCP_EVENT_STOP))
1279 client->state = DHCP_STATE_STOPPED;
1284 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1288 assert_return(client, -EINVAL);
1289 assert_return(!client->event, -EBUSY);
1292 client->event = sd_event_ref(event);
1294 r = sd_event_default(&client->event);
1299 client->event_priority = priority;
1304 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1305 assert_return(client, -EINVAL);
1307 client->event = sd_event_unref(client->event);
1312 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1316 return client->event;
1319 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1321 assert_se(REFCNT_INC(client->n_ref) >= 2);
1326 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1327 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1328 log_dhcp_client(client, "UNREF");
1330 client_initialize(client);
1332 client->receive_message =
1333 sd_event_source_unref(client->receive_message);
1335 sd_dhcp_client_detach_event(client);
1337 free(client->req_opts);
1346 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
1347 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
1349 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1350 _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1352 assert_return(ret, -EINVAL);
1354 client = new0(sd_dhcp_client, 1);
1358 client->n_ref = REFCNT_INIT;
1359 client->state = DHCP_STATE_INIT;
1362 client->attempt = 1;
1364 client->req_opts_size = ELEMENTSOF(default_req_opts);
1366 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1367 if (!client->req_opts)