2 This file is part of systemd.
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5 Copyright (C) 2014 Tom Gundersen
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.
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.
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/>.
25 #include <arpa/inet.h>
28 #include "unaligned.h"
29 #include "in-addr-util.h"
30 #include "hostname-util.h"
31 #include "dhcp-protocol.h"
32 #include "dhcp-lease-internal.h"
33 #include "sd-dhcp-lease.h"
34 #include "network-internal.h"
35 #include "dns-domain.h"
37 int sd_dhcp_lease_get_address(sd_dhcp_lease
*lease
, struct in_addr
*addr
) {
38 assert_return(lease
, -EINVAL
);
39 assert_return(addr
, -EINVAL
);
41 addr
->s_addr
= lease
->address
;
46 int sd_dhcp_lease_get_lifetime(sd_dhcp_lease
*lease
, uint32_t *lifetime
) {
47 assert_return(lease
, -EINVAL
);
48 assert_return(lifetime
, -EINVAL
);
50 *lifetime
= lease
->lifetime
;
55 int sd_dhcp_lease_get_mtu(sd_dhcp_lease
*lease
, uint16_t *mtu
) {
56 assert_return(lease
, -EINVAL
);
57 assert_return(mtu
, -EINVAL
);
67 int sd_dhcp_lease_get_dns(sd_dhcp_lease
*lease
, const struct in_addr
**addr
) {
68 assert_return(lease
, -EINVAL
);
69 assert_return(addr
, -EINVAL
);
71 if (lease
->dns_size
) {
73 return lease
->dns_size
;
80 int sd_dhcp_lease_get_ntp(sd_dhcp_lease
*lease
, const struct in_addr
**addr
) {
81 assert_return(lease
, -EINVAL
);
82 assert_return(addr
, -EINVAL
);
84 if (lease
->ntp_size
) {
86 return lease
->ntp_size
;
93 int sd_dhcp_lease_get_domainname(sd_dhcp_lease
*lease
, const char **domainname
) {
94 assert_return(lease
, -EINVAL
);
95 assert_return(domainname
, -EINVAL
);
97 if (lease
->domainname
)
98 *domainname
= lease
->domainname
;
105 int sd_dhcp_lease_get_hostname(sd_dhcp_lease
*lease
, const char **hostname
) {
106 assert_return(lease
, -EINVAL
);
107 assert_return(hostname
, -EINVAL
);
110 *hostname
= lease
->hostname
;
117 int sd_dhcp_lease_get_root_path(sd_dhcp_lease
*lease
, const char **root_path
) {
118 assert_return(lease
, -EINVAL
);
119 assert_return(root_path
, -EINVAL
);
121 if (lease
->root_path
)
122 *root_path
= lease
->root_path
;
129 int sd_dhcp_lease_get_router(sd_dhcp_lease
*lease
, struct in_addr
*addr
) {
130 assert_return(lease
, -EINVAL
);
131 assert_return(addr
, -EINVAL
);
133 if (lease
->router
!= INADDR_ANY
)
134 addr
->s_addr
= lease
->router
;
141 int sd_dhcp_lease_get_netmask(sd_dhcp_lease
*lease
, struct in_addr
*addr
) {
142 assert_return(lease
, -EINVAL
);
143 assert_return(addr
, -EINVAL
);
145 addr
->s_addr
= lease
->subnet_mask
;
150 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease
*lease
, struct in_addr
*addr
) {
151 assert_return(lease
, -EINVAL
);
152 assert_return(addr
, -EINVAL
);
154 addr
->s_addr
= lease
->server_address
;
159 int sd_dhcp_lease_get_next_server(sd_dhcp_lease
*lease
, struct in_addr
*addr
) {
160 assert_return(lease
, -EINVAL
);
161 assert_return(addr
, -EINVAL
);
163 addr
->s_addr
= lease
->next_server
;
168 int sd_dhcp_lease_get_routes(sd_dhcp_lease
*lease
, struct sd_dhcp_route
**routes
) {
170 assert_return(lease
, -EINVAL
);
171 assert_return(routes
, -EINVAL
);
173 if (lease
->static_route_size
) {
174 *routes
= lease
->static_route
;
175 return lease
->static_route_size
;
182 sd_dhcp_lease
*sd_dhcp_lease_ref(sd_dhcp_lease
*lease
) {
184 assert_se(REFCNT_INC(lease
->n_ref
) >= 2);
189 sd_dhcp_lease
*sd_dhcp_lease_unref(sd_dhcp_lease
*lease
) {
190 if (lease
&& REFCNT_DEC(lease
->n_ref
) == 0) {
191 free(lease
->hostname
);
192 free(lease
->domainname
);
195 free(lease
->static_route
);
196 free(lease
->client_id
);
203 static void lease_parse_u32(const uint8_t *option
, size_t len
, uint32_t *ret
, uint32_t min
) {
208 *ret
= unaligned_read_be32((be32_t
*) option
);
215 static void lease_parse_s32(const uint8_t *option
, size_t len
, int32_t *ret
) {
216 lease_parse_u32(option
, len
, (uint32_t *)ret
, 0);
219 static void lease_parse_u16(const uint8_t *option
, size_t len
, uint16_t *ret
, uint16_t min
) {
224 *ret
= unaligned_read_be16((be16_t
*) option
);
231 static void lease_parse_be32(const uint8_t *option
, size_t len
, be32_t
*ret
) {
236 memcpy(ret
, option
, 4);
239 static void lease_parse_bool(const uint8_t *option
, size_t len
, bool *ret
) {
247 static void lease_parse_u8(const uint8_t *option
, size_t len
, uint8_t *ret
, uint8_t min
) {
259 static int lease_parse_string(const uint8_t *option
, size_t len
, char **ret
) {
266 string
= strndup((const char *)option
, len
);
277 static int lease_parse_in_addrs_aux(const uint8_t *option
, size_t len
, struct in_addr
**ret
, size_t *ret_size
, size_t mult
) {
282 if (len
&& !(len
% (4 * mult
))) {
284 struct in_addr
*addresses
;
288 addresses
= newdup(struct in_addr
, option
, size
);
300 static int lease_parse_in_addrs(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
, 1);
304 static int lease_parse_in_addrs_pairs(const uint8_t *option
, size_t len
, struct in_addr
**ret
, size_t *ret_size
) {
305 return lease_parse_in_addrs_aux(option
, len
, ret
, ret_size
, 2);
308 static int lease_parse_routes(const uint8_t *option
, size_t len
, struct sd_dhcp_route
**routes
,
309 size_t *routes_size
, size_t *routes_allocated
) {
316 assert(routes_allocated
);
324 if (!GREEDY_REALLOC(*routes
, *routes_allocated
, *routes_size
+ (len
/ 8)))
328 struct sd_dhcp_route
*route
= *routes
+ *routes_size
;
331 r
= in_addr_default_prefixlen((struct in_addr
*) option
, &route
->dst_prefixlen
);
333 log_error("Failed to determine destination prefix length from class based IP, ignoring");
337 lease_parse_be32(option
, 4, &addr
.s_addr
);
338 route
->dst_addr
= inet_makeaddr(inet_netof(addr
), 0);
341 lease_parse_be32(option
, 4, &route
->gw_addr
.s_addr
);
351 /* parses RFC3442 Classless Static Route Option */
352 static int lease_parse_classless_routes(const uint8_t *option
, size_t len
, struct sd_dhcp_route
**routes
,
353 size_t *routes_size
, size_t *routes_allocated
) {
358 assert(routes_allocated
);
360 /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
364 struct sd_dhcp_route
*route
;
366 if (!GREEDY_REALLOC(*routes
, *routes_allocated
, *routes_size
+ 1))
369 route
= *routes
+ *routes_size
;
371 dst_octets
= (*option
== 0 ? 0 : ((*option
- 1) / 8) + 1);
372 route
->dst_prefixlen
= *option
;
376 /* can't have more than 4 octets in IPv4 */
377 if (dst_octets
> 4 || len
< dst_octets
)
380 route
->dst_addr
.s_addr
= 0;
381 memcpy(&route
->dst_addr
.s_addr
, option
, dst_octets
);
382 option
+= dst_octets
;
388 lease_parse_be32(option
, 4, &route
->gw_addr
.s_addr
);
398 int dhcp_lease_parse_options(uint8_t code
, uint8_t len
, const uint8_t *option
,
400 sd_dhcp_lease
*lease
= user_data
;
407 case DHCP_OPTION_TIME_OFFSET
:
408 lease_parse_s32(option
, len
, &lease
->time_offset
);
412 case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT
:
413 lease_parse_u32(option
, len
, &lease
->mtu_aging_timeout
, 0);
417 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME
:
418 lease_parse_u32(option
, len
, &lease
->lifetime
, 1);
422 case DHCP_OPTION_SERVER_IDENTIFIER
:
423 lease_parse_be32(option
, len
, &lease
->server_address
);
427 case DHCP_OPTION_SUBNET_MASK
:
428 lease_parse_be32(option
, len
, &lease
->subnet_mask
);
432 case DHCP_OPTION_BROADCAST
:
433 lease_parse_be32(option
, len
, &lease
->broadcast
);
437 case DHCP_OPTION_ROUTER
:
439 lease_parse_be32(option
, 4, &lease
->router
);
443 case DHCP_OPTION_DOMAIN_NAME_SERVER
:
444 r
= lease_parse_in_addrs(option
, len
, &lease
->dns
, &lease
->dns_size
);
450 case DHCP_OPTION_NTP_SERVER
:
451 r
= lease_parse_in_addrs(option
, len
, &lease
->ntp
, &lease
->ntp_size
);
457 case DHCP_OPTION_POLICY_FILTER
:
458 r
= lease_parse_in_addrs_pairs(option
, len
, &lease
->policy_filter
, &lease
->policy_filter_size
);
464 case DHCP_OPTION_STATIC_ROUTE
:
465 r
= lease_parse_routes(option
, len
, &lease
->static_route
, &lease
->static_route_size
,
466 &lease
->static_route_allocated
);
472 case DHCP_OPTION_INTERFACE_MTU
:
473 lease_parse_u16(option
, len
, &lease
->mtu
, 68);
477 case DHCP_OPTION_INTERFACE_MDR
:
478 lease_parse_u16(option
, len
, &lease
->mdr
, 576);
482 case DHCP_OPTION_INTERFACE_TTL
:
483 lease_parse_u8(option
, len
, &lease
->ttl
, 1);
487 case DHCP_OPTION_BOOT_FILE_SIZE
:
488 lease_parse_u16(option
, len
, &lease
->boot_file_size
, 0);
492 case DHCP_OPTION_DOMAIN_NAME
:
494 _cleanup_free_
char *domainname
= NULL
;
497 r
= lease_parse_string(option
, len
, &domainname
);
501 /* Chop off trailing dot of domain name that some DHCP
502 * servers send us back. Internally we want to store
503 * host names without trailing dots and
504 * host_name_is_valid() doesn't accept them. */
505 e
= endswith(domainname
, ".");
509 if (is_localhost(domainname
))
512 r
= dns_name_is_valid(domainname
);
515 log_error_errno(r
, "Failed to validate domain name: %s: %m", domainname
);
517 log_warning("Domain name is not valid, ignoring: %s", domainname
);
521 free(lease
->domainname
);
522 lease
->domainname
= domainname
;
527 case DHCP_OPTION_HOST_NAME
:
529 _cleanup_free_
char *hostname
= NULL
;
532 r
= lease_parse_string(option
, len
, &hostname
);
536 e
= endswith(hostname
, ".");
540 if (!hostname_is_valid(hostname
) || is_localhost(hostname
))
543 free(lease
->hostname
);
544 lease
->hostname
= hostname
;
549 case DHCP_OPTION_ROOT_PATH
:
550 r
= lease_parse_string(option
, len
, &lease
->root_path
);
556 case DHCP_OPTION_RENEWAL_T1_TIME
:
557 lease_parse_u32(option
, len
, &lease
->t1
, 1);
561 case DHCP_OPTION_REBINDING_T2_TIME
:
562 lease_parse_u32(option
, len
, &lease
->t2
, 1);
566 case DHCP_OPTION_ENABLE_IP_FORWARDING
:
567 lease_parse_bool(option
, len
, &lease
->ip_forward
);
571 case DHCP_OPTION_ENABLE_IP_FORWARDING_NL
:
572 lease_parse_bool(option
, len
, &lease
->ip_forward_non_local
);
576 case DHCP_OPTION_CLASSLESS_STATIC_ROUTE
:
577 r
= lease_parse_classless_routes(option
, len
, &lease
->static_route
, &lease
->static_route_size
,
578 &lease
->static_route_allocated
);
588 int dhcp_lease_new(sd_dhcp_lease
**ret
) {
589 sd_dhcp_lease
*lease
;
591 lease
= new0(sd_dhcp_lease
, 1);
595 lease
->router
= INADDR_ANY
;
596 lease
->n_ref
= REFCNT_INIT
;
602 int sd_dhcp_lease_save(sd_dhcp_lease
*lease
, const char *lease_file
) {
603 _cleanup_free_
char *temp_path
= NULL
;
604 _cleanup_fclose_
FILE *f
= NULL
;
605 struct in_addr address
;
606 const struct in_addr
*addresses
;
607 const uint8_t *client_id
;
608 size_t client_id_len
;
611 struct sd_dhcp_route
*routes
;
617 r
= fopen_temporary(lease_file
, &f
, &temp_path
);
621 fchmod(fileno(f
), 0644);
623 r
= sd_dhcp_lease_get_address(lease
, &address
);
628 "# This is private data. Do not parse.\n"
629 "ADDRESS=%s\n", inet_ntoa(address
));
631 r
= sd_dhcp_lease_get_netmask(lease
, &address
);
635 fprintf(f
, "NETMASK=%s\n", inet_ntoa(address
));
637 r
= sd_dhcp_lease_get_router(lease
, &address
);
639 fprintf(f
, "ROUTER=%s\n", inet_ntoa(address
));
641 r
= sd_dhcp_lease_get_server_identifier(lease
, &address
);
643 fprintf(f
, "SERVER_ADDRESS=%s\n",
646 r
= sd_dhcp_lease_get_next_server(lease
, &address
);
648 fprintf(f
, "NEXT_SERVER=%s\n", inet_ntoa(address
));
650 r
= sd_dhcp_lease_get_mtu(lease
, &mtu
);
652 fprintf(f
, "MTU=%" PRIu16
"\n", mtu
);
655 r
= sd_dhcp_lease_get_dns(lease
, &addresses
);
657 serialize_in_addrs(f
, addresses
, r
);
661 r
= sd_dhcp_lease_get_ntp(lease
, &addresses
);
663 serialize_in_addrs(f
, addresses
, r
);
666 r
= sd_dhcp_lease_get_domainname(lease
, &string
);
668 fprintf(f
, "DOMAINNAME=%s\n", string
);
670 r
= sd_dhcp_lease_get_hostname(lease
, &string
);
672 fprintf(f
, "HOSTNAME=%s\n", string
);
674 r
= sd_dhcp_lease_get_root_path(lease
, &string
);
676 fprintf(f
, "ROOT_PATH=%s\n", string
);
678 r
= sd_dhcp_lease_get_routes(lease
, &routes
);
680 serialize_dhcp_routes(f
, "ROUTES", routes
, r
);
682 r
= sd_dhcp_lease_get_client_id(lease
, &client_id
, &client_id_len
);
684 _cleanup_free_
char *client_id_hex
;
686 client_id_hex
= hexmem(client_id
, client_id_len
);
687 if (!client_id_hex
) {
691 fprintf(f
, "CLIENTID=%s\n", client_id_hex
);
698 if (ferror(f
) || rename(temp_path
, lease_file
) < 0) {
706 log_error_errno(r
, "Failed to save lease data %s: %m", lease_file
);
711 int sd_dhcp_lease_load(sd_dhcp_lease
**ret
, const char *lease_file
) {
712 _cleanup_dhcp_lease_unref_ sd_dhcp_lease
*lease
= NULL
;
713 _cleanup_free_
char *address
= NULL
, *router
= NULL
, *netmask
= NULL
,
714 *server_address
= NULL
, *next_server
= NULL
,
715 *dns
= NULL
, *ntp
= NULL
, *mtu
= NULL
,
716 *routes
= NULL
, *client_id_hex
= NULL
;
723 r
= dhcp_lease_new(&lease
);
727 r
= parse_env_file(lease_file
, NEWLINE
,
731 "SERVER_IDENTIFIER", &server_address
,
732 "NEXT_SERVER", &next_server
,
736 "DOMAINNAME", &lease
->domainname
,
737 "HOSTNAME", &lease
->hostname
,
738 "ROOT_PATH", &lease
->root_path
,
740 "CLIENTID", &client_id_hex
,
746 return log_error_errno(r
, "Failed to read %s: %m", lease_file
);
749 r
= inet_pton(AF_INET
, address
, &addr
);
753 lease
->address
= addr
.s_addr
;
756 r
= inet_pton(AF_INET
, router
, &addr
);
760 lease
->router
= addr
.s_addr
;
763 r
= inet_pton(AF_INET
, netmask
, &addr
);
767 lease
->subnet_mask
= addr
.s_addr
;
769 if (server_address
) {
770 r
= inet_pton(AF_INET
, server_address
, &addr
);
774 lease
->server_address
= addr
.s_addr
;
778 r
= inet_pton(AF_INET
, next_server
, &addr
);
782 lease
->next_server
= addr
.s_addr
;
786 r
= deserialize_in_addrs(&lease
->dns
, dns
);
794 r
= deserialize_in_addrs(&lease
->ntp
, ntp
);
803 if (sscanf(mtu
, "%" SCNu16
, &u
) > 0)
808 r
= deserialize_dhcp_routes(&lease
->static_route
, &lease
->static_route_size
,
809 &lease
->static_route_allocated
, routes
);
815 if (strlen (client_id_hex
) % 2)
818 lease
->client_id
= unhexmem (client_id_hex
, strlen (client_id_hex
));
819 if (!lease
->client_id
)
821 lease
->client_id_len
= strlen (client_id_hex
) / 2;
830 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease
*lease
) {
831 struct in_addr address
;
837 address
.s_addr
= lease
->address
;
839 /* fall back to the default subnet masks based on address class */
840 r
= in_addr_default_subnet_mask(&address
, &mask
);
844 lease
->subnet_mask
= mask
.s_addr
;
849 int sd_dhcp_lease_get_client_id(sd_dhcp_lease
*lease
, const uint8_t **client_id
,
850 size_t *client_id_len
) {
851 assert_return(lease
, -EINVAL
);
852 assert_return(client_id
, -EINVAL
);
853 assert_return(client_id_len
, -EINVAL
);
855 *client_id
= lease
->client_id
;
856 *client_id_len
= lease
->client_id_len
;
860 int dhcp_lease_set_client_id(sd_dhcp_lease
*lease
, const uint8_t *client_id
,
861 size_t client_id_len
) {
862 assert_return(lease
, -EINVAL
);
863 assert_return((!client_id
&& !client_id_len
) ||
864 (client_id
&& client_id_len
), -EINVAL
);
866 free (lease
->client_id
);
867 lease
->client_id
= NULL
;
868 lease
->client_id_len
= 0;
871 lease
->client_id
= memdup (client_id
, client_id_len
);
872 lease
->client_id_len
= client_id_len
;