]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
dhcp-network: remove unused DHCP6_STATE_RS
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25
26 #include "udev.h"
27 #include "udev-util.h"
28 #include "virt.h"
29 #include "siphash24.h"
30 #include "util.h"
31 #include "refcnt.h"
32
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
38
39 #define SYSTEMD_PEN 43793
40 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
41
42 struct sd_dhcp6_client {
43 RefCount n_ref;
44
45 enum DHCP6State state;
46 sd_event *event;
47 int event_priority;
48 int index;
49 struct ether_addr mac_addr;
50 DHCP6IA ia_na;
51 be32_t transaction_id;
52 struct sd_dhcp6_lease *lease;
53 int fd;
54 be16_t *req_opts;
55 size_t req_opts_allocated;
56 size_t req_opts_len;
57 sd_event_source *receive_message;
58 usec_t retransmit_time;
59 uint8_t retransmit_count;
60 sd_event_source *timeout_resend;
61 sd_event_source *timeout_resend_expire;
62 sd_dhcp6_client_cb_t cb;
63 void *userdata;
64
65 struct duid_en {
66 uint16_t type; /* DHCP6_DUID_EN */
67 uint32_t pen;
68 uint8_t id[8];
69 } _packed_ duid;
70 };
71
72 static const uint16_t default_req_opts[] = {
73 DHCP6_OPTION_DNS_SERVERS,
74 DHCP6_OPTION_DOMAIN_LIST,
75 DHCP6_OPTION_NTP_SERVER,
76 };
77
78 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
79 [DHCP6_SOLICIT] = "SOLICIT",
80 [DHCP6_ADVERTISE] = "ADVERTISE",
81 [DHCP6_REQUEST] = "REQUEST",
82 [DHCP6_CONFIRM] = "CONFIRM",
83 [DHCP6_RENEW] = "RENEW",
84 [DHCP6_REBIND] = "REBIND",
85 [DHCP6_REPLY] = "REPLY",
86 [DHCP6_RELEASE] = "RELEASE",
87 [DHCP6_DECLINE] = "DECLINE",
88 [DHCP6_RECONFIGURE] = "RECONFIGURE",
89 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
90 [DHCP6_RELAY_FORW] = "RELAY-FORW",
91 [DHCP6_RELAY_REPL] = "RELAY-REPL",
92 };
93
94 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
95
96 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
97 [DHCP6_STATUS_SUCCESS] = "Success",
98 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
99 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
100 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
101 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
102 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
103 };
104
105 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
106
107 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
108 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112
113 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
114
115 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
116 sd_dhcp6_client_cb_t cb, void *userdata)
117 {
118 assert_return(client, -EINVAL);
119
120 client->cb = cb;
121 client->userdata = userdata;
122
123 return 0;
124 }
125
126 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
127 {
128 assert_return(client, -EINVAL);
129 assert_return(interface_index >= -1, -EINVAL);
130
131 client->index = interface_index;
132
133 return 0;
134 }
135
136 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
137 const struct ether_addr *mac_addr)
138 {
139 assert_return(client, -EINVAL);
140
141 if (mac_addr)
142 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
143 else
144 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
145
146 return 0;
147 }
148
149 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
150 uint16_t option) {
151 size_t t;
152
153 assert_return(client, -EINVAL);
154 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
155
156 switch(option) {
157 case DHCP6_OPTION_DNS_SERVERS:
158 case DHCP6_OPTION_DOMAIN_LIST:
159 case DHCP6_OPTION_SNTP_SERVERS:
160 case DHCP6_OPTION_NTP_SERVER:
161 break;
162
163 default:
164 return -EINVAL;
165 }
166
167 for (t = 0; t < client->req_opts_len; t++)
168 if (client->req_opts[t] == htobe16(option))
169 return -EEXIST;
170
171 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
172 client->req_opts_len + 1))
173 return -ENOMEM;
174
175 client->req_opts[client->req_opts_len++] = htobe16(option);
176
177 return 0;
178 }
179
180 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
181 assert_return(client, -EINVAL);
182 assert_return(ret, -EINVAL);
183
184 if (!client->lease)
185 return -ENOMSG;
186
187 *ret = sd_dhcp6_lease_ref(client->lease);
188
189 return 0;
190 }
191
192 static void client_notify(sd_dhcp6_client *client, int event) {
193 if (client->cb)
194 client->cb(client, event, client->userdata);
195 }
196
197 static int client_reset(sd_dhcp6_client *client) {
198 assert_return(client, -EINVAL);
199
200 client->receive_message =
201 sd_event_source_unref(client->receive_message);
202
203 client->fd = safe_close(client->fd);
204
205 client->transaction_id = 0;
206
207 client->ia_na.timeout_t1 =
208 sd_event_source_unref(client->ia_na.timeout_t1);
209 client->ia_na.timeout_t2 =
210 sd_event_source_unref(client->ia_na.timeout_t2);
211
212 client->retransmit_time = 0;
213 client->retransmit_count = 0;
214 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215 client->timeout_resend_expire =
216 sd_event_source_unref(client->timeout_resend_expire);
217
218 client->state = DHCP6_STATE_STOPPED;
219
220 return 0;
221 }
222
223 static void client_stop(sd_dhcp6_client *client, int error) {
224 DHCP6_CLIENT_DONT_DESTROY(client);
225
226 assert(client);
227
228 client_notify(client, error);
229
230 client_reset(client);
231 }
232
233 static int client_send_message(sd_dhcp6_client *client) {
234 _cleanup_free_ DHCP6Message *message = NULL;
235 struct in6_addr all_servers =
236 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
237 size_t len, optlen = 512;
238 uint8_t *opt;
239 int r;
240
241 len = sizeof(DHCP6Message) + optlen;
242
243 message = malloc0(len);
244 if (!message)
245 return -ENOMEM;
246
247 opt = (uint8_t *)(message + 1);
248
249 message->transaction_id = client->transaction_id;
250
251 switch(client->state) {
252 case DHCP6_STATE_SOLICITATION:
253 message->type = DHCP6_SOLICIT;
254
255 r = dhcp6_option_append(&opt, &optlen,
256 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
257 if (r < 0)
258 return r;
259
260 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
261 if (r < 0)
262 return r;
263
264 break;
265
266 case DHCP6_STATE_REQUEST:
267 case DHCP6_STATE_RENEW:
268
269 if (client->state == DHCP6_STATE_REQUEST)
270 message->type = DHCP6_REQUEST;
271 else
272 message->type = DHCP6_RENEW;
273
274 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
275 client->lease->serverid_len,
276 client->lease->serverid);
277 if (r < 0)
278 return r;
279
280 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
281 if (r < 0)
282 return r;
283
284 break;
285
286 case DHCP6_STATE_REBIND:
287 message->type = DHCP6_REBIND;
288
289 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290 if (r < 0)
291 return r;
292
293 break;
294
295 case DHCP6_STATE_STOPPED:
296 case DHCP6_STATE_BOUND:
297 return -EINVAL;
298 }
299
300 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
301 client->req_opts_len * sizeof(be16_t),
302 client->req_opts);
303 if (r < 0)
304 return r;
305
306 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
307 sizeof(client->duid), &client->duid);
308 if (r < 0)
309 return r;
310
311 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
312 len - optlen);
313 if (r < 0)
314 return r;
315
316 log_dhcp6_client(client, "Sent %s",
317 dhcp6_message_type_to_string(message->type));
318
319 return 0;
320 }
321
322 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
323 void *userdata) {
324 sd_dhcp6_client *client = userdata;
325
326 assert_return(s, -EINVAL);
327 assert_return(client, -EINVAL);
328 assert_return(client->lease, -EINVAL);
329
330 client->lease->ia.timeout_t2 =
331 sd_event_source_unref(client->lease->ia.timeout_t2);
332
333 log_dhcp6_client(client, "Timeout T2");
334
335 client_start(client, DHCP6_STATE_REBIND);
336
337 return 0;
338 }
339
340 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
341 void *userdata) {
342 sd_dhcp6_client *client = userdata;
343
344 assert_return(s, -EINVAL);
345 assert_return(client, -EINVAL);
346 assert_return(client->lease, -EINVAL);
347
348 client->lease->ia.timeout_t1 =
349 sd_event_source_unref(client->lease->ia.timeout_t1);
350
351 log_dhcp6_client(client, "Timeout T1");
352
353 client_start(client, DHCP6_STATE_RENEW);
354
355 return 0;
356 }
357
358 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
359 void *userdata) {
360 sd_dhcp6_client *client = userdata;
361 DHCP6_CLIENT_DONT_DESTROY(client);
362 enum DHCP6State state;
363
364 assert(s);
365 assert(client);
366 assert(client->event);
367
368 state = client->state;
369
370 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
371
372 /* RFC 3315, section 18.1.4., says that "...the client may choose to
373 use a Solicit message to locate a new DHCP server..." */
374 if (state == DHCP6_STATE_REBIND)
375 client_start(client, DHCP6_STATE_SOLICITATION);
376
377 return 0;
378 }
379
380 static usec_t client_timeout_compute_random(usec_t val) {
381 return val - val / 10 +
382 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
383 }
384
385 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
386 void *userdata) {
387 int r = 0;
388 sd_dhcp6_client *client = userdata;
389 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
390 usec_t max_retransmit_duration = 0;
391 uint8_t max_retransmit_count = 0;
392 char time_string[FORMAT_TIMESPAN_MAX];
393 uint32_t expire = 0;
394
395 assert(s);
396 assert(client);
397 assert(client->event);
398
399 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
400
401 switch (client->state) {
402 case DHCP6_STATE_SOLICITATION:
403
404 if (client->retransmit_count && client->lease) {
405 client_start(client, DHCP6_STATE_REQUEST);
406 return 0;
407 }
408
409 init_retransmit_time = DHCP6_SOL_TIMEOUT;
410 max_retransmit_time = DHCP6_SOL_MAX_RT;
411
412 break;
413
414 case DHCP6_STATE_REQUEST:
415 init_retransmit_time = DHCP6_REQ_TIMEOUT;
416 max_retransmit_time = DHCP6_REQ_MAX_RT;
417 max_retransmit_count = DHCP6_REQ_MAX_RC;
418
419 break;
420
421 case DHCP6_STATE_RENEW:
422 init_retransmit_time = DHCP6_REN_TIMEOUT;
423 max_retransmit_time = DHCP6_REN_MAX_RT;
424
425 /* RFC 3315, section 18.1.3. says max retransmit duration will
426 be the remaining time until T2. Instead of setting MRD,
427 wait for T2 to trigger with the same end result */
428
429 break;
430
431 case DHCP6_STATE_REBIND:
432 init_retransmit_time = DHCP6_REB_TIMEOUT;
433 max_retransmit_time = DHCP6_REB_MAX_RT;
434
435 if (!client->timeout_resend_expire) {
436 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
437 &expire);
438 if (r < 0) {
439 client_stop(client, r);
440 return 0;
441 }
442 max_retransmit_duration = expire * USEC_PER_SEC;
443 }
444
445 break;
446
447 case DHCP6_STATE_STOPPED:
448 case DHCP6_STATE_BOUND:
449 return 0;
450 }
451
452 if (max_retransmit_count &&
453 client->retransmit_count >= max_retransmit_count) {
454 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
455 return 0;
456 }
457
458 r = client_send_message(client);
459 if (r >= 0)
460 client->retransmit_count++;
461
462
463 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
464 if (r < 0)
465 goto error;
466
467 if (!client->retransmit_time) {
468 client->retransmit_time =
469 client_timeout_compute_random(init_retransmit_time);
470
471 if (client->state == DHCP6_STATE_SOLICITATION)
472 client->retransmit_time += init_retransmit_time / 10;
473
474 } else {
475 if (max_retransmit_time &&
476 client->retransmit_time > max_retransmit_time / 2)
477 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
478 else
479 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
480 }
481
482 log_dhcp6_client(client, "Next retransmission in %s",
483 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
484 client->retransmit_time, 0));
485
486 r = sd_event_add_time(client->event, &client->timeout_resend,
487 CLOCK_MONOTONIC,
488 time_now + client->retransmit_time,
489 10 * USEC_PER_MSEC, client_timeout_resend,
490 client);
491 if (r < 0)
492 goto error;
493
494 r = sd_event_source_set_priority(client->timeout_resend,
495 client->event_priority);
496 if (r < 0)
497 goto error;
498
499 if (max_retransmit_duration && !client->timeout_resend_expire) {
500
501 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
502 max_retransmit_duration / USEC_PER_SEC);
503
504 r = sd_event_add_time(client->event,
505 &client->timeout_resend_expire,
506 CLOCK_MONOTONIC,
507 time_now + max_retransmit_duration,
508 USEC_PER_SEC,
509 client_timeout_resend_expire, client);
510 if (r < 0)
511 goto error;
512
513 r = sd_event_source_set_priority(client->timeout_resend_expire,
514 client->event_priority);
515 if (r < 0)
516 goto error;
517 }
518
519 error:
520 if (r < 0)
521 client_stop(client, r);
522
523 return 0;
524 }
525
526 static int client_ensure_iaid(sd_dhcp6_client *client) {
527 /* name is a pointer to memory in the udev_device struct, so must
528 have the same scope */
529 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
530 const char *name = NULL;
531 uint64_t id;
532
533 assert(client);
534
535 if (client->ia_na.id)
536 return 0;
537
538 if (detect_container(NULL) <= 0) {
539 /* not in a container, udev will be around */
540 _cleanup_udev_unref_ struct udev *udev;
541 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
542
543 udev = udev_new();
544 if (!udev)
545 return -ENOMEM;
546
547 sprintf(ifindex_str, "n%d", client->index);
548 device = udev_device_new_from_device_id(udev, ifindex_str);
549 if (!device)
550 return -errno;
551
552 if (udev_device_get_is_initialized(device) <= 0)
553 /* not yet ready */
554 return -EBUSY;
555
556 name = net_get_name(device);
557 }
558
559 if (name)
560 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
561 else
562 /* fall back to mac address if no predictable name available */
563 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
564 HASH_KEY.bytes);
565
566 /* fold into 32 bits */
567 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
568
569 return 0;
570 }
571
572 static int client_parse_message(sd_dhcp6_client *client,
573 DHCP6Message *message, size_t len,
574 sd_dhcp6_lease *lease) {
575 int r;
576 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
577 uint16_t optcode, status;
578 size_t optlen, id_len;
579 bool clientid = false;
580 be32_t iaid_lease;
581
582 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
583 &optval)) >= 0) {
584 switch (optcode) {
585 case DHCP6_OPTION_CLIENTID:
586 if (clientid) {
587 log_dhcp6_client(client, "%s contains multiple clientids",
588 dhcp6_message_type_to_string(message->type));
589 return -EINVAL;
590 }
591
592 if (optlen != sizeof(client->duid) ||
593 memcmp(&client->duid, optval, optlen) != 0) {
594 log_dhcp6_client(client, "%s DUID does not match",
595 dhcp6_message_type_to_string(message->type));
596
597 return -EINVAL;
598 }
599 clientid = true;
600
601 break;
602
603 case DHCP6_OPTION_SERVERID:
604 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
605 if (r >= 0 && id) {
606 log_dhcp6_client(client, "%s contains multiple serverids",
607 dhcp6_message_type_to_string(message->type));
608 return -EINVAL;
609 }
610
611 r = dhcp6_lease_set_serverid(lease, optval, optlen);
612 if (r < 0)
613 return r;
614
615 break;
616
617 case DHCP6_OPTION_PREFERENCE:
618 if (optlen != 1)
619 return -EINVAL;
620
621 r = dhcp6_lease_set_preference(lease, *optval);
622 if (r < 0)
623 return r;
624
625 break;
626
627 case DHCP6_OPTION_STATUS_CODE:
628 if (optlen < 2)
629 return -EINVAL;
630
631 status = optval[0] << 8 | optval[1];
632 if (status) {
633 log_dhcp6_client(client, "%s Status %s",
634 dhcp6_message_type_to_string(message->type),
635 dhcp6_message_status_to_string(status));
636 return -EINVAL;
637 }
638
639 break;
640
641 case DHCP6_OPTION_IA_NA:
642 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
643 &lease->ia);
644 if (r < 0 && r != -ENOMSG)
645 return r;
646
647 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
648 if (r < 0)
649 return r;
650
651 if (client->ia_na.id != iaid_lease) {
652 log_dhcp6_client(client, "%s has wrong IAID",
653 dhcp6_message_type_to_string(message->type));
654 return -EINVAL;
655 }
656
657 break;
658
659 case DHCP6_OPTION_RAPID_COMMIT:
660 r = dhcp6_lease_set_rapid_commit(lease);
661 if (r < 0)
662 return r;
663
664 break;
665 }
666 }
667
668 if ((r < 0 && r != -ENOMSG) || !clientid) {
669 log_dhcp6_client(client, "%s has incomplete options",
670 dhcp6_message_type_to_string(message->type));
671 return -EINVAL;
672 }
673
674 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
675 if (r < 0)
676 log_dhcp6_client(client, "%s has no server id",
677 dhcp6_message_type_to_string(message->type));
678
679 return r;
680 }
681
682 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
683 size_t len)
684 {
685 int r;
686 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
687 bool rapid_commit;
688
689 if (reply->type != DHCP6_REPLY)
690 return 0;
691
692 r = dhcp6_lease_new(&lease);
693 if (r < 0)
694 return -ENOMEM;
695
696 r = client_parse_message(client, reply, len, lease);
697 if (r < 0)
698 return r;
699
700 if (client->state == DHCP6_STATE_SOLICITATION) {
701 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
702 if (r < 0)
703 return r;
704
705 if (!rapid_commit)
706 return 0;
707 }
708
709 if (client->lease)
710 dhcp6_lease_clear_timers(&client->lease->ia);
711
712 client->lease = sd_dhcp6_lease_unref(client->lease);
713 client->lease = lease;
714 lease = NULL;
715
716 return DHCP6_STATE_BOUND;
717 }
718
719 static int client_receive_advertise(sd_dhcp6_client *client,
720 DHCP6Message *advertise, size_t len) {
721 int r;
722 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
723 uint8_t pref_advertise = 0, pref_lease = 0;
724
725 if (advertise->type != DHCP6_ADVERTISE)
726 return 0;
727
728 r = dhcp6_lease_new(&lease);
729 if (r < 0)
730 return r;
731
732 r = client_parse_message(client, advertise, len, lease);
733 if (r < 0)
734 return r;
735
736 r = dhcp6_lease_get_preference(lease, &pref_advertise);
737 if (r < 0)
738 return r;
739
740 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
741 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
742 sd_dhcp6_lease_unref(client->lease);
743 client->lease = lease;
744 lease = NULL;
745 r = 0;
746 }
747
748 if (pref_advertise == 255 || client->retransmit_count > 1)
749 r = DHCP6_STATE_REQUEST;
750
751 return r;
752 }
753
754 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
755 void *userdata) {
756 sd_dhcp6_client *client = userdata;
757 DHCP6_CLIENT_DONT_DESTROY(client);
758 _cleanup_free_ DHCP6Message *message;
759 int r, buflen, len;
760
761 assert(s);
762 assert(client);
763 assert(client->event);
764
765 r = ioctl(fd, FIONREAD, &buflen);
766 if (r < 0 || buflen <= 0)
767 buflen = DHCP6_MIN_OPTIONS_SIZE;
768
769 message = malloc0(buflen);
770 if (!message)
771 return -ENOMEM;
772
773 len = read(fd, message, buflen);
774 if ((size_t)len < sizeof(DHCP6Message)) {
775 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
776 return 0;
777 }
778
779 switch(message->type) {
780 case DHCP6_SOLICIT:
781 case DHCP6_REQUEST:
782 case DHCP6_CONFIRM:
783 case DHCP6_RENEW:
784 case DHCP6_REBIND:
785 case DHCP6_RELEASE:
786 case DHCP6_DECLINE:
787 case DHCP6_INFORMATION_REQUEST:
788 case DHCP6_RELAY_FORW:
789 case DHCP6_RELAY_REPL:
790 return 0;
791
792 case DHCP6_ADVERTISE:
793 case DHCP6_REPLY:
794 case DHCP6_RECONFIGURE:
795 break;
796
797 default:
798 log_dhcp6_client(client, "unknown message type %d",
799 message->type);
800 return 0;
801 }
802
803 if (client->transaction_id != (message->transaction_id &
804 htobe32(0x00ffffff)))
805 return 0;
806
807 switch (client->state) {
808 case DHCP6_STATE_SOLICITATION:
809 r = client_receive_advertise(client, message, len);
810
811 if (r == DHCP6_STATE_REQUEST) {
812 client_start(client, r);
813
814 break;
815 }
816
817 /* fall through for Soliciation Rapid Commit option check */
818 case DHCP6_STATE_REQUEST:
819 case DHCP6_STATE_RENEW:
820 case DHCP6_STATE_REBIND:
821
822 r = client_receive_reply(client, message, len);
823 if (r < 0)
824 return 0;
825
826 if (r == DHCP6_STATE_BOUND) {
827
828 r = client_start(client, DHCP6_STATE_BOUND);
829 if (r < 0) {
830 client_stop(client, r);
831 return 0;
832 }
833
834 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
835 }
836
837 break;
838
839 case DHCP6_STATE_BOUND:
840
841 break;
842
843 case DHCP6_STATE_STOPPED:
844 return 0;
845 }
846
847 if (r >= 0) {
848 log_dhcp6_client(client, "Recv %s",
849 dhcp6_message_type_to_string(message->type));
850 }
851
852 return 0;
853 }
854
855 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
856 {
857 int r;
858 usec_t timeout, time_now;
859 char time_string[FORMAT_TIMESPAN_MAX];
860
861 assert_return(client, -EINVAL);
862 assert_return(client->event, -EINVAL);
863 assert_return(client->index > 0, -EINVAL);
864 assert_return(client->state != state, -EINVAL);
865
866 client->timeout_resend_expire =
867 sd_event_source_unref(client->timeout_resend_expire);
868 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
869 client->retransmit_time = 0;
870 client->retransmit_count = 0;
871
872 switch (state) {
873 case DHCP6_STATE_STOPPED:
874 case DHCP6_STATE_SOLICITATION:
875
876 r = client_ensure_iaid(client);
877 if (r < 0)
878 return r;
879
880 r = dhcp6_network_bind_udp_socket(client->index, NULL);
881 if (r < 0)
882 return r;
883
884 client->fd = r;
885
886 r = sd_event_add_io(client->event, &client->receive_message,
887 client->fd, EPOLLIN, client_receive_message,
888 client);
889 if (r < 0)
890 return r;
891
892 r = sd_event_source_set_priority(client->receive_message,
893 client->event_priority);
894 if (r < 0)
895 return r;
896
897 client->state = DHCP6_STATE_SOLICITATION;
898
899 break;
900
901 case DHCP6_STATE_REQUEST:
902 case DHCP6_STATE_RENEW:
903 case DHCP6_STATE_REBIND:
904
905 client->state = state;
906
907 break;
908
909 case DHCP6_STATE_BOUND:
910
911 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
912 if (r < 0)
913 return r;
914
915 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
916 client->lease->ia.lifetime_t2 == 0xffffffff) {
917
918 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
919 be32toh(client->lease->ia.lifetime_t1),
920 be32toh(client->lease->ia.lifetime_t2));
921
922 return 0;
923 }
924
925 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
926
927 log_dhcp6_client(client, "T1 expires in %s",
928 format_timespan(time_string,
929 FORMAT_TIMESPAN_MAX,
930 timeout, 0));
931
932 r = sd_event_add_time(client->event,
933 &client->lease->ia.timeout_t1,
934 CLOCK_MONOTONIC, time_now + timeout,
935 10 * USEC_PER_SEC, client_timeout_t1,
936 client);
937 if (r < 0)
938 return r;
939
940 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
941 client->event_priority);
942 if (r < 0)
943 return r;
944
945 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
946
947 log_dhcp6_client(client, "T2 expires in %s",
948 format_timespan(time_string,
949 FORMAT_TIMESPAN_MAX,
950 timeout, 0));
951
952 r = sd_event_add_time(client->event,
953 &client->lease->ia.timeout_t2,
954 CLOCK_MONOTONIC, time_now + timeout,
955 10 * USEC_PER_SEC, client_timeout_t2,
956 client);
957 if (r < 0)
958 return r;
959
960 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
961 client->event_priority);
962 if (r < 0)
963 return r;
964
965 client->state = state;
966
967 return 0;
968 }
969
970 client->transaction_id = random_u32() & htobe32(0x00ffffff);
971
972 r = sd_event_add_time(client->event, &client->timeout_resend,
973 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
974 client);
975 if (r < 0)
976 return r;
977
978 r = sd_event_source_set_priority(client->timeout_resend,
979 client->event_priority);
980 if (r < 0)
981 return r;
982
983 return 0;
984 }
985
986 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
987 {
988 client_stop(client, DHCP6_EVENT_STOP);
989
990 return 0;
991 }
992
993 int sd_dhcp6_client_start(sd_dhcp6_client *client)
994 {
995 int r = 0;
996
997 assert_return(client, -EINVAL);
998 assert_return(client->event, -EINVAL);
999 assert_return(client->index > 0, -EINVAL);
1000
1001 r = client_reset(client);
1002 if (r < 0)
1003 return r;
1004
1005 return client_start(client, DHCP6_STATE_SOLICITATION);
1006 }
1007
1008 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1009 int priority)
1010 {
1011 int r;
1012
1013 assert_return(client, -EINVAL);
1014 assert_return(!client->event, -EBUSY);
1015
1016 if (event)
1017 client->event = sd_event_ref(event);
1018 else {
1019 r = sd_event_default(&client->event);
1020 if (r < 0)
1021 return 0;
1022 }
1023
1024 client->event_priority = priority;
1025
1026 return 0;
1027 }
1028
1029 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1030 assert_return(client, -EINVAL);
1031
1032 client->event = sd_event_unref(client->event);
1033
1034 return 0;
1035 }
1036
1037 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1038 if (!client)
1039 return NULL;
1040
1041 return client->event;
1042 }
1043
1044 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1045 if (client)
1046 assert_se(REFCNT_INC(client->n_ref) >= 2);
1047
1048 return client;
1049 }
1050
1051 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1052 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1053 client_reset(client);
1054
1055 sd_dhcp6_client_detach_event(client);
1056
1057 free(client->req_opts);
1058 free(client);
1059
1060 return NULL;
1061 }
1062
1063 return client;
1064 }
1065
1066 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1067 {
1068 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1069 sd_id128_t machine_id;
1070 int r;
1071 size_t t;
1072
1073 assert_return(ret, -EINVAL);
1074
1075 client = new0(sd_dhcp6_client, 1);
1076 if (!client)
1077 return -ENOMEM;
1078
1079 client->n_ref = REFCNT_INIT;
1080
1081 client->ia_na.type = DHCP6_OPTION_IA_NA;
1082
1083 client->index = -1;
1084
1085 client->fd = -1;
1086
1087 /* initialize DUID */
1088 client->duid.type = htobe16(DHCP6_DUID_EN);
1089 client->duid.pen = htobe32(SYSTEMD_PEN);
1090
1091 r = sd_id128_get_machine(&machine_id);
1092 if (r < 0)
1093 return r;
1094
1095 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1096 directly */
1097 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1098 HASH_KEY.bytes);
1099
1100 client->req_opts_len = ELEMENTSOF(default_req_opts);
1101
1102 client->req_opts = new0(be16_t, client->req_opts_len);
1103 if (!client->req_opts)
1104 return -ENOMEM;
1105
1106 for (t = 0; t < client->req_opts_len; t++)
1107 client->req_opts[t] = htobe16(default_req_opts[t]);
1108
1109 *ret = client;
1110 client = NULL;
1111
1112 return 0;
1113 }