1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <linux/if_arp.h>
9 #include <linux/if_infiniband.h>
10
11 #include "sd-dhcp6-client.h"
12
13 #include "alloc-util.h"
14 #include "dhcp-identifier.h"
15 #include "dhcp6-internal.h"
16 #include "dhcp6-lease-internal.h"
17 #include "dns-domain.h"
18 #include "event-util.h"
19 #include "fd-util.h"
20 #include "hexdecoct.h"
21 #include "hostname-util.h"
22 #include "in-addr-util.h"
23 #include "io-util.h"
24 #include "random-util.h"
25 #include "socket-util.h"
26 #include "sort-util.h"
27 #include "strv.h"
28 #include "web-util.h"
29
30 #define DHCP6_CLIENT_DONT_DESTROY(client) \
31 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
32
33 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state);
34
sd_dhcp6_client_set_callback(sd_dhcp6_client * client,sd_dhcp6_client_callback_t cb,void * userdata)35 int sd_dhcp6_client_set_callback(
36 sd_dhcp6_client *client,
37 sd_dhcp6_client_callback_t cb,
38 void *userdata) {
39
40 assert_return(client, -EINVAL);
41
42 client->callback = cb;
43 client->userdata = userdata;
44
45 return 0;
46 }
47
sd_dhcp6_client_set_ifindex(sd_dhcp6_client * client,int ifindex)48 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
49 assert_return(client, -EINVAL);
50 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
51 assert_return(ifindex > 0, -EINVAL);
52
53 client->ifindex = ifindex;
54 return 0;
55 }
56
sd_dhcp6_client_set_ifname(sd_dhcp6_client * client,const char * ifname)57 int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
58 assert_return(client, -EINVAL);
59 assert_return(ifname, -EINVAL);
60
61 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
62 return -EINVAL;
63
64 return free_and_strdup(&client->ifname, ifname);
65 }
66
sd_dhcp6_client_get_ifname(sd_dhcp6_client * client,const char ** ret)67 int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
68 int r;
69
70 assert_return(client, -EINVAL);
71
72 r = get_ifname(client->ifindex, &client->ifname);
73 if (r < 0)
74 return r;
75
76 if (ret)
77 *ret = client->ifname;
78
79 return 0;
80 }
81
sd_dhcp6_client_set_local_address(sd_dhcp6_client * client,const struct in6_addr * local_address)82 int sd_dhcp6_client_set_local_address(
83 sd_dhcp6_client *client,
84 const struct in6_addr *local_address) {
85
86 assert_return(client, -EINVAL);
87 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
88 assert_return(local_address, -EINVAL);
89 assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
90
91 client->local_address = *local_address;
92
93 return 0;
94 }
95
sd_dhcp6_client_set_mac(sd_dhcp6_client * client,const uint8_t * addr,size_t addr_len,uint16_t arp_type)96 int sd_dhcp6_client_set_mac(
97 sd_dhcp6_client *client,
98 const uint8_t *addr,
99 size_t addr_len,
100 uint16_t arp_type) {
101
102 assert_return(client, -EINVAL);
103 assert_return(addr, -EINVAL);
104 assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL);
105
106 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
107 * as the MAC address is used only when setting DUID or IAID. */
108
109 if (arp_type == ARPHRD_ETHER)
110 assert_return(addr_len == ETH_ALEN, -EINVAL);
111 else if (arp_type == ARPHRD_INFINIBAND)
112 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
113 else {
114 client->arp_type = ARPHRD_NONE;
115 client->hw_addr.length = 0;
116 return 0;
117 }
118
119 memcpy(client->hw_addr.bytes, addr, addr_len);
120 client->hw_addr.length = addr_len;
121 client->arp_type = arp_type;
122
123 return 0;
124 }
125
sd_dhcp6_client_set_prefix_delegation_hint(sd_dhcp6_client * client,uint8_t prefixlen,const struct in6_addr * pd_prefix)126 int sd_dhcp6_client_set_prefix_delegation_hint(
127 sd_dhcp6_client *client,
128 uint8_t prefixlen,
129 const struct in6_addr *pd_prefix) {
130
131 _cleanup_free_ DHCP6Address *prefix = NULL;
132
133 assert_return(client, -EINVAL);
134 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
135
136 if (!pd_prefix) {
137 /* clear previous assignments. */
138 dhcp6_ia_clear_addresses(&client->ia_pd);
139 return 0;
140 }
141
142 assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL);
143
144 prefix = new(DHCP6Address, 1);
145 if (!prefix)
146 return -ENOMEM;
147
148 *prefix = (DHCP6Address) {
149 .iapdprefix.address = *pd_prefix,
150 .iapdprefix.prefixlen = prefixlen,
151 };
152
153 LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix));
154 return 1;
155 }
156
sd_dhcp6_client_add_vendor_option(sd_dhcp6_client * client,sd_dhcp6_option * v)157 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
158 int r;
159
160 assert_return(client, -EINVAL);
161 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
162
163 if (!v) {
164 /* Clear the previous assignments. */
165 ordered_set_clear(client->vendor_options);
166 return 0;
167 }
168
169 r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v);
170 if (r < 0)
171 return r;
172
173 sd_dhcp6_option_ref(v);
174
175 return 1;
176 }
177
client_ensure_duid(sd_dhcp6_client * client)178 static int client_ensure_duid(sd_dhcp6_client *client) {
179 assert(client);
180
181 if (client->duid_len != 0)
182 return 0;
183
184 return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
185 }
186
187 /**
188 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
189 * without further modification. Otherwise, if duid_type is supported, DUID
190 * is set based on that type. Otherwise, an error is returned.
191 */
dhcp6_client_set_duid_internal(sd_dhcp6_client * client,DUIDType duid_type,const void * duid,size_t duid_len,usec_t llt_time)192 static int dhcp6_client_set_duid_internal(
193 sd_dhcp6_client *client,
194 DUIDType duid_type,
195 const void *duid,
196 size_t duid_len,
197 usec_t llt_time) {
198 int r;
199
200 assert_return(client, -EINVAL);
201 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
202 assert_return(duid_len == 0 || duid, -EINVAL);
203
204 if (duid) {
205 r = dhcp_validate_duid_len(duid_type, duid_len, true);
206 if (r < 0) {
207 r = dhcp_validate_duid_len(duid_type, duid_len, false);
208 if (r < 0)
209 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
210
211 log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
212 }
213
214 client->duid.type = htobe16(duid_type);
215 memcpy(&client->duid.raw.data, duid, duid_len);
216 client->duid_len = sizeof(client->duid.type) + duid_len;
217
218 } else {
219 r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length,
220 client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len);
221 if (r == -EOPNOTSUPP)
222 return log_dhcp6_client_errno(client, r,
223 "Failed to set %s. MAC address is not set or "
224 "interface type is not supported.",
225 duid_type_to_string(duid_type));
226 if (r < 0)
227 return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
228 duid_type_to_string(duid_type));
229 }
230
231 return 0;
232 }
233
sd_dhcp6_client_set_duid(sd_dhcp6_client * client,uint16_t duid_type,const void * duid,size_t duid_len)234 int sd_dhcp6_client_set_duid(
235 sd_dhcp6_client *client,
236 uint16_t duid_type,
237 const void *duid,
238 size_t duid_len) {
239 return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
240 }
241
sd_dhcp6_client_set_duid_llt(sd_dhcp6_client * client,usec_t llt_time)242 int sd_dhcp6_client_set_duid_llt(
243 sd_dhcp6_client *client,
244 usec_t llt_time) {
245 return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
246 }
247
sd_dhcp6_client_duid_as_string(sd_dhcp6_client * client,char ** duid)248 int sd_dhcp6_client_duid_as_string(
249 sd_dhcp6_client *client,
250 char **duid) {
251 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
252 const char *v;
253 int r;
254
255 assert_return(client, -EINVAL);
256 assert_return(client->duid_len > 0, -ENODATA);
257 assert_return(duid, -EINVAL);
258
259 v = duid_type_to_string(be16toh(client->duid.type));
260 if (v) {
261 s = strdup(v);
262 if (!s)
263 return -ENOMEM;
264 } else {
265 r = asprintf(&s, "%0x", client->duid.type);
266 if (r < 0)
267 return -ENOMEM;
268 }
269
270 t = hexmem(&client->duid.raw.data, client->duid_len);
271 if (!t)
272 return -ENOMEM;
273
274 p = strjoin(s, ":", t);
275 if (!p)
276 return -ENOMEM;
277
278 *duid = TAKE_PTR(p);
279
280 return 0;
281 }
282
sd_dhcp6_client_set_iaid(sd_dhcp6_client * client,uint32_t iaid)283 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
284 assert_return(client, -EINVAL);
285 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
286
287 client->ia_na.header.id = htobe32(iaid);
288 client->ia_pd.header.id = htobe32(iaid);
289 client->iaid_set = true;
290
291 return 0;
292 }
293
client_ensure_iaid(sd_dhcp6_client * client)294 static int client_ensure_iaid(sd_dhcp6_client *client) {
295 int r;
296 uint32_t iaid;
297
298 assert(client);
299
300 if (client->iaid_set)
301 return 0;
302
303 r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length,
304 /* legacy_unstable_byteorder = */ true,
305 /* use_mac = */ client->test_mode,
306 &iaid);
307 if (r < 0)
308 return r;
309
310 client->ia_na.header.id = iaid;
311 client->ia_pd.header.id = iaid;
312 client->iaid_set = true;
313
314 return 0;
315 }
316
sd_dhcp6_client_get_iaid(sd_dhcp6_client * client,uint32_t * iaid)317 int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
318 assert_return(client, -EINVAL);
319 assert_return(iaid, -EINVAL);
320
321 if (!client->iaid_set)
322 return -ENODATA;
323
324 *iaid = be32toh(client->ia_na.header.id);
325
326 return 0;
327 }
328
dhcp6_client_set_test_mode(sd_dhcp6_client * client,bool test_mode)329 void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
330 assert(client);
331
332 client->test_mode = test_mode;
333 }
334
sd_dhcp6_client_set_fqdn(sd_dhcp6_client * client,const char * fqdn)335 int sd_dhcp6_client_set_fqdn(
336 sd_dhcp6_client *client,
337 const char *fqdn) {
338
339 assert_return(client, -EINVAL);
340 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
341
342 /* Make sure FQDN qualifies as DNS and as Linux hostname */
343 if (fqdn &&
344 !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
345 return -EINVAL;
346
347 return free_and_strdup(&client->fqdn, fqdn);
348 }
349
sd_dhcp6_client_set_information_request(sd_dhcp6_client * client,int enabled)350 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
351 assert_return(client, -EINVAL);
352 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
353
354 client->information_request = enabled;
355
356 return 0;
357 }
358
sd_dhcp6_client_get_information_request(sd_dhcp6_client * client,int * enabled)359 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
360 assert_return(client, -EINVAL);
361 assert_return(enabled, -EINVAL);
362
363 *enabled = client->information_request;
364
365 return 0;
366 }
367
be16_compare_func(const be16_t * a,const be16_t * b)368 static int be16_compare_func(const be16_t *a, const be16_t *b) {
369 return CMP(be16toh(*a), be16toh(*b));
370 }
371
sd_dhcp6_client_set_request_option(sd_dhcp6_client * client,uint16_t option)372 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
373 be16_t opt;
374
375 assert_return(client, -EINVAL);
376 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
377
378 if (!dhcp6_option_can_request(option))
379 return -EINVAL;
380
381 opt = htobe16(option);
382 if (typesafe_bsearch(&opt, client->req_opts, client->n_req_opts, be16_compare_func))
383 return -EEXIST;
384
385 if (!GREEDY_REALLOC(client->req_opts, client->n_req_opts + 1))
386 return -ENOMEM;
387
388 client->req_opts[client->n_req_opts++] = opt;
389
390 /* Sort immediately to make the above binary search will work for the next time. */
391 typesafe_qsort(client->req_opts, client->n_req_opts, be16_compare_func);
392 return 0;
393 }
394
sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client * client,const char * mudurl)395 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
396 assert_return(client, -EINVAL);
397 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
398 assert_return(mudurl, -EINVAL);
399 assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
400 assert_return(http_url_is_valid(mudurl), -EINVAL);
401
402 return free_and_strdup(&client->mudurl, mudurl);
403 }
404
sd_dhcp6_client_set_request_user_class(sd_dhcp6_client * client,char * const * user_class)405 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
406 char **s;
407
408 assert_return(client, -EINVAL);
409 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
410 assert_return(!strv_isempty(user_class), -EINVAL);
411
412 STRV_FOREACH(p, user_class) {
413 size_t len = strlen(*p);
414
415 if (len > UINT16_MAX || len == 0)
416 return -EINVAL;
417 }
418
419 s = strv_copy(user_class);
420 if (!s)
421 return -ENOMEM;
422
423 return strv_free_and_replace(client->user_class, s);
424 }
425
sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client * client,char * const * vendor_class)426 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
427 char **s;
428
429 assert_return(client, -EINVAL);
430 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
431 assert_return(!strv_isempty(vendor_class), -EINVAL);
432
433 STRV_FOREACH(p, vendor_class) {
434 size_t len = strlen(*p);
435
436 if (len > UINT16_MAX || len == 0)
437 return -EINVAL;
438 }
439
440 s = strv_copy(vendor_class);
441 if (!s)
442 return -ENOMEM;
443
444 return strv_free_and_replace(client->vendor_class, s);
445 }
446
sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client * client,int * delegation)447 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
448 assert_return(client, -EINVAL);
449 assert_return(delegation, -EINVAL);
450
451 *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
452
453 return 0;
454 }
455
sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client * client,int delegation)456 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
457 assert_return(client, -EINVAL);
458 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
459
460 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
461
462 return 0;
463 }
464
sd_dhcp6_client_get_address_request(sd_dhcp6_client * client,int * request)465 int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
466 assert_return(client, -EINVAL);
467 assert_return(request, -EINVAL);
468
469 *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
470
471 return 0;
472 }
473
sd_dhcp6_client_set_address_request(sd_dhcp6_client * client,int request)474 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
475 assert_return(client, -EINVAL);
476 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
477
478 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
479
480 return 0;
481 }
482
dhcp6_client_set_transaction_id(sd_dhcp6_client * client,uint32_t transaction_id)483 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
484 assert(client);
485 assert(client->test_mode);
486
487 /* This is for tests or fuzzers. */
488
489 client->transaction_id = transaction_id & htobe32(0x00ffffff);
490
491 return 0;
492 }
493
sd_dhcp6_client_get_lease(sd_dhcp6_client * client,sd_dhcp6_lease ** ret)494 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
495 assert_return(client, -EINVAL);
496
497 if (!client->lease)
498 return -ENOMSG;
499
500 if (ret)
501 *ret = client->lease;
502
503 return 0;
504 }
505
sd_dhcp6_client_add_option(sd_dhcp6_client * client,sd_dhcp6_option * v)506 int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
507 int r;
508
509 assert_return(client, -EINVAL);
510 assert_return(v, -EINVAL);
511
512 r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
513 if (r < 0)
514 return r;
515
516 sd_dhcp6_option_ref(v);
517 return 0;
518 }
519
client_set_state(sd_dhcp6_client * client,DHCP6State state)520 static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
521 assert(client);
522
523 if (client->state == state)
524 return;
525
526 log_dhcp6_client(client, "State changed: %s -> %s",
527 dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
528
529 client->state = state;
530 }
531
client_notify(sd_dhcp6_client * client,int event)532 static void client_notify(sd_dhcp6_client *client, int event) {
533 assert(client);
534
535 if (client->callback)
536 client->callback(client, event, client->userdata);
537 }
538
client_stop(sd_dhcp6_client * client,int error)539 static void client_stop(sd_dhcp6_client *client, int error) {
540 DHCP6_CLIENT_DONT_DESTROY(client);
541
542 assert(client);
543
544 client_notify(client, error);
545
546 client->lease = sd_dhcp6_lease_unref(client->lease);
547
548 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
549 * even though the lease is freed below. */
550 client->information_request_time_usec = 0;
551 client->information_refresh_time_usec = 0;
552
553 (void) event_source_disable(client->receive_message);
554 (void) event_source_disable(client->timeout_resend);
555 (void) event_source_disable(client->timeout_expire);
556 (void) event_source_disable(client->timeout_t1);
557 (void) event_source_disable(client->timeout_t2);
558
559 client_set_state(client, DHCP6_STATE_STOPPED);
560 }
561
client_append_common_options_in_managed_mode(sd_dhcp6_client * client,uint8_t ** opt,size_t * optlen,const DHCP6IA * ia_na,const DHCP6IA * ia_pd)562 static int client_append_common_options_in_managed_mode(
563 sd_dhcp6_client *client,
564 uint8_t **opt,
565 size_t *optlen,
566 const DHCP6IA *ia_na,
567 const DHCP6IA *ia_pd) {
568
569 int r;
570
571 assert(client);
572 assert(IN_SET(client->state,
573 DHCP6_STATE_SOLICITATION,
574 DHCP6_STATE_REQUEST,
575 DHCP6_STATE_RENEW,
576 DHCP6_STATE_REBIND));
577 assert(opt);
578 assert(optlen);
579
580 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) {
581 r = dhcp6_option_append_ia(opt, optlen, ia_na);
582 if (r < 0)
583 return r;
584 }
585
586 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) {
587 r = dhcp6_option_append_ia(opt, optlen, ia_pd);
588 if (r < 0)
589 return r;
590 }
591
592 if (client->fqdn) {
593 r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn);
594 if (r < 0)
595 return r;
596 }
597
598 if (client->user_class) {
599 r = dhcp6_option_append_user_class(opt, optlen, client->user_class);
600 if (r < 0)
601 return r;
602 }
603
604 if (client->vendor_class) {
605 r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class);
606 if (r < 0)
607 return r;
608 }
609
610 if (!ordered_set_isempty(client->vendor_options)) {
611 r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options);
612 if (r < 0)
613 return r;
614 }
615
616 return 0;
617 }
618
client_message_type_from_state(sd_dhcp6_client * client)619 static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) {
620 assert(client);
621
622 switch (client->state) {
623 case DHCP6_STATE_INFORMATION_REQUEST:
624 return DHCP6_MESSAGE_INFORMATION_REQUEST;
625 case DHCP6_STATE_SOLICITATION:
626 return DHCP6_MESSAGE_SOLICIT;
627 case DHCP6_STATE_REQUEST:
628 return DHCP6_MESSAGE_REQUEST;
629 case DHCP6_STATE_RENEW:
630 return DHCP6_MESSAGE_RENEW;
631 case DHCP6_STATE_REBIND:
632 return DHCP6_MESSAGE_REBIND;
633 default:
634 assert_not_reached();
635 }
636 }
637
client_append_oro(sd_dhcp6_client * client,uint8_t ** opt,size_t * optlen)638 static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *optlen) {
639 _cleanup_free_ be16_t *buf = NULL;
640 be16_t *req_opts;
641 size_t n;
642
643 assert(client);
644 assert(opt);
645 assert(optlen);
646
647 switch (client->state) {
648 case DHCP6_STATE_INFORMATION_REQUEST:
649 n = client->n_req_opts;
650 buf = new(be16_t, n + 2);
651 if (!buf)
652 return -ENOMEM;
653
654 memcpy_safe(buf, client->req_opts, n * sizeof(be16_t));
655 buf[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */
656 buf[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */
657
658 typesafe_qsort(buf, n, be16_compare_func);
659 req_opts = buf;
660 break;
661
662 case DHCP6_STATE_SOLICITATION:
663 n = client->n_req_opts;
664 buf = new(be16_t, n + 1);
665 if (!buf)
666 return -ENOMEM;
667
668 memcpy_safe(buf, client->req_opts, n * sizeof(be16_t));
669 buf[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */
670
671 typesafe_qsort(buf, n, be16_compare_func);
672 req_opts = buf;
673 break;
674
675 default:
676 n = client->n_req_opts;
677 req_opts = client->req_opts;
678 }
679
680 return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
681 }
682
dhcp6_client_send_message(sd_dhcp6_client * client)683 int dhcp6_client_send_message(sd_dhcp6_client *client) {
684 _cleanup_free_ DHCP6Message *message = NULL;
685 struct in6_addr all_servers =
686 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
687 struct sd_dhcp6_option *j;
688 size_t len, optlen = 512;
689 uint8_t *opt;
690 usec_t elapsed_usec, time_now;
691 be16_t elapsed_time;
692 int r;
693
694 assert(client);
695 assert(client->event);
696
697 r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
698 if (r < 0)
699 return r;
700
701 len = sizeof(DHCP6Message) + optlen;
702
703 message = malloc0(len);
704 if (!message)
705 return -ENOMEM;
706
707 opt = (uint8_t *)(message + 1);
708
709 message->transaction_id = client->transaction_id;
710 message->type = client_message_type_from_state(client);
711
712 switch (client->state) {
713 case DHCP6_STATE_INFORMATION_REQUEST:
714 break;
715
716 case DHCP6_STATE_SOLICITATION:
717 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
718 if (r < 0)
719 return r;
720
721 r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
722 &client->ia_na, &client->ia_pd);
723 if (r < 0)
724 return r;
725 break;
726
727 case DHCP6_STATE_REQUEST:
728 case DHCP6_STATE_RENEW:
729
730 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
731 client->lease->serverid_len,
732 client->lease->serverid);
733 if (r < 0)
734 return r;
735
736 _fallthrough_;
737 case DHCP6_STATE_REBIND:
738
739 assert(client->lease);
740
741 r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
742 client->lease->ia_na, client->lease->ia_pd);
743 if (r < 0)
744 return r;
745 break;
746
747 case DHCP6_STATE_STOPPED:
748 case DHCP6_STATE_BOUND:
749 default:
750 assert_not_reached();
751 }
752
753 if (client->mudurl) {
754 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6,
755 strlen(client->mudurl), client->mudurl);
756 if (r < 0)
757 return r;
758 }
759
760 r = client_append_oro(client, &opt, &optlen);
761 if (r < 0)
762 return r;
763
764 assert(client->duid_len);
765 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
766 client->duid_len, &client->duid);
767 if (r < 0)
768 return r;
769
770 ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
771 r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
772 if (r < 0)
773 return r;
774 }
775
776 /* RFC 8415 Section 21.9.
777 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
778 * been trying to complete a DHCP message exchange. */
779 elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
780 elapsed_time = htobe16(elapsed_usec);
781 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
782 if (r < 0)
783 return r;
784
785 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
786 len - optlen);
787 if (r < 0)
788 return r;
789
790 log_dhcp6_client(client, "Sent %s",
791 dhcp6_message_type_to_string(message->type));
792
793 return 0;
794 }
795
client_timeout_compute_random(usec_t val)796 static usec_t client_timeout_compute_random(usec_t val) {
797 return usec_sub_unsigned(val, random_u64_range(val / 10));
798 }
799
client_timeout_resend(sd_event_source * s,uint64_t usec,void * userdata)800 static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
801 sd_dhcp6_client *client = ASSERT_PTR(userdata);
802 usec_t init_retransmit_time, max_retransmit_time;
803 int r;
804
805 assert(client->event);
806
807 switch (client->state) {
808 case DHCP6_STATE_INFORMATION_REQUEST:
809 init_retransmit_time = DHCP6_INF_TIMEOUT;
810 max_retransmit_time = DHCP6_INF_MAX_RT;
811 break;
812
813 case DHCP6_STATE_SOLICITATION:
814
815 if (client->retransmit_count > 0 && client->lease) {
816 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
817 return 0;
818 }
819
820 init_retransmit_time = DHCP6_SOL_TIMEOUT;
821 max_retransmit_time = DHCP6_SOL_MAX_RT;
822 break;
823
824 case DHCP6_STATE_REQUEST:
825
826 if (client->retransmit_count >= DHCP6_REQ_MAX_RC) {
827 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
828 return 0;
829 }
830
831 init_retransmit_time = DHCP6_REQ_TIMEOUT;
832 max_retransmit_time = DHCP6_REQ_MAX_RT;
833 break;
834
835 case DHCP6_STATE_RENEW:
836 init_retransmit_time = DHCP6_REN_TIMEOUT;
837 max_retransmit_time = DHCP6_REN_MAX_RT;
838
839 /* RFC 3315, section 18.1.3. says max retransmit duration will
840 be the remaining time until T2. Instead of setting MRD,
841 wait for T2 to trigger with the same end result */
842 break;
843
844 case DHCP6_STATE_REBIND:
845 init_retransmit_time = DHCP6_REB_TIMEOUT;
846 max_retransmit_time = DHCP6_REB_MAX_RT;
847
848 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
849 break;
850
851 case DHCP6_STATE_STOPPED:
852 case DHCP6_STATE_BOUND:
853 default:
854 assert_not_reached();
855 }
856
857 r = dhcp6_client_send_message(client);
858 if (r >= 0)
859 client->retransmit_count++;
860
861 if (client->retransmit_time == 0) {
862 client->retransmit_time = client_timeout_compute_random(init_retransmit_time);
863
864 if (client->state == DHCP6_STATE_SOLICITATION)
865 client->retransmit_time += init_retransmit_time / 10;
866
867 } else if (client->retransmit_time > max_retransmit_time / 2)
868 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
869 else
870 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
871
872 log_dhcp6_client(client, "Next retransmission in %s",
873 FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
874
875 r = event_reset_time_relative(client->event, &client->timeout_resend,
876 CLOCK_BOOTTIME,
877 client->retransmit_time, 10 * USEC_PER_MSEC,
878 client_timeout_resend, client,
879 client->event_priority, "dhcp6-resend-timer", true);
880 if (r < 0)
881 client_stop(client, r);
882
883 return 0;
884 }
885
client_start_transaction(sd_dhcp6_client * client,DHCP6State state)886 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) {
887 int r;
888
889 assert(client);
890 assert(client->event);
891
892 switch (state) {
893 case DHCP6_STATE_INFORMATION_REQUEST:
894 case DHCP6_STATE_SOLICITATION:
895 assert(client->state == DHCP6_STATE_STOPPED);
896 break;
897 case DHCP6_STATE_REQUEST:
898 assert(client->state == DHCP6_STATE_SOLICITATION);
899 break;
900 case DHCP6_STATE_RENEW:
901 assert(client->state == DHCP6_STATE_BOUND);
902 break;
903 case DHCP6_STATE_REBIND:
904 assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW));
905 break;
906 case DHCP6_STATE_STOPPED:
907 case DHCP6_STATE_BOUND:
908 default:
909 assert_not_reached();
910 }
911
912 client_set_state(client, state);
913
914 client->retransmit_time = 0;
915 client->retransmit_count = 0;
916 client->transaction_id = random_u32() & htobe32(0x00ffffff);
917
918 r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start);
919 if (r < 0)
920 goto error;
921
922 r = event_reset_time(client->event, &client->timeout_resend,
923 CLOCK_BOOTTIME,
924 0, 0,
925 client_timeout_resend, client,
926 client->event_priority, "dhcp6-resend-timeout", true);
927 if (r < 0)
928 goto error;
929
930 r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON);
931 if (r < 0)
932 goto error;
933
934 return 0;
935
936 error:
937 client_stop(client, r);
938 return r;
939 }
940
client_timeout_expire(sd_event_source * s,uint64_t usec,void * userdata)941 static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
942 sd_dhcp6_client *client = ASSERT_PTR(userdata);
943 DHCP6_CLIENT_DONT_DESTROY(client);
944 DHCP6State state;
945
946 (void) event_source_disable(client->timeout_expire);
947 (void) event_source_disable(client->timeout_t2);
948 (void) event_source_disable(client->timeout_t1);
949
950 state = client->state;
951
952 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
953
954 /* RFC 3315, section 18.1.4., says that "...the client may choose to
955 use a Solicit message to locate a new DHCP server..." */
956 if (state == DHCP6_STATE_REBIND)
957 (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION);
958
959 return 0;
960 }
961
client_timeout_t2(sd_event_source * s,uint64_t usec,void * userdata)962 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
963 sd_dhcp6_client *client = ASSERT_PTR(userdata);
964
965 (void) event_source_disable(client->timeout_t2);
966 (void) event_source_disable(client->timeout_t1);
967
968 log_dhcp6_client(client, "Timeout T2");
969
970 (void) client_start_transaction(client, DHCP6_STATE_REBIND);
971
972 return 0;
973 }
974
client_timeout_t1(sd_event_source * s,uint64_t usec,void * userdata)975 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
976 sd_dhcp6_client *client = ASSERT_PTR(userdata);
977
978 (void) event_source_disable(client->timeout_t1);
979
980 log_dhcp6_client(client, "Timeout T1");
981
982 (void) client_start_transaction(client, DHCP6_STATE_RENEW);
983
984 return 0;
985 }
986
client_enter_bound_state(sd_dhcp6_client * client)987 static int client_enter_bound_state(sd_dhcp6_client *client) {
988 usec_t lifetime_t1, lifetime_t2, lifetime_valid;
989 int r;
990
991 assert(client);
992 assert(client->lease);
993 assert(IN_SET(client->state,
994 DHCP6_STATE_SOLICITATION,
995 DHCP6_STATE_REQUEST,
996 DHCP6_STATE_RENEW,
997 DHCP6_STATE_REBIND));
998
999 (void) event_source_disable(client->receive_message);
1000 (void) event_source_disable(client->timeout_resend);
1001
1002 r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
1003 if (r < 0)
1004 goto error;
1005
1006 lifetime_t2 = client_timeout_compute_random(lifetime_t2);
1007 lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
1008
1009 if (lifetime_t1 == USEC_INFINITY) {
1010 log_dhcp6_client(client, "Infinite T1");
1011 event_source_disable(client->timeout_t1);
1012 } else {
1013 log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC));
1014 r = event_reset_time_relative(client->event, &client->timeout_t1,
1015 CLOCK_BOOTTIME,
1016 lifetime_t1, 10 * USEC_PER_SEC,
1017 client_timeout_t1, client,
1018 client->event_priority, "dhcp6-t1-timeout", true);
1019 if (r < 0)
1020 goto error;
1021 }
1022
1023 if (lifetime_t2 == USEC_INFINITY) {
1024 log_dhcp6_client(client, "Infinite T2");
1025 event_source_disable(client->timeout_t2);
1026 } else {
1027 log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC));
1028 r = event_reset_time_relative(client->event, &client->timeout_t2,
1029 CLOCK_BOOTTIME,
1030 lifetime_t2, 10 * USEC_PER_SEC,
1031 client_timeout_t2, client,
1032 client->event_priority, "dhcp6-t2-timeout", true);
1033 if (r < 0)
1034 goto error;
1035 }
1036
1037 if (lifetime_valid == USEC_INFINITY) {
1038 log_dhcp6_client(client, "Infinite valid lifetime");
1039 event_source_disable(client->timeout_expire);
1040 } else {
1041 log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC));
1042
1043 r = event_reset_time_relative(client->event, &client->timeout_expire,
1044 CLOCK_BOOTTIME,
1045 lifetime_valid, USEC_PER_SEC,
1046 client_timeout_expire, client,
1047 client->event_priority, "dhcp6-lease-expire", true);
1048 if (r < 0)
1049 goto error;
1050 }
1051
1052 client_set_state(client, DHCP6_STATE_BOUND);
1053 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1054 return 0;
1055
1056 error:
1057 client_stop(client, r);
1058 return r;
1059 }
1060
log_invalid_message_type(sd_dhcp6_client * client,const DHCP6Message * message)1061 static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) {
1062 const char *type_str;
1063
1064 assert(client);
1065 assert(message);
1066
1067 type_str = dhcp6_message_type_to_string(message->type);
1068 if (type_str)
1069 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1070 "Received unexpected %s message, ignoring.", type_str);
1071 else
1072 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1073 "Received unsupported message type %u, ignoring.", message->type);
1074 }
1075
client_process_information(sd_dhcp6_client * client,DHCP6Message * message,size_t len,const triple_timestamp * timestamp,const struct in6_addr * server_address)1076 static int client_process_information(
1077 sd_dhcp6_client *client,
1078 DHCP6Message *message,
1079 size_t len,
1080 const triple_timestamp *timestamp,
1081 const struct in6_addr *server_address) {
1082
1083 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1084 int r;
1085
1086 assert(client);
1087 assert(message);
1088
1089 if (message->type != DHCP6_MESSAGE_REPLY)
1090 return log_invalid_message_type(client, message);
1091
1092 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1093 if (r < 0)
1094 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1095
1096 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1097
1098 sd_dhcp6_lease_unref(client->lease);
1099 client->lease = TAKE_PTR(lease);
1100
1101 /* Do not call client_stop() here, as it frees the acquired lease. */
1102 (void) event_source_disable(client->receive_message);
1103 (void) event_source_disable(client->timeout_resend);
1104 client_set_state(client, DHCP6_STATE_STOPPED);
1105
1106 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1107 return 0;
1108 }
1109
client_process_reply(sd_dhcp6_client * client,DHCP6Message * message,size_t len,const triple_timestamp * timestamp,const struct in6_addr * server_address)1110 static int client_process_reply(
1111 sd_dhcp6_client *client,
1112 DHCP6Message *message,
1113 size_t len,
1114 const triple_timestamp *timestamp,
1115 const struct in6_addr *server_address) {
1116
1117 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1118 int r;
1119
1120 assert(client);
1121 assert(message);
1122
1123 if (message->type != DHCP6_MESSAGE_REPLY)
1124 return log_invalid_message_type(client, message);
1125
1126 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1127 if (r < 0)
1128 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1129
1130 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1131
1132 sd_dhcp6_lease_unref(client->lease);
1133 client->lease = TAKE_PTR(lease);
1134
1135 return client_enter_bound_state(client);
1136 }
1137
client_process_advertise_or_rapid_commit_reply(sd_dhcp6_client * client,DHCP6Message * message,size_t len,const triple_timestamp * timestamp,const struct in6_addr * server_address)1138 static int client_process_advertise_or_rapid_commit_reply(
1139 sd_dhcp6_client *client,
1140 DHCP6Message *message,
1141 size_t len,
1142 const triple_timestamp *timestamp,
1143 const struct in6_addr *server_address) {
1144
1145 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1146 uint8_t pref_advertise, pref_lease = 0;
1147 int r;
1148
1149 assert(client);
1150 assert(message);
1151
1152 if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY))
1153 return log_invalid_message_type(client, message);
1154
1155 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1156 if (r < 0)
1157 return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m",
1158 dhcp6_message_type_to_string(message->type));
1159
1160 if (message->type == DHCP6_MESSAGE_REPLY) {
1161 bool rapid_commit;
1162
1163 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1164 if (r < 0)
1165 return r;
1166
1167 if (!rapid_commit)
1168 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1169 "Received reply message without rapid commit flag, ignoring.");
1170
1171 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1172
1173 sd_dhcp6_lease_unref(client->lease);
1174 client->lease = TAKE_PTR(lease);
1175
1176 return client_enter_bound_state(client);
1177 }
1178
1179 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1180 if (r < 0)
1181 return r;
1182
1183 if (client->lease) {
1184 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1185 if (r < 0)
1186 return r;
1187 }
1188
1189 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1190
1191 if (!client->lease || pref_advertise > pref_lease) {
1192 /* If this is the first advertise message or has higher preference, then save the lease. */
1193 sd_dhcp6_lease_unref(client->lease);
1194 client->lease = TAKE_PTR(lease);
1195 }
1196
1197 if (pref_advertise == 255 || client->retransmit_count > 1)
1198 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
1199
1200 return 0;
1201 }
1202
client_receive_message(sd_event_source * s,int fd,uint32_t revents,void * userdata)1203 static int client_receive_message(
1204 sd_event_source *s,
1205 int fd, uint32_t
1206 revents,
1207 void *userdata) {
1208
1209 sd_dhcp6_client *client = ASSERT_PTR(userdata);
1210 DHCP6_CLIENT_DONT_DESTROY(client);
1211 /* This needs to be initialized with zero. See #20741. */
1212 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1213 struct iovec iov;
1214 union sockaddr_union sa = {};
1215 struct msghdr msg = {
1216 .msg_name = &sa.sa,
1217 .msg_namelen = sizeof(sa),
1218 .msg_iov = &iov,
1219 .msg_iovlen = 1,
1220 .msg_control = &control,
1221 .msg_controllen = sizeof(control),
1222 };
1223 struct cmsghdr *cmsg;
1224 triple_timestamp t = {};
1225 _cleanup_free_ DHCP6Message *message = NULL;
1226 struct in6_addr *server_address = NULL;
1227 ssize_t buflen, len;
1228
1229 buflen = next_datagram_size_fd(fd);
1230 if (buflen < 0) {
1231 if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
1232 return 0;
1233
1234 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
1235 return 0;
1236 }
1237
1238 message = malloc(buflen);
1239 if (!message)
1240 return -ENOMEM;
1241
1242 iov = IOVEC_MAKE(message, buflen);
1243
1244 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
1245 if (len < 0) {
1246 if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
1247 return 0;
1248
1249 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
1250 return 0;
1251 }
1252 if ((size_t) len < sizeof(DHCP6Message)) {
1253 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1254 return 0;
1255 }
1256
1257 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1258 if (msg.msg_namelen > 0) {
1259 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1260 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1261 return 0;
1262 }
1263
1264 server_address = &sa.in6.sin6_addr;
1265 }
1266
1267 CMSG_FOREACH(cmsg, &msg) {
1268 if (cmsg->cmsg_level == SOL_SOCKET &&
1269 cmsg->cmsg_type == SO_TIMESTAMP &&
1270 cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
1271 triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
1272 }
1273
1274 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
1275 return 0;
1276
1277 switch (client->state) {
1278 case DHCP6_STATE_INFORMATION_REQUEST:
1279 if (client_process_information(client, message, len, &t, server_address) < 0)
1280 return 0;
1281 break;
1282
1283 case DHCP6_STATE_SOLICITATION:
1284 if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0)
1285 return 0;
1286 break;
1287
1288 case DHCP6_STATE_REQUEST:
1289 case DHCP6_STATE_RENEW:
1290 case DHCP6_STATE_REBIND:
1291 if (client_process_reply(client, message, len, &t, server_address) < 0)
1292 return 0;
1293 break;
1294
1295 case DHCP6_STATE_BOUND:
1296 case DHCP6_STATE_STOPPED:
1297 default:
1298 assert_not_reached();
1299 }
1300
1301 return 0;
1302 }
1303
sd_dhcp6_client_stop(sd_dhcp6_client * client)1304 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1305 if (!client)
1306 return 0;
1307
1308 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1309
1310 client->receive_message = sd_event_source_unref(client->receive_message);
1311 client->fd = safe_close(client->fd);
1312
1313 return 0;
1314 }
1315
sd_dhcp6_client_is_running(sd_dhcp6_client * client)1316 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1317 assert_return(client, -EINVAL);
1318
1319 return client->state != DHCP6_STATE_STOPPED;
1320 }
1321
sd_dhcp6_client_start(sd_dhcp6_client * client)1322 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1323 DHCP6State state = DHCP6_STATE_SOLICITATION;
1324 int r;
1325
1326 assert_return(client, -EINVAL);
1327 assert_return(client->event, -EINVAL);
1328 assert_return(client->ifindex > 0, -EINVAL);
1329 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
1330 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1331 assert_return(client->information_request || client->request_ia != 0, -EINVAL);
1332
1333 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1334 * request may be stored. */
1335 client->lease = sd_dhcp6_lease_unref(client->lease);
1336
1337 r = client_ensure_iaid(client);
1338 if (r < 0)
1339 return r;
1340
1341 r = client_ensure_duid(client);
1342 if (r < 0)
1343 return r;
1344
1345 if (client->fd < 0) {
1346 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1347 if (r < 0) {
1348 _cleanup_free_ char *p = NULL;
1349
1350 (void) in6_addr_to_string(&client->local_address, &p);
1351 return log_dhcp6_client_errno(client, r,
1352 "Failed to bind to UDP socket at address %s: %m", strna(p));
1353 }
1354
1355 client->fd = r;
1356 }
1357
1358 if (!client->receive_message) {
1359 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL;
1360
1361 r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client);
1362 if (r < 0)
1363 return r;
1364
1365 r = sd_event_source_set_priority(s, client->event_priority);
1366 if (r < 0)
1367 return r;
1368
1369 r = sd_event_source_set_description(s, "dhcp6-receive-message");
1370 if (r < 0)
1371 return r;
1372
1373 client->receive_message = TAKE_PTR(s);
1374 }
1375
1376 if (client->information_request) {
1377 usec_t t = now(CLOCK_MONOTONIC);
1378
1379 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1380 return 0;
1381
1382 client->information_request_time_usec = t;
1383 state = DHCP6_STATE_INFORMATION_REQUEST;
1384 }
1385
1386 log_dhcp6_client(client, "Starting in %s mode",
1387 client->information_request ? "Information request" : "Solicit");
1388
1389 return client_start_transaction(client, state);
1390 }
1391
sd_dhcp6_client_attach_event(sd_dhcp6_client * client,sd_event * event,int64_t priority)1392 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1393 int r;
1394
1395 assert_return(client, -EINVAL);
1396 assert_return(!client->event, -EBUSY);
1397 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1398
1399 if (event)
1400 client->event = sd_event_ref(event);
1401 else {
1402 r = sd_event_default(&client->event);
1403 if (r < 0)
1404 return 0;
1405 }
1406
1407 client->event_priority = priority;
1408
1409 return 0;
1410 }
1411
sd_dhcp6_client_detach_event(sd_dhcp6_client * client)1412 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1413 assert_return(client, -EINVAL);
1414 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1415
1416 client->event = sd_event_unref(client->event);
1417
1418 return 0;
1419 }
1420
sd_dhcp6_client_get_event(sd_dhcp6_client * client)1421 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1422 assert_return(client, NULL);
1423
1424 return client->event;
1425 }
1426
dhcp6_client_free(sd_dhcp6_client * client)1427 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1428 if (!client)
1429 return NULL;
1430
1431 sd_dhcp6_lease_unref(client->lease);
1432
1433 sd_event_source_disable_unref(client->receive_message);
1434 sd_event_source_disable_unref(client->timeout_resend);
1435 sd_event_source_disable_unref(client->timeout_expire);
1436 sd_event_source_disable_unref(client->timeout_t1);
1437 sd_event_source_disable_unref(client->timeout_t2);
1438 sd_event_unref(client->event);
1439
1440 client->fd = safe_close(client->fd);
1441
1442 free(client->req_opts);
1443 free(client->fqdn);
1444 free(client->mudurl);
1445 dhcp6_ia_clear_addresses(&client->ia_pd);
1446 ordered_hashmap_free(client->extra_options);
1447 ordered_set_free(client->vendor_options);
1448 strv_free(client->user_class);
1449 strv_free(client->vendor_class);
1450 free(client->ifname);
1451
1452 return mfree(client);
1453 }
1454
1455 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1456
sd_dhcp6_client_new(sd_dhcp6_client ** ret)1457 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1458 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1459
1460 assert_return(ret, -EINVAL);
1461
1462 client = new(sd_dhcp6_client, 1);
1463 if (!client)
1464 return -ENOMEM;
1465
1466 *client = (sd_dhcp6_client) {
1467 .n_ref = 1,
1468 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1469 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1470 .ifindex = -1,
1471 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
1472 .fd = -1,
1473 };
1474
1475 *ret = TAKE_PTR(client);
1476
1477 return 0;
1478 }
1479