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