]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #16080 from YmrDtnJu/9p
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
139b011a 2/***
810adae9 3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
139b011a
PF
4***/
5
6#include <errno.h>
631bbe71 7#include <sys/ioctl.h>
8f815e8b 8#include <linux/if_arp.h>
76253e73 9#include <linux/if_infiniband.h>
139b011a 10
139b011a 11#include "sd-dhcp6-client.h"
07630cea 12
b5efdb8a 13#include "alloc-util.h"
07630cea 14#include "dhcp-identifier.h"
f12abb48 15#include "dhcp6-internal.h"
631bbe71 16#include "dhcp6-lease-internal.h"
07630cea 17#include "dhcp6-protocol.h"
8006aa32 18#include "dns-domain.h"
c9393e8c 19#include "event-util.h"
3ffd4af2 20#include "fd-util.h"
8006aa32 21#include "hostname-util.h"
c601ebf7 22#include "in-addr-util.h"
07630cea
LP
23#include "network-internal.h"
24#include "random-util.h"
4edc2c9b 25#include "socket-util.h"
8b43440b 26#include "string-table.h"
07630cea 27#include "util.h"
de8d6e55 28#include "web-util.h"
139b011a 29
76253e73
DW
30#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
31
6ffe71d0
YW
32#define IRT_DEFAULT (1 * USEC_PER_DAY)
33#define IRT_MINIMUM (600 * USEC_PER_SEC)
fcb51238 34
f311a62b
PF
35/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
36enum {
37 DHCP6_REQUEST_IA_NA = 1,
38 DHCP6_REQUEST_IA_TA = 2, /* currently not used */
39 DHCP6_REQUEST_IA_PD = 4,
40};
41
139b011a 42struct sd_dhcp6_client {
3733eec3 43 unsigned n_ref;
139b011a
PF
44
45 enum DHCP6State state;
46 sd_event *event;
47 int event_priority;
2f8e7633 48 int ifindex;
2805536b 49 DHCP6Address hint_pd_prefix;
c601ebf7 50 struct in6_addr local_address;
76253e73
DW
51 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
52 size_t mac_addr_len;
53 uint16_t arp_type;
f12abb48 54 DHCP6IA ia_na;
dce6563f 55 DHCP6IA ia_pd;
9a7225de
PF
56 sd_event_source *timeout_t1;
57 sd_event_source *timeout_t2;
1346489f 58 unsigned request;
a9aff361 59 be32_t transaction_id;
346e13a2 60 usec_t transaction_start;
631bbe71 61 struct sd_dhcp6_lease *lease;
a9aff361 62 int fd;
bbfa43ca 63 bool information_request;
8217ed5e 64 bool iaid_set;
da6fe470
PF
65 be16_t *req_opts;
66 size_t req_opts_allocated;
67 size_t req_opts_len;
8006aa32 68 char *fqdn;
de8d6e55 69 char *mudurl;
33923925 70 char **user_class;
73c8ced7 71 char **vendor_class;
a9aff361 72 sd_event_source *receive_message;
d1b0afe3
PF
73 usec_t retransmit_time;
74 uint8_t retransmit_count;
75 sd_event_source *timeout_resend;
76 sd_event_source *timeout_resend_expire;
45aa74c7 77 sd_dhcp6_client_callback_t callback;
139b011a 78 void *userdata;
764aad62 79 struct duid duid;
66eac120 80 size_t duid_len;
fcb51238
YW
81 usec_t information_request_time_usec;
82 usec_t information_refresh_time_usec;
e7d5fe17 83 OrderedHashmap *extra_options;
99ccb8ff 84 OrderedHashmap *vendor_options;
139b011a
PF
85};
86
da6fe470 87static const uint16_t default_req_opts[] = {
2c1ab8ca
BG
88 SD_DHCP6_OPTION_DNS_SERVERS,
89 SD_DHCP6_OPTION_DOMAIN_LIST,
90 SD_DHCP6_OPTION_NTP_SERVER,
91 SD_DHCP6_OPTION_SNTP_SERVERS,
da6fe470
PF
92};
93
a9aff361
PF
94const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
95 [DHCP6_SOLICIT] = "SOLICIT",
96 [DHCP6_ADVERTISE] = "ADVERTISE",
97 [DHCP6_REQUEST] = "REQUEST",
98 [DHCP6_CONFIRM] = "CONFIRM",
99 [DHCP6_RENEW] = "RENEW",
100 [DHCP6_REBIND] = "REBIND",
101 [DHCP6_REPLY] = "REPLY",
102 [DHCP6_RELEASE] = "RELEASE",
103 [DHCP6_DECLINE] = "DECLINE",
104 [DHCP6_RECONFIGURE] = "RECONFIGURE",
105 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
106 [DHCP6_RELAY_FORW] = "RELAY-FORW",
107 [DHCP6_RELAY_REPL] = "RELAY-REPL",
108};
109
110DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
111
631bbe71 112const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
76643fed
SS
113 [DHCP6_STATUS_SUCCESS] = "Success",
114 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
115 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
116 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
117 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
118 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
119 [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available",
120 [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type",
121 [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query",
122 [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured",
123 [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed",
124 [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated",
125 [DHCP6_STATUS_DATA_MISSING] = "Data missing",
126 [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete",
127 [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported",
128 [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused",
129 [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use",
130 [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict",
131 [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information",
132 [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
133 [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down",
134 [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported",
135 [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew",
631bbe71
PF
136};
137
138DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
139
3f0c075f 140#define DHCP6_CLIENT_DONT_DESTROY(client) \
4afd3348 141 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
3f0c075f 142
c3e2adea
PF
143static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
144
4b558378
ZJS
145int sd_dhcp6_client_set_callback(
146 sd_dhcp6_client *client,
147 sd_dhcp6_client_callback_t cb,
148 void *userdata) {
45aa74c7 149
139b011a
PF
150 assert_return(client, -EINVAL);
151
45aa74c7 152 client->callback = cb;
139b011a
PF
153 client->userdata = userdata;
154
155 return 0;
156}
157
2f8e7633 158int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
139b011a 159
2f8e7633
LP
160 assert_return(client, -EINVAL);
161 assert_return(ifindex >= -1, -EINVAL);
d7c9c21f
PF
162 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
163
2f8e7633 164 client->ifindex = ifindex;
139b011a
PF
165 return 0;
166}
167
4b558378
ZJS
168int sd_dhcp6_client_set_local_address(
169 sd_dhcp6_client *client,
170 const struct in6_addr *local_address) {
171
c601ebf7
TG
172 assert_return(client, -EINVAL);
173 assert_return(local_address, -EINVAL);
174 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
175
176 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
177
178 client->local_address = *local_address;
179
180 return 0;
181}
182
0ae0e5cd
LP
183int sd_dhcp6_client_set_mac(
184 sd_dhcp6_client *client,
185 const uint8_t *addr, size_t addr_len,
186 uint16_t arp_type) {
187
139b011a 188 assert_return(client, -EINVAL);
76253e73
DW
189 assert_return(addr, -EINVAL);
190 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
191 assert_return(arp_type > 0, -EINVAL);
192
d7c9c21f
PF
193 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
194
76253e73
DW
195 if (arp_type == ARPHRD_ETHER)
196 assert_return(addr_len == ETH_ALEN, -EINVAL);
197 else if (arp_type == ARPHRD_INFINIBAND)
198 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
139b011a 199 else
76253e73
DW
200 return -EINVAL;
201
202 if (client->mac_addr_len == addr_len &&
203 memcmp(&client->mac_addr, addr, addr_len) == 0)
204 return 0;
205
206 memcpy(&client->mac_addr, addr, addr_len);
207 client->mac_addr_len = addr_len;
208 client->arp_type = arp_type;
139b011a
PF
209
210 return 0;
211}
212
2805536b
SS
213int sd_dhcp6_client_set_prefix_delegation_hint(
214 sd_dhcp6_client *client,
215 uint8_t prefixlen,
216 const struct in6_addr *pd_address) {
217
218 assert_return(client, -EINVAL);
219 assert_return(pd_address, -EINVAL);
220
221 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
222
223 client->hint_pd_prefix.iapdprefix.address = *pd_address;
224 client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
225
226 return 0;
227}
228
99ccb8ff
SS
229int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
230 int r;
231
232 assert_return(client, -EINVAL);
233 assert_return(v, -EINVAL);
234
235 r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
236 if (r < 0)
237 return r;
238
239 r = ordered_hashmap_put(client->vendor_options, v, v);
240 if (r < 0)
241 return r;
242
243 sd_dhcp6_option_ref(v);
244
245 return 1;
246}
247
0ae0e5cd 248static int client_ensure_duid(sd_dhcp6_client *client) {
cc22955c
TH
249 if (client->duid_len != 0)
250 return 0;
0ae0e5cd 251
cc22955c
TH
252 return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
253}
254
d7df2fd3
ZJS
255/**
256 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
257 * without further modification. Otherwise, if duid_type is supported, DUID
258 * is set based on that type. Otherwise, an error is returned.
259 */
7e90a499 260static int dhcp6_client_set_duid_internal(
4b558378
ZJS
261 sd_dhcp6_client *client,
262 uint16_t duid_type,
f7a92d1a 263 const void *duid,
7e90a499
YW
264 size_t duid_len,
265 usec_t llt_time) {
413708d1 266 int r;
27eba50e 267
66eac120 268 assert_return(client, -EINVAL);
d7df2fd3 269 assert_return(duid_len == 0 || duid != NULL, -EINVAL);
afec4539 270 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
c83321e6 271
d7df2fd3 272 if (duid != NULL) {
ab4a88bc
TH
273 r = dhcp_validate_duid_len(duid_type, duid_len, true);
274 if (r < 0) {
275 r = dhcp_validate_duid_len(duid_type, duid_len, false);
276 if (r < 0)
a339859f 277 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
01bcea49
LP
278
279 log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
ab4a88bc 280 }
d7df2fd3 281
413708d1
VK
282 client->duid.type = htobe16(duid_type);
283 memcpy(&client->duid.raw.data, duid, duid_len);
d7df2fd3 284 client->duid_len = sizeof(client->duid.type) + duid_len;
d7df2fd3 285 } else
27eba50e 286 switch (duid_type) {
335f80a6 287 case DUID_TYPE_LLT:
339697f0 288 if (client->mac_addr_len == 0)
a339859f 289 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
335f80a6 290
53253d9c 291 r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
335f80a6 292 if (r < 0)
a339859f 293 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
335f80a6 294 break;
27eba50e
YW
295 case DUID_TYPE_EN:
296 r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
297 if (r < 0)
a339859f 298 return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
27eba50e 299 break;
335f80a6 300 case DUID_TYPE_LL:
339697f0 301 if (client->mac_addr_len == 0)
a339859f 302 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
335f80a6
YW
303
304 r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
305 if (r < 0)
a339859f 306 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
335f80a6 307 break;
27eba50e
YW
308 case DUID_TYPE_UUID:
309 r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
310 if (r < 0)
a339859f 311 return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
27eba50e
YW
312 break;
313 default:
a339859f 314 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
27eba50e 315 }
fe4b2156 316
413708d1
VK
317 return 0;
318}
319
7e90a499
YW
320int sd_dhcp6_client_set_duid(
321 sd_dhcp6_client *client,
322 uint16_t duid_type,
323 const void *duid,
324 size_t duid_len) {
325 return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
326}
327
328int sd_dhcp6_client_set_duid_llt(
329 sd_dhcp6_client *client,
330 usec_t llt_time) {
331 return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
332}
333
413708d1
VK
334int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
335 assert_return(client, -EINVAL);
336 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
337
e0026dcb 338 client->ia_na.ia_na.id = htobe32(iaid);
dce6563f 339 client->ia_pd.ia_pd.id = htobe32(iaid);
8217ed5e 340 client->iaid_set = true;
66eac120
DW
341
342 return 0;
343}
344
8006aa32
SA
345int sd_dhcp6_client_set_fqdn(
346 sd_dhcp6_client *client,
347 const char *fqdn) {
348
349 assert_return(client, -EINVAL);
350
351 /* Make sure FQDN qualifies as DNS and as Linux hostname */
352 if (fqdn &&
353 !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
354 return -EINVAL;
355
356 return free_and_strdup(&client->fqdn, fqdn);
357}
358
04c01369 359int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
bbfa43ca 360 assert_return(client, -EINVAL);
d7c9c21f
PF
361 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
362
bbfa43ca
PF
363 client->information_request = enabled;
364
365 return 0;
366}
367
04c01369 368int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
bbfa43ca
PF
369 assert_return(client, -EINVAL);
370 assert_return(enabled, -EINVAL);
371
372 *enabled = client->information_request;
373
374 return 0;
375}
376
0ae0e5cd 377int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
da6fe470
PF
378 size_t t;
379
380 assert_return(client, -EINVAL);
381 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
382
2d3adfa6 383 if (option <= 0 || option >= UINT8_MAX)
da6fe470 384 return -EINVAL;
da6fe470
PF
385
386 for (t = 0; t < client->req_opts_len; t++)
387 if (client->req_opts[t] == htobe16(option))
388 return -EEXIST;
389
390 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
391 client->req_opts_len + 1))
392 return -ENOMEM;
393
394 client->req_opts[client->req_opts_len++] = htobe16(option);
395
396 return 0;
397}
398
feb7d7a2 399int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
de8d6e55
SS
400
401 assert_return(client, -EINVAL);
402 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
403 assert_return(mudurl, -EINVAL);
2d3adfa6 404 assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
de8d6e55
SS
405 assert_return(http_url_is_valid(mudurl), -EINVAL);
406
407 return free_and_strdup(&client->mudurl, mudurl);
408}
409
33923925
SS
410int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
411 _cleanup_strv_free_ char **s = NULL;
412 char **p;
413
414 assert_return(client, -EINVAL);
415 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
73c8ced7 416
33923925
SS
417 assert_return(user_class, -EINVAL);
418
419 STRV_FOREACH(p, user_class)
420 if (strlen(*p) > UINT16_MAX)
421 return -ENAMETOOLONG;
422
2d3adfa6 423 s = strv_copy(user_class);
33923925
SS
424 if (!s)
425 return -ENOMEM;
426
427 client->user_class = TAKE_PTR(s);
428
429 return 0;
430}
431
73c8ced7
SS
432int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
433 _cleanup_strv_free_ char **s = NULL;
434 char **p;
435
436 assert_return(client, -EINVAL);
437 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
438 assert_return(vendor_class, -EINVAL);
439
440 STRV_FOREACH(p, vendor_class)
441 if (strlen(*p) > UINT8_MAX)
442 return -ENAMETOOLONG;
443
444 s = strv_copy(vendor_class);
445 if (!s)
446 return -ENOMEM;
447
448 client->vendor_class = TAKE_PTR(s);
449
450 return 0;
451}
452
d8c51121
PF
453int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
454 assert_return(client, -EINVAL);
455 assert_return(delegation, -EINVAL);
456
f311a62b 457 *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
d8c51121
PF
458
459 return 0;
460}
461
462int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
7c3de8f8
PF
463 assert_return(client, -EINVAL);
464
f311a62b
PF
465 SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
466
467 return 0;
468}
469
470int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
471 assert_return(client, -EINVAL);
472 assert_return(request, -EINVAL);
473
474 *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
475
476 return 0;
477}
478
479int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
480 assert_return(client, -EINVAL);
481
482 SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
7c3de8f8
PF
483
484 return 0;
485}
486
b62f9008 487int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
d89a400e
EV
488 assert_return(client, -EINVAL);
489
490 client->transaction_id = transaction_id;
491
492 return 0;
493}
494
ea3b3a75
PF
495int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
496 assert_return(client, -EINVAL);
ea3b3a75
PF
497
498 if (!client->lease)
499 return -ENOMSG;
500
3098562c
TG
501 if (ret)
502 *ret = client->lease;
ea3b3a75
PF
503
504 return 0;
505}
506
e7d5fe17
AD
507int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
508 int r;
509
510 assert_return(client, -EINVAL);
511 assert_return(v, -EINVAL);
512
513 r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
514 if (r < 0)
515 return r;
516
517 r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
518 if (r < 0)
519 return r;
520
521 sd_dhcp6_option_ref(v);
522 return 0;
523}
524
3f0c075f 525static void client_notify(sd_dhcp6_client *client, int event) {
45aa74c7
LP
526 assert(client);
527
528 if (client->callback)
529 client->callback(client, event, client->userdata);
139b011a
PF
530}
531
f8908727 532static int client_reset(sd_dhcp6_client *client) {
a1140666 533 assert(client);
f8908727 534
431a4bc8 535 client->lease = sd_dhcp6_lease_unref(client->lease);
4e3e6679 536
a9aff361
PF
537 client->receive_message =
538 sd_event_source_unref(client->receive_message);
539
c3e2adea 540 client->transaction_id = 0;
346e13a2 541 client->transaction_start = 0;
a9aff361 542
d1b0afe3
PF
543 client->retransmit_time = 0;
544 client->retransmit_count = 0;
d1b0afe3 545
c9393e8c
YW
546 (void) event_source_disable(client->timeout_resend);
547 (void) event_source_disable(client->timeout_resend_expire);
548 (void) event_source_disable(client->timeout_t1);
549 (void) event_source_disable(client->timeout_t2);
213e759a 550
139b011a
PF
551 client->state = DHCP6_STATE_STOPPED;
552
553 return 0;
554}
555
3f0c075f
PF
556static void client_stop(sd_dhcp6_client *client, int error) {
557 DHCP6_CLIENT_DONT_DESTROY(client);
139b011a 558
3f0c075f 559 assert(client);
139b011a 560
3f0c075f
PF
561 client_notify(client, error);
562
563 client_reset(client);
139b011a
PF
564}
565
346e13a2 566static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
a9aff361
PF
567 _cleanup_free_ DHCP6Message *message = NULL;
568 struct in6_addr all_servers =
569 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
e7d5fe17 570 struct sd_dhcp6_option *j;
a9aff361 571 size_t len, optlen = 512;
e7d5fe17 572 Iterator i;
a9aff361
PF
573 uint8_t *opt;
574 int r;
346e13a2
PF
575 usec_t elapsed_usec;
576 be16_t elapsed_time;
a9aff361 577
a1140666
LP
578 assert(client);
579
a9aff361
PF
580 len = sizeof(DHCP6Message) + optlen;
581
582 message = malloc0(len);
583 if (!message)
584 return -ENOMEM;
585
586 opt = (uint8_t *)(message + 1);
587
588 message->transaction_id = client->transaction_id;
589
590 switch(client->state) {
bbfa43ca
PF
591 case DHCP6_STATE_INFORMATION_REQUEST:
592 message->type = DHCP6_INFORMATION_REQUEST;
593
de8d6e55
SS
594 if (client->mudurl) {
595 r = dhcp6_option_append(&opt, &optlen,
596 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
597 client->mudurl);
598 if (r < 0)
599 return r;
600 }
601
bbfa43ca
PF
602 break;
603
a9aff361
PF
604 case DHCP6_STATE_SOLICITATION:
605 message->type = DHCP6_SOLICIT;
606
ed6ee219 607 r = dhcp6_option_append(&opt, &optlen,
2c1ab8ca 608 SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
926695f1
TA
609 if (r < 0)
610 return r;
ed6ee219 611
f311a62b
PF
612 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
613 r = dhcp6_option_append_ia(&opt, &optlen,
614 &client->ia_na);
615 if (r < 0)
616 return r;
617 }
a9aff361 618
8006aa32
SA
619 if (client->fqdn) {
620 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
621 if (r < 0)
622 return r;
623 }
624
de8d6e55
SS
625 if (client->mudurl) {
626 r = dhcp6_option_append(&opt, &optlen,
627 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
628 client->mudurl);
629 if (r < 0)
630 return r;
631 }
632
33923925
SS
633 if (client->user_class) {
634 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
635 if (r < 0)
636 return r;
637 }
638
73c8ced7
SS
639 if (client->vendor_class) {
640 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
641 if (r < 0)
642 return r;
643 }
644
99ccb8ff
SS
645 if (!ordered_hashmap_isempty(client->vendor_options)) {
646 r = dhcp6_option_append_vendor_option(&opt, &optlen,
647 client->vendor_options);
648 if (r < 0)
649 return r;
650 }
651
f311a62b 652 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
2805536b 653 r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
7c3de8f8
PF
654 if (r < 0)
655 return r;
656
657 opt += r;
658 optlen -= r;
659 }
660
7246333c
PF
661 break;
662
663 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
664 case DHCP6_STATE_RENEW:
665
666 if (client->state == DHCP6_STATE_REQUEST)
667 message->type = DHCP6_REQUEST;
668 else
669 message->type = DHCP6_RENEW;
7246333c 670
2c1ab8ca 671 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
7246333c
PF
672 client->lease->serverid_len,
673 client->lease->serverid);
674 if (r < 0)
675 return r;
676
f311a62b
PF
677 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
678 r = dhcp6_option_append_ia(&opt, &optlen,
679 &client->lease->ia);
680 if (r < 0)
681 return r;
682 }
a9aff361 683
8006aa32
SA
684 if (client->fqdn) {
685 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
686 if (r < 0)
687 return r;
688 }
689
de8d6e55
SS
690 if (client->mudurl) {
691 r = dhcp6_option_append(&opt, &optlen,
692 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
693 client->mudurl);
694 if (r < 0)
695 return r;
696 }
697
33923925
SS
698 if (client->user_class) {
699 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
700 if (r < 0)
701 return r;
702 }
703
73c8ced7
SS
704 if (client->vendor_class) {
705 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
706 if (r < 0)
707 return r;
708 }
709
99ccb8ff
SS
710 if (!ordered_hashmap_isempty(client->vendor_options)) {
711 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
712 if (r < 0)
713 return r;
714 }
715
f311a62b 716 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
2805536b 717 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
7c3de8f8
PF
718 if (r < 0)
719 return r;
720
721 opt += r;
722 optlen -= r;
723 }
724
a9aff361
PF
725 break;
726
3dc34fcc
PF
727 case DHCP6_STATE_REBIND:
728 message->type = DHCP6_REBIND;
729
f311a62b
PF
730 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
731 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
732 if (r < 0)
733 return r;
734 }
3dc34fcc 735
8006aa32
SA
736 if (client->fqdn) {
737 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
738 if (r < 0)
739 return r;
740 }
741
de8d6e55
SS
742 if (client->mudurl) {
743 r = dhcp6_option_append(&opt, &optlen,
744 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
745 client->mudurl);
746 if (r < 0)
747 return r;
748 }
749
33923925
SS
750 if (client->user_class) {
751 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
752 if (r < 0)
753 return r;
754 }
755
73c8ced7
SS
756 if (client->vendor_class) {
757 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
758 if (r < 0)
759 return r;
760 }
761
99ccb8ff
SS
762 if (!ordered_hashmap_isempty(client->vendor_options)) {
763 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
764 if (r < 0)
765 return r;
766 }
767
f311a62b 768 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
2805536b 769 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
7c3de8f8
PF
770 if (r < 0)
771 return r;
772
773 opt += r;
774 optlen -= r;
775 }
776
3dc34fcc
PF
777 break;
778
a9aff361 779 case DHCP6_STATE_STOPPED:
a34b57c0 780 case DHCP6_STATE_BOUND:
a9aff361
PF
781 return -EINVAL;
782 }
783
2c1ab8ca 784 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
da6fe470
PF
785 client->req_opts_len * sizeof(be16_t),
786 client->req_opts);
787 if (r < 0)
788 return r;
789
ccd1fc2f 790 assert(client->duid_len);
2c1ab8ca 791 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
66eac120 792 client->duid_len, &client->duid);
7246333c
PF
793 if (r < 0)
794 return r;
795
346e13a2
PF
796 elapsed_usec = time_now - client->transaction_start;
797 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
798 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
799 else
800 elapsed_time = 0xffff;
801
2c1ab8ca 802 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
346e13a2
PF
803 sizeof(elapsed_time), &elapsed_time);
804 if (r < 0)
805 return r;
806
e7d5fe17
AD
807 ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
808 r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
809 if (r < 0)
810 return r;
811 }
812
a9aff361
PF
813 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
814 len - optlen);
815 if (r < 0)
816 return r;
817
818 log_dhcp6_client(client, "Sent %s",
819 dhcp6_message_type_to_string(message->type));
820
821 return 0;
822}
823
4b558378 824static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
825 sd_dhcp6_client *client = userdata;
826
a1140666
LP
827 assert(s);
828 assert(client);
829 assert(client->lease);
a34b57c0 830
c9393e8c 831 (void) event_source_disable(client->timeout_t2);
a34b57c0
PF
832
833 log_dhcp6_client(client, "Timeout T2");
834
3dc34fcc
PF
835 client_start(client, DHCP6_STATE_REBIND);
836
a34b57c0
PF
837 return 0;
838}
839
4b558378 840static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
841 sd_dhcp6_client *client = userdata;
842
a1140666
LP
843 assert(s);
844 assert(client);
845 assert(client->lease);
a34b57c0 846
c9393e8c 847 (void) event_source_disable(client->timeout_t1);
a34b57c0
PF
848
849 log_dhcp6_client(client, "Timeout T1");
850
3dc34fcc
PF
851 client_start(client, DHCP6_STATE_RENEW);
852
a34b57c0
PF
853 return 0;
854}
855
4b558378 856static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3 857 sd_dhcp6_client *client = userdata;
3dc34fcc
PF
858 DHCP6_CLIENT_DONT_DESTROY(client);
859 enum DHCP6State state;
d1b0afe3
PF
860
861 assert(s);
862 assert(client);
863 assert(client->event);
864
3dc34fcc
PF
865 state = client->state;
866
10c9ce61 867 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
d1b0afe3 868
3dc34fcc
PF
869 /* RFC 3315, section 18.1.4., says that "...the client may choose to
870 use a Solicit message to locate a new DHCP server..." */
871 if (state == DHCP6_STATE_REBIND)
872 client_start(client, DHCP6_STATE_SOLICITATION);
873
d1b0afe3
PF
874 return 0;
875}
876
877static usec_t client_timeout_compute_random(usec_t val) {
9de8a425 878 return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
d1b0afe3
PF
879}
880
4b558378 881static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3
PF
882 int r = 0;
883 sd_dhcp6_client *client = userdata;
4b4923e6 884 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 885 usec_t max_retransmit_duration = 0;
513a6fa8 886 uint8_t max_retransmit_count = 0;
d1b0afe3
PF
887 char time_string[FORMAT_TIMESPAN_MAX];
888
889 assert(s);
890 assert(client);
891 assert(client->event);
892
c9393e8c 893 (void) event_source_disable(client->timeout_resend);
d1b0afe3
PF
894
895 switch (client->state) {
bbfa43ca
PF
896 case DHCP6_STATE_INFORMATION_REQUEST:
897 init_retransmit_time = DHCP6_INF_TIMEOUT;
898 max_retransmit_time = DHCP6_INF_MAX_RT;
899
900 break;
901
d1b0afe3 902 case DHCP6_STATE_SOLICITATION:
7246333c
PF
903
904 if (client->retransmit_count && client->lease) {
905 client_start(client, DHCP6_STATE_REQUEST);
906 return 0;
907 }
908
d1b0afe3
PF
909 init_retransmit_time = DHCP6_SOL_TIMEOUT;
910 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
911
912 break;
913
7246333c
PF
914 case DHCP6_STATE_REQUEST:
915 init_retransmit_time = DHCP6_REQ_TIMEOUT;
916 max_retransmit_time = DHCP6_REQ_MAX_RT;
917 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
918
919 break;
920
3dc34fcc
PF
921 case DHCP6_STATE_RENEW:
922 init_retransmit_time = DHCP6_REN_TIMEOUT;
923 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
924
925 /* RFC 3315, section 18.1.3. says max retransmit duration will
926 be the remaining time until T2. Instead of setting MRD,
927 wait for T2 to trigger with the same end result */
3dc34fcc
PF
928
929 break;
930
931 case DHCP6_STATE_REBIND:
932 init_retransmit_time = DHCP6_REB_TIMEOUT;
933 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc 934
c9393e8c 935 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
3ae01632
YW
936 uint32_t expire = 0;
937
938 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
3dc34fcc
PF
939 if (r < 0) {
940 client_stop(client, r);
941 return 0;
942 }
943 max_retransmit_duration = expire * USEC_PER_SEC;
944 }
945
946 break;
947
d1b0afe3 948 case DHCP6_STATE_STOPPED:
a34b57c0 949 case DHCP6_STATE_BOUND:
d1b0afe3
PF
950 return 0;
951 }
952
3ae01632 953 if (max_retransmit_count > 0 &&
d1b0afe3 954 client->retransmit_count >= max_retransmit_count) {
10c9ce61 955 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
d1b0afe3
PF
956 return 0;
957 }
958
fa94c34b 959 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
960 if (r < 0)
961 goto error;
962
346e13a2
PF
963 r = client_send_message(client, time_now);
964 if (r >= 0)
965 client->retransmit_count++;
966
3ae01632 967 if (client->retransmit_time == 0) {
d1b0afe3
PF
968 client->retransmit_time =
969 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
970
971 if (client->state == DHCP6_STATE_SOLICITATION)
972 client->retransmit_time += init_retransmit_time / 10;
973
d1b0afe3 974 } else {
3ae01632 975 if (max_retransmit_time > 0 &&
d1b0afe3
PF
976 client->retransmit_time > max_retransmit_time / 2)
977 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
978 else
979 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
980 }
981
982 log_dhcp6_client(client, "Next retransmission in %s",
ed19c567 983 format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
d1b0afe3 984
c9393e8c
YW
985 r = event_reset_time(client->event, &client->timeout_resend,
986 clock_boottime_or_monotonic(),
987 time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
988 client_timeout_resend, client,
989 client->event_priority, "dhcp6-resend-timer", true);
d1b0afe3
PF
990 if (r < 0)
991 goto error;
992
3ae01632 993 if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
d1b0afe3
PF
994
995 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
996 max_retransmit_duration / USEC_PER_SEC);
997
c9393e8c
YW
998 r = event_reset_time(client->event, &client->timeout_resend_expire,
999 clock_boottime_or_monotonic(),
1000 time_now + max_retransmit_duration, USEC_PER_SEC,
1001 client_timeout_resend_expire, client,
1002 client->event_priority, "dhcp6-resend-expire-timer", true);
9021bb9f
TG
1003 if (r < 0)
1004 goto error;
d1b0afe3
PF
1005 }
1006
1007error:
1008 if (r < 0)
1009 client_stop(client, r);
1010
1011 return 0;
1012}
1013
f12abb48 1014static int client_ensure_iaid(sd_dhcp6_client *client) {
cfb5b380 1015 int r;
6d13616b 1016 uint32_t iaid;
f12abb48
PF
1017
1018 assert(client);
1019
8217ed5e 1020 if (client->iaid_set)
f12abb48
PF
1021 return 0;
1022
6d13616b 1023 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
cfb5b380
TG
1024 if (r < 0)
1025 return r;
f12abb48 1026
69b43997
PF
1027 client->ia_na.ia_na.id = iaid;
1028 client->ia_pd.ia_pd.id = iaid;
8217ed5e 1029 client->iaid_set = true;
69b43997 1030
f12abb48
PF
1031 return 0;
1032}
1033
4b558378
ZJS
1034static int client_parse_message(
1035 sd_dhcp6_client *client,
1036 DHCP6Message *message,
1037 size_t len,
1038 sd_dhcp6_lease *lease) {
da07cf35 1039
5c95a913 1040 uint16_t ia_na_status = 0, ia_pd_status = 0;
da07cf35 1041 uint32_t lt_t1 = ~0, lt_t2 = ~0;
5c95a913 1042 usec_t irt = IRT_DEFAULT;
da07cf35 1043 bool clientid = false;
3bc424a3 1044 size_t pos = 0;
631bbe71 1045 int r;
631bbe71 1046
a1140666
LP
1047 assert(client);
1048 assert(message);
1049 assert(len >= sizeof(DHCP6Message));
1050 assert(lease);
1051
44481a8b
ZJS
1052 len -= sizeof(DHCP6Message);
1053
3bc424a3 1054 while (pos < len) {
da07cf35 1055 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
c6b4f32a 1056 uint16_t optcode, optlen;
3bc424a3 1057 be32_t iaid_lease;
5c95a913 1058 int status;
da07cf35 1059 uint8_t *optval;
3bc424a3 1060
da07cf35 1061 if (len < pos + offsetof(DHCP6Option, data))
3bc424a3
PF
1062 return -ENOBUFS;
1063
1064 optcode = be16toh(option->code);
1065 optlen = be16toh(option->len);
1066 optval = option->data;
1067
da07cf35
YW
1068 if (len < pos + offsetof(DHCP6Option, data) + optlen)
1069 return -ENOBUFS;
1070
631bbe71 1071 switch (optcode) {
2c1ab8ca 1072 case SD_DHCP6_OPTION_CLIENTID:
631bbe71
PF
1073 if (clientid) {
1074 log_dhcp6_client(client, "%s contains multiple clientids",
1075 dhcp6_message_type_to_string(message->type));
1076 return -EINVAL;
1077 }
1078
66eac120 1079 if (optlen != client->duid_len ||
631bbe71
PF
1080 memcmp(&client->duid, optval, optlen) != 0) {
1081 log_dhcp6_client(client, "%s DUID does not match",
1082 dhcp6_message_type_to_string(message->type));
1083
1084 return -EINVAL;
1085 }
1086 clientid = true;
1087
1088 break;
1089
2c1ab8ca 1090 case SD_DHCP6_OPTION_SERVERID:
99f1d3fc
ZJS
1091 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1092 if (r >= 0) {
631bbe71
PF
1093 log_dhcp6_client(client, "%s contains multiple serverids",
1094 dhcp6_message_type_to_string(message->type));
1095 return -EINVAL;
1096 }
1097
1098 r = dhcp6_lease_set_serverid(lease, optval, optlen);
1099 if (r < 0)
1100 return r;
1101
1102 break;
1103
2c1ab8ca 1104 case SD_DHCP6_OPTION_PREFERENCE:
631bbe71
PF
1105 if (optlen != 1)
1106 return -EINVAL;
1107
3bc424a3 1108 r = dhcp6_lease_set_preference(lease, optval[0]);
631bbe71
PF
1109 if (r < 0)
1110 return r;
1111
1112 break;
1113
2c1ab8ca 1114 case SD_DHCP6_OPTION_STATUS_CODE:
91c43f39
YW
1115 status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
1116 if (status < 0)
1117 return status;
1118
1119 if (status > 0) {
631bbe71
PF
1120 log_dhcp6_client(client, "%s Status %s",
1121 dhcp6_message_type_to_string(message->type),
1122 dhcp6_message_status_to_string(status));
c6b4f32a 1123
631bbe71
PF
1124 return -EINVAL;
1125 }
1126
1127 break;
1128
2c1ab8ca 1129 case SD_DHCP6_OPTION_IA_NA:
bbfa43ca
PF
1130 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1131 log_dhcp6_client(client, "Information request ignoring IA NA option");
1132
1133 break;
1134 }
1135
5c95a913 1136 r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
631bbe71
PF
1137 if (r < 0 && r != -ENOMSG)
1138 return r;
1139
5c95a913
SS
1140 if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
1141 pos += offsetof(DHCP6Option, data) + optlen;
1142 continue;
1143 }
1144
631bbe71
PF
1145 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
1146 if (r < 0)
1147 return r;
1148
e0026dcb 1149 if (client->ia_na.ia_na.id != iaid_lease) {
dce6563f
PF
1150 log_dhcp6_client(client, "%s has wrong IAID for IA NA",
1151 dhcp6_message_type_to_string(message->type));
1152 return -EINVAL;
1153 }
1154
b47fb949
PF
1155 if (lease->ia.addresses) {
1156 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
1157 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
1158 }
1159
dce6563f
PF
1160 break;
1161
1162 case SD_DHCP6_OPTION_IA_PD:
1163 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1164 log_dhcp6_client(client, "Information request ignoring IA PD option");
1165
1166 break;
1167 }
1168
5c95a913 1169 r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
dce6563f
PF
1170 if (r < 0 && r != -ENOMSG)
1171 return r;
1172
5c95a913
SS
1173 if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
1174 pos += offsetof(DHCP6Option, data) + optlen;
1175 continue;
1176 }
1177
49228c75 1178 r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
dce6563f
PF
1179 if (r < 0)
1180 return r;
1181
1182 if (client->ia_pd.ia_pd.id != iaid_lease) {
1183 log_dhcp6_client(client, "%s has wrong IAID for IA PD",
631bbe71
PF
1184 dhcp6_message_type_to_string(message->type));
1185 return -EINVAL;
1186 }
1187
b47fb949
PF
1188 if (lease->pd.addresses) {
1189 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
1190 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
1191 }
1192
631bbe71 1193 break;
ed6ee219 1194
2c1ab8ca 1195 case SD_DHCP6_OPTION_RAPID_COMMIT:
ed6ee219
PF
1196 r = dhcp6_lease_set_rapid_commit(lease);
1197 if (r < 0)
1198 return r;
1199
1200 break;
7bd8e95d 1201
2c1ab8ca 1202 case SD_DHCP6_OPTION_DNS_SERVERS:
7bd8e95d
PF
1203 r = dhcp6_lease_set_dns(lease, optval, optlen);
1204 if (r < 0)
1205 return r;
1206
1207 break;
5da1b97f 1208
2c1ab8ca 1209 case SD_DHCP6_OPTION_DOMAIN_LIST:
5da1b97f
PF
1210 r = dhcp6_lease_set_domains(lease, optval, optlen);
1211 if (r < 0)
1212 return r;
1213
1214 break;
1215
2c1ab8ca 1216 case SD_DHCP6_OPTION_NTP_SERVER:
6599680e
PF
1217 r = dhcp6_lease_set_ntp(lease, optval, optlen);
1218 if (r < 0)
1219 return r;
1220
1221 break;
41e4615d 1222
2c1ab8ca 1223 case SD_DHCP6_OPTION_SNTP_SERVERS:
41e4615d
PF
1224 r = dhcp6_lease_set_sntp(lease, optval, optlen);
1225 if (r < 0)
1226 return r;
1227
1228 break;
fcb51238
YW
1229
1230 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
6ffe71d0
YW
1231 if (optlen != 4)
1232 return -EINVAL;
1233
0eb5e6d3 1234 irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
fcb51238 1235 break;
631bbe71 1236 }
6599680e 1237
23976cb9 1238 pos += offsetof(DHCP6Option, data) + optlen;
631bbe71
PF
1239 }
1240
5c95a913
SS
1241 if (ia_na_status > 0 && ia_pd_status > 0) {
1242 log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
1243 return -EINVAL;
1244 }
1245
1eeddba4 1246 if (!clientid) {
631bbe71
PF
1247 log_dhcp6_client(client, "%s has incomplete options",
1248 dhcp6_message_type_to_string(message->type));
1249 return -EINVAL;
1250 }
1251
bbfa43ca 1252 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
99f1d3fc
ZJS
1253 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1254 if (r < 0) {
bbfa43ca
PF
1255 log_dhcp6_client(client, "%s has no server id",
1256 dhcp6_message_type_to_string(message->type));
99f1d3fc
ZJS
1257 return -EINVAL;
1258 }
b47fb949 1259
99f1d3fc
ZJS
1260 } else {
1261 if (lease->ia.addresses) {
1262 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1263 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1264 }
b47fb949 1265
99f1d3fc
ZJS
1266 if (lease->pd.addresses) {
1267 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1268 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1269 }
bbfa43ca 1270 }
631bbe71 1271
fcb51238
YW
1272 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1273
21a9905c 1274 return 0;
631bbe71
PF
1275}
1276
0ae0e5cd 1277static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
4afd3348 1278 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
ed6ee219 1279 bool rapid_commit;
a1140666
LP
1280 int r;
1281
1282 assert(client);
1283 assert(reply);
a34b57c0
PF
1284
1285 if (reply->type != DHCP6_REPLY)
ed6ee219 1286 return 0;
a34b57c0
PF
1287
1288 r = dhcp6_lease_new(&lease);
1289 if (r < 0)
1290 return -ENOMEM;
1291
1292 r = client_parse_message(client, reply, len, lease);
1293 if (r < 0)
1294 return r;
1295
ed6ee219
PF
1296 if (client->state == DHCP6_STATE_SOLICITATION) {
1297 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1298 if (r < 0)
1299 return r;
1300
1301 if (!rapid_commit)
1302 return 0;
1303 }
1304
431a4bc8
YW
1305 sd_dhcp6_lease_unref(client->lease);
1306 client->lease = TAKE_PTR(lease);
a34b57c0
PF
1307
1308 return DHCP6_STATE_BOUND;
1309}
1310
0ae0e5cd 1311static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
4afd3348 1312 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
631bbe71 1313 uint8_t pref_advertise = 0, pref_lease = 0;
a1140666 1314 int r;
631bbe71
PF
1315
1316 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 1317 return 0;
631bbe71
PF
1318
1319 r = dhcp6_lease_new(&lease);
1320 if (r < 0)
1321 return r;
1322
1323 r = client_parse_message(client, advertise, len, lease);
1324 if (r < 0)
1325 return r;
1326
1327 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1328 if (r < 0)
1329 return r;
1330
1331 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
bbfa43ca
PF
1332
1333 if (r < 0 || pref_advertise > pref_lease) {
431a4bc8
YW
1334 sd_dhcp6_lease_unref(client->lease);
1335 client->lease = TAKE_PTR(lease);
631bbe71
PF
1336 r = 0;
1337 }
1338
7246333c
PF
1339 if (pref_advertise == 255 || client->retransmit_count > 1)
1340 r = DHCP6_STATE_REQUEST;
1341
631bbe71
PF
1342 return r;
1343}
1344
004845d1
LP
1345static int client_receive_message(
1346 sd_event_source *s,
1347 int fd, uint32_t
1348 revents,
1349 void *userdata) {
1350
631bbe71 1351 sd_dhcp6_client *client = userdata;
3f0c075f 1352 DHCP6_CLIENT_DONT_DESTROY(client);
0d43d2fc 1353 _cleanup_free_ DHCP6Message *message = NULL;
4edc2c9b
LP
1354 ssize_t buflen, len;
1355 int r = 0;
631bbe71
PF
1356
1357 assert(s);
1358 assert(client);
1359 assert(client->event);
1360
4edc2c9b 1361 buflen = next_datagram_size_fd(fd);
22a3fd2d
BG
1362 if (buflen == -ENETDOWN) {
1363 /* the link is down. Don't return an error or the I/O event
1364 source will be disconnected and we won't be able to receive
1365 packets again when the link comes back. */
1366 return 0;
1367 }
4edc2c9b
LP
1368 if (buflen < 0)
1369 return buflen;
631bbe71 1370
0d43d2fc 1371 message = malloc(buflen);
631bbe71
PF
1372 if (!message)
1373 return -ENOMEM;
1374
cf447cb6 1375 len = recv(fd, message, buflen, 0);
0d43d2fc 1376 if (len < 0) {
22a3fd2d
BG
1377 /* see comment above for why we shouldn't error out on ENETDOWN. */
1378 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
0d43d2fc
TG
1379 return 0;
1380
bd9a7221 1381 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
0d43d2fc 1382
004845d1
LP
1383 }
1384 if ((size_t) len < sizeof(DHCP6Message)) {
1385 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 1386 return 0;
004845d1 1387 }
631bbe71
PF
1388
1389 switch(message->type) {
1390 case DHCP6_SOLICIT:
1391 case DHCP6_REQUEST:
1392 case DHCP6_CONFIRM:
1393 case DHCP6_RENEW:
1394 case DHCP6_REBIND:
1395 case DHCP6_RELEASE:
1396 case DHCP6_DECLINE:
1397 case DHCP6_INFORMATION_REQUEST:
1398 case DHCP6_RELAY_FORW:
1399 case DHCP6_RELAY_REPL:
1400 return 0;
1401
1402 case DHCP6_ADVERTISE:
1403 case DHCP6_REPLY:
1404 case DHCP6_RECONFIGURE:
1405 break;
1406
1407 default:
bd9a7221 1408 log_dhcp6_client(client, "Unknown message type %d", message->type);
631bbe71
PF
1409 return 0;
1410 }
1411
1412 if (client->transaction_id != (message->transaction_id &
1413 htobe32(0x00ffffff)))
1414 return 0;
1415
1416 switch (client->state) {
bbfa43ca
PF
1417 case DHCP6_STATE_INFORMATION_REQUEST:
1418 r = client_receive_reply(client, message, len);
1419 if (r < 0)
1420 return 0;
1421
10c9ce61 1422 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
1423
1424 client_start(client, DHCP6_STATE_STOPPED);
1425
1426 break;
1427
631bbe71
PF
1428 case DHCP6_STATE_SOLICITATION:
1429 r = client_receive_advertise(client, message, len);
1430
ed6ee219 1431 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
1432 client_start(client, r);
1433
ed6ee219
PF
1434 break;
1435 }
631bbe71 1436
4831981d 1437 _fallthrough_; /* for Soliciation Rapid Commit option check */
7246333c 1438 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1439 case DHCP6_STATE_RENEW:
1440 case DHCP6_STATE_REBIND:
1441
a34b57c0
PF
1442 r = client_receive_reply(client, message, len);
1443 if (r < 0)
1444 return 0;
1445
1446 if (r == DHCP6_STATE_BOUND) {
1447
1448 r = client_start(client, DHCP6_STATE_BOUND);
1449 if (r < 0) {
1450 client_stop(client, r);
1451 return 0;
1452 }
1453
10c9ce61 1454 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
1455 }
1456
1457 break;
1458
1459 case DHCP6_STATE_BOUND:
1460
1461 break;
1462
631bbe71 1463 case DHCP6_STATE_STOPPED:
631bbe71
PF
1464 return 0;
1465 }
1466
712fdfd6
YW
1467 log_dhcp6_client(client, "Recv %s",
1468 dhcp6_message_type_to_string(message->type));
631bbe71 1469
a9aff361
PF
1470 return 0;
1471}
1472
134ebaa4
PF
1473static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1474 uint32_t *lifetime_t2) {
1475 assert_return(client, -EINVAL);
1476 assert_return(client->lease, -EINVAL);
1477
1478 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1479 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1480 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1481
1482 return 0;
1483 }
1484
1485 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1486 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1487 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1488
1489 return 0;
1490 }
1491
1492 return -ENOMSG;
1493}
1494
0ae0e5cd 1495static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
f12abb48 1496 int r;
a34b57c0
PF
1497 usec_t timeout, time_now;
1498 char time_string[FORMAT_TIMESPAN_MAX];
134ebaa4 1499 uint32_t lifetime_t1, lifetime_t2;
f12abb48
PF
1500
1501 assert_return(client, -EINVAL);
1502 assert_return(client->event, -EINVAL);
2f8e7633 1503 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1504 assert_return(client->state != state, -EINVAL);
f12abb48 1505
c9393e8c
YW
1506 (void) event_source_disable(client->timeout_resend_expire);
1507 (void) event_source_disable(client->timeout_resend);
c3e2adea
PF
1508 client->retransmit_time = 0;
1509 client->retransmit_count = 0;
f12abb48 1510
38a03f06
LP
1511 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1512 if (r < 0)
1513 return r;
346e13a2 1514
7ac6c26a
PF
1515 if (!client->receive_message) {
1516 r = sd_event_add_io(client->event, &client->receive_message,
1517 client->fd, EPOLLIN, client_receive_message,
1518 client);
1519 if (r < 0)
1520 goto error;
1521
1522 r = sd_event_source_set_priority(client->receive_message,
1523 client->event_priority);
1524 if (r < 0)
1525 goto error;
1526
1527 r = sd_event_source_set_description(client->receive_message,
1528 "dhcp6-receive-message");
1529 if (r < 0)
1530 goto error;
1531 }
1532
c3e2adea
PF
1533 switch (state) {
1534 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1535 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1536 client->state = DHCP6_STATE_STOPPED;
a9aff361 1537
bbfa43ca
PF
1538 return 0;
1539 }
9021bb9f 1540
4831981d 1541 _fallthrough_;
bbfa43ca 1542 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1543 client->state = DHCP6_STATE_SOLICITATION;
1544
7246333c
PF
1545 break;
1546
bbfa43ca 1547 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1548 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1549 case DHCP6_STATE_RENEW:
1550 case DHCP6_STATE_REBIND:
a34b57c0 1551
7246333c
PF
1552 client->state = state;
1553
c3e2adea 1554 break;
a34b57c0
PF
1555
1556 case DHCP6_STATE_BOUND:
1557
134ebaa4
PF
1558 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1559 if (r < 0)
1560 goto error;
a34b57c0 1561
134ebaa4 1562 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
bd9a7221 1563 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
134ebaa4 1564 lifetime_t1, lifetime_t2);
a34b57c0
PF
1565
1566 return 0;
1567 }
1568
134ebaa4 1569 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
a34b57c0
PF
1570
1571 log_dhcp6_client(client, "T1 expires in %s",
ed19c567 1572 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1573
c9393e8c
YW
1574 r = event_reset_time(client->event, &client->timeout_t1,
1575 clock_boottime_or_monotonic(),
1576 time_now + timeout, 10 * USEC_PER_SEC,
1577 client_timeout_t1, client,
1578 client->event_priority, "dhcp6-t1-timeout", true);
9021bb9f 1579 if (r < 0)
7ac6c26a 1580 goto error;
9021bb9f 1581
134ebaa4 1582 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
a34b57c0
PF
1583
1584 log_dhcp6_client(client, "T2 expires in %s",
ed19c567 1585 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1586
c9393e8c
YW
1587 r = event_reset_time(client->event, &client->timeout_t2,
1588 clock_boottime_or_monotonic(),
1589 time_now + timeout, 10 * USEC_PER_SEC,
1590 client_timeout_t2, client,
1591 client->event_priority, "dhcp6-t2-timeout", true);
9021bb9f 1592 if (r < 0)
7ac6c26a 1593 goto error;
9021bb9f 1594
3dc34fcc
PF
1595 client->state = state;
1596
a34b57c0 1597 return 0;
c3e2adea 1598 }
a9aff361 1599
c3e2adea 1600 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1601 client->transaction_start = time_now;
d1b0afe3 1602
c9393e8c
YW
1603 r = event_reset_time(client->event, &client->timeout_resend,
1604 clock_boottime_or_monotonic(),
1605 0, 0,
1606 client_timeout_resend, client,
1607 client->event_priority, "dhcp6-resend-timeout", true);
9021bb9f 1608 if (r < 0)
7ac6c26a 1609 goto error;
9021bb9f 1610
f12abb48 1611 return 0;
7ac6c26a
PF
1612
1613 error:
1614 client_reset(client);
1615 return r;
f12abb48
PF
1616}
1617
0ae0e5cd 1618int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
f667c150
TG
1619 assert_return(client, -EINVAL);
1620
10c9ce61 1621 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a 1622
7ac6c26a
PF
1623 client->fd = safe_close(client->fd);
1624
139b011a
PF
1625 return 0;
1626}
1627
f667c150
TG
1628int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1629 assert_return(client, -EINVAL);
1630
1631 return client->state != DHCP6_STATE_STOPPED;
1632}
1633
0ae0e5cd 1634int sd_dhcp6_client_start(sd_dhcp6_client *client) {
bbfa43ca 1635 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
2f8e7633 1636 int r = 0;
139b011a
PF
1637
1638 assert_return(client, -EINVAL);
1639 assert_return(client->event, -EINVAL);
2f8e7633 1640 assert_return(client->ifindex > 0, -EINVAL);
c601ebf7 1641 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
139b011a 1642
d7c9c21f 1643 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
63348d13 1644 return -EBUSY;
d7c9c21f 1645
f311a62b
PF
1646 if (!client->information_request && !client->request)
1647 return -EINVAL;
1648
c806ffb9 1649 r = client_reset(client);
f12abb48
PF
1650 if (r < 0)
1651 return r;
1652
bbfa43ca
PF
1653 r = client_ensure_iaid(client);
1654 if (r < 0)
1655 return r;
1656
cc22955c
TH
1657 r = client_ensure_duid(client);
1658 if (r < 0)
1659 return r;
1660
10a0f27b
PF
1661 if (client->fd < 0) {
1662 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1663 if (r < 0) {
1664 _cleanup_free_ char *p = NULL;
1665
1666 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1667 return log_dhcp6_client_errno(client, r,
1668 "Failed to bind to UDP socket at address %s: %m", strna(p));
1669 }
bd9a7221 1670
10a0f27b 1671 client->fd = r;
bd9a7221 1672 }
bbfa43ca 1673
fcb51238
YW
1674 if (client->information_request) {
1675 usec_t t = now(CLOCK_MONOTONIC);
1676
1677 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1678 return 0;
1679
1680 client->information_request_time_usec = t;
bbfa43ca 1681 state = DHCP6_STATE_INFORMATION_REQUEST;
fcb51238 1682 }
bbfa43ca
PF
1683
1684 log_dhcp6_client(client, "Started in %s mode",
483d099e
ZJS
1685 client->information_request? "Information request":
1686 "Managed");
bbfa43ca
PF
1687
1688 return client_start(client, state);
139b011a
PF
1689}
1690
32d20645 1691int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1692 int r;
1693
1694 assert_return(client, -EINVAL);
1695 assert_return(!client->event, -EBUSY);
1696
1697 if (event)
1698 client->event = sd_event_ref(event);
1699 else {
1700 r = sd_event_default(&client->event);
1701 if (r < 0)
1702 return 0;
1703 }
1704
1705 client->event_priority = priority;
1706
1707 return 0;
1708}
1709
1710int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1711 assert_return(client, -EINVAL);
1712
1713 client->event = sd_event_unref(client->event);
1714
1715 return 0;
1716}
1717
1718sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1719 assert_return(client, NULL);
139b011a
PF
1720
1721 return client->event;
1722}
1723
8301aa0b
YW
1724static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1725 assert(client);
139b011a 1726
c9393e8c
YW
1727 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1728 client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1729 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1730 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1731
3733eec3
LP
1732 client_reset(client);
1733
10a0f27b
PF
1734 client->fd = safe_close(client->fd);
1735
3733eec3 1736 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1737
1738 free(client->req_opts);
8006aa32 1739 free(client->fqdn);
de8d6e55 1740 free(client->mudurl);
33923925 1741
e7d5fe17 1742 ordered_hashmap_free(client->extra_options);
33923925 1743 strv_free(client->user_class);
73c8ced7 1744 strv_free(client->vendor_class);
33923925 1745
6b430fdb 1746 return mfree(client);
139b011a
PF
1747}
1748
8301aa0b
YW
1749DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1750
0ae0e5cd 1751int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1752 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8b8ecac8 1753 _cleanup_free_ be16_t *req_opts = NULL;
da6fe470 1754 size_t t;
139b011a
PF
1755
1756 assert_return(ret, -EINVAL);
1757
8b8ecac8
YW
1758 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1759 if (!req_opts)
139b011a
PF
1760 return -ENOMEM;
1761
8b8ecac8
YW
1762 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1763 req_opts[t] = htobe16(default_req_opts[t]);
c806ffb9 1764
8b8ecac8
YW
1765 client = new(sd_dhcp6_client, 1);
1766 if (!client)
da6fe470
PF
1767 return -ENOMEM;
1768
8b8ecac8
YW
1769 *client = (sd_dhcp6_client) {
1770 .n_ref = 1,
1771 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1772 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1773 .ifindex = -1,
1774 .request = DHCP6_REQUEST_IA_NA,
1775 .fd = -1,
1776 .req_opts_len = ELEMENTSOF(default_req_opts),
2805536b
SS
1777 .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
1778 .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
8b8ecac8
YW
1779 .req_opts = TAKE_PTR(req_opts),
1780 };
da6fe470 1781
1cc6c93a 1782 *ret = TAKE_PTR(client);
139b011a
PF
1783
1784 return 0;
1785}