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