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