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