]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
networkd: generalize IPv4LL to LinkLocal
[thirdparty/systemd.git] / src / network / networkd-network.c
CommitLineData
f579559b
TG
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
69a93e7d 22#include <ctype.h>
987efa17 23#include <net/if.h>
69a93e7d 24
f579559b
TG
25#include "path-util.h"
26#include "conf-files.h"
27#include "conf-parser.h"
477e73b5 28#include "util.h"
5a8bcb67
LP
29#include "networkd.h"
30#include "networkd-netdev.h"
31#include "networkd-link.h"
32#include "network-internal.h"
f579559b
TG
33
34static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _cleanup_fclose_ FILE *file = NULL;
dbffab87 37 char *d;
b3070dc0
TG
38 Route *route;
39 Address *address;
f579559b
TG
40 int r;
41
bf1bc670
TA
42 assert(manager);
43 assert(filename);
44
f579559b
TG
45 file = fopen(filename, "re");
46 if (!file) {
47 if (errno == ENOENT)
48 return 0;
49 else
ecb08ec6 50 return -errno;
f579559b
TG
51 }
52
ed88bcfb
ZJS
53 if (null_or_empty_fd(fileno(file))) {
54 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
55 return 0;
56 }
57
f579559b
TG
58 network = new0(Network, 1);
59 if (!network)
60 return log_oom();
61
5a3eb5a7
TG
62 network->manager = manager;
63
f048a16b
TG
64 LIST_HEAD_INIT(network->static_addresses);
65 LIST_HEAD_INIT(network->static_routes);
b98b483b 66 LIST_HEAD_INIT(network->static_fdb_entries);
f579559b 67
d5099efc 68 network->stacked_netdevs = hashmap_new(&string_hash_ops);
6a0a2f86 69 if (!network->stacked_netdevs)
326cb406
SS
70 return log_oom();
71
d5099efc 72 network->addresses_by_section = hashmap_new(NULL);
6ae115c1
TG
73 if (!network->addresses_by_section)
74 return log_oom();
75
d5099efc 76 network->routes_by_section = hashmap_new(NULL);
6ae115c1
TG
77 if (!network->routes_by_section)
78 return log_oom();
79
b98b483b
AR
80 network->fdb_entries_by_section = hashmap_new(NULL);
81 if (!network->fdb_entries_by_section)
82 return log_oom();
83
6ae115c1
TG
84 network->filename = strdup(filename);
85 if (!network->filename)
86 return log_oom();
87
dbffab87
TG
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
cb9fc36a 100 network->dhcp = ADDRESS_FAMILY_NO;
bcb7a07e 101 network->dhcp_ntp = true;
5be4d38e 102 network->dhcp_dns = true;
1346b1f0 103 network->dhcp_hostname = true;
e1ea665e 104 network->dhcp_routes = true;
4cc7a82c 105 network->dhcp_sendhost = true;
84b5b79a 106 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
5be4d38e 107
bd8f6538
TG
108 network->llmnr = LLMNR_SUPPORT_YES;
109
d0d6a4cd
TG
110 network->link_local = ADDRESS_FAMILY_IPV6;
111
e9f3d2d5 112 r = config_parse(NULL, filename, file,
c106cc36
TG
113 "Match\0"
114 "Link\0"
115 "Network\0"
116 "Address\0"
117 "Route\0"
118 "DHCP\0"
119 "DHCPv4\0"
b98b483b
AR
120 "Bridge\0"
121 "BridgeFDB\0",
e9f3d2d5 122 config_item_perf_lookup, network_network_gperf_lookup,
36f822c4
ZJS
123 false, false, true, network);
124 if (r < 0)
f579559b 125 return r;
f579559b 126
5a8bcb67
LP
127 /* IPMasquerade=yes implies IPForward=yes */
128 if (network->ip_masquerade)
769d324c 129 network->ip_forward |= ADDRESS_FAMILY_IPV4;
5a8bcb67 130
f579559b 131 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 132
dbffab87
TG
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
3d3d4255 141 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
142 if (!route->family) {
143 log_warning("Route section without Gateway field configured in %s. "
144 "Ignoring", filename);
145 return 0;
146 }
b3070dc0
TG
147 }
148
3d3d4255 149 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
150 if (!address->family) {
151 log_warning("Address section without Address field configured in %s. "
152 "Ignoring", filename);
153 return 0;
154 }
155 }
156
f579559b
TG
157 network = NULL;
158
159 return 0;
160}
161
162int network_load(Manager *manager) {
163 Network *network;
477e73b5
ZJS
164 _cleanup_strv_free_ char **files = NULL;
165 char **f;
f579559b
TG
166 int r;
167
168 assert(manager);
169
170 while ((network = manager->networks))
171 network_free(network);
172
2ad8416d 173 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
f647962d
MS
174 if (r < 0)
175 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
176
177 STRV_FOREACH_BACKWARDS(f, files) {
178 r = network_load_one(manager, *f);
179 if (r < 0)
180 return r;
181 }
182
f579559b
TG
183 return 0;
184}
185
f579559b 186void network_free(Network *network) {
47e2dc31 187 NetDev *netdev;
f579559b
TG
188 Route *route;
189 Address *address;
b98b483b 190 FdbEntry *fdb_entry;
06f021a8 191 Iterator i;
f579559b
TG
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);
edb85f0d 205 free(network->dhcp_vendor_class_identifier);
f579559b 206
c106cc36
TG
207 free(network->mac);
208
b0e39c82
TG
209 strv_free(network->ntp);
210 strv_free(network->dns);
486d1a81 211 strv_free(network->domains);
3bef724f 212
47e2dc31
TG
213 netdev_unref(network->bridge);
214
215 netdev_unref(network->bond);
216
2c36be2f
TG
217 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
218 hashmap_remove(network->stacked_netdevs, netdev->ifname);
326cb406 219 netdev_unref(netdev);
2c36be2f 220 }
6a0a2f86 221 hashmap_free(network->stacked_netdevs);
326cb406 222
f048a16b 223 while ((route = network->static_routes))
f579559b
TG
224 route_free(route);
225
f048a16b 226 while ((address = network->static_addresses))
f579559b
TG
227 address_free(address);
228
b98b483b
AR
229 while ((fdb_entry = network->static_fdb_entries))
230 fdb_entry_free(fdb_entry);
231
6ae115c1
TG
232 hashmap_free(network->addresses_by_section);
233 hashmap_free(network->routes_by_section);
b98b483b 234 hashmap_free(network->fdb_entries_by_section);
6ae115c1 235
dbffab87
TG
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);
f579559b 245
79e16ce3
LP
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
f579559b
TG
251 free(network);
252}
253
dbffab87
TG
254int 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
505f8da7
TG
270int network_get(Manager *manager, struct udev_device *device,
271 const char *ifname, const struct ether_addr *address,
272 Network **ret) {
f579559b
TG
273 Network *network;
274
275 assert(manager);
f579559b
TG
276 assert(ret);
277
f579559b
TG
278 LIST_FOREACH(networks, network, manager->networks) {
279 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
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),
32bc8adc 289 ifname)) {
32bc8adc 290 if (network->match_name) {
ca6038b8
TG
291 const char *attr;
292 uint8_t name_assign_type = NET_NAME_UNKNOWN;
293
32bc8adc 294 attr = udev_device_get_sysattr_value(device, "name_assign_type");
285760fe
DR
295 if (attr)
296 (void)safe_atou8(attr, &name_assign_type);
32bc8adc
TG
297
298 if (name_assign_type == NET_NAME_ENUM)
ca6038b8 299 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
32bc8adc
TG
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
f579559b
TG
306 *ret = network;
307 return 0;
308 }
309 }
310
311 *ret = NULL;
312
313 return -ENOENT;
314}
315
316int network_apply(Manager *manager, Network *network, Link *link) {
f579559b
TG
317 int r;
318
f579559b
TG
319 link->network = network;
320
bfa695b5
TG
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
bcb7a07e 341 if (network->dns || network->ntp) {
091a364c 342 r = link_save(link);
3bef724f
TG
343 if (r < 0)
344 return r;
345 }
346
f579559b
TG
347 return 0;
348}
02b59d57 349
69a93e7d 350int config_parse_netdev(const char *unit,
02b59d57
TG
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;
31d0ac36
TG
361 _cleanup_free_ char *kind_string = NULL;
362 char *p;
1a436809 363 NetDev *netdev;
69a93e7d 364 NetDevKind kind;
02b59d57
TG
365 int r;
366
367 assert(filename);
368 assert(lvalue);
369 assert(rvalue);
370 assert(data);
371
69a93e7d
TG
372 kind_string = strdup(lvalue);
373 if (!kind_string)
374 return log_oom();
52433f6b 375
69a93e7d
TG
376 /* the keys are CamelCase versions of the kind */
377 for (p = kind_string; *p; p++)
378 *p = tolower(*p);
52433f6b 379
69a93e7d
TG
380 kind = netdev_kind_from_string(kind_string);
381 if (kind == _NETDEV_KIND_INVALID) {
52433f6b 382 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 383 "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
384 return 0;
385 }
386
54abf461
TG
387 r = netdev_get(network->manager, rvalue, &netdev);
388 if (r < 0) {
389 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 390 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
391 return 0;
392 }
393
69a93e7d 394 if (netdev->kind != kind) {
54abf461 395 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 396 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
397 return 0;
398 }
399
69a93e7d
TG
400 switch (kind) {
401 case NETDEV_KIND_BRIDGE:
402 network->bridge = netdev;
54abf461 403
69a93e7d
TG
404 break;
405 case NETDEV_KIND_BOND:
406 network->bond = netdev;
fe6b2d55 407
69a93e7d
TG
408 break;
409 case NETDEV_KIND_VLAN:
69a93e7d 410 case NETDEV_KIND_MACVLAN:
c4a5ddc9 411 case NETDEV_KIND_IPVLAN:
326cb406 412 case NETDEV_KIND_VXLAN:
6a0a2f86 413 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406
SS
414 if (r < 0) {
415 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
6a0a2f86 416 "Can not add VLAN '%s' to network: %s",
aa9f1140 417 rvalue, strerror(-r));
326cb406
SS
418 return 0;
419 }
420
69a93e7d
TG
421 break;
422 default:
423 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
424 }
425
47e2dc31
TG
426 netdev_ref(netdev);
427
fe6b2d55
TG
428 return 0;
429}
7951dea2 430
6192b846
TG
431int 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) {
67272d15 441 Network *network = userdata;
6192b846
TG
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);
f15b6e5a 452 network->wildcard_domain = !!strv_find(*domains, "*");
67272d15 453
40274ed6
LP
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 }
f15b6e5a 468
6192b846
TG
469 return 0;
470}
471
7951dea2
SS
472int 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 &&
a613382b 500 netdev->kind != NETDEV_KIND_GRE &&
1af2536a 501 netdev->kind != NETDEV_KIND_GRETAP &&
b16492f8
SS
502 netdev->kind != NETDEV_KIND_IP6GRE &&
503 netdev->kind != NETDEV_KIND_IP6GRETAP &&
855ee1a1
SS
504 netdev->kind != NETDEV_KIND_VTI &&
505 netdev->kind != NETDEV_KIND_IP6TNL
506 ) {
7951dea2
SS
507 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
508 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
509 return 0;
510 }
511
6a0a2f86
TG
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);
7951dea2
SS
521
522 return 0;
523}
bd8f6538 524
d0d6a4cd
TG
525int 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
bd8f6538
TG
556int 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
cb9fc36a 568 AddressFamilyBoolean *dhcp = data, s;
bd8f6538
TG
569
570 assert(filename);
571 assert(lvalue);
572 assert(rvalue);
573 assert(data);
574
769d324c
LP
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
cb9fc36a
LP
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);
bd8f6538
TG
595 return 0;
596 }
bd8f6538
TG
597 }
598
cb9fc36a 599 *dhcp = s;
bd8f6538
TG
600 return 0;
601}
602
603static 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
609DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
610
611int 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}