]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
networkd: introduce an AddressFamilyBoolean enum type
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <ctype.h>
23 #include <net/if.h>
24
25 #include "path-util.h"
26 #include "conf-files.h"
27 #include "conf-parser.h"
28 #include "util.h"
29 #include "networkd.h"
30 #include "networkd-netdev.h"
31 #include "networkd-link.h"
32 #include "network-internal.h"
33
34 static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _cleanup_fclose_ FILE *file = NULL;
37 Route *route;
38 Address *address;
39 int r;
40
41 assert(manager);
42 assert(filename);
43
44 file = fopen(filename, "re");
45 if (!file) {
46 if (errno == ENOENT)
47 return 0;
48 else
49 return -errno;
50 }
51
52 if (null_or_empty_fd(fileno(file))) {
53 log_debug("Skipping empty file: %s", filename);
54 return 0;
55 }
56
57 network = new0(Network, 1);
58 if (!network)
59 return log_oom();
60
61 network->manager = manager;
62
63 LIST_HEAD_INIT(network->static_addresses);
64 LIST_HEAD_INIT(network->static_routes);
65 LIST_HEAD_INIT(network->static_fdb_entries);
66
67 network->stacked_netdevs = hashmap_new(&string_hash_ops);
68 if (!network->stacked_netdevs)
69 return log_oom();
70
71 network->addresses_by_section = hashmap_new(NULL);
72 if (!network->addresses_by_section)
73 return log_oom();
74
75 network->routes_by_section = hashmap_new(NULL);
76 if (!network->routes_by_section)
77 return log_oom();
78
79 network->fdb_entries_by_section = hashmap_new(NULL);
80 if (!network->fdb_entries_by_section)
81 return log_oom();
82
83 network->filename = strdup(filename);
84 if (!network->filename)
85 return log_oom();
86
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;
94
95 network->llmnr = LLMNR_SUPPORT_YES;
96
97 r = config_parse(NULL, filename, file,
98 "Match\0"
99 "Link\0"
100 "Network\0"
101 "Address\0"
102 "Route\0"
103 "DHCP\0"
104 "DHCPv4\0"
105 "Bridge\0"
106 "BridgeFDB\0",
107 config_item_perf_lookup, network_network_gperf_lookup,
108 false, false, true, network);
109 if (r < 0)
110 return r;
111
112 /* IPMasquerade=yes implies IPForward=yes */
113 if (network->ip_masquerade)
114 network->ip_forward = true;
115
116 LIST_PREPEND(networks, manager->networks, network);
117
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);
122 return 0;
123 }
124 }
125
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);
130 return 0;
131 }
132 }
133
134 network = NULL;
135
136 return 0;
137 }
138
139 int network_load(Manager *manager) {
140 Network *network;
141 _cleanup_strv_free_ char **files = NULL;
142 char **f;
143 int r;
144
145 assert(manager);
146
147 while ((network = manager->networks))
148 network_free(network);
149
150 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
151 if (r < 0)
152 return log_error_errno(r, "Failed to enumerate network files: %m");
153
154 STRV_FOREACH_BACKWARDS(f, files) {
155 r = network_load_one(manager, *f);
156 if (r < 0)
157 return r;
158 }
159
160 return 0;
161 }
162
163 void network_free(Network *network) {
164 NetDev *netdev;
165 Route *route;
166 Address *address;
167 FdbEntry *fdb_entry;
168 Iterator i;
169
170 if (!network)
171 return;
172
173 free(network->filename);
174
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);
180
181 free(network->description);
182 free(network->dhcp_vendor_class_identifier);
183
184 free(network->mac);
185
186 strv_free(network->ntp);
187 strv_free(network->dns);
188 strv_free(network->domains);
189
190 netdev_unref(network->bridge);
191
192 netdev_unref(network->bond);
193
194 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
195 hashmap_remove(network->stacked_netdevs, netdev->ifname);
196 netdev_unref(netdev);
197 }
198 hashmap_free(network->stacked_netdevs);
199
200 while ((route = network->static_routes))
201 route_free(route);
202
203 while ((address = network->static_addresses))
204 address_free(address);
205
206 while ((fdb_entry = network->static_fdb_entries))
207 fdb_entry_free(fdb_entry);
208
209 hashmap_free(network->addresses_by_section);
210 hashmap_free(network->routes_by_section);
211 hashmap_free(network->fdb_entries_by_section);
212
213 if (network->manager && network->manager->networks)
214 LIST_REMOVE(networks, network->manager->networks, network);
215
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);
220
221 free(network);
222 }
223
224 int network_get(Manager *manager, struct udev_device *device,
225 const char *ifname, const struct ether_addr *address,
226 Network **ret) {
227 Network *network;
228
229 assert(manager);
230 assert(ret);
231
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,
237 network->match_arch,
238 address,
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),
243 ifname)) {
244 if (network->match_name) {
245 const char *attr;
246 uint8_t name_assign_type = NET_NAME_UNKNOWN;
247
248 attr = udev_device_get_sysattr_value(device, "name_assign_type");
249 if (attr)
250 (void)safe_atou8(attr, &name_assign_type);
251
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);
255 else
256 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
257 } else
258 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
259
260 *ret = network;
261 return 0;
262 }
263 }
264
265 *ret = NULL;
266
267 return -ENOENT;
268 }
269
270 int network_apply(Manager *manager, Network *network, Link *link) {
271 int r;
272
273 link->network = network;
274
275 if (network->ipv4ll_route) {
276 Route *route;
277
278 r = route_new_static(network, 0, &route);
279 if (r < 0)
280 return r;
281
282 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
283 if (r == 0)
284 return -EINVAL;
285 if (r < 0)
286 return -errno;
287
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;
293 }
294
295 if (network->dns || network->ntp) {
296 r = link_save(link);
297 if (r < 0)
298 return r;
299 }
300
301 return 0;
302 }
303
304 int config_parse_netdev(const char *unit,
305 const char *filename,
306 unsigned line,
307 const char *section,
308 unsigned section_line,
309 const char *lvalue,
310 int ltype,
311 const char *rvalue,
312 void *data,
313 void *userdata) {
314 Network *network = userdata;
315 _cleanup_free_ char *kind_string = NULL;
316 char *p;
317 NetDev *netdev;
318 NetDevKind kind;
319 int r;
320
321 assert(filename);
322 assert(lvalue);
323 assert(rvalue);
324 assert(data);
325
326 kind_string = strdup(lvalue);
327 if (!kind_string)
328 return log_oom();
329
330 /* the keys are CamelCase versions of the kind */
331 for (p = kind_string; *p; p++)
332 *p = tolower(*p);
333
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);
338 return 0;
339 }
340
341 r = netdev_get(network->manager, rvalue, &netdev);
342 if (r < 0) {
343 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
344 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
345 return 0;
346 }
347
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);
351 return 0;
352 }
353
354 switch (kind) {
355 case NETDEV_KIND_BRIDGE:
356 network->bridge = netdev;
357
358 break;
359 case NETDEV_KIND_BOND:
360 network->bond = netdev;
361
362 break;
363 case NETDEV_KIND_VLAN:
364 case NETDEV_KIND_MACVLAN:
365 case NETDEV_KIND_VXLAN:
366 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
367 if (r < 0) {
368 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
369 "Can not add VLAN '%s' to network: %s",
370 rvalue, strerror(-r));
371 return 0;
372 }
373
374 break;
375 default:
376 assert_not_reached("Can not parse NetDev");
377 }
378
379 netdev_ref(netdev);
380
381 return 0;
382 }
383
384 int config_parse_domains(const char *unit,
385 const char *filename,
386 unsigned line,
387 const char *section,
388 unsigned section_line,
389 const char *lvalue,
390 int ltype,
391 const char *rvalue,
392 void *data,
393 void *userdata) {
394 Network *network = userdata;
395 char ***domains = data;
396 char **domain;
397 int r;
398
399 r = config_parse_strv(unit, filename, line, section, section_line,
400 lvalue, ltype, rvalue, domains, userdata);
401 if (r < 0)
402 return r;
403
404 strv_uniq(*domains);
405 network->wildcard_domain = !!strv_find(*domains, "*");
406
407 STRV_FOREACH(domain, *domains) {
408 if (is_localhost(*domain))
409 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
410 else if (!hostname_is_valid(*domain)) {
411 if (!streq(*domain, "*"))
412 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
413 } else
414 continue;
415
416 strv_remove(*domains, *domain);
417
418 /* We removed one entry, make sure we don't skip the next one */
419 domain--;
420 }
421
422 return 0;
423 }
424
425 int config_parse_tunnel(const char *unit,
426 const char *filename,
427 unsigned line,
428 const char *section,
429 unsigned section_line,
430 const char *lvalue,
431 int ltype,
432 const char *rvalue,
433 void *data,
434 void *userdata) {
435 Network *network = userdata;
436 NetDev *netdev;
437 int r;
438
439 assert(filename);
440 assert(lvalue);
441 assert(rvalue);
442 assert(data);
443
444 r = netdev_get(network->manager, rvalue, &netdev);
445 if (r < 0) {
446 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
447 "Tunnel is invalid, ignoring assignment: %s", rvalue);
448 return 0;
449 }
450
451 if (netdev->kind != NETDEV_KIND_IPIP &&
452 netdev->kind != NETDEV_KIND_SIT &&
453 netdev->kind != NETDEV_KIND_GRE &&
454 netdev->kind != NETDEV_KIND_VTI) {
455 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
456 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
457 return 0;
458 }
459
460 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
461 if (r < 0) {
462 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
463 "Can not add VLAN '%s' to network: %s",
464 rvalue, strerror(-r));
465 return 0;
466 }
467
468 netdev_ref(netdev);
469
470 return 0;
471 }
472
473 int config_parse_dhcp(
474 const char* unit,
475 const char *filename,
476 unsigned line,
477 const char *section,
478 unsigned section_line,
479 const char *lvalue,
480 int ltype,
481 const char *rvalue,
482 void *data,
483 void *userdata) {
484
485 AddressFamilyBoolean *dhcp = data, s;
486
487 assert(filename);
488 assert(lvalue);
489 assert(rvalue);
490 assert(data);
491
492 s = address_family_boolean_from_string(rvalue);
493 if (s < 0) {
494
495 /* Previously, we had a slightly different enum here,
496 * support its values for compatbility. */
497
498 if (streq(rvalue, "none"))
499 s = ADDRESS_FAMILY_NO;
500 else if (streq(rvalue, "v4"))
501 s = ADDRESS_FAMILY_IPV4;
502 else if (streq(rvalue, "v6"))
503 s = ADDRESS_FAMILY_IPV6;
504 else if (streq(rvalue, "both"))
505 s = ADDRESS_FAMILY_YES;
506 else {
507 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
508 return 0;
509 }
510 }
511
512 *dhcp = s;
513 return 0;
514 }
515
516 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
517 [LLMNR_SUPPORT_NO] = "no",
518 [LLMNR_SUPPORT_YES] = "yes",
519 [LLMNR_SUPPORT_RESOLVE] = "resolve",
520 };
521
522 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
523
524 int config_parse_llmnr(
525 const char* unit,
526 const char *filename,
527 unsigned line,
528 const char *section,
529 unsigned section_line,
530 const char *lvalue,
531 int ltype,
532 const char *rvalue,
533 void *data,
534 void *userdata) {
535
536 LLMNRSupport *llmnr = data;
537 int k;
538
539 assert(filename);
540 assert(lvalue);
541 assert(rvalue);
542 assert(data);
543
544 /* Our enum shall be a superset of booleans, hence first try
545 * to parse as boolean, and then as enum */
546
547 k = parse_boolean(rvalue);
548 if (k > 0)
549 *llmnr = LLMNR_SUPPORT_YES;
550 else if (k == 0)
551 *llmnr = LLMNR_SUPPORT_NO;
552 else {
553 LLMNRSupport s;
554
555 s = llmnr_support_from_string(rvalue);
556 if (s < 0){
557 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
558 return 0;
559 }
560
561 *llmnr = s;
562 }
563
564 return 0;
565 }