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