]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-lease.c
sd-dhcp6-client: always use ENODATA when a lease does not have requested data
[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 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
15 assert_return(lease, -EINVAL);
16 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
17 assert_return(clock_supported(clock), -EOPNOTSUPP);
18 assert_return(ret, -EINVAL);
19
20 if (!triple_timestamp_is_set(&lease->timestamp))
21 return -ENODATA;
22
23 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
24 return 0;
25 }
26
27 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
28 assert_return(lease, -EINVAL);
29 assert_return(ret, -EINVAL);
30
31 *ret = lease->server_address;
32 return 0;
33 }
34
35 int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
36 DHCP6Address *addr;
37 uint32_t valid = 0, t;
38
39 assert(ia);
40 assert(expire);
41
42 LIST_FOREACH(addresses, addr, ia->addresses) {
43 t = be32toh(addr->iaaddr.lifetime_valid);
44 if (valid < t)
45 valid = t;
46 }
47
48 t = be32toh(ia->header.lifetime_t2);
49 if (t > valid)
50 return -EINVAL;
51
52 *expire = valid - t;
53
54 return 0;
55 }
56
57 void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
58 DHCP6Address *a, *n;
59
60 assert(ia);
61
62 LIST_FOREACH_SAFE(addresses, a, n, ia->addresses)
63 free(a);
64
65 ia->addresses = NULL;
66 }
67
68 DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
69 if (!ia)
70 return NULL;
71
72 dhcp6_ia_clear_addresses(ia);
73
74 return mfree(ia);
75 }
76
77 int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
78 uint8_t *clientid = NULL;
79
80 assert(lease);
81 assert(id || len == 0);
82
83 if (len > 0) {
84 clientid = memdup(id, len);
85 if (!clientid)
86 return -ENOMEM;
87 }
88
89 free_and_replace(lease->clientid, clientid);
90 lease->clientid_len = len;
91
92 return 0;
93 }
94
95 int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
96 assert(lease);
97
98 if (!lease->clientid)
99 return -ENODATA;
100
101 if (ret_id)
102 *ret_id = lease->clientid;
103 if (ret_len)
104 *ret_len = lease->clientid_len;
105
106 return 0;
107 }
108
109 int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
110 uint8_t *serverid = NULL;
111
112 assert(lease);
113 assert(id || len == 0);
114
115 if (len > 0) {
116 serverid = memdup(id, len);
117 if (!serverid)
118 return -ENOMEM;
119 }
120
121 free_and_replace(lease->serverid, serverid);
122 lease->serverid_len = len;
123
124 return 0;
125 }
126
127 int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
128 assert(lease);
129
130 if (!lease->serverid)
131 return -ENODATA;
132
133 if (ret_id)
134 *ret_id = lease->serverid;
135 if (ret_len)
136 *ret_len = lease->serverid_len;
137 return 0;
138 }
139
140 int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
141 assert(lease);
142
143 lease->preference = preference;
144 return 0;
145 }
146
147 int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
148 assert(lease);
149 assert(ret);
150
151 *ret = lease->preference;
152 return 0;
153 }
154
155 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
156 assert(lease);
157
158 lease->rapid_commit = true;
159 return 0;
160 }
161
162 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
163 assert(lease);
164 assert(ret);
165
166 *ret = lease->rapid_commit;
167 return 0;
168 }
169
170 int sd_dhcp6_lease_get_address(
171 sd_dhcp6_lease *lease,
172 struct in6_addr *ret_addr,
173 uint32_t *ret_lifetime_preferred,
174 uint32_t *ret_lifetime_valid) {
175
176 assert_return(lease, -EINVAL);
177
178 if (!lease->addr_iter)
179 return -ENODATA;
180
181 if (ret_addr)
182 *ret_addr = lease->addr_iter->iaaddr.address;
183 if (ret_lifetime_preferred)
184 *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
185 if (ret_lifetime_valid)
186 *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
187
188 lease->addr_iter = lease->addr_iter->addresses_next;
189 return 0;
190 }
191
192 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
193 if (lease)
194 lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
195 }
196
197 int sd_dhcp6_lease_get_pd(
198 sd_dhcp6_lease *lease,
199 struct in6_addr *ret_prefix,
200 uint8_t *ret_prefix_len,
201 uint32_t *ret_lifetime_preferred,
202 uint32_t *ret_lifetime_valid) {
203
204 assert_return(lease, -EINVAL);
205
206 if (!lease->prefix_iter)
207 return -ENODATA;
208
209 if (ret_prefix)
210 *ret_prefix = lease->prefix_iter->iapdprefix.address;
211 if (ret_prefix_len)
212 *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
213 if (ret_lifetime_preferred)
214 *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
215 if (ret_lifetime_valid)
216 *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
217
218 lease->prefix_iter = lease->prefix_iter->addresses_next;
219 return 0;
220 }
221
222 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
223 if (lease)
224 lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
225 }
226
227 int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
228 assert(lease);
229 assert(optval || optlen == 0);
230
231 if (optlen == 0)
232 return 0;
233
234 return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
235 }
236
237 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
238 assert_return(lease, -EINVAL);
239
240 if (!lease->dns)
241 return -ENODATA;
242
243 if (ret)
244 *ret = lease->dns;
245
246 return lease->dns_count;
247 }
248
249 int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
250 _cleanup_strv_free_ char **domains = NULL;
251 int r;
252
253 assert(lease);
254 assert(optval || optlen == 0);
255
256 if (optlen == 0)
257 return 0;
258
259 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
260 if (r < 0)
261 return r;
262
263 return strv_extend_strv(&lease->domains, domains, true);
264 }
265
266 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
267 assert_return(lease, -EINVAL);
268 assert_return(ret, -EINVAL);
269
270 if (!lease->domains)
271 return -ENODATA;
272
273 *ret = lease->domains;
274 return strv_length(lease->domains);
275 }
276
277 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
278 int r;
279
280 assert(lease);
281 assert(optval || optlen == 0);
282
283 for (size_t offset = 0; offset < optlen;) {
284 const uint8_t *subval;
285 size_t sublen;
286 uint16_t subopt;
287
288 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
289 if (r < 0)
290 return r;
291
292 switch(subopt) {
293 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
294 case DHCP6_NTP_SUBOPTION_MC_ADDR:
295 if (sublen != 16)
296 return 0;
297
298 r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
299 if (r < 0)
300 return r;
301
302 break;
303
304 case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
305 _cleanup_free_ char *server = NULL;
306
307 r = dhcp6_option_parse_domainname(subval, sublen, &server);
308 if (r < 0)
309 return r;
310
311 if (strv_contains(lease->ntp_fqdn, server))
312 continue;
313
314 r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
315 if (r < 0)
316 return r;
317
318 break;
319 }}
320 }
321
322 return 0;
323 }
324
325 int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
326 assert(lease);
327 assert(optval || optlen == 0);
328
329 if (optlen == 0)
330 return 0;
331
332 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
333 return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
334 }
335
336 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
337 assert_return(lease, -EINVAL);
338
339 if (lease->ntp) {
340 if (ret)
341 *ret = lease->ntp;
342 return lease->ntp_count;
343 }
344
345 if (lease->sntp && !lease->ntp_fqdn) {
346 /* Fallback to the deprecated SNTP option. */
347 if (ret)
348 *ret = lease->sntp;
349 return lease->sntp_count;
350 }
351
352 return -ENODATA;
353 }
354
355 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
356 assert_return(lease, -EINVAL);
357
358 if (!lease->ntp_fqdn)
359 return -ENODATA;
360
361 if (ret)
362 *ret = lease->ntp_fqdn;
363 return strv_length(lease->ntp_fqdn);
364 }
365
366 int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
367 char *fqdn;
368 int r;
369
370 assert(lease);
371 assert(optval || optlen == 0);
372
373 if (optlen == 0)
374 return 0;
375
376 if (optlen < 2)
377 return -ENODATA;
378
379 /* Ignore the flags field, it doesn't carry any useful
380 information for clients. */
381 r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
382 if (r < 0)
383 return r;
384
385 return free_and_replace(lease->fqdn, fqdn);
386 }
387
388 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
389 assert_return(lease, -EINVAL);
390 assert_return(ret, -EINVAL);
391
392 if (!lease->fqdn)
393 return -ENODATA;
394
395 *ret = lease->fqdn;
396 return 0;
397 }
398
399 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
400 if (!lease)
401 return NULL;
402
403 free(lease->clientid);
404 free(lease->serverid);
405 dhcp6_ia_free(lease->ia_na);
406 dhcp6_ia_free(lease->ia_pd);
407 free(lease->dns);
408 free(lease->fqdn);
409 strv_free(lease->domains);
410 free(lease->ntp);
411 strv_free(lease->ntp_fqdn);
412 free(lease->sntp);
413
414 return mfree(lease);
415 }
416
417 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
418
419 int dhcp6_lease_new(sd_dhcp6_lease **ret) {
420 sd_dhcp6_lease *lease;
421
422 assert(ret);
423
424 lease = new0(sd_dhcp6_lease, 1);
425 if (!lease)
426 return -ENOMEM;
427
428 lease->n_ref = 1;
429
430 *ret = lease;
431 return 0;
432 }