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);
66 network->stacked_netdevs = hashmap_new(string_hash_func, string_compare_func);
67 if (!network->stacked_netdevs)
70 network->addresses_by_section = hashmap_new(NULL, NULL);
71 if (!network->addresses_by_section)
74 network->routes_by_section = hashmap_new(NULL, NULL);
75 if (!network->routes_by_section)
78 network->filename = strdup(filename);
79 if (!network->filename)
82 network->dhcp = DHCP_SUPPORT_NONE;
83 network->dhcp_ntp = true;
84 network->dhcp_dns = true;
85 network->dhcp_hostname = true;
86 network->dhcp_routes = true;
87 network->dhcp_sendhost = true;
89 network->llmnr = LLMNR_SUPPORT_YES;
91 r = config_parse(NULL, filename, file,
92 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
93 config_item_perf_lookup, network_network_gperf_lookup,
94 false, false, true, network);
98 LIST_PREPEND(networks, manager->networks, network);
100 LIST_FOREACH(routes, route, network->static_routes) {
101 if (!route->family) {
102 log_warning("Route section without Gateway field configured in %s. "
103 "Ignoring", filename);
108 LIST_FOREACH(addresses, address, network->static_addresses) {
109 if (!address->family) {
110 log_warning("Address section without Address field configured in %s. "
111 "Ignoring", filename);
121 int network_load(Manager *manager) {
123 _cleanup_strv_free_ char **files = NULL;
129 while ((network = manager->networks))
130 network_free(network);
132 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
134 log_error("Failed to enumerate network files: %s", strerror(-r));
138 STRV_FOREACH_BACKWARDS(f, files) {
139 r = network_load_one(manager, *f);
147 void network_free(Network *network) {
156 free(network->filename);
158 free(network->match_mac);
159 free(network->match_path);
160 free(network->match_driver);
161 free(network->match_type);
162 free(network->match_name);
164 free(network->description);
165 free(network->dhcp_vendor_class_identifier);
167 strv_free(network->ntp);
168 strv_free(network->dns);
169 strv_free(network->domains);
171 netdev_unref(network->bridge);
173 netdev_unref(network->bond);
175 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
176 hashmap_remove(network->stacked_netdevs, netdev->ifname);
177 netdev_unref(netdev);
179 hashmap_free(network->stacked_netdevs);
181 while ((route = network->static_routes))
184 while ((address = network->static_addresses))
185 address_free(address);
187 hashmap_free(network->addresses_by_section);
188 hashmap_free(network->routes_by_section);
190 if (network->manager && network->manager->networks)
191 LIST_REMOVE(networks, network->manager->networks, network);
193 condition_free_list(network->match_host);
194 condition_free_list(network->match_virt);
195 condition_free_list(network->match_kernel);
196 condition_free_list(network->match_arch);
201 int network_get(Manager *manager, struct udev_device *device,
202 const char *ifname, const struct ether_addr *address,
209 LIST_FOREACH(networks, network, manager->networks) {
210 if (net_match_config(network->match_mac, network->match_path,
211 network->match_driver, network->match_type,
212 network->match_name, network->match_host,
213 network->match_virt, network->match_kernel,
216 udev_device_get_property_value(device, "ID_PATH"),
217 udev_device_get_driver(udev_device_get_parent(device)),
218 udev_device_get_property_value(device, "ID_NET_DRIVER"),
219 udev_device_get_devtype(device),
221 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
233 int network_apply(Manager *manager, Network *network, Link *link) {
236 link->network = network;
238 if (network->ipv4ll_route) {
241 r = route_new_static(network, 0, &route);
245 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
251 route->family = AF_INET;
252 route->dst_prefixlen = 16;
253 route->scope = RT_SCOPE_LINK;
254 route->metrics = IPV4LL_ROUTE_METRIC;
255 route->protocol = RTPROT_STATIC;
258 if (network->dns || network->ntp) {
267 int config_parse_netdev(const char *unit,
268 const char *filename,
271 unsigned section_line,
277 Network *network = userdata;
278 _cleanup_free_ char *kind_string = NULL;
289 kind_string = strdup(lvalue);
293 /* the keys are CamelCase versions of the kind */
294 for (p = kind_string; *p; p++)
297 kind = netdev_kind_from_string(kind_string);
298 if (kind == _NETDEV_KIND_INVALID) {
299 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
300 "Invalid NetDev kind: %s", lvalue);
304 r = netdev_get(network->manager, rvalue, &netdev);
306 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
307 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
311 if (netdev->kind != kind) {
312 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
313 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
318 case NETDEV_KIND_BRIDGE:
319 network->bridge = netdev;
322 case NETDEV_KIND_BOND:
323 network->bond = netdev;
326 case NETDEV_KIND_VLAN:
327 case NETDEV_KIND_MACVLAN:
328 case NETDEV_KIND_VXLAN:
329 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
331 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
332 "Can not add VLAN '%s' to network: %s",
333 rvalue, strerror(-r));
339 assert_not_reached("Can not parse NetDev");
347 int config_parse_domains(const char *unit,
348 const char *filename,
351 unsigned section_line,
357 Network *network = userdata;
358 char ***domains = data;
362 r = config_parse_strv(unit, filename, line, section, section_line,
363 lvalue, ltype, rvalue, domains, userdata);
368 network->wildcard_domain = !!strv_find(*domains, "*");
370 STRV_FOREACH(domain, *domains) {
371 if (is_localhost(*domain))
372 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
373 else if (!hostname_is_valid(*domain)) {
374 if (!streq(*domain, "*"))
375 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
379 strv_remove(*domains, *domain);
381 /* We removed one entry, make sure we don't skip the next one */
388 int config_parse_tunnel(const char *unit,
389 const char *filename,
392 unsigned section_line,
398 Network *network = userdata;
407 r = netdev_get(network->manager, rvalue, &netdev);
409 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
410 "Tunnel is invalid, ignoring assignment: %s", rvalue);
414 if (netdev->kind != NETDEV_KIND_IPIP &&
415 netdev->kind != NETDEV_KIND_SIT &&
416 netdev->kind != NETDEV_KIND_GRE &&
417 netdev->kind != NETDEV_KIND_VTI) {
418 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
419 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
423 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
425 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
426 "Can not add VLAN '%s' to network: %s",
427 rvalue, strerror(-r));
436 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
437 [DHCP_SUPPORT_NONE] = "none",
438 [DHCP_SUPPORT_BOTH] = "both",
439 [DHCP_SUPPORT_V4] = "v4",
440 [DHCP_SUPPORT_V6] = "v6",
443 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
445 int config_parse_dhcp(
447 const char *filename,
450 unsigned section_line,
457 DHCPSupport *dhcp = data;
465 /* Our enum shall be a superset of booleans, hence first try
466 * to parse as boolean, and then as enum */
468 k = parse_boolean(rvalue);
470 *dhcp = DHCP_SUPPORT_BOTH;
472 *dhcp = DHCP_SUPPORT_NONE;
476 s = dhcp_support_from_string(rvalue);
478 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
488 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
489 [LLMNR_SUPPORT_NO] = "no",
490 [LLMNR_SUPPORT_YES] = "yes",
491 [LLMNR_SUPPORT_RESOLVE] = "resolve",
494 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
496 int config_parse_llmnr(
498 const char *filename,
501 unsigned section_line,
508 LLMNRSupport *llmnr = data;
516 /* Our enum shall be a superset of booleans, hence first try
517 * to parse as boolean, and then as enum */
519 k = parse_boolean(rvalue);
521 *llmnr = LLMNR_SUPPORT_YES;
523 *llmnr = LLMNR_SUPPORT_NO;
527 s = llmnr_support_from_string(rvalue);
529 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);