chiark / gitweb /
networkd: add support for Uplink Failure Detection
[elogind.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 "networkd.h"
29 #include "networkd-netdev.h"
30 #include "networkd-link.h"
31 #include "network-internal.h"
32
33 static int network_load_one(Manager *manager, const char *filename) {
34         _cleanup_network_free_ Network *network = NULL;
35         _cleanup_fclose_ FILE *file = NULL;
36         char *d;
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->name = strdup(basename(filename));
88         if (!network->name)
89                 return log_oom();
90
91         d = strrchr(network->name, '.');
92         if (!d)
93                 return -EINVAL;
94
95         assert(streq(d, ".network"));
96
97         *d = '\0';
98
99         network->dhcp = ADDRESS_FAMILY_NO;
100         network->dhcp_ntp = true;
101         network->dhcp_dns = true;
102         network->dhcp_hostname = true;
103         network->dhcp_routes = true;
104         network->dhcp_sendhost = true;
105         network->dhcp_route_metric = DHCP_ROUTE_METRIC;
106
107         network->llmnr = LLMNR_SUPPORT_YES;
108
109         network->link_local = ADDRESS_FAMILY_IPV6;
110
111         r = config_parse(NULL, filename, file,
112                          "Match\0"
113                          "Link\0"
114                          "Network\0"
115                          "Address\0"
116                          "Route\0"
117                          "DHCP\0"
118                          "DHCPv4\0"
119                          "Bridge\0"
120                          "BridgeFDB\0",
121                          config_item_perf_lookup, network_network_gperf_lookup,
122                          false, false, true, network);
123         if (r < 0)
124                 return r;
125
126         /* IPMasquerade=yes implies IPForward=yes */
127         if (network->ip_masquerade)
128                 network->ip_forward |= ADDRESS_FAMILY_IPV4;
129
130         LIST_PREPEND(networks, manager->networks, network);
131
132         r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
133         if (r < 0)
134                 return r;
135
136         r = hashmap_put(manager->networks_by_name, network->name, network);
137         if (r < 0)
138                 return r;
139
140         LIST_FOREACH(routes, route, network->static_routes) {
141                 if (!route->family) {
142                         log_warning("Route section without Gateway field configured in %s. "
143                                     "Ignoring", filename);
144                         return 0;
145                 }
146         }
147
148         LIST_FOREACH(addresses, address, network->static_addresses) {
149                 if (!address->family) {
150                         log_warning("Address section without Address field configured in %s. "
151                                     "Ignoring", filename);
152                         return 0;
153                 }
154         }
155
156         network = NULL;
157
158         return 0;
159 }
160
161 int network_load(Manager *manager) {
162         Network *network;
163         _cleanup_strv_free_ char **files = NULL;
164         char **f;
165         int r;
166
167         assert(manager);
168
169         while ((network = manager->networks))
170                 network_free(network);
171
172         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
173         if (r < 0)
174                 return log_error_errno(r, "Failed to enumerate network files: %m");
175
176         STRV_FOREACH_BACKWARDS(f, files) {
177                 r = network_load_one(manager, *f);
178                 if (r < 0)
179                         return r;
180         }
181
182         return 0;
183 }
184
185 void network_free(Network *network) {
186         NetDev *netdev;
187         Route *route;
188         Address *address;
189         FdbEntry *fdb_entry;
190         Iterator i;
191
192         if (!network)
193                 return;
194
195         free(network->filename);
196
197         free(network->match_mac);
198         strv_free(network->match_path);
199         strv_free(network->match_driver);
200         strv_free(network->match_type);
201         strv_free(network->match_name);
202
203         free(network->description);
204         free(network->dhcp_vendor_class_identifier);
205
206         free(network->mac);
207
208         strv_free(network->ntp);
209         strv_free(network->dns);
210         strv_free(network->domains);
211         strv_free(network->bind_carrier);
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(llmnr);
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 }
653
654 int config_parse_ipv6token(
655                 const char* unit,
656                 const char *filename,
657                 unsigned line,
658                 const char *section,
659                 unsigned section_line,
660                 const char *lvalue,
661                 int ltype,
662                 const char *rvalue,
663                 void *data,
664                 void *userdata) {
665
666         union in_addr_union buffer;
667         struct in6_addr *token = data;
668         int r;
669
670         assert(filename);
671         assert(lvalue);
672         assert(rvalue);
673         assert(token);
674
675         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
676         if (r < 0) {
677                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
678                 return 0;
679         }
680
681         r = in_addr_is_null(AF_INET6, &buffer);
682         if (r < 0) {
683                 log_syntax(unit, LOG_ERR, filename, line, -r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
684                 return 0;
685         }
686
687         if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
688                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
689                 return 0;
690         }
691
692         *token = buffer.in6;
693
694         return 0;
695 }