]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd-network/sd-dhcp-client.c
sd-dhcp-client/networkd: set lifetimes for IPv4 addresses
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-client.c
... / ...
CommitLineData
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5
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.
10
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.
15
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/>.
18***/
19
20#include <stdlib.h>
21#include <errno.h>
22#include <string.h>
23#include <stdio.h>
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>
29
30#include "util.h"
31#include "list.h"
32#include "refcnt.h"
33#include "async.h"
34
35#include "dhcp-protocol.h"
36#include "dhcp-internal.h"
37#include "dhcp-lease-internal.h"
38#include "sd-dhcp-client.h"
39
40struct sd_dhcp_client {
41 RefCount n_ref;
42
43 DHCPState state;
44 sd_event *event;
45 int event_priority;
46 sd_event_source *timeout_resend;
47 int index;
48 int fd;
49 union sockaddr_union link;
50 sd_event_source *receive_message;
51 uint8_t *req_opts;
52 size_t req_opts_allocated;
53 size_t req_opts_size;
54 be32_t last_addr;
55 struct {
56 uint8_t type;
57 struct ether_addr mac_addr;
58 } _packed_ client_id;
59 uint32_t xid;
60 usec_t start_time;
61 uint16_t secs;
62 unsigned int attempt;
63 usec_t request_sent;
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;
68 void *userdata;
69 sd_dhcp_lease *lease;
70};
71
72static const uint8_t default_req_opts[] = {
73 DHCP_OPTION_SUBNET_MASK,
74 DHCP_OPTION_ROUTER,
75 DHCP_OPTION_HOST_NAME,
76 DHCP_OPTION_DOMAIN_NAME,
77 DHCP_OPTION_DOMAIN_NAME_SERVER,
78 DHCP_OPTION_NTP_SERVER,
79};
80
81static int client_receive_message_raw(sd_event_source *s, int fd,
82 uint32_t revents, void *userdata);
83static int client_receive_message_udp(sd_event_source *s, int fd,
84 uint32_t revents, void *userdata);
85static void client_stop(sd_dhcp_client *client, int error);
86
87int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
88 void *userdata) {
89 assert_return(client, -EINVAL);
90
91 client->cb = cb;
92 client->userdata = userdata;
93
94 return 0;
95}
96
97int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
98 size_t i;
99
100 assert_return(client, -EINVAL);
101 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
102 DHCP_STATE_STOPPED), -EBUSY);
103
104 switch(option) {
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:
110 return -EINVAL;
111
112 default:
113 break;
114 }
115
116 for (i = 0; i < client->req_opts_size; i++)
117 if (client->req_opts[i] == option)
118 return -EEXIST;
119
120 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
121 client->req_opts_size + 1))
122 return -ENOMEM;
123
124 client->req_opts[client->req_opts_size++] = option;
125
126 return 0;
127}
128
129int 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);
134
135 if (last_addr)
136 client->last_addr = last_addr->s_addr;
137 else
138 client->last_addr = INADDR_ANY;
139
140 return 0;
141}
142
143int 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);
148
149 client->index = interface_index;
150
151 return 0;
152}
153
154int sd_dhcp_client_set_mac(sd_dhcp_client *client,
155 const struct ether_addr *addr) {
156 DHCP_CLIENT_DONT_DESTROY(client);
157 bool need_restart = false;
158
159 assert_return(client, -EINVAL);
160 assert_return(addr, -EINVAL);
161
162 if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
163 return 0;
164
165 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
166 log_dhcp_client(client, "Changing MAC address on running DHCP "
167 "client, restarting");
168 need_restart = true;
169 client_stop(client, DHCP_EVENT_STOP);
170 }
171
172 memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
173 client->client_id.type = 0x01;
174
175 if (need_restart && client->state != DHCP_STATE_STOPPED)
176 sd_dhcp_client_start(client);
177
178 return 0;
179}
180
181int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
182 assert_return(client, -EINVAL);
183 assert_return(ret, -EINVAL);
184
185 if (client->state != DHCP_STATE_BOUND &&
186 client->state != DHCP_STATE_RENEWING &&
187 client->state != DHCP_STATE_REBINDING)
188 return -EADDRNOTAVAIL;
189
190 *ret = sd_dhcp_lease_ref(client->lease);
191
192 return 0;
193}
194
195static void client_notify(sd_dhcp_client *client, int event) {
196 if (client->cb)
197 client->cb(client, event, client->userdata);
198}
199
200static int client_initialize(sd_dhcp_client *client) {
201 assert_return(client, -EINVAL);
202
203 client->receive_message =
204 sd_event_source_unref(client->receive_message);
205
206 client->fd = asynchronous_close(client->fd);
207
208 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
209
210 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
211 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
212 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
213
214 client->attempt = 1;
215
216 client->state = DHCP_STATE_INIT;
217 client->xid = 0;
218
219 if (client->lease)
220 client->lease = sd_dhcp_lease_unref(client->lease);
221
222 return 0;
223}
224
225static void client_stop(sd_dhcp_client *client, int error) {
226 assert(client);
227
228 if (error < 0)
229 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
230 else {
231 switch(error) {
232 case DHCP_EVENT_STOP:
233 log_dhcp_client(client, "STOPPED");
234 break;
235 case DHCP_EVENT_NO_LEASE:
236 log_dhcp_client(client, "STOPPED: No lease");
237 break;
238 default:
239 log_dhcp_client(client, "STOPPED: Unknown reason");
240 break;
241 }
242 }
243
244 client_notify(client, error);
245
246 client_initialize(client);
247}
248
249static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
250 uint8_t type, size_t *_optlen, size_t *_optoffset) {
251 _cleanup_free_ DHCPPacket *packet;
252 size_t optlen, optoffset, size;
253 be16_t max_size;
254 int r;
255
256 assert(client);
257 assert(client->secs);
258 assert(ret);
259 assert(_optlen);
260 assert(_optoffset);
261 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
262
263 optlen = DHCP_MIN_OPTIONS_SIZE;
264 size = sizeof(DHCPPacket) + optlen;
265
266 packet = malloc0(size);
267 if (!packet)
268 return -ENOMEM;
269
270 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
271 optlen, &optoffset);
272 if (r < 0)
273 return r;
274
275 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
276 refuse to issue an DHCP lease if 'secs' is set to zero */
277 packet->dhcp.secs = htobe16(client->secs);
278
279 /* RFC2132 section 4.1
280 A client that cannot receive unicast IP datagrams until its protocol
281 software has been configured with an IP address SHOULD set the
282 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
283 DHCPREQUEST messages that client sends. The BROADCAST bit will
284 provide a hint to the DHCP server and BOOTP relay agent to broadcast
285 any messages to the client on the client's subnet. */
286 packet->dhcp.flags = htobe16(0x8000);
287
288 /* RFC2132 section 4.1.1:
289 The client MUST include its hardware address in the ’chaddr’ field, if
290 necessary for delivery of DHCP reply messages.
291 */
292 memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
293
294 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
295 Identifier option is not set */
296 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
297 DHCP_OPTION_CLIENT_IDENTIFIER,
298 sizeof(client->client_id), &client->client_id);
299 if (r < 0)
300 return r;
301
302
303 /* RFC2131 section 3.5:
304 in its initial DHCPDISCOVER or DHCPREQUEST message, a
305 client may provide the server with a list of specific
306 parameters the client is interested in. If the client
307 includes a list of parameters in a DHCPDISCOVER message,
308 it MUST include that list in any subsequent DHCPREQUEST
309 messages.
310 */
311 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
312 DHCP_OPTION_PARAMETER_REQUEST_LIST,
313 client->req_opts_size, client->req_opts);
314 if (r < 0)
315 return r;
316
317 /* RFC2131 section 3.5:
318 The client SHOULD include the ’maximum DHCP message size’ option to
319 let the server know how large the server may make its DHCP messages.
320
321 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
322 than the defined default size unless the Maximum Messge Size option
323 is explicitely set
324 */
325 max_size = htobe16(size);
326 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
327 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
328 2, &max_size);
329 if (r < 0)
330 return r;
331
332 *_optlen = optlen;
333 *_optoffset = optoffset;
334 *ret = packet;
335 packet = NULL;
336
337 return 0;
338}
339
340static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
341 size_t len) {
342 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
343 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
344
345 return dhcp_network_send_raw_socket(client->fd, &client->link,
346 packet, len);
347}
348
349static int client_send_discover(sd_dhcp_client *client) {
350 _cleanup_free_ DHCPPacket *discover = NULL;
351 size_t optoffset, optlen;
352 usec_t time_now;
353 int r;
354
355 assert(client);
356 assert(client->state == DHCP_STATE_INIT ||
357 client->state == DHCP_STATE_SELECTING);
358
359 /* See RFC2131 section 4.4.1 */
360
361 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
362 if (r < 0)
363 return r;
364 assert(time_now >= client->start_time);
365
366 /* seconds between sending first and last DISCOVER
367 * must always be strictly positive to deal with broken servers */
368 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
369
370 r = client_message_init(client, &discover, DHCP_DISCOVER,
371 &optlen, &optoffset);
372 if (r < 0)
373 return r;
374
375 /* the client may suggest values for the network address
376 and lease time in the DHCPDISCOVER message. The client may include
377 the ’requested IP address’ option to suggest that a particular IP
378 address be assigned, and may include the ’IP address lease time’
379 option to suggest the lease time it would like.
380 */
381 if (client->last_addr != INADDR_ANY) {
382 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
383 DHCP_OPTION_REQUESTED_IP_ADDRESS,
384 4, &client->last_addr);
385 if (r < 0)
386 return r;
387 }
388
389 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
390 DHCP_OPTION_END, 0, NULL);
391 if (r < 0)
392 return r;
393
394 /* We currently ignore:
395 The client SHOULD wait a random time between one and ten seconds to
396 desynchronize the use of DHCP at startup.
397 */
398 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
399 if (r < 0)
400 return r;
401
402 log_dhcp_client(client, "DISCOVER");
403
404 return 0;
405}
406
407static int client_send_request(sd_dhcp_client *client) {
408 _cleanup_free_ DHCPPacket *request = NULL;
409 size_t optoffset, optlen;
410 int r;
411
412 r = client_message_init(client, &request, DHCP_REQUEST,
413 &optlen, &optoffset);
414 if (r < 0)
415 return r;
416
417 switch (client->state) {
418 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
419 SELECTING should be REQUESTING)
420 */
421
422 case DHCP_STATE_REQUESTING:
423 /* Client inserts the address of the selected server in ’server
424 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
425 filled in with the yiaddr value from the chosen DHCPOFFER.
426 */
427
428 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
429 DHCP_OPTION_SERVER_IDENTIFIER,
430 4, &client->lease->server_address);
431 if (r < 0)
432 return r;
433
434 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
435 DHCP_OPTION_REQUESTED_IP_ADDRESS,
436 4, &client->lease->address);
437 if (r < 0)
438 return r;
439
440 break;
441
442 case DHCP_STATE_INIT_REBOOT:
443 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
444 option MUST be filled in with client’s notion of its previously
445 assigned address. ’ciaddr’ MUST be zero.
446 */
447 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
448 DHCP_OPTION_REQUESTED_IP_ADDRESS,
449 4, &client->last_addr);
450 if (r < 0)
451 return r;
452 break;
453
454 case DHCP_STATE_RENEWING:
455 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
456 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
457 client’s IP address.
458 */
459
460 /* fall through */
461 case DHCP_STATE_REBINDING:
462 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
463 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
464 client’s IP address.
465
466 This message MUST be broadcast to the 0xffffffff IP broadcast address.
467 */
468 request->dhcp.ciaddr = client->lease->address;
469
470 break;
471
472 case DHCP_STATE_INIT:
473 case DHCP_STATE_SELECTING:
474 case DHCP_STATE_REBOOTING:
475 case DHCP_STATE_BOUND:
476 case DHCP_STATE_STOPPED:
477 return -EINVAL;
478 }
479
480 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
481 DHCP_OPTION_END, 0, NULL);
482 if (r < 0)
483 return r;
484
485 if (client->state == DHCP_STATE_RENEWING) {
486 r = dhcp_network_send_udp_socket(client->fd,
487 client->lease->server_address,
488 DHCP_PORT_SERVER,
489 &request->dhcp,
490 sizeof(DHCPMessage) + optoffset);
491 } else {
492 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
493 }
494 if (r < 0)
495 return r;
496
497 switch (client->state) {
498 case DHCP_STATE_REQUESTING:
499 log_dhcp_client(client, "REQUEST (requesting)");
500 break;
501 case DHCP_STATE_INIT_REBOOT:
502 log_dhcp_client(client, "REQUEST (init-reboot)");
503 break;
504 case DHCP_STATE_RENEWING:
505 log_dhcp_client(client, "REQUEST (renewing)");
506 break;
507 case DHCP_STATE_REBINDING:
508 log_dhcp_client(client, "REQUEST (rebinding)");
509 break;
510 default:
511 log_dhcp_client(client, "REQUEST (invalid)");
512 break;
513 }
514
515 return 0;
516}
517
518static int client_start(sd_dhcp_client *client);
519
520static int client_timeout_resend(sd_event_source *s, uint64_t usec,
521 void *userdata) {
522 sd_dhcp_client *client = userdata;
523 DHCP_CLIENT_DONT_DESTROY(client);
524 usec_t next_timeout = 0;
525 uint64_t time_now;
526 uint32_t time_left;
527 int r;
528
529 assert(s);
530 assert(client);
531 assert(client->event);
532
533 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
534 if (r < 0)
535 goto error;
536
537 switch (client->state) {
538 case DHCP_STATE_RENEWING:
539
540 time_left = (client->lease->t2 - client->lease->t1) / 2;
541 if (time_left < 60)
542 time_left = 60;
543
544 next_timeout = time_now + time_left * USEC_PER_SEC;
545
546 break;
547
548 case DHCP_STATE_REBINDING:
549
550 time_left = (client->lease->lifetime - client->lease->t2) / 2;
551 if (time_left < 60)
552 time_left = 60;
553
554 next_timeout = time_now + time_left * USEC_PER_SEC;
555 break;
556
557 case DHCP_STATE_REBOOTING:
558 /* start over as we did not receive a timely ack or nak */
559 r = client_initialize(client);
560 if (r < 0)
561 goto error;
562
563 r = client_start(client);
564 if (r < 0)
565 goto error;
566 else {
567 log_dhcp_client(client, "REBOOTED");
568 return 0;
569 }
570
571 case DHCP_STATE_INIT:
572 case DHCP_STATE_INIT_REBOOT:
573 case DHCP_STATE_SELECTING:
574 case DHCP_STATE_REQUESTING:
575 case DHCP_STATE_BOUND:
576
577 if (client->attempt < 64)
578 client->attempt *= 2;
579
580 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
581
582 break;
583
584 case DHCP_STATE_STOPPED:
585 r = -EINVAL;
586 goto error;
587 }
588
589 next_timeout += (random_u32() & 0x1fffff);
590
591 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
592
593 r = sd_event_add_time(client->event,
594 &client->timeout_resend,
595 CLOCK_MONOTONIC,
596 next_timeout, 10 * USEC_PER_MSEC,
597 client_timeout_resend, client);
598 if (r < 0)
599 goto error;
600
601 r = sd_event_source_set_priority(client->timeout_resend,
602 client->event_priority);
603 if (r < 0)
604 goto error;
605
606 switch (client->state) {
607 case DHCP_STATE_INIT:
608 r = client_send_discover(client);
609 if (r >= 0) {
610 client->state = DHCP_STATE_SELECTING;
611 client->attempt = 1;
612 } else {
613 if (client->attempt >= 64)
614 goto error;
615 }
616
617 break;
618
619 case DHCP_STATE_SELECTING:
620 r = client_send_discover(client);
621 if (r < 0 && client->attempt >= 64)
622 goto error;
623
624 break;
625
626 case DHCP_STATE_INIT_REBOOT:
627 case DHCP_STATE_REQUESTING:
628 case DHCP_STATE_RENEWING:
629 case DHCP_STATE_REBINDING:
630 r = client_send_request(client);
631 if (r < 0 && client->attempt >= 64)
632 goto error;
633
634 if (client->state == DHCP_STATE_INIT_REBOOT)
635 client->state = DHCP_STATE_REBOOTING;
636
637 client->request_sent = time_now;
638
639 break;
640
641 case DHCP_STATE_REBOOTING:
642 case DHCP_STATE_BOUND:
643
644 break;
645
646 case DHCP_STATE_STOPPED:
647 r = -EINVAL;
648 goto error;
649 }
650
651 return 0;
652
653error:
654 client_stop(client, r);
655
656 /* Errors were dealt with when stopping the client, don't spill
657 errors into the event loop handler */
658 return 0;
659}
660
661static int client_initialize_events(sd_dhcp_client *client,
662 sd_event_io_handler_t io_callback) {
663 int r;
664
665 assert(client);
666 assert(client->event);
667
668 r = sd_event_add_io(client->event, &client->receive_message,
669 client->fd, EPOLLIN, io_callback,
670 client);
671 if (r < 0)
672 goto error;
673
674 r = sd_event_source_set_priority(client->receive_message,
675 client->event_priority);
676 if (r < 0)
677 goto error;
678
679 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
680
681 r = sd_event_add_time(client->event,
682 &client->timeout_resend,
683 CLOCK_MONOTONIC,
684 0, 0,
685 client_timeout_resend, client);
686 if (r < 0)
687 goto error;
688
689 r = sd_event_source_set_priority(client->timeout_resend,
690 client->event_priority);
691
692error:
693 if (r < 0)
694 client_stop(client, r);
695
696 return 0;
697
698}
699
700static int client_start(sd_dhcp_client *client) {
701 int r;
702
703 assert_return(client, -EINVAL);
704 assert_return(client->event, -EINVAL);
705 assert_return(client->index > 0, -EINVAL);
706 assert_return(client->fd < 0, -EBUSY);
707 assert_return(client->xid == 0, -EINVAL);
708 assert_return(client->state == DHCP_STATE_INIT ||
709 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
710
711 client->xid = random_u32();
712
713 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
714 if (r < 0) {
715 client_stop(client, r);
716 return r;
717 }
718 client->fd = r;
719
720 if (client->state == DHCP_STATE_INIT) {
721 client->start_time = now(CLOCK_MONOTONIC);
722 client->secs = 0;
723 }
724
725 return client_initialize_events(client, client_receive_message_raw);
726}
727
728static int client_timeout_expire(sd_event_source *s, uint64_t usec,
729 void *userdata) {
730 sd_dhcp_client *client = userdata;
731 DHCP_CLIENT_DONT_DESTROY(client);
732
733 log_dhcp_client(client, "EXPIRED");
734
735 client_notify(client, DHCP_EVENT_EXPIRED);
736
737 /* lease was lost, start over if not freed or stopped in callback */
738 if (client->state != DHCP_STATE_STOPPED) {
739 client_initialize(client);
740 client_start(client);
741 }
742
743 return 0;
744}
745
746static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
747 sd_dhcp_client *client = userdata;
748 DHCP_CLIENT_DONT_DESTROY(client);
749 int r;
750
751 client->receive_message = sd_event_source_unref(client->receive_message);
752 client->fd = asynchronous_close(client->fd);
753
754 client->state = DHCP_STATE_REBINDING;
755 client->attempt = 1;
756
757 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
758 if (r < 0) {
759 client_stop(client, r);
760 return 0;
761 }
762 client->fd = r;
763
764 return client_initialize_events(client, client_receive_message_raw);
765}
766
767static int client_timeout_t1(sd_event_source *s, uint64_t usec,
768 void *userdata) {
769 sd_dhcp_client *client = userdata;
770 DHCP_CLIENT_DONT_DESTROY(client);
771 int r;
772
773 client->state = DHCP_STATE_RENEWING;
774 client->attempt = 1;
775
776 r = dhcp_network_bind_udp_socket(client->lease->address,
777 DHCP_PORT_CLIENT);
778 if (r < 0) {
779 client_stop(client, r);
780 return 0;
781 }
782
783 client->fd = r;
784
785 return client_initialize_events(client, client_receive_message_udp);
786}
787
788static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
789 size_t len) {
790 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
791 int r;
792
793 r = dhcp_lease_new(&lease);
794 if (r < 0)
795 return r;
796
797 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
798 if (r != DHCP_OFFER) {
799 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
800 return -ENOMSG;
801 }
802
803 lease->next_server = offer->siaddr;
804
805 lease->address = offer->yiaddr;
806
807 if (lease->address == INADDR_ANY ||
808 lease->server_address == INADDR_ANY ||
809 lease->lifetime == 0) {
810 log_dhcp_client(client, "receieved lease lacks address, server "
811 "address or lease lifetime, ignoring");
812 return -ENOMSG;
813 }
814
815 if (lease->subnet_mask == INADDR_ANY) {
816 r = dhcp_lease_set_default_subnet_mask(lease);
817 if (r < 0) {
818 log_dhcp_client(client, "receieved lease lacks subnet "
819 "mask, and a fallback one can not be "
820 "generated, ignoring");
821 return -ENOMSG;
822 }
823 }
824
825 sd_dhcp_lease_unref(client->lease);
826 client->lease = lease;
827 lease = NULL;
828
829 log_dhcp_client(client, "OFFER");
830
831 return 0;
832}
833
834static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
835 size_t len) {
836 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
837 int r;
838
839 r = dhcp_lease_new(&lease);
840 if (r < 0)
841 return r;
842
843 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
844 if (r == DHCP_NAK) {
845 log_dhcp_client(client, "NAK");
846 return DHCP_EVENT_NO_LEASE;
847 }
848
849 if (r != DHCP_ACK) {
850 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
851 return -ENOMSG;
852 }
853
854 lease->next_server = ack->siaddr;
855
856 lease->address = ack->yiaddr;
857
858 if (lease->address == INADDR_ANY ||
859 lease->server_address == INADDR_ANY ||
860 lease->lifetime == 0) {
861 log_dhcp_client(client, "receieved lease lacks address, server "
862 "address or lease lifetime, ignoring");
863 return -ENOMSG;
864 }
865
866 if (lease->subnet_mask == INADDR_ANY) {
867 r = dhcp_lease_set_default_subnet_mask(lease);
868 if (r < 0) {
869 log_dhcp_client(client, "receieved lease lacks subnet "
870 "mask, and a fallback one can not be "
871 "generated, ignoring");
872 return -ENOMSG;
873 }
874 }
875
876 r = DHCP_EVENT_IP_ACQUIRE;
877 if (client->lease) {
878 if (client->lease->address != lease->address ||
879 client->lease->subnet_mask != lease->subnet_mask ||
880 client->lease->router != lease->router) {
881 r = DHCP_EVENT_IP_CHANGE;
882 } else
883 r = DHCP_EVENT_RENEW;
884
885 client->lease = sd_dhcp_lease_unref(client->lease);
886 }
887
888 client->lease = lease;
889 lease = NULL;
890
891 log_dhcp_client(client, "ACK");
892
893 return r;
894}
895
896static uint64_t client_compute_timeout(sd_dhcp_client *client,
897 uint32_t lifetime, double factor) {
898 assert(client);
899 assert(client->request_sent);
900 assert(lifetime);
901
902 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
903 + (random_u32() & 0x1fffff);
904}
905
906static int client_set_lease_timeouts(sd_dhcp_client *client) {
907 usec_t time_now;
908 uint64_t lifetime_timeout;
909 uint64_t t2_timeout;
910 uint64_t t1_timeout;
911 char time_string[FORMAT_TIMESPAN_MAX];
912 int r;
913
914 assert(client);
915 assert(client->event);
916 assert(client->lease);
917 assert(client->lease->lifetime);
918
919 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
920 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
921 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
922
923 /* don't set timers for infinite leases */
924 if (client->lease->lifetime == 0xffffffff)
925 return 0;
926
927 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
928 if (r < 0)
929 return r;
930 assert(client->request_sent <= time_now);
931
932 /* convert the various timeouts from relative (secs) to absolute (usecs) */
933 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
934 if (client->lease->t1 && client->lease->t2) {
935 /* both T1 and T2 are given */
936 if (client->lease->t1 < client->lease->t2 &&
937 client->lease->t2 < client->lease->lifetime) {
938 /* they are both valid */
939 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
940 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
941 } else {
942 /* discard both */
943 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
944 client->lease->t2 = (client->lease->lifetime * 7) / 8;
945 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
946 client->lease->t1 = client->lease->lifetime / 2;
947 }
948 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
949 /* only T2 is given, and it is valid */
950 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
951 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
952 client->lease->t1 = client->lease->lifetime / 2;
953 if (t2_timeout <= t1_timeout) {
954 /* the computed T1 would be invalid, so discard T2 */
955 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
956 client->lease->t2 = (client->lease->lifetime * 7) / 8;
957 }
958 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
959 /* only T1 is given, and it is valid */
960 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
961 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
962 client->lease->t2 = (client->lease->lifetime * 7) / 8;
963 if (t2_timeout <= t1_timeout) {
964 /* the computed T2 would be invalid, so discard T1 */
965 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
966 client->lease->t2 = client->lease->lifetime / 2;
967 }
968 } else {
969 /* fall back to the default timeouts */
970 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
971 client->lease->t1 = client->lease->lifetime / 2;
972 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
973 client->lease->t2 = (client->lease->lifetime * 7) / 8;
974 }
975
976 /* arm lifetime timeout */
977 r = sd_event_add_time(client->event, &client->timeout_expire,
978 CLOCK_MONOTONIC,
979 lifetime_timeout, 10 * USEC_PER_MSEC,
980 client_timeout_expire, client);
981 if (r < 0)
982 return r;
983
984 r = sd_event_source_set_priority(client->timeout_expire,
985 client->event_priority);
986 if (r < 0)
987 return r;
988
989 log_dhcp_client(client, "lease expires in %s",
990 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
991 lifetime_timeout - time_now, 0));
992
993 /* don't arm earlier timeouts if this has already expired */
994 if (lifetime_timeout <= time_now)
995 return 0;
996
997 /* arm T2 timeout */
998 r = sd_event_add_time(client->event,
999 &client->timeout_t2,
1000 CLOCK_MONOTONIC,
1001 t2_timeout,
1002 10 * USEC_PER_MSEC,
1003 client_timeout_t2, client);
1004 if (r < 0)
1005 return r;
1006
1007 r = sd_event_source_set_priority(client->timeout_t2,
1008 client->event_priority);
1009 if (r < 0)
1010 return r;
1011
1012 log_dhcp_client(client, "T2 expires in %s",
1013 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1014 t2_timeout - time_now, 0));
1015
1016 /* don't arm earlier timeout if this has already expired */
1017 if (t2_timeout <= time_now)
1018 return 0;
1019
1020 /* arm T1 timeout */
1021 r = sd_event_add_time(client->event,
1022 &client->timeout_t1,
1023 CLOCK_MONOTONIC,
1024 t1_timeout, 10 * USEC_PER_MSEC,
1025 client_timeout_t1, client);
1026 if (r < 0)
1027 return r;
1028
1029 r = sd_event_source_set_priority(client->timeout_t1,
1030 client->event_priority);
1031 if (r < 0)
1032 return r;
1033
1034 log_dhcp_client(client, "T1 expires in %s",
1035 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1036 t1_timeout - time_now, 0));
1037
1038 return 0;
1039}
1040
1041static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
1042 int len) {
1043 DHCP_CLIENT_DONT_DESTROY(client);
1044 int r = 0, notify_event = 0;
1045
1046 assert(client);
1047 assert(client->event);
1048 assert(message);
1049
1050 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1051 log_dhcp_client(client, "not a DHCP message: ignoring");
1052 return 0;
1053 }
1054
1055 if (message->op != BOOTREPLY) {
1056 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1057 return 0;
1058 }
1059
1060 if (be32toh(message->xid) != client->xid) {
1061 log_dhcp_client(client, "received xid (%u) does not match "
1062 "expected (%u): ignoring",
1063 be32toh(message->xid), client->xid);
1064 return 0;
1065 }
1066
1067 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1068 log_dhcp_client(client, "not an ethernet packet");
1069 return 0;
1070 }
1071
1072 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1073 ETH_ALEN)) {
1074 log_dhcp_client(client, "received chaddr does not match "
1075 "expected: ignoring");
1076 return 0;
1077 }
1078
1079 switch (client->state) {
1080 case DHCP_STATE_SELECTING:
1081
1082 r = client_handle_offer(client, message, len);
1083 if (r >= 0) {
1084
1085 client->timeout_resend =
1086 sd_event_source_unref(client->timeout_resend);
1087
1088 client->state = DHCP_STATE_REQUESTING;
1089 client->attempt = 1;
1090
1091 r = sd_event_add_time(client->event,
1092 &client->timeout_resend,
1093 CLOCK_MONOTONIC,
1094 0, 0,
1095 client_timeout_resend, client);
1096 if (r < 0)
1097 goto error;
1098
1099 r = sd_event_source_set_priority(client->timeout_resend,
1100 client->event_priority);
1101 if (r < 0)
1102 goto error;
1103 } else if (r == -ENOMSG)
1104 /* invalid message, let's ignore it */
1105 return 0;
1106
1107 break;
1108
1109 case DHCP_STATE_REBOOTING:
1110 case DHCP_STATE_REQUESTING:
1111 case DHCP_STATE_RENEWING:
1112 case DHCP_STATE_REBINDING:
1113
1114 r = client_handle_ack(client, message, len);
1115 if (r == DHCP_EVENT_NO_LEASE) {
1116
1117 client->timeout_resend =
1118 sd_event_source_unref(client->timeout_resend);
1119
1120 if (client->state == DHCP_STATE_REBOOTING) {
1121 r = client_initialize(client);
1122 if (r < 0)
1123 goto error;
1124
1125 r = client_start(client);
1126 if (r < 0)
1127 goto error;
1128
1129 log_dhcp_client(client, "REBOOTED");
1130 }
1131
1132 goto error;
1133 } else if (r >= 0) {
1134 client->timeout_resend =
1135 sd_event_source_unref(client->timeout_resend);
1136
1137 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1138 DHCP_STATE_REBOOTING))
1139 notify_event = DHCP_EVENT_IP_ACQUIRE;
1140 else if (r != DHCP_EVENT_IP_ACQUIRE)
1141 notify_event = r;
1142
1143 client->state = DHCP_STATE_BOUND;
1144 client->attempt = 1;
1145
1146 client->last_addr = client->lease->address;
1147
1148 r = client_set_lease_timeouts(client);
1149 if (r < 0)
1150 goto error;
1151
1152 if (notify_event) {
1153 client_notify(client, notify_event);
1154 if (client->state == DHCP_STATE_STOPPED)
1155 return 0;
1156 }
1157
1158 client->receive_message =
1159 sd_event_source_unref(client->receive_message);
1160 client->fd = asynchronous_close(client->fd);
1161 } else if (r == -ENOMSG)
1162 /* invalid message, let's ignore it */
1163 return 0;
1164
1165 break;
1166
1167 case DHCP_STATE_INIT:
1168 case DHCP_STATE_INIT_REBOOT:
1169 case DHCP_STATE_BOUND:
1170
1171 break;
1172
1173 case DHCP_STATE_STOPPED:
1174 r = -EINVAL;
1175 goto error;
1176 }
1177
1178error:
1179 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1180 client_stop(client, r);
1181
1182 return r;
1183}
1184
1185static int client_receive_message_udp(sd_event_source *s, int fd,
1186 uint32_t revents, void *userdata) {
1187 sd_dhcp_client *client = userdata;
1188 _cleanup_free_ DHCPMessage *message = NULL;
1189 int buflen = 0, len, r;
1190
1191 assert(s);
1192 assert(client);
1193
1194 r = ioctl(fd, FIONREAD, &buflen);
1195 if (r < 0)
1196 return r;
1197
1198 if (buflen < 0)
1199 /* this can't be right */
1200 return -EIO;
1201
1202 message = malloc0(buflen);
1203 if (!message)
1204 return -ENOMEM;
1205
1206 len = read(fd, message, buflen);
1207 if (len < 0) {
1208 log_dhcp_client(client, "could not receive message from UDP "
1209 "socket: %m");
1210 return 0;
1211 } else if ((size_t)len < sizeof(DHCPMessage))
1212 return 0;
1213
1214 return client_handle_message(client, message, len);
1215}
1216
1217static int client_receive_message_raw(sd_event_source *s, int fd,
1218 uint32_t revents, void *userdata) {
1219 sd_dhcp_client *client = userdata;
1220 _cleanup_free_ DHCPPacket *packet = NULL;
1221 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1222 struct iovec iov = {};
1223 struct msghdr msg = {
1224 .msg_iov = &iov,
1225 .msg_iovlen = 1,
1226 .msg_control = cmsgbuf,
1227 .msg_controllen = sizeof(cmsgbuf),
1228 };
1229 struct cmsghdr *cmsg;
1230 bool checksum = true;
1231 int buflen = 0, len, r;
1232
1233 assert(s);
1234 assert(client);
1235
1236 r = ioctl(fd, FIONREAD, &buflen);
1237 if (r < 0)
1238 return r;
1239
1240 if (buflen < 0)
1241 /* this can't be right */
1242 return -EIO;
1243
1244 packet = malloc0(buflen);
1245 if (!packet)
1246 return -ENOMEM;
1247
1248 iov.iov_base = packet;
1249 iov.iov_len = buflen;
1250
1251 len = recvmsg(fd, &msg, 0);
1252 if (len < 0) {
1253 log_dhcp_client(client, "could not receive message from raw "
1254 "socket: %m");
1255 return 0;
1256 } else if ((size_t)len < sizeof(DHCPPacket))
1257 return 0;
1258
1259 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1260 if (cmsg->cmsg_level == SOL_PACKET &&
1261 cmsg->cmsg_type == PACKET_AUXDATA &&
1262 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1263 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1264
1265 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1266 break;
1267 }
1268 }
1269
1270 r = dhcp_packet_verify_headers(packet, len, checksum);
1271 if (r < 0)
1272 return 0;
1273
1274 len -= DHCP_IP_UDP_SIZE;
1275
1276 return client_handle_message(client, &packet->dhcp, len);
1277}
1278
1279int sd_dhcp_client_start(sd_dhcp_client *client) {
1280 int r;
1281
1282 assert_return(client, -EINVAL);
1283
1284 r = client_initialize(client);
1285 if (r < 0)
1286 return r;
1287
1288 if (client->last_addr)
1289 client->state = DHCP_STATE_INIT_REBOOT;
1290
1291 r = client_start(client);
1292 if (r >= 0)
1293 log_dhcp_client(client, "STARTED on ifindex %u with address %s",
1294 client->index,
1295 ether_ntoa(&client->client_id.mac_addr));
1296
1297 return r;
1298}
1299
1300int sd_dhcp_client_stop(sd_dhcp_client *client) {
1301 DHCP_CLIENT_DONT_DESTROY(client);
1302
1303 assert_return(client, -EINVAL);
1304
1305 client_stop(client, DHCP_EVENT_STOP);
1306 client->state = DHCP_STATE_STOPPED;
1307
1308 return 0;
1309}
1310
1311int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1312 int priority) {
1313 int r;
1314
1315 assert_return(client, -EINVAL);
1316 assert_return(!client->event, -EBUSY);
1317
1318 if (event)
1319 client->event = sd_event_ref(event);
1320 else {
1321 r = sd_event_default(&client->event);
1322 if (r < 0)
1323 return 0;
1324 }
1325
1326 client->event_priority = priority;
1327
1328 return 0;
1329}
1330
1331int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1332 assert_return(client, -EINVAL);
1333
1334 client->event = sd_event_unref(client->event);
1335
1336 return 0;
1337}
1338
1339sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1340 if (!client)
1341 return NULL;
1342
1343 return client->event;
1344}
1345
1346sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1347 if (client)
1348 assert_se(REFCNT_INC(client->n_ref) >= 2);
1349
1350 return client;
1351}
1352
1353sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1354 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1355 log_dhcp_client(client, "FREE");
1356
1357 client_initialize(client);
1358
1359 client->receive_message =
1360 sd_event_source_unref(client->receive_message);
1361
1362 sd_dhcp_client_detach_event(client);
1363
1364 sd_dhcp_lease_unref(client->lease);
1365
1366 free(client->req_opts);
1367 free(client);
1368 }
1369
1370 return NULL;
1371}
1372
1373int sd_dhcp_client_new(sd_dhcp_client **ret) {
1374 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
1375
1376 assert_return(ret, -EINVAL);
1377
1378 client = new0(sd_dhcp_client, 1);
1379 if (!client)
1380 return -ENOMEM;
1381
1382 client->n_ref = REFCNT_INIT;
1383 client->state = DHCP_STATE_INIT;
1384 client->index = -1;
1385 client->fd = -1;
1386 client->attempt = 1;
1387
1388 client->req_opts_size = ELEMENTSOF(default_req_opts);
1389
1390 client->req_opts = memdup(default_req_opts, client->req_opts_size);
1391 if (!client->req_opts)
1392 return -ENOMEM;
1393
1394 *ret = client;
1395 client = NULL;
1396
1397 return 0;
1398}