chiark / gitweb /
78bbf74bff03e3c351d942d5a4878bc318bec1c9
[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         network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
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         strv_free(network->match_path);
200         strv_free(network->match_driver);
201         strv_free(network->match_type);
202         strv_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         strv_free(network->bind_carrier);
213
214         netdev_unref(network->bridge);
215
216         netdev_unref(network->bond);
217
218         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
219                 hashmap_remove(network->stacked_netdevs, netdev->ifname);
220                 netdev_unref(netdev);
221         }
222         hashmap_free(network->stacked_netdevs);
223
224         while ((route = network->static_routes))
225                 route_free(route);
226
227         while ((address = network->static_addresses))
228                 address_free(address);
229
230         while ((fdb_entry = network->static_fdb_entries))
231                 fdb_entry_free(fdb_entry);
232
233         hashmap_free(network->addresses_by_section);
234         hashmap_free(network->routes_by_section);
235         hashmap_free(network->fdb_entries_by_section);
236
237         if (network->manager) {
238                 if (network->manager->networks)
239                         LIST_REMOVE(networks, network->manager->networks, network);
240
241                 if (network->manager->networks_by_name)
242                         hashmap_remove(network->manager->networks_by_name, network->name);
243         }
244
245         free(network->name);
246
247         condition_free_list(network->match_host);
248         condition_free_list(network->match_virt);
249         condition_free_list(network->match_kernel);
250         condition_free_list(network->match_arch);
251
252         free(network);
253 }
254
255 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
256         Network *network;
257
258         assert(manager);
259         assert(name);
260         assert(ret);
261
262         network = hashmap_get(manager->networks_by_name, name);
263         if (!network)
264                 return -ENOENT;
265
266         *ret = network;
267
268         return 0;
269 }
270
271 int network_get(Manager *manager, struct udev_device *device,
272                 const char *ifname, const struct ether_addr *address,
273                 Network **ret) {
274         Network *network;
275         struct udev_device *parent;
276         const char *path, *parent_driver, *driver, *devtype;
277
278         assert(manager);
279         assert(ret);
280         assert(device);
281
282         path = udev_device_get_property_value(device, "ID_PATH");
283
284         parent = udev_device_get_parent(device);
285         if (parent)
286                 parent_driver = udev_device_get_driver(parent);
287         else
288                 parent_driver = NULL;
289
290         driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
291
292         devtype = udev_device_get_devtype(device);
293
294         LIST_FOREACH(networks, network, manager->networks) {
295                 if (net_match_config(network->match_mac, network->match_path,
296                                      network->match_driver, network->match_type,
297                                      network->match_name, network->match_host,
298                                      network->match_virt, network->match_kernel,
299                                      network->match_arch,
300                                      address, path, parent_driver, driver,
301                                      devtype, ifname)) {
302                         if (network->match_name) {
303                                 const char *attr;
304                                 uint8_t name_assign_type = NET_NAME_UNKNOWN;
305
306                                 attr = udev_device_get_sysattr_value(device, "name_assign_type");
307                                 if (attr)
308                                         (void) safe_atou8(attr, &name_assign_type);
309
310                                 if (name_assign_type == NET_NAME_ENUM)
311                                         log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
312                                                     IFNAMSIZ, ifname, network->filename);
313                                 else
314                                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
315                         } else
316                                 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
317
318                         *ret = network;
319                         return 0;
320                 }
321         }
322
323         *ret = NULL;
324
325         return -ENOENT;
326 }
327
328 int network_apply(Manager *manager, Network *network, Link *link) {
329         int r;
330
331         link->network = network;
332
333         if (network->ipv4ll_route) {
334                 Route *route;
335
336                 r = route_new_static(network, 0, &route);
337                 if (r < 0)
338                         return r;
339
340                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
341                 if (r == 0)
342                         return -EINVAL;
343                 if (r < 0)
344                         return -errno;
345
346                 route->family = AF_INET;
347                 route->dst_prefixlen = 16;
348                 route->scope = RT_SCOPE_LINK;
349                 route->metrics = IPV4LL_ROUTE_METRIC;
350                 route->protocol = RTPROT_STATIC;
351         }
352
353         if (network->dns || network->ntp) {
354                 r = link_save(link);
355                 if (r < 0)
356                         return r;
357         }
358
359         return 0;
360 }
361
362 int config_parse_netdev(const char *unit,
363                 const char *filename,
364                 unsigned line,
365                 const char *section,
366                 unsigned section_line,
367                 const char *lvalue,
368                 int ltype,
369                 const char *rvalue,
370                 void *data,
371                 void *userdata) {
372         Network *network = userdata;
373         _cleanup_free_ char *kind_string = NULL;
374         char *p;
375         NetDev *netdev;
376         NetDevKind kind;
377         int r;
378
379         assert(filename);
380         assert(lvalue);
381         assert(rvalue);
382         assert(data);
383
384         kind_string = strdup(lvalue);
385         if (!kind_string)
386                 return log_oom();
387
388         /* the keys are CamelCase versions of the kind */
389         for (p = kind_string; *p; p++)
390                 *p = tolower(*p);
391
392         kind = netdev_kind_from_string(kind_string);
393         if (kind == _NETDEV_KIND_INVALID) {
394                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
395                            "Invalid NetDev kind: %s", lvalue);
396                 return 0;
397         }
398
399         r = netdev_get(network->manager, rvalue, &netdev);
400         if (r < 0) {
401                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
402                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
403                 return 0;
404         }
405
406         if (netdev->kind != kind) {
407                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
408                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
409                 return 0;
410         }
411
412         switch (kind) {
413         case NETDEV_KIND_BRIDGE:
414                 network->bridge = netdev;
415
416                 break;
417         case NETDEV_KIND_BOND:
418                 network->bond = netdev;
419
420                 break;
421         case NETDEV_KIND_VLAN:
422         case NETDEV_KIND_MACVLAN:
423         case NETDEV_KIND_IPVLAN:
424         case NETDEV_KIND_VXLAN:
425                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
426                 if (r < 0) {
427                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
428                                    "Can not add VLAN '%s' to network: %s",
429                                    rvalue, strerror(-r));
430                         return 0;
431                 }
432
433                 break;
434         default:
435                 assert_not_reached("Can not parse NetDev");
436         }
437
438         netdev_ref(netdev);
439
440         return 0;
441 }
442
443 int config_parse_domains(const char *unit,
444                          const char *filename,
445                          unsigned line,
446                          const char *section,
447                          unsigned section_line,
448                          const char *lvalue,
449                          int ltype,
450                          const char *rvalue,
451                          void *data,
452                          void *userdata) {
453         Network *network = userdata;
454         char ***domains = data;
455         char **domain;
456         int r;
457
458         r = config_parse_strv(unit, filename, line, section, section_line,
459                               lvalue, ltype, rvalue, domains, userdata);
460         if (r < 0)
461                 return r;
462
463         strv_uniq(*domains);
464         network->wildcard_domain = !!strv_find(*domains, "*");
465
466         STRV_FOREACH(domain, *domains) {
467                 if (is_localhost(*domain))
468                         log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
469                 else if (!hostname_is_valid(*domain)) {
470                         if (!streq(*domain, "*"))
471                                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
472                 } else
473                         continue;
474
475                 strv_remove(*domains, *domain);
476
477                 /* We removed one entry, make sure we don't skip the next one */
478                 domain--;
479         }
480
481         return 0;
482 }
483
484 int config_parse_tunnel(const char *unit,
485                         const char *filename,
486                         unsigned line,
487                         const char *section,
488                         unsigned section_line,
489                         const char *lvalue,
490                         int ltype,
491                         const char *rvalue,
492                         void *data,
493                         void *userdata) {
494         Network *network = userdata;
495         NetDev *netdev;
496         int r;
497
498         assert(filename);
499         assert(lvalue);
500         assert(rvalue);
501         assert(data);
502
503         r = netdev_get(network->manager, rvalue, &netdev);
504         if (r < 0) {
505                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
506                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
507                 return 0;
508         }
509
510         if (netdev->kind != NETDEV_KIND_IPIP &&
511             netdev->kind != NETDEV_KIND_SIT &&
512             netdev->kind != NETDEV_KIND_GRE &&
513             netdev->kind != NETDEV_KIND_GRETAP &&
514             netdev->kind != NETDEV_KIND_IP6GRE &&
515             netdev->kind != NETDEV_KIND_IP6GRETAP &&
516             netdev->kind != NETDEV_KIND_VTI &&
517             netdev->kind != NETDEV_KIND_IP6TNL
518             ) {
519                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
520                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
521                 return 0;
522         }
523
524         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
525         if (r < 0) {
526                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
527                            "Can not add VLAN '%s' to network: %s",
528                            rvalue, strerror(-r));
529                 return 0;
530         }
531
532         netdev_ref(netdev);
533
534         return 0;
535 }
536
537 int config_parse_ipv4ll(
538                 const char* unit,
539                 const char *filename,
540                 unsigned line,
541                 const char *section,
542                 unsigned section_line,
543                 const char *lvalue,
544                 int ltype,
545                 const char *rvalue,
546                 void *data,
547                 void *userdata) {
548
549         AddressFamilyBoolean *link_local = data;
550
551         assert(filename);
552         assert(lvalue);
553         assert(rvalue);
554         assert(data);
555
556         /* Note that this is mostly like
557          * config_parse_address_family_boolean(), except that it
558          * applies only to IPv4 */
559
560         if (parse_boolean(rvalue))
561                 *link_local |= ADDRESS_FAMILY_IPV4;
562         else
563                 *link_local &= ~ADDRESS_FAMILY_IPV4;
564
565         return 0;
566 }
567
568 int config_parse_dhcp(
569                 const char* unit,
570                 const char *filename,
571                 unsigned line,
572                 const char *section,
573                 unsigned section_line,
574                 const char *lvalue,
575                 int ltype,
576                 const char *rvalue,
577                 void *data,
578                 void *userdata) {
579
580         AddressFamilyBoolean *dhcp = data, s;
581
582         assert(filename);
583         assert(lvalue);
584         assert(rvalue);
585         assert(data);
586
587         /* Note that this is mostly like
588          * config_parse_address_family_boolean(), except that it
589          * understands some old names for the enum values */
590
591         s = address_family_boolean_from_string(rvalue);
592         if (s < 0) {
593
594                 /* Previously, we had a slightly different enum here,
595                  * support its values for compatbility. */
596
597                 if (streq(rvalue, "none"))
598                         s = ADDRESS_FAMILY_NO;
599                 else if (streq(rvalue, "v4"))
600                         s = ADDRESS_FAMILY_IPV4;
601                 else if (streq(rvalue, "v6"))
602                         s = ADDRESS_FAMILY_IPV6;
603                 else if (streq(rvalue, "both"))
604                         s = ADDRESS_FAMILY_YES;
605                 else {
606                         log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
607                         return 0;
608                 }
609         }
610
611         *dhcp = s;
612         return 0;
613 }
614
615 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
616         [DHCP_CLIENT_ID_MAC] = "mac",
617         [DHCP_CLIENT_ID_DUID] = "duid"
618 };
619
620 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
621 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
622
623 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
624         [LLMNR_SUPPORT_NO] = "no",
625         [LLMNR_SUPPORT_YES] = "yes",
626         [LLMNR_SUPPORT_RESOLVE] = "resolve",
627 };
628
629 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
630
631 int config_parse_llmnr(
632                 const char* unit,
633                 const char *filename,
634                 unsigned line,
635                 const char *section,
636                 unsigned section_line,
637                 const char *lvalue,
638                 int ltype,
639                 const char *rvalue,
640                 void *data,
641                 void *userdata) {
642
643         LLMNRSupport *llmnr = data;
644         int k;
645
646         assert(filename);
647         assert(lvalue);
648         assert(rvalue);
649         assert(llmnr);
650
651         /* Our enum shall be a superset of booleans, hence first try
652          * to parse as boolean, and then as enum */
653
654         k = parse_boolean(rvalue);
655         if (k > 0)
656                 *llmnr = LLMNR_SUPPORT_YES;
657         else if (k == 0)
658                 *llmnr = LLMNR_SUPPORT_NO;
659         else {
660                 LLMNRSupport s;
661
662                 s = llmnr_support_from_string(rvalue);
663                 if (s < 0){
664                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
665                         return 0;
666                 }
667
668                 *llmnr = s;
669         }
670
671         return 0;
672 }
673
674 int config_parse_ipv6token(
675                 const char* unit,
676                 const char *filename,
677                 unsigned line,
678                 const char *section,
679                 unsigned section_line,
680                 const char *lvalue,
681                 int ltype,
682                 const char *rvalue,
683                 void *data,
684                 void *userdata) {
685
686         union in_addr_union buffer;
687         struct in6_addr *token = data;
688         int r;
689
690         assert(filename);
691         assert(lvalue);
692         assert(rvalue);
693         assert(token);
694
695         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
696         if (r < 0) {
697                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
698                 return 0;
699         }
700
701         r = in_addr_is_null(AF_INET6, &buffer);
702         if (r < 0) {
703                 log_syntax(unit, LOG_ERR, filename, line, -r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
704                 return 0;
705         }
706
707         if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
708                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
709                 return 0;
710         }
711
712         *token = buffer.in6;
713
714         return 0;
715 }