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