]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-client.c
libsystemd-network: Add Init-Reboot support
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-client.c
CommitLineData
011feef8
PF
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>
46a66b79 24#include <net/ethernet.h>
aba26854 25#include <sys/param.h>
5266a81e 26#include <sys/ioctl.h>
011feef8
PF
27
28#include "util.h"
29#include "list.h"
30
31#include "dhcp-protocol.h"
46a66b79 32#include "dhcp-internal.h"
fe8db0c5 33#include "dhcp-lease-internal.h"
011feef8
PF
34#include "sd-dhcp-client.h"
35
36struct sd_dhcp_client {
37 DHCPState state;
d3d8ac2f 38 sd_event *event;
b25ef18b 39 int event_priority;
d3d8ac2f 40 sd_event_source *timeout_resend;
011feef8 41 int index;
8c00042c
PF
42 int fd;
43 union sockaddr_union link;
44 sd_event_source *receive_message;
011feef8 45 uint8_t *req_opts;
7a7c74ca 46 size_t req_opts_allocated;
011feef8 47 size_t req_opts_size;
3b349af6 48 be32_t last_addr;
46a66b79
PF
49 struct ether_addr mac_addr;
50 uint32_t xid;
d3d8ac2f 51 usec_t start_time;
40e39f62 52 uint16_t secs;
e2dfc79f 53 unsigned int attempt;
51debc1e
PF
54 usec_t request_sent;
55 sd_event_source *timeout_t1;
56 sd_event_source *timeout_t2;
57 sd_event_source *timeout_expire;
751246ee
PF
58 sd_dhcp_client_cb_t cb;
59 void *userdata;
a6cc569e 60 sd_dhcp_lease *lease;
011feef8
PF
61};
62
63static const uint8_t default_req_opts[] = {
64 DHCP_OPTION_SUBNET_MASK,
65 DHCP_OPTION_ROUTER,
66 DHCP_OPTION_HOST_NAME,
67 DHCP_OPTION_DOMAIN_NAME,
68 DHCP_OPTION_DOMAIN_NAME_SERVER,
69 DHCP_OPTION_NTP_SERVER,
70};
71
e5002702
TG
72static int client_receive_message_raw(sd_event_source *s, int fd,
73 uint32_t revents, void *userdata);
74static int client_receive_message_udp(sd_event_source *s, int fd,
75 uint32_t revents, void *userdata);
aba26854 76
751246ee 77int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
5ee482df 78 void *userdata) {
751246ee
PF
79 assert_return(client, -EINVAL);
80
81 client->cb = cb;
82 client->userdata = userdata;
83
84 return 0;
85}
86
5ee482df 87int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
011feef8
PF
88 size_t i;
89
90 assert_return(client, -EINVAL);
91 assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
92
93 switch(option) {
94 case DHCP_OPTION_PAD:
95 case DHCP_OPTION_OVERLOAD:
96 case DHCP_OPTION_MESSAGE_TYPE:
97 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
98 case DHCP_OPTION_END:
99 return -EINVAL;
100
101 default:
102 break;
103 }
104
105 for (i = 0; i < client->req_opts_size; i++)
106 if (client->req_opts[i] == option)
107 return -EEXIST;
108
7a7c74ca 109 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
011feef8
PF
110 client->req_opts_size + 1))
111 return -ENOMEM;
112
7a7c74ca 113 client->req_opts[client->req_opts_size++] = option;
011feef8
PF
114
115 return 0;
116}
117
118int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
5ee482df 119 const struct in_addr *last_addr) {
011feef8
PF
120 assert_return(client, -EINVAL);
121 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
122
123 if (last_addr)
124 client->last_addr = last_addr->s_addr;
125 else
126 client->last_addr = INADDR_ANY;
127
128 return 0;
129}
130
5ee482df 131int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
011feef8
PF
132 assert_return(client, -EINVAL);
133 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
134 assert_return(interface_index >= -1, -EINVAL);
135
136 client->index = interface_index;
137
138 return 0;
139}
140
46a66b79 141int sd_dhcp_client_set_mac(sd_dhcp_client *client,
5ee482df 142 const struct ether_addr *addr) {
46a66b79
PF
143 assert_return(client, -EINVAL);
144 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
145
02ec5cd7
TG
146 log_dhcp_client(client, "set MAC address to "
147 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
148 addr->ether_addr_octet[0],
149 addr->ether_addr_octet[1],
150 addr->ether_addr_octet[2],
151 addr->ether_addr_octet[3],
152 addr->ether_addr_octet[4],
153 addr->ether_addr_octet[5]);
154
46a66b79
PF
155 memcpy(&client->mac_addr, addr, ETH_ALEN);
156
157 return 0;
158}
159
a6cc569e 160int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
751246ee 161 assert_return(client, -EINVAL);
a6cc569e 162 assert_return(ret, -EINVAL);
751246ee 163
a6cc569e
TG
164 if (client->state != DHCP_STATE_BOUND &&
165 client->state != DHCP_STATE_RENEWING &&
166 client->state != DHCP_STATE_REBINDING)
167 return -EADDRNOTAVAIL;
751246ee 168
a6cc569e 169 *ret = sd_dhcp_lease_ref(client->lease);
4f882b2a
TG
170
171 return 0;
172}
173
5ee482df 174static int client_notify(sd_dhcp_client *client, int event) {
751246ee
PF
175 if (client->cb)
176 client->cb(client, event, client->userdata);
177
3e3d8f78
PF
178 return 0;
179}
180
0f941add 181static int client_initialize(sd_dhcp_client *client) {
bbdf06d9 182 assert_return(client, -EINVAL);
bbdf06d9 183
8c00042c
PF
184 client->receive_message =
185 sd_event_source_unref(client->receive_message);
186
03e334a1 187 client->fd = safe_close(client->fd);
8c00042c 188
d3d8ac2f
PF
189 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
190
51debc1e
PF
191 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
192 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
193 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
194
e2dfc79f
PF
195 client->attempt = 1;
196
f8fdefe4 197 client->start_time = 0;
40e39f62 198 client->secs = 0;
f8fdefe4 199 client->state = DHCP_STATE_INIT;
0f941add 200 client->xid = 0;
bbdf06d9 201
a6cc569e
TG
202 if (client->lease)
203 client->lease = sd_dhcp_lease_unref(client->lease);
8c00042c 204
0f941add
PF
205 return 0;
206}
207
208static int client_stop(sd_dhcp_client *client, int error) {
209 assert_return(client, -EINVAL);
210
211 client_notify(client, error);
212
213 client_initialize(client);
214
ee57a737
TG
215 log_dhcp_client(client, "STOPPED");
216
bbdf06d9
PF
217 return 0;
218}
219
cf597f65
TG
220static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
221 uint8_t type, uint16_t secs, uint8_t **opt,
222 size_t *optlen) {
223 int r;
46a66b79 224
0a1b6da8
TG
225 assert(secs);
226
227 r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
228 optlen);
cf597f65
TG
229 if (r < 0)
230 return r;
46a66b79 231
0a1b6da8
TG
232 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
233 refuse to issue an DHCP lease if 'secs' is set to zero */
234 message->secs = htobe16(secs);
235
cf597f65 236 memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
f5a70de7 237
3dd71400
PF
238 if (client->state == DHCP_STATE_RENEWING ||
239 client->state == DHCP_STATE_REBINDING)
aba26854
PF
240 message->ciaddr = client->lease->address;
241
ee57a737 242 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
46a66b79 243 Identifier option is not set */
cf597f65
TG
244 r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
245 ETH_ALEN, &client->mac_addr);
246 if (r < 0)
247 return r;
46a66b79
PF
248
249 if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
cf597f65
TG
250 be16_t max_size;
251
252 r = dhcp_option_append(opt, optlen,
253 DHCP_OPTION_PARAMETER_REQUEST_LIST,
254 client->req_opts_size,
255 client->req_opts);
256 if (r < 0)
257 return r;
564ba3b0
PF
258
259 /* Some DHCP servers will send bigger DHCP packets than the
260 defined default size unless the Maximum Messge Size option
261 is explicitely set */
262 max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
0bfedf14 263 DHCP_MIN_OPTIONS_SIZE);
cf597f65
TG
264 r = dhcp_option_append(opt, optlen,
265 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
266 2, &max_size);
267 if (r < 0)
268 return r;
46a66b79
PF
269 }
270
271 return 0;
272}
273
63edaa62
TG
274static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
275 size_t len) {
276 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
277 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
278
279 return dhcp_network_send_raw_socket(client->fd, &client->link,
280 packet, len);
281}
282
5ee482df 283static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
46a66b79
PF
284 int err = 0;
285 _cleanup_free_ DHCPPacket *discover;
286 size_t optlen, len;
287 uint8_t *opt;
288
0bfedf14 289 optlen = DHCP_MIN_OPTIONS_SIZE;
46a66b79
PF
290 len = sizeof(DHCPPacket) + optlen;
291
292 discover = malloc0(len);
293
294 if (!discover)
295 return -ENOMEM;
296
cf597f65
TG
297 err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
298 secs, &opt, &optlen);
46a66b79
PF
299 if (err < 0)
300 return err;
301
302 if (client->last_addr != INADDR_ANY) {
303 err = dhcp_option_append(&opt, &optlen,
304 DHCP_OPTION_REQUESTED_IP_ADDRESS,
305 4, &client->last_addr);
306 if (err < 0)
307 return err;
308 }
309
310 err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
311 if (err < 0)
312 return err;
313
63edaa62
TG
314 err = dhcp_client_send_raw(client, discover, len);
315 if (err < 0)
316 return err;
e2dfc79f 317
ee57a737
TG
318 log_dhcp_client(client, "DISCOVER");
319
63edaa62 320 return 0;
e2dfc79f
PF
321}
322
5ee482df 323static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
e2dfc79f
PF
324 _cleanup_free_ DHCPPacket *request;
325 size_t optlen, len;
326 int err;
327 uint8_t *opt;
328
0bfedf14 329 optlen = DHCP_MIN_OPTIONS_SIZE;
279efc70 330 len = sizeof(DHCPPacket) + optlen;
e2dfc79f
PF
331
332 request = malloc0(len);
333 if (!request)
334 return -ENOMEM;
335
cf597f65
TG
336 err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
337 &opt, &optlen);
e2dfc79f
PF
338 if (err < 0)
339 return err;
46a66b79 340
8b1243f7
PF
341 switch (client->state) {
342
343 case DHCP_STATE_INIT_REBOOT:
344 err = dhcp_option_append(&opt, &optlen,
345 DHCP_OPTION_REQUESTED_IP_ADDRESS,
346 4, &client->last_addr);
347 if (err < 0)
348 return err;
349 break;
350
351 case DHCP_STATE_REQUESTING:
e2dfc79f
PF
352 err = dhcp_option_append(&opt, &optlen,
353 DHCP_OPTION_REQUESTED_IP_ADDRESS,
354 4, &client->lease->address);
355 if (err < 0)
356 return err;
46a66b79 357
e2dfc79f
PF
358 err = dhcp_option_append(&opt, &optlen,
359 DHCP_OPTION_SERVER_IDENTIFIER,
360 4, &client->lease->server_address);
361 if (err < 0)
362 return err;
8b1243f7
PF
363 break;
364
365 case DHCP_STATE_INIT:
366 case DHCP_STATE_SELECTING:
367 case DHCP_STATE_REBOOTING:
368 case DHCP_STATE_BOUND:
369 case DHCP_STATE_RENEWING:
370 case DHCP_STATE_REBINDING:
371
372 break;
e2dfc79f 373 }
46a66b79 374
e2dfc79f
PF
375 err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
376 if (err < 0)
377 return err;
46a66b79 378
aba26854
PF
379 if (client->state == DHCP_STATE_RENEWING) {
380 err = dhcp_network_send_udp_socket(client->fd,
381 client->lease->server_address,
080ab276 382 DHCP_PORT_SERVER,
aba26854
PF
383 &request->dhcp,
384 len - DHCP_IP_UDP_SIZE);
385 } else {
63edaa62 386 err = dhcp_client_send_raw(client, request, len);
aba26854 387 }
63edaa62
TG
388 if (err < 0)
389 return err;
46a66b79 390
ee57a737
TG
391 log_dhcp_client(client, "REQUEST");
392
63edaa62 393 return 0;
46a66b79
PF
394}
395
40e39f62
PF
396static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
397{
c6f7b693 398 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
40e39f62
PF
399
400 return client->secs;
401}
402
d3d8ac2f 403static int client_timeout_resend(sd_event_source *s, uint64_t usec,
5ee482df 404 void *userdata) {
d3d8ac2f 405 sd_dhcp_client *client = userdata;
aba26854 406 usec_t next_timeout = 0;
d23c45bf 407 uint64_t time_now;
aba26854 408 uint32_t time_left;
d23c45bf 409 int r;
b25ef18b
TG
410
411 assert(s);
412 assert(client);
413 assert(client->event);
d3d8ac2f 414
d23c45bf
TG
415 r = sd_event_get_now_monotonic(client->event, &time_now);
416 if (r < 0)
417 goto error;
418
aba26854
PF
419 switch (client->state) {
420 case DHCP_STATE_RENEWING:
421
e5002702 422 time_left = (client->lease->t2 - client->lease->t1) / 2;
aba26854
PF
423 if (time_left < 60)
424 time_left = 60;
d3d8ac2f 425
d23c45bf 426 next_timeout = time_now + time_left * USEC_PER_SEC;
d3d8ac2f 427
aba26854
PF
428 break;
429
3dd71400
PF
430 case DHCP_STATE_REBINDING:
431
e5002702 432 time_left = (client->lease->lifetime - client->lease->t2) / 2;
3dd71400
PF
433 if (time_left < 60)
434 time_left = 60;
435
d23c45bf 436 next_timeout = time_now + time_left * USEC_PER_SEC;
3dd71400
PF
437 break;
438
8b1243f7
PF
439 case DHCP_STATE_REBOOTING:
440 /* start over as we did not receive a timely ack or nak */
441 client->state = DHCP_STATE_INIT;
442 client->attempt = 1;
443 client->xid = random_u32();
444
445 /* fall through */
aba26854
PF
446 case DHCP_STATE_INIT:
447 case DHCP_STATE_INIT_REBOOT:
aba26854
PF
448 case DHCP_STATE_SELECTING:
449 case DHCP_STATE_REQUESTING:
450 case DHCP_STATE_BOUND:
aba26854
PF
451
452 if (client->attempt < 64)
453 client->attempt *= 2;
454
d23c45bf 455 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
aba26854
PF
456
457 break;
458 }
459
9bf3b535 460 next_timeout += (random_u32() & 0x1fffff);
d3d8ac2f 461
2333154a
UTL
462 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
463
151b9b96
LP
464 r = sd_event_add_monotonic(client->event,
465 &client->timeout_resend,
466 next_timeout,
e2dfc79f 467 10 * USEC_PER_MSEC,
151b9b96 468 client_timeout_resend, client);
b25ef18b
TG
469 if (r < 0)
470 goto error;
471
e5002702
TG
472 r = sd_event_source_set_priority(client->timeout_resend,
473 client->event_priority);
b25ef18b 474 if (r < 0)
e2dfc79f 475 goto error;
d3d8ac2f 476
e2dfc79f
PF
477 switch (client->state) {
478 case DHCP_STATE_INIT:
40e39f62 479
d23c45bf 480 client_update_secs(client, time_now);
40e39f62
PF
481
482 r = client_send_discover(client, client->secs);
b25ef18b 483 if (r >= 0) {
e2dfc79f
PF
484 client->state = DHCP_STATE_SELECTING;
485 client->attempt = 1;
486 } else {
487 if (client->attempt >= 64)
488 goto error;
489 }
490
491 break;
492
493 case DHCP_STATE_SELECTING:
d23c45bf 494 client_update_secs(client, time_now);
40e39f62
PF
495
496 r = client_send_discover(client, client->secs);
b25ef18b 497 if (r < 0 && client->attempt >= 64)
d3d8ac2f
PF
498 goto error;
499
e2dfc79f
PF
500 break;
501
8b1243f7 502 case DHCP_STATE_INIT_REBOOT:
e2dfc79f 503 case DHCP_STATE_REQUESTING:
aba26854 504 case DHCP_STATE_RENEWING:
3dd71400 505 case DHCP_STATE_REBINDING:
40e39f62 506 r = client_send_request(client, client->secs);
b25ef18b 507 if (r < 0 && client->attempt >= 64)
e2dfc79f 508 goto error;
d3d8ac2f 509
8b1243f7
PF
510 if (client->state == DHCP_STATE_INIT_REBOOT)
511 client->state = DHCP_STATE_REBOOTING;
512
d23c45bf 513 client->request_sent = time_now;
51debc1e 514
d3d8ac2f
PF
515 break;
516
d3d8ac2f 517 case DHCP_STATE_REBOOTING:
d3d8ac2f 518 case DHCP_STATE_BOUND:
d3d8ac2f
PF
519
520 break;
521 }
522
523 return 0;
524
525error:
b25ef18b 526 client_stop(client, r);
d3d8ac2f
PF
527
528 /* Errors were dealt with when stopping the client, don't spill
529 errors into the event loop handler */
530 return 0;
531}
532
e5002702 533static int client_initialize_events(sd_dhcp_client *client,
d23c45bf 534 sd_event_io_handler_t io_callback) {
6a1cd41e
PF
535 int r;
536
b25ef18b
TG
537 assert(client);
538 assert(client->event);
539
151b9b96
LP
540 r = sd_event_add_io(client->event, &client->receive_message,
541 client->fd, EPOLLIN, io_callback,
542 client);
6a1cd41e
PF
543 if (r < 0)
544 goto error;
545
e5002702
TG
546 r = sd_event_source_set_priority(client->receive_message,
547 client->event_priority);
b25ef18b
TG
548 if (r < 0)
549 goto error;
550
2333154a
UTL
551 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
552
151b9b96 553 r = sd_event_add_monotonic(client->event,
d23c45bf 554 &client->timeout_resend, 0, 0,
151b9b96 555 client_timeout_resend, client);
b25ef18b
TG
556 if (r < 0)
557 goto error;
558
e5002702
TG
559 r = sd_event_source_set_priority(client->timeout_resend,
560 client->event_priority);
6a1cd41e
PF
561
562error:
563 if (r < 0)
564 client_stop(client, r);
565
566 return 0;
567
568}
569
0f941add
PF
570static int client_start(sd_dhcp_client *client) {
571 int r;
572
573 assert_return(client, -EINVAL);
574 assert_return(client->event, -EINVAL);
575 assert_return(client->index > 0, -EINVAL);
576 assert_return(client->fd < 0, -EBUSY);
577 assert_return(client->xid == 0, -EINVAL);
578 assert_return(client->state == DHCP_STATE_INIT ||
579 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
580
581 client->xid = random_u32();
582
583 r = dhcp_network_bind_raw_socket(client->index, &client->link);
584
585 if (r < 0) {
586 client_stop(client, r);
587 return r;
588 }
589
590 client->fd = r;
591 client->start_time = now(CLOCK_MONOTONIC);
592 client->secs = 0;
593
594 log_dhcp_client(client, "STARTED");
595
596 return client_initialize_events(client, client_receive_message_raw);
597}
598
51debc1e 599static int client_timeout_expire(sd_event_source *s, uint64_t usec,
5ee482df 600 void *userdata) {
751246ee
PF
601 sd_dhcp_client *client = userdata;
602
ee57a737
TG
603 log_dhcp_client(client, "EXPIRED");
604
0f941add
PF
605 client_notify(client, DHCP_EVENT_EXPIRED);
606
607 /* start over as the lease was lost */
608 client_initialize(client);
609 client_start(client);
751246ee 610
51debc1e
PF
611 return 0;
612}
613
5ee482df 614static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
3dd71400
PF
615 sd_dhcp_client *client = userdata;
616 int r;
617
03e334a1
LP
618 client->receive_message = sd_event_source_unref(client->receive_message);
619 client->fd = safe_close(client->fd);
3dd71400
PF
620
621 client->state = DHCP_STATE_REBINDING;
622 client->attempt = 1;
623
624 r = dhcp_network_bind_raw_socket(client->index, &client->link);
625 if (r < 0) {
626 client_stop(client, r);
627 return 0;
628 }
629
630 client->fd = r;
631
ee57a737
TG
632 log_dhcp_client(client, "TIMEOUT T2");
633
d23c45bf 634 return client_initialize_events(client, client_receive_message_raw);
51debc1e
PF
635}
636
e5002702
TG
637static int client_timeout_t1(sd_event_source *s, uint64_t usec,
638 void *userdata) {
aba26854
PF
639 sd_dhcp_client *client = userdata;
640 int r;
641
642 client->state = DHCP_STATE_RENEWING;
643 client->attempt = 1;
644
645 r = dhcp_network_bind_udp_socket(client->index,
080ab276
TG
646 client->lease->address,
647 DHCP_PORT_CLIENT);
6a1cd41e
PF
648 if (r < 0) {
649 client_stop(client, r);
650 return 0;
651 }
aba26854
PF
652
653 client->fd = r;
aba26854 654
ee57a737
TG
655 log_dhcp_client(client, "TIMEOUT T1");
656
d23c45bf 657 return client_initialize_events(client, client_receive_message_udp);
51debc1e
PF
658}
659
e5002702
TG
660static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
661 size_t len) {
a6cc569e 662 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 663 int r;
3e3d8f78 664
a6cc569e
TG
665 r = dhcp_lease_new(&lease);
666 if (r < 0)
667 return r;
8c00042c 668
e5002702 669 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
5ee482df
TG
670 if (r != DHCP_OFFER)
671 return -ENOMSG;
8c00042c 672
8e34a618
TG
673 lease->next_server = offer->siaddr;
674
e5002702 675 lease->address = offer->yiaddr;
8c00042c
PF
676
677 if (lease->address == INADDR_ANY ||
678 lease->server_address == INADDR_ANY ||
679 lease->subnet_mask == INADDR_ANY ||
680 lease->lifetime == 0)
5ee482df 681 return -ENOMSG;
8c00042c
PF
682
683 client->lease = lease;
5ee482df 684 lease = NULL;
8c00042c 685
ee57a737
TG
686 log_dhcp_client(client, "OFFER");
687
8c00042c 688 return 0;
8c00042c
PF
689}
690
e5002702
TG
691static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
692 size_t len) {
a6cc569e 693 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 694 int r;
3e3d8f78 695
a6cc569e
TG
696 r = dhcp_lease_new(&lease);
697 if (r < 0)
698 return r;
3e3d8f78 699
e5002702 700 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
ee57a737
TG
701 if (r == DHCP_NAK) {
702 log_dhcp_client(client, "NAK");
5ee482df 703 return DHCP_EVENT_NO_LEASE;
ee57a737 704 }
3e3d8f78 705
5ee482df
TG
706 if (r != DHCP_ACK)
707 return -ENOMSG;
3e3d8f78 708
8e34a618
TG
709 lease->next_server = ack->siaddr;
710
e5002702 711 lease->address = ack->yiaddr;
3e3d8f78
PF
712
713 if (lease->address == INADDR_ANY ||
714 lease->server_address == INADDR_ANY ||
5ee482df
TG
715 lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
716 return -ENOMSG;
3e3d8f78
PF
717
718 r = DHCP_EVENT_IP_ACQUIRE;
719 if (client->lease) {
720 if (client->lease->address != lease->address ||
721 client->lease->subnet_mask != lease->subnet_mask ||
722 client->lease->router != lease->router) {
723 r = DHCP_EVENT_IP_CHANGE;
724 }
725
a6cc569e 726 client->lease = sd_dhcp_lease_unref(client->lease);
3e3d8f78
PF
727 }
728
729 client->lease = lease;
5ee482df 730 lease = NULL;
3e3d8f78 731
ee57a737
TG
732 log_dhcp_client(client, "ACK");
733
3e3d8f78
PF
734 return r;
735}
736
51debc1e 737static uint64_t client_compute_timeout(uint64_t request_sent,
5ee482df 738 uint32_t lifetime) {
51debc1e 739 return request_sent + (lifetime - 3) * USEC_PER_SEC +
9bf3b535 740 + (random_u32() & 0x1fffff);
51debc1e
PF
741}
742
5ee482df 743static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
51debc1e 744 uint64_t next_timeout;
5ee482df 745 int r;
51debc1e 746
b25ef18b
TG
747 assert(client);
748 assert(client->event);
749
51debc1e
PF
750 if (client->lease->lifetime < 10)
751 return -EINVAL;
752
aba26854
PF
753 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
754 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
755 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
756
51debc1e
PF
757 if (!client->lease->t1)
758 client->lease->t1 = client->lease->lifetime / 2;
759
760 next_timeout = client_compute_timeout(client->request_sent,
761 client->lease->t1);
762 if (next_timeout < usec)
763 return -EINVAL;
764
151b9b96 765 r = sd_event_add_monotonic(client->event,
d23c45bf
TG
766 &client->timeout_t1,
767 next_timeout,
768 10 * USEC_PER_MSEC,
769 client_timeout_t1, client);
5ee482df
TG
770 if (r < 0)
771 return r;
51debc1e 772
e5002702
TG
773 r = sd_event_source_set_priority(client->timeout_t1,
774 client->event_priority);
b25ef18b
TG
775 if (r < 0)
776 return r;
777
51debc1e
PF
778 if (!client->lease->t2)
779 client->lease->t2 = client->lease->lifetime * 7 / 8;
780
781 if (client->lease->t2 < client->lease->t1)
782 return -EINVAL;
783
784 if (client->lease->lifetime < client->lease->t2)
785 return -EINVAL;
786
787 next_timeout = client_compute_timeout(client->request_sent,
788 client->lease->t2);
789 if (next_timeout < usec)
790 return -EINVAL;
791
151b9b96 792 r = sd_event_add_monotonic(client->event,
d23c45bf
TG
793 &client->timeout_t2,
794 next_timeout,
795 10 * USEC_PER_MSEC,
796 client_timeout_t2, client);
5ee482df
TG
797 if (r < 0)
798 return r;
51debc1e 799
e5002702
TG
800 r = sd_event_source_set_priority(client->timeout_t2,
801 client->event_priority);
b25ef18b
TG
802 if (r < 0)
803 return r;
804
51debc1e
PF
805 next_timeout = client_compute_timeout(client->request_sent,
806 client->lease->lifetime);
807 if (next_timeout < usec)
808 return -EINVAL;
809
151b9b96 810 r = sd_event_add_monotonic(client->event,
d23c45bf
TG
811 &client->timeout_expire, next_timeout,
812 10 * USEC_PER_MSEC,
813 client_timeout_expire, client);
5ee482df
TG
814 if (r < 0)
815 return r;
51debc1e 816
e5002702
TG
817 r = sd_event_source_set_priority(client->timeout_expire,
818 client->event_priority);
b25ef18b
TG
819 if (r < 0)
820 return r;
821
51debc1e
PF
822 return 0;
823}
824
e5002702
TG
825static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
826 int len, usec_t time_now) {
827 int r = 0, notify_event = 0;
8c00042c 828
b25ef18b
TG
829 assert(client);
830 assert(client->event);
e5002702 831 assert(message);
b25ef18b 832
06b44be7
TG
833 if (len < DHCP_MESSAGE_SIZE) {
834 log_dhcp_client(client, "message too small (%d bytes): "
835 "ignoring", len);
9fadd4ca 836 return 0;
06b44be7
TG
837 }
838
839 if (message->op != BOOTREPLY) {
840 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
841 return 0;
842 }
843
844 if (be32toh(message->xid) != client->xid) {
845 log_dhcp_client(client, "received xid (%u) does not match "
846 "expected (%u): ignoring",
847 be32toh(message->xid), client->xid);
848 return 0;
849 }
e2dfc79f 850
e5002702 851 if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
06b44be7
TG
852 ETHER_ADDR_LEN)) {
853 log_dhcp_client(client, "received chaddr does not match "
854 "expected: ignoring");
9fadd4ca 855 return 0;
06b44be7 856 }
8c00042c 857
8c00042c
PF
858 switch (client->state) {
859 case DHCP_STATE_SELECTING:
860
e5002702
TG
861 r = client_handle_offer(client, message, len);
862 if (r >= 0) {
8c00042c 863
8c00042c
PF
864 client->timeout_resend =
865 sd_event_source_unref(client->timeout_resend);
866
867 client->state = DHCP_STATE_REQUESTING;
e2dfc79f
PF
868 client->attempt = 1;
869
151b9b96 870 r = sd_event_add_monotonic(client->event,
d23c45bf
TG
871 &client->timeout_resend, 0,
872 0, client_timeout_resend,
151b9b96 873 client);
3e3d8f78 874 if (r < 0)
e2dfc79f 875 goto error;
b25ef18b 876
e5002702
TG
877 r = sd_event_source_set_priority(client->timeout_resend,
878 client->event_priority);
b25ef18b
TG
879 if (r < 0)
880 goto error;
8c00042c
PF
881 }
882
883 break;
884
8b1243f7 885 case DHCP_STATE_REBOOTING:
3e3d8f78 886 case DHCP_STATE_REQUESTING:
aba26854 887 case DHCP_STATE_RENEWING:
3dd71400 888 case DHCP_STATE_REBINDING:
aba26854 889
e5002702 890 r = client_handle_ack(client, message, len);
3e3d8f78 891
8b1243f7
PF
892 if (r == DHCP_EVENT_NO_LEASE) {
893
894 client->timeout_resend =
895 sd_event_source_unref(client->timeout_resend);
896
897 if (client->state == DHCP_STATE_REBOOTING) {
898 r = client_initialize(client);
899 if (r < 0)
900 goto error;
901
902 r = client_start(client);
903 if (r < 0)
904 goto error;
905 }
906
3e3d8f78 907 goto error;
8b1243f7 908 }
3e3d8f78
PF
909
910 if (r >= 0) {
911 client->timeout_resend =
912 sd_event_source_unref(client->timeout_resend);
913
8b1243f7
PF
914 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
915 DHCP_STATE_REBOOTING))
aba26854
PF
916 notify_event = DHCP_EVENT_IP_ACQUIRE;
917 else if (r != DHCP_EVENT_IP_ACQUIRE)
918 notify_event = r;
919
3e3d8f78
PF
920 client->state = DHCP_STATE_BOUND;
921 client->attempt = 1;
922
923 client->last_addr = client->lease->address;
924
51debc1e 925 r = client_set_lease_timeouts(client, time_now);
aba26854 926 if (r < 0)
51debc1e
PF
927 goto error;
928
aba26854
PF
929 if (notify_event)
930 client_notify(client, notify_event);
3e3d8f78 931
3e3d8f78
PF
932 client->receive_message =
933 sd_event_source_unref(client->receive_message);
03e334a1 934 client->fd = safe_close(client->fd);
3e3d8f78 935 }
97b9372d
PF
936
937 r = 0;
938
3e3d8f78
PF
939 break;
940
8c00042c
PF
941 case DHCP_STATE_INIT:
942 case DHCP_STATE_INIT_REBOOT:
8c00042c 943 case DHCP_STATE_BOUND:
8c00042c
PF
944
945 break;
946 }
947
948error:
97b9372d 949 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
3e3d8f78 950 return client_stop(client, r);
e2dfc79f 951
8c00042c
PF
952 return 0;
953}
954
8f61afd8 955static int client_receive_message_udp(sd_event_source *s, int fd,
e5002702
TG
956 uint32_t revents, void *userdata) {
957 sd_dhcp_client *client = userdata;
5266a81e
TG
958 _cleanup_free_ DHCPMessage *message = NULL;
959 int buflen = 0, len, r;
e5002702
TG
960 usec_t time_now;
961
962 assert(s);
963 assert(client);
964 assert(client->event);
965
5266a81e
TG
966 r = ioctl(fd, FIONREAD, &buflen);
967 if (r < 0 || buflen <= 0)
968 buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
969
970 message = malloc0(buflen);
971 if (!message)
972 return -ENOMEM;
973
974 len = read(fd, message, buflen);
e5002702
TG
975 if (len < 0)
976 return 0;
977
978 r = sd_event_get_now_monotonic(client->event, &time_now);
979 if (r < 0)
980 return client_stop(client, r);
981
5266a81e 982 return client_handle_message(client, message, len,
e5002702
TG
983 time_now);
984}
985
8f61afd8 986static int client_receive_message_raw(sd_event_source *s, int fd,
e5002702
TG
987 uint32_t revents, void *userdata) {
988 sd_dhcp_client *client = userdata;
5266a81e 989 _cleanup_free_ DHCPPacket *packet = NULL;
e5002702 990 usec_t time_now;
55dab2ed
TG
991 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
992 struct iovec iov = {};
993 struct msghdr msg = {
994 .msg_iov = &iov,
995 .msg_iovlen = 1,
996 .msg_control = cmsgbuf,
997 .msg_controllen = sizeof(cmsgbuf),
998 };
999 struct cmsghdr *cmsg;
1000 bool checksum = true;
1001 int buflen = 0, len, r;
e5002702
TG
1002
1003 assert(s);
1004 assert(client);
1005 assert(client->event);
1006
5266a81e
TG
1007 r = ioctl(fd, FIONREAD, &buflen);
1008 if (r < 0 || buflen <= 0)
1009 buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
1010
1011 packet = malloc0(buflen);
1012 if (!packet)
1013 return -ENOMEM;
1014
55dab2ed
TG
1015 iov.iov_base = packet;
1016 iov.iov_len = buflen;
1017
1018 len = recvmsg(fd, &msg, 0);
1019 if (len < 0) {
1020 log_dhcp_client(client, "could not receive message from raw "
1021 "socket: %s", strerror(errno));
e5002702 1022 return 0;
55dab2ed
TG
1023 }
1024
1025 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1026 if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
1027 struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
1028
1029 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1030 break;
1031 }
1032 }
e5002702 1033
55dab2ed 1034 r = dhcp_packet_verify_headers(packet, len, checksum);
ac4f16ab 1035 if (r < 0)
9fadd4ca 1036 return 0;
e5002702
TG
1037
1038 len -= DHCP_IP_UDP_SIZE;
1039
1040 r = sd_event_get_now_monotonic(client->event, &time_now);
1041 if (r < 0)
1042 return client_stop(client, r);
1043
1044 return client_handle_message(client, &packet->dhcp, len, time_now);
1045}
1046
5ee482df 1047int sd_dhcp_client_start(sd_dhcp_client *client) {
6a1cd41e 1048 int r;
d3d8ac2f 1049
46a66b79 1050 assert_return(client, -EINVAL);
46a66b79 1051
0f941add
PF
1052 r = client_initialize(client);
1053 if (r < 0)
6a1cd41e 1054 return r;
8c00042c 1055
0f941add
PF
1056 if (client->last_addr)
1057 client->state = DHCP_STATE_INIT_REBOOT;
d3d8ac2f 1058
0f941add 1059 return client_start(client);
46a66b79
PF
1060}
1061
5ee482df 1062int sd_dhcp_client_stop(sd_dhcp_client *client) {
751246ee 1063 return client_stop(client, DHCP_EVENT_STOP);
bbdf06d9
PF
1064}
1065
e5002702
TG
1066int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1067 int priority) {
b25ef18b
TG
1068 int r;
1069
1070 assert_return(client, -EINVAL);
1071 assert_return(!client->event, -EBUSY);
1072
1073 if (event)
1074 client->event = sd_event_ref(event);
1075 else {
1076 r = sd_event_default(&client->event);
1077 if (r < 0)
1078 return 0;
1079 }
1080
1081 client->event_priority = priority;
1082
1083 return 0;
1084}
1085
1086int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1087 assert_return(client, -EINVAL);
1088
1089 client->event = sd_event_unref(client->event);
1090
1091 return 0;
1092}
1093
1094sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1095 if (!client)
1096 return NULL;
1097
1098 return client->event;
1099}
1100
1101void sd_dhcp_client_free(sd_dhcp_client *client) {
1102 if (!client)
1103 return;
d2fe46b5
PF
1104
1105 sd_dhcp_client_stop(client);
b25ef18b 1106 sd_dhcp_client_detach_event(client);
d2fe46b5 1107
d2fe46b5
PF
1108 free(client->req_opts);
1109 free(client);
d2fe46b5
PF
1110}
1111
b25ef18b
TG
1112DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
1113#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
1114
1115int sd_dhcp_client_new(sd_dhcp_client **ret) {
1116 _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
011feef8 1117
b25ef18b 1118 assert_return(ret, -EINVAL);
d3d8ac2f 1119
011feef8
PF
1120 client = new0(sd_dhcp_client, 1);
1121 if (!client)
b25ef18b 1122 return -ENOMEM;
011feef8
PF
1123
1124 client->state = DHCP_STATE_INIT;
1125 client->index = -1;
8c00042c 1126 client->fd = -1;
e2dfc79f 1127 client->attempt = 1;
011feef8
PF
1128
1129 client->req_opts_size = ELEMENTSOF(default_req_opts);
1130
1131 client->req_opts = memdup(default_req_opts, client->req_opts_size);
b25ef18b
TG
1132 if (!client->req_opts)
1133 return -ENOMEM;
1134
1135 *ret = client;
1136 client = NULL;
011feef8 1137
b25ef18b 1138 return 0;
011feef8 1139}