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 netdev_unref(netdev);
177 hashmap_free(network->stacked_netdevs);
179 while ((route = network->static_routes))
182 while ((address = network->static_addresses))
183 address_free(address);
185 hashmap_free(network->addresses_by_section);
186 hashmap_free(network->routes_by_section);
188 if (network->manager && network->manager->networks)
189 LIST_REMOVE(networks, network->manager->networks, network);
191 condition_free_list(network->match_host);
192 condition_free_list(network->match_virt);
193 condition_free_list(network->match_kernel);
194 condition_free_list(network->match_arch);
199 int network_get(Manager *manager, struct udev_device *device,
200 const char *ifname, const struct ether_addr *address,
207 LIST_FOREACH(networks, network, manager->networks) {
208 if (net_match_config(network->match_mac, network->match_path,
209 network->match_driver, network->match_type,
210 network->match_name, network->match_host,
211 network->match_virt, network->match_kernel,
214 udev_device_get_property_value(device, "ID_PATH"),
215 udev_device_get_driver(udev_device_get_parent(device)),
216 udev_device_get_property_value(device, "ID_NET_DRIVER"),
217 udev_device_get_devtype(device),
219 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
231 int network_apply(Manager *manager, Network *network, Link *link) {
234 link->network = network;
236 if (network->ipv4ll_route) {
239 r = route_new_static(network, 0, &route);
243 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
249 route->family = AF_INET;
250 route->dst_prefixlen = 16;
251 route->scope = RT_SCOPE_LINK;
252 route->metrics = IPV4LL_ROUTE_METRIC;
253 route->protocol = RTPROT_STATIC;
256 if (network->dns || network->ntp) {
265 int config_parse_netdev(const char *unit,
266 const char *filename,
269 unsigned section_line,
275 Network *network = userdata;
276 _cleanup_free_ char *kind_string = NULL;
287 kind_string = strdup(lvalue);
291 /* the keys are CamelCase versions of the kind */
292 for (p = kind_string; *p; p++)
295 kind = netdev_kind_from_string(kind_string);
296 if (kind == _NETDEV_KIND_INVALID) {
297 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
298 "Invalid NetDev kind: %s", lvalue);
302 r = netdev_get(network->manager, rvalue, &netdev);
304 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
305 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
309 if (netdev->kind != kind) {
310 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
311 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
316 case NETDEV_KIND_BRIDGE:
317 network->bridge = netdev;
320 case NETDEV_KIND_BOND:
321 network->bond = netdev;
324 case NETDEV_KIND_VLAN:
325 case NETDEV_KIND_MACVLAN:
326 case NETDEV_KIND_VXLAN:
327 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
329 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
330 "Can not add VLAN '%s' to network: %s",
331 rvalue, strerror(-r));
337 assert_not_reached("Can not parse NetDev");
345 int config_parse_domains(const char *unit,
346 const char *filename,
349 unsigned section_line,
355 Network *network = userdata;
356 char ***domains = data;
360 r = config_parse_strv(unit, filename, line, section, section_line,
361 lvalue, ltype, rvalue, domains, userdata);
366 network->wildcard_domain = !!strv_find(*domains, "*");
368 STRV_FOREACH(domain, *domains) {
369 if (is_localhost(*domain))
370 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
371 else if (!hostname_is_valid(*domain)) {
372 if (!streq(*domain, "*"))
373 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
377 strv_remove(*domains, *domain);
379 /* We removed one entry, make sure we don't skip the next one */
386 int config_parse_tunnel(const char *unit,
387 const char *filename,
390 unsigned section_line,
396 Network *network = userdata;
405 r = netdev_get(network->manager, rvalue, &netdev);
407 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
408 "Tunnel is invalid, ignoring assignment: %s", rvalue);
412 if (netdev->kind != NETDEV_KIND_IPIP &&
413 netdev->kind != NETDEV_KIND_SIT &&
414 netdev->kind != NETDEV_KIND_GRE &&
415 netdev->kind != NETDEV_KIND_VTI) {
416 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
417 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
421 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
423 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
424 "Can not add VLAN '%s' to network: %s",
425 rvalue, strerror(-r));
434 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
435 [DHCP_SUPPORT_NONE] = "none",
436 [DHCP_SUPPORT_BOTH] = "both",
437 [DHCP_SUPPORT_V4] = "v4",
438 [DHCP_SUPPORT_V6] = "v6",
441 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
443 int config_parse_dhcp(
445 const char *filename,
448 unsigned section_line,
455 DHCPSupport *dhcp = data;
463 /* Our enum shall be a superset of booleans, hence first try
464 * to parse as boolean, and then as enum */
466 k = parse_boolean(rvalue);
468 *dhcp = DHCP_SUPPORT_BOTH;
470 *dhcp = DHCP_SUPPORT_NONE;
474 s = dhcp_support_from_string(rvalue);
476 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
486 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
487 [LLMNR_SUPPORT_NO] = "no",
488 [LLMNR_SUPPORT_YES] = "yes",
489 [LLMNR_SUPPORT_RESOLVE] = "resolve",
492 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
494 int config_parse_llmnr(
496 const char *filename,
499 unsigned section_line,
506 LLMNRSupport *llmnr = data;
514 /* Our enum shall be a superset of booleans, hence first try
515 * to parse as boolean, and then as enum */
517 k = parse_boolean(rvalue);
519 *llmnr = LLMNR_SUPPORT_YES;
521 *llmnr = LLMNR_SUPPORT_NO;
525 s = llmnr_support_from_string(rvalue);
527 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);