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;
52 size_t req_opts_allocated;
57 struct ether_addr mac_addr;
64 sd_event_source *timeout_t1;
65 sd_event_source *timeout_t2;
66 sd_event_source *timeout_expire;
67 sd_dhcp_client_cb_t cb;
72 static const uint8_t default_req_opts[] = {
73 DHCP_OPTION_SUBNET_MASK,
75 DHCP_OPTION_HOST_NAME,
76 DHCP_OPTION_DOMAIN_NAME,
77 DHCP_OPTION_DOMAIN_NAME_SERVER,
78 DHCP_OPTION_NTP_SERVER,
81 static int client_receive_message_raw(sd_event_source *s, int fd,
82 uint32_t revents, void *userdata);
83 static int client_receive_message_udp(sd_event_source *s, int fd,
84 uint32_t revents, void *userdata);
85 static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error);
87 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
89 assert_return(client, -EINVAL);
92 client->userdata = userdata;
97 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
100 assert_return(client, -EINVAL);
101 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
102 DHCP_STATE_STOPPED), -EBUSY);
105 case DHCP_OPTION_PAD:
106 case DHCP_OPTION_OVERLOAD:
107 case DHCP_OPTION_MESSAGE_TYPE:
108 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
109 case DHCP_OPTION_END:
116 for (i = 0; i < client->req_opts_size; i++)
117 if (client->req_opts[i] == option)
120 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
121 client->req_opts_size + 1))
124 client->req_opts[client->req_opts_size++] = option;
129 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
130 const struct in_addr *last_addr) {
131 assert_return(client, -EINVAL);
132 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
133 DHCP_STATE_STOPPED), -EBUSY);
136 client->last_addr = last_addr->s_addr;
138 client->last_addr = INADDR_ANY;
143 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
144 assert_return(client, -EINVAL);
145 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
146 DHCP_STATE_STOPPED), -EBUSY);
147 assert_return(interface_index > 0, -EINVAL);
149 client->index = interface_index;
154 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
155 const struct ether_addr *addr) {
156 bool need_restart = false;
158 assert_return(client, -EINVAL);
159 assert_return(addr, -EINVAL);
161 if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
164 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
165 log_dhcp_client(client, "Changing MAC address on running DHCP "
166 "client, restarting");
168 client = client_stop(client, DHCP_EVENT_STOP);
174 memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
175 client->client_id.type = 0x01;
177 if (need_restart && client->state != DHCP_STATE_STOPPED)
178 sd_dhcp_client_start(client);
183 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
184 assert_return(client, -EINVAL);
185 assert_return(ret, -EINVAL);
187 if (client->state != DHCP_STATE_BOUND &&
188 client->state != DHCP_STATE_RENEWING &&
189 client->state != DHCP_STATE_REBINDING)
190 return -EADDRNOTAVAIL;
192 *ret = sd_dhcp_lease_ref(client->lease);
197 static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) {
199 client = sd_dhcp_client_ref(client);
200 client->cb(client, event, client->userdata);
201 client = sd_dhcp_client_unref(client);
207 static int client_initialize(sd_dhcp_client *client) {
208 assert_return(client, -EINVAL);
210 client->receive_message =
211 sd_event_source_unref(client->receive_message);
213 client->fd = asynchronous_close(client->fd);
215 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
217 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
218 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
219 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
223 client->state = DHCP_STATE_INIT;
227 client->lease = sd_dhcp_lease_unref(client->lease);
232 static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
233 assert_return(client, NULL);
236 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
239 case DHCP_EVENT_STOP:
240 log_dhcp_client(client, "STOPPED");
242 case DHCP_EVENT_NO_LEASE:
243 log_dhcp_client(client, "STOPPED: No lease");
246 log_dhcp_client(client, "STOPPED: Unknown reason");
251 client = client_notify(client, error);
254 client_initialize(client);
259 static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
260 uint8_t type, size_t *_optlen, size_t *_optoffset) {
261 _cleanup_free_ DHCPPacket *packet;
262 size_t optlen, optoffset, size;
267 assert(client->secs);
271 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
273 optlen = DHCP_MIN_OPTIONS_SIZE;
274 size = sizeof(DHCPPacket) + optlen;
276 packet = malloc0(size);
280 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
285 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
286 refuse to issue an DHCP lease if 'secs' is set to zero */
287 packet->dhcp.secs = htobe16(client->secs);
289 /* RFC2132 section 4.1
290 A client that cannot receive unicast IP datagrams until its protocol
291 software has been configured with an IP address SHOULD set the
292 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
293 DHCPREQUEST messages that client sends. The BROADCAST bit will
294 provide a hint to the DHCP server and BOOTP relay agent to broadcast
295 any messages to the client on the client's subnet. */
296 packet->dhcp.flags = htobe16(0x8000);
298 /* RFC2132 section 4.1.1:
299 The client MUST include its hardware address in the ’chaddr’ field, if
300 necessary for delivery of DHCP reply messages.
302 memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
304 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
305 Identifier option is not set */
306 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
307 DHCP_OPTION_CLIENT_IDENTIFIER,
308 sizeof(client->client_id), &client->client_id);
313 /* RFC2131 section 3.5:
314 in its initial DHCPDISCOVER or DHCPREQUEST message, a
315 client may provide the server with a list of specific
316 parameters the client is interested in. If the client
317 includes a list of parameters in a DHCPDISCOVER message,
318 it MUST include that list in any subsequent DHCPREQUEST
321 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
322 DHCP_OPTION_PARAMETER_REQUEST_LIST,
323 client->req_opts_size, client->req_opts);
327 /* RFC2131 section 3.5:
328 The client SHOULD include the ’maximum DHCP message size’ option to
329 let the server know how large the server may make its DHCP messages.
331 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
332 than the defined default size unless the Maximum Messge Size option
335 max_size = htobe16(size);
336 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
337 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
343 *_optoffset = optoffset;
350 static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
352 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
353 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
355 return dhcp_network_send_raw_socket(client->fd, &client->link,
359 static int client_send_discover(sd_dhcp_client *client) {
360 _cleanup_free_ DHCPPacket *discover = NULL;
361 size_t optoffset, optlen;
366 assert(client->state == DHCP_STATE_INIT ||
367 client->state == DHCP_STATE_SELECTING);
369 /* See RFC2131 section 4.4.1 */
371 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
374 assert(time_now >= client->start_time);
376 /* seconds between sending first and last DISCOVER
377 * must always be strictly positive to deal with broken servers */
378 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
380 r = client_message_init(client, &discover, DHCP_DISCOVER,
381 &optlen, &optoffset);
385 /* the client may suggest values for the network address
386 and lease time in the DHCPDISCOVER message. The client may include
387 the ’requested IP address’ option to suggest that a particular IP
388 address be assigned, and may include the ’IP address lease time’
389 option to suggest the lease time it would like.
391 if (client->last_addr != INADDR_ANY) {
392 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
393 DHCP_OPTION_REQUESTED_IP_ADDRESS,
394 4, &client->last_addr);
399 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
400 DHCP_OPTION_END, 0, NULL);
402 /* We currently ignore:
403 The client SHOULD wait a random time between one and ten seconds to
404 desynchronize the use of DHCP at startup.
406 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
410 log_dhcp_client(client, "DISCOVER");
415 static int client_send_request(sd_dhcp_client *client) {
416 _cleanup_free_ DHCPPacket *request;
417 size_t optoffset, optlen;
420 r = client_message_init(client, &request, DHCP_REQUEST,
421 &optlen, &optoffset);
425 switch (client->state) {
426 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
427 SELECTING should be REQUESTING)
430 case DHCP_STATE_REQUESTING:
431 /* Client inserts the address of the selected server in ’server
432 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
433 filled in with the yiaddr value from the chosen DHCPOFFER.
436 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
437 DHCP_OPTION_SERVER_IDENTIFIER,
438 4, &client->lease->server_address);
442 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
443 DHCP_OPTION_REQUESTED_IP_ADDRESS,
444 4, &client->lease->address);
450 case DHCP_STATE_INIT_REBOOT:
451 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
452 option MUST be filled in with client’s notion of its previously
453 assigned address. ’ciaddr’ MUST be zero.
455 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
456 DHCP_OPTION_REQUESTED_IP_ADDRESS,
457 4, &client->last_addr);
462 case DHCP_STATE_RENEWING:
463 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
464 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
465 client’s IP address.
469 case DHCP_STATE_REBINDING:
470 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
471 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
472 client’s IP address.
474 This message MUST be broadcast to the 0xffffffff IP broadcast address.
476 request->dhcp.ciaddr = client->lease->address;
480 case DHCP_STATE_INIT:
481 case DHCP_STATE_SELECTING:
482 case DHCP_STATE_REBOOTING:
483 case DHCP_STATE_BOUND:
484 case DHCP_STATE_STOPPED:
488 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
489 DHCP_OPTION_END, 0, NULL);
493 if (client->state == DHCP_STATE_RENEWING) {
494 r = dhcp_network_send_udp_socket(client->fd,
495 client->lease->server_address,
498 sizeof(DHCPMessage) + optoffset);
500 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
505 switch (client->state) {
506 case DHCP_STATE_REQUESTING:
507 log_dhcp_client(client, "REQUEST (requesting)");
509 case DHCP_STATE_INIT_REBOOT:
510 log_dhcp_client(client, "REQUEST (init-reboot)");
512 case DHCP_STATE_RENEWING:
513 log_dhcp_client(client, "REQUEST (renewing)");
515 case DHCP_STATE_REBINDING:
516 log_dhcp_client(client, "REQUEST (rebinding)");
519 log_dhcp_client(client, "REQUEST (invalid)");
526 static int client_start(sd_dhcp_client *client);
528 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
530 sd_dhcp_client *client = userdata;
531 usec_t next_timeout = 0;
538 assert(client->event);
540 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
544 switch (client->state) {
545 case DHCP_STATE_RENEWING:
547 time_left = (client->lease->t2 - client->lease->t1) / 2;
551 next_timeout = time_now + time_left * USEC_PER_SEC;
555 case DHCP_STATE_REBINDING:
557 time_left = (client->lease->lifetime - client->lease->t2) / 2;
561 next_timeout = time_now + time_left * USEC_PER_SEC;
564 case DHCP_STATE_REBOOTING:
565 /* start over as we did not receive a timely ack or nak */
566 r = client_initialize(client);
570 r = client_start(client);
574 log_dhcp_client(client, "REBOOTED");
578 case DHCP_STATE_INIT:
579 case DHCP_STATE_INIT_REBOOT:
580 case DHCP_STATE_SELECTING:
581 case DHCP_STATE_REQUESTING:
582 case DHCP_STATE_BOUND:
584 if (client->attempt < 64)
585 client->attempt *= 2;
587 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
591 case DHCP_STATE_STOPPED:
596 next_timeout += (random_u32() & 0x1fffff);
598 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
600 r = sd_event_add_time(client->event,
601 &client->timeout_resend,
603 next_timeout, 10 * USEC_PER_MSEC,
604 client_timeout_resend, client);
608 r = sd_event_source_set_priority(client->timeout_resend,
609 client->event_priority);
613 switch (client->state) {
614 case DHCP_STATE_INIT:
615 r = client_send_discover(client);
617 client->state = DHCP_STATE_SELECTING;
620 if (client->attempt >= 64)
626 case DHCP_STATE_SELECTING:
627 r = client_send_discover(client);
628 if (r < 0 && client->attempt >= 64)
633 case DHCP_STATE_INIT_REBOOT:
634 case DHCP_STATE_REQUESTING:
635 case DHCP_STATE_RENEWING:
636 case DHCP_STATE_REBINDING:
637 r = client_send_request(client);
638 if (r < 0 && client->attempt >= 64)
641 if (client->state == DHCP_STATE_INIT_REBOOT)
642 client->state = DHCP_STATE_REBOOTING;
644 client->request_sent = time_now;
648 case DHCP_STATE_REBOOTING:
649 case DHCP_STATE_BOUND:
653 case DHCP_STATE_STOPPED:
661 client_stop(client, r);
663 /* Errors were dealt with when stopping the client, don't spill
664 errors into the event loop handler */
668 static int client_initialize_events(sd_dhcp_client *client,
669 sd_event_io_handler_t io_callback) {
673 assert(client->event);
675 r = sd_event_add_io(client->event, &client->receive_message,
676 client->fd, EPOLLIN, io_callback,
681 r = sd_event_source_set_priority(client->receive_message,
682 client->event_priority);
686 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
688 r = sd_event_add_time(client->event,
689 &client->timeout_resend,
692 client_timeout_resend, client);
696 r = sd_event_source_set_priority(client->timeout_resend,
697 client->event_priority);
701 client_stop(client, r);
707 static int client_start(sd_dhcp_client *client) {
710 assert_return(client, -EINVAL);
711 assert_return(client->event, -EINVAL);
712 assert_return(client->index > 0, -EINVAL);
713 assert_return(client->fd < 0, -EBUSY);
714 assert_return(client->xid == 0, -EINVAL);
715 assert_return(client->state == DHCP_STATE_INIT ||
716 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
718 client->xid = random_u32();
720 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
722 client_stop(client, r);
727 if (client->state == DHCP_STATE_INIT) {
728 client->start_time = now(CLOCK_MONOTONIC);
732 return client_initialize_events(client, client_receive_message_raw);
735 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
737 sd_dhcp_client *client = userdata;
739 log_dhcp_client(client, "EXPIRED");
741 client = client_notify(client, DHCP_EVENT_EXPIRED);
743 /* lease was lost, start over if not freed or stopped in callback */
744 if (client && client->state != DHCP_STATE_STOPPED) {
745 client_initialize(client);
746 client_start(client);
752 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
753 sd_dhcp_client *client = userdata;
756 client->receive_message = sd_event_source_unref(client->receive_message);
757 client->fd = asynchronous_close(client->fd);
759 client->state = DHCP_STATE_REBINDING;
762 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
764 client_stop(client, r);
769 return client_initialize_events(client, client_receive_message_raw);
772 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
774 sd_dhcp_client *client = userdata;
777 client->state = DHCP_STATE_RENEWING;
780 r = dhcp_network_bind_udp_socket(client->lease->address,
783 client_stop(client, r);
789 return client_initialize_events(client, client_receive_message_udp);
792 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
794 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
797 r = dhcp_lease_new(&lease);
801 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
802 if (r != DHCP_OFFER) {
803 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
807 lease->next_server = offer->siaddr;
809 lease->address = offer->yiaddr;
811 if (lease->address == INADDR_ANY ||
812 lease->server_address == INADDR_ANY ||
813 lease->lifetime == 0) {
814 log_dhcp_client(client, "receieved lease lacks address, server "
815 "address or lease lifetime, ignoring");
819 if (lease->subnet_mask == INADDR_ANY) {
820 r = dhcp_lease_set_default_subnet_mask(lease);
822 log_dhcp_client(client, "receieved lease lacks subnet "
823 "mask, and a fallback one can not be "
824 "generated, ignoring");
829 sd_dhcp_lease_unref(client->lease);
830 client->lease = lease;
833 log_dhcp_client(client, "OFFER");
838 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
840 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
843 r = dhcp_lease_new(&lease);
847 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
849 log_dhcp_client(client, "NAK");
850 return DHCP_EVENT_NO_LEASE;
854 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
858 lease->next_server = ack->siaddr;
860 lease->address = ack->yiaddr;
862 if (lease->address == INADDR_ANY ||
863 lease->server_address == INADDR_ANY ||
864 lease->lifetime == 0) {
865 log_dhcp_client(client, "receieved lease lacks address, server "
866 "address or lease lifetime, ignoring");
870 if (lease->subnet_mask == INADDR_ANY) {
871 r = dhcp_lease_set_default_subnet_mask(lease);
873 log_dhcp_client(client, "receieved lease lacks subnet "
874 "mask, and a fallback one can not be "
875 "generated, ignoring");
880 r = DHCP_EVENT_IP_ACQUIRE;
882 if (client->lease->address != lease->address ||
883 client->lease->subnet_mask != lease->subnet_mask ||
884 client->lease->router != lease->router) {
885 r = DHCP_EVENT_IP_CHANGE;
888 client->lease = sd_dhcp_lease_unref(client->lease);
891 client->lease = lease;
894 log_dhcp_client(client, "ACK");
899 static uint64_t client_compute_timeout(sd_dhcp_client *client,
900 uint32_t lifetime, double factor) {
902 assert(client->request_sent);
905 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
906 + (random_u32() & 0x1fffff);
909 static int client_set_lease_timeouts(sd_dhcp_client *client) {
911 uint64_t lifetime_timeout;
914 char time_string[FORMAT_TIMESPAN_MAX];
918 assert(client->event);
919 assert(client->lease);
920 assert(client->lease->lifetime);
922 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
923 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
924 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
926 /* don't set timers for infinite leases */
927 if (client->lease->lifetime == 0xffffffff)
930 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
933 assert(client->request_sent <= time_now);
935 /* convert the various timeouts from relative (secs) to absolute (usecs) */
936 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
937 if (client->lease->t1 && client->lease->t2) {
938 /* both T1 and T2 are given */
939 if (client->lease->t1 < client->lease->t2 &&
940 client->lease->t2 < client->lease->lifetime) {
941 /* they are both valid */
942 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
943 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 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
949 client->lease->t1 = client->lease->lifetime / 2;
951 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
952 /* only T2 is given, and it is valid */
953 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
954 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
955 client->lease->t1 = client->lease->lifetime / 2;
956 if (t2_timeout <= t1_timeout) {
957 /* the computed T1 would be invalid, so discard T2 */
958 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
959 client->lease->t2 = (client->lease->lifetime * 7) / 8;
961 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
962 /* only T1 is given, and it is valid */
963 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
964 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
965 client->lease->t2 = (client->lease->lifetime * 7) / 8;
966 if (t2_timeout <= t1_timeout) {
967 /* the computed T2 would be invalid, so discard T1 */
968 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
969 client->lease->t2 = client->lease->lifetime / 2;
972 /* fall back to the default timeouts */
973 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
974 client->lease->t1 = client->lease->lifetime / 2;
975 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
976 client->lease->t2 = (client->lease->lifetime * 7) / 8;
979 /* arm lifetime timeout */
980 r = sd_event_add_time(client->event, &client->timeout_expire,
982 lifetime_timeout, 10 * USEC_PER_MSEC,
983 client_timeout_expire, client);
987 r = sd_event_source_set_priority(client->timeout_expire,
988 client->event_priority);
992 log_dhcp_client(client, "lease expires in %s",
993 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
994 lifetime_timeout - time_now, 0));
996 /* don't arm earlier timeouts if this has already expired */
997 if (lifetime_timeout <= time_now)
1000 /* arm T2 timeout */
1001 r = sd_event_add_time(client->event,
1002 &client->timeout_t2,
1006 client_timeout_t2, client);
1010 r = sd_event_source_set_priority(client->timeout_t2,
1011 client->event_priority);
1015 log_dhcp_client(client, "T2 expires in %s",
1016 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1017 t2_timeout - time_now, 0));
1019 /* don't arm earlier timeout if this has already expired */
1020 if (t2_timeout <= time_now)
1023 /* arm T1 timeout */
1024 r = sd_event_add_time(client->event,
1025 &client->timeout_t1,
1027 t1_timeout, 10 * USEC_PER_MSEC,
1028 client_timeout_t1, client);
1032 r = sd_event_source_set_priority(client->timeout_t1,
1033 client->event_priority);
1037 log_dhcp_client(client, "T1 expires in %s",
1038 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1039 t1_timeout - time_now, 0));
1044 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1046 int r = 0, notify_event = 0;
1049 assert(client->event);
1052 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1053 log_dhcp_client(client, "not a DHCP message: ignoring");
1057 if (message->op != BOOTREPLY) {
1058 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1062 if (be32toh(message->xid) != client->xid) {
1063 log_dhcp_client(client, "received xid (%u) does not match "
1064 "expected (%u): ignoring",
1065 be32toh(message->xid), client->xid);
1069 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1070 log_dhcp_client(client, "not an ethernet packet");
1074 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1076 log_dhcp_client(client, "received chaddr does not match "
1077 "expected: ignoring");
1081 switch (client->state) {
1082 case DHCP_STATE_SELECTING:
1084 r = client_handle_offer(client, message, len);
1087 client->timeout_resend =
1088 sd_event_source_unref(client->timeout_resend);
1090 client->state = DHCP_STATE_REQUESTING;
1091 client->attempt = 1;
1093 r = sd_event_add_time(client->event,
1094 &client->timeout_resend,
1097 client_timeout_resend, client);
1101 r = sd_event_source_set_priority(client->timeout_resend,
1102 client->event_priority);
1105 } else if (r == -ENOMSG)
1106 /* invalid message, let's ignore it */
1111 case DHCP_STATE_REBOOTING:
1112 case DHCP_STATE_REQUESTING:
1113 case DHCP_STATE_RENEWING:
1114 case DHCP_STATE_REBINDING:
1116 r = client_handle_ack(client, message, len);
1117 if (r == DHCP_EVENT_NO_LEASE) {
1119 client->timeout_resend =
1120 sd_event_source_unref(client->timeout_resend);
1122 if (client->state == DHCP_STATE_REBOOTING) {
1123 r = client_initialize(client);
1127 r = client_start(client);
1131 log_dhcp_client(client, "REBOOTED");
1135 } else if (r >= 0) {
1136 client->timeout_resend =
1137 sd_event_source_unref(client->timeout_resend);
1139 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1140 DHCP_STATE_REBOOTING))
1141 notify_event = DHCP_EVENT_IP_ACQUIRE;
1142 else if (r != DHCP_EVENT_IP_ACQUIRE)
1145 client->state = DHCP_STATE_BOUND;
1146 client->attempt = 1;
1148 client->last_addr = client->lease->address;
1150 r = client_set_lease_timeouts(client);
1155 client = client_notify(client, notify_event);
1157 client->state == DHCP_STATE_STOPPED)
1161 client->receive_message =
1162 sd_event_source_unref(client->receive_message);
1163 client->fd = asynchronous_close(client->fd);
1164 } else if (r == -ENOMSG)
1165 /* invalid message, let's ignore it */
1170 case DHCP_STATE_INIT:
1171 case DHCP_STATE_INIT_REBOOT:
1172 case DHCP_STATE_BOUND:
1176 case DHCP_STATE_STOPPED:
1182 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1183 client_stop(client, r);
1188 static int client_receive_message_udp(sd_event_source *s, int fd,
1189 uint32_t revents, void *userdata) {
1190 sd_dhcp_client *client = userdata;
1191 _cleanup_free_ DHCPMessage *message = NULL;
1192 int buflen = 0, len, r;
1197 r = ioctl(fd, FIONREAD, &buflen);
1202 /* this can't be right */
1205 message = malloc0(buflen);
1209 len = read(fd, message, buflen);
1211 log_dhcp_client(client, "could not receive message from UDP "
1214 } else if ((size_t)len < sizeof(DHCPMessage))
1217 return client_handle_message(client, message, len);
1220 static int client_receive_message_raw(sd_event_source *s, int fd,
1221 uint32_t revents, void *userdata) {
1222 sd_dhcp_client *client = userdata;
1223 _cleanup_free_ DHCPPacket *packet = NULL;
1224 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1225 struct iovec iov = {};
1226 struct msghdr msg = {
1229 .msg_control = cmsgbuf,
1230 .msg_controllen = sizeof(cmsgbuf),
1232 struct cmsghdr *cmsg;
1233 bool checksum = true;
1234 int buflen = 0, len, r;
1239 r = ioctl(fd, FIONREAD, &buflen);
1244 /* this can't be right */
1247 packet = malloc0(buflen);
1251 iov.iov_base = packet;
1252 iov.iov_len = buflen;
1254 len = recvmsg(fd, &msg, 0);
1256 log_dhcp_client(client, "could not receive message from raw "
1259 } else if ((size_t)len < sizeof(DHCPPacket))
1262 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1263 if (cmsg->cmsg_level == SOL_PACKET &&
1264 cmsg->cmsg_type == PACKET_AUXDATA &&
1265 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1266 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1268 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1273 r = dhcp_packet_verify_headers(packet, len, checksum);
1277 len -= DHCP_IP_UDP_SIZE;
1279 return client_handle_message(client, &packet->dhcp, len);
1282 int sd_dhcp_client_start(sd_dhcp_client *client) {
1285 assert_return(client, -EINVAL);
1287 r = client_initialize(client);
1291 if (client->last_addr)
1292 client->state = DHCP_STATE_INIT_REBOOT;
1294 r = client_start(client);
1296 log_dhcp_client(client, "STARTED on ifindex %u with address %s",
1298 ether_ntoa(&client->client_id.mac_addr));
1303 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1304 assert_return(client, -EINVAL);
1306 if (client_stop(client, DHCP_EVENT_STOP))
1307 client->state = DHCP_STATE_STOPPED;
1312 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1316 assert_return(client, -EINVAL);
1317 assert_return(!client->event, -EBUSY);
1320 client->event = sd_event_ref(event);
1322 r = sd_event_default(&client->event);
1327 client->event_priority = priority;
1332 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1333 assert_return(client, -EINVAL);
1335 client->event = sd_event_unref(client->event);
1340 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1344 return client->event;
1347 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1349 assert_se(REFCNT_INC(client->n_ref) >= 2);
1354 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1355 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1356 log_dhcp_client(client, "UNREF");
1358 client_initialize(client);
1360 client->receive_message =
1361 sd_event_source_unref(client->receive_message);
1363 sd_dhcp_client_detach_event(client);
1365 sd_dhcp_lease_unref(client->lease);
1367 free(client->req_opts);
1376 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
1377 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
1379 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1380 _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1382 assert_return(ret, -EINVAL);
1384 client = new0(sd_dhcp_client, 1);
1388 client->n_ref = REFCNT_INIT;
1389 client->state = DHCP_STATE_INIT;
1392 client->attempt = 1;
1394 client->req_opts_size = ELEMENTSOF(default_req_opts);
1396 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1397 if (!client->req_opts)