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 "path-util.h"
26 #include "conf-files.h"
27 #include "conf-parser.h"
30 #include "networkd-netdev.h"
31 #include "networkd-link.h"
32 #include "network-internal.h"
34 static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _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->dhcp = ADDRESS_FAMILY_NO;
88 network->dhcp_ntp = true;
89 network->dhcp_dns = true;
90 network->dhcp_hostname = true;
91 network->dhcp_routes = true;
92 network->dhcp_sendhost = true;
93 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
95 network->llmnr = LLMNR_SUPPORT_YES;
97 r = config_parse(NULL, filename, file,
107 config_item_perf_lookup, network_network_gperf_lookup,
108 false, false, true, network);
112 /* IPMasquerade=yes implies IPForward=yes */
113 if (network->ip_masquerade)
114 network->ip_forward |= ADDRESS_FAMILY_IPV4;
116 LIST_PREPEND(networks, manager->networks, network);
118 LIST_FOREACH(routes, route, network->static_routes) {
119 if (!route->family) {
120 log_warning("Route section without Gateway field configured in %s. "
121 "Ignoring", filename);
126 LIST_FOREACH(addresses, address, network->static_addresses) {
127 if (!address->family) {
128 log_warning("Address section without Address field configured in %s. "
129 "Ignoring", filename);
139 int network_load(Manager *manager) {
141 _cleanup_strv_free_ char **files = NULL;
147 while ((network = manager->networks))
148 network_free(network);
150 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
152 return log_error_errno(r, "Failed to enumerate network files: %m");
154 STRV_FOREACH_BACKWARDS(f, files) {
155 r = network_load_one(manager, *f);
163 void network_free(Network *network) {
173 free(network->filename);
175 free(network->match_mac);
176 free(network->match_path);
177 free(network->match_driver);
178 free(network->match_type);
179 free(network->match_name);
181 free(network->description);
182 free(network->dhcp_vendor_class_identifier);
186 strv_free(network->ntp);
187 strv_free(network->dns);
188 strv_free(network->domains);
190 netdev_unref(network->bridge);
192 netdev_unref(network->bond);
194 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
195 hashmap_remove(network->stacked_netdevs, netdev->ifname);
196 netdev_unref(netdev);
198 hashmap_free(network->stacked_netdevs);
200 while ((route = network->static_routes))
203 while ((address = network->static_addresses))
204 address_free(address);
206 while ((fdb_entry = network->static_fdb_entries))
207 fdb_entry_free(fdb_entry);
209 hashmap_free(network->addresses_by_section);
210 hashmap_free(network->routes_by_section);
211 hashmap_free(network->fdb_entries_by_section);
213 if (network->manager && network->manager->networks)
214 LIST_REMOVE(networks, network->manager->networks, network);
216 condition_free_list(network->match_host);
217 condition_free_list(network->match_virt);
218 condition_free_list(network->match_kernel);
219 condition_free_list(network->match_arch);
224 int network_get(Manager *manager, struct udev_device *device,
225 const char *ifname, const struct ether_addr *address,
232 LIST_FOREACH(networks, network, manager->networks) {
233 if (net_match_config(network->match_mac, network->match_path,
234 network->match_driver, network->match_type,
235 network->match_name, network->match_host,
236 network->match_virt, network->match_kernel,
239 udev_device_get_property_value(device, "ID_PATH"),
240 udev_device_get_driver(udev_device_get_parent(device)),
241 udev_device_get_property_value(device, "ID_NET_DRIVER"),
242 udev_device_get_devtype(device),
244 if (network->match_name) {
246 uint8_t name_assign_type = NET_NAME_UNKNOWN;
248 attr = udev_device_get_sysattr_value(device, "name_assign_type");
250 (void)safe_atou8(attr, &name_assign_type);
252 if (name_assign_type == NET_NAME_ENUM)
253 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
254 IFNAMSIZ, ifname, network->filename);
256 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
258 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
270 int network_apply(Manager *manager, Network *network, Link *link) {
273 link->network = network;
275 if (network->ipv4ll_route) {
278 r = route_new_static(network, 0, &route);
282 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
288 route->family = AF_INET;
289 route->dst_prefixlen = 16;
290 route->scope = RT_SCOPE_LINK;
291 route->metrics = IPV4LL_ROUTE_METRIC;
292 route->protocol = RTPROT_STATIC;
295 if (network->dns || network->ntp) {
304 int config_parse_netdev(const char *unit,
305 const char *filename,
308 unsigned section_line,
314 Network *network = userdata;
315 _cleanup_free_ char *kind_string = NULL;
326 kind_string = strdup(lvalue);
330 /* the keys are CamelCase versions of the kind */
331 for (p = kind_string; *p; p++)
334 kind = netdev_kind_from_string(kind_string);
335 if (kind == _NETDEV_KIND_INVALID) {
336 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
337 "Invalid NetDev kind: %s", lvalue);
341 r = netdev_get(network->manager, rvalue, &netdev);
343 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
344 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
348 if (netdev->kind != kind) {
349 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
350 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
355 case NETDEV_KIND_BRIDGE:
356 network->bridge = netdev;
359 case NETDEV_KIND_BOND:
360 network->bond = netdev;
363 case NETDEV_KIND_VLAN:
364 case NETDEV_KIND_MACVLAN:
365 case NETDEV_KIND_IPVLAN:
366 case NETDEV_KIND_VXLAN:
367 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
369 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
370 "Can not add VLAN '%s' to network: %s",
371 rvalue, strerror(-r));
377 assert_not_reached("Can not parse NetDev");
385 int config_parse_domains(const char *unit,
386 const char *filename,
389 unsigned section_line,
395 Network *network = userdata;
396 char ***domains = data;
400 r = config_parse_strv(unit, filename, line, section, section_line,
401 lvalue, ltype, rvalue, domains, userdata);
406 network->wildcard_domain = !!strv_find(*domains, "*");
408 STRV_FOREACH(domain, *domains) {
409 if (is_localhost(*domain))
410 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
411 else if (!hostname_is_valid(*domain)) {
412 if (!streq(*domain, "*"))
413 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
417 strv_remove(*domains, *domain);
419 /* We removed one entry, make sure we don't skip the next one */
426 int config_parse_tunnel(const char *unit,
427 const char *filename,
430 unsigned section_line,
436 Network *network = userdata;
445 r = netdev_get(network->manager, rvalue, &netdev);
447 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
448 "Tunnel is invalid, ignoring assignment: %s", rvalue);
452 if (netdev->kind != NETDEV_KIND_IPIP &&
453 netdev->kind != NETDEV_KIND_SIT &&
454 netdev->kind != NETDEV_KIND_GRE &&
455 netdev->kind != NETDEV_KIND_GRETAP &&
456 netdev->kind != NETDEV_KIND_IP6GRE &&
457 netdev->kind != NETDEV_KIND_IP6GRETAP &&
458 netdev->kind != NETDEV_KIND_VTI &&
459 netdev->kind != NETDEV_KIND_IP6TNL
461 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
462 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
466 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
468 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
469 "Can not add VLAN '%s' to network: %s",
470 rvalue, strerror(-r));
479 int config_parse_dhcp(
481 const char *filename,
484 unsigned section_line,
491 AddressFamilyBoolean *dhcp = data, s;
498 /* Note that this is mostly like
499 * config_parse_address_family_boolean(), except that it
500 * understands some old names for the enum values */
502 s = address_family_boolean_from_string(rvalue);
505 /* Previously, we had a slightly different enum here,
506 * support its values for compatbility. */
508 if (streq(rvalue, "none"))
509 s = ADDRESS_FAMILY_NO;
510 else if (streq(rvalue, "v4"))
511 s = ADDRESS_FAMILY_IPV4;
512 else if (streq(rvalue, "v6"))
513 s = ADDRESS_FAMILY_IPV6;
514 else if (streq(rvalue, "both"))
515 s = ADDRESS_FAMILY_YES;
517 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
526 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
527 [LLMNR_SUPPORT_NO] = "no",
528 [LLMNR_SUPPORT_YES] = "yes",
529 [LLMNR_SUPPORT_RESOLVE] = "resolve",
532 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
534 int config_parse_llmnr(
536 const char *filename,
539 unsigned section_line,
546 LLMNRSupport *llmnr = data;
554 /* Our enum shall be a superset of booleans, hence first try
555 * to parse as boolean, and then as enum */
557 k = parse_boolean(rvalue);
559 *llmnr = LLMNR_SUPPORT_YES;
561 *llmnr = LLMNR_SUPPORT_NO;
565 s = llmnr_support_from_string(rvalue);
567 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);