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_ops);
67 if (!network->stacked_netdevs)
70 network->addresses_by_section = hashmap_new(NULL);
71 if (!network->addresses_by_section)
74 network->routes_by_section = hashmap_new(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;
88 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
90 network->llmnr = LLMNR_SUPPORT_YES;
92 r = config_parse(NULL, filename, file,
93 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
94 config_item_perf_lookup, network_network_gperf_lookup,
95 false, false, true, network);
99 LIST_PREPEND(networks, manager->networks, network);
101 LIST_FOREACH(routes, route, network->static_routes) {
102 if (!route->family) {
103 log_warning("Route section without Gateway field configured in %s. "
104 "Ignoring", filename);
109 LIST_FOREACH(addresses, address, network->static_addresses) {
110 if (!address->family) {
111 log_warning("Address section without Address field configured in %s. "
112 "Ignoring", filename);
122 int network_load(Manager *manager) {
124 _cleanup_strv_free_ char **files = NULL;
130 while ((network = manager->networks))
131 network_free(network);
133 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
135 return log_error_errno(r, "Failed to enumerate network files: %m");
137 STRV_FOREACH_BACKWARDS(f, files) {
138 r = network_load_one(manager, *f);
146 void network_free(Network *network) {
155 free(network->filename);
157 free(network->match_mac);
158 free(network->match_path);
159 free(network->match_driver);
160 free(network->match_type);
161 free(network->match_name);
163 free(network->description);
164 free(network->dhcp_vendor_class_identifier);
166 strv_free(network->ntp);
167 strv_free(network->dns);
168 strv_free(network->domains);
170 netdev_unref(network->bridge);
172 netdev_unref(network->bond);
174 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
175 hashmap_remove(network->stacked_netdevs, netdev->ifname);
176 netdev_unref(netdev);
178 hashmap_free(network->stacked_netdevs);
180 while ((route = network->static_routes))
183 while ((address = network->static_addresses))
184 address_free(address);
186 hashmap_free(network->addresses_by_section);
187 hashmap_free(network->routes_by_section);
189 if (network->manager && network->manager->networks)
190 LIST_REMOVE(networks, network->manager->networks, network);
192 condition_free_list(network->match_host);
193 condition_free_list(network->match_virt);
194 condition_free_list(network->match_kernel);
195 condition_free_list(network->match_arch);
200 int network_get(Manager *manager, struct udev_device *device,
201 const char *ifname, const struct ether_addr *address,
208 LIST_FOREACH(networks, network, manager->networks) {
209 if (net_match_config(network->match_mac, network->match_path,
210 network->match_driver, network->match_type,
211 network->match_name, network->match_host,
212 network->match_virt, network->match_kernel,
215 udev_device_get_property_value(device, "ID_PATH"),
216 udev_device_get_driver(udev_device_get_parent(device)),
217 udev_device_get_property_value(device, "ID_NET_DRIVER"),
218 udev_device_get_devtype(device),
220 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
232 int network_apply(Manager *manager, Network *network, Link *link) {
235 link->network = network;
237 if (network->ipv4ll_route) {
240 r = route_new_static(network, 0, &route);
244 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
250 route->family = AF_INET;
251 route->dst_prefixlen = 16;
252 route->scope = RT_SCOPE_LINK;
253 route->metrics = IPV4LL_ROUTE_METRIC;
254 route->protocol = RTPROT_STATIC;
257 if (network->dns || network->ntp) {
266 int config_parse_netdev(const char *unit,
267 const char *filename,
270 unsigned section_line,
276 Network *network = userdata;
277 _cleanup_free_ char *kind_string = NULL;
288 kind_string = strdup(lvalue);
292 /* the keys are CamelCase versions of the kind */
293 for (p = kind_string; *p; p++)
296 kind = netdev_kind_from_string(kind_string);
297 if (kind == _NETDEV_KIND_INVALID) {
298 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
299 "Invalid NetDev kind: %s", lvalue);
303 r = netdev_get(network->manager, rvalue, &netdev);
305 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
306 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
310 if (netdev->kind != kind) {
311 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
312 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
317 case NETDEV_KIND_BRIDGE:
318 network->bridge = netdev;
321 case NETDEV_KIND_BOND:
322 network->bond = netdev;
325 case NETDEV_KIND_VLAN:
326 case NETDEV_KIND_MACVLAN:
327 case NETDEV_KIND_VXLAN:
328 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
330 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331 "Can not add VLAN '%s' to network: %s",
332 rvalue, strerror(-r));
338 assert_not_reached("Can not parse NetDev");
346 int config_parse_domains(const char *unit,
347 const char *filename,
350 unsigned section_line,
356 Network *network = userdata;
357 char ***domains = data;
361 r = config_parse_strv(unit, filename, line, section, section_line,
362 lvalue, ltype, rvalue, domains, userdata);
367 network->wildcard_domain = !!strv_find(*domains, "*");
369 STRV_FOREACH(domain, *domains) {
370 if (is_localhost(*domain))
371 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
372 else if (!hostname_is_valid(*domain)) {
373 if (!streq(*domain, "*"))
374 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
378 strv_remove(*domains, *domain);
380 /* We removed one entry, make sure we don't skip the next one */
387 int config_parse_tunnel(const char *unit,
388 const char *filename,
391 unsigned section_line,
397 Network *network = userdata;
406 r = netdev_get(network->manager, rvalue, &netdev);
408 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
409 "Tunnel is invalid, ignoring assignment: %s", rvalue);
413 if (netdev->kind != NETDEV_KIND_IPIP &&
414 netdev->kind != NETDEV_KIND_SIT &&
415 netdev->kind != NETDEV_KIND_GRE &&
416 netdev->kind != NETDEV_KIND_VTI) {
417 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
418 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
422 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
424 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
425 "Can not add VLAN '%s' to network: %s",
426 rvalue, strerror(-r));
435 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
436 [DHCP_SUPPORT_NONE] = "none",
437 [DHCP_SUPPORT_BOTH] = "both",
438 [DHCP_SUPPORT_V4] = "v4",
439 [DHCP_SUPPORT_V6] = "v6",
442 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
444 int config_parse_dhcp(
446 const char *filename,
449 unsigned section_line,
456 DHCPSupport *dhcp = data;
464 /* Our enum shall be a superset of booleans, hence first try
465 * to parse as boolean, and then as enum */
467 k = parse_boolean(rvalue);
469 *dhcp = DHCP_SUPPORT_BOTH;
471 *dhcp = DHCP_SUPPORT_NONE;
475 s = dhcp_support_from_string(rvalue);
477 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
487 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
488 [LLMNR_SUPPORT_NO] = "no",
489 [LLMNR_SUPPORT_YES] = "yes",
490 [LLMNR_SUPPORT_RESOLVE] = "resolve",
493 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
495 int config_parse_llmnr(
497 const char *filename,
500 unsigned section_line,
507 LLMNRSupport *llmnr = data;
515 /* Our enum shall be a superset of booleans, hence first try
516 * to parse as boolean, and then as enum */
518 k = parse_boolean(rvalue);
520 *llmnr = LLMNR_SUPPORT_YES;
522 *llmnr = LLMNR_SUPPORT_NO;
526 s = llmnr_support_from_string(rvalue);
528 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);