]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
1d6e42ec83b47d54c567b71401d541a54fb50821
[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_RS:
297 case DHCP6_STATE_BOUND:
298 return -EINVAL;
299 }
300
301 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
302 client->req_opts_len * sizeof(be16_t),
303 client->req_opts);
304 if (r < 0)
305 return r;
306
307 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
308 sizeof(client->duid), &client->duid);
309 if (r < 0)
310 return r;
311
312 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
313 len - optlen);
314 if (r < 0)
315 return r;
316
317 log_dhcp6_client(client, "Sent %s",
318 dhcp6_message_type_to_string(message->type));
319
320 return 0;
321 }
322
323 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
324 void *userdata) {
325 sd_dhcp6_client *client = userdata;
326
327 assert_return(s, -EINVAL);
328 assert_return(client, -EINVAL);
329 assert_return(client->lease, -EINVAL);
330
331 client->lease->ia.timeout_t2 =
332 sd_event_source_unref(client->lease->ia.timeout_t2);
333
334 log_dhcp6_client(client, "Timeout T2");
335
336 client_start(client, DHCP6_STATE_REBIND);
337
338 return 0;
339 }
340
341 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
342 void *userdata) {
343 sd_dhcp6_client *client = userdata;
344
345 assert_return(s, -EINVAL);
346 assert_return(client, -EINVAL);
347 assert_return(client->lease, -EINVAL);
348
349 client->lease->ia.timeout_t1 =
350 sd_event_source_unref(client->lease->ia.timeout_t1);
351
352 log_dhcp6_client(client, "Timeout T1");
353
354 client_start(client, DHCP6_STATE_RENEW);
355
356 return 0;
357 }
358
359 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
360 void *userdata) {
361 sd_dhcp6_client *client = userdata;
362 DHCP6_CLIENT_DONT_DESTROY(client);
363 enum DHCP6State state;
364
365 assert(s);
366 assert(client);
367 assert(client->event);
368
369 state = client->state;
370
371 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
372
373 /* RFC 3315, section 18.1.4., says that "...the client may choose to
374 use a Solicit message to locate a new DHCP server..." */
375 if (state == DHCP6_STATE_REBIND)
376 client_start(client, DHCP6_STATE_SOLICITATION);
377
378 return 0;
379 }
380
381 static usec_t client_timeout_compute_random(usec_t val) {
382 return val - val / 10 +
383 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
384 }
385
386 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
387 void *userdata) {
388 int r = 0;
389 sd_dhcp6_client *client = userdata;
390 usec_t time_now, init_retransmit_time, max_retransmit_time;
391 usec_t max_retransmit_duration;
392 uint8_t max_retransmit_count = 0;
393 char time_string[FORMAT_TIMESPAN_MAX];
394 uint32_t expire = 0;
395
396 assert(s);
397 assert(client);
398 assert(client->event);
399
400 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
401
402 switch (client->state) {
403 case DHCP6_STATE_SOLICITATION:
404
405 if (client->retransmit_count && client->lease) {
406 client_start(client, DHCP6_STATE_REQUEST);
407 return 0;
408 }
409
410 init_retransmit_time = DHCP6_SOL_TIMEOUT;
411 max_retransmit_time = DHCP6_SOL_MAX_RT;
412 max_retransmit_count = 0;
413 max_retransmit_duration = 0;
414
415 break;
416
417 case DHCP6_STATE_REQUEST:
418 init_retransmit_time = DHCP6_REQ_TIMEOUT;
419 max_retransmit_time = DHCP6_REQ_MAX_RT;
420 max_retransmit_count = DHCP6_REQ_MAX_RC;
421 max_retransmit_duration = 0;
422
423 break;
424
425 case DHCP6_STATE_RENEW:
426 init_retransmit_time = DHCP6_REN_TIMEOUT;
427 max_retransmit_time = DHCP6_REN_MAX_RT;
428 max_retransmit_count = 0;
429
430 /* RFC 3315, section 18.1.3. says max retransmit duration will
431 be the remaining time until T2. Instead of setting MRD,
432 wait for T2 to trigger with the same end result */
433 max_retransmit_duration = 0;
434
435 break;
436
437 case DHCP6_STATE_REBIND:
438 init_retransmit_time = DHCP6_REB_TIMEOUT;
439 max_retransmit_time = DHCP6_REB_MAX_RT;
440 max_retransmit_count = 0;
441
442 max_retransmit_duration = 0;
443
444 if (!client->timeout_resend_expire) {
445 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
446 &expire);
447 if (r < 0) {
448 client_stop(client, r);
449 return 0;
450 }
451 max_retransmit_duration = expire * USEC_PER_SEC;
452 }
453
454 break;
455
456 case DHCP6_STATE_STOPPED:
457 case DHCP6_STATE_RS:
458 case DHCP6_STATE_BOUND:
459 return 0;
460 }
461
462 if (max_retransmit_count &&
463 client->retransmit_count >= max_retransmit_count) {
464 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
465 return 0;
466 }
467
468 r = client_send_message(client);
469 if (r >= 0)
470 client->retransmit_count++;
471
472
473 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
474 if (r < 0)
475 goto error;
476
477 if (!client->retransmit_time) {
478 client->retransmit_time =
479 client_timeout_compute_random(init_retransmit_time);
480
481 if (client->state == DHCP6_STATE_SOLICITATION)
482 client->retransmit_time += init_retransmit_time / 10;
483
484 } else {
485 if (max_retransmit_time &&
486 client->retransmit_time > max_retransmit_time / 2)
487 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
488 else
489 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
490 }
491
492 log_dhcp6_client(client, "Next retransmission in %s",
493 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
494 client->retransmit_time, 0));
495
496 r = sd_event_add_time(client->event, &client->timeout_resend,
497 CLOCK_MONOTONIC,
498 time_now + client->retransmit_time,
499 10 * USEC_PER_MSEC, client_timeout_resend,
500 client);
501 if (r < 0)
502 goto error;
503
504 r = sd_event_source_set_priority(client->timeout_resend,
505 client->event_priority);
506 if (r < 0)
507 goto error;
508
509 if (max_retransmit_duration && !client->timeout_resend_expire) {
510
511 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
512 max_retransmit_duration / USEC_PER_SEC);
513
514 r = sd_event_add_time(client->event,
515 &client->timeout_resend_expire,
516 CLOCK_MONOTONIC,
517 time_now + max_retransmit_duration,
518 USEC_PER_SEC,
519 client_timeout_resend_expire, client);
520 if (r < 0)
521 goto error;
522
523 r = sd_event_source_set_priority(client->timeout_resend_expire,
524 client->event_priority);
525 if (r < 0)
526 goto error;
527 }
528
529 error:
530 if (r < 0)
531 client_stop(client, r);
532
533 return 0;
534 }
535
536 static int client_ensure_iaid(sd_dhcp6_client *client) {
537 /* name is a pointer to memory in the udev_device struct, so must
538 have the same scope */
539 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
540 const char *name = NULL;
541 uint64_t id;
542
543 assert(client);
544
545 if (client->ia_na.id)
546 return 0;
547
548 if (detect_container(NULL) <= 0) {
549 /* not in a container, udev will be around */
550 _cleanup_udev_unref_ struct udev *udev;
551 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
552
553 udev = udev_new();
554 if (!udev)
555 return -ENOMEM;
556
557 sprintf(ifindex_str, "n%d", client->index);
558 device = udev_device_new_from_device_id(udev, ifindex_str);
559 if (!device)
560 return -errno;
561
562 if (udev_device_get_is_initialized(device) <= 0)
563 /* not yet ready */
564 return -EBUSY;
565
566 name = net_get_name(device);
567 }
568
569 if (name)
570 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
571 else
572 /* fall back to mac address if no predictable name available */
573 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
574 HASH_KEY.bytes);
575
576 /* fold into 32 bits */
577 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
578
579 return 0;
580 }
581
582 static int client_parse_message(sd_dhcp6_client *client,
583 DHCP6Message *message, size_t len,
584 sd_dhcp6_lease *lease) {
585 int r;
586 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
587 uint16_t optcode, status;
588 size_t optlen, id_len;
589 bool clientid = false;
590 be32_t iaid_lease;
591
592 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
593 &optval)) >= 0) {
594 switch (optcode) {
595 case DHCP6_OPTION_CLIENTID:
596 if (clientid) {
597 log_dhcp6_client(client, "%s contains multiple clientids",
598 dhcp6_message_type_to_string(message->type));
599 return -EINVAL;
600 }
601
602 if (optlen != sizeof(client->duid) ||
603 memcmp(&client->duid, optval, optlen) != 0) {
604 log_dhcp6_client(client, "%s DUID does not match",
605 dhcp6_message_type_to_string(message->type));
606
607 return -EINVAL;
608 }
609 clientid = true;
610
611 break;
612
613 case DHCP6_OPTION_SERVERID:
614 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
615 if (r >= 0 && id) {
616 log_dhcp6_client(client, "%s contains multiple serverids",
617 dhcp6_message_type_to_string(message->type));
618 return -EINVAL;
619 }
620
621 r = dhcp6_lease_set_serverid(lease, optval, optlen);
622 if (r < 0)
623 return r;
624
625 break;
626
627 case DHCP6_OPTION_PREFERENCE:
628 if (optlen != 1)
629 return -EINVAL;
630
631 r = dhcp6_lease_set_preference(lease, *optval);
632 if (r < 0)
633 return r;
634
635 break;
636
637 case DHCP6_OPTION_STATUS_CODE:
638 if (optlen < 2)
639 return -EINVAL;
640
641 status = optval[0] << 8 | optval[1];
642 if (status) {
643 log_dhcp6_client(client, "%s Status %s",
644 dhcp6_message_type_to_string(message->type),
645 dhcp6_message_status_to_string(status));
646 return -EINVAL;
647 }
648
649 break;
650
651 case DHCP6_OPTION_IA_NA:
652 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
653 &lease->ia);
654 if (r < 0 && r != -ENOMSG)
655 return r;
656
657 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
658 if (r < 0)
659 return r;
660
661 if (client->ia_na.id != iaid_lease) {
662 log_dhcp6_client(client, "%s has wrong IAID",
663 dhcp6_message_type_to_string(message->type));
664 return -EINVAL;
665 }
666
667 break;
668
669 case DHCP6_OPTION_RAPID_COMMIT:
670 r = dhcp6_lease_set_rapid_commit(lease);
671 if (r < 0)
672 return r;
673
674 break;
675 }
676 }
677
678 if ((r < 0 && r != -ENOMSG) || !clientid) {
679 log_dhcp6_client(client, "%s has incomplete options",
680 dhcp6_message_type_to_string(message->type));
681 return -EINVAL;
682 }
683
684 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
685 if (r < 0)
686 log_dhcp6_client(client, "%s has no server id",
687 dhcp6_message_type_to_string(message->type));
688
689 return r;
690 }
691
692 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
693 size_t len)
694 {
695 int r;
696 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
697 bool rapid_commit;
698
699 if (reply->type != DHCP6_REPLY)
700 return 0;
701
702 r = dhcp6_lease_new(&lease);
703 if (r < 0)
704 return -ENOMEM;
705
706 r = client_parse_message(client, reply, len, lease);
707 if (r < 0)
708 return r;
709
710 if (client->state == DHCP6_STATE_SOLICITATION) {
711 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
712 if (r < 0)
713 return r;
714
715 if (!rapid_commit)
716 return 0;
717 }
718
719 dhcp6_lease_clear_timers(&client->lease->ia);
720
721 client->lease = sd_dhcp6_lease_unref(client->lease);
722 client->lease = lease;
723 lease = NULL;
724
725 return DHCP6_STATE_BOUND;
726 }
727
728 static int client_receive_advertise(sd_dhcp6_client *client,
729 DHCP6Message *advertise, size_t len) {
730 int r;
731 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
732 uint8_t pref_advertise = 0, pref_lease = 0;
733
734 if (advertise->type != DHCP6_ADVERTISE)
735 return 0;
736
737 r = dhcp6_lease_new(&lease);
738 if (r < 0)
739 return r;
740
741 r = client_parse_message(client, advertise, len, lease);
742 if (r < 0)
743 return r;
744
745 r = dhcp6_lease_get_preference(lease, &pref_advertise);
746 if (r < 0)
747 return r;
748
749 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
750 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
751 sd_dhcp6_lease_unref(client->lease);
752 client->lease = lease;
753 lease = NULL;
754 r = 0;
755 }
756
757 if (pref_advertise == 255 || client->retransmit_count > 1)
758 r = DHCP6_STATE_REQUEST;
759
760 return r;
761 }
762
763 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
764 void *userdata) {
765 sd_dhcp6_client *client = userdata;
766 DHCP6_CLIENT_DONT_DESTROY(client);
767 _cleanup_free_ DHCP6Message *message;
768 int r, buflen, len;
769
770 assert(s);
771 assert(client);
772 assert(client->event);
773
774 r = ioctl(fd, FIONREAD, &buflen);
775 if (r < 0 || buflen <= 0)
776 buflen = DHCP6_MIN_OPTIONS_SIZE;
777
778 message = malloc0(buflen);
779 if (!message)
780 return -ENOMEM;
781
782 len = read(fd, message, buflen);
783 if ((size_t)len < sizeof(DHCP6Message)) {
784 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
785 return 0;
786 }
787
788 switch(message->type) {
789 case DHCP6_SOLICIT:
790 case DHCP6_REQUEST:
791 case DHCP6_CONFIRM:
792 case DHCP6_RENEW:
793 case DHCP6_REBIND:
794 case DHCP6_RELEASE:
795 case DHCP6_DECLINE:
796 case DHCP6_INFORMATION_REQUEST:
797 case DHCP6_RELAY_FORW:
798 case DHCP6_RELAY_REPL:
799 return 0;
800
801 case DHCP6_ADVERTISE:
802 case DHCP6_REPLY:
803 case DHCP6_RECONFIGURE:
804 break;
805
806 default:
807 log_dhcp6_client(client, "unknown message type %d",
808 message->type);
809 return 0;
810 }
811
812 if (client->transaction_id != (message->transaction_id &
813 htobe32(0x00ffffff)))
814 return 0;
815
816 switch (client->state) {
817 case DHCP6_STATE_SOLICITATION:
818 r = client_receive_advertise(client, message, len);
819
820 if (r == DHCP6_STATE_REQUEST) {
821 client_start(client, r);
822
823 break;
824 }
825
826 /* fall through for Soliciation Rapid Commit option check */
827 case DHCP6_STATE_REQUEST:
828 case DHCP6_STATE_RENEW:
829 case DHCP6_STATE_REBIND:
830
831 r = client_receive_reply(client, message, len);
832 if (r < 0)
833 return 0;
834
835 if (r == DHCP6_STATE_BOUND) {
836
837 r = client_start(client, DHCP6_STATE_BOUND);
838 if (r < 0) {
839 client_stop(client, r);
840 return 0;
841 }
842
843 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
844 }
845
846 break;
847
848 case DHCP6_STATE_BOUND:
849
850 break;
851
852 case DHCP6_STATE_STOPPED:
853 case DHCP6_STATE_RS:
854 return 0;
855 }
856
857 if (r >= 0) {
858 log_dhcp6_client(client, "Recv %s",
859 dhcp6_message_type_to_string(message->type));
860 }
861
862 return 0;
863 }
864
865 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
866 {
867 int r;
868 usec_t timeout, time_now;
869 char time_string[FORMAT_TIMESPAN_MAX];
870
871 assert_return(client, -EINVAL);
872 assert_return(client->event, -EINVAL);
873 assert_return(client->index > 0, -EINVAL);
874 assert_return(client->state != state, -EINVAL);
875
876 client->timeout_resend_expire =
877 sd_event_source_unref(client->timeout_resend_expire);
878 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
879 client->retransmit_time = 0;
880 client->retransmit_count = 0;
881
882 switch (state) {
883 case DHCP6_STATE_STOPPED:
884 case DHCP6_STATE_RS:
885 case DHCP6_STATE_SOLICITATION:
886
887 r = client_ensure_iaid(client);
888 if (r < 0)
889 return r;
890
891 r = dhcp6_network_bind_udp_socket(client->index, NULL);
892 if (r < 0)
893 return r;
894
895 client->fd = r;
896
897 r = sd_event_add_io(client->event, &client->receive_message,
898 client->fd, EPOLLIN, client_receive_message,
899 client);
900 if (r < 0)
901 return r;
902
903 r = sd_event_source_set_priority(client->receive_message,
904 client->event_priority);
905 if (r < 0)
906 return r;
907
908 client->state = DHCP6_STATE_SOLICITATION;
909
910 break;
911
912 case DHCP6_STATE_REQUEST:
913 case DHCP6_STATE_RENEW:
914 case DHCP6_STATE_REBIND:
915
916 client->state = state;
917
918 break;
919
920 case DHCP6_STATE_BOUND:
921
922 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
923 if (r < 0)
924 return r;
925
926 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
927 client->lease->ia.lifetime_t2 == 0xffffffff) {
928
929 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
930 be32toh(client->lease->ia.lifetime_t1),
931 be32toh(client->lease->ia.lifetime_t2));
932
933 return 0;
934 }
935
936 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
937
938 log_dhcp6_client(client, "T1 expires in %s",
939 format_timespan(time_string,
940 FORMAT_TIMESPAN_MAX,
941 timeout, 0));
942
943 r = sd_event_add_time(client->event,
944 &client->lease->ia.timeout_t1,
945 CLOCK_MONOTONIC, time_now + timeout,
946 10 * USEC_PER_SEC, client_timeout_t1,
947 client);
948 if (r < 0)
949 return r;
950
951 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
952 client->event_priority);
953 if (r < 0)
954 return r;
955
956 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
957
958 log_dhcp6_client(client, "T2 expires in %s",
959 format_timespan(time_string,
960 FORMAT_TIMESPAN_MAX,
961 timeout, 0));
962
963 r = sd_event_add_time(client->event,
964 &client->lease->ia.timeout_t2,
965 CLOCK_MONOTONIC, time_now + timeout,
966 10 * USEC_PER_SEC, client_timeout_t2,
967 client);
968 if (r < 0)
969 return r;
970
971 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
972 client->event_priority);
973 if (r < 0)
974 return r;
975
976 client->state = state;
977
978 return 0;
979 }
980
981 client->transaction_id = random_u32() & htobe32(0x00ffffff);
982
983 r = sd_event_add_time(client->event, &client->timeout_resend,
984 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
985 client);
986 if (r < 0)
987 return r;
988
989 r = sd_event_source_set_priority(client->timeout_resend,
990 client->event_priority);
991 if (r < 0)
992 return r;
993
994 return 0;
995 }
996
997 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
998 {
999 client_stop(client, DHCP6_EVENT_STOP);
1000
1001 return 0;
1002 }
1003
1004 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1005 {
1006 int r = 0;
1007
1008 assert_return(client, -EINVAL);
1009 assert_return(client->event, -EINVAL);
1010 assert_return(client->index > 0, -EINVAL);
1011
1012 r = client_reset(client);
1013 if (r < 0)
1014 return r;
1015
1016 return client_start(client, DHCP6_STATE_SOLICITATION);
1017 }
1018
1019 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1020 int priority)
1021 {
1022 int r;
1023
1024 assert_return(client, -EINVAL);
1025 assert_return(!client->event, -EBUSY);
1026
1027 if (event)
1028 client->event = sd_event_ref(event);
1029 else {
1030 r = sd_event_default(&client->event);
1031 if (r < 0)
1032 return 0;
1033 }
1034
1035 client->event_priority = priority;
1036
1037 return 0;
1038 }
1039
1040 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1041 assert_return(client, -EINVAL);
1042
1043 client->event = sd_event_unref(client->event);
1044
1045 return 0;
1046 }
1047
1048 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1049 if (!client)
1050 return NULL;
1051
1052 return client->event;
1053 }
1054
1055 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1056 if (client)
1057 assert_se(REFCNT_INC(client->n_ref) >= 2);
1058
1059 return client;
1060 }
1061
1062 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1063 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1064 client_reset(client);
1065
1066 sd_dhcp6_client_detach_event(client);
1067
1068 free(client->req_opts);
1069 free(client);
1070
1071 return NULL;
1072 }
1073
1074 return client;
1075 }
1076
1077 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1078 {
1079 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1080 sd_id128_t machine_id;
1081 int r;
1082 size_t t;
1083
1084 assert_return(ret, -EINVAL);
1085
1086 client = new0(sd_dhcp6_client, 1);
1087 if (!client)
1088 return -ENOMEM;
1089
1090 client->n_ref = REFCNT_INIT;
1091
1092 client->ia_na.type = DHCP6_OPTION_IA_NA;
1093
1094 client->index = -1;
1095
1096 client->fd = -1;
1097
1098 /* initialize DUID */
1099 client->duid.type = htobe16(DHCP6_DUID_EN);
1100 client->duid.pen = htobe32(SYSTEMD_PEN);
1101
1102 r = sd_id128_get_machine(&machine_id);
1103 if (r < 0)
1104 return r;
1105
1106 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1107 directly */
1108 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1109 HASH_KEY.bytes);
1110
1111 client->req_opts_len = ELEMENTSOF(default_req_opts);
1112
1113 client->req_opts = new0(be16_t, client->req_opts_len);
1114 if (!client->req_opts)
1115 return -ENOMEM;
1116
1117 for (t = 0; t < client->req_opts_len; t++)
1118 client->req_opts[t] = htobe16(default_req_opts[t]);
1119
1120 *ret = client;
1121 client = NULL;
1122
1123 return 0;
1124 }