]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
dhcp: make DHCP_OPTION_* enum public
[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
07630cea 21#include <arpa/inet.h>
a6cc569e 22#include <errno.h>
a6cc569e 23#include <stdio.h>
07630cea
LP
24#include <stdlib.h>
25#include <string.h>
26
27#include "sd-dhcp-lease.h"
a6cc569e 28
b5efdb8a 29#include "alloc-util.h"
07630cea
LP
30#include "dhcp-lease-internal.h"
31#include "dhcp-protocol.h"
32#include "dns-domain.h"
3ffd4af2 33#include "fd-util.h"
fe8db0c5 34#include "fileio.h"
b11d6a7b 35#include "hexdecoct.h"
958b66ea 36#include "hostname-util.h"
07630cea 37#include "in-addr-util.h"
0339cd77 38#include "network-internal.h"
6bedfcbb 39#include "parse-util.h"
d054f0a4 40#include "stdio-util.h"
b11d6a7b 41#include "string-util.h"
07630cea 42#include "unaligned.h"
a6cc569e
TG
43
44int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
45 assert_return(lease, -EINVAL);
46 assert_return(addr, -EINVAL);
47
0339cd77
LP
48 if (lease->address == 0)
49 return -ENODATA;
50
a6cc569e 51 addr->s_addr = lease->address;
0339cd77
LP
52 return 0;
53}
a6cc569e 54
0339cd77
LP
55int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
56 assert_return(lease, -EINVAL);
57 assert_return(addr, -EINVAL);
58
59 if (!lease->have_broadcast)
60 return -ENODATA;
61
62 addr->s_addr = lease->broadcast;
a6cc569e
TG
63 return 0;
64}
65
68ceb9df
PF
66int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
67 assert_return(lease, -EINVAL);
1c6eb4e3 68 assert_return(lifetime, -EINVAL);
68ceb9df 69
0339cd77
LP
70 if (lease->lifetime <= 0)
71 return -ENODATA;
72
68ceb9df 73 *lifetime = lease->lifetime;
0339cd77
LP
74 return 0;
75}
68ceb9df 76
0339cd77
LP
77int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
78 assert_return(lease, -EINVAL);
79 assert_return(t1, -EINVAL);
80
81 if (lease->t1 <= 0)
82 return -ENODATA;
83
84 *t1 = lease->t1;
85 return 0;
86}
87
88int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
89 assert_return(lease, -EINVAL);
90 assert_return(t2, -EINVAL);
91
92 if (lease->t2 <= 0)
93 return -ENODATA;
94
95 *t2 = lease->t2;
68ceb9df
PF
96 return 0;
97}
98
a6cc569e
TG
99int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
100 assert_return(lease, -EINVAL);
101 assert_return(mtu, -EINVAL);
102
0339cd77
LP
103 if (lease->mtu <= 0)
104 return -ENODATA;
a6cc569e 105
0339cd77 106 *mtu = lease->mtu;
a6cc569e
TG
107 return 0;
108}
109
a2ba62c7 110int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
a6cc569e
TG
111 assert_return(lease, -EINVAL);
112 assert_return(addr, -EINVAL);
a6cc569e 113
0339cd77
LP
114 if (lease->dns_size <= 0)
115 return -ENODATA;
a6cc569e 116
0339cd77
LP
117 *addr = lease->dns;
118 return (int) lease->dns_size;
a6cc569e
TG
119}
120
a2ba62c7 121int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
46844696
TG
122 assert_return(lease, -EINVAL);
123 assert_return(addr, -EINVAL);
46844696 124
0339cd77
LP
125 if (lease->ntp_size <= 0)
126 return -ENODATA;
46844696 127
0339cd77
LP
128 *addr = lease->ntp;
129 return (int) lease->ntp_size;
46844696
TG
130}
131
a6cc569e
TG
132int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
133 assert_return(lease, -EINVAL);
134 assert_return(domainname, -EINVAL);
135
0339cd77
LP
136 if (!lease->domainname)
137 return -ENODATA;
a6cc569e 138
0339cd77 139 *domainname = lease->domainname;
a6cc569e
TG
140 return 0;
141}
142
143int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
144 assert_return(lease, -EINVAL);
145 assert_return(hostname, -EINVAL);
146
0339cd77
LP
147 if (!lease->hostname)
148 return -ENODATA;
a6cc569e 149
0339cd77 150 *hostname = lease->hostname;
a6cc569e
TG
151 return 0;
152}
153
ce78df79
TG
154int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
155 assert_return(lease, -EINVAL);
156 assert_return(root_path, -EINVAL);
157
0339cd77
LP
158 if (!lease->root_path)
159 return -ENODATA;
ce78df79 160
0339cd77 161 *root_path = lease->root_path;
ce78df79
TG
162 return 0;
163}
164
a6cc569e
TG
165int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
166 assert_return(lease, -EINVAL);
167 assert_return(addr, -EINVAL);
168
0339cd77
LP
169 if (lease->router == 0)
170 return -ENODATA;
a6cc569e 171
0339cd77 172 addr->s_addr = lease->router;
a6cc569e
TG
173 return 0;
174}
175
176int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
177 assert_return(lease, -EINVAL);
178 assert_return(addr, -EINVAL);
179
0339cd77
LP
180 if (!lease->have_subnet_mask)
181 return -ENODATA;
a6cc569e 182
0339cd77 183 addr->s_addr = lease->subnet_mask;
a6cc569e
TG
184 return 0;
185}
186
0ad853bc
TG
187int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
188 assert_return(lease, -EINVAL);
189 assert_return(addr, -EINVAL);
190
0339cd77
LP
191 if (lease->server_address == 0)
192 return -ENODATA;
0ad853bc 193
0339cd77 194 addr->s_addr = lease->server_address;
0ad853bc
TG
195 return 0;
196}
197
8e34a618
TG
198int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
199 assert_return(lease, -EINVAL);
200 assert_return(addr, -EINVAL);
201
0339cd77
LP
202 if (lease->next_server == 0)
203 return -ENODATA;
8e34a618 204
0339cd77 205 addr->s_addr = lease->next_server;
8e34a618
TG
206 return 0;
207}
208
f8693fc7
BG
209/*
210 * The returned routes array must be freed by the caller.
211 * Route objects have the same lifetime of the lease and must not be freed.
212 */
213int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
214 sd_dhcp_route **ret;
215 unsigned i;
216
e1ea665e
EY
217 assert_return(lease, -EINVAL);
218 assert_return(routes, -EINVAL);
e1ea665e 219
0339cd77
LP
220 if (lease->static_route_size <= 0)
221 return -ENODATA;
e1ea665e 222
f8693fc7
BG
223 ret = new(sd_dhcp_route *, lease->static_route_size);
224 if (!ret)
225 return -ENOMEM;
226
227 for (i = 0; i < lease->static_route_size; i++)
228 ret[i] = &lease->static_route[i];
229
230 *routes = ret;
0339cd77 231 return (int) lease->static_route_size;
e1ea665e
EY
232}
233
0339cd77 234int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
e43a8393
BG
235 assert_return(lease, -EINVAL);
236 assert_return(data, -EINVAL);
237 assert_return(data_len, -EINVAL);
238
0339cd77
LP
239 if (lease->vendor_specific_len <= 0)
240 return -ENODATA;
e43a8393
BG
241
242 *data = lease->vendor_specific;
243 *data_len = lease->vendor_specific_len;
e43a8393
BG
244 return 0;
245}
246
a6cc569e 247sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
3733eec3
LP
248
249 if (!lease)
250 return NULL;
251
252 assert(lease->n_ref >= 1);
253 lease->n_ref++;
a6cc569e
TG
254
255 return lease;
256}
257
258sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
7e753d9d 259
3733eec3
LP
260 if (!lease)
261 return NULL;
7e753d9d 262
3733eec3
LP
263 assert(lease->n_ref >= 1);
264 lease->n_ref--;
265
266 if (lease->n_ref > 0)
267 return NULL;
268
269 while (lease->private_options) {
270 struct sd_dhcp_raw_option *option = lease->private_options;
271
272 LIST_REMOVE(options, lease->private_options, option);
273
274 free(option->data);
275 free(option);
a6cc569e
TG
276 }
277
3733eec3
LP
278 free(lease->hostname);
279 free(lease->domainname);
280 free(lease->dns);
281 free(lease->ntp);
282 free(lease->static_route);
283 free(lease->client_id);
284 free(lease->vendor_specific);
285 free(lease);
286
a6cc569e
TG
287 return NULL;
288}
289
0339cd77 290static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
e140ae58
TG
291 assert(option);
292 assert(ret);
293
0339cd77
LP
294 if (len != 4)
295 return -EINVAL;
e140ae58 296
0339cd77
LP
297 *ret = unaligned_read_be32((be32_t*) option);
298 if (*ret < min)
299 *ret = min;
e140ae58 300
0339cd77 301 return 0;
e140ae58
TG
302}
303
0339cd77 304static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
e140ae58
TG
305 assert(option);
306 assert(ret);
307
0339cd77
LP
308 if (len != 2)
309 return -EINVAL;
e140ae58 310
0339cd77
LP
311 *ret = unaligned_read_be16((be16_t*) option);
312 if (*ret < min)
313 *ret = min;
f5c0c00f 314
0339cd77 315 return 0;
f5c0c00f
TG
316}
317
0339cd77 318static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
f5c0c00f
TG
319 assert(option);
320 assert(ret);
321
0339cd77
LP
322 if (len != 4)
323 return -EINVAL;
f5c0c00f 324
0339cd77
LP
325 memcpy(ret, option, 4);
326 return 0;
f5c0c00f
TG
327}
328
e140ae58
TG
329static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
330 assert(option);
331 assert(ret);
332
0339cd77
LP
333 if (len <= 0)
334 *ret = mfree(*ret);
335 else {
e140ae58
TG
336 char *string;
337
e989fd9b
LP
338 /*
339 * One trailing NUL byte is OK, we don't mind. See:
340 * https://github.com/systemd/systemd/issues/1337
341 */
342 if (memchr(option, 0, len - 1))
43f447b1
LP
343 return -EINVAL;
344
e989fd9b 345 string = strndup((const char *) option, len);
e140ae58 346 if (!string)
43f447b1 347 return -ENOMEM;
e140ae58
TG
348
349 free(*ret);
350 *ret = string;
0339cd77 351 }
e140ae58
TG
352
353 return 0;
354}
355
0339cd77 356static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
e140ae58
TG
357 assert(option);
358 assert(ret);
0339cd77 359 assert(n_ret);
e140ae58 360
0339cd77
LP
361 if (len <= 0) {
362 *ret = mfree(*ret);
363 *n_ret = 0;
364 } else {
365 size_t n_addresses;
e140ae58
TG
366 struct in_addr *addresses;
367
0339cd77
LP
368 if (len % 4 != 0)
369 return -EINVAL;
e140ae58 370
0339cd77
LP
371 n_addresses = len / 4;
372
373 addresses = newdup(struct in_addr, option, n_addresses);
e140ae58
TG
374 if (!addresses)
375 return -ENOMEM;
376
377 free(*ret);
378 *ret = addresses;
0339cd77 379 *n_ret = n_addresses;
e140ae58
TG
380 }
381
382 return 0;
383}
384
0339cd77
LP
385static int lease_parse_routes(
386 const uint8_t *option, size_t len,
387 struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
e1ea665e
EY
388
389 struct in_addr addr;
390
0339cd77 391 assert(option || len <= 0);
e1ea665e
EY
392 assert(routes);
393 assert(routes_size);
394 assert(routes_allocated);
395
0339cd77 396 if (len <= 0)
e1ea665e
EY
397 return 0;
398
399 if (len % 8 != 0)
400 return -EINVAL;
401
402 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
403 return -ENOMEM;
404
405 while (len >= 8) {
406 struct sd_dhcp_route *route = *routes + *routes_size;
1caa12d0 407 int r;
e1ea665e 408
1caa12d0
TG
409 r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
410 if (r < 0) {
0339cd77 411 log_debug("Failed to determine destination prefix length from class based IP, ignoring");
e1ea665e
EY
412 continue;
413 }
414
0339cd77 415 assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
e1ea665e
EY
416 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
417 option += 4;
418
0339cd77 419 assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
e1ea665e
EY
420 option += 4;
421
422 len -= 8;
423 (*routes_size)++;
424 }
425
426 return 0;
427}
428
429/* parses RFC3442 Classless Static Route Option */
0339cd77
LP
430static int lease_parse_classless_routes(
431 const uint8_t *option, size_t len,
432 struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
e1ea665e 433
0339cd77 434 assert(option || len <= 0);
e1ea665e
EY
435 assert(routes);
436 assert(routes_size);
437 assert(routes_allocated);
438
0339cd77
LP
439 if (len <= 0)
440 return 0;
441
e1ea665e
EY
442 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
443
444 while (len > 0) {
445 uint8_t dst_octets;
446 struct sd_dhcp_route *route;
447
448 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
0339cd77 449 return -ENOMEM;
e1ea665e
EY
450
451 route = *routes + *routes_size;
452
453 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
454 route->dst_prefixlen = *option;
455 option++;
456 len--;
457
458 /* can't have more than 4 octets in IPv4 */
459 if (dst_octets > 4 || len < dst_octets)
460 return -EINVAL;
461
462 route->dst_addr.s_addr = 0;
463 memcpy(&route->dst_addr.s_addr, option, dst_octets);
464 option += dst_octets;
465 len -= dst_octets;
466
467 if (len < 4)
468 return -EINVAL;
469
470 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
471 option += 4;
472 len -= 4;
473
474 (*routes_size)++;
475 }
476
477 return 0;
478}
479
e4735228 480int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
2d03c0b8 481 sd_dhcp_lease *lease = userdata;
e140ae58
TG
482 int r;
483
484 assert(lease);
a6cc569e
TG
485
486 switch(code) {
487
22805d92 488 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
0339cd77
LP
489 r = lease_parse_u32(option, len, &lease->lifetime, 1);
490 if (r < 0)
491 log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
492
a6cc569e
TG
493 break;
494
22805d92 495 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
0339cd77
LP
496 r = lease_parse_be32(option, len, &lease->server_address);
497 if (r < 0)
498 log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
499
a6cc569e
TG
500 break;
501
22805d92 502 case SD_DHCP_OPTION_SUBNET_MASK:
0339cd77
LP
503 r = lease_parse_be32(option, len, &lease->subnet_mask);
504 if (r < 0)
505 log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
506 else
507 lease->have_subnet_mask = true;
a6cc569e
TG
508 break;
509
22805d92 510 case SD_DHCP_OPTION_BROADCAST:
0339cd77
LP
511 r = lease_parse_be32(option, len, &lease->broadcast);
512 if (r < 0)
513 log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
514 else
515 lease->have_broadcast = true;
f5c0c00f
TG
516 break;
517
22805d92 518 case SD_DHCP_OPTION_ROUTER:
0339cd77
LP
519 if (len >= 4) {
520 r = lease_parse_be32(option, 4, &lease->router);
521 if (r < 0)
522 log_debug_errno(r, "Failed to parse router address, ignoring: %m");
523 }
a6cc569e
TG
524 break;
525
22805d92 526 case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
0339cd77
LP
527 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
528 if (r < 0)
529 log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
a6cc569e
TG
530 break;
531
22805d92 532 case SD_DHCP_OPTION_NTP_SERVER:
0339cd77
LP
533 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
534 if (r < 0)
535 log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
f5c0c00f
TG
536 break;
537
22805d92 538 case SD_DHCP_OPTION_STATIC_ROUTE:
0339cd77
LP
539 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
540 if (r < 0)
541 log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
f5c0c00f
TG
542 break;
543
22805d92 544 case SD_DHCP_OPTION_INTERFACE_MTU:
0339cd77
LP
545 r = lease_parse_u16(option, len, &lease->mtu, 68);
546 if (r < 0)
547 log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
f5c0c00f
TG
548 break;
549
22805d92 550 case SD_DHCP_OPTION_DOMAIN_NAME: {
90981625 551 _cleanup_free_ char *domainname = NULL, *normalized = NULL;
784d9b9c
TG
552
553 r = lease_parse_string(option, len, &domainname);
0339cd77
LP
554 if (r < 0) {
555 log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
556 return 0;
557 }
a6cc569e 558
90981625
LP
559 r = dns_name_normalize(domainname, &normalized);
560 if (r < 0) {
561 log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
562 return 0;
563 }
784d9b9c 564
90981625 565 if (is_localhost(normalized)) {
e37d2c94 566 log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring.");
37de2509
NO
567 break;
568 }
569
784d9b9c 570 free(lease->domainname);
90981625
LP
571 lease->domainname = normalized;
572 normalized = NULL;
a6cc569e 573
784d9b9c
TG
574 break;
575 }
90981625 576
22805d92 577 case SD_DHCP_OPTION_HOST_NAME: {
90981625 578 _cleanup_free_ char *hostname = NULL, *normalized = NULL;
784d9b9c
TG
579
580 r = lease_parse_string(option, len, &hostname);
0339cd77
LP
581 if (r < 0) {
582 log_debug_errno(r, "Failed to parse host name, ignoring: %m");
583 return 0;
584 }
a6cc569e 585
90981625
LP
586 r = dns_name_normalize(hostname, &normalized);
587 if (r < 0) {
0339cd77 588 log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname);
90981625
LP
589 return 0;
590 }
f50f01f4 591
90981625 592 if (is_localhost(normalized)) {
e37d2c94 593 log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring.");
90981625
LP
594 return 0;
595 }
784d9b9c 596
90981625
LP
597 free(lease->hostname);
598 lease->hostname = normalized;
599 normalized = NULL;
a6cc569e 600
784d9b9c
TG
601 break;
602 }
ce78df79 603
22805d92 604 case SD_DHCP_OPTION_ROOT_PATH:
0339cd77
LP
605 r = lease_parse_string(option, len, &lease->root_path);
606 if (r < 0)
607 log_debug_errno(r, "Failed to parse root path, ignoring: %m");
608 break;
ce78df79 609
22805d92 610 case SD_DHCP_OPTION_RENEWAL_T1_TIME:
0339cd77
LP
611 r = lease_parse_u32(option, len, &lease->t1, 1);
612 if (r < 0)
613 log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
a6cc569e
TG
614 break;
615
22805d92 616 case SD_DHCP_OPTION_REBINDING_T2_TIME:
0339cd77
LP
617 r = lease_parse_u32(option, len, &lease->t2, 1);
618 if (r < 0)
619 log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
a6cc569e 620 break;
e1ea665e 621
22805d92 622 case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
0339cd77 623 r = lease_parse_classless_routes(
2d03c0b8
LP
624 option, len,
625 &lease->static_route,
626 &lease->static_route_size,
627 &lease->static_route_allocated);
0339cd77
LP
628 if (r < 0)
629 log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
630 break;
e43a8393 631
22805d92 632 case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
8eb9058d
LP
633 _cleanup_free_ char *tz = NULL;
634
635 r = lease_parse_string(option, len, &tz);
0339cd77
LP
636 if (r < 0) {
637 log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
638 return 0;
639 }
8eb9058d 640
0339cd77
LP
641 if (!timezone_is_valid(tz)) {
642 log_debug_errno(r, "Timezone is not valid, ignoring: %m");
643 return 0;
644 }
8eb9058d
LP
645
646 free(lease->timezone);
647 lease->timezone = tz;
648 tz = NULL;
0339cd77 649
8eb9058d
LP
650 break;
651 }
652
22805d92 653 case SD_DHCP_OPTION_VENDOR_SPECIFIC:
2d03c0b8 654
0339cd77
LP
655 if (len <= 0)
656 lease->vendor_specific = mfree(lease->vendor_specific);
657 else {
658 void *p;
659
660 p = memdup(option, len);
661 if (!p)
e43a8393 662 return -ENOMEM;
e43a8393 663
0339cd77
LP
664 free(lease->vendor_specific);
665 lease->vendor_specific = p;
666 }
7e753d9d 667
0339cd77
LP
668 lease->vendor_specific_len = len;
669 break;
7e753d9d 670
22805d92 671 case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
7e753d9d
AC
672 r = dhcp_lease_insert_private_option(lease, code, option, len);
673 if (r < 0)
674 return r;
0339cd77
LP
675
676 break;
677
678 default:
f693e9b3 679 log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
0339cd77 680 break;
a6cc569e
TG
681 }
682
683 return 0;
684}
685
0339cd77 686int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
7e753d9d
AC
687 struct sd_dhcp_raw_option *cur, *option;
688
0339cd77
LP
689 assert(lease);
690
7e753d9d
AC
691 LIST_FOREACH(options, cur, lease->private_options) {
692 if (tag < cur->tag)
693 break;
0339cd77
LP
694 if (tag == cur->tag) {
695 log_debug("Ignoring duplicate option, tagged %i.", tag);
7e753d9d
AC
696 return 0;
697 }
698 }
699
700 option = new(struct sd_dhcp_raw_option, 1);
701 if (!option)
702 return -ENOMEM;
703
704 option->tag = tag;
705 option->length = len;
706 option->data = memdup(data, len);
707 if (!option->data) {
708 free(option);
709 return -ENOMEM;
710 }
711
712 LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
7e753d9d
AC
713 return 0;
714}
715
a6cc569e 716int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 717 sd_dhcp_lease *lease;
a6cc569e
TG
718
719 lease = new0(sd_dhcp_lease, 1);
720 if (!lease)
721 return -ENOMEM;
722
8ddbeaa2 723 lease->router = INADDR_ANY;
3733eec3 724 lease->n_ref = 1;
a6cc569e
TG
725
726 *ret = lease;
a6cc569e
TG
727 return 0;
728}
fe8db0c5 729
bd91b83e 730int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fe8db0c5
TG
731 _cleanup_free_ char *temp_path = NULL;
732 _cleanup_fclose_ FILE *f = NULL;
a073309f 733 struct sd_dhcp_raw_option *option;
fe8db0c5 734 struct in_addr address;
a2ba62c7 735 const struct in_addr *addresses;
e4735228 736 const void *client_id, *data;
e43a8393 737 size_t client_id_len, data_len;
fe8db0c5
TG
738 const char *string;
739 uint16_t mtu;
f8693fc7 740 _cleanup_free_ sd_dhcp_route **routes = NULL;
0339cd77 741 uint32_t t1, t2, lifetime;
fe8db0c5
TG
742 int r;
743
744 assert(lease);
745 assert(lease_file);
746
fe8db0c5
TG
747 r = fopen_temporary(lease_file, &f, &temp_path);
748 if (r < 0)
dacd6cee 749 goto fail;
fe8db0c5
TG
750
751 fchmod(fileno(f), 0644);
752
fe8db0c5 753 fprintf(f,
0339cd77 754 "# This is private data. Do not parse.\n");
fe8db0c5 755
0339cd77
LP
756 r = sd_dhcp_lease_get_address(lease, &address);
757 if (r >= 0)
758 fprintf(f, "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 759
0339cd77
LP
760 r = sd_dhcp_lease_get_netmask(lease, &address);
761 if (r >= 0)
762 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 763
8ddbeaa2
UTL
764 r = sd_dhcp_lease_get_router(lease, &address);
765 if (r >= 0)
766 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
767
0ad853bc 768 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb 769 if (r >= 0)
0339cd77 770 fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address));
0ad853bc 771
8e34a618 772 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
773 if (r >= 0)
774 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 775
0339cd77
LP
776 r = sd_dhcp_lease_get_broadcast(lease, &address);
777 if (r >= 0)
778 fprintf(f, "BROADCAST=%s\n", inet_ntoa(address));
779
fe8db0c5
TG
780 r = sd_dhcp_lease_get_mtu(lease, &mtu);
781 if (r >= 0)
782 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
783
0339cd77
LP
784 r = sd_dhcp_lease_get_t1(lease, &t1);
785 if (r >= 0)
786 fprintf(f, "T1=%" PRIu32 "\n", t1);
787
788 r = sd_dhcp_lease_get_t2(lease, &t2);
109731eb 789 if (r >= 0)
0339cd77
LP
790 fprintf(f, "T2=%" PRIu32 "\n", t2);
791
792 r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
793 if (r >= 0)
794 fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
795
796 r = sd_dhcp_lease_get_dns(lease, &addresses);
797 if (r > 0) {
798 fputs("DNS=", f);
b0e39c82 799 serialize_in_addrs(f, addresses, r);
0339cd77
LP
800 fputs("\n", f);
801 }
109731eb 802
a2ba62c7 803 r = sd_dhcp_lease_get_ntp(lease, &addresses);
0339cd77
LP
804 if (r > 0) {
805 fputs("NTP=", f);
b0e39c82 806 serialize_in_addrs(f, addresses, r);
0339cd77
LP
807 fputs("\n", f);
808 }
fe8db0c5
TG
809
810 r = sd_dhcp_lease_get_domainname(lease, &string);
811 if (r >= 0)
812 fprintf(f, "DOMAINNAME=%s\n", string);
813
814 r = sd_dhcp_lease_get_hostname(lease, &string);
815 if (r >= 0)
816 fprintf(f, "HOSTNAME=%s\n", string);
817
ce78df79
TG
818 r = sd_dhcp_lease_get_root_path(lease, &string);
819 if (r >= 0)
820 fprintf(f, "ROOT_PATH=%s\n", string);
821
a2ba62c7 822 r = sd_dhcp_lease_get_routes(lease, &routes);
0339cd77 823 if (r > 0)
a2ba62c7 824 serialize_dhcp_routes(f, "ROUTES", routes, r);
e1ea665e 825
8eb9058d
LP
826 r = sd_dhcp_lease_get_timezone(lease, &string);
827 if (r >= 0)
828 fprintf(f, "TIMEZONE=%s\n", string);
829
e37f74a6
DW
830 r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
831 if (r >= 0) {
832 _cleanup_free_ char *client_id_hex;
833
dde8bb32 834 client_id_hex = hexmem(client_id, client_id_len);
e37f74a6
DW
835 if (!client_id_hex) {
836 r = -ENOMEM;
dacd6cee 837 goto fail;
e37f74a6
DW
838 }
839 fprintf(f, "CLIENTID=%s\n", client_id_hex);
840 }
841
e43a8393
BG
842 r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
843 if (r >= 0) {
844 _cleanup_free_ char *option_hex = NULL;
845
846 option_hex = hexmem(data, data_len);
847 if (!option_hex) {
848 r = -ENOMEM;
dacd6cee 849 goto fail;
e43a8393
BG
850 }
851 fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
852 }
853
a073309f 854 LIST_FOREACH(options, option, lease->private_options) {
30afef87 855 char key[strlen("OPTION_000")+1];
0339cd77 856
d054f0a4 857 xsprintf(key, "OPTION_%" PRIu8, option->tag);
a073309f
AC
858 r = serialize_dhcp_option(f, key, option->data, option->length);
859 if (r < 0)
860 goto fail;
861 }
862
dacd6cee
LP
863 r = fflush_and_check(f);
864 if (r < 0)
865 goto fail;
fe8db0c5 866
dacd6cee 867 if (rename(temp_path, lease_file) < 0) {
fe8db0c5 868 r = -errno;
dacd6cee 869 goto fail;
fe8db0c5
TG
870 }
871
dacd6cee
LP
872 return 0;
873
874fail:
875 if (temp_path)
876 (void) unlink(temp_path);
fe8db0c5 877
dacd6cee 878 return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
fe8db0c5
TG
879}
880
bd91b83e 881int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
0339cd77 882
4afd3348 883 _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
0339cd77
LP
884 _cleanup_free_ char
885 *address = NULL,
886 *router = NULL,
887 *netmask = NULL,
888 *server_address = NULL,
889 *next_server = NULL,
890 *broadcast = NULL,
891 *dns = NULL,
892 *ntp = NULL,
893 *mtu = NULL,
894 *routes = NULL,
895 *client_id_hex = NULL,
896 *vendor_specific_hex = NULL,
897 *lifetime = NULL,
898 *t1 = NULL,
899 *t2 = NULL,
22805d92 900 *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
0339cd77 901
a073309f 902 int r, i;
fe8db0c5
TG
903
904 assert(lease_file);
905 assert(ret);
906
907 r = dhcp_lease_new(&lease);
908 if (r < 0)
909 return r;
910
911 r = parse_env_file(lease_file, NEWLINE,
912 "ADDRESS", &address,
913 "ROUTER", &router,
914 "NETMASK", &netmask,
0ad853bc 915 "SERVER_IDENTIFIER", &server_address,
8e34a618 916 "NEXT_SERVER", &next_server,
0339cd77 917 "BROADCAST", &broadcast,
109731eb
TG
918 "DNS", &dns,
919 "NTP", &ntp,
fe8db0c5
TG
920 "MTU", &mtu,
921 "DOMAINNAME", &lease->domainname,
922 "HOSTNAME", &lease->hostname,
ce78df79 923 "ROOT_PATH", &lease->root_path,
e1ea665e 924 "ROUTES", &routes,
e37f74a6 925 "CLIENTID", &client_id_hex,
8eb9058d 926 "TIMEZONE", &lease->timezone,
e43a8393 927 "VENDOR_SPECIFIC", &vendor_specific_hex,
0339cd77
LP
928 "LIFETIME", &lifetime,
929 "T1", &t1,
930 "T2", &t2,
a073309f
AC
931 "OPTION_224", &options[0],
932 "OPTION_225", &options[1],
933 "OPTION_226", &options[2],
934 "OPTION_227", &options[3],
935 "OPTION_228", &options[4],
936 "OPTION_229", &options[5],
937 "OPTION_230", &options[6],
938 "OPTION_231", &options[7],
939 "OPTION_232", &options[8],
940 "OPTION_233", &options[9],
941 "OPTION_234", &options[10],
942 "OPTION_235", &options[11],
943 "OPTION_236", &options[12],
944 "OPTION_237", &options[13],
945 "OPTION_238", &options[14],
946 "OPTION_239", &options[15],
947 "OPTION_240", &options[16],
948 "OPTION_241", &options[17],
949 "OPTION_242", &options[18],
950 "OPTION_243", &options[19],
951 "OPTION_244", &options[20],
952 "OPTION_245", &options[21],
953 "OPTION_246", &options[22],
954 "OPTION_247", &options[23],
955 "OPTION_248", &options[24],
956 "OPTION_249", &options[25],
957 "OPTION_250", &options[26],
958 "OPTION_251", &options[27],
959 "OPTION_252", &options[28],
960 "OPTION_253", &options[29],
961 "OPTION_254", &options[30],
fe8db0c5 962 NULL);
fe8db0c5
TG
963 if (r < 0)
964 return r;
965
0339cd77
LP
966 if (address) {
967 r = inet_pton(AF_INET, address, &lease->address);
968 if (r <= 0)
e26ea7fc 969 log_debug("Failed to parse address %s, ignoring.", address);
0339cd77 970 }
fe8db0c5 971
8ddbeaa2 972 if (router) {
0339cd77
LP
973 r = inet_pton(AF_INET, router, &lease->router);
974 if (r <= 0)
e26ea7fc 975 log_debug("Failed to parse router %s, ignoring.", router);
8ddbeaa2 976 }
fe8db0c5 977
0339cd77
LP
978 if (netmask) {
979 r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
980 if (r <= 0)
e26ea7fc 981 log_debug("Failed to parse netmask %s, ignoring.", netmask);
0339cd77
LP
982 else
983 lease->have_subnet_mask = true;
984 }
fe8db0c5 985
0ad853bc 986 if (server_address) {
0339cd77
LP
987 r = inet_pton(AF_INET, server_address, &lease->server_address);
988 if (r <= 0)
e26ea7fc 989 log_debug("Failed to parse server address %s, ignoring.", server_address);
0ad853bc
TG
990 }
991
8e34a618 992 if (next_server) {
0339cd77
LP
993 r = inet_pton(AF_INET, next_server, &lease->next_server);
994 if (r <= 0)
e26ea7fc 995 log_debug("Failed to parse next server %s, ignoring.", next_server);
0339cd77 996 }
8e34a618 997
0339cd77
LP
998 if (broadcast) {
999 r = inet_pton(AF_INET, broadcast, &lease->broadcast);
1000 if (r <= 0)
e26ea7fc 1001 log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
0339cd77
LP
1002 else
1003 lease->have_broadcast = true;
8e34a618
TG
1004 }
1005
109731eb 1006 if (dns) {
a2ba62c7 1007 r = deserialize_in_addrs(&lease->dns, dns);
109731eb 1008 if (r < 0)
0339cd77
LP
1009 log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
1010 else
1011 lease->dns_size = r;
109731eb
TG
1012 }
1013
1014 if (ntp) {
a2ba62c7 1015 r = deserialize_in_addrs(&lease->ntp, ntp);
109731eb 1016 if (r < 0)
0339cd77
LP
1017 log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
1018 else
1019 lease->ntp_size = r;
109731eb
TG
1020 }
1021
fe8db0c5 1022 if (mtu) {
0339cd77
LP
1023 r = safe_atou16(mtu, &lease->mtu);
1024 if (r < 0)
1025 log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
fe8db0c5
TG
1026 }
1027
e1ea665e 1028 if (routes) {
0339cd77
LP
1029 r = deserialize_dhcp_routes(
1030 &lease->static_route,
1031 &lease->static_route_size,
1032 &lease->static_route_allocated,
1033 routes);
1034 if (r < 0)
1035 log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
1036 }
1037
1038 if (lifetime) {
1039 r = safe_atou32(lifetime, &lease->lifetime);
1040 if (r < 0)
1041 log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
1042 }
1043
1044 if (t1) {
1045 r = safe_atou32(t1, &lease->t1);
1046 if (r < 0)
1047 log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
1048 }
1049
1050 if (t2) {
1051 r = safe_atou32(t2, &lease->t2);
e1ea665e 1052 if (r < 0)
0339cd77 1053 log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
e1ea665e
EY
1054 }
1055
e37f74a6 1056 if (client_id_hex) {
9b57d9ae 1057 r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
30494563 1058 if (r < 0)
0339cd77 1059 log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
e37f74a6
DW
1060 }
1061
e43a8393 1062 if (vendor_specific_hex) {
9b57d9ae 1063 r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
e43a8393 1064 if (r < 0)
0339cd77 1065 log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
e43a8393
BG
1066 }
1067
22805d92 1068 for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
e4735228 1069 _cleanup_free_ void *data = NULL;
a073309f
AC
1070 size_t len;
1071
1072 if (!options[i])
1073 continue;
1074
1075 r = deserialize_dhcp_option(&data, &len, options[i]);
0339cd77
LP
1076 if (r < 0) {
1077 log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
1078 continue;
1079 }
a073309f 1080
22805d92 1081 r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
626be147 1082 if (r < 0)
a073309f
AC
1083 return r;
1084 }
1085
fe8db0c5
TG
1086 *ret = lease;
1087 lease = NULL;
1088
1089 return 0;
1090}
9e64dd72
TG
1091
1092int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
0339cd77 1093 struct in_addr address, mask;
df40eee8 1094 int r;
9e64dd72
TG
1095
1096 assert(lease);
9e64dd72 1097
0339cd77
LP
1098 if (lease->address == 0)
1099 return -ENODATA;
1100
df40eee8 1101 address.s_addr = lease->address;
9e64dd72
TG
1102
1103 /* fall back to the default subnet masks based on address class */
df40eee8
TG
1104 r = in_addr_default_subnet_mask(&address, &mask);
1105 if (r < 0)
1106 return r;
9e64dd72 1107
df40eee8 1108 lease->subnet_mask = mask.s_addr;
0339cd77 1109 lease->have_subnet_mask = true;
9e64dd72
TG
1110
1111 return 0;
1112}
e37f74a6 1113
0339cd77 1114int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
e37f74a6
DW
1115 assert_return(lease, -EINVAL);
1116 assert_return(client_id, -EINVAL);
1117 assert_return(client_id_len, -EINVAL);
1118
0339cd77
LP
1119 if (!lease->client_id)
1120 return -ENODATA;
1121
e37f74a6
DW
1122 *client_id = lease->client_id;
1123 *client_id_len = lease->client_id_len;
0339cd77 1124
e37f74a6
DW
1125 return 0;
1126}
1127
0339cd77 1128int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
e37f74a6 1129 assert_return(lease, -EINVAL);
0339cd77
LP
1130 assert_return(client_id || client_id_len <= 0, -EINVAL);
1131
1132 if (client_id_len <= 0)
1133 lease->client_id = mfree(lease->client_id);
1134 else {
1135 void *p;
e37f74a6 1136
0339cd77
LP
1137 p = memdup(client_id, client_id_len);
1138 if (!p)
1139 return -ENOMEM;
e37f74a6 1140
0339cd77
LP
1141 free(lease->client_id);
1142 lease->client_id = p;
e37f74a6
DW
1143 lease->client_id_len = client_id_len;
1144 }
1145
1146 return 0;
1147}
8eb9058d 1148
64d6c229 1149int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
8eb9058d 1150 assert_return(lease, -EINVAL);
64d6c229 1151 assert_return(tz, -EINVAL);
8eb9058d
LP
1152
1153 if (!lease->timezone)
0339cd77 1154 return -ENODATA;
8eb9058d 1155
64d6c229 1156 *tz = lease->timezone;
8eb9058d
LP
1157 return 0;
1158}
f8693fc7
BG
1159
1160int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
1161 assert_return(route, -EINVAL);
1162 assert_return(destination, -EINVAL);
1163
1164 *destination = route->dst_addr;
1165 return 0;
1166}
1167
1168int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
1169 assert_return(route, -EINVAL);
1170 assert_return(length, -EINVAL);
1171
1172 *length = route->dst_prefixlen;
1173 return 0;
1174}
1175
1176int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
1177 assert_return(route, -EINVAL);
1178 assert_return(gateway, -EINVAL);
1179
1180 *gateway = route->gw_addr;
1181 return 0;
1182}