]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-lease.c
Merge pull request #24001 from yuwata/network-lifetime-fix
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-lease.c
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
8 #include "alloc-util.h"
9 #include "dhcp6-internal.h"
10 #include "dhcp6-lease-internal.h"
11 #include "strv.h"
12
13 #define IRT_DEFAULT (1 * USEC_PER_DAY)
14 #define IRT_MINIMUM (600 * USEC_PER_SEC)
15
16 static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
17 assert(lease);
18
19 if (timestamp && triple_timestamp_is_set(timestamp))
20 lease->timestamp = *timestamp;
21 else
22 triple_timestamp_get(&lease->timestamp);
23 }
24
25 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
26 assert_return(lease, -EINVAL);
27 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
28 assert_return(clock_supported(clock), -EOPNOTSUPP);
29 assert_return(ret, -EINVAL);
30
31 if (!triple_timestamp_is_set(&lease->timestamp))
32 return -ENODATA;
33
34 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
35 return 0;
36 }
37
38 static usec_t sec2usec(uint32_t sec) {
39 return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
40 }
41
42 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
43 uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
44
45 assert(lease);
46 assert(lease->ia_na || lease->ia_pd);
47
48 if (lease->ia_na) {
49 t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
50 t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
51
52 LIST_FOREACH(addresses, a, lease->ia_na->addresses)
53 min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
54 }
55
56 if (lease->ia_pd) {
57 t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
58 t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
59
60 LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
61 min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
62 }
63
64 if (t2 == 0 || t2 > min_valid_lt) {
65 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
66 * then adjust lifetime with it. */
67 t1 = min_valid_lt / 2;
68 t2 = min_valid_lt / 10 * 8;
69 }
70
71 lease->lifetime_valid = sec2usec(min_valid_lt);
72 lease->lifetime_t1 = sec2usec(t1);
73 lease->lifetime_t2 = sec2usec(t2);
74 }
75
76 int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
77 assert(lease);
78
79 if (!lease->ia_na && !lease->ia_pd)
80 return -ENODATA;
81
82 if (ret_t1)
83 *ret_t1 = lease->lifetime_t1;
84 if (ret_t2)
85 *ret_t2 = lease->lifetime_t2;
86 if (ret_valid)
87 *ret_valid = lease->lifetime_valid;
88 return 0;
89 }
90
91 static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
92 assert(lease);
93
94 if (server_address)
95 lease->server_address = *server_address;
96 else
97 lease->server_address = (struct in6_addr) {};
98 }
99
100 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
101 assert_return(lease, -EINVAL);
102 assert_return(ret, -EINVAL);
103
104 *ret = lease->server_address;
105 return 0;
106 }
107
108 void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
109 assert(ia);
110
111 LIST_FOREACH(addresses, a, ia->addresses)
112 free(a);
113
114 ia->addresses = NULL;
115 }
116
117 DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
118 if (!ia)
119 return NULL;
120
121 dhcp6_ia_clear_addresses(ia);
122
123 return mfree(ia);
124 }
125
126 int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
127 uint8_t *clientid = NULL;
128
129 assert(lease);
130 assert(id || len == 0);
131
132 if (len > 0) {
133 clientid = memdup(id, len);
134 if (!clientid)
135 return -ENOMEM;
136 }
137
138 free_and_replace(lease->clientid, clientid);
139 lease->clientid_len = len;
140
141 return 0;
142 }
143
144 int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
145 assert(lease);
146
147 if (!lease->clientid)
148 return -ENODATA;
149
150 if (ret_id)
151 *ret_id = lease->clientid;
152 if (ret_len)
153 *ret_len = lease->clientid_len;
154
155 return 0;
156 }
157
158 int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
159 uint8_t *serverid = NULL;
160
161 assert(lease);
162 assert(id || len == 0);
163
164 if (len > 0) {
165 serverid = memdup(id, len);
166 if (!serverid)
167 return -ENOMEM;
168 }
169
170 free_and_replace(lease->serverid, serverid);
171 lease->serverid_len = len;
172
173 return 0;
174 }
175
176 int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
177 assert(lease);
178
179 if (!lease->serverid)
180 return -ENODATA;
181
182 if (ret_id)
183 *ret_id = lease->serverid;
184 if (ret_len)
185 *ret_len = lease->serverid_len;
186 return 0;
187 }
188
189 int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
190 assert(lease);
191
192 lease->preference = preference;
193 return 0;
194 }
195
196 int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
197 assert(lease);
198 assert(ret);
199
200 *ret = lease->preference;
201 return 0;
202 }
203
204 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
205 assert(lease);
206
207 lease->rapid_commit = true;
208 return 0;
209 }
210
211 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
212 assert(lease);
213 assert(ret);
214
215 *ret = lease->rapid_commit;
216 return 0;
217 }
218
219 int sd_dhcp6_lease_get_address(
220 sd_dhcp6_lease *lease,
221 struct in6_addr *ret_addr,
222 uint32_t *ret_lifetime_preferred,
223 uint32_t *ret_lifetime_valid) {
224
225 assert_return(lease, -EINVAL);
226
227 if (!lease->addr_iter)
228 return -ENODATA;
229
230 if (ret_addr)
231 *ret_addr = lease->addr_iter->iaaddr.address;
232 if (ret_lifetime_preferred)
233 *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
234 if (ret_lifetime_valid)
235 *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
236
237 lease->addr_iter = lease->addr_iter->addresses_next;
238 return 0;
239 }
240
241 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
242 if (lease)
243 lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
244 }
245
246 int sd_dhcp6_lease_get_pd(
247 sd_dhcp6_lease *lease,
248 struct in6_addr *ret_prefix,
249 uint8_t *ret_prefix_len,
250 uint32_t *ret_lifetime_preferred,
251 uint32_t *ret_lifetime_valid) {
252
253 assert_return(lease, -EINVAL);
254
255 if (!lease->prefix_iter)
256 return -ENODATA;
257
258 if (ret_prefix)
259 *ret_prefix = lease->prefix_iter->iapdprefix.address;
260 if (ret_prefix_len)
261 *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
262 if (ret_lifetime_preferred)
263 *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
264 if (ret_lifetime_valid)
265 *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
266
267 lease->prefix_iter = lease->prefix_iter->addresses_next;
268 return 0;
269 }
270
271 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
272 if (lease)
273 lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
274 }
275
276 int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
277 assert(lease);
278 assert(optval || optlen == 0);
279
280 if (optlen == 0)
281 return 0;
282
283 return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
284 }
285
286 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
287 assert_return(lease, -EINVAL);
288
289 if (!lease->dns)
290 return -ENODATA;
291
292 if (ret)
293 *ret = lease->dns;
294
295 return lease->dns_count;
296 }
297
298 int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
299 _cleanup_strv_free_ char **domains = NULL;
300 int r;
301
302 assert(lease);
303 assert(optval || optlen == 0);
304
305 if (optlen == 0)
306 return 0;
307
308 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
309 if (r < 0)
310 return r;
311
312 return strv_extend_strv(&lease->domains, domains, true);
313 }
314
315 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
316 assert_return(lease, -EINVAL);
317 assert_return(ret, -EINVAL);
318
319 if (!lease->domains)
320 return -ENODATA;
321
322 *ret = lease->domains;
323 return strv_length(lease->domains);
324 }
325
326 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
327 int r;
328
329 assert(lease);
330 assert(optval || optlen == 0);
331
332 for (size_t offset = 0; offset < optlen;) {
333 const uint8_t *subval;
334 size_t sublen;
335 uint16_t subopt;
336
337 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
338 if (r < 0)
339 return r;
340
341 switch (subopt) {
342 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
343 case DHCP6_NTP_SUBOPTION_MC_ADDR:
344 if (sublen != 16)
345 return -EINVAL;
346
347 r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
348 if (r < 0)
349 return r;
350
351 break;
352
353 case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
354 _cleanup_free_ char *server = NULL;
355
356 r = dhcp6_option_parse_domainname(subval, sublen, &server);
357 if (r < 0)
358 return r;
359
360 if (strv_contains(lease->ntp_fqdn, server))
361 continue;
362
363 r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
364 if (r < 0)
365 return r;
366
367 break;
368 }}
369 }
370
371 return 0;
372 }
373
374 int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
375 assert(lease);
376 assert(optval || optlen == 0);
377
378 if (optlen == 0)
379 return 0;
380
381 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
382 return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
383 }
384
385 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
386 assert_return(lease, -EINVAL);
387
388 if (lease->ntp) {
389 if (ret)
390 *ret = lease->ntp;
391 return lease->ntp_count;
392 }
393
394 if (lease->sntp && !lease->ntp_fqdn) {
395 /* Fallback to the deprecated SNTP option. */
396 if (ret)
397 *ret = lease->sntp;
398 return lease->sntp_count;
399 }
400
401 return -ENODATA;
402 }
403
404 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
405 assert_return(lease, -EINVAL);
406
407 if (!lease->ntp_fqdn)
408 return -ENODATA;
409
410 if (ret)
411 *ret = lease->ntp_fqdn;
412 return strv_length(lease->ntp_fqdn);
413 }
414
415 int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
416 char *fqdn;
417 int r;
418
419 assert(lease);
420 assert(optval || optlen == 0);
421
422 if (optlen == 0)
423 return 0;
424
425 if (optlen < 2)
426 return -ENODATA;
427
428 /* Ignore the flags field, it doesn't carry any useful
429 information for clients. */
430 r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
431 if (r < 0)
432 return r;
433
434 return free_and_replace(lease->fqdn, fqdn);
435 }
436
437 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
438 assert_return(lease, -EINVAL);
439 assert_return(ret, -EINVAL);
440
441 if (!lease->fqdn)
442 return -ENODATA;
443
444 *ret = lease->fqdn;
445 return 0;
446 }
447
448 static int dhcp6_lease_parse_message(
449 sd_dhcp6_client *client,
450 sd_dhcp6_lease *lease,
451 const DHCP6Message *message,
452 size_t len) {
453
454 usec_t irt = IRT_DEFAULT;
455 int r;
456
457 assert(client);
458 assert(lease);
459 assert(message);
460 assert(len >= sizeof(DHCP6Message));
461
462 len -= sizeof(DHCP6Message);
463 for (size_t offset = 0; offset < len;) {
464 uint16_t optcode;
465 size_t optlen;
466 const uint8_t *optval;
467
468 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
469 if (r < 0)
470 return log_dhcp6_client_errno(client, r,
471 "Failed to parse option header at offset %zu of total length %zu: %m",
472 offset, len);
473
474 switch (optcode) {
475 case SD_DHCP6_OPTION_CLIENTID:
476 if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
477 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
478 dhcp6_message_type_to_string(message->type));
479
480 r = dhcp6_lease_set_clientid(lease, optval, optlen);
481 if (r < 0)
482 return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
483
484 break;
485
486 case SD_DHCP6_OPTION_SERVERID:
487 if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
488 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
489 dhcp6_message_type_to_string(message->type));
490
491 r = dhcp6_lease_set_serverid(lease, optval, optlen);
492 if (r < 0)
493 return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
494
495 break;
496
497 case SD_DHCP6_OPTION_PREFERENCE:
498 if (optlen != 1)
499 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
500
501 r = dhcp6_lease_set_preference(lease, optval[0]);
502 if (r < 0)
503 return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
504
505 break;
506
507 case SD_DHCP6_OPTION_STATUS_CODE: {
508 _cleanup_free_ char *msg = NULL;
509
510 r = dhcp6_option_parse_status(optval, optlen, &msg);
511 if (r < 0)
512 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
513
514 if (r > 0)
515 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
516 "Received %s message with non-zero status: %s%s%s",
517 dhcp6_message_type_to_string(message->type),
518 strempty(msg), isempty(msg) ? "" : ": ",
519 dhcp6_message_status_to_string(r));
520 break;
521 }
522 case SD_DHCP6_OPTION_IA_NA: {
523 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
524
525 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
526 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
527 break;
528 }
529
530 r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
531 if (r == -ENOMEM)
532 return log_oom_debug();
533 if (r < 0) {
534 log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
535 continue;
536 }
537
538 if (lease->ia_na) {
539 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
540 continue;
541 }
542
543 dhcp6_ia_free(lease->ia_na);
544 lease->ia_na = TAKE_PTR(ia);
545 break;
546 }
547 case SD_DHCP6_OPTION_IA_PD: {
548 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
549
550 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
551 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
552 break;
553 }
554
555 r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
556 if (r == -ENOMEM)
557 return log_oom_debug();
558 if (r < 0) {
559 log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
560 continue;
561 }
562
563 if (lease->ia_pd) {
564 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
565 continue;
566 }
567
568 dhcp6_ia_free(lease->ia_pd);
569 lease->ia_pd = TAKE_PTR(ia);
570 break;
571 }
572 case SD_DHCP6_OPTION_RAPID_COMMIT:
573 if (optlen != 0)
574 log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
575
576 r = dhcp6_lease_set_rapid_commit(lease);
577 if (r < 0)
578 return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
579
580 break;
581
582 case SD_DHCP6_OPTION_DNS_SERVER:
583 r = dhcp6_lease_add_dns(lease, optval, optlen);
584 if (r < 0)
585 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
586
587 break;
588
589 case SD_DHCP6_OPTION_DOMAIN:
590 r = dhcp6_lease_add_domains(lease, optval, optlen);
591 if (r < 0)
592 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
593
594 break;
595
596 case SD_DHCP6_OPTION_NTP_SERVER:
597 r = dhcp6_lease_add_ntp(lease, optval, optlen);
598 if (r < 0)
599 log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
600
601 break;
602
603 case SD_DHCP6_OPTION_SNTP_SERVER:
604 r = dhcp6_lease_add_sntp(lease, optval, optlen);
605 if (r < 0)
606 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
607
608 break;
609
610 case SD_DHCP6_OPTION_CLIENT_FQDN:
611 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
612 if (r < 0)
613 log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
614
615 break;
616
617 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
618 if (optlen != 4)
619 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
620 "Received information refresh time option with an invalid length (%zu).", optlen);
621
622 irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
623 break;
624 }
625 }
626
627 uint8_t *clientid;
628 size_t clientid_len;
629 if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
630 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
631 "%s message does not contain client ID. Ignoring.",
632 dhcp6_message_type_to_string(message->type));
633
634 if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
635 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
636 "The client ID in %s message does not match. Ignoring.",
637 dhcp6_message_type_to_string(message->type));
638
639 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
640 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
641 log_dhcp6_client(client, "New information request will be refused in %s.",
642 FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
643
644 } else {
645 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
646 if (r < 0)
647 return log_dhcp6_client_errno(client, r, "%s has no server id",
648 dhcp6_message_type_to_string(message->type));
649
650 if (!lease->ia_na && !lease->ia_pd)
651 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
652 "No IA_PD prefix or IA_NA address received. Ignoring.");
653
654 dhcp6_lease_set_lifetime(lease);
655 }
656
657 return 0;
658 }
659
660 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
661 if (!lease)
662 return NULL;
663
664 free(lease->clientid);
665 free(lease->serverid);
666 dhcp6_ia_free(lease->ia_na);
667 dhcp6_ia_free(lease->ia_pd);
668 free(lease->dns);
669 free(lease->fqdn);
670 strv_free(lease->domains);
671 free(lease->ntp);
672 strv_free(lease->ntp_fqdn);
673 free(lease->sntp);
674
675 return mfree(lease);
676 }
677
678 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
679
680 int dhcp6_lease_new(sd_dhcp6_lease **ret) {
681 sd_dhcp6_lease *lease;
682
683 assert(ret);
684
685 lease = new(sd_dhcp6_lease, 1);
686 if (!lease)
687 return -ENOMEM;
688
689 *lease = (sd_dhcp6_lease) {
690 .n_ref = 1,
691 };
692
693 *ret = lease;
694 return 0;
695 }
696
697 int dhcp6_lease_new_from_message(
698 sd_dhcp6_client *client,
699 const DHCP6Message *message,
700 size_t len,
701 const triple_timestamp *timestamp,
702 const struct in6_addr *server_address,
703 sd_dhcp6_lease **ret) {
704
705 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
706 int r;
707
708 assert(client);
709 assert(message);
710 assert(len >= sizeof(DHCP6Message));
711 assert(ret);
712
713 r = dhcp6_lease_new(&lease);
714 if (r < 0)
715 return r;
716
717 dhcp6_lease_set_timestamp(lease, timestamp);
718 dhcp6_lease_set_server_address(lease, server_address);
719
720 r = dhcp6_lease_parse_message(client, lease, message, len);
721 if (r < 0)
722 return r;
723
724 *ret = TAKE_PTR(lease);
725 return 0;
726 }