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/>.
26 #include "networkd-netdev.h"
27 #include "networkd-link.h"
28 #include "network-internal.h"
29 #include "path-util.h"
30 #include "conf-files.h"
31 #include "conf-parser.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 = DHCP_SUPPORT_NONE;
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 LIST_PREPEND(networks, manager->networks, network);
114 LIST_FOREACH(routes, route, network->static_routes) {
115 if (!route->family) {
116 log_warning("Route section without Gateway field configured in %s. "
117 "Ignoring", filename);
122 LIST_FOREACH(addresses, address, network->static_addresses) {
123 if (!address->family) {
124 log_warning("Address section without Address field configured in %s. "
125 "Ignoring", filename);
135 int network_load(Manager *manager) {
137 _cleanup_strv_free_ char **files = NULL;
143 while ((network = manager->networks))
144 network_free(network);
146 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
148 return log_error_errno(r, "Failed to enumerate network files: %m");
150 STRV_FOREACH_BACKWARDS(f, files) {
151 r = network_load_one(manager, *f);
159 void network_free(Network *network) {
169 free(network->filename);
171 free(network->match_mac);
172 free(network->match_path);
173 free(network->match_driver);
174 free(network->match_type);
175 free(network->match_name);
177 free(network->description);
178 free(network->dhcp_vendor_class_identifier);
182 strv_free(network->ntp);
183 strv_free(network->dns);
184 strv_free(network->domains);
186 netdev_unref(network->bridge);
188 netdev_unref(network->bond);
190 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
191 hashmap_remove(network->stacked_netdevs, netdev->ifname);
192 netdev_unref(netdev);
194 hashmap_free(network->stacked_netdevs);
196 while ((route = network->static_routes))
199 while ((address = network->static_addresses))
200 address_free(address);
202 while ((fdb_entry = network->static_fdb_entries))
203 fdb_entry_free(fdb_entry);
205 hashmap_free(network->addresses_by_section);
206 hashmap_free(network->routes_by_section);
207 hashmap_free(network->fdb_entries_by_section);
209 if (network->manager && network->manager->networks)
210 LIST_REMOVE(networks, network->manager->networks, network);
212 condition_free_list(network->match_host);
213 condition_free_list(network->match_virt);
214 condition_free_list(network->match_kernel);
215 condition_free_list(network->match_arch);
220 int network_get(Manager *manager, struct udev_device *device,
221 const char *ifname, const struct ether_addr *address,
228 LIST_FOREACH(networks, network, manager->networks) {
229 if (net_match_config(network->match_mac, network->match_path,
230 network->match_driver, network->match_type,
231 network->match_name, network->match_host,
232 network->match_virt, network->match_kernel,
235 udev_device_get_property_value(device, "ID_PATH"),
236 udev_device_get_driver(udev_device_get_parent(device)),
237 udev_device_get_property_value(device, "ID_NET_DRIVER"),
238 udev_device_get_devtype(device),
240 if (network->match_name) {
242 uint8_t name_assign_type = NET_NAME_UNKNOWN;
244 attr = udev_device_get_sysattr_value(device, "name_assign_type");
246 (void)safe_atou8(attr, &name_assign_type);
248 if (name_assign_type == NET_NAME_ENUM)
249 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
250 IFNAMSIZ, ifname, network->filename);
252 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
254 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
266 int network_apply(Manager *manager, Network *network, Link *link) {
269 link->network = network;
271 if (network->ipv4ll_route) {
274 r = route_new_static(network, 0, &route);
278 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
284 route->family = AF_INET;
285 route->dst_prefixlen = 16;
286 route->scope = RT_SCOPE_LINK;
287 route->metrics = IPV4LL_ROUTE_METRIC;
288 route->protocol = RTPROT_STATIC;
291 if (network->dns || network->ntp) {
300 int config_parse_netdev(const char *unit,
301 const char *filename,
304 unsigned section_line,
310 Network *network = userdata;
311 _cleanup_free_ char *kind_string = NULL;
322 kind_string = strdup(lvalue);
326 /* the keys are CamelCase versions of the kind */
327 for (p = kind_string; *p; p++)
330 kind = netdev_kind_from_string(kind_string);
331 if (kind == _NETDEV_KIND_INVALID) {
332 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
333 "Invalid NetDev kind: %s", lvalue);
337 r = netdev_get(network->manager, rvalue, &netdev);
339 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
340 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
344 if (netdev->kind != kind) {
345 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
346 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
351 case NETDEV_KIND_BRIDGE:
352 network->bridge = netdev;
355 case NETDEV_KIND_BOND:
356 network->bond = netdev;
359 case NETDEV_KIND_VLAN:
360 case NETDEV_KIND_MACVLAN:
361 case NETDEV_KIND_VXLAN:
362 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
364 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
365 "Can not add VLAN '%s' to network: %s",
366 rvalue, strerror(-r));
372 assert_not_reached("Can not parse NetDev");
380 int config_parse_domains(const char *unit,
381 const char *filename,
384 unsigned section_line,
390 Network *network = userdata;
391 char ***domains = data;
395 r = config_parse_strv(unit, filename, line, section, section_line,
396 lvalue, ltype, rvalue, domains, userdata);
401 network->wildcard_domain = !!strv_find(*domains, "*");
403 STRV_FOREACH(domain, *domains) {
404 if (is_localhost(*domain))
405 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
406 else if (!hostname_is_valid(*domain)) {
407 if (!streq(*domain, "*"))
408 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
412 strv_remove(*domains, *domain);
414 /* We removed one entry, make sure we don't skip the next one */
421 int config_parse_tunnel(const char *unit,
422 const char *filename,
425 unsigned section_line,
431 Network *network = userdata;
440 r = netdev_get(network->manager, rvalue, &netdev);
442 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
443 "Tunnel is invalid, ignoring assignment: %s", rvalue);
447 if (netdev->kind != NETDEV_KIND_IPIP &&
448 netdev->kind != NETDEV_KIND_SIT &&
449 netdev->kind != NETDEV_KIND_GRE &&
450 netdev->kind != NETDEV_KIND_VTI) {
451 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
452 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
456 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
458 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
459 "Can not add VLAN '%s' to network: %s",
460 rvalue, strerror(-r));
469 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
470 [DHCP_SUPPORT_NONE] = "none",
471 [DHCP_SUPPORT_BOTH] = "both",
472 [DHCP_SUPPORT_V4] = "v4",
473 [DHCP_SUPPORT_V6] = "v6",
476 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
478 int config_parse_dhcp(
480 const char *filename,
483 unsigned section_line,
490 DHCPSupport *dhcp = data;
498 /* Our enum shall be a superset of booleans, hence first try
499 * to parse as boolean, and then as enum */
501 k = parse_boolean(rvalue);
503 *dhcp = DHCP_SUPPORT_BOTH;
505 *dhcp = DHCP_SUPPORT_NONE;
509 s = dhcp_support_from_string(rvalue);
511 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
521 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
522 [LLMNR_SUPPORT_NO] = "no",
523 [LLMNR_SUPPORT_YES] = "yes",
524 [LLMNR_SUPPORT_RESOLVE] = "resolve",
527 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
529 int config_parse_llmnr(
531 const char *filename,
534 unsigned section_line,
541 LLMNRSupport *llmnr = data;
549 /* Our enum shall be a superset of booleans, hence first try
550 * to parse as boolean, and then as enum */
552 k = parse_boolean(rvalue);
554 *llmnr = LLMNR_SUPPORT_YES;
556 *llmnr = LLMNR_SUPPORT_NO;
560 s = llmnr_support_from_string(rvalue);
562 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);