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 
dhcp6_lease_set_timestamp(sd_dhcp6_lease * lease,const triple_timestamp * timestamp)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 
sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease * lease,clockid_t clock,uint64_t * ret)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 
sec2usec(uint32_t sec)38 static usec_t sec2usec(uint32_t sec) {
39         return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
40 }
41 
dhcp6_lease_set_lifetime(sd_dhcp6_lease * lease)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 
dhcp6_lease_get_lifetime(sd_dhcp6_lease * lease,usec_t * ret_t1,usec_t * ret_t2,usec_t * ret_valid)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 
dhcp6_lease_set_server_address(sd_dhcp6_lease * lease,const struct in6_addr * server_address)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 
sd_dhcp6_lease_get_server_address(sd_dhcp6_lease * lease,struct in6_addr * ret)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 
dhcp6_ia_clear_addresses(DHCP6IA * ia)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 
dhcp6_ia_free(DHCP6IA * ia)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 
dhcp6_lease_set_clientid(sd_dhcp6_lease * lease,const uint8_t * id,size_t len)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 
dhcp6_lease_get_clientid(sd_dhcp6_lease * lease,uint8_t ** ret_id,size_t * ret_len)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 
dhcp6_lease_set_serverid(sd_dhcp6_lease * lease,const uint8_t * id,size_t len)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 
dhcp6_lease_get_serverid(sd_dhcp6_lease * lease,uint8_t ** ret_id,size_t * ret_len)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 
dhcp6_lease_set_preference(sd_dhcp6_lease * lease,uint8_t preference)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 
dhcp6_lease_get_preference(sd_dhcp6_lease * lease,uint8_t * ret)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 
dhcp6_lease_set_rapid_commit(sd_dhcp6_lease * lease)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 
dhcp6_lease_get_rapid_commit(sd_dhcp6_lease * lease,bool * ret)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 
sd_dhcp6_lease_get_address(sd_dhcp6_lease * lease,struct in6_addr * ret_addr,uint32_t * ret_lifetime_preferred,uint32_t * ret_lifetime_valid)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 
sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease * lease)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 
sd_dhcp6_lease_get_pd(sd_dhcp6_lease * lease,struct in6_addr * ret_prefix,uint8_t * ret_prefix_len,uint32_t * ret_lifetime_preferred,uint32_t * ret_lifetime_valid)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 
sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease * lease)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 
dhcp6_lease_add_dns(sd_dhcp6_lease * lease,const uint8_t * optval,size_t optlen)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 
sd_dhcp6_lease_get_dns(sd_dhcp6_lease * lease,const struct in6_addr ** ret)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 
dhcp6_lease_add_domains(sd_dhcp6_lease * lease,const uint8_t * optval,size_t optlen)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 
sd_dhcp6_lease_get_domains(sd_dhcp6_lease * lease,char *** ret)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 
dhcp6_lease_add_ntp(sd_dhcp6_lease * lease,const uint8_t * optval,size_t optlen)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 
dhcp6_lease_add_sntp(sd_dhcp6_lease * lease,const uint8_t * optval,size_t optlen)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 
sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease * lease,const struct in6_addr ** ret)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 
sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease * lease,char *** ret)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 
dhcp6_lease_set_fqdn(sd_dhcp6_lease * lease,const uint8_t * optval,size_t optlen)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 
sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease * lease,const char ** ret)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 
dhcp6_lease_parse_message(sd_dhcp6_client * client,sd_dhcp6_lease * lease,const DHCP6Message * message,size_t len)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 r;
471 
472                 switch (optcode) {
473                 case SD_DHCP6_OPTION_CLIENTID:
474                         if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
475                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
476                                                               dhcp6_message_type_to_string(message->type));
477 
478                         r = dhcp6_lease_set_clientid(lease, optval, optlen);
479                         if (r < 0)
480                                 return r;
481 
482                         break;
483 
484                 case SD_DHCP6_OPTION_SERVERID:
485                         if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
486                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
487                                                               dhcp6_message_type_to_string(message->type));
488 
489                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
490                         if (r < 0)
491                                 return r;
492 
493                         break;
494 
495                 case SD_DHCP6_OPTION_PREFERENCE:
496                         if (optlen != 1)
497                                 return -EINVAL;
498 
499                         r = dhcp6_lease_set_preference(lease, optval[0]);
500                         if (r < 0)
501                                 return r;
502 
503                         break;
504 
505                 case SD_DHCP6_OPTION_STATUS_CODE: {
506                         _cleanup_free_ char *msg = NULL;
507 
508                         r = dhcp6_option_parse_status(optval, optlen, &msg);
509                         if (r < 0)
510                                 return r;
511 
512                         if (r > 0)
513                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
514                                                               "Received %s message with non-zero status: %s%s%s",
515                                                               dhcp6_message_type_to_string(message->type),
516                                                               strempty(msg), isempty(msg) ? "" : ": ",
517                                                               dhcp6_message_status_to_string(r));
518                         break;
519                 }
520                 case SD_DHCP6_OPTION_IA_NA: {
521                         _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
522 
523                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
524                                 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
525                                 break;
526                         }
527 
528                         r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
529                         if (r == -ENOMEM)
530                                 return r;
531                         if (r < 0)
532                                 continue;
533 
534                         if (lease->ia_na) {
535                                 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
536                                 continue;
537                         }
538 
539                         dhcp6_ia_free(lease->ia_na);
540                         lease->ia_na = TAKE_PTR(ia);
541                         break;
542                 }
543                 case SD_DHCP6_OPTION_IA_PD: {
544                         _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
545 
546                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
547                                 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
548                                 break;
549                         }
550 
551                         r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
552                         if (r == -ENOMEM)
553                                 return r;
554                         if (r < 0)
555                                 continue;
556 
557                         if (lease->ia_pd) {
558                                 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
559                                 continue;
560                         }
561 
562                         dhcp6_ia_free(lease->ia_pd);
563                         lease->ia_pd = TAKE_PTR(ia);
564                         break;
565                 }
566                 case SD_DHCP6_OPTION_RAPID_COMMIT:
567                         r = dhcp6_lease_set_rapid_commit(lease);
568                         if (r < 0)
569                                 return r;
570 
571                         break;
572 
573                 case SD_DHCP6_OPTION_DNS_SERVER:
574                         r = dhcp6_lease_add_dns(lease, optval, optlen);
575                         if (r < 0)
576                                 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
577 
578                         break;
579 
580                 case SD_DHCP6_OPTION_DOMAIN:
581                         r = dhcp6_lease_add_domains(lease, optval, optlen);
582                         if (r < 0)
583                                 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
584 
585                         break;
586 
587                 case SD_DHCP6_OPTION_NTP_SERVER:
588                         r = dhcp6_lease_add_ntp(lease, optval, optlen);
589                         if (r < 0)
590                                 log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
591 
592                         break;
593 
594                 case SD_DHCP6_OPTION_SNTP_SERVER:
595                         r = dhcp6_lease_add_sntp(lease, optval, optlen);
596                         if (r < 0)
597                                 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
598 
599                         break;
600 
601                 case SD_DHCP6_OPTION_CLIENT_FQDN:
602                         r = dhcp6_lease_set_fqdn(lease, optval, optlen);
603                         if (r < 0)
604                                 log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
605 
606                         break;
607 
608                 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
609                         if (optlen != 4)
610                                 return -EINVAL;
611 
612                         irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
613                         break;
614                 }
615         }
616 
617         uint8_t *clientid;
618         size_t clientid_len;
619         if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
620                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
621                                               "%s message does not contain client ID. Ignoring.",
622                                               dhcp6_message_type_to_string(message->type));
623 
624         if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
625                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
626                                               "The client ID in %s message does not match. Ignoring.",
627                                               dhcp6_message_type_to_string(message->type));
628 
629         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
630                 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
631                 log_dhcp6_client(client, "New information request will be refused in %s.",
632                                  FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
633 
634         } else {
635                 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
636                 if (r < 0)
637                         return log_dhcp6_client_errno(client, r, "%s has no server id",
638                                                       dhcp6_message_type_to_string(message->type));
639 
640                 if (!lease->ia_na && !lease->ia_pd)
641                         return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
642                                                       "No IA_PD prefix or IA_NA address received. Ignoring.");
643 
644                 dhcp6_lease_set_lifetime(lease);
645         }
646 
647         return 0;
648 }
649 
dhcp6_lease_free(sd_dhcp6_lease * lease)650 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
651         if (!lease)
652                 return NULL;
653 
654         free(lease->clientid);
655         free(lease->serverid);
656         dhcp6_ia_free(lease->ia_na);
657         dhcp6_ia_free(lease->ia_pd);
658         free(lease->dns);
659         free(lease->fqdn);
660         strv_free(lease->domains);
661         free(lease->ntp);
662         strv_free(lease->ntp_fqdn);
663         free(lease->sntp);
664 
665         return mfree(lease);
666 }
667 
668 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
669 
dhcp6_lease_new(sd_dhcp6_lease ** ret)670 int dhcp6_lease_new(sd_dhcp6_lease **ret) {
671         sd_dhcp6_lease *lease;
672 
673         assert(ret);
674 
675         lease = new(sd_dhcp6_lease, 1);
676         if (!lease)
677                 return -ENOMEM;
678 
679         *lease = (sd_dhcp6_lease) {
680                 .n_ref = 1,
681         };
682 
683         *ret = lease;
684         return 0;
685 }
686 
dhcp6_lease_new_from_message(sd_dhcp6_client * client,const DHCP6Message * message,size_t len,const triple_timestamp * timestamp,const struct in6_addr * server_address,sd_dhcp6_lease ** ret)687 int dhcp6_lease_new_from_message(
688                 sd_dhcp6_client *client,
689                 const DHCP6Message *message,
690                 size_t len,
691                 const triple_timestamp *timestamp,
692                 const struct in6_addr *server_address,
693                 sd_dhcp6_lease **ret) {
694 
695         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
696         int r;
697 
698         assert(client);
699         assert(message);
700         assert(len >= sizeof(DHCP6Message));
701         assert(ret);
702 
703         r = dhcp6_lease_new(&lease);
704         if (r < 0)
705                 return r;
706 
707         dhcp6_lease_set_timestamp(lease, timestamp);
708         dhcp6_lease_set_server_address(lease, server_address);
709 
710         r = dhcp6_lease_parse_message(client, lease, message, len);
711         if (r < 0)
712                 return r;
713 
714         *ret = TAKE_PTR(lease);
715         return 0;
716 }
717