]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
Merge pull request #914 from reverendhomer/patch-2
[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
TG
197sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
198 if (lease)
199 assert_se(REFCNT_INC(lease->n_ref) >= 2);
200
201 return lease;
202}
203
204sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
f0c4b1c3 205 if (lease && REFCNT_DEC(lease->n_ref) == 0) {
7e753d9d
AC
206 while (lease->private_options) {
207 struct sd_dhcp_raw_option *option = lease->private_options;
208
209 LIST_REMOVE(options, lease->private_options, option);
210
211 free(option->data);
212 free(option);
213 }
a6cc569e
TG
214 free(lease->hostname);
215 free(lease->domainname);
216 free(lease->dns);
81d98a39 217 free(lease->ntp);
e1ea665e 218 free(lease->static_route);
e37f74a6 219 free(lease->client_id);
e43a8393 220 free(lease->vendor_specific);
a6cc569e
TG
221 free(lease);
222 }
223
224 return NULL;
225}
226
e140ae58 227static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
e140ae58
TG
228 assert(option);
229 assert(ret);
230
231 if (len == 4) {
bab47929 232 *ret = unaligned_read_be32((be32_t*) option);
e140ae58
TG
233
234 if (*ret < min)
235 *ret = min;
236 }
237}
238
f5c0c00f
TG
239static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
240 lease_parse_u32(option, len, (uint32_t *)ret, 0);
241}
242
e140ae58 243static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
244 assert(option);
245 assert(ret);
246
247 if (len == 2) {
bab47929 248 *ret = unaligned_read_be16((be16_t*) option);
e140ae58
TG
249
250 if (*ret < min)
251 *ret = min;
252 }
253}
254
255static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
256 assert(option);
257 assert(ret);
258
259 if (len == 4)
260 memcpy(ret, option, 4);
261}
262
f5c0c00f
TG
263static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
264 assert(option);
265 assert(ret);
266
267 if (len == 1)
268 *ret = !!(*option);
269}
270
271static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
272 assert(option);
273 assert(ret);
274
275 if (len == 1) {
276 *ret = *option;
277
278 if (*ret < min)
279 *ret = min;
280 }
281}
282
e140ae58
TG
283static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
284 assert(option);
285 assert(ret);
286
287 if (len >= 1) {
288 char *string;
289
290 string = strndup((const char *)option, len);
291 if (!string)
292 return -errno;
293
294 free(*ret);
295 *ret = string;
296 }
297
298 return 0;
299}
300
f5c0c00f 301static 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
302 assert(option);
303 assert(ret);
304 assert(ret_size);
305
f5c0c00f 306 if (len && !(len % (4 * mult))) {
e140ae58
TG
307 size_t size;
308 struct in_addr *addresses;
309
310 size = len / 4;
311
312 addresses = newdup(struct in_addr, option, size);
313 if (!addresses)
314 return -ENOMEM;
315
316 free(*ret);
317 *ret = addresses;
318 *ret_size = size;
319 }
320
321 return 0;
322}
323
f5c0c00f
TG
324static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
325 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
326}
327
328static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
329 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
330}
331
e1ea665e
EY
332static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
333 size_t *routes_size, size_t *routes_allocated) {
334
335 struct in_addr addr;
336
337 assert(option);
338 assert(routes);
339 assert(routes_size);
340 assert(routes_allocated);
341
342 if (!len)
343 return 0;
344
345 if (len % 8 != 0)
346 return -EINVAL;
347
348 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
349 return -ENOMEM;
350
351 while (len >= 8) {
352 struct sd_dhcp_route *route = *routes + *routes_size;
1caa12d0 353 int r;
e1ea665e 354
1caa12d0
TG
355 r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
356 if (r < 0) {
e1ea665e
EY
357 log_error("Failed to determine destination prefix length from class based IP, ignoring");
358 continue;
359 }
360
361 lease_parse_be32(option, 4, &addr.s_addr);
362 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
363 option += 4;
364
365 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
366 option += 4;
367
368 len -= 8;
369 (*routes_size)++;
370 }
371
372 return 0;
373}
374
375/* parses RFC3442 Classless Static Route Option */
376static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
377 size_t *routes_size, size_t *routes_allocated) {
378
379 assert(option);
380 assert(routes);
381 assert(routes_size);
382 assert(routes_allocated);
383
384 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
385
386 while (len > 0) {
387 uint8_t dst_octets;
388 struct sd_dhcp_route *route;
389
390 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
391 return -ENOMEM;
392
393 route = *routes + *routes_size;
394
395 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
396 route->dst_prefixlen = *option;
397 option++;
398 len--;
399
400 /* can't have more than 4 octets in IPv4 */
401 if (dst_octets > 4 || len < dst_octets)
402 return -EINVAL;
403
404 route->dst_addr.s_addr = 0;
405 memcpy(&route->dst_addr.s_addr, option, dst_octets);
406 option += dst_octets;
407 len -= dst_octets;
408
409 if (len < 4)
410 return -EINVAL;
411
412 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
413 option += 4;
414 len -= 4;
415
416 (*routes_size)++;
417 }
418
419 return 0;
420}
421
a6cc569e
TG
422int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
423 void *user_data) {
424 sd_dhcp_lease *lease = user_data;
e140ae58
TG
425 int r;
426
427 assert(lease);
a6cc569e
TG
428
429 switch(code) {
430
f5c0c00f
TG
431 case DHCP_OPTION_TIME_OFFSET:
432 lease_parse_s32(option, len, &lease->time_offset);
433
434 break;
435
436 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
437 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
438
439 break;
440
a6cc569e 441 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
e140ae58 442 lease_parse_u32(option, len, &lease->lifetime, 1);
a6cc569e
TG
443
444 break;
445
446 case DHCP_OPTION_SERVER_IDENTIFIER:
e140ae58 447 lease_parse_be32(option, len, &lease->server_address);
a6cc569e
TG
448
449 break;
450
451 case DHCP_OPTION_SUBNET_MASK:
e140ae58 452 lease_parse_be32(option, len, &lease->subnet_mask);
a6cc569e
TG
453
454 break;
455
f5c0c00f
TG
456 case DHCP_OPTION_BROADCAST:
457 lease_parse_be32(option, len, &lease->broadcast);
458
459 break;
460
a6cc569e 461 case DHCP_OPTION_ROUTER:
a0518527
NO
462 if(len >= 4)
463 lease_parse_be32(option, 4, &lease->router);
a6cc569e
TG
464
465 break;
466
467 case DHCP_OPTION_DOMAIN_NAME_SERVER:
e140ae58
TG
468 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
469 if (r < 0)
470 return r;
46844696
TG
471
472 break;
473
474 case DHCP_OPTION_NTP_SERVER:
e140ae58
TG
475 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
476 if (r < 0)
477 return r;
a6cc569e
TG
478
479 break;
480
f5c0c00f
TG
481 case DHCP_OPTION_POLICY_FILTER:
482 r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
483 if (r < 0)
484 return r;
485
486 break;
487
488 case DHCP_OPTION_STATIC_ROUTE:
e1ea665e
EY
489 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
490 &lease->static_route_allocated);
f5c0c00f
TG
491 if (r < 0)
492 return r;
493
494 break;
495
a6cc569e 496 case DHCP_OPTION_INTERFACE_MTU:
e140ae58 497 lease_parse_u16(option, len, &lease->mtu, 68);
a6cc569e
TG
498
499 break;
500
f5c0c00f
TG
501 case DHCP_OPTION_INTERFACE_MDR:
502 lease_parse_u16(option, len, &lease->mdr, 576);
503
504 break;
505
506 case DHCP_OPTION_INTERFACE_TTL:
507 lease_parse_u8(option, len, &lease->ttl, 1);
508
509 break;
510
511 case DHCP_OPTION_BOOT_FILE_SIZE:
512 lease_parse_u16(option, len, &lease->boot_file_size, 0);
513
514 break;
515
a6cc569e 516 case DHCP_OPTION_DOMAIN_NAME:
784d9b9c
TG
517 {
518 _cleanup_free_ char *domainname = NULL;
f50f01f4 519 char *e;
784d9b9c
TG
520
521 r = lease_parse_string(option, len, &domainname);
e140ae58
TG
522 if (r < 0)
523 return r;
a6cc569e 524
f50f01f4
LP
525 /* Chop off trailing dot of domain name that some DHCP
526 * servers send us back. Internally we want to store
527 * host names without trailing dots and
528 * host_name_is_valid() doesn't accept them. */
529 e = endswith(domainname, ".");
530 if (e)
531 *e = 0;
532
37de2509 533 if (is_localhost(domainname))
784d9b9c
TG
534 break;
535
37de2509
NO
536 r = dns_name_is_valid(domainname);
537 if (r <= 0) {
538 if (r < 0)
539 log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
540 if (r == 0)
541 log_warning("Domain name is not valid, ignoring: %s", domainname);
542 break;
543 }
544
784d9b9c
TG
545 free(lease->domainname);
546 lease->domainname = domainname;
547 domainname = NULL;
a6cc569e 548
784d9b9c
TG
549 break;
550 }
a6cc569e 551 case DHCP_OPTION_HOST_NAME:
784d9b9c
TG
552 {
553 _cleanup_free_ char *hostname = NULL;
f50f01f4 554 char *e;
784d9b9c
TG
555
556 r = lease_parse_string(option, len, &hostname);
e140ae58
TG
557 if (r < 0)
558 return r;
a6cc569e 559
f50f01f4
LP
560 e = endswith(hostname, ".");
561 if (e)
562 *e = 0;
563
8fb49443 564 if (!hostname_is_valid(hostname, false) || is_localhost(hostname))
784d9b9c
TG
565 break;
566
8fb49443 567 free_and_replace(&lease->hostname, hostname);
784d9b9c 568 hostname = NULL;
a6cc569e 569
784d9b9c
TG
570 break;
571 }
ce78df79 572 case DHCP_OPTION_ROOT_PATH:
e140ae58
TG
573 r = lease_parse_string(option, len, &lease->root_path);
574 if (r < 0)
575 return r;
ce78df79
TG
576
577 break;
578
a6cc569e 579 case DHCP_OPTION_RENEWAL_T1_TIME:
e140ae58 580 lease_parse_u32(option, len, &lease->t1, 1);
a6cc569e
TG
581
582 break;
583
584 case DHCP_OPTION_REBINDING_T2_TIME:
e140ae58 585 lease_parse_u32(option, len, &lease->t2, 1);
a6cc569e 586
f5c0c00f
TG
587 break;
588
589 case DHCP_OPTION_ENABLE_IP_FORWARDING:
590 lease_parse_bool(option, len, &lease->ip_forward);
591
592 break;
593
594 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
595 lease_parse_bool(option, len, &lease->ip_forward_non_local);
596
a6cc569e 597 break;
e1ea665e
EY
598
599 case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
600 r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
601 &lease->static_route_allocated);
602 if (r < 0)
603 return r;
604
605 break;
e43a8393
BG
606
607 case DHCP_OPTION_VENDOR_SPECIFIC:
608 if (len >= 1) {
609 free(lease->vendor_specific);
610 lease->vendor_specific = memdup(option, len);
611 if (!lease->vendor_specific)
612 return -ENOMEM;
613 lease->vendor_specific_len = len;
614 }
615
616 break;
7e753d9d
AC
617
618 default:
619 if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
620 break;
621
622 r = dhcp_lease_insert_private_option(lease, code, option, len);
623 if (r < 0)
624 return r;
a6cc569e
TG
625 }
626
627 return 0;
628}
629
7e753d9d
AC
630int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
631 const uint8_t *data, uint8_t len) {
632 struct sd_dhcp_raw_option *cur, *option;
633
634 LIST_FOREACH(options, cur, lease->private_options) {
635 if (tag < cur->tag)
636 break;
637 else if (tag == cur->tag) {
638 log_error("Ignoring duplicate option, tagged %d.", tag);
639 return 0;
640 }
641 }
642
643 option = new(struct sd_dhcp_raw_option, 1);
644 if (!option)
645 return -ENOMEM;
646
647 option->tag = tag;
648 option->length = len;
649 option->data = memdup(data, len);
650 if (!option->data) {
651 free(option);
652 return -ENOMEM;
653 }
654
655 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
656
657 return 0;
658}
659
a6cc569e 660int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 661 sd_dhcp_lease *lease;
a6cc569e
TG
662
663 lease = new0(sd_dhcp_lease, 1);
664 if (!lease)
665 return -ENOMEM;
666
8ddbeaa2 667 lease->router = INADDR_ANY;
a6cc569e 668 lease->n_ref = REFCNT_INIT;
7e753d9d 669 LIST_HEAD_INIT(lease->private_options);
a6cc569e
TG
670
671 *ret = lease;
a6cc569e
TG
672 return 0;
673}
fe8db0c5 674
1dc24d5f 675int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
676 _cleanup_free_ char *temp_path = NULL;
677 _cleanup_fclose_ FILE *f = NULL;
a073309f 678 struct sd_dhcp_raw_option *option;
fe8db0c5 679 struct in_addr address;
a2ba62c7 680 const struct in_addr *addresses;
e43a8393
BG
681 const uint8_t *client_id, *data;
682 size_t client_id_len, data_len;
fe8db0c5
TG
683 const char *string;
684 uint16_t mtu;
e1ea665e 685 struct sd_dhcp_route *routes;
fe8db0c5
TG
686 int r;
687
688 assert(lease);
689 assert(lease_file);
690
fe8db0c5
TG
691 r = fopen_temporary(lease_file, &f, &temp_path);
692 if (r < 0)
dacd6cee 693 goto fail;
fe8db0c5
TG
694
695 fchmod(fileno(f), 0644);
696
697 r = sd_dhcp_lease_get_address(lease, &address);
698 if (r < 0)
dacd6cee 699 goto fail;
fe8db0c5 700
fe8db0c5
TG
701 fprintf(f,
702 "# This is private data. Do not parse.\n"
109731eb 703 "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 704
fe8db0c5
TG
705 r = sd_dhcp_lease_get_netmask(lease, &address);
706 if (r < 0)
dacd6cee 707 goto fail;
fe8db0c5 708
109731eb 709 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 710
8ddbeaa2
UTL
711 r = sd_dhcp_lease_get_router(lease, &address);
712 if (r >= 0)
713 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
714
0ad853bc 715 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb
TG
716 if (r >= 0)
717 fprintf(f, "SERVER_ADDRESS=%s\n",
718 inet_ntoa(address));
0ad853bc 719
8e34a618 720 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
721 if (r >= 0)
722 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 723
fe8db0c5
TG
724 r = sd_dhcp_lease_get_mtu(lease, &mtu);
725 if (r >= 0)
726 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
727
b0e39c82 728 fputs("DNS=", f);
a2ba62c7 729 r = sd_dhcp_lease_get_dns(lease, &addresses);
109731eb 730 if (r >= 0)
b0e39c82
TG
731 serialize_in_addrs(f, addresses, r);
732 fputs("\n", f);
109731eb 733
b0e39c82 734 fputs("NTP=", f);
a2ba62c7 735 r = sd_dhcp_lease_get_ntp(lease, &addresses);
109731eb 736 if (r >= 0)
b0e39c82
TG
737 serialize_in_addrs(f, addresses, r);
738 fputs("\n", f);
fe8db0c5
TG
739
740 r = sd_dhcp_lease_get_domainname(lease, &string);
741 if (r >= 0)
742 fprintf(f, "DOMAINNAME=%s\n", string);
743
744 r = sd_dhcp_lease_get_hostname(lease, &string);
745 if (r >= 0)
746 fprintf(f, "HOSTNAME=%s\n", string);
747
ce78df79
TG
748 r = sd_dhcp_lease_get_root_path(lease, &string);
749 if (r >= 0)
750 fprintf(f, "ROOT_PATH=%s\n", string);
751
a2ba62c7 752 r = sd_dhcp_lease_get_routes(lease, &routes);
e1ea665e 753 if (r >= 0)
a2ba62c7 754 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 755
e37f74a6
DW
756 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
757 if (r >= 0) {
758 _cleanup_free_ char *client_id_hex;
759
dde8bb32 760 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
761 if (!client_id_hex) {
762 r = -ENOMEM;
dacd6cee 763 goto fail;
e37f74a6
DW
764 }
765 fprintf(f, "CLIENTID=%s\n", client_id_hex);
766 }
767
e43a8393
BG
768 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
769 if (r >= 0) {
770 _cleanup_free_ char *option_hex = NULL;
771
772 option_hex = hexmem(data, data_len);
773 if (!option_hex) {
774 r = -ENOMEM;
dacd6cee 775 goto fail;
e43a8393
BG
776 }
777 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
778 }
779
a073309f 780 LIST_FOREACH(options, option, lease->private_options) {
30afef87 781 char key[strlen("OPTION_000")+1];
a073309f
AC
782 snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
783 r = serialize_dhcp_option(f, key, option->data, option->length);
784 if (r < 0)
785 goto fail;
786 }
787
dacd6cee
LP
788 r = fflush_and_check(f);
789 if (r < 0)
790 goto fail;
fe8db0c5 791
dacd6cee 792 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 793 r = -errno;
dacd6cee 794 goto fail;
fe8db0c5
TG
795 }
796
dacd6cee
LP
797 return 0;
798
799fail:
800 if (temp_path)
801 (void) unlink(temp_path);
fe8db0c5 802
dacd6cee 803 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
804}
805
1dc24d5f 806int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
fe8db0c5
TG
807 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
808 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
8e34a618 809 *server_address = NULL, *next_server = NULL,
e37f74a6 810 *dns = NULL, *ntp = NULL, *mtu = NULL,
e43a8393 811 *routes = NULL, *client_id_hex = NULL,
a073309f
AC
812 *vendor_specific_hex = NULL,
813 *options[DHCP_OPTION_PRIVATE_LAST -
814 DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
fe8db0c5 815 struct in_addr addr;
a073309f 816 int r, i;
fe8db0c5
TG
817
818 assert(lease_file);
819 assert(ret);
820
821 r = dhcp_lease_new(&lease);
822 if (r < 0)
823 return r;
824
825 r = parse_env_file(lease_file, NEWLINE,
826 "ADDRESS", &address,
827 "ROUTER", &router,
828 "NETMASK", &netmask,
0ad853bc 829 "SERVER_IDENTIFIER", &server_address,
8e34a618 830 "NEXT_SERVER", &next_server,
109731eb
TG
831 "DNS", &dns,
832 "NTP", &ntp,
fe8db0c5
TG
833 "MTU", &mtu,
834 "DOMAINNAME", &lease->domainname,
835 "HOSTNAME", &lease->hostname,
ce78df79 836 "ROOT_PATH", &lease->root_path,
e1ea665e 837 "ROUTES", &routes,
e37f74a6 838 "CLIENTID", &client_id_hex,
e43a8393 839 "VENDOR_SPECIFIC", &vendor_specific_hex,
a073309f
AC
840 "OPTION_224", &options[0],
841 "OPTION_225", &options[1],
842 "OPTION_226", &options[2],
843 "OPTION_227", &options[3],
844 "OPTION_228", &options[4],
845 "OPTION_229", &options[5],
846 "OPTION_230", &options[6],
847 "OPTION_231", &options[7],
848 "OPTION_232", &options[8],
849 "OPTION_233", &options[9],
850 "OPTION_234", &options[10],
851 "OPTION_235", &options[11],
852 "OPTION_236", &options[12],
853 "OPTION_237", &options[13],
854 "OPTION_238", &options[14],
855 "OPTION_239", &options[15],
856 "OPTION_240", &options[16],
857 "OPTION_241", &options[17],
858 "OPTION_242", &options[18],
859 "OPTION_243", &options[19],
860 "OPTION_244", &options[20],
861 "OPTION_245", &options[21],
862 "OPTION_246", &options[22],
863 "OPTION_247", &options[23],
864 "OPTION_248", &options[24],
865 "OPTION_249", &options[25],
866 "OPTION_250", &options[26],
867 "OPTION_251", &options[27],
868 "OPTION_252", &options[28],
869 "OPTION_253", &options[29],
870 "OPTION_254", &options[30],
fe8db0c5
TG
871 NULL);
872 if (r < 0) {
873 if (r == -ENOENT)
874 return 0;
875
23bbb0de 876 return log_error_errno(r, "Failed to read %s: %m", lease_file);
fe8db0c5
TG
877 }
878
879 r = inet_pton(AF_INET, address, &addr);
880 if (r < 0)
881 return r;
882
883 lease->address = addr.s_addr;
884
8ddbeaa2
UTL
885 if (router) {
886 r = inet_pton(AF_INET, router, &addr);
887 if (r < 0)
888 return r;
fe8db0c5 889
8ddbeaa2
UTL
890 lease->router = addr.s_addr;
891 }
fe8db0c5
TG
892
893 r = inet_pton(AF_INET, netmask, &addr);
894 if (r < 0)
895 return r;
896
897 lease->subnet_mask = addr.s_addr;
898
0ad853bc
TG
899 if (server_address) {
900 r = inet_pton(AF_INET, server_address, &addr);
901 if (r < 0)
902 return r;
903
904 lease->server_address = addr.s_addr;
905 }
906
8e34a618
TG
907 if (next_server) {
908 r = inet_pton(AF_INET, next_server, &addr);
909 if (r < 0)
910 return r;
911
912 lease->next_server = addr.s_addr;
913 }
914
109731eb 915 if (dns) {
a2ba62c7 916 r = deserialize_in_addrs(&lease->dns, dns);
109731eb
TG
917 if (r < 0)
918 return r;
a2ba62c7
LP
919
920 lease->dns_size = r;
109731eb
TG
921 }
922
923 if (ntp) {
a2ba62c7 924 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb
TG
925 if (r < 0)
926 return r;
a2ba62c7
LP
927
928 lease->ntp_size = r;
109731eb
TG
929 }
930
fe8db0c5
TG
931 if (mtu) {
932 uint16_t u;
933 if (sscanf(mtu, "%" SCNu16, &u) > 0)
934 lease->mtu = u;
935 }
936
e1ea665e
EY
937 if (routes) {
938 r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
939 &lease->static_route_allocated, routes);
940 if (r < 0)
941 return r;
942 }
943
e37f74a6 944 if (client_id_hex) {
9b57d9ae 945 r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
30494563
TG
946 if (r < 0)
947 return r;
e37f74a6
DW
948 }
949
e43a8393 950 if (vendor_specific_hex) {
9b57d9ae 951 r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
e43a8393
BG
952 if (r < 0)
953 return r;
954 }
955
a073309f
AC
956 for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
957 uint8_t *data;
958 size_t len;
959
960 if (!options[i])
961 continue;
962
963 r = deserialize_dhcp_option(&data, &len, options[i]);
964 if (r < 0)
965 return r;
966
967 r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
92d0218e
DM
968 if (r < 0) {
969 free(data);
a073309f 970 return r;
92d0218e 971 }
a073309f
AC
972 }
973
fe8db0c5
TG
974 *ret = lease;
975 lease = NULL;
976
977 return 0;
978}
9e64dd72
TG
979
980int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
df40eee8
TG
981 struct in_addr address;
982 struct in_addr mask;
983 int r;
9e64dd72
TG
984
985 assert(lease);
9e64dd72 986
df40eee8 987 address.s_addr = lease->address;
9e64dd72
TG
988
989 /* fall back to the default subnet masks based on address class */
df40eee8
TG
990 r = in_addr_default_subnet_mask(&address, &mask);
991 if (r < 0)
992 return r;
9e64dd72 993
df40eee8 994 lease->subnet_mask = mask.s_addr;
9e64dd72
TG
995
996 return 0;
997}
e37f74a6
DW
998
999int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
1000 size_t *client_id_len) {
1001 assert_return(lease, -EINVAL);
1002 assert_return(client_id, -EINVAL);
1003 assert_return(client_id_len, -EINVAL);
1004
1005 *client_id = lease->client_id;
1006 *client_id_len = lease->client_id_len;
1007 return 0;
1008}
1009
1010int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
1011 size_t client_id_len) {
1012 assert_return(lease, -EINVAL);
1013 assert_return((!client_id && !client_id_len) ||
1014 (client_id && client_id_len), -EINVAL);
1015
1016 free (lease->client_id);
1017 lease->client_id = NULL;
1018 lease->client_id_len = 0;
1019
1020 if (client_id) {
1021 lease->client_id = memdup (client_id, client_id_len);
1022 lease->client_id_len = client_id_len;
1023 }
1024
1025 return 0;
1026}