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