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