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