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