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"
28 #include "hostname-util.h"
29 #include "network-internal.h"
31 #include "string-util.h"
33 #include "networkd-network.h"
35 static int network_load_one(Manager
*manager
, const char *filename
) {
36 _cleanup_network_free_ Network
*network
= NULL
;
37 _cleanup_fclose_
FILE *file
= NULL
;
46 file
= fopen(filename
, "re");
54 if (null_or_empty_fd(fileno(file
))) {
55 log_debug("Skipping empty file: %s", filename
);
59 network
= new0(Network
, 1);
63 network
->manager
= manager
;
65 LIST_HEAD_INIT(network
->static_addresses
);
66 LIST_HEAD_INIT(network
->static_routes
);
67 LIST_HEAD_INIT(network
->static_fdb_entries
);
69 network
->stacked_netdevs
= hashmap_new(&string_hash_ops
);
70 if (!network
->stacked_netdevs
)
73 network
->addresses_by_section
= hashmap_new(NULL
);
74 if (!network
->addresses_by_section
)
77 network
->routes_by_section
= hashmap_new(NULL
);
78 if (!network
->routes_by_section
)
81 network
->fdb_entries_by_section
= hashmap_new(NULL
);
82 if (!network
->fdb_entries_by_section
)
85 network
->filename
= strdup(filename
);
86 if (!network
->filename
)
89 network
->name
= strdup(basename(filename
));
93 d
= strrchr(network
->name
, '.');
97 assert(streq(d
, ".network"));
101 network
->dhcp
= ADDRESS_FAMILY_NO
;
102 network
->dhcp_ntp
= true;
103 network
->dhcp_dns
= true;
104 network
->dhcp_hostname
= true;
105 network
->dhcp_routes
= true;
106 network
->dhcp_sendhost
= true;
107 network
->dhcp_route_metric
= DHCP_ROUTE_METRIC
;
108 network
->dhcp_client_identifier
= DHCP_CLIENT_ID_DUID
;
110 network
->dhcp_server_emit_dns
= true;
111 network
->dhcp_server_emit_ntp
= true;
112 network
->dhcp_server_emit_timezone
= true;
114 network
->use_bpdu
= true;
115 network
->allow_port_to_be_root
= true;
116 network
->unicast_flood
= true;
118 network
->llmnr
= RESOLVE_SUPPORT_YES
;
120 network
->link_local
= ADDRESS_FAMILY_IPV6
;
122 network
->ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_NO
;
123 network
->ipv6_accept_ra
= -1;
124 network
->ipv6_dad_transmits
= -1;
126 r
= config_parse(NULL
, filename
, file
,
133 "DHCPv4\0" /* compat */
137 config_item_perf_lookup
, network_network_gperf_lookup
,
138 false, false, true, network
);
142 /* IPMasquerade=yes implies IPForward=yes */
143 if (network
->ip_masquerade
)
144 network
->ip_forward
|= ADDRESS_FAMILY_IPV4
;
146 LIST_PREPEND(networks
, manager
->networks
, network
);
148 r
= hashmap_ensure_allocated(&manager
->networks_by_name
, &string_hash_ops
);
152 r
= hashmap_put(manager
->networks_by_name
, network
->name
, network
);
156 LIST_FOREACH(routes
, route
, network
->static_routes
) {
157 if (!route
->family
) {
158 log_warning("Route section without Gateway field configured in %s. "
159 "Ignoring", filename
);
164 LIST_FOREACH(addresses
, address
, network
->static_addresses
) {
165 if (!address
->family
) {
166 log_warning("Address section without Address field configured in %s. "
167 "Ignoring", filename
);
177 int network_load(Manager
*manager
) {
179 _cleanup_strv_free_
char **files
= NULL
;
185 while ((network
= manager
->networks
))
186 network_free(network
);
188 r
= conf_files_list_strv(&files
, ".network", NULL
, network_dirs
);
190 return log_error_errno(r
, "Failed to enumerate network files: %m");
192 STRV_FOREACH_BACKWARDS(f
, files
) {
193 r
= network_load_one(manager
, *f
);
201 void network_free(Network
*network
) {
211 free(network
->filename
);
213 free(network
->match_mac
);
214 strv_free(network
->match_path
);
215 strv_free(network
->match_driver
);
216 strv_free(network
->match_type
);
217 strv_free(network
->match_name
);
219 free(network
->description
);
220 free(network
->dhcp_vendor_class_identifier
);
221 free(network
->hostname
);
225 strv_free(network
->ntp
);
226 strv_free(network
->dns
);
227 strv_free(network
->domains
);
228 strv_free(network
->bind_carrier
);
230 netdev_unref(network
->bridge
);
232 netdev_unref(network
->bond
);
234 HASHMAP_FOREACH(netdev
, network
->stacked_netdevs
, i
) {
235 hashmap_remove(network
->stacked_netdevs
, netdev
->ifname
);
236 netdev_unref(netdev
);
238 hashmap_free(network
->stacked_netdevs
);
240 while ((route
= network
->static_routes
))
243 while ((address
= network
->static_addresses
))
244 address_free(address
);
246 while ((fdb_entry
= network
->static_fdb_entries
))
247 fdb_entry_free(fdb_entry
);
249 hashmap_free(network
->addresses_by_section
);
250 hashmap_free(network
->routes_by_section
);
251 hashmap_free(network
->fdb_entries_by_section
);
253 if (network
->manager
) {
254 if (network
->manager
->networks
)
255 LIST_REMOVE(networks
, network
->manager
->networks
, network
);
257 if (network
->manager
->networks_by_name
)
258 hashmap_remove(network
->manager
->networks_by_name
, network
->name
);
263 condition_free_list(network
->match_host
);
264 condition_free_list(network
->match_virt
);
265 condition_free_list(network
->match_kernel
);
266 condition_free_list(network
->match_arch
);
268 free(network
->dhcp_server_timezone
);
269 free(network
->dhcp_server_dns
);
270 free(network
->dhcp_server_ntp
);
275 int network_get_by_name(Manager
*manager
, const char *name
, Network
**ret
) {
282 network
= hashmap_get(manager
->networks_by_name
, name
);
291 int network_get(Manager
*manager
, struct udev_device
*device
,
292 const char *ifname
, const struct ether_addr
*address
,
295 struct udev_device
*parent
;
296 const char *path
= NULL
, *parent_driver
= NULL
, *driver
= NULL
, *devtype
= NULL
;
302 path
= udev_device_get_property_value(device
, "ID_PATH");
304 parent
= udev_device_get_parent(device
);
306 parent_driver
= udev_device_get_driver(parent
);
308 driver
= udev_device_get_property_value(device
, "ID_NET_DRIVER");
310 devtype
= udev_device_get_devtype(device
);
313 LIST_FOREACH(networks
, network
, manager
->networks
) {
314 if (net_match_config(network
->match_mac
, network
->match_path
,
315 network
->match_driver
, network
->match_type
,
316 network
->match_name
, network
->match_host
,
317 network
->match_virt
, network
->match_kernel
,
319 address
, path
, parent_driver
, driver
,
321 if (network
->match_name
&& device
) {
323 uint8_t name_assign_type
= NET_NAME_UNKNOWN
;
325 attr
= udev_device_get_sysattr_value(device
, "name_assign_type");
327 (void) safe_atou8(attr
, &name_assign_type
);
329 if (name_assign_type
== NET_NAME_ENUM
)
330 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
331 ifname
, network
->filename
);
333 log_debug("%s: found matching network '%s'", ifname
, network
->filename
);
335 log_debug("%s: found matching network '%s'", ifname
, network
->filename
);
347 int network_apply(Manager
*manager
, Network
*network
, Link
*link
) {
350 link
->network
= network
;
352 if (network
->ipv4ll_route
) {
355 r
= route_new_static(network
, 0, &route
);
359 r
= inet_pton(AF_INET
, "169.254.0.0", &route
->dst_addr
.in
);
365 route
->family
= AF_INET
;
366 route
->dst_prefixlen
= 16;
367 route
->scope
= RT_SCOPE_LINK
;
368 route
->metrics
= IPV4LL_ROUTE_METRIC
;
369 route
->protocol
= RTPROT_STATIC
;
372 if (network
->dns
|| network
->ntp
|| network
->domains
) {
373 manager_dirty(manager
);
380 int config_parse_netdev(const char *unit
,
381 const char *filename
,
384 unsigned section_line
,
390 Network
*network
= userdata
;
391 _cleanup_free_
char *kind_string
= NULL
;
402 kind_string
= strdup(lvalue
);
406 /* the keys are CamelCase versions of the kind */
407 for (p
= kind_string
; *p
; p
++)
410 kind
= netdev_kind_from_string(kind_string
);
411 if (kind
== _NETDEV_KIND_INVALID
) {
412 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid NetDev kind: %s", lvalue
);
416 r
= netdev_get(network
->manager
, rvalue
, &netdev
);
418 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "%s could not be found, ignoring assignment: %s", lvalue
, rvalue
);
422 if (netdev
->kind
!= kind
) {
423 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue
, rvalue
);
428 case NETDEV_KIND_BRIDGE
:
429 network
->bridge
= netdev
;
432 case NETDEV_KIND_BOND
:
433 network
->bond
= netdev
;
436 case NETDEV_KIND_VLAN
:
437 case NETDEV_KIND_MACVLAN
:
438 case NETDEV_KIND_MACVTAP
:
439 case NETDEV_KIND_IPVLAN
:
440 case NETDEV_KIND_VXLAN
:
441 r
= hashmap_put(network
->stacked_netdevs
, netdev
->ifname
, netdev
);
443 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Can not add VLAN '%s' to network: %m", rvalue
);
449 assert_not_reached("Can not parse NetDev");
457 int config_parse_domains(const char *unit
,
458 const char *filename
,
461 unsigned section_line
,
467 Network
*network
= userdata
;
468 char ***domains
= data
;
472 r
= config_parse_strv(unit
, filename
, line
, section
, section_line
,
473 lvalue
, ltype
, rvalue
, domains
, userdata
);
478 network
->wildcard_domain
= !!strv_find(*domains
, "*");
480 STRV_FOREACH(domain
, *domains
) {
481 if (is_localhost(*domain
))
482 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain
);
484 r
= dns_name_is_valid(*domain
);
485 if (r
<= 0 && !streq(*domain
, "*")) {
487 log_error_errno(r
, "Failed to validate domain name: %s: %m", *domain
);
489 log_warning("Domain name is not valid, ignoring assignment: %s", *domain
);
494 strv_remove(*domains
, *domain
);
496 /* We removed one entry, make sure we don't skip the next one */
503 int config_parse_tunnel(const char *unit
,
504 const char *filename
,
507 unsigned section_line
,
513 Network
*network
= userdata
;
522 r
= netdev_get(network
->manager
, rvalue
, &netdev
);
524 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Tunnel is invalid, ignoring assignment: %s", rvalue
);
528 if (netdev
->kind
!= NETDEV_KIND_IPIP
&&
529 netdev
->kind
!= NETDEV_KIND_SIT
&&
530 netdev
->kind
!= NETDEV_KIND_GRE
&&
531 netdev
->kind
!= NETDEV_KIND_GRETAP
&&
532 netdev
->kind
!= NETDEV_KIND_IP6GRE
&&
533 netdev
->kind
!= NETDEV_KIND_IP6GRETAP
&&
534 netdev
->kind
!= NETDEV_KIND_VTI
&&
535 netdev
->kind
!= NETDEV_KIND_VTI6
&&
536 netdev
->kind
!= NETDEV_KIND_IP6TNL
538 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
539 "NetDev is not a tunnel, ignoring assignment: %s", rvalue
);
543 r
= hashmap_put(network
->stacked_netdevs
, netdev
->ifname
, netdev
);
545 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue
);
554 int config_parse_ipv4ll(
556 const char *filename
,
559 unsigned section_line
,
566 AddressFamilyBoolean
*link_local
= data
;
573 /* Note that this is mostly like
574 * config_parse_address_family_boolean(), except that it
575 * applies only to IPv4 */
577 if (parse_boolean(rvalue
))
578 *link_local
|= ADDRESS_FAMILY_IPV4
;
580 *link_local
&= ~ADDRESS_FAMILY_IPV4
;
585 int config_parse_dhcp(
587 const char *filename
,
590 unsigned section_line
,
597 AddressFamilyBoolean
*dhcp
= data
, s
;
604 /* Note that this is mostly like
605 * config_parse_address_family_boolean(), except that it
606 * understands some old names for the enum values */
608 s
= address_family_boolean_from_string(rvalue
);
611 /* Previously, we had a slightly different enum here,
612 * support its values for compatbility. */
614 if (streq(rvalue
, "none"))
615 s
= ADDRESS_FAMILY_NO
;
616 else if (streq(rvalue
, "v4"))
617 s
= ADDRESS_FAMILY_IPV4
;
618 else if (streq(rvalue
, "v6"))
619 s
= ADDRESS_FAMILY_IPV6
;
620 else if (streq(rvalue
, "both"))
621 s
= ADDRESS_FAMILY_YES
;
623 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse DHCP option, ignoring: %s", rvalue
);
632 static const char* const dhcp_client_identifier_table
[_DHCP_CLIENT_ID_MAX
] = {
633 [DHCP_CLIENT_ID_MAC
] = "mac",
634 [DHCP_CLIENT_ID_DUID
] = "duid"
637 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier
, DCHPClientIdentifier
);
638 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier
, dhcp_client_identifier
, DCHPClientIdentifier
, "Failed to parse client identifier type");
640 int config_parse_ipv6token(
642 const char *filename
,
645 unsigned section_line
,
652 union in_addr_union buffer
;
653 struct in6_addr
*token
= data
;
661 r
= in_addr_from_string(AF_INET6
, rvalue
, &buffer
);
663 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IPv6 token, ignoring: %s", rvalue
);
667 r
= in_addr_is_null(AF_INET6
, &buffer
);
669 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IPv6 token can not be the ANY address, ignoring: %s", rvalue
);
673 if ((buffer
.in6
.s6_addr32
[0] | buffer
.in6
.s6_addr32
[1]) != 0) {
674 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue
);
683 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
684 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
685 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
686 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
689 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions
, IPv6PrivacyExtensions
);
691 int config_parse_ipv6_privacy_extensions(
693 const char *filename
,
696 unsigned section_line
,
703 IPv6PrivacyExtensions
*ipv6_privacy_extensions
= data
;
709 assert(ipv6_privacy_extensions
);
711 /* Our enum shall be a superset of booleans, hence first try
712 * to parse as boolean, and then as enum */
714 k
= parse_boolean(rvalue
);
716 *ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_YES
;
718 *ipv6_privacy_extensions
= IPV6_PRIVACY_EXTENSIONS_NO
;
720 IPv6PrivacyExtensions s
;
722 s
= ipv6_privacy_extensions_from_string(rvalue
);
725 if (streq(rvalue
, "kernel"))
726 s
= _IPV6_PRIVACY_EXTENSIONS_INVALID
;
728 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue
);
733 *ipv6_privacy_extensions
= s
;
739 int config_parse_hostname(
741 const char *filename
,
744 unsigned section_line
,
751 char **hostname
= data
, *hn
= NULL
;
758 r
= config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, &hn
, userdata
);
762 if (!hostname_is_valid(hn
, false)) {
763 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Hostname is not valid, ignoring assignment: %s", rvalue
);
769 *hostname
= hostname_cleanup(hn
);
773 int config_parse_timezone(
775 const char *filename
,
778 unsigned section_line
,
785 char **datap
= data
, *tz
= NULL
;
792 r
= config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, &tz
, userdata
);
796 if (!timezone_is_valid(tz
)) {
797 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Timezone is not valid, ignoring assignment: %s", rvalue
);
808 int config_parse_dhcp_server_dns(
810 const char *filename
,
813 unsigned section_line
,
821 const char *p
= rvalue
;
829 _cleanup_free_
char *w
= NULL
;
830 struct in_addr a
, *m
;
832 r
= extract_first_word(&p
, &w
, NULL
, 0);
834 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract word, ignoring: %s", rvalue
);
841 if (inet_pton(AF_INET
, w
, &a
) <= 0) {
842 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse DNS server address, ignoring: %s", w
);
846 m
= realloc(n
->dhcp_server_dns
, (n
->n_dhcp_server_dns
+ 1) * sizeof(struct in_addr
));
850 m
[n
->n_dhcp_server_dns
++] = a
;
851 n
->dhcp_server_dns
= m
;
855 int config_parse_dhcp_server_ntp(
857 const char *filename
,
860 unsigned section_line
,
868 const char *p
= rvalue
;
876 _cleanup_free_
char *w
= NULL
;
877 struct in_addr a
, *m
;
879 r
= extract_first_word(&p
, &w
, NULL
, 0);
881 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract word, ignoring: %s", rvalue
);
888 if (inet_pton(AF_INET
, w
, &a
) <= 0) {
889 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse NTP server address, ignoring: %s", w
);
893 m
= realloc(n
->dhcp_server_ntp
, (n
->n_dhcp_server_ntp
+ 1) * sizeof(struct in_addr
));
897 m
[n
->n_dhcp_server_ntp
++] = a
;
898 n
->dhcp_server_ntp
= m
;