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);
211 strv_free(network->bind_carrier);
213 netdev_unref(network->bridge);
215 netdev_unref(network->bond);
217 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
218 hashmap_remove(network->stacked_netdevs, netdev->ifname);
219 netdev_unref(netdev);
221 hashmap_free(network->stacked_netdevs);
223 while ((route = network->static_routes))
226 while ((address = network->static_addresses))
227 address_free(address);
229 while ((fdb_entry = network->static_fdb_entries))
230 fdb_entry_free(fdb_entry);
232 hashmap_free(network->addresses_by_section);
233 hashmap_free(network->routes_by_section);
234 hashmap_free(network->fdb_entries_by_section);
236 if (network->manager) {
237 if (network->manager->networks)
238 LIST_REMOVE(networks, network->manager->networks, network);
240 if (network->manager->networks_by_name)
241 hashmap_remove(network->manager->networks_by_name, network->name);
246 condition_free_list(network->match_host);
247 condition_free_list(network->match_virt);
248 condition_free_list(network->match_kernel);
249 condition_free_list(network->match_arch);
254 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
261 network = hashmap_get(manager->networks_by_name, name);
270 int network_get(Manager *manager, struct udev_device *device,
271 const char *ifname, const struct ether_addr *address,
278 LIST_FOREACH(networks, network, manager->networks) {
279 if (net_match_config(network->match_mac, network->match_path,
280 network->match_driver, network->match_type,
281 network->match_name, network->match_host,
282 network->match_virt, network->match_kernel,
285 udev_device_get_property_value(device, "ID_PATH"),
286 udev_device_get_driver(udev_device_get_parent(device)),
287 udev_device_get_property_value(device, "ID_NET_DRIVER"),
288 udev_device_get_devtype(device),
290 if (network->match_name) {
292 uint8_t name_assign_type = NET_NAME_UNKNOWN;
294 attr = udev_device_get_sysattr_value(device, "name_assign_type");
296 (void)safe_atou8(attr, &name_assign_type);
298 if (name_assign_type == NET_NAME_ENUM)
299 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
300 IFNAMSIZ, ifname, network->filename);
302 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
304 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
316 int network_apply(Manager *manager, Network *network, Link *link) {
319 link->network = network;
321 if (network->ipv4ll_route) {
324 r = route_new_static(network, 0, &route);
328 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
334 route->family = AF_INET;
335 route->dst_prefixlen = 16;
336 route->scope = RT_SCOPE_LINK;
337 route->metrics = IPV4LL_ROUTE_METRIC;
338 route->protocol = RTPROT_STATIC;
341 if (network->dns || network->ntp) {
350 int config_parse_netdev(const char *unit,
351 const char *filename,
354 unsigned section_line,
360 Network *network = userdata;
361 _cleanup_free_ char *kind_string = NULL;
372 kind_string = strdup(lvalue);
376 /* the keys are CamelCase versions of the kind */
377 for (p = kind_string; *p; p++)
380 kind = netdev_kind_from_string(kind_string);
381 if (kind == _NETDEV_KIND_INVALID) {
382 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
383 "Invalid NetDev kind: %s", lvalue);
387 r = netdev_get(network->manager, rvalue, &netdev);
389 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
390 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
394 if (netdev->kind != kind) {
395 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
396 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
401 case NETDEV_KIND_BRIDGE:
402 network->bridge = netdev;
405 case NETDEV_KIND_BOND:
406 network->bond = netdev;
409 case NETDEV_KIND_VLAN:
410 case NETDEV_KIND_MACVLAN:
411 case NETDEV_KIND_IPVLAN:
412 case NETDEV_KIND_VXLAN:
413 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
415 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
416 "Can not add VLAN '%s' to network: %s",
417 rvalue, strerror(-r));
423 assert_not_reached("Can not parse NetDev");
431 int config_parse_domains(const char *unit,
432 const char *filename,
435 unsigned section_line,
441 Network *network = userdata;
442 char ***domains = data;
446 r = config_parse_strv(unit, filename, line, section, section_line,
447 lvalue, ltype, rvalue, domains, userdata);
452 network->wildcard_domain = !!strv_find(*domains, "*");
454 STRV_FOREACH(domain, *domains) {
455 if (is_localhost(*domain))
456 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
457 else if (!hostname_is_valid(*domain)) {
458 if (!streq(*domain, "*"))
459 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
463 strv_remove(*domains, *domain);
465 /* We removed one entry, make sure we don't skip the next one */
472 int config_parse_tunnel(const char *unit,
473 const char *filename,
476 unsigned section_line,
482 Network *network = userdata;
491 r = netdev_get(network->manager, rvalue, &netdev);
493 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
494 "Tunnel is invalid, ignoring assignment: %s", rvalue);
498 if (netdev->kind != NETDEV_KIND_IPIP &&
499 netdev->kind != NETDEV_KIND_SIT &&
500 netdev->kind != NETDEV_KIND_GRE &&
501 netdev->kind != NETDEV_KIND_GRETAP &&
502 netdev->kind != NETDEV_KIND_IP6GRE &&
503 netdev->kind != NETDEV_KIND_IP6GRETAP &&
504 netdev->kind != NETDEV_KIND_VTI &&
505 netdev->kind != NETDEV_KIND_IP6TNL
507 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
508 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
512 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
514 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
515 "Can not add VLAN '%s' to network: %s",
516 rvalue, strerror(-r));
525 int config_parse_ipv4ll(
527 const char *filename,
530 unsigned section_line,
537 AddressFamilyBoolean *link_local = data;
544 /* Note that this is mostly like
545 * config_parse_address_family_boolean(), except that it
546 * applies only to IPv4 */
548 if (parse_boolean(rvalue))
549 *link_local |= ADDRESS_FAMILY_IPV4;
551 *link_local &= ~ADDRESS_FAMILY_IPV4;
556 int config_parse_dhcp(
558 const char *filename,
561 unsigned section_line,
568 AddressFamilyBoolean *dhcp = data, s;
575 /* Note that this is mostly like
576 * config_parse_address_family_boolean(), except that it
577 * understands some old names for the enum values */
579 s = address_family_boolean_from_string(rvalue);
582 /* Previously, we had a slightly different enum here,
583 * support its values for compatbility. */
585 if (streq(rvalue, "none"))
586 s = ADDRESS_FAMILY_NO;
587 else if (streq(rvalue, "v4"))
588 s = ADDRESS_FAMILY_IPV4;
589 else if (streq(rvalue, "v6"))
590 s = ADDRESS_FAMILY_IPV6;
591 else if (streq(rvalue, "both"))
592 s = ADDRESS_FAMILY_YES;
594 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
603 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
604 [LLMNR_SUPPORT_NO] = "no",
605 [LLMNR_SUPPORT_YES] = "yes",
606 [LLMNR_SUPPORT_RESOLVE] = "resolve",
609 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
611 int config_parse_llmnr(
613 const char *filename,
616 unsigned section_line,
623 LLMNRSupport *llmnr = data;
631 /* Our enum shall be a superset of booleans, hence first try
632 * to parse as boolean, and then as enum */
634 k = parse_boolean(rvalue);
636 *llmnr = LLMNR_SUPPORT_YES;
638 *llmnr = LLMNR_SUPPORT_NO;
642 s = llmnr_support_from_string(rvalue);
644 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
654 int config_parse_ipv6token(
656 const char *filename,
659 unsigned section_line,
666 union in_addr_union buffer;
667 struct in6_addr *token = data;
675 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
677 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
681 r = in_addr_is_null(AF_INET6, &buffer);
683 log_syntax(unit, LOG_ERR, filename, line, -r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
687 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
688 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);