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"
29 #include "networkd-netdev.h"
30 #include "networkd-link.h"
31 #include "network-internal.h"
33 static int network_load_one(Manager *manager, const char *filename) {
34 _cleanup_network_free_ Network *network = NULL;
35 _cleanup_fclose_ FILE *file = NULL;
44 file = fopen(filename, "re");
52 if (null_or_empty_fd(fileno(file))) {
53 log_debug("Skipping empty file: %s", filename);
57 network = new0(Network, 1);
61 network->manager = manager;
63 LIST_HEAD_INIT(network->static_addresses);
64 LIST_HEAD_INIT(network->static_routes);
65 LIST_HEAD_INIT(network->static_fdb_entries);
67 network->stacked_netdevs = hashmap_new(&string_hash_ops);
68 if (!network->stacked_netdevs)
71 network->addresses_by_section = hashmap_new(NULL);
72 if (!network->addresses_by_section)
75 network->routes_by_section = hashmap_new(NULL);
76 if (!network->routes_by_section)
79 network->fdb_entries_by_section = hashmap_new(NULL);
80 if (!network->fdb_entries_by_section)
83 network->filename = strdup(filename);
84 if (!network->filename)
87 network->name = strdup(basename(filename));
91 d = strrchr(network->name, '.');
95 assert(streq(d, ".network"));
99 network->dhcp = ADDRESS_FAMILY_NO;
100 network->dhcp_ntp = true;
101 network->dhcp_dns = true;
102 network->dhcp_hostname = true;
103 network->dhcp_routes = true;
104 network->dhcp_sendhost = true;
105 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
107 network->llmnr = LLMNR_SUPPORT_YES;
109 network->link_local = ADDRESS_FAMILY_IPV6;
111 r = config_parse(NULL, filename, file,
121 config_item_perf_lookup, network_network_gperf_lookup,
122 false, false, true, network);
126 /* IPMasquerade=yes implies IPForward=yes */
127 if (network->ip_masquerade)
128 network->ip_forward |= ADDRESS_FAMILY_IPV4;
130 LIST_PREPEND(networks, manager->networks, network);
132 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
136 r = hashmap_put(manager->networks_by_name, network->name, network);
140 LIST_FOREACH(routes, route, network->static_routes) {
141 if (!route->family) {
142 log_warning("Route section without Gateway field configured in %s. "
143 "Ignoring", filename);
148 LIST_FOREACH(addresses, address, network->static_addresses) {
149 if (!address->family) {
150 log_warning("Address section without Address field configured in %s. "
151 "Ignoring", filename);
161 int network_load(Manager *manager) {
163 _cleanup_strv_free_ char **files = NULL;
169 while ((network = manager->networks))
170 network_free(network);
172 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
174 return log_error_errno(r, "Failed to enumerate network files: %m");
176 STRV_FOREACH_BACKWARDS(f, files) {
177 r = network_load_one(manager, *f);
185 void network_free(Network *network) {
195 free(network->filename);
197 free(network->match_mac);
198 strv_free(network->match_path);
199 strv_free(network->match_driver);
200 strv_free(network->match_type);
201 strv_free(network->match_name);
203 free(network->description);
204 free(network->dhcp_vendor_class_identifier);
208 strv_free(network->ntp);
209 strv_free(network->dns);
210 strv_free(network->domains);
212 netdev_unref(network->bridge);
214 netdev_unref(network->bond);
216 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
217 hashmap_remove(network->stacked_netdevs, netdev->ifname);
218 netdev_unref(netdev);
220 hashmap_free(network->stacked_netdevs);
222 while ((route = network->static_routes))
225 while ((address = network->static_addresses))
226 address_free(address);
228 while ((fdb_entry = network->static_fdb_entries))
229 fdb_entry_free(fdb_entry);
231 hashmap_free(network->addresses_by_section);
232 hashmap_free(network->routes_by_section);
233 hashmap_free(network->fdb_entries_by_section);
235 if (network->manager) {
236 if (network->manager->networks)
237 LIST_REMOVE(networks, network->manager->networks, network);
239 if (network->manager->networks_by_name)
240 hashmap_remove(network->manager->networks_by_name, network->name);
245 condition_free_list(network->match_host);
246 condition_free_list(network->match_virt);
247 condition_free_list(network->match_kernel);
248 condition_free_list(network->match_arch);
253 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
260 network = hashmap_get(manager->networks_by_name, name);
269 int network_get(Manager *manager, struct udev_device *device,
270 const char *ifname, const struct ether_addr *address,
277 LIST_FOREACH(networks, network, manager->networks) {
278 if (net_match_config(network->match_mac, network->match_path,
279 network->match_driver, network->match_type,
280 network->match_name, network->match_host,
281 network->match_virt, network->match_kernel,
284 udev_device_get_property_value(device, "ID_PATH"),
285 udev_device_get_driver(udev_device_get_parent(device)),
286 udev_device_get_property_value(device, "ID_NET_DRIVER"),
287 udev_device_get_devtype(device),
289 if (network->match_name) {
291 uint8_t name_assign_type = NET_NAME_UNKNOWN;
293 attr = udev_device_get_sysattr_value(device, "name_assign_type");
295 (void)safe_atou8(attr, &name_assign_type);
297 if (name_assign_type == NET_NAME_ENUM)
298 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
299 IFNAMSIZ, ifname, network->filename);
301 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
303 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
315 int network_apply(Manager *manager, Network *network, Link *link) {
318 link->network = network;
320 if (network->ipv4ll_route) {
323 r = route_new_static(network, 0, &route);
327 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
333 route->family = AF_INET;
334 route->dst_prefixlen = 16;
335 route->scope = RT_SCOPE_LINK;
336 route->metrics = IPV4LL_ROUTE_METRIC;
337 route->protocol = RTPROT_STATIC;
340 if (network->dns || network->ntp) {
349 int config_parse_netdev(const char *unit,
350 const char *filename,
353 unsigned section_line,
359 Network *network = userdata;
360 _cleanup_free_ char *kind_string = NULL;
371 kind_string = strdup(lvalue);
375 /* the keys are CamelCase versions of the kind */
376 for (p = kind_string; *p; p++)
379 kind = netdev_kind_from_string(kind_string);
380 if (kind == _NETDEV_KIND_INVALID) {
381 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
382 "Invalid NetDev kind: %s", lvalue);
386 r = netdev_get(network->manager, rvalue, &netdev);
388 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
389 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
393 if (netdev->kind != kind) {
394 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
395 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
400 case NETDEV_KIND_BRIDGE:
401 network->bridge = netdev;
404 case NETDEV_KIND_BOND:
405 network->bond = netdev;
408 case NETDEV_KIND_VLAN:
409 case NETDEV_KIND_MACVLAN:
410 case NETDEV_KIND_IPVLAN:
411 case NETDEV_KIND_VXLAN:
412 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
414 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
415 "Can not add VLAN '%s' to network: %s",
416 rvalue, strerror(-r));
422 assert_not_reached("Can not parse NetDev");
430 int config_parse_domains(const char *unit,
431 const char *filename,
434 unsigned section_line,
440 Network *network = userdata;
441 char ***domains = data;
445 r = config_parse_strv(unit, filename, line, section, section_line,
446 lvalue, ltype, rvalue, domains, userdata);
451 network->wildcard_domain = !!strv_find(*domains, "*");
453 STRV_FOREACH(domain, *domains) {
454 if (is_localhost(*domain))
455 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
456 else if (!hostname_is_valid(*domain)) {
457 if (!streq(*domain, "*"))
458 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
462 strv_remove(*domains, *domain);
464 /* We removed one entry, make sure we don't skip the next one */
471 int config_parse_tunnel(const char *unit,
472 const char *filename,
475 unsigned section_line,
481 Network *network = userdata;
490 r = netdev_get(network->manager, rvalue, &netdev);
492 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
493 "Tunnel is invalid, ignoring assignment: %s", rvalue);
497 if (netdev->kind != NETDEV_KIND_IPIP &&
498 netdev->kind != NETDEV_KIND_SIT &&
499 netdev->kind != NETDEV_KIND_GRE &&
500 netdev->kind != NETDEV_KIND_GRETAP &&
501 netdev->kind != NETDEV_KIND_IP6GRE &&
502 netdev->kind != NETDEV_KIND_IP6GRETAP &&
503 netdev->kind != NETDEV_KIND_VTI &&
504 netdev->kind != NETDEV_KIND_IP6TNL
506 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
507 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
511 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
513 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
514 "Can not add VLAN '%s' to network: %s",
515 rvalue, strerror(-r));
524 int config_parse_ipv4ll(
526 const char *filename,
529 unsigned section_line,
536 AddressFamilyBoolean *link_local = data;
543 /* Note that this is mostly like
544 * config_parse_address_family_boolean(), except that it
545 * applies only to IPv4 */
547 if (parse_boolean(rvalue))
548 *link_local |= ADDRESS_FAMILY_IPV4;
550 *link_local &= ~ADDRESS_FAMILY_IPV4;
555 int config_parse_dhcp(
557 const char *filename,
560 unsigned section_line,
567 AddressFamilyBoolean *dhcp = data, s;
574 /* Note that this is mostly like
575 * config_parse_address_family_boolean(), except that it
576 * understands some old names for the enum values */
578 s = address_family_boolean_from_string(rvalue);
581 /* Previously, we had a slightly different enum here,
582 * support its values for compatbility. */
584 if (streq(rvalue, "none"))
585 s = ADDRESS_FAMILY_NO;
586 else if (streq(rvalue, "v4"))
587 s = ADDRESS_FAMILY_IPV4;
588 else if (streq(rvalue, "v6"))
589 s = ADDRESS_FAMILY_IPV6;
590 else if (streq(rvalue, "both"))
591 s = ADDRESS_FAMILY_YES;
593 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
602 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
603 [LLMNR_SUPPORT_NO] = "no",
604 [LLMNR_SUPPORT_YES] = "yes",
605 [LLMNR_SUPPORT_RESOLVE] = "resolve",
608 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
610 int config_parse_llmnr(
612 const char *filename,
615 unsigned section_line,
622 LLMNRSupport *llmnr = data;
630 /* Our enum shall be a superset of booleans, hence first try
631 * to parse as boolean, and then as enum */
633 k = parse_boolean(rvalue);
635 *llmnr = LLMNR_SUPPORT_YES;
637 *llmnr = LLMNR_SUPPORT_NO;
641 s = llmnr_support_from_string(rvalue);
643 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
653 int config_parse_ipv6token(
655 const char *filename,
658 unsigned section_line,
665 union in_addr_union buffer;
666 struct in6_addr *token = data;
674 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
676 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
680 r = in_addr_is_null(AF_INET6, &buffer);
682 log_syntax(unit, LOG_ERR, filename, line, -r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
686 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
687 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);