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