]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
networkd: generalize IPv4LL to LinkLocal
[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 char *d;
38 Route *route;
39 Address *address;
40 int r;
41
42 assert(manager);
43 assert(filename);
44
45 file = fopen(filename, "re");
46 if (!file) {
47 if (errno == ENOENT)
48 return 0;
49 else
50 return -errno;
51 }
52
53 if (null_or_empty_fd(fileno(file))) {
54 log_debug("Skipping empty file: %s", filename);
55 return 0;
56 }
57
58 network = new0(Network, 1);
59 if (!network)
60 return log_oom();
61
62 network->manager = manager;
63
64 LIST_HEAD_INIT(network->static_addresses);
65 LIST_HEAD_INIT(network->static_routes);
66 LIST_HEAD_INIT(network->static_fdb_entries);
67
68 network->stacked_netdevs = hashmap_new(&string_hash_ops);
69 if (!network->stacked_netdevs)
70 return log_oom();
71
72 network->addresses_by_section = hashmap_new(NULL);
73 if (!network->addresses_by_section)
74 return log_oom();
75
76 network->routes_by_section = hashmap_new(NULL);
77 if (!network->routes_by_section)
78 return log_oom();
79
80 network->fdb_entries_by_section = hashmap_new(NULL);
81 if (!network->fdb_entries_by_section)
82 return log_oom();
83
84 network->filename = strdup(filename);
85 if (!network->filename)
86 return log_oom();
87
88 network->name = strdup(basename(filename));
89 if (!network->name)
90 return log_oom();
91
92 d = strrchr(network->name, '.');
93 if (!d)
94 return -EINVAL;
95
96 assert(streq(d, ".network"));
97
98 *d = '\0';
99
100 network->dhcp = ADDRESS_FAMILY_NO;
101 network->dhcp_ntp = true;
102 network->dhcp_dns = true;
103 network->dhcp_hostname = true;
104 network->dhcp_routes = true;
105 network->dhcp_sendhost = true;
106 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
107
108 network->llmnr = LLMNR_SUPPORT_YES;
109
110 network->link_local = ADDRESS_FAMILY_IPV6;
111
112 r = config_parse(NULL, filename, file,
113 "Match\0"
114 "Link\0"
115 "Network\0"
116 "Address\0"
117 "Route\0"
118 "DHCP\0"
119 "DHCPv4\0"
120 "Bridge\0"
121 "BridgeFDB\0",
122 config_item_perf_lookup, network_network_gperf_lookup,
123 false, false, true, network);
124 if (r < 0)
125 return r;
126
127 /* IPMasquerade=yes implies IPForward=yes */
128 if (network->ip_masquerade)
129 network->ip_forward |= ADDRESS_FAMILY_IPV4;
130
131 LIST_PREPEND(networks, manager->networks, network);
132
133 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
134 if (r < 0)
135 return r;
136
137 r = hashmap_put(manager->networks_by_name, network->name, network);
138 if (r < 0)
139 return r;
140
141 LIST_FOREACH(routes, route, network->static_routes) {
142 if (!route->family) {
143 log_warning("Route section without Gateway field configured in %s. "
144 "Ignoring", filename);
145 return 0;
146 }
147 }
148
149 LIST_FOREACH(addresses, address, network->static_addresses) {
150 if (!address->family) {
151 log_warning("Address section without Address field configured in %s. "
152 "Ignoring", filename);
153 return 0;
154 }
155 }
156
157 network = NULL;
158
159 return 0;
160 }
161
162 int network_load(Manager *manager) {
163 Network *network;
164 _cleanup_strv_free_ char **files = NULL;
165 char **f;
166 int r;
167
168 assert(manager);
169
170 while ((network = manager->networks))
171 network_free(network);
172
173 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
174 if (r < 0)
175 return log_error_errno(r, "Failed to enumerate network files: %m");
176
177 STRV_FOREACH_BACKWARDS(f, files) {
178 r = network_load_one(manager, *f);
179 if (r < 0)
180 return r;
181 }
182
183 return 0;
184 }
185
186 void network_free(Network *network) {
187 NetDev *netdev;
188 Route *route;
189 Address *address;
190 FdbEntry *fdb_entry;
191 Iterator i;
192
193 if (!network)
194 return;
195
196 free(network->filename);
197
198 free(network->match_mac);
199 free(network->match_path);
200 free(network->match_driver);
201 free(network->match_type);
202 free(network->match_name);
203
204 free(network->description);
205 free(network->dhcp_vendor_class_identifier);
206
207 free(network->mac);
208
209 strv_free(network->ntp);
210 strv_free(network->dns);
211 strv_free(network->domains);
212
213 netdev_unref(network->bridge);
214
215 netdev_unref(network->bond);
216
217 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
218 hashmap_remove(network->stacked_netdevs, netdev->ifname);
219 netdev_unref(netdev);
220 }
221 hashmap_free(network->stacked_netdevs);
222
223 while ((route = network->static_routes))
224 route_free(route);
225
226 while ((address = network->static_addresses))
227 address_free(address);
228
229 while ((fdb_entry = network->static_fdb_entries))
230 fdb_entry_free(fdb_entry);
231
232 hashmap_free(network->addresses_by_section);
233 hashmap_free(network->routes_by_section);
234 hashmap_free(network->fdb_entries_by_section);
235
236 if (network->manager) {
237 if (network->manager->networks)
238 LIST_REMOVE(networks, network->manager->networks, network);
239
240 if (network->manager->networks_by_name)
241 hashmap_remove(network->manager->networks_by_name, network->name);
242 }
243
244 free(network->name);
245
246 condition_free_list(network->match_host);
247 condition_free_list(network->match_virt);
248 condition_free_list(network->match_kernel);
249 condition_free_list(network->match_arch);
250
251 free(network);
252 }
253
254 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
255 Network *network;
256
257 assert(manager);
258 assert(name);
259 assert(ret);
260
261 network = hashmap_get(manager->networks_by_name, name);
262 if (!network)
263 return -ENOENT;
264
265 *ret = network;
266
267 return 0;
268 }
269
270 int network_get(Manager *manager, struct udev_device *device,
271 const char *ifname, const struct ether_addr *address,
272 Network **ret) {
273 Network *network;
274
275 assert(manager);
276 assert(ret);
277
278 LIST_FOREACH(networks, network, manager->networks) {
279 if (net_match_config(network->match_mac, network->match_path,
280 network->match_driver, network->match_type,
281 network->match_name, network->match_host,
282 network->match_virt, network->match_kernel,
283 network->match_arch,
284 address,
285 udev_device_get_property_value(device, "ID_PATH"),
286 udev_device_get_driver(udev_device_get_parent(device)),
287 udev_device_get_property_value(device, "ID_NET_DRIVER"),
288 udev_device_get_devtype(device),
289 ifname)) {
290 if (network->match_name) {
291 const char *attr;
292 uint8_t name_assign_type = NET_NAME_UNKNOWN;
293
294 attr = udev_device_get_sysattr_value(device, "name_assign_type");
295 if (attr)
296 (void)safe_atou8(attr, &name_assign_type);
297
298 if (name_assign_type == NET_NAME_ENUM)
299 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
300 IFNAMSIZ, ifname, network->filename);
301 else
302 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
303 } else
304 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
305
306 *ret = network;
307 return 0;
308 }
309 }
310
311 *ret = NULL;
312
313 return -ENOENT;
314 }
315
316 int network_apply(Manager *manager, Network *network, Link *link) {
317 int r;
318
319 link->network = network;
320
321 if (network->ipv4ll_route) {
322 Route *route;
323
324 r = route_new_static(network, 0, &route);
325 if (r < 0)
326 return r;
327
328 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
329 if (r == 0)
330 return -EINVAL;
331 if (r < 0)
332 return -errno;
333
334 route->family = AF_INET;
335 route->dst_prefixlen = 16;
336 route->scope = RT_SCOPE_LINK;
337 route->metrics = IPV4LL_ROUTE_METRIC;
338 route->protocol = RTPROT_STATIC;
339 }
340
341 if (network->dns || network->ntp) {
342 r = link_save(link);
343 if (r < 0)
344 return r;
345 }
346
347 return 0;
348 }
349
350 int config_parse_netdev(const char *unit,
351 const char *filename,
352 unsigned line,
353 const char *section,
354 unsigned section_line,
355 const char *lvalue,
356 int ltype,
357 const char *rvalue,
358 void *data,
359 void *userdata) {
360 Network *network = userdata;
361 _cleanup_free_ char *kind_string = NULL;
362 char *p;
363 NetDev *netdev;
364 NetDevKind kind;
365 int r;
366
367 assert(filename);
368 assert(lvalue);
369 assert(rvalue);
370 assert(data);
371
372 kind_string = strdup(lvalue);
373 if (!kind_string)
374 return log_oom();
375
376 /* the keys are CamelCase versions of the kind */
377 for (p = kind_string; *p; p++)
378 *p = tolower(*p);
379
380 kind = netdev_kind_from_string(kind_string);
381 if (kind == _NETDEV_KIND_INVALID) {
382 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
383 "Invalid NetDev kind: %s", lvalue);
384 return 0;
385 }
386
387 r = netdev_get(network->manager, rvalue, &netdev);
388 if (r < 0) {
389 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
390 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
391 return 0;
392 }
393
394 if (netdev->kind != kind) {
395 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
396 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
397 return 0;
398 }
399
400 switch (kind) {
401 case NETDEV_KIND_BRIDGE:
402 network->bridge = netdev;
403
404 break;
405 case NETDEV_KIND_BOND:
406 network->bond = netdev;
407
408 break;
409 case NETDEV_KIND_VLAN:
410 case NETDEV_KIND_MACVLAN:
411 case NETDEV_KIND_IPVLAN:
412 case NETDEV_KIND_VXLAN:
413 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
414 if (r < 0) {
415 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
416 "Can not add VLAN '%s' to network: %s",
417 rvalue, strerror(-r));
418 return 0;
419 }
420
421 break;
422 default:
423 assert_not_reached("Can not parse NetDev");
424 }
425
426 netdev_ref(netdev);
427
428 return 0;
429 }
430
431 int config_parse_domains(const char *unit,
432 const char *filename,
433 unsigned line,
434 const char *section,
435 unsigned section_line,
436 const char *lvalue,
437 int ltype,
438 const char *rvalue,
439 void *data,
440 void *userdata) {
441 Network *network = userdata;
442 char ***domains = data;
443 char **domain;
444 int r;
445
446 r = config_parse_strv(unit, filename, line, section, section_line,
447 lvalue, ltype, rvalue, domains, userdata);
448 if (r < 0)
449 return r;
450
451 strv_uniq(*domains);
452 network->wildcard_domain = !!strv_find(*domains, "*");
453
454 STRV_FOREACH(domain, *domains) {
455 if (is_localhost(*domain))
456 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
457 else if (!hostname_is_valid(*domain)) {
458 if (!streq(*domain, "*"))
459 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
460 } else
461 continue;
462
463 strv_remove(*domains, *domain);
464
465 /* We removed one entry, make sure we don't skip the next one */
466 domain--;
467 }
468
469 return 0;
470 }
471
472 int config_parse_tunnel(const char *unit,
473 const char *filename,
474 unsigned line,
475 const char *section,
476 unsigned section_line,
477 const char *lvalue,
478 int ltype,
479 const char *rvalue,
480 void *data,
481 void *userdata) {
482 Network *network = userdata;
483 NetDev *netdev;
484 int r;
485
486 assert(filename);
487 assert(lvalue);
488 assert(rvalue);
489 assert(data);
490
491 r = netdev_get(network->manager, rvalue, &netdev);
492 if (r < 0) {
493 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
494 "Tunnel is invalid, ignoring assignment: %s", rvalue);
495 return 0;
496 }
497
498 if (netdev->kind != NETDEV_KIND_IPIP &&
499 netdev->kind != NETDEV_KIND_SIT &&
500 netdev->kind != NETDEV_KIND_GRE &&
501 netdev->kind != NETDEV_KIND_GRETAP &&
502 netdev->kind != NETDEV_KIND_IP6GRE &&
503 netdev->kind != NETDEV_KIND_IP6GRETAP &&
504 netdev->kind != NETDEV_KIND_VTI &&
505 netdev->kind != NETDEV_KIND_IP6TNL
506 ) {
507 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
508 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
509 return 0;
510 }
511
512 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
513 if (r < 0) {
514 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
515 "Can not add VLAN '%s' to network: %s",
516 rvalue, strerror(-r));
517 return 0;
518 }
519
520 netdev_ref(netdev);
521
522 return 0;
523 }
524
525 int config_parse_ipv4ll(
526 const char* unit,
527 const char *filename,
528 unsigned line,
529 const char *section,
530 unsigned section_line,
531 const char *lvalue,
532 int ltype,
533 const char *rvalue,
534 void *data,
535 void *userdata) {
536
537 AddressFamilyBoolean *link_local = data;
538
539 assert(filename);
540 assert(lvalue);
541 assert(rvalue);
542 assert(data);
543
544 /* Note that this is mostly like
545 * config_parse_address_family_boolean(), except that it
546 * applies only to IPv4 */
547
548 if (parse_boolean(rvalue))
549 *link_local |= ADDRESS_FAMILY_IPV4;
550 else
551 *link_local &= ~ADDRESS_FAMILY_IPV4;
552
553 return 0;
554 }
555
556 int config_parse_dhcp(
557 const char* unit,
558 const char *filename,
559 unsigned line,
560 const char *section,
561 unsigned section_line,
562 const char *lvalue,
563 int ltype,
564 const char *rvalue,
565 void *data,
566 void *userdata) {
567
568 AddressFamilyBoolean *dhcp = data, s;
569
570 assert(filename);
571 assert(lvalue);
572 assert(rvalue);
573 assert(data);
574
575 /* Note that this is mostly like
576 * config_parse_address_family_boolean(), except that it
577 * understands some old names for the enum values */
578
579 s = address_family_boolean_from_string(rvalue);
580 if (s < 0) {
581
582 /* Previously, we had a slightly different enum here,
583 * support its values for compatbility. */
584
585 if (streq(rvalue, "none"))
586 s = ADDRESS_FAMILY_NO;
587 else if (streq(rvalue, "v4"))
588 s = ADDRESS_FAMILY_IPV4;
589 else if (streq(rvalue, "v6"))
590 s = ADDRESS_FAMILY_IPV6;
591 else if (streq(rvalue, "both"))
592 s = ADDRESS_FAMILY_YES;
593 else {
594 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
595 return 0;
596 }
597 }
598
599 *dhcp = s;
600 return 0;
601 }
602
603 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
604 [LLMNR_SUPPORT_NO] = "no",
605 [LLMNR_SUPPORT_YES] = "yes",
606 [LLMNR_SUPPORT_RESOLVE] = "resolve",
607 };
608
609 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
610
611 int config_parse_llmnr(
612 const char* unit,
613 const char *filename,
614 unsigned line,
615 const char *section,
616 unsigned section_line,
617 const char *lvalue,
618 int ltype,
619 const char *rvalue,
620 void *data,
621 void *userdata) {
622
623 LLMNRSupport *llmnr = data;
624 int k;
625
626 assert(filename);
627 assert(lvalue);
628 assert(rvalue);
629 assert(data);
630
631 /* Our enum shall be a superset of booleans, hence first try
632 * to parse as boolean, and then as enum */
633
634 k = parse_boolean(rvalue);
635 if (k > 0)
636 *llmnr = LLMNR_SUPPORT_YES;
637 else if (k == 0)
638 *llmnr = LLMNR_SUPPORT_NO;
639 else {
640 LLMNRSupport s;
641
642 s = llmnr_support_from_string(rvalue);
643 if (s < 0){
644 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
645 return 0;
646 }
647
648 *llmnr = s;
649 }
650
651 return 0;
652 }