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