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