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