]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
network: s/user_data/userdata/
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
CommitLineData
a6cc569e
TG
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5 Copyright (C) 2014 Tom Gundersen
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <stdlib.h>
22#include <errno.h>
23#include <string.h>
24#include <stdio.h>
fe8db0c5 25#include <arpa/inet.h>
a6cc569e 26
fe8db0c5 27#include "fileio.h"
bab47929 28#include "unaligned.h"
df40eee8 29#include "in-addr-util.h"
958b66ea 30#include "hostname-util.h"
a6cc569e 31#include "dhcp-protocol.h"
fe8db0c5
TG
32#include "dhcp-lease-internal.h"
33#include "sd-dhcp-lease.h"
09bee74d 34#include "network-internal.h"
37de2509 35#include "dns-domain.h"
a6cc569e
TG
36
37int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
38 assert_return(lease, -EINVAL);
39 assert_return(addr, -EINVAL);
40
41 addr->s_addr = lease->address;
42
43 return 0;
44}
45
68ceb9df
PF
46int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
47 assert_return(lease, -EINVAL);
1c6eb4e3 48 assert_return(lifetime, -EINVAL);
68ceb9df
PF
49
50 *lifetime = lease->lifetime;
51
52 return 0;
53}
54
a6cc569e
TG
55int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
56 assert_return(lease, -EINVAL);
57 assert_return(mtu, -EINVAL);
58
59 if (lease->mtu)
60 *mtu = lease->mtu;
61 else
62 return -ENOENT;
63
64 return 0;
65}
66
a2ba62c7 67int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
a6cc569e
TG
68 assert_return(lease, -EINVAL);
69 assert_return(addr, -EINVAL);
a6cc569e
TG
70
71 if (lease->dns_size) {
a6cc569e 72 *addr = lease->dns;
a2ba62c7 73 return lease->dns_size;
a6cc569e
TG
74 } else
75 return -ENOENT;
76
77 return 0;
78}
79
a2ba62c7 80int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
46844696
TG
81 assert_return(lease, -EINVAL);
82 assert_return(addr, -EINVAL);
46844696
TG
83
84 if (lease->ntp_size) {
46844696 85 *addr = lease->ntp;
a2ba62c7 86 return lease->ntp_size;
46844696
TG
87 } else
88 return -ENOENT;
89
90 return 0;
91}
92
a6cc569e
TG
93int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
94 assert_return(lease, -EINVAL);
95 assert_return(domainname, -EINVAL);
96
97 if (lease->domainname)
98 *domainname = lease->domainname;
99 else
100 return -ENOENT;
101
102 return 0;
103}
104
105int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
106 assert_return(lease, -EINVAL);
107 assert_return(hostname, -EINVAL);
108
109 if (lease->hostname)
110 *hostname = lease->hostname;
111 else
112 return -ENOENT;
113
114 return 0;
115}
116
ce78df79
TG
117int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
118 assert_return(lease, -EINVAL);
119 assert_return(root_path, -EINVAL);
120
121 if (lease->root_path)
122 *root_path = lease->root_path;
123 else
124 return -ENOENT;
125
126 return 0;
127}
128
a6cc569e
TG
129int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
130 assert_return(lease, -EINVAL);
131 assert_return(addr, -EINVAL);
132
8ddbeaa2
UTL
133 if (lease->router != INADDR_ANY)
134 addr->s_addr = lease->router;
135 else
136 return -ENOENT;
a6cc569e
TG
137
138 return 0;
139}
140
141int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
142 assert_return(lease, -EINVAL);
143 assert_return(addr, -EINVAL);
144
145 addr->s_addr = lease->subnet_mask;
146
147 return 0;
148}
149
0ad853bc
TG
150int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
151 assert_return(lease, -EINVAL);
152 assert_return(addr, -EINVAL);
153
154 addr->s_addr = lease->server_address;
155
156 return 0;
157}
158
8e34a618
TG
159int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
160 assert_return(lease, -EINVAL);
161 assert_return(addr, -EINVAL);
162
163 addr->s_addr = lease->next_server;
164
165 return 0;
166}
167
a2ba62c7 168int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
e1ea665e
EY
169
170 assert_return(lease, -EINVAL);
171 assert_return(routes, -EINVAL);
e1ea665e
EY
172
173 if (lease->static_route_size) {
174 *routes = lease->static_route;
a2ba62c7 175 return lease->static_route_size;
e1ea665e
EY
176 } else
177 return -ENOENT;
178
179 return 0;
180}
181
e43a8393
BG
182int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
183 size_t *data_len) {
184 assert_return(lease, -EINVAL);
185 assert_return(data, -EINVAL);
186 assert_return(data_len, -EINVAL);
187
188 if (!lease->vendor_specific)
189 return -ENOENT;
190
191 *data = lease->vendor_specific;
192 *data_len = lease->vendor_specific_len;
193
194 return 0;
195}
196
a6cc569e 197sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
3733eec3
LP
198
199 if (!lease)
200 return NULL;
201
202 assert(lease->n_ref >= 1);
203 lease->n_ref++;
a6cc569e
TG
204
205 return lease;
206}
207
208sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
7e753d9d 209
3733eec3
LP
210 if (!lease)
211 return NULL;
7e753d9d 212
3733eec3
LP
213 assert(lease->n_ref >= 1);
214 lease->n_ref--;
215
216 if (lease->n_ref > 0)
217 return NULL;
218
219 while (lease->private_options) {
220 struct sd_dhcp_raw_option *option = lease->private_options;
221
222 LIST_REMOVE(options, lease->private_options, option);
223
224 free(option->data);
225 free(option);
a6cc569e
TG
226 }
227
3733eec3
LP
228 free(lease->hostname);
229 free(lease->domainname);
230 free(lease->dns);
231 free(lease->ntp);
232 free(lease->static_route);
233 free(lease->client_id);
234 free(lease->vendor_specific);
235 free(lease);
236
a6cc569e
TG
237 return NULL;
238}
239
e140ae58 240static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
e140ae58
TG
241 assert(option);
242 assert(ret);
243
244 if (len == 4) {
bab47929 245 *ret = unaligned_read_be32((be32_t*) option);
e140ae58
TG
246
247 if (*ret < min)
248 *ret = min;
249 }
250}
251
f5c0c00f
TG
252static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
253 lease_parse_u32(option, len, (uint32_t *)ret, 0);
254}
255
e140ae58 256static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
257 assert(option);
258 assert(ret);
259
260 if (len == 2) {
bab47929 261 *ret = unaligned_read_be16((be16_t*) option);
e140ae58
TG
262
263 if (*ret < min)
264 *ret = min;
265 }
266}
267
268static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
269 assert(option);
270 assert(ret);
271
272 if (len == 4)
273 memcpy(ret, option, 4);
274}
275
f5c0c00f
TG
276static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
277 assert(option);
278 assert(ret);
279
280 if (len == 1)
281 *ret = !!(*option);
282}
283
284static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
285 assert(option);
286 assert(ret);
287
288 if (len == 1) {
289 *ret = *option;
290
291 if (*ret < min)
292 *ret = min;
293 }
294}
295
e140ae58
TG
296static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
297 assert(option);
298 assert(ret);
299
300 if (len >= 1) {
301 char *string;
302
43f447b1
LP
303 if (memchr(option, 0, len))
304 return -EINVAL;
305
e140ae58
TG
306 string = strndup((const char *)option, len);
307 if (!string)
43f447b1 308 return -ENOMEM;
e140ae58
TG
309
310 free(*ret);
311 *ret = string;
43f447b1
LP
312 } else
313 *ret = mfree(*ret);
e140ae58
TG
314
315 return 0;
316}
317
f5c0c00f 318static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
e140ae58
TG
319 assert(option);
320 assert(ret);
321 assert(ret_size);
322
f5c0c00f 323 if (len && !(len % (4 * mult))) {
e140ae58
TG
324 size_t size;
325 struct in_addr *addresses;
326
327 size = len / 4;
328
329 addresses = newdup(struct in_addr, option, size);
330 if (!addresses)
331 return -ENOMEM;
332
333 free(*ret);
334 *ret = addresses;
335 *ret_size = size;
336 }
337
338 return 0;
339}
340
f5c0c00f
TG
341static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
342 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
343}
344
345static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
346 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
347}
348
e1ea665e
EY
349static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
350 size_t *routes_size, size_t *routes_allocated) {
351
352 struct in_addr addr;
353
354 assert(option);
355 assert(routes);
356 assert(routes_size);
357 assert(routes_allocated);
358
359 if (!len)
360 return 0;
361
362 if (len % 8 != 0)
363 return -EINVAL;
364
365 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
366 return -ENOMEM;
367
368 while (len >= 8) {
369 struct sd_dhcp_route *route = *routes + *routes_size;
1caa12d0 370 int r;
e1ea665e 371
1caa12d0
TG
372 r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
373 if (r < 0) {
e1ea665e
EY
374 log_error("Failed to determine destination prefix length from class based IP, ignoring");
375 continue;
376 }
377
378 lease_parse_be32(option, 4, &addr.s_addr);
379 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
380 option += 4;
381
382 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
383 option += 4;
384
385 len -= 8;
386 (*routes_size)++;
387 }
388
389 return 0;
390}
391
392/* parses RFC3442 Classless Static Route Option */
393static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
394 size_t *routes_size, size_t *routes_allocated) {
395
396 assert(option);
397 assert(routes);
398 assert(routes_size);
399 assert(routes_allocated);
400
401 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
402
403 while (len > 0) {
404 uint8_t dst_octets;
405 struct sd_dhcp_route *route;
406
407 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
408 return -ENOMEM;
409
410 route = *routes + *routes_size;
411
412 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
413 route->dst_prefixlen = *option;
414 option++;
415 len--;
416
417 /* can't have more than 4 octets in IPv4 */
418 if (dst_octets > 4 || len < dst_octets)
419 return -EINVAL;
420
421 route->dst_addr.s_addr = 0;
422 memcpy(&route->dst_addr.s_addr, option, dst_octets);
423 option += dst_octets;
424 len -= dst_octets;
425
426 if (len < 4)
427 return -EINVAL;
428
429 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
430 option += 4;
431 len -= 4;
432
433 (*routes_size)++;
434 }
435
436 return 0;
437}
438
2d03c0b8
LP
439int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, void *userdata) {
440 sd_dhcp_lease *lease = userdata;
e140ae58
TG
441 int r;
442
443 assert(lease);
a6cc569e
TG
444
445 switch(code) {
446
f5c0c00f
TG
447 case DHCP_OPTION_TIME_OFFSET:
448 lease_parse_s32(option, len, &lease->time_offset);
f5c0c00f
TG
449 break;
450
451 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
452 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
f5c0c00f
TG
453 break;
454
a6cc569e 455 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
e140ae58 456 lease_parse_u32(option, len, &lease->lifetime, 1);
a6cc569e
TG
457 break;
458
459 case DHCP_OPTION_SERVER_IDENTIFIER:
e140ae58 460 lease_parse_be32(option, len, &lease->server_address);
a6cc569e
TG
461 break;
462
463 case DHCP_OPTION_SUBNET_MASK:
e140ae58 464 lease_parse_be32(option, len, &lease->subnet_mask);
a6cc569e
TG
465 break;
466
f5c0c00f
TG
467 case DHCP_OPTION_BROADCAST:
468 lease_parse_be32(option, len, &lease->broadcast);
f5c0c00f
TG
469 break;
470
a6cc569e 471 case DHCP_OPTION_ROUTER:
2d03c0b8 472 if (len >= 4)
a0518527 473 lease_parse_be32(option, 4, &lease->router);
a6cc569e
TG
474
475 break;
476
477 case DHCP_OPTION_DOMAIN_NAME_SERVER:
2d03c0b8 478 return lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
46844696
TG
479
480 case DHCP_OPTION_NTP_SERVER:
2d03c0b8 481 return lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
a6cc569e 482
f5c0c00f 483 case DHCP_OPTION_POLICY_FILTER:
2d03c0b8 484 return lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
f5c0c00f
TG
485
486 case DHCP_OPTION_STATIC_ROUTE:
2d03c0b8 487 return lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
f5c0c00f 488
a6cc569e 489 case DHCP_OPTION_INTERFACE_MTU:
e140ae58 490 lease_parse_u16(option, len, &lease->mtu, 68);
a6cc569e
TG
491 break;
492
f5c0c00f
TG
493 case DHCP_OPTION_INTERFACE_MDR:
494 lease_parse_u16(option, len, &lease->mdr, 576);
f5c0c00f
TG
495 break;
496
497 case DHCP_OPTION_INTERFACE_TTL:
498 lease_parse_u8(option, len, &lease->ttl, 1);
f5c0c00f
TG
499 break;
500
501 case DHCP_OPTION_BOOT_FILE_SIZE:
502 lease_parse_u16(option, len, &lease->boot_file_size, 0);
f5c0c00f
TG
503 break;
504
90981625
LP
505 case DHCP_OPTION_DOMAIN_NAME: {
506 _cleanup_free_ char *domainname = NULL, *normalized = NULL;
784d9b9c
TG
507
508 r = lease_parse_string(option, len, &domainname);
e140ae58
TG
509 if (r < 0)
510 return r;
a6cc569e 511
90981625
LP
512 r = dns_name_normalize(domainname, &normalized);
513 if (r < 0) {
514 log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
515 return 0;
516 }
784d9b9c 517
90981625
LP
518 if (is_localhost(normalized)) {
519 log_debug_errno(r, "Detected 'localhost' as suggested hostname, ignoring.");
37de2509
NO
520 break;
521 }
522
784d9b9c 523 free(lease->domainname);
90981625
LP
524 lease->domainname = normalized;
525 normalized = NULL;
a6cc569e 526
784d9b9c
TG
527 break;
528 }
90981625
LP
529
530 case DHCP_OPTION_HOST_NAME: {
531 _cleanup_free_ char *hostname = NULL, *normalized = NULL;
784d9b9c
TG
532
533 r = lease_parse_string(option, len, &hostname);
e140ae58
TG
534 if (r < 0)
535 return r;
a6cc569e 536
90981625
LP
537 r = dns_name_normalize(hostname, &normalized);
538 if (r < 0) {
539 log_debug_errno(r, "Failed to normalize host name '%s': %m", hostname);
540 return 0;
541 }
f50f01f4 542
90981625
LP
543 if (is_localhost(normalized)) {
544 log_debug_errno(r, "Detected 'localhost' as suggested hostname, ignoring.");
545 return 0;
546 }
784d9b9c 547
90981625
LP
548 free(lease->hostname);
549 lease->hostname = normalized;
550 normalized = NULL;
a6cc569e 551
784d9b9c
TG
552 break;
553 }
ce78df79 554
2d03c0b8
LP
555 case DHCP_OPTION_ROOT_PATH:
556 return lease_parse_string(option, len, &lease->root_path);
ce78df79 557
a6cc569e 558 case DHCP_OPTION_RENEWAL_T1_TIME:
e140ae58 559 lease_parse_u32(option, len, &lease->t1, 1);
a6cc569e
TG
560 break;
561
562 case DHCP_OPTION_REBINDING_T2_TIME:
e140ae58 563 lease_parse_u32(option, len, &lease->t2, 1);
f5c0c00f
TG
564 break;
565
566 case DHCP_OPTION_ENABLE_IP_FORWARDING:
567 lease_parse_bool(option, len, &lease->ip_forward);
f5c0c00f
TG
568 break;
569
570 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
571 lease_parse_bool(option, len, &lease->ip_forward_non_local);
a6cc569e 572 break;
e1ea665e
EY
573
574 case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
2d03c0b8
LP
575 return lease_parse_classless_routes(
576 option, len,
577 &lease->static_route,
578 &lease->static_route_size,
579 &lease->static_route_allocated);
e43a8393 580
8eb9058d
LP
581 case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
582 _cleanup_free_ char *tz = NULL;
583
584 r = lease_parse_string(option, len, &tz);
585 if (r < 0)
586 return r;
587
588 if (!timezone_is_valid(tz))
589 return -EINVAL;
590
591 free(lease->timezone);
592 lease->timezone = tz;
593 tz = NULL;
594 break;
595 }
596
e43a8393 597 case DHCP_OPTION_VENDOR_SPECIFIC:
2d03c0b8 598
e43a8393
BG
599 if (len >= 1) {
600 free(lease->vendor_specific);
601 lease->vendor_specific = memdup(option, len);
602 if (!lease->vendor_specific)
603 return -ENOMEM;
604 lease->vendor_specific_len = len;
605 }
606
607 break;
7e753d9d
AC
608
609 default:
610 if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
611 break;
612
613 r = dhcp_lease_insert_private_option(lease, code, option, len);
614 if (r < 0)
615 return r;
a6cc569e
TG
616 }
617
618 return 0;
619}
620
7e753d9d
AC
621int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
622 const uint8_t *data, uint8_t len) {
623 struct sd_dhcp_raw_option *cur, *option;
624
625 LIST_FOREACH(options, cur, lease->private_options) {
626 if (tag < cur->tag)
627 break;
628 else if (tag == cur->tag) {
629 log_error("Ignoring duplicate option, tagged %d.", tag);
630 return 0;
631 }
632 }
633
634 option = new(struct sd_dhcp_raw_option, 1);
635 if (!option)
636 return -ENOMEM;
637
638 option->tag = tag;
639 option->length = len;
640 option->data = memdup(data, len);
641 if (!option->data) {
642 free(option);
643 return -ENOMEM;
644 }
645
646 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
647
648 return 0;
649}
650
a6cc569e 651int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 652 sd_dhcp_lease *lease;
a6cc569e
TG
653
654 lease = new0(sd_dhcp_lease, 1);
655 if (!lease)
656 return -ENOMEM;
657
8ddbeaa2 658 lease->router = INADDR_ANY;
3733eec3 659 lease->n_ref = 1;
7e753d9d 660 LIST_HEAD_INIT(lease->private_options);
a6cc569e
TG
661
662 *ret = lease;
a6cc569e
TG
663 return 0;
664}
fe8db0c5 665
bd91b83e 666int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
667 _cleanup_free_ char *temp_path = NULL;
668 _cleanup_fclose_ FILE *f = NULL;
a073309f 669 struct sd_dhcp_raw_option *option;
fe8db0c5 670 struct in_addr address;
a2ba62c7 671 const struct in_addr *addresses;
e43a8393
BG
672 const uint8_t *client_id, *data;
673 size_t client_id_len, data_len;
fe8db0c5
TG
674 const char *string;
675 uint16_t mtu;
e1ea665e 676 struct sd_dhcp_route *routes;
fe8db0c5
TG
677 int r;
678
679 assert(lease);
680 assert(lease_file);
681
fe8db0c5
TG
682 r = fopen_temporary(lease_file, &f, &temp_path);
683 if (r < 0)
dacd6cee 684 goto fail;
fe8db0c5
TG
685
686 fchmod(fileno(f), 0644);
687
688 r = sd_dhcp_lease_get_address(lease, &address);
689 if (r < 0)
dacd6cee 690 goto fail;
fe8db0c5 691
fe8db0c5
TG
692 fprintf(f,
693 "# This is private data. Do not parse.\n"
109731eb 694 "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 695
fe8db0c5
TG
696 r = sd_dhcp_lease_get_netmask(lease, &address);
697 if (r < 0)
dacd6cee 698 goto fail;
fe8db0c5 699
109731eb 700 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 701
8ddbeaa2
UTL
702 r = sd_dhcp_lease_get_router(lease, &address);
703 if (r >= 0)
704 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
705
0ad853bc 706 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb
TG
707 if (r >= 0)
708 fprintf(f, "SERVER_ADDRESS=%s\n",
709 inet_ntoa(address));
0ad853bc 710
8e34a618 711 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
712 if (r >= 0)
713 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 714
fe8db0c5
TG
715 r = sd_dhcp_lease_get_mtu(lease, &mtu);
716 if (r >= 0)
717 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
718
b0e39c82 719 fputs("DNS=", f);
a2ba62c7 720 r = sd_dhcp_lease_get_dns(lease, &addresses);
109731eb 721 if (r >= 0)
b0e39c82
TG
722 serialize_in_addrs(f, addresses, r);
723 fputs("\n", f);
109731eb 724
b0e39c82 725 fputs("NTP=", f);
a2ba62c7 726 r = sd_dhcp_lease_get_ntp(lease, &addresses);
109731eb 727 if (r >= 0)
b0e39c82
TG
728 serialize_in_addrs(f, addresses, r);
729 fputs("\n", f);
fe8db0c5
TG
730
731 r = sd_dhcp_lease_get_domainname(lease, &string);
732 if (r >= 0)
733 fprintf(f, "DOMAINNAME=%s\n", string);
734
735 r = sd_dhcp_lease_get_hostname(lease, &string);
736 if (r >= 0)
737 fprintf(f, "HOSTNAME=%s\n", string);
738
ce78df79
TG
739 r = sd_dhcp_lease_get_root_path(lease, &string);
740 if (r >= 0)
741 fprintf(f, "ROOT_PATH=%s\n", string);
742
a2ba62c7 743 r = sd_dhcp_lease_get_routes(lease, &routes);
e1ea665e 744 if (r >= 0)
a2ba62c7 745 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 746
8eb9058d
LP
747 r = sd_dhcp_lease_get_timezone(lease, &string);
748 if (r >= 0)
749 fprintf(f, "TIMEZONE=%s\n", string);
750
e37f74a6
DW
751 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
752 if (r >= 0) {
753 _cleanup_free_ char *client_id_hex;
754
dde8bb32 755 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
756 if (!client_id_hex) {
757 r = -ENOMEM;
dacd6cee 758 goto fail;
e37f74a6
DW
759 }
760 fprintf(f, "CLIENTID=%s\n", client_id_hex);
761 }
762
e43a8393
BG
763 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
764 if (r >= 0) {
765 _cleanup_free_ char *option_hex = NULL;
766
767 option_hex = hexmem(data, data_len);
768 if (!option_hex) {
769 r = -ENOMEM;
dacd6cee 770 goto fail;
e43a8393
BG
771 }
772 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
773 }
774
a073309f 775 LIST_FOREACH(options, option, lease->private_options) {
30afef87 776 char key[strlen("OPTION_000")+1];
a073309f
AC
777 snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
778 r = serialize_dhcp_option(f, key, option->data, option->length);
779 if (r < 0)
780 goto fail;
781 }
782
dacd6cee
LP
783 r = fflush_and_check(f);
784 if (r < 0)
785 goto fail;
fe8db0c5 786
dacd6cee 787 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 788 r = -errno;
dacd6cee 789 goto fail;
fe8db0c5
TG
790 }
791
dacd6cee
LP
792 return 0;
793
794fail:
795 if (temp_path)
796 (void) unlink(temp_path);
fe8db0c5 797
dacd6cee 798 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
799}
800
bd91b83e 801int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
fe8db0c5
TG
802 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
803 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
8e34a618 804 *server_address = NULL, *next_server = NULL,
e37f74a6 805 *dns = NULL, *ntp = NULL, *mtu = NULL,
e43a8393 806 *routes = NULL, *client_id_hex = NULL,
a073309f
AC
807 *vendor_specific_hex = NULL,
808 *options[DHCP_OPTION_PRIVATE_LAST -
809 DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
fe8db0c5 810 struct in_addr addr;
a073309f 811 int r, i;
fe8db0c5
TG
812
813 assert(lease_file);
814 assert(ret);
815
816 r = dhcp_lease_new(&lease);
817 if (r < 0)
818 return r;
819
820 r = parse_env_file(lease_file, NEWLINE,
821 "ADDRESS", &address,
822 "ROUTER", &router,
823 "NETMASK", &netmask,
0ad853bc 824 "SERVER_IDENTIFIER", &server_address,
8e34a618 825 "NEXT_SERVER", &next_server,
109731eb
TG
826 "DNS", &dns,
827 "NTP", &ntp,
fe8db0c5
TG
828 "MTU", &mtu,
829 "DOMAINNAME", &lease->domainname,
830 "HOSTNAME", &lease->hostname,
ce78df79 831 "ROOT_PATH", &lease->root_path,
e1ea665e 832 "ROUTES", &routes,
e37f74a6 833 "CLIENTID", &client_id_hex,
8eb9058d 834 "TIMEZONE", &lease->timezone,
e43a8393 835 "VENDOR_SPECIFIC", &vendor_specific_hex,
a073309f
AC
836 "OPTION_224", &options[0],
837 "OPTION_225", &options[1],
838 "OPTION_226", &options[2],
839 "OPTION_227", &options[3],
840 "OPTION_228", &options[4],
841 "OPTION_229", &options[5],
842 "OPTION_230", &options[6],
843 "OPTION_231", &options[7],
844 "OPTION_232", &options[8],
845 "OPTION_233", &options[9],
846 "OPTION_234", &options[10],
847 "OPTION_235", &options[11],
848 "OPTION_236", &options[12],
849 "OPTION_237", &options[13],
850 "OPTION_238", &options[14],
851 "OPTION_239", &options[15],
852 "OPTION_240", &options[16],
853 "OPTION_241", &options[17],
854 "OPTION_242", &options[18],
855 "OPTION_243", &options[19],
856 "OPTION_244", &options[20],
857 "OPTION_245", &options[21],
858 "OPTION_246", &options[22],
859 "OPTION_247", &options[23],
860 "OPTION_248", &options[24],
861 "OPTION_249", &options[25],
862 "OPTION_250", &options[26],
863 "OPTION_251", &options[27],
864 "OPTION_252", &options[28],
865 "OPTION_253", &options[29],
866 "OPTION_254", &options[30],
fe8db0c5
TG
867 NULL);
868 if (r < 0) {
869 if (r == -ENOENT)
870 return 0;
871
23bbb0de 872 return log_error_errno(r, "Failed to read %s: %m", lease_file);
fe8db0c5
TG
873 }
874
875 r = inet_pton(AF_INET, address, &addr);
876 if (r < 0)
877 return r;
878
879 lease->address = addr.s_addr;
880
8ddbeaa2
UTL
881 if (router) {
882 r = inet_pton(AF_INET, router, &addr);
883 if (r < 0)
884 return r;
fe8db0c5 885
8ddbeaa2
UTL
886 lease->router = addr.s_addr;
887 }
fe8db0c5
TG
888
889 r = inet_pton(AF_INET, netmask, &addr);
890 if (r < 0)
891 return r;
892
893 lease->subnet_mask = addr.s_addr;
894
0ad853bc
TG
895 if (server_address) {
896 r = inet_pton(AF_INET, server_address, &addr);
897 if (r < 0)
898 return r;
899
900 lease->server_address = addr.s_addr;
901 }
902
8e34a618
TG
903 if (next_server) {
904 r = inet_pton(AF_INET, next_server, &addr);
905 if (r < 0)
906 return r;
907
908 lease->next_server = addr.s_addr;
909 }
910
109731eb 911 if (dns) {
a2ba62c7 912 r = deserialize_in_addrs(&lease->dns, dns);
109731eb
TG
913 if (r < 0)
914 return r;
a2ba62c7
LP
915
916 lease->dns_size = r;
109731eb
TG
917 }
918
919 if (ntp) {
a2ba62c7 920 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb
TG
921 if (r < 0)
922 return r;
a2ba62c7
LP
923
924 lease->ntp_size = r;
109731eb
TG
925 }
926
fe8db0c5
TG
927 if (mtu) {
928 uint16_t u;
929 if (sscanf(mtu, "%" SCNu16, &u) > 0)
930 lease->mtu = u;
931 }
932
e1ea665e
EY
933 if (routes) {
934 r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
935 &lease->static_route_allocated, routes);
936 if (r < 0)
937 return r;
938 }
939
e37f74a6 940 if (client_id_hex) {
9b57d9ae 941 r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
30494563
TG
942 if (r < 0)
943 return r;
e37f74a6
DW
944 }
945
e43a8393 946 if (vendor_specific_hex) {
9b57d9ae 947 r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
e43a8393
BG
948 if (r < 0)
949 return r;
950 }
951
a073309f 952 for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
626be147 953 _cleanup_free_ uint8_t *data = NULL;
a073309f
AC
954 size_t len;
955
956 if (!options[i])
957 continue;
958
959 r = deserialize_dhcp_option(&data, &len, options[i]);
960 if (r < 0)
961 return r;
962
963 r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 964 if (r < 0)
a073309f
AC
965 return r;
966 }
967
fe8db0c5
TG
968 *ret = lease;
969 lease = NULL;
970
971 return 0;
972}
9e64dd72
TG
973
974int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
df40eee8
TG
975 struct in_addr address;
976 struct in_addr mask;
977 int r;
9e64dd72
TG
978
979 assert(lease);
9e64dd72 980
df40eee8 981 address.s_addr = lease->address;
9e64dd72
TG
982
983 /* fall back to the default subnet masks based on address class */
df40eee8
TG
984 r = in_addr_default_subnet_mask(&address, &mask);
985 if (r < 0)
986 return r;
9e64dd72 987
df40eee8 988 lease->subnet_mask = mask.s_addr;
9e64dd72
TG
989
990 return 0;
991}
e37f74a6
DW
992
993int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
994 size_t *client_id_len) {
995 assert_return(lease, -EINVAL);
996 assert_return(client_id, -EINVAL);
997 assert_return(client_id_len, -EINVAL);
998
999 *client_id = lease->client_id;
1000 *client_id_len = lease->client_id_len;
1001 return 0;
1002}
1003
1004int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
1005 size_t client_id_len) {
1006 assert_return(lease, -EINVAL);
1007 assert_return((!client_id && !client_id_len) ||
1008 (client_id && client_id_len), -EINVAL);
1009
1010 free (lease->client_id);
1011 lease->client_id = NULL;
1012 lease->client_id_len = 0;
1013
1014 if (client_id) {
1015 lease->client_id = memdup (client_id, client_id_len);
1016 lease->client_id_len = client_id_len;
1017 }
1018
1019 return 0;
1020}
8eb9058d
LP
1021
1022int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone) {
1023 assert_return(lease, -EINVAL);
1024 assert_return(timezone, -EINVAL);
1025
1026 if (!lease->timezone)
1027 return -ENXIO;
1028
1029 *timezone = lease->timezone;
1030 return 0;
1031}