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