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 %d", 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 log_dhcp_client(client, "REQUEST");
488 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
490 sd_dhcp_client *client = userdata;
491 usec_t next_timeout = 0;
498 assert(client->event);
500 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
504 switch (client->state) {
505 case DHCP_STATE_RENEWING:
507 time_left = (client->lease->t2 - client->lease->t1) / 2;
511 next_timeout = time_now + time_left * USEC_PER_SEC;
515 case DHCP_STATE_REBINDING:
517 time_left = (client->lease->lifetime - client->lease->t2) / 2;
521 next_timeout = time_now + time_left * USEC_PER_SEC;
524 case DHCP_STATE_REBOOTING:
525 /* start over as we did not receive a timely ack or nak */
526 client->state = DHCP_STATE_INIT;
529 client->fd = safe_close(client->fd);
530 client->xid = random_u32();
531 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
537 case DHCP_STATE_INIT:
538 case DHCP_STATE_INIT_REBOOT:
539 case DHCP_STATE_SELECTING:
540 case DHCP_STATE_REQUESTING:
541 case DHCP_STATE_BOUND:
543 if (client->attempt < 64)
544 client->attempt *= 2;
546 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
550 case DHCP_STATE_STOPPED:
555 next_timeout += (random_u32() & 0x1fffff);
557 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
559 r = sd_event_add_time(client->event,
560 &client->timeout_resend,
562 next_timeout, 10 * USEC_PER_MSEC,
563 client_timeout_resend, client);
567 r = sd_event_source_set_priority(client->timeout_resend,
568 client->event_priority);
572 switch (client->state) {
573 case DHCP_STATE_INIT:
574 r = client_send_discover(client);
576 client->state = DHCP_STATE_SELECTING;
579 if (client->attempt >= 64)
585 case DHCP_STATE_SELECTING:
586 r = client_send_discover(client);
587 if (r < 0 && client->attempt >= 64)
592 case DHCP_STATE_INIT_REBOOT:
593 case DHCP_STATE_REQUESTING:
594 case DHCP_STATE_RENEWING:
595 case DHCP_STATE_REBINDING:
596 r = client_send_request(client);
597 if (r < 0 && client->attempt >= 64)
600 if (client->state == DHCP_STATE_INIT_REBOOT)
601 client->state = DHCP_STATE_REBOOTING;
603 client->request_sent = time_now;
607 case DHCP_STATE_REBOOTING:
608 case DHCP_STATE_BOUND:
612 case DHCP_STATE_STOPPED:
620 client_stop(client, r);
622 /* Errors were dealt with when stopping the client, don't spill
623 errors into the event loop handler */
627 static int client_initialize_events(sd_dhcp_client *client,
628 sd_event_io_handler_t io_callback) {
632 assert(client->event);
634 r = sd_event_add_io(client->event, &client->receive_message,
635 client->fd, EPOLLIN, io_callback,
640 r = sd_event_source_set_priority(client->receive_message,
641 client->event_priority);
645 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
647 r = sd_event_add_time(client->event,
648 &client->timeout_resend,
651 client_timeout_resend, client);
655 r = sd_event_source_set_priority(client->timeout_resend,
656 client->event_priority);
660 client_stop(client, r);
666 static int client_start(sd_dhcp_client *client) {
669 assert_return(client, -EINVAL);
670 assert_return(client->event, -EINVAL);
671 assert_return(client->index > 0, -EINVAL);
672 assert_return(client->fd < 0, -EBUSY);
673 assert_return(client->xid == 0, -EINVAL);
674 assert_return(client->state == DHCP_STATE_INIT ||
675 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
677 client->xid = random_u32();
679 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
682 client_stop(client, r);
687 if (client->state == DHCP_STATE_INIT) {
688 client->start_time = now(CLOCK_MONOTONIC);
692 log_dhcp_client(client, "STARTED");
694 return client_initialize_events(client, client_receive_message_raw);
697 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
699 sd_dhcp_client *client = userdata;
701 log_dhcp_client(client, "EXPIRED");
703 client = client_notify(client, DHCP_EVENT_EXPIRED);
705 /* lease was lost, start over if not freed or stopped in callback */
706 if (client && client->state != DHCP_STATE_STOPPED) {
707 client_initialize(client);
708 client_start(client);
714 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
715 sd_dhcp_client *client = userdata;
718 client->receive_message = sd_event_source_unref(client->receive_message);
719 client->fd = safe_close(client->fd);
721 client->state = DHCP_STATE_REBINDING;
724 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
726 client_stop(client, r);
732 log_dhcp_client(client, "TIMEOUT T2");
734 return client_initialize_events(client, client_receive_message_raw);
737 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
739 sd_dhcp_client *client = userdata;
742 client->state = DHCP_STATE_RENEWING;
745 r = dhcp_network_bind_udp_socket(client->index,
746 client->lease->address,
749 client_stop(client, r);
755 log_dhcp_client(client, "TIMEOUT T1");
757 return client_initialize_events(client, client_receive_message_udp);
760 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
762 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
765 r = dhcp_lease_new(&lease);
769 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
770 if (r != DHCP_OFFER) {
771 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
775 lease->next_server = offer->siaddr;
777 lease->address = offer->yiaddr;
779 if (lease->address == INADDR_ANY ||
780 lease->server_address == INADDR_ANY ||
781 lease->lifetime == 0) {
782 log_dhcp_client(client, "receieved lease lacks address, server "
783 "address or lease lifetime, ignoring");
787 if (lease->subnet_mask == INADDR_ANY) {
788 r = dhcp_lease_set_default_subnet_mask(lease);
790 log_dhcp_client(client, "receieved lease lacks subnet "
791 "mask, and a fallback one can not be "
792 "generated, ignoring");
797 client->lease = lease;
800 log_dhcp_client(client, "OFFER");
805 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
807 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
810 r = dhcp_lease_new(&lease);
814 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
816 log_dhcp_client(client, "NAK");
817 return DHCP_EVENT_NO_LEASE;
821 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
825 lease->next_server = ack->siaddr;
827 lease->address = ack->yiaddr;
829 if (lease->address == INADDR_ANY ||
830 lease->server_address == INADDR_ANY ||
831 lease->lifetime == 0) {
832 log_dhcp_client(client, "receieved lease lacks address, server "
833 "address or lease lifetime, ignoring");
837 if (lease->subnet_mask == INADDR_ANY) {
838 r = dhcp_lease_set_default_subnet_mask(lease);
840 log_dhcp_client(client, "receieved lease lacks subnet "
841 "mask, and a fallback one can not be "
842 "generated, ignoring");
847 r = DHCP_EVENT_IP_ACQUIRE;
849 if (client->lease->address != lease->address ||
850 client->lease->subnet_mask != lease->subnet_mask ||
851 client->lease->router != lease->router) {
852 r = DHCP_EVENT_IP_CHANGE;
855 client->lease = sd_dhcp_lease_unref(client->lease);
858 client->lease = lease;
861 log_dhcp_client(client, "ACK");
866 static uint64_t client_compute_timeout(sd_dhcp_client *client,
867 uint32_t lifetime, double factor) {
869 assert(client->request_sent);
872 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
873 + (random_u32() & 0x1fffff);
876 static int client_set_lease_timeouts(sd_dhcp_client *client) {
878 uint64_t lifetime_timeout;
881 char time_string[FORMAT_TIMESPAN_MAX];
885 assert(client->event);
886 assert(client->lease);
887 assert(client->lease->lifetime);
889 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
890 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
891 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
893 /* don't set timers for infinite leases */
894 if (client->lease->lifetime == 0xffffffff)
897 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
900 assert(client->request_sent <= time_now);
902 /* convert the various timeouts from relative (secs) to absolute (usecs) */
903 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
904 if (client->lease->t1 && client->lease->t2) {
905 /* both T1 and T2 are given */
906 if (client->lease->t1 < client->lease->t2 &&
907 client->lease->t2 < client->lease->lifetime) {
908 /* they are both valid */
909 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
910 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
913 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
914 client->lease->t2 = (client->lease->lifetime * 7) / 8;
915 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
916 client->lease->t1 = client->lease->lifetime / 2;
918 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
919 /* only T2 is given, and it is valid */
920 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
921 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
922 client->lease->t1 = client->lease->lifetime / 2;
923 if (t2_timeout <= t1_timeout) {
924 /* the computed T1 would be invalid, so discard T2 */
925 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
926 client->lease->t2 = (client->lease->lifetime * 7) / 8;
928 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
929 /* only T1 is given, and it is valid */
930 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
931 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
932 client->lease->t2 = (client->lease->lifetime * 7) / 8;
933 if (t2_timeout <= t1_timeout) {
934 /* the computed T2 would be invalid, so discard T1 */
935 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
936 client->lease->t2 = client->lease->lifetime / 2;
939 /* fall back to the default timeouts */
940 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
941 client->lease->t1 = client->lease->lifetime / 2;
942 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
943 client->lease->t2 = (client->lease->lifetime * 7) / 8;
946 /* arm lifetime timeout */
947 r = sd_event_add_time(client->event, &client->timeout_expire,
949 lifetime_timeout, 10 * USEC_PER_MSEC,
950 client_timeout_expire, client);
954 r = sd_event_source_set_priority(client->timeout_expire,
955 client->event_priority);
959 log_dhcp_client(client, "lease expires in %s",
960 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
961 lifetime_timeout - time_now, 0));
963 /* don't arm earlier timeouts if this has already expired */
964 if (lifetime_timeout <= time_now)
968 r = sd_event_add_time(client->event,
973 client_timeout_t2, client);
977 r = sd_event_source_set_priority(client->timeout_t2,
978 client->event_priority);
982 log_dhcp_client(client, "T2 expires in %s",
983 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
984 t2_timeout - time_now, 0));
986 /* don't arm earlier timeout if this has already expired */
987 if (t2_timeout <= time_now)
991 r = sd_event_add_time(client->event,
994 t1_timeout, 10 * USEC_PER_MSEC,
995 client_timeout_t1, client);
999 r = sd_event_source_set_priority(client->timeout_t1,
1000 client->event_priority);
1004 log_dhcp_client(client, "T1 expires in %s",
1005 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1006 t1_timeout - time_now, 0));
1011 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1013 int r = 0, notify_event = 0;
1016 assert(client->event);
1019 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1020 log_dhcp_client(client, "not a DHCP message: ignoring");
1024 if (message->op != BOOTREPLY) {
1025 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1029 if (be32toh(message->xid) != client->xid) {
1030 log_dhcp_client(client, "received xid (%u) does not match "
1031 "expected (%u): ignoring",
1032 be32toh(message->xid), client->xid);
1036 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1037 log_dhcp_client(client, "not an ethernet packet");
1041 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1043 log_dhcp_client(client, "received chaddr does not match "
1044 "expected: ignoring");
1048 switch (client->state) {
1049 case DHCP_STATE_SELECTING:
1051 r = client_handle_offer(client, message, len);
1054 client->timeout_resend =
1055 sd_event_source_unref(client->timeout_resend);
1057 client->state = DHCP_STATE_REQUESTING;
1058 client->attempt = 1;
1060 r = sd_event_add_time(client->event,
1061 &client->timeout_resend,
1064 client_timeout_resend, client);
1068 r = sd_event_source_set_priority(client->timeout_resend,
1069 client->event_priority);
1072 } else if (r == -ENOMSG)
1073 /* invalid message, let's ignore it */
1078 case DHCP_STATE_REBOOTING:
1079 case DHCP_STATE_REQUESTING:
1080 case DHCP_STATE_RENEWING:
1081 case DHCP_STATE_REBINDING:
1083 r = client_handle_ack(client, message, len);
1084 if (r == DHCP_EVENT_NO_LEASE) {
1086 client->timeout_resend =
1087 sd_event_source_unref(client->timeout_resend);
1089 if (client->state == DHCP_STATE_REBOOTING) {
1090 r = client_initialize(client);
1094 r = client_start(client);
1100 } else if (r >= 0) {
1101 client->timeout_resend =
1102 sd_event_source_unref(client->timeout_resend);
1104 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1105 DHCP_STATE_REBOOTING))
1106 notify_event = DHCP_EVENT_IP_ACQUIRE;
1107 else if (r != DHCP_EVENT_IP_ACQUIRE)
1110 client->state = DHCP_STATE_BOUND;
1111 client->attempt = 1;
1113 client->last_addr = client->lease->address;
1115 r = client_set_lease_timeouts(client);
1120 client = client_notify(client, notify_event);
1122 client->state == DHCP_STATE_STOPPED)
1126 client->receive_message =
1127 sd_event_source_unref(client->receive_message);
1128 client->fd = safe_close(client->fd);
1129 } else if (r == -ENOMSG)
1130 /* invalid message, let's ignore it */
1135 case DHCP_STATE_INIT:
1136 case DHCP_STATE_INIT_REBOOT:
1137 case DHCP_STATE_BOUND:
1141 case DHCP_STATE_STOPPED:
1147 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1148 client_stop(client, r);
1153 static int client_receive_message_udp(sd_event_source *s, int fd,
1154 uint32_t revents, void *userdata) {
1155 sd_dhcp_client *client = userdata;
1156 _cleanup_free_ DHCPMessage *message = NULL;
1157 int buflen = 0, len, r;
1162 r = ioctl(fd, FIONREAD, &buflen);
1163 if (r < 0 || buflen <= 0)
1164 buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
1166 message = malloc0(buflen);
1170 len = read(fd, message, buflen);
1172 log_dhcp_client(client, "could not receive message from UDP "
1173 "socket: %s", strerror(errno));
1175 } else if ((size_t)len < sizeof(DHCPMessage))
1178 return client_handle_message(client, message, len);
1181 static int client_receive_message_raw(sd_event_source *s, int fd,
1182 uint32_t revents, void *userdata) {
1183 sd_dhcp_client *client = userdata;
1184 _cleanup_free_ DHCPPacket *packet = NULL;
1185 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1186 struct iovec iov = {};
1187 struct msghdr msg = {
1190 .msg_control = cmsgbuf,
1191 .msg_controllen = sizeof(cmsgbuf),
1193 struct cmsghdr *cmsg;
1194 bool checksum = true;
1195 int buflen = 0, len, r;
1200 r = ioctl(fd, FIONREAD, &buflen);
1201 if (r < 0 || buflen <= 0)
1202 buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
1204 packet = malloc0(buflen);
1208 iov.iov_base = packet;
1209 iov.iov_len = buflen;
1211 len = recvmsg(fd, &msg, 0);
1213 log_dhcp_client(client, "could not receive message from raw "
1214 "socket: %s", strerror(errno));
1216 } else if ((size_t)len < sizeof(DHCPPacket))
1219 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1220 if (cmsg->cmsg_level == SOL_PACKET &&
1221 cmsg->cmsg_type == PACKET_AUXDATA &&
1222 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1223 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1225 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1230 r = dhcp_packet_verify_headers(packet, len, checksum);
1234 len -= DHCP_IP_UDP_SIZE;
1236 return client_handle_message(client, &packet->dhcp, len);
1239 int sd_dhcp_client_start(sd_dhcp_client *client) {
1242 assert_return(client, -EINVAL);
1244 r = client_initialize(client);
1248 if (client->last_addr)
1249 client->state = DHCP_STATE_INIT_REBOOT;
1251 return client_start(client);
1254 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1255 assert_return(client, -EINVAL);
1257 if (client_stop(client, DHCP_EVENT_STOP))
1258 client->state = DHCP_STATE_STOPPED;
1263 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1267 assert_return(client, -EINVAL);
1268 assert_return(!client->event, -EBUSY);
1271 client->event = sd_event_ref(event);
1273 r = sd_event_default(&client->event);
1278 client->event_priority = priority;
1283 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1284 assert_return(client, -EINVAL);
1286 client->event = sd_event_unref(client->event);
1291 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1295 return client->event;
1298 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1300 assert_se(REFCNT_INC(client->n_ref) >= 2);
1305 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1306 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1307 log_dhcp_client(client, "UNREF");
1309 client_initialize(client);
1311 client->receive_message =
1312 sd_event_source_unref(client->receive_message);
1314 sd_dhcp_client_detach_event(client);
1316 free(client->req_opts);
1325 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
1326 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
1328 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1329 _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1331 assert_return(ret, -EINVAL);
1333 client = new0(sd_dhcp_client, 1);
1337 client->n_ref = REFCNT_INIT;
1338 client->state = DHCP_STATE_INIT;
1341 client->attempt = 1;
1343 client->req_opts_size = ELEMENTSOF(default_req_opts);
1345 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1346 if (!client->req_opts)