1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "dns-domain.h"
29 #include "hostname-util.h"
30 #include "network-internal.h"
31 #include "networkd-network.h"
33 #include "string-util.h"
36 static int network_load_one(Manager
*manager
, const char *filename
) {
37 _cleanup_network_free_ Network
*network
= NULL
;
38 _cleanup_fclose_
FILE *file
= NULL
;
47 file
= fopen(filename
, "re");
55 if (null_or_empty_fd(fileno(file
))) {
56 log_debug("Skipping empty file: %s", filename
);
60 network
= new0(Network
, 1);
64 network
->manager
= manager
;
66 LIST_HEAD_INIT(network
->static_addresses
);
67 LIST_HEAD_INIT(network
->static_routes
);
68 LIST_HEAD_INIT(network
->static_fdb_entries
);
70 network
->stacked_netdevs
= hashmap_new(&string_hash_ops
);
71 if (!network
->stacked_netdevs
)
74 network
->addresses_by_section
= hashmap_new(NULL
);
75 if (!network
->addresses_by_section
)
78 network
->routes_by_section
= hashmap_new(NULL
);
79 if (!network
->routes_by_section
)
82 network
->fdb_entries_by_section
= hashmap_new(NULL
);
83 if (!network
->fdb_entries_by_section
)
86 network
->filename
= strdup(filename
);
87 if (!network
->filename
)
90 network
->name
= strdup(basename(filename
));
94 d
= strrchr(network
->name
, '.');
98 assert(streq(d
, ".network"));
102 network
->dhcp
= ADDRESS_FAMILY_NO
;
103 network
->dhcp_ntp
= true;
104 network
->dhcp_dns
= true;
105 network
->dhcp_hostname
= true;
106 network
->dhcp_routes
= true;
107 network
->dhcp_sendhost
= true;
108 network
->dhcp_route_metric
= DHCP_ROUTE_METRIC
;
109 network
->dhcp_client_identifier
= DHCP_CLIENT_ID_DUID
;
111 network
->dhcp_server_emit_dns
= true;
112 network
->dhcp_server_emit_ntp
= true;
113 network
->dhcp_server_emit_timezone
= true;
115 network
->use_bpdu
= true;
116 network
->allow_port_to_be_root
= true;
117 network
->unicast_flood
= true;
119 network
->llmnr
= RESOLVE_SUPPORT_YES
;
121 network
->link_local
= ADDRESS_FAMILY_IPV6
;
123 network
->ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_NO
;
124 network
->ipv6_accept_ra
= -1;
125 network
->ipv6_dad_transmits
= -1;
127 r
= config_parse(NULL
, filename
, file
,
134 "DHCPv4\0" /* compat */
138 config_item_perf_lookup
, network_network_gperf_lookup
,
139 false, false, true, network
);
143 /* IPMasquerade=yes implies IPForward=yes */
144 if (network
->ip_masquerade
)
145 network
->ip_forward
|= ADDRESS_FAMILY_IPV4
;
147 LIST_PREPEND(networks
, manager
->networks
, network
);
149 r
= hashmap_ensure_allocated(&manager
->networks_by_name
, &string_hash_ops
);
153 r
= hashmap_put(manager
->networks_by_name
, network
->name
, network
);
157 LIST_FOREACH(routes
, route
, network
->static_routes
) {
158 if (!route
->family
) {
159 log_warning("Route section without Gateway field configured in %s. "
160 "Ignoring", filename
);
165 LIST_FOREACH(addresses
, address
, network
->static_addresses
) {
166 if (!address
->family
) {
167 log_warning("Address section without Address field configured in %s. "
168 "Ignoring", filename
);
178 int network_load(Manager
*manager
) {
180 _cleanup_strv_free_
char **files
= NULL
;
186 while ((network
= manager
->networks
))
187 network_free(network
);
189 r
= conf_files_list_strv(&files
, ".network", NULL
, network_dirs
);
191 return log_error_errno(r
, "Failed to enumerate network files: %m");
193 STRV_FOREACH_BACKWARDS(f
, files
) {
194 r
= network_load_one(manager
, *f
);
202 void network_free(Network
*network
) {
212 free(network
->filename
);
214 free(network
->match_mac
);
215 strv_free(network
->match_path
);
216 strv_free(network
->match_driver
);
217 strv_free(network
->match_type
);
218 strv_free(network
->match_name
);
220 free(network
->description
);
221 free(network
->dhcp_vendor_class_identifier
);
222 free(network
->hostname
);
226 strv_free(network
->ntp
);
227 strv_free(network
->dns
);
228 strv_free(network
->domains
);
229 strv_free(network
->bind_carrier
);
231 netdev_unref(network
->bridge
);
233 netdev_unref(network
->bond
);
235 HASHMAP_FOREACH(netdev
, network
->stacked_netdevs
, i
) {
236 hashmap_remove(network
->stacked_netdevs
, netdev
->ifname
);
237 netdev_unref(netdev
);
239 hashmap_free(network
->stacked_netdevs
);
241 while ((route
= network
->static_routes
))
244 while ((address
= network
->static_addresses
))
245 address_free(address
);
247 while ((fdb_entry
= network
->static_fdb_entries
))
248 fdb_entry_free(fdb_entry
);
250 hashmap_free(network
->addresses_by_section
);
251 hashmap_free(network
->routes_by_section
);
252 hashmap_free(network
->fdb_entries_by_section
);
254 if (network
->manager
) {
255 if (network
->manager
->networks
)
256 LIST_REMOVE(networks
, network
->manager
->networks
, network
);
258 if (network
->manager
->networks_by_name
)
259 hashmap_remove(network
->manager
->networks_by_name
, network
->name
);
264 condition_free_list(network
->match_host
);
265 condition_free_list(network
->match_virt
);
266 condition_free_list(network
->match_kernel
);
267 condition_free_list(network
->match_arch
);
269 free(network
->dhcp_server_timezone
);
270 free(network
->dhcp_server_dns
);
271 free(network
->dhcp_server_ntp
);
276 int network_get_by_name(Manager
*manager
, const char *name
, Network
**ret
) {
283 network
= hashmap_get(manager
->networks_by_name
, name
);
292 int network_get(Manager
*manager
, struct udev_device
*device
,
293 const char *ifname
, const struct ether_addr
*address
,
296 struct udev_device
*parent
;
297 const char *path
= NULL
, *parent_driver
= NULL
, *driver
= NULL
, *devtype
= NULL
;
303 path
= udev_device_get_property_value(device
, "ID_PATH");
305 parent
= udev_device_get_parent(device
);
307 parent_driver
= udev_device_get_driver(parent
);
309 driver
= udev_device_get_property_value(device
, "ID_NET_DRIVER");
311 devtype
= udev_device_get_devtype(device
);
314 LIST_FOREACH(networks
, network
, manager
->networks
) {
315 if (net_match_config(network
->match_mac
, network
->match_path
,
316 network
->match_driver
, network
->match_type
,
317 network
->match_name
, network
->match_host
,
318 network
->match_virt
, network
->match_kernel
,
320 address
, path
, parent_driver
, driver
,
322 if (network
->match_name
&& device
) {
324 uint8_t name_assign_type
= NET_NAME_UNKNOWN
;
326 attr
= udev_device_get_sysattr_value(device
, "name_assign_type");
328 (void) safe_atou8(attr
, &name_assign_type
);
330 if (name_assign_type
== NET_NAME_ENUM
)
331 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
332 ifname
, network
->filename
);
334 log_debug("%s: found matching network '%s'", ifname
, network
->filename
);
336 log_debug("%s: found matching network '%s'", ifname
, network
->filename
);
348 int network_apply(Manager
*manager
, Network
*network
, Link
*link
) {
351 link
->network
= network
;
353 if (network
->ipv4ll_route
) {
356 r
= route_new_static(network
, 0, &route
);
360 r
= inet_pton(AF_INET
, "169.254.0.0", &route
->dst_addr
.in
);
366 route
->family
= AF_INET
;
367 route
->dst_prefixlen
= 16;
368 route
->scope
= RT_SCOPE_LINK
;
369 route
->metrics
= IPV4LL_ROUTE_METRIC
;
370 route
->protocol
= RTPROT_STATIC
;
373 if (network
->dns
|| network
->ntp
|| network
->domains
) {
374 manager_dirty(manager
);
381 int config_parse_netdev(const char *unit
,
382 const char *filename
,
385 unsigned section_line
,
391 Network
*network
= userdata
;
392 _cleanup_free_
char *kind_string
= NULL
;
403 kind_string
= strdup(lvalue
);
407 /* the keys are CamelCase versions of the kind */
408 for (p
= kind_string
; *p
; p
++)
411 kind
= netdev_kind_from_string(kind_string
);
412 if (kind
== _NETDEV_KIND_INVALID
) {
413 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid NetDev kind: %s", lvalue
);
417 r
= netdev_get(network
->manager
, rvalue
, &netdev
);
419 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "%s could not be found, ignoring assignment: %s", lvalue
, rvalue
);
423 if (netdev
->kind
!= kind
) {
424 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue
, rvalue
);
429 case NETDEV_KIND_BRIDGE
:
430 network
->bridge
= netdev
;
433 case NETDEV_KIND_BOND
:
434 network
->bond
= netdev
;
437 case NETDEV_KIND_VLAN
:
438 case NETDEV_KIND_MACVLAN
:
439 case NETDEV_KIND_MACVTAP
:
440 case NETDEV_KIND_IPVLAN
:
441 case NETDEV_KIND_VXLAN
:
442 r
= hashmap_put(network
->stacked_netdevs
, netdev
->ifname
, netdev
);
444 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Can not add VLAN '%s' to network: %m", rvalue
);
450 assert_not_reached("Can not parse NetDev");
458 int config_parse_domains(const char *unit
,
459 const char *filename
,
462 unsigned section_line
,
468 Network
*network
= userdata
;
469 char ***domains
= data
;
473 r
= config_parse_strv(unit
, filename
, line
, section
, section_line
,
474 lvalue
, ltype
, rvalue
, domains
, userdata
);
479 network
->wildcard_domain
= !!strv_find(*domains
, "*");
481 STRV_FOREACH(domain
, *domains
) {
482 if (is_localhost(*domain
))
483 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain
);
485 r
= dns_name_is_valid(*domain
);
486 if (r
<= 0 && !streq(*domain
, "*")) {
488 log_error_errno(r
, "Failed to validate domain name: %s: %m", *domain
);
490 log_warning("Domain name is not valid, ignoring assignment: %s", *domain
);
495 strv_remove(*domains
, *domain
);
497 /* We removed one entry, make sure we don't skip the next one */
504 int config_parse_tunnel(const char *unit
,
505 const char *filename
,
508 unsigned section_line
,
514 Network
*network
= userdata
;
523 r
= netdev_get(network
->manager
, rvalue
, &netdev
);
525 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Tunnel is invalid, ignoring assignment: %s", rvalue
);
529 if (netdev
->kind
!= NETDEV_KIND_IPIP
&&
530 netdev
->kind
!= NETDEV_KIND_SIT
&&
531 netdev
->kind
!= NETDEV_KIND_GRE
&&
532 netdev
->kind
!= NETDEV_KIND_GRETAP
&&
533 netdev
->kind
!= NETDEV_KIND_IP6GRE
&&
534 netdev
->kind
!= NETDEV_KIND_IP6GRETAP
&&
535 netdev
->kind
!= NETDEV_KIND_VTI
&&
536 netdev
->kind
!= NETDEV_KIND_VTI6
&&
537 netdev
->kind
!= NETDEV_KIND_IP6TNL
539 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
540 "NetDev is not a tunnel, ignoring assignment: %s", rvalue
);
544 r
= hashmap_put(network
->stacked_netdevs
, netdev
->ifname
, netdev
);
546 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue
);
555 int config_parse_ipv4ll(
557 const char *filename
,
560 unsigned section_line
,
567 AddressFamilyBoolean
*link_local
= data
;
574 /* Note that this is mostly like
575 * config_parse_address_family_boolean(), except that it
576 * applies only to IPv4 */
578 if (parse_boolean(rvalue
))
579 *link_local
|= ADDRESS_FAMILY_IPV4
;
581 *link_local
&= ~ADDRESS_FAMILY_IPV4
;
586 int config_parse_dhcp(
588 const char *filename
,
591 unsigned section_line
,
598 AddressFamilyBoolean
*dhcp
= data
, s
;
605 /* Note that this is mostly like
606 * config_parse_address_family_boolean(), except that it
607 * understands some old names for the enum values */
609 s
= address_family_boolean_from_string(rvalue
);
612 /* Previously, we had a slightly different enum here,
613 * support its values for compatbility. */
615 if (streq(rvalue
, "none"))
616 s
= ADDRESS_FAMILY_NO
;
617 else if (streq(rvalue
, "v4"))
618 s
= ADDRESS_FAMILY_IPV4
;
619 else if (streq(rvalue
, "v6"))
620 s
= ADDRESS_FAMILY_IPV6
;
621 else if (streq(rvalue
, "both"))
622 s
= ADDRESS_FAMILY_YES
;
624 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse DHCP option, ignoring: %s", rvalue
);
633 static const char* const dhcp_client_identifier_table
[_DHCP_CLIENT_ID_MAX
] = {
634 [DHCP_CLIENT_ID_MAC
] = "mac",
635 [DHCP_CLIENT_ID_DUID
] = "duid"
638 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier
, DCHPClientIdentifier
);
639 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier
, dhcp_client_identifier
, DCHPClientIdentifier
, "Failed to parse client identifier type");
641 int config_parse_ipv6token(
643 const char *filename
,
646 unsigned section_line
,
653 union in_addr_union buffer
;
654 struct in6_addr
*token
= data
;
662 r
= in_addr_from_string(AF_INET6
, rvalue
, &buffer
);
664 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IPv6 token, ignoring: %s", rvalue
);
668 r
= in_addr_is_null(AF_INET6
, &buffer
);
670 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IPv6 token can not be the ANY address, ignoring: %s", rvalue
);
674 if ((buffer
.in6
.s6_addr32
[0] | buffer
.in6
.s6_addr32
[1]) != 0) {
675 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue
);
684 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
685 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
686 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
687 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
690 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions
, IPv6PrivacyExtensions
);
692 int config_parse_ipv6_privacy_extensions(
694 const char *filename
,
697 unsigned section_line
,
704 IPv6PrivacyExtensions
*ipv6_privacy_extensions
= data
;
710 assert(ipv6_privacy_extensions
);
712 /* Our enum shall be a superset of booleans, hence first try
713 * to parse as boolean, and then as enum */
715 k
= parse_boolean(rvalue
);
717 *ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_YES
;
719 *ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_NO
;
721 IPv6PrivacyExtensions s
;
723 s
= ipv6_privacy_extensions_from_string(rvalue
);
726 if (streq(rvalue
, "kernel"))
727 s
= _IPV6_PRIVACY_EXTENSIONS_INVALID
;
729 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue
);
734 *ipv6_privacy_extensions
= s
;
740 int config_parse_hostname(
742 const char *filename
,
745 unsigned section_line
,
752 char **hostname
= data
, *hn
= NULL
;
759 r
= config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, &hn
, userdata
);
763 if (!hostname_is_valid(hn
, false)) {
764 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Hostname is not valid, ignoring assignment: %s", rvalue
);
770 *hostname
= hostname_cleanup(hn
);
774 int config_parse_timezone(
776 const char *filename
,
779 unsigned section_line
,
786 char **datap
= data
, *tz
= NULL
;
793 r
= config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, &tz
, userdata
);
797 if (!timezone_is_valid(tz
)) {
798 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Timezone is not valid, ignoring assignment: %s", rvalue
);
809 int config_parse_dhcp_server_dns(
811 const char *filename
,
814 unsigned section_line
,
822 const char *p
= rvalue
;
830 _cleanup_free_
char *w
= NULL
;
831 struct in_addr a
, *m
;
833 r
= extract_first_word(&p
, &w
, NULL
, 0);
835 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract word, ignoring: %s", rvalue
);
842 if (inet_pton(AF_INET
, w
, &a
) <= 0) {
843 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse DNS server address, ignoring: %s", w
);
847 m
= realloc(n
->dhcp_server_dns
, (n
->n_dhcp_server_dns
+ 1) * sizeof(struct in_addr
));
851 m
[n
->n_dhcp_server_dns
++] = a
;
852 n
->dhcp_server_dns
= m
;
856 int config_parse_dhcp_server_ntp(
858 const char *filename
,
861 unsigned section_line
,
869 const char *p
= rvalue
;
877 _cleanup_free_
char *w
= NULL
;
878 struct in_addr a
, *m
;
880 r
= extract_first_word(&p
, &w
, NULL
, 0);
882 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract word, ignoring: %s", rvalue
);
889 if (inet_pton(AF_INET
, w
, &a
) <= 0) {
890 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse NTP server address, ignoring: %s", w
);
894 m
= realloc(n
->dhcp_server_ntp
, (n
->n_dhcp_server_ntp
+ 1) * sizeof(struct in_addr
));
898 m
[n
->n_dhcp_server_ntp
++] = a
;
899 n
->dhcp_server_ntp
= m
;