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);
404 /* We currently ignore:
405 The client SHOULD wait a random time between one and ten seconds to
406 desynchronize the use of DHCP at startup.
408 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
412 log_dhcp_client(client, "DISCOVER");
417 static int client_send_request(sd_dhcp_client *client) {
418 _cleanup_free_ DHCPPacket *request = NULL;
419 size_t optoffset, optlen;
422 r = client_message_init(client, &request, DHCP_REQUEST,
423 &optlen, &optoffset);
427 switch (client->state) {
428 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
429 SELECTING should be REQUESTING)
432 case DHCP_STATE_REQUESTING:
433 /* Client inserts the address of the selected server in ’server
434 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
435 filled in with the yiaddr value from the chosen DHCPOFFER.
438 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
439 DHCP_OPTION_SERVER_IDENTIFIER,
440 4, &client->lease->server_address);
444 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
445 DHCP_OPTION_REQUESTED_IP_ADDRESS,
446 4, &client->lease->address);
452 case DHCP_STATE_INIT_REBOOT:
453 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
454 option MUST be filled in with client’s notion of its previously
455 assigned address. ’ciaddr’ MUST be zero.
457 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
458 DHCP_OPTION_REQUESTED_IP_ADDRESS,
459 4, &client->last_addr);
464 case DHCP_STATE_RENEWING:
465 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
466 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
467 client’s IP address.
471 case DHCP_STATE_REBINDING:
472 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
473 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
474 client’s IP address.
476 This message MUST be broadcast to the 0xffffffff IP broadcast address.
478 request->dhcp.ciaddr = client->lease->address;
482 case DHCP_STATE_INIT:
483 case DHCP_STATE_SELECTING:
484 case DHCP_STATE_REBOOTING:
485 case DHCP_STATE_BOUND:
486 case DHCP_STATE_STOPPED:
490 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
491 DHCP_OPTION_END, 0, NULL);
495 if (client->state == DHCP_STATE_RENEWING) {
496 r = dhcp_network_send_udp_socket(client->fd,
497 client->lease->server_address,
500 sizeof(DHCPMessage) + optoffset);
502 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
507 switch (client->state) {
508 case DHCP_STATE_REQUESTING:
509 log_dhcp_client(client, "REQUEST (requesting)");
511 case DHCP_STATE_INIT_REBOOT:
512 log_dhcp_client(client, "REQUEST (init-reboot)");
514 case DHCP_STATE_RENEWING:
515 log_dhcp_client(client, "REQUEST (renewing)");
517 case DHCP_STATE_REBINDING:
518 log_dhcp_client(client, "REQUEST (rebinding)");
521 log_dhcp_client(client, "REQUEST (invalid)");
528 static int client_start(sd_dhcp_client *client);
530 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
532 sd_dhcp_client *client = userdata;
533 usec_t next_timeout = 0;
540 assert(client->event);
542 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
546 switch (client->state) {
547 case DHCP_STATE_RENEWING:
549 time_left = (client->lease->t2 - client->lease->t1) / 2;
553 next_timeout = time_now + time_left * USEC_PER_SEC;
557 case DHCP_STATE_REBINDING:
559 time_left = (client->lease->lifetime - client->lease->t2) / 2;
563 next_timeout = time_now + time_left * USEC_PER_SEC;
566 case DHCP_STATE_REBOOTING:
567 /* start over as we did not receive a timely ack or nak */
568 r = client_initialize(client);
572 r = client_start(client);
576 log_dhcp_client(client, "REBOOTED");
580 case DHCP_STATE_INIT:
581 case DHCP_STATE_INIT_REBOOT:
582 case DHCP_STATE_SELECTING:
583 case DHCP_STATE_REQUESTING:
584 case DHCP_STATE_BOUND:
586 if (client->attempt < 64)
587 client->attempt *= 2;
589 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
593 case DHCP_STATE_STOPPED:
598 next_timeout += (random_u32() & 0x1fffff);
600 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
602 r = sd_event_add_time(client->event,
603 &client->timeout_resend,
605 next_timeout, 10 * USEC_PER_MSEC,
606 client_timeout_resend, client);
610 r = sd_event_source_set_priority(client->timeout_resend,
611 client->event_priority);
615 switch (client->state) {
616 case DHCP_STATE_INIT:
617 r = client_send_discover(client);
619 client->state = DHCP_STATE_SELECTING;
622 if (client->attempt >= 64)
628 case DHCP_STATE_SELECTING:
629 r = client_send_discover(client);
630 if (r < 0 && client->attempt >= 64)
635 case DHCP_STATE_INIT_REBOOT:
636 case DHCP_STATE_REQUESTING:
637 case DHCP_STATE_RENEWING:
638 case DHCP_STATE_REBINDING:
639 r = client_send_request(client);
640 if (r < 0 && client->attempt >= 64)
643 if (client->state == DHCP_STATE_INIT_REBOOT)
644 client->state = DHCP_STATE_REBOOTING;
646 client->request_sent = time_now;
650 case DHCP_STATE_REBOOTING:
651 case DHCP_STATE_BOUND:
655 case DHCP_STATE_STOPPED:
663 client_stop(client, r);
665 /* Errors were dealt with when stopping the client, don't spill
666 errors into the event loop handler */
670 static int client_initialize_events(sd_dhcp_client *client,
671 sd_event_io_handler_t io_callback) {
675 assert(client->event);
677 r = sd_event_add_io(client->event, &client->receive_message,
678 client->fd, EPOLLIN, io_callback,
683 r = sd_event_source_set_priority(client->receive_message,
684 client->event_priority);
688 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
690 r = sd_event_add_time(client->event,
691 &client->timeout_resend,
694 client_timeout_resend, client);
698 r = sd_event_source_set_priority(client->timeout_resend,
699 client->event_priority);
703 client_stop(client, r);
709 static int client_start(sd_dhcp_client *client) {
712 assert_return(client, -EINVAL);
713 assert_return(client->event, -EINVAL);
714 assert_return(client->index > 0, -EINVAL);
715 assert_return(client->fd < 0, -EBUSY);
716 assert_return(client->xid == 0, -EINVAL);
717 assert_return(client->state == DHCP_STATE_INIT ||
718 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
720 client->xid = random_u32();
722 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
724 client_stop(client, r);
729 if (client->state == DHCP_STATE_INIT) {
730 client->start_time = now(CLOCK_MONOTONIC);
734 return client_initialize_events(client, client_receive_message_raw);
737 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
739 sd_dhcp_client *client = userdata;
741 log_dhcp_client(client, "EXPIRED");
743 client = client_notify(client, DHCP_EVENT_EXPIRED);
745 /* lease was lost, start over if not freed or stopped in callback */
746 if (client && client->state != DHCP_STATE_STOPPED) {
747 client_initialize(client);
748 client_start(client);
754 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
755 sd_dhcp_client *client = userdata;
758 client->receive_message = sd_event_source_unref(client->receive_message);
759 client->fd = asynchronous_close(client->fd);
761 client->state = DHCP_STATE_REBINDING;
764 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
766 client_stop(client, r);
771 return client_initialize_events(client, client_receive_message_raw);
774 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
776 sd_dhcp_client *client = userdata;
779 client->state = DHCP_STATE_RENEWING;
782 r = dhcp_network_bind_udp_socket(client->lease->address,
785 client_stop(client, r);
791 return client_initialize_events(client, client_receive_message_udp);
794 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
796 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
799 r = dhcp_lease_new(&lease);
803 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
804 if (r != DHCP_OFFER) {
805 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
809 lease->next_server = offer->siaddr;
811 lease->address = offer->yiaddr;
813 if (lease->address == INADDR_ANY ||
814 lease->server_address == INADDR_ANY ||
815 lease->lifetime == 0) {
816 log_dhcp_client(client, "receieved lease lacks address, server "
817 "address or lease lifetime, ignoring");
821 if (lease->subnet_mask == INADDR_ANY) {
822 r = dhcp_lease_set_default_subnet_mask(lease);
824 log_dhcp_client(client, "receieved lease lacks subnet "
825 "mask, and a fallback one can not be "
826 "generated, ignoring");
831 sd_dhcp_lease_unref(client->lease);
832 client->lease = lease;
835 log_dhcp_client(client, "OFFER");
840 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
842 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
845 r = dhcp_lease_new(&lease);
849 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
851 log_dhcp_client(client, "NAK");
852 return DHCP_EVENT_NO_LEASE;
856 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
860 lease->next_server = ack->siaddr;
862 lease->address = ack->yiaddr;
864 if (lease->address == INADDR_ANY ||
865 lease->server_address == INADDR_ANY ||
866 lease->lifetime == 0) {
867 log_dhcp_client(client, "receieved lease lacks address, server "
868 "address or lease lifetime, ignoring");
872 if (lease->subnet_mask == INADDR_ANY) {
873 r = dhcp_lease_set_default_subnet_mask(lease);
875 log_dhcp_client(client, "receieved lease lacks subnet "
876 "mask, and a fallback one can not be "
877 "generated, ignoring");
882 r = DHCP_EVENT_IP_ACQUIRE;
884 if (client->lease->address != lease->address ||
885 client->lease->subnet_mask != lease->subnet_mask ||
886 client->lease->router != lease->router) {
887 r = DHCP_EVENT_IP_CHANGE;
890 client->lease = sd_dhcp_lease_unref(client->lease);
893 client->lease = lease;
896 log_dhcp_client(client, "ACK");
901 static uint64_t client_compute_timeout(sd_dhcp_client *client,
902 uint32_t lifetime, double factor) {
904 assert(client->request_sent);
907 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
908 + (random_u32() & 0x1fffff);
911 static int client_set_lease_timeouts(sd_dhcp_client *client) {
913 uint64_t lifetime_timeout;
916 char time_string[FORMAT_TIMESPAN_MAX];
920 assert(client->event);
921 assert(client->lease);
922 assert(client->lease->lifetime);
924 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
925 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
926 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
928 /* don't set timers for infinite leases */
929 if (client->lease->lifetime == 0xffffffff)
932 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
935 assert(client->request_sent <= time_now);
937 /* convert the various timeouts from relative (secs) to absolute (usecs) */
938 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
939 if (client->lease->t1 && client->lease->t2) {
940 /* both T1 and T2 are given */
941 if (client->lease->t1 < client->lease->t2 &&
942 client->lease->t2 < client->lease->lifetime) {
943 /* they are both valid */
944 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
945 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
948 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
949 client->lease->t2 = (client->lease->lifetime * 7) / 8;
950 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
951 client->lease->t1 = client->lease->lifetime / 2;
953 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
954 /* only T2 is given, and it is valid */
955 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
956 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
957 client->lease->t1 = client->lease->lifetime / 2;
958 if (t2_timeout <= t1_timeout) {
959 /* the computed T1 would be invalid, so discard T2 */
960 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
961 client->lease->t2 = (client->lease->lifetime * 7) / 8;
963 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
964 /* only T1 is given, and it is valid */
965 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
966 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
967 client->lease->t2 = (client->lease->lifetime * 7) / 8;
968 if (t2_timeout <= t1_timeout) {
969 /* the computed T2 would be invalid, so discard T1 */
970 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
971 client->lease->t2 = client->lease->lifetime / 2;
974 /* fall back to the default timeouts */
975 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
976 client->lease->t1 = client->lease->lifetime / 2;
977 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
978 client->lease->t2 = (client->lease->lifetime * 7) / 8;
981 /* arm lifetime timeout */
982 r = sd_event_add_time(client->event, &client->timeout_expire,
984 lifetime_timeout, 10 * USEC_PER_MSEC,
985 client_timeout_expire, client);
989 r = sd_event_source_set_priority(client->timeout_expire,
990 client->event_priority);
994 log_dhcp_client(client, "lease expires in %s",
995 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
996 lifetime_timeout - time_now, 0));
998 /* don't arm earlier timeouts if this has already expired */
999 if (lifetime_timeout <= time_now)
1002 /* arm T2 timeout */
1003 r = sd_event_add_time(client->event,
1004 &client->timeout_t2,
1008 client_timeout_t2, client);
1012 r = sd_event_source_set_priority(client->timeout_t2,
1013 client->event_priority);
1017 log_dhcp_client(client, "T2 expires in %s",
1018 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1019 t2_timeout - time_now, 0));
1021 /* don't arm earlier timeout if this has already expired */
1022 if (t2_timeout <= time_now)
1025 /* arm T1 timeout */
1026 r = sd_event_add_time(client->event,
1027 &client->timeout_t1,
1029 t1_timeout, 10 * USEC_PER_MSEC,
1030 client_timeout_t1, client);
1034 r = sd_event_source_set_priority(client->timeout_t1,
1035 client->event_priority);
1039 log_dhcp_client(client, "T1 expires in %s",
1040 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1041 t1_timeout - time_now, 0));
1046 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1048 int r = 0, notify_event = 0;
1051 assert(client->event);
1054 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1055 log_dhcp_client(client, "not a DHCP message: ignoring");
1059 if (message->op != BOOTREPLY) {
1060 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1064 if (be32toh(message->xid) != client->xid) {
1065 log_dhcp_client(client, "received xid (%u) does not match "
1066 "expected (%u): ignoring",
1067 be32toh(message->xid), client->xid);
1071 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1072 log_dhcp_client(client, "not an ethernet packet");
1076 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1078 log_dhcp_client(client, "received chaddr does not match "
1079 "expected: ignoring");
1083 switch (client->state) {
1084 case DHCP_STATE_SELECTING:
1086 r = client_handle_offer(client, message, len);
1089 client->timeout_resend =
1090 sd_event_source_unref(client->timeout_resend);
1092 client->state = DHCP_STATE_REQUESTING;
1093 client->attempt = 1;
1095 r = sd_event_add_time(client->event,
1096 &client->timeout_resend,
1099 client_timeout_resend, client);
1103 r = sd_event_source_set_priority(client->timeout_resend,
1104 client->event_priority);
1107 } else if (r == -ENOMSG)
1108 /* invalid message, let's ignore it */
1113 case DHCP_STATE_REBOOTING:
1114 case DHCP_STATE_REQUESTING:
1115 case DHCP_STATE_RENEWING:
1116 case DHCP_STATE_REBINDING:
1118 r = client_handle_ack(client, message, len);
1119 if (r == DHCP_EVENT_NO_LEASE) {
1121 client->timeout_resend =
1122 sd_event_source_unref(client->timeout_resend);
1124 if (client->state == DHCP_STATE_REBOOTING) {
1125 r = client_initialize(client);
1129 r = client_start(client);
1133 log_dhcp_client(client, "REBOOTED");
1137 } else if (r >= 0) {
1138 client->timeout_resend =
1139 sd_event_source_unref(client->timeout_resend);
1141 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1142 DHCP_STATE_REBOOTING))
1143 notify_event = DHCP_EVENT_IP_ACQUIRE;
1144 else if (r != DHCP_EVENT_IP_ACQUIRE)
1147 client->state = DHCP_STATE_BOUND;
1148 client->attempt = 1;
1150 client->last_addr = client->lease->address;
1152 r = client_set_lease_timeouts(client);
1157 client = client_notify(client, notify_event);
1159 client->state == DHCP_STATE_STOPPED)
1163 client->receive_message =
1164 sd_event_source_unref(client->receive_message);
1165 client->fd = asynchronous_close(client->fd);
1166 } else if (r == -ENOMSG)
1167 /* invalid message, let's ignore it */
1172 case DHCP_STATE_INIT:
1173 case DHCP_STATE_INIT_REBOOT:
1174 case DHCP_STATE_BOUND:
1178 case DHCP_STATE_STOPPED:
1184 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1185 client_stop(client, r);
1190 static int client_receive_message_udp(sd_event_source *s, int fd,
1191 uint32_t revents, void *userdata) {
1192 sd_dhcp_client *client = userdata;
1193 _cleanup_free_ DHCPMessage *message = NULL;
1194 int buflen = 0, len, r;
1199 r = ioctl(fd, FIONREAD, &buflen);
1204 /* this can't be right */
1207 message = malloc0(buflen);
1211 len = read(fd, message, buflen);
1213 log_dhcp_client(client, "could not receive message from UDP "
1216 } else if ((size_t)len < sizeof(DHCPMessage))
1219 return client_handle_message(client, message, len);
1222 static int client_receive_message_raw(sd_event_source *s, int fd,
1223 uint32_t revents, void *userdata) {
1224 sd_dhcp_client *client = userdata;
1225 _cleanup_free_ DHCPPacket *packet = NULL;
1226 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1227 struct iovec iov = {};
1228 struct msghdr msg = {
1231 .msg_control = cmsgbuf,
1232 .msg_controllen = sizeof(cmsgbuf),
1234 struct cmsghdr *cmsg;
1235 bool checksum = true;
1236 int buflen = 0, len, r;
1241 r = ioctl(fd, FIONREAD, &buflen);
1246 /* this can't be right */
1249 packet = malloc0(buflen);
1253 iov.iov_base = packet;
1254 iov.iov_len = buflen;
1256 len = recvmsg(fd, &msg, 0);
1258 log_dhcp_client(client, "could not receive message from raw "
1261 } else if ((size_t)len < sizeof(DHCPPacket))
1264 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1265 if (cmsg->cmsg_level == SOL_PACKET &&
1266 cmsg->cmsg_type == PACKET_AUXDATA &&
1267 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1268 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1270 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1275 r = dhcp_packet_verify_headers(packet, len, checksum);
1279 len -= DHCP_IP_UDP_SIZE;
1281 return client_handle_message(client, &packet->dhcp, len);
1284 int sd_dhcp_client_start(sd_dhcp_client *client) {
1287 assert_return(client, -EINVAL);
1289 r = client_initialize(client);
1293 if (client->last_addr)
1294 client->state = DHCP_STATE_INIT_REBOOT;
1296 r = client_start(client);
1298 log_dhcp_client(client, "STARTED on ifindex %u with address %s",
1300 ether_ntoa(&client->client_id.mac_addr));
1305 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1306 assert_return(client, -EINVAL);
1308 if (client_stop(client, DHCP_EVENT_STOP))
1309 client->state = DHCP_STATE_STOPPED;
1314 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1318 assert_return(client, -EINVAL);
1319 assert_return(!client->event, -EBUSY);
1322 client->event = sd_event_ref(event);
1324 r = sd_event_default(&client->event);
1329 client->event_priority = priority;
1334 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1335 assert_return(client, -EINVAL);
1337 client->event = sd_event_unref(client->event);
1342 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1346 return client->event;
1349 sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1351 assert_se(REFCNT_INC(client->n_ref) >= 2);
1356 sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1357 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1358 log_dhcp_client(client, "UNREF");
1360 client_initialize(client);
1362 client->receive_message =
1363 sd_event_source_unref(client->receive_message);
1365 sd_dhcp_client_detach_event(client);
1367 sd_dhcp_lease_unref(client->lease);
1369 free(client->req_opts);
1378 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
1379 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
1381 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1382 _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1384 assert_return(ret, -EINVAL);
1386 client = new0(sd_dhcp_client, 1);
1390 client->n_ref = REFCNT_INIT;
1391 client->state = DHCP_STATE_INIT;
1394 client->attempt = 1;
1396 client->req_opts_size = ELEMENTSOF(default_req_opts);
1398 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1399 if (!client->req_opts)