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 <sys/param.h>
26 #include <sys/ioctl.h>
31 #include "dhcp-protocol.h"
32 #include "dhcp-internal.h"
33 #include "dhcp-lease-internal.h"
34 #include "sd-dhcp-client.h"
36 struct sd_dhcp_client
{
40 sd_event_source
*timeout_resend
;
43 union sockaddr_union link
;
44 sd_event_source
*receive_message
;
46 size_t req_opts_allocated
;
51 struct ether_addr mac_addr
;
58 sd_event_source
*timeout_t1
;
59 sd_event_source
*timeout_t2
;
60 sd_event_source
*timeout_expire
;
61 sd_dhcp_client_cb_t cb
;
66 static const uint8_t default_req_opts
[] = {
67 DHCP_OPTION_SUBNET_MASK
,
69 DHCP_OPTION_HOST_NAME
,
70 DHCP_OPTION_DOMAIN_NAME
,
71 DHCP_OPTION_DOMAIN_NAME_SERVER
,
72 DHCP_OPTION_NTP_SERVER
,
75 static int client_receive_message_raw(sd_event_source
*s
, int fd
,
76 uint32_t revents
, void *userdata
);
77 static int client_receive_message_udp(sd_event_source
*s
, int fd
,
78 uint32_t revents
, void *userdata
);
80 int sd_dhcp_client_set_callback(sd_dhcp_client
*client
, sd_dhcp_client_cb_t cb
,
82 assert_return(client
, -EINVAL
);
85 client
->userdata
= userdata
;
90 int sd_dhcp_client_set_request_option(sd_dhcp_client
*client
, uint8_t option
) {
93 assert_return(client
, -EINVAL
);
94 assert_return (client
->state
== DHCP_STATE_INIT
, -EBUSY
);
98 case DHCP_OPTION_OVERLOAD
:
99 case DHCP_OPTION_MESSAGE_TYPE
:
100 case DHCP_OPTION_PARAMETER_REQUEST_LIST
:
101 case DHCP_OPTION_END
:
108 for (i
= 0; i
< client
->req_opts_size
; i
++)
109 if (client
->req_opts
[i
] == option
)
112 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
113 client
->req_opts_size
+ 1))
116 client
->req_opts
[client
->req_opts_size
++] = option
;
121 int sd_dhcp_client_set_request_address(sd_dhcp_client
*client
,
122 const struct in_addr
*last_addr
) {
123 assert_return(client
, -EINVAL
);
124 assert_return(client
->state
== DHCP_STATE_INIT
, -EBUSY
);
127 client
->last_addr
= last_addr
->s_addr
;
129 client
->last_addr
= INADDR_ANY
;
134 int sd_dhcp_client_set_index(sd_dhcp_client
*client
, int interface_index
) {
135 assert_return(client
, -EINVAL
);
136 assert_return(client
->state
== DHCP_STATE_INIT
, -EBUSY
);
137 assert_return(interface_index
>= -1, -EINVAL
);
139 client
->index
= interface_index
;
144 int sd_dhcp_client_set_mac(sd_dhcp_client
*client
,
145 const struct ether_addr
*addr
) {
146 assert_return(client
, -EINVAL
);
147 assert_return(client
->state
== DHCP_STATE_INIT
, -EBUSY
);
149 log_dhcp_client(client
, "set MAC address to "
150 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
151 addr
->ether_addr_octet
[0],
152 addr
->ether_addr_octet
[1],
153 addr
->ether_addr_octet
[2],
154 addr
->ether_addr_octet
[3],
155 addr
->ether_addr_octet
[4],
156 addr
->ether_addr_octet
[5]);
158 memcpy(&client
->client_id
.mac_addr
, addr
, ETH_ALEN
);
159 client
->client_id
.type
= 0x01;
164 int sd_dhcp_client_get_lease(sd_dhcp_client
*client
, sd_dhcp_lease
**ret
) {
165 assert_return(client
, -EINVAL
);
166 assert_return(ret
, -EINVAL
);
168 if (client
->state
!= DHCP_STATE_BOUND
&&
169 client
->state
!= DHCP_STATE_RENEWING
&&
170 client
->state
!= DHCP_STATE_REBINDING
)
171 return -EADDRNOTAVAIL
;
173 *ret
= sd_dhcp_lease_ref(client
->lease
);
178 static int client_notify(sd_dhcp_client
*client
, int event
) {
180 client
->cb(client
, event
, client
->userdata
);
185 static int client_initialize(sd_dhcp_client
*client
) {
186 assert_return(client
, -EINVAL
);
188 client
->receive_message
=
189 sd_event_source_unref(client
->receive_message
);
191 client
->fd
= safe_close(client
->fd
);
193 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
195 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
196 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
197 client
->timeout_expire
= sd_event_source_unref(client
->timeout_expire
);
201 client
->start_time
= 0;
203 client
->state
= DHCP_STATE_INIT
;
207 client
->lease
= sd_dhcp_lease_unref(client
->lease
);
212 static int client_stop(sd_dhcp_client
*client
, int error
) {
213 assert_return(client
, -EINVAL
);
215 client_notify(client
, error
);
217 client_initialize(client
);
219 log_dhcp_client(client
, "STOPPED");
224 static int client_message_init(sd_dhcp_client
*client
, DHCPMessage
*message
,
225 uint8_t type
, uint16_t secs
, uint8_t **opt
,
231 r
= dhcp_message_init(message
, BOOTREQUEST
, client
->xid
, type
, opt
,
236 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
237 refuse to issue an DHCP lease if 'secs' is set to zero */
238 message
->secs
= htobe16(secs
);
240 memcpy(&message
->chaddr
, &client
->client_id
.mac_addr
, ETH_ALEN
);
242 if (client
->state
== DHCP_STATE_RENEWING
||
243 client
->state
== DHCP_STATE_REBINDING
)
244 message
->ciaddr
= client
->lease
->address
;
246 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
247 Identifier option is not set */
248 r
= dhcp_option_append(opt
, optlen
, DHCP_OPTION_CLIENT_IDENTIFIER
,
249 sizeof(client
->client_id
), &client
->client_id
);
253 if (type
== DHCP_DISCOVER
|| type
== DHCP_REQUEST
) {
256 r
= dhcp_option_append(opt
, optlen
,
257 DHCP_OPTION_PARAMETER_REQUEST_LIST
,
258 client
->req_opts_size
,
263 /* Some DHCP servers will send bigger DHCP packets than the
264 defined default size unless the Maximum Messge Size option
265 is explicitely set */
266 max_size
= htobe16(DHCP_IP_UDP_SIZE
+ DHCP_MESSAGE_SIZE
+
267 DHCP_MIN_OPTIONS_SIZE
);
268 r
= dhcp_option_append(opt
, optlen
,
269 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE
,
278 static int dhcp_client_send_raw(sd_dhcp_client
*client
, DHCPPacket
*packet
,
280 dhcp_packet_append_ip_headers(packet
, INADDR_ANY
, DHCP_PORT_CLIENT
,
281 INADDR_BROADCAST
, DHCP_PORT_SERVER
, len
);
283 return dhcp_network_send_raw_socket(client
->fd
, &client
->link
,
287 static int client_send_discover(sd_dhcp_client
*client
, uint16_t secs
) {
289 _cleanup_free_ DHCPPacket
*discover
;
293 optlen
= DHCP_MIN_OPTIONS_SIZE
;
294 len
= sizeof(DHCPPacket
) + optlen
;
296 discover
= malloc0(len
);
301 err
= client_message_init(client
, &discover
->dhcp
, DHCP_DISCOVER
,
302 secs
, &opt
, &optlen
);
306 if (client
->last_addr
!= INADDR_ANY
) {
307 err
= dhcp_option_append(&opt
, &optlen
,
308 DHCP_OPTION_REQUESTED_IP_ADDRESS
,
309 4, &client
->last_addr
);
314 err
= dhcp_option_append(&opt
, &optlen
, DHCP_OPTION_END
, 0, NULL
);
318 err
= dhcp_client_send_raw(client
, discover
, len
);
322 log_dhcp_client(client
, "DISCOVER");
327 static int client_send_request(sd_dhcp_client
*client
, uint16_t secs
) {
328 _cleanup_free_ DHCPPacket
*request
;
333 optlen
= DHCP_MIN_OPTIONS_SIZE
;
334 len
= sizeof(DHCPPacket
) + optlen
;
336 request
= malloc0(len
);
340 err
= client_message_init(client
, &request
->dhcp
, DHCP_REQUEST
, secs
,
345 switch (client
->state
) {
347 case DHCP_STATE_INIT_REBOOT
:
348 err
= dhcp_option_append(&opt
, &optlen
,
349 DHCP_OPTION_REQUESTED_IP_ADDRESS
,
350 4, &client
->last_addr
);
355 case DHCP_STATE_REQUESTING
:
356 err
= dhcp_option_append(&opt
, &optlen
,
357 DHCP_OPTION_REQUESTED_IP_ADDRESS
,
358 4, &client
->lease
->address
);
362 err
= dhcp_option_append(&opt
, &optlen
,
363 DHCP_OPTION_SERVER_IDENTIFIER
,
364 4, &client
->lease
->server_address
);
369 case DHCP_STATE_INIT
:
370 case DHCP_STATE_SELECTING
:
371 case DHCP_STATE_REBOOTING
:
372 case DHCP_STATE_BOUND
:
373 case DHCP_STATE_RENEWING
:
374 case DHCP_STATE_REBINDING
:
379 err
= dhcp_option_append(&opt
, &optlen
, DHCP_OPTION_END
, 0, NULL
);
383 if (client
->state
== DHCP_STATE_RENEWING
) {
384 err
= dhcp_network_send_udp_socket(client
->fd
,
385 client
->lease
->server_address
,
388 len
- DHCP_IP_UDP_SIZE
);
390 err
= dhcp_client_send_raw(client
, request
, len
);
395 log_dhcp_client(client
, "REQUEST");
400 static uint16_t client_update_secs(sd_dhcp_client
*client
, usec_t time_now
)
402 client
->secs
= ((time_now
- client
->start_time
) / USEC_PER_SEC
) ? : 1;
407 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
409 sd_dhcp_client
*client
= userdata
;
410 usec_t next_timeout
= 0;
417 assert(client
->event
);
419 r
= sd_event_get_now_monotonic(client
->event
, &time_now
);
423 switch (client
->state
) {
424 case DHCP_STATE_RENEWING
:
426 time_left
= (client
->lease
->t2
- client
->lease
->t1
) / 2;
430 next_timeout
= time_now
+ time_left
* USEC_PER_SEC
;
434 case DHCP_STATE_REBINDING
:
436 time_left
= (client
->lease
->lifetime
- client
->lease
->t2
) / 2;
440 next_timeout
= time_now
+ time_left
* USEC_PER_SEC
;
443 case DHCP_STATE_REBOOTING
:
444 /* start over as we did not receive a timely ack or nak */
445 client
->state
= DHCP_STATE_INIT
;
447 client
->xid
= random_u32();
450 case DHCP_STATE_INIT
:
451 case DHCP_STATE_INIT_REBOOT
:
452 case DHCP_STATE_SELECTING
:
453 case DHCP_STATE_REQUESTING
:
454 case DHCP_STATE_BOUND
:
456 if (client
->attempt
< 64)
457 client
->attempt
*= 2;
459 next_timeout
= time_now
+ (client
->attempt
- 1) * USEC_PER_SEC
;
464 next_timeout
+= (random_u32() & 0x1fffff);
466 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
468 r
= sd_event_add_monotonic(client
->event
,
469 &client
->timeout_resend
,
472 client_timeout_resend
, client
);
476 r
= sd_event_source_set_priority(client
->timeout_resend
,
477 client
->event_priority
);
481 switch (client
->state
) {
482 case DHCP_STATE_INIT
:
484 client_update_secs(client
, time_now
);
486 r
= client_send_discover(client
, client
->secs
);
488 client
->state
= DHCP_STATE_SELECTING
;
491 if (client
->attempt
>= 64)
497 case DHCP_STATE_SELECTING
:
498 client_update_secs(client
, time_now
);
500 r
= client_send_discover(client
, client
->secs
);
501 if (r
< 0 && client
->attempt
>= 64)
506 case DHCP_STATE_INIT_REBOOT
:
507 case DHCP_STATE_REQUESTING
:
508 case DHCP_STATE_RENEWING
:
509 case DHCP_STATE_REBINDING
:
510 r
= client_send_request(client
, client
->secs
);
511 if (r
< 0 && client
->attempt
>= 64)
514 if (client
->state
== DHCP_STATE_INIT_REBOOT
)
515 client
->state
= DHCP_STATE_REBOOTING
;
517 client
->request_sent
= time_now
;
521 case DHCP_STATE_REBOOTING
:
522 case DHCP_STATE_BOUND
:
530 client_stop(client
, r
);
532 /* Errors were dealt with when stopping the client, don't spill
533 errors into the event loop handler */
537 static int client_initialize_events(sd_dhcp_client
*client
,
538 sd_event_io_handler_t io_callback
) {
542 assert(client
->event
);
544 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
545 client
->fd
, EPOLLIN
, io_callback
,
550 r
= sd_event_source_set_priority(client
->receive_message
,
551 client
->event_priority
);
555 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
557 r
= sd_event_add_monotonic(client
->event
,
558 &client
->timeout_resend
, 0, 0,
559 client_timeout_resend
, client
);
563 r
= sd_event_source_set_priority(client
->timeout_resend
,
564 client
->event_priority
);
568 client_stop(client
, r
);
574 static int client_start(sd_dhcp_client
*client
) {
577 assert_return(client
, -EINVAL
);
578 assert_return(client
->event
, -EINVAL
);
579 assert_return(client
->index
> 0, -EINVAL
);
580 assert_return(client
->fd
< 0, -EBUSY
);
581 assert_return(client
->xid
== 0, -EINVAL
);
582 assert_return(client
->state
== DHCP_STATE_INIT
||
583 client
->state
== DHCP_STATE_INIT_REBOOT
, -EBUSY
);
585 client
->xid
= random_u32();
587 r
= dhcp_network_bind_raw_socket(client
->index
, &client
->link
);
590 client_stop(client
, r
);
595 client
->start_time
= now(CLOCK_MONOTONIC
);
598 log_dhcp_client(client
, "STARTED");
600 return client_initialize_events(client
, client_receive_message_raw
);
603 static int client_timeout_expire(sd_event_source
*s
, uint64_t usec
,
605 sd_dhcp_client
*client
= userdata
;
607 log_dhcp_client(client
, "EXPIRED");
609 client_notify(client
, DHCP_EVENT_EXPIRED
);
611 /* start over as the lease was lost */
612 client_initialize(client
);
613 client_start(client
);
618 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
619 sd_dhcp_client
*client
= userdata
;
622 client
->receive_message
= sd_event_source_unref(client
->receive_message
);
623 client
->fd
= safe_close(client
->fd
);
625 client
->state
= DHCP_STATE_REBINDING
;
628 r
= dhcp_network_bind_raw_socket(client
->index
, &client
->link
);
630 client_stop(client
, r
);
636 log_dhcp_client(client
, "TIMEOUT T2");
638 return client_initialize_events(client
, client_receive_message_raw
);
641 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
643 sd_dhcp_client
*client
= userdata
;
646 client
->state
= DHCP_STATE_RENEWING
;
649 r
= dhcp_network_bind_udp_socket(client
->index
,
650 client
->lease
->address
,
653 client_stop(client
, r
);
659 log_dhcp_client(client
, "TIMEOUT T1");
661 return client_initialize_events(client
, client_receive_message_udp
);
664 static int client_handle_offer(sd_dhcp_client
*client
, DHCPMessage
*offer
,
666 _cleanup_dhcp_lease_unref_ sd_dhcp_lease
*lease
= NULL
;
669 r
= dhcp_lease_new(&lease
);
673 r
= dhcp_option_parse(offer
, len
, dhcp_lease_parse_options
, lease
);
677 lease
->next_server
= offer
->siaddr
;
679 lease
->address
= offer
->yiaddr
;
681 if (lease
->address
== INADDR_ANY
||
682 lease
->server_address
== INADDR_ANY
||
683 lease
->subnet_mask
== INADDR_ANY
||
684 lease
->lifetime
== 0)
687 client
->lease
= lease
;
690 log_dhcp_client(client
, "OFFER");
695 static int client_handle_ack(sd_dhcp_client
*client
, DHCPMessage
*ack
,
697 _cleanup_dhcp_lease_unref_ sd_dhcp_lease
*lease
= NULL
;
700 r
= dhcp_lease_new(&lease
);
704 r
= dhcp_option_parse(ack
, len
, dhcp_lease_parse_options
, lease
);
706 log_dhcp_client(client
, "NAK");
707 return DHCP_EVENT_NO_LEASE
;
713 lease
->next_server
= ack
->siaddr
;
715 lease
->address
= ack
->yiaddr
;
717 if (lease
->address
== INADDR_ANY
||
718 lease
->server_address
== INADDR_ANY
||
719 lease
->subnet_mask
== INADDR_ANY
|| lease
->lifetime
== 0)
722 r
= DHCP_EVENT_IP_ACQUIRE
;
724 if (client
->lease
->address
!= lease
->address
||
725 client
->lease
->subnet_mask
!= lease
->subnet_mask
||
726 client
->lease
->router
!= lease
->router
) {
727 r
= DHCP_EVENT_IP_CHANGE
;
730 client
->lease
= sd_dhcp_lease_unref(client
->lease
);
733 client
->lease
= lease
;
736 log_dhcp_client(client
, "ACK");
741 static uint64_t client_compute_timeout(uint64_t request_sent
,
743 return request_sent
+ (lifetime
- 3) * USEC_PER_SEC
+
744 + (random_u32() & 0x1fffff);
747 static int client_set_lease_timeouts(sd_dhcp_client
*client
, uint64_t usec
) {
748 uint64_t next_timeout
;
752 assert(client
->event
);
754 /* don't set timers for infinite leases */
755 if (client
->lease
->lifetime
== 0xffffffff)
758 if (client
->lease
->lifetime
< 10)
761 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
762 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
763 client
->timeout_expire
= sd_event_source_unref(client
->timeout_expire
);
765 if (!client
->lease
->t1
)
766 client
->lease
->t1
= client
->lease
->lifetime
/ 2;
768 next_timeout
= client_compute_timeout(client
->request_sent
,
770 if (next_timeout
< usec
)
773 r
= sd_event_add_monotonic(client
->event
,
777 client_timeout_t1
, client
);
781 r
= sd_event_source_set_priority(client
->timeout_t1
,
782 client
->event_priority
);
786 if (!client
->lease
->t2
)
787 client
->lease
->t2
= client
->lease
->lifetime
* 7 / 8;
789 if (client
->lease
->t2
< client
->lease
->t1
)
792 if (client
->lease
->lifetime
< client
->lease
->t2
)
795 next_timeout
= client_compute_timeout(client
->request_sent
,
797 if (next_timeout
< usec
)
800 r
= sd_event_add_monotonic(client
->event
,
804 client_timeout_t2
, client
);
808 r
= sd_event_source_set_priority(client
->timeout_t2
,
809 client
->event_priority
);
813 next_timeout
= client_compute_timeout(client
->request_sent
,
814 client
->lease
->lifetime
);
815 if (next_timeout
< usec
)
818 r
= sd_event_add_monotonic(client
->event
,
819 &client
->timeout_expire
, next_timeout
,
821 client_timeout_expire
, client
);
825 r
= sd_event_source_set_priority(client
->timeout_expire
,
826 client
->event_priority
);
833 static int client_handle_message(sd_dhcp_client
*client
, DHCPMessage
*message
,
834 int len
, usec_t time_now
) {
835 int r
= 0, notify_event
= 0;
838 assert(client
->event
);
841 if (len
< DHCP_MESSAGE_SIZE
) {
842 log_dhcp_client(client
, "message too small (%d bytes): "
847 if (message
->op
!= BOOTREPLY
) {
848 log_dhcp_client(client
, "not a BOOTREPLY message: ignoring");
852 if (be32toh(message
->xid
) != client
->xid
) {
853 log_dhcp_client(client
, "received xid (%u) does not match "
854 "expected (%u): ignoring",
855 be32toh(message
->xid
), client
->xid
);
859 if (memcmp(&message
->chaddr
[0], &client
->client_id
.mac_addr
,
861 log_dhcp_client(client
, "received chaddr does not match "
862 "expected: ignoring");
866 switch (client
->state
) {
867 case DHCP_STATE_SELECTING
:
869 r
= client_handle_offer(client
, message
, len
);
872 client
->timeout_resend
=
873 sd_event_source_unref(client
->timeout_resend
);
875 client
->state
= DHCP_STATE_REQUESTING
;
878 r
= sd_event_add_monotonic(client
->event
,
879 &client
->timeout_resend
, 0,
880 0, client_timeout_resend
,
885 r
= sd_event_source_set_priority(client
->timeout_resend
,
886 client
->event_priority
);
893 case DHCP_STATE_REBOOTING
:
894 case DHCP_STATE_REQUESTING
:
895 case DHCP_STATE_RENEWING
:
896 case DHCP_STATE_REBINDING
:
898 r
= client_handle_ack(client
, message
, len
);
900 if (r
== DHCP_EVENT_NO_LEASE
) {
902 client
->timeout_resend
=
903 sd_event_source_unref(client
->timeout_resend
);
905 if (client
->state
== DHCP_STATE_REBOOTING
) {
906 r
= client_initialize(client
);
910 r
= client_start(client
);
919 client
->timeout_resend
=
920 sd_event_source_unref(client
->timeout_resend
);
922 if (IN_SET(client
->state
, DHCP_STATE_REQUESTING
,
923 DHCP_STATE_REBOOTING
))
924 notify_event
= DHCP_EVENT_IP_ACQUIRE
;
925 else if (r
!= DHCP_EVENT_IP_ACQUIRE
)
928 client
->state
= DHCP_STATE_BOUND
;
931 client
->last_addr
= client
->lease
->address
;
933 r
= client_set_lease_timeouts(client
, time_now
);
938 client_notify(client
, notify_event
);
940 client
->receive_message
=
941 sd_event_source_unref(client
->receive_message
);
942 client
->fd
= safe_close(client
->fd
);
949 case DHCP_STATE_INIT
:
950 case DHCP_STATE_INIT_REBOOT
:
951 case DHCP_STATE_BOUND
:
957 if (r
< 0 || r
== DHCP_EVENT_NO_LEASE
)
958 return client_stop(client
, r
);
963 static int client_receive_message_udp(sd_event_source
*s
, int fd
,
964 uint32_t revents
, void *userdata
) {
965 sd_dhcp_client
*client
= userdata
;
966 _cleanup_free_ DHCPMessage
*message
= NULL
;
967 int buflen
= 0, len
, r
;
972 assert(client
->event
);
974 r
= ioctl(fd
, FIONREAD
, &buflen
);
975 if (r
< 0 || buflen
<= 0)
976 buflen
= sizeof(DHCPMessage
) + DHCP_MIN_OPTIONS_SIZE
;
978 message
= malloc0(buflen
);
982 len
= read(fd
, message
, buflen
);
986 r
= sd_event_get_now_monotonic(client
->event
, &time_now
);
988 return client_stop(client
, r
);
990 return client_handle_message(client
, message
, len
,
994 static int client_receive_message_raw(sd_event_source
*s
, int fd
,
995 uint32_t revents
, void *userdata
) {
996 sd_dhcp_client
*client
= userdata
;
997 _cleanup_free_ DHCPPacket
*packet
= NULL
;
999 uint8_t cmsgbuf
[CMSG_LEN(sizeof(struct tpacket_auxdata
))];
1000 struct iovec iov
= {};
1001 struct msghdr msg
= {
1004 .msg_control
= cmsgbuf
,
1005 .msg_controllen
= sizeof(cmsgbuf
),
1007 struct cmsghdr
*cmsg
;
1008 bool checksum
= true;
1009 int buflen
= 0, len
, r
;
1013 assert(client
->event
);
1015 r
= ioctl(fd
, FIONREAD
, &buflen
);
1016 if (r
< 0 || buflen
<= 0)
1017 buflen
= sizeof(DHCPPacket
) + DHCP_MIN_OPTIONS_SIZE
;
1019 packet
= malloc0(buflen
);
1023 iov
.iov_base
= packet
;
1024 iov
.iov_len
= buflen
;
1026 len
= recvmsg(fd
, &msg
, 0);
1028 log_dhcp_client(client
, "could not receive message from raw "
1029 "socket: %s", strerror(errno
));
1033 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
; cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
1034 if (cmsg
->cmsg_level
== SOL_PACKET
&& cmsg
->cmsg_type
== PACKET_AUXDATA
) {
1035 struct tpacket_auxdata
*aux
= (void *)CMSG_DATA(cmsg
);
1037 checksum
= !(aux
->tp_status
& TP_STATUS_CSUMNOTREADY
);
1042 r
= dhcp_packet_verify_headers(packet
, len
, checksum
);
1046 len
-= DHCP_IP_UDP_SIZE
;
1048 r
= sd_event_get_now_monotonic(client
->event
, &time_now
);
1050 return client_stop(client
, r
);
1052 return client_handle_message(client
, &packet
->dhcp
, len
, time_now
);
1055 int sd_dhcp_client_start(sd_dhcp_client
*client
) {
1058 assert_return(client
, -EINVAL
);
1060 r
= client_initialize(client
);
1064 if (client
->last_addr
)
1065 client
->state
= DHCP_STATE_INIT_REBOOT
;
1067 return client_start(client
);
1070 int sd_dhcp_client_stop(sd_dhcp_client
*client
) {
1071 return client_stop(client
, DHCP_EVENT_STOP
);
1074 int sd_dhcp_client_attach_event(sd_dhcp_client
*client
, sd_event
*event
,
1078 assert_return(client
, -EINVAL
);
1079 assert_return(!client
->event
, -EBUSY
);
1082 client
->event
= sd_event_ref(event
);
1084 r
= sd_event_default(&client
->event
);
1089 client
->event_priority
= priority
;
1094 int sd_dhcp_client_detach_event(sd_dhcp_client
*client
) {
1095 assert_return(client
, -EINVAL
);
1097 client
->event
= sd_event_unref(client
->event
);
1102 sd_event
*sd_dhcp_client_get_event(sd_dhcp_client
*client
) {
1106 return client
->event
;
1109 void sd_dhcp_client_free(sd_dhcp_client
*client
) {
1113 sd_dhcp_client_stop(client
);
1114 sd_dhcp_client_detach_event(client
);
1116 free(client
->req_opts
);
1120 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client
*, sd_dhcp_client_free
);
1121 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
1123 int sd_dhcp_client_new(sd_dhcp_client
**ret
) {
1124 _cleanup_dhcp_client_free_ sd_dhcp_client
*client
= NULL
;
1126 assert_return(ret
, -EINVAL
);
1128 client
= new0(sd_dhcp_client
, 1);
1132 client
->state
= DHCP_STATE_INIT
;
1135 client
->attempt
= 1;
1137 client
->req_opts_size
= ELEMENTSOF(default_req_opts
);
1139 client
->req_opts
= memdup(default_req_opts
, client
->req_opts_size
);
1140 if (!client
->req_opts
)