chiark / gitweb /
networkd: log when finished enumerating links and addresses
[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 "path-util.h"
26 #include "conf-files.h"
27 #include "conf-parser.h"
28 #include "util.h"
29 #include "networkd.h"
30 #include "networkd-netdev.h"
31 #include "networkd-link.h"
32 #include "network-internal.h"
33
34 static int network_load_one(Manager *manager, const char *filename) {
35         _cleanup_network_free_ Network *network = NULL;
36         _cleanup_fclose_ FILE *file = NULL;
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->dhcp = ADDRESS_FAMILY_NO;
88         network->dhcp_ntp = true;
89         network->dhcp_dns = true;
90         network->dhcp_hostname = true;
91         network->dhcp_routes = true;
92         network->dhcp_sendhost = true;
93         network->dhcp_route_metric = DHCP_ROUTE_METRIC;
94
95         network->llmnr = LLMNR_SUPPORT_YES;
96
97         r = config_parse(NULL, filename, file,
98                          "Match\0"
99                          "Link\0"
100                          "Network\0"
101                          "Address\0"
102                          "Route\0"
103                          "DHCP\0"
104                          "DHCPv4\0"
105                          "Bridge\0"
106                          "BridgeFDB\0",
107                          config_item_perf_lookup, network_network_gperf_lookup,
108                          false, false, true, network);
109         if (r < 0)
110                 return r;
111
112         /* IPMasquerade=yes implies IPForward=yes */
113         if (network->ip_masquerade)
114                 network->ip_forward |= ADDRESS_FAMILY_IPV4;
115
116         LIST_PREPEND(networks, manager->networks, network);
117
118         LIST_FOREACH(routes, route, network->static_routes) {
119                 if (!route->family) {
120                         log_warning("Route section without Gateway field configured in %s. "
121                                     "Ignoring", filename);
122                         return 0;
123                 }
124         }
125
126         LIST_FOREACH(addresses, address, network->static_addresses) {
127                 if (!address->family) {
128                         log_warning("Address section without Address field configured in %s. "
129                                     "Ignoring", filename);
130                         return 0;
131                 }
132         }
133
134         network = NULL;
135
136         return 0;
137 }
138
139 int network_load(Manager *manager) {
140         Network *network;
141         _cleanup_strv_free_ char **files = NULL;
142         char **f;
143         int r;
144
145         assert(manager);
146
147         while ((network = manager->networks))
148                 network_free(network);
149
150         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
151         if (r < 0)
152                 return log_error_errno(r, "Failed to enumerate network files: %m");
153
154         STRV_FOREACH_BACKWARDS(f, files) {
155                 r = network_load_one(manager, *f);
156                 if (r < 0)
157                         return r;
158         }
159
160         return 0;
161 }
162
163 void network_free(Network *network) {
164         NetDev *netdev;
165         Route *route;
166         Address *address;
167         FdbEntry *fdb_entry;
168         Iterator i;
169
170         if (!network)
171                 return;
172
173         free(network->filename);
174
175         free(network->match_mac);
176         free(network->match_path);
177         free(network->match_driver);
178         free(network->match_type);
179         free(network->match_name);
180
181         free(network->description);
182         free(network->dhcp_vendor_class_identifier);
183
184         free(network->mac);
185
186         strv_free(network->ntp);
187         strv_free(network->dns);
188         strv_free(network->domains);
189
190         netdev_unref(network->bridge);
191
192         netdev_unref(network->bond);
193
194         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
195                 hashmap_remove(network->stacked_netdevs, netdev->ifname);
196                 netdev_unref(netdev);
197         }
198         hashmap_free(network->stacked_netdevs);
199
200         while ((route = network->static_routes))
201                 route_free(route);
202
203         while ((address = network->static_addresses))
204                 address_free(address);
205
206         while ((fdb_entry = network->static_fdb_entries))
207                 fdb_entry_free(fdb_entry);
208
209         hashmap_free(network->addresses_by_section);
210         hashmap_free(network->routes_by_section);
211         hashmap_free(network->fdb_entries_by_section);
212
213         if (network->manager && network->manager->networks)
214                 LIST_REMOVE(networks, network->manager->networks, network);
215
216         condition_free_list(network->match_host);
217         condition_free_list(network->match_virt);
218         condition_free_list(network->match_kernel);
219         condition_free_list(network->match_arch);
220
221         free(network);
222 }
223
224 int network_get(Manager *manager, struct udev_device *device,
225                 const char *ifname, const struct ether_addr *address,
226                 Network **ret) {
227         Network *network;
228
229         assert(manager);
230         assert(ret);
231
232         LIST_FOREACH(networks, network, manager->networks) {
233                 if (net_match_config(network->match_mac, network->match_path,
234                                      network->match_driver, network->match_type,
235                                      network->match_name, network->match_host,
236                                      network->match_virt, network->match_kernel,
237                                      network->match_arch,
238                                      address,
239                                      udev_device_get_property_value(device, "ID_PATH"),
240                                      udev_device_get_driver(udev_device_get_parent(device)),
241                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
242                                      udev_device_get_devtype(device),
243                                      ifname)) {
244                         if (network->match_name) {
245                                 const char *attr;
246                                 uint8_t name_assign_type = NET_NAME_UNKNOWN;
247
248                                 attr = udev_device_get_sysattr_value(device, "name_assign_type");
249                                 if (attr)
250                                         (void)safe_atou8(attr, &name_assign_type);
251
252                                 if (name_assign_type == NET_NAME_ENUM)
253                                         log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
254                                                     IFNAMSIZ, ifname, network->filename);
255                                 else
256                                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
257                         } else
258                                 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
259
260                         *ret = network;
261                         return 0;
262                 }
263         }
264
265         *ret = NULL;
266
267         return -ENOENT;
268 }
269
270 int network_apply(Manager *manager, Network *network, Link *link) {
271         int r;
272
273         link->network = network;
274
275         if (network->ipv4ll_route) {
276                 Route *route;
277
278                 r = route_new_static(network, 0, &route);
279                 if (r < 0)
280                         return r;
281
282                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
283                 if (r == 0)
284                         return -EINVAL;
285                 if (r < 0)
286                         return -errno;
287
288                 route->family = AF_INET;
289                 route->dst_prefixlen = 16;
290                 route->scope = RT_SCOPE_LINK;
291                 route->metrics = IPV4LL_ROUTE_METRIC;
292                 route->protocol = RTPROT_STATIC;
293         }
294
295         if (network->dns || network->ntp) {
296                 r = link_save(link);
297                 if (r < 0)
298                         return r;
299         }
300
301         return 0;
302 }
303
304 int config_parse_netdev(const char *unit,
305                 const char *filename,
306                 unsigned line,
307                 const char *section,
308                 unsigned section_line,
309                 const char *lvalue,
310                 int ltype,
311                 const char *rvalue,
312                 void *data,
313                 void *userdata) {
314         Network *network = userdata;
315         _cleanup_free_ char *kind_string = NULL;
316         char *p;
317         NetDev *netdev;
318         NetDevKind kind;
319         int r;
320
321         assert(filename);
322         assert(lvalue);
323         assert(rvalue);
324         assert(data);
325
326         kind_string = strdup(lvalue);
327         if (!kind_string)
328                 return log_oom();
329
330         /* the keys are CamelCase versions of the kind */
331         for (p = kind_string; *p; p++)
332                 *p = tolower(*p);
333
334         kind = netdev_kind_from_string(kind_string);
335         if (kind == _NETDEV_KIND_INVALID) {
336                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
337                            "Invalid NetDev kind: %s", lvalue);
338                 return 0;
339         }
340
341         r = netdev_get(network->manager, rvalue, &netdev);
342         if (r < 0) {
343                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
344                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
345                 return 0;
346         }
347
348         if (netdev->kind != kind) {
349                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
350                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
351                 return 0;
352         }
353
354         switch (kind) {
355         case NETDEV_KIND_BRIDGE:
356                 network->bridge = netdev;
357
358                 break;
359         case NETDEV_KIND_BOND:
360                 network->bond = netdev;
361
362                 break;
363         case NETDEV_KIND_VLAN:
364         case NETDEV_KIND_MACVLAN:
365         case NETDEV_KIND_IPVLAN:
366         case NETDEV_KIND_VXLAN:
367                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
368                 if (r < 0) {
369                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
370                                    "Can not add VLAN '%s' to network: %s",
371                                    rvalue, strerror(-r));
372                         return 0;
373                 }
374
375                 break;
376         default:
377                 assert_not_reached("Can not parse NetDev");
378         }
379
380         netdev_ref(netdev);
381
382         return 0;
383 }
384
385 int config_parse_domains(const char *unit,
386                          const char *filename,
387                          unsigned line,
388                          const char *section,
389                          unsigned section_line,
390                          const char *lvalue,
391                          int ltype,
392                          const char *rvalue,
393                          void *data,
394                          void *userdata) {
395         Network *network = userdata;
396         char ***domains = data;
397         char **domain;
398         int r;
399
400         r = config_parse_strv(unit, filename, line, section, section_line,
401                               lvalue, ltype, rvalue, domains, userdata);
402         if (r < 0)
403                 return r;
404
405         strv_uniq(*domains);
406         network->wildcard_domain = !!strv_find(*domains, "*");
407
408         STRV_FOREACH(domain, *domains) {
409                 if (is_localhost(*domain))
410                         log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
411                 else if (!hostname_is_valid(*domain)) {
412                         if (!streq(*domain, "*"))
413                                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
414                 } else
415                         continue;
416
417                 strv_remove(*domains, *domain);
418
419                 /* We removed one entry, make sure we don't skip the next one */
420                 domain--;
421         }
422
423         return 0;
424 }
425
426 int config_parse_tunnel(const char *unit,
427                         const char *filename,
428                         unsigned line,
429                         const char *section,
430                         unsigned section_line,
431                         const char *lvalue,
432                         int ltype,
433                         const char *rvalue,
434                         void *data,
435                         void *userdata) {
436         Network *network = userdata;
437         NetDev *netdev;
438         int r;
439
440         assert(filename);
441         assert(lvalue);
442         assert(rvalue);
443         assert(data);
444
445         r = netdev_get(network->manager, rvalue, &netdev);
446         if (r < 0) {
447                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
448                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
449                 return 0;
450         }
451
452         if (netdev->kind != NETDEV_KIND_IPIP &&
453             netdev->kind != NETDEV_KIND_SIT &&
454             netdev->kind != NETDEV_KIND_GRE &&
455             netdev->kind != NETDEV_KIND_GRETAP &&
456             netdev->kind != NETDEV_KIND_IP6GRE &&
457             netdev->kind != NETDEV_KIND_IP6GRETAP &&
458             netdev->kind != NETDEV_KIND_VTI &&
459             netdev->kind != NETDEV_KIND_IP6TNL
460             ) {
461                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
462                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
463                 return 0;
464         }
465
466         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
467         if (r < 0) {
468                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
469                            "Can not add VLAN '%s' to network: %s",
470                            rvalue, strerror(-r));
471                 return 0;
472         }
473
474         netdev_ref(netdev);
475
476         return 0;
477 }
478
479 int config_parse_dhcp(
480                 const char* unit,
481                 const char *filename,
482                 unsigned line,
483                 const char *section,
484                 unsigned section_line,
485                 const char *lvalue,
486                 int ltype,
487                 const char *rvalue,
488                 void *data,
489                 void *userdata) {
490
491         AddressFamilyBoolean *dhcp = data, s;
492
493         assert(filename);
494         assert(lvalue);
495         assert(rvalue);
496         assert(data);
497
498         /* Note that this is mostly like
499          * config_parse_address_family_boolean(), except that it
500          * understands some old names for the enum values */
501
502         s = address_family_boolean_from_string(rvalue);
503         if (s < 0) {
504
505                 /* Previously, we had a slightly different enum here,
506                  * support its values for compatbility. */
507
508                 if (streq(rvalue, "none"))
509                         s = ADDRESS_FAMILY_NO;
510                 else if (streq(rvalue, "v4"))
511                         s = ADDRESS_FAMILY_IPV4;
512                 else if (streq(rvalue, "v6"))
513                         s = ADDRESS_FAMILY_IPV6;
514                 else if (streq(rvalue, "both"))
515                         s = ADDRESS_FAMILY_YES;
516                 else {
517                         log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
518                         return 0;
519                 }
520         }
521
522         *dhcp = s;
523         return 0;
524 }
525
526 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
527         [LLMNR_SUPPORT_NO] = "no",
528         [LLMNR_SUPPORT_YES] = "yes",
529         [LLMNR_SUPPORT_RESOLVE] = "resolve",
530 };
531
532 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
533
534 int config_parse_llmnr(
535                 const char* unit,
536                 const char *filename,
537                 unsigned line,
538                 const char *section,
539                 unsigned section_line,
540                 const char *lvalue,
541                 int ltype,
542                 const char *rvalue,
543                 void *data,
544                 void *userdata) {
545
546         LLMNRSupport *llmnr = data;
547         int k;
548
549         assert(filename);
550         assert(lvalue);
551         assert(rvalue);
552         assert(data);
553
554         /* Our enum shall be a superset of booleans, hence first try
555          * to parse as boolean, and then as enum */
556
557         k = parse_boolean(rvalue);
558         if (k > 0)
559                 *llmnr = LLMNR_SUPPORT_YES;
560         else if (k == 0)
561                 *llmnr = LLMNR_SUPPORT_NO;
562         else {
563                 LLMNRSupport s;
564
565                 s = llmnr_support_from_string(rvalue);
566                 if (s < 0){
567                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
568                         return 0;
569                 }
570
571                 *llmnr = s;
572         }
573
574         return 0;
575 }