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