]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-lease.c
sd-dhcp-client/networkd: set lifetimes for IPv4 addresses
[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>
25#include <net/ethernet.h>
fe8db0c5 26#include <arpa/inet.h>
a6cc569e
TG
27#include <sys/param.h>
28
29#include "util.h"
30#include "list.h"
fe8db0c5
TG
31#include "mkdir.h"
32#include "fileio.h"
a6cc569e
TG
33
34#include "dhcp-protocol.h"
35#include "dhcp-internal.h"
fe8db0c5
TG
36#include "dhcp-lease-internal.h"
37#include "sd-dhcp-lease.h"
a6cc569e 38#include "sd-dhcp-client.h"
09bee74d 39#include "network-internal.h"
a6cc569e
TG
40
41int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
42 assert_return(lease, -EINVAL);
43 assert_return(addr, -EINVAL);
44
45 addr->s_addr = lease->address;
46
47 return 0;
48}
49
68ceb9df
PF
50int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
51 assert_return(lease, -EINVAL);
52 assert_return(lease, -EINVAL);
53
54 *lifetime = lease->lifetime;
55
56 return 0;
57}
58
a6cc569e
TG
59int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
60 assert_return(lease, -EINVAL);
61 assert_return(mtu, -EINVAL);
62
63 if (lease->mtu)
64 *mtu = lease->mtu;
65 else
66 return -ENOENT;
67
68 return 0;
69}
70
71int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
72 assert_return(lease, -EINVAL);
73 assert_return(addr, -EINVAL);
74 assert_return(addr_size, -EINVAL);
75
76 if (lease->dns_size) {
77 *addr_size = lease->dns_size;
78 *addr = lease->dns;
79 } else
80 return -ENOENT;
81
82 return 0;
83}
84
46844696
TG
85int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
86 assert_return(lease, -EINVAL);
87 assert_return(addr, -EINVAL);
88 assert_return(addr_size, -EINVAL);
89
90 if (lease->ntp_size) {
91 *addr_size = lease->ntp_size;
92 *addr = lease->ntp;
93 } else
94 return -ENOENT;
95
96 return 0;
97}
98
a6cc569e
TG
99int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
100 assert_return(lease, -EINVAL);
101 assert_return(domainname, -EINVAL);
102
103 if (lease->domainname)
104 *domainname = lease->domainname;
105 else
106 return -ENOENT;
107
108 return 0;
109}
110
111int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
112 assert_return(lease, -EINVAL);
113 assert_return(hostname, -EINVAL);
114
115 if (lease->hostname)
116 *hostname = lease->hostname;
117 else
118 return -ENOENT;
119
120 return 0;
121}
122
ce78df79
TG
123int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
124 assert_return(lease, -EINVAL);
125 assert_return(root_path, -EINVAL);
126
127 if (lease->root_path)
128 *root_path = lease->root_path;
129 else
130 return -ENOENT;
131
132 return 0;
133}
134
a6cc569e
TG
135int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
136 assert_return(lease, -EINVAL);
137 assert_return(addr, -EINVAL);
138
8ddbeaa2
UTL
139 if (lease->router != INADDR_ANY)
140 addr->s_addr = lease->router;
141 else
142 return -ENOENT;
a6cc569e
TG
143
144 return 0;
145}
146
147int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
148 assert_return(lease, -EINVAL);
149 assert_return(addr, -EINVAL);
150
151 addr->s_addr = lease->subnet_mask;
152
153 return 0;
154}
155
0ad853bc
TG
156int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
157 assert_return(lease, -EINVAL);
158 assert_return(addr, -EINVAL);
159
160 addr->s_addr = lease->server_address;
161
162 return 0;
163}
164
8e34a618
TG
165int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
166 assert_return(lease, -EINVAL);
167 assert_return(addr, -EINVAL);
168
169 addr->s_addr = lease->next_server;
170
171 return 0;
172}
173
a6cc569e
TG
174sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
175 if (lease)
176 assert_se(REFCNT_INC(lease->n_ref) >= 2);
177
178 return lease;
179}
180
181sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
182 if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
183 free(lease->hostname);
184 free(lease->domainname);
185 free(lease->dns);
81d98a39 186 free(lease->ntp);
a6cc569e
TG
187 free(lease);
188 }
189
190 return NULL;
191}
192
e140ae58
TG
193static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
194 be32_t val;
195
196 assert(option);
197 assert(ret);
198
199 if (len == 4) {
200 memcpy(&val, option, 4);
201 *ret = be32toh(val);
202
203 if (*ret < min)
204 *ret = min;
205 }
206}
207
f5c0c00f
TG
208static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
209 lease_parse_u32(option, len, (uint32_t *)ret, 0);
210}
211
e140ae58
TG
212static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
213 be16_t val;
214
215 assert(option);
216 assert(ret);
217
218 if (len == 2) {
219 memcpy(&val, option, 2);
220 *ret = be16toh(val);
221
222 if (*ret < min)
223 *ret = min;
224 }
225}
226
227static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
228 assert(option);
229 assert(ret);
230
231 if (len == 4)
232 memcpy(ret, option, 4);
233}
234
f5c0c00f
TG
235static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
236 assert(option);
237 assert(ret);
238
239 if (len == 1)
240 *ret = !!(*option);
241}
242
243static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
244 assert(option);
245 assert(ret);
246
247 if (len == 1) {
248 *ret = *option;
249
250 if (*ret < min)
251 *ret = min;
252 }
253}
254
e140ae58
TG
255static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
256 assert(option);
257 assert(ret);
258
259 if (len >= 1) {
260 char *string;
261
262 string = strndup((const char *)option, len);
263 if (!string)
264 return -errno;
265
266 free(*ret);
267 *ret = string;
268 }
269
270 return 0;
271}
272
f5c0c00f 273static 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
274 assert(option);
275 assert(ret);
276 assert(ret_size);
277
f5c0c00f 278 if (len && !(len % (4 * mult))) {
e140ae58
TG
279 size_t size;
280 struct in_addr *addresses;
281
282 size = len / 4;
283
284 addresses = newdup(struct in_addr, option, size);
285 if (!addresses)
286 return -ENOMEM;
287
288 free(*ret);
289 *ret = addresses;
290 *ret_size = size;
291 }
292
293 return 0;
294}
295
f5c0c00f
TG
296static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
297 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
298}
299
300static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
301 return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
302}
303
a6cc569e
TG
304int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
305 void *user_data) {
306 sd_dhcp_lease *lease = user_data;
e140ae58
TG
307 int r;
308
309 assert(lease);
a6cc569e
TG
310
311 switch(code) {
312
f5c0c00f
TG
313 case DHCP_OPTION_TIME_OFFSET:
314 lease_parse_s32(option, len, &lease->time_offset);
315
316 break;
317
318 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
319 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
320
321 break;
322
a6cc569e 323 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
e140ae58 324 lease_parse_u32(option, len, &lease->lifetime, 1);
a6cc569e
TG
325
326 break;
327
328 case DHCP_OPTION_SERVER_IDENTIFIER:
e140ae58 329 lease_parse_be32(option, len, &lease->server_address);
a6cc569e
TG
330
331 break;
332
333 case DHCP_OPTION_SUBNET_MASK:
e140ae58 334 lease_parse_be32(option, len, &lease->subnet_mask);
a6cc569e
TG
335
336 break;
337
f5c0c00f
TG
338 case DHCP_OPTION_BROADCAST:
339 lease_parse_be32(option, len, &lease->broadcast);
340
341 break;
342
a6cc569e 343 case DHCP_OPTION_ROUTER:
e140ae58 344 lease_parse_be32(option, len, &lease->router);
a6cc569e
TG
345
346 break;
347
348 case DHCP_OPTION_DOMAIN_NAME_SERVER:
e140ae58
TG
349 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
350 if (r < 0)
351 return r;
46844696
TG
352
353 break;
354
355 case DHCP_OPTION_NTP_SERVER:
e140ae58
TG
356 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
357 if (r < 0)
358 return r;
a6cc569e
TG
359
360 break;
361
f5c0c00f
TG
362 case DHCP_OPTION_POLICY_FILTER:
363 r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
364 if (r < 0)
365 return r;
366
367 break;
368
369 case DHCP_OPTION_STATIC_ROUTE:
370 r = lease_parse_in_addrs_pairs(option, len, &lease->static_route, &lease->static_route_size);
371 if (r < 0)
372 return r;
373
374 break;
375
a6cc569e 376 case DHCP_OPTION_INTERFACE_MTU:
e140ae58 377 lease_parse_u16(option, len, &lease->mtu, 68);
a6cc569e
TG
378
379 break;
380
f5c0c00f
TG
381 case DHCP_OPTION_INTERFACE_MDR:
382 lease_parse_u16(option, len, &lease->mdr, 576);
383
384 break;
385
386 case DHCP_OPTION_INTERFACE_TTL:
387 lease_parse_u8(option, len, &lease->ttl, 1);
388
389 break;
390
391 case DHCP_OPTION_BOOT_FILE_SIZE:
392 lease_parse_u16(option, len, &lease->boot_file_size, 0);
393
394 break;
395
a6cc569e 396 case DHCP_OPTION_DOMAIN_NAME:
e140ae58
TG
397 r = lease_parse_string(option, len, &lease->domainname);
398 if (r < 0)
399 return r;
a6cc569e
TG
400
401 break;
402
403 case DHCP_OPTION_HOST_NAME:
e140ae58
TG
404 r = lease_parse_string(option, len, &lease->hostname);
405 if (r < 0)
406 return r;
a6cc569e
TG
407
408 break;
409
ce78df79 410 case DHCP_OPTION_ROOT_PATH:
e140ae58
TG
411 r = lease_parse_string(option, len, &lease->root_path);
412 if (r < 0)
413 return r;
ce78df79
TG
414
415 break;
416
a6cc569e 417 case DHCP_OPTION_RENEWAL_T1_TIME:
e140ae58 418 lease_parse_u32(option, len, &lease->t1, 1);
a6cc569e
TG
419
420 break;
421
422 case DHCP_OPTION_REBINDING_T2_TIME:
e140ae58 423 lease_parse_u32(option, len, &lease->t2, 1);
a6cc569e 424
f5c0c00f
TG
425 break;
426
427 case DHCP_OPTION_ENABLE_IP_FORWARDING:
428 lease_parse_bool(option, len, &lease->ip_forward);
429
430 break;
431
432 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
433 lease_parse_bool(option, len, &lease->ip_forward_non_local);
434
a6cc569e
TG
435 break;
436 }
437
438 return 0;
439}
440
441int dhcp_lease_new(sd_dhcp_lease **ret) {
6e00a806 442 sd_dhcp_lease *lease;
a6cc569e
TG
443
444 lease = new0(sd_dhcp_lease, 1);
445 if (!lease)
446 return -ENOMEM;
447
8ddbeaa2 448 lease->router = INADDR_ANY;
a6cc569e
TG
449 lease->n_ref = REFCNT_INIT;
450
451 *ret = lease;
a6cc569e
TG
452 return 0;
453}
fe8db0c5
TG
454
455int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
456 _cleanup_free_ char *temp_path = NULL;
457 _cleanup_fclose_ FILE *f = NULL;
fe8db0c5 458 struct in_addr address;
109731eb
TG
459 struct in_addr *addresses;
460 size_t addresses_size;
fe8db0c5
TG
461 const char *string;
462 uint16_t mtu;
463 int r;
464
465 assert(lease);
466 assert(lease_file);
467
fe8db0c5
TG
468 r = fopen_temporary(lease_file, &f, &temp_path);
469 if (r < 0)
470 goto finish;
471
472 fchmod(fileno(f), 0644);
473
474 r = sd_dhcp_lease_get_address(lease, &address);
475 if (r < 0)
476 goto finish;
477
fe8db0c5
TG
478 fprintf(f,
479 "# This is private data. Do not parse.\n"
109731eb 480 "ADDRESS=%s\n", inet_ntoa(address));
fe8db0c5 481
fe8db0c5
TG
482 r = sd_dhcp_lease_get_netmask(lease, &address);
483 if (r < 0)
484 goto finish;
485
109731eb 486 fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
fe8db0c5 487
8ddbeaa2
UTL
488 r = sd_dhcp_lease_get_router(lease, &address);
489 if (r >= 0)
490 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
491
0ad853bc 492 r = sd_dhcp_lease_get_server_identifier(lease, &address);
109731eb
TG
493 if (r >= 0)
494 fprintf(f, "SERVER_ADDRESS=%s\n",
495 inet_ntoa(address));
0ad853bc 496
8e34a618 497 r = sd_dhcp_lease_get_next_server(lease, &address);
109731eb
TG
498 if (r >= 0)
499 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
8e34a618 500
fe8db0c5
TG
501 r = sd_dhcp_lease_get_mtu(lease, &mtu);
502 if (r >= 0)
503 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
504
109731eb
TG
505 r = sd_dhcp_lease_get_dns(lease, &addresses, &addresses_size);
506 if (r >= 0)
09bee74d 507 serialize_in_addrs(f, "DNS", addresses, addresses_size);
109731eb
TG
508
509 r = sd_dhcp_lease_get_ntp(lease, &addresses, &addresses_size);
510 if (r >= 0)
09bee74d 511 serialize_in_addrs(f, "NTP", addresses, addresses_size);
fe8db0c5
TG
512
513 r = sd_dhcp_lease_get_domainname(lease, &string);
514 if (r >= 0)
515 fprintf(f, "DOMAINNAME=%s\n", string);
516
517 r = sd_dhcp_lease_get_hostname(lease, &string);
518 if (r >= 0)
519 fprintf(f, "HOSTNAME=%s\n", string);
520
ce78df79
TG
521 r = sd_dhcp_lease_get_root_path(lease, &string);
522 if (r >= 0)
523 fprintf(f, "ROOT_PATH=%s\n", string);
524
fe8db0c5
TG
525 r = 0;
526
527 fflush(f);
528
529 if (ferror(f) || rename(temp_path, lease_file) < 0) {
530 r = -errno;
531 unlink(lease_file);
532 unlink(temp_path);
533 }
534
535finish:
536 if (r < 0)
537 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
538
539 return r;
540}
541
542int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
543 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
544 _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
8e34a618 545 *server_address = NULL, *next_server = NULL,
109731eb 546 *dns = NULL, *ntp = NULL, *mtu = NULL;
fe8db0c5
TG
547 struct in_addr addr;
548 int r;
549
550 assert(lease_file);
551 assert(ret);
552
553 r = dhcp_lease_new(&lease);
554 if (r < 0)
555 return r;
556
557 r = parse_env_file(lease_file, NEWLINE,
558 "ADDRESS", &address,
559 "ROUTER", &router,
560 "NETMASK", &netmask,
0ad853bc 561 "SERVER_IDENTIFIER", &server_address,
8e34a618 562 "NEXT_SERVER", &next_server,
109731eb
TG
563 "DNS", &dns,
564 "NTP", &ntp,
fe8db0c5
TG
565 "MTU", &mtu,
566 "DOMAINNAME", &lease->domainname,
567 "HOSTNAME", &lease->hostname,
ce78df79 568 "ROOT_PATH", &lease->root_path,
fe8db0c5
TG
569 NULL);
570 if (r < 0) {
571 if (r == -ENOENT)
572 return 0;
573
574 log_error("Failed to read %s: %s", lease_file, strerror(-r));
575 return r;
576 }
577
578 r = inet_pton(AF_INET, address, &addr);
579 if (r < 0)
580 return r;
581
582 lease->address = addr.s_addr;
583
8ddbeaa2
UTL
584 if (router) {
585 r = inet_pton(AF_INET, router, &addr);
586 if (r < 0)
587 return r;
fe8db0c5 588
8ddbeaa2
UTL
589 lease->router = addr.s_addr;
590 }
fe8db0c5
TG
591
592 r = inet_pton(AF_INET, netmask, &addr);
593 if (r < 0)
594 return r;
595
596 lease->subnet_mask = addr.s_addr;
597
0ad853bc
TG
598 if (server_address) {
599 r = inet_pton(AF_INET, server_address, &addr);
600 if (r < 0)
601 return r;
602
603 lease->server_address = addr.s_addr;
604 }
605
8e34a618
TG
606 if (next_server) {
607 r = inet_pton(AF_INET, next_server, &addr);
608 if (r < 0)
609 return r;
610
611 lease->next_server = addr.s_addr;
612 }
613
109731eb 614 if (dns) {
09bee74d 615 r = deserialize_in_addrs(&lease->dns, &lease->dns_size, dns);
109731eb
TG
616 if (r < 0)
617 return r;
618 }
619
620 if (ntp) {
09bee74d 621 r = deserialize_in_addrs(&lease->ntp, &lease->ntp_size, dns);
109731eb
TG
622 if (r < 0)
623 return r;
624 }
625
fe8db0c5
TG
626 if (mtu) {
627 uint16_t u;
628 if (sscanf(mtu, "%" SCNu16, &u) > 0)
629 lease->mtu = u;
630 }
631
632 *ret = lease;
633 lease = NULL;
634
635 return 0;
636}
9e64dd72
TG
637
638int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
639 uint32_t address;
640
641 assert(lease);
642 assert(lease->address != INADDR_ANY);
643
644 address = be32toh(lease->address);
645
646 /* fall back to the default subnet masks based on address class */
647
648 if ((address >> 31) == 0x0)
649 /* class A, leading bits: 0 */
650 lease->subnet_mask = htobe32(0xff000000);
651 else if ((address >> 30) == 0x2)
652 /* class B, leading bits 10 */
653 lease->subnet_mask = htobe32(0xffff0000);
654 else if ((address >> 29) == 0x6)
655 /* class C, leading bits 110 */
656 lease->subnet_mask = htobe32(0xffffff00);
657 else
658 /* class D or E, no default mask. give up */
659 return -ERANGE;
660
661 return 0;
662}