chiark / gitweb /
d6504cc1787cfebe17307eae97d98b07cb656361
[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 = DHCP_SUPPORT_NONE;
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 = true;
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_VXLAN:
366                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
367                 if (r < 0) {
368                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
369                                    "Can not add VLAN '%s' to network: %s",
370                                    rvalue, strerror(-r));
371                         return 0;
372                 }
373
374                 break;
375         default:
376                 assert_not_reached("Can not parse NetDev");
377         }
378
379         netdev_ref(netdev);
380
381         return 0;
382 }
383
384 int config_parse_domains(const char *unit,
385                          const char *filename,
386                          unsigned line,
387                          const char *section,
388                          unsigned section_line,
389                          const char *lvalue,
390                          int ltype,
391                          const char *rvalue,
392                          void *data,
393                          void *userdata) {
394         Network *network = userdata;
395         char ***domains = data;
396         char **domain;
397         int r;
398
399         r = config_parse_strv(unit, filename, line, section, section_line,
400                               lvalue, ltype, rvalue, domains, userdata);
401         if (r < 0)
402                 return r;
403
404         strv_uniq(*domains);
405         network->wildcard_domain = !!strv_find(*domains, "*");
406
407         STRV_FOREACH(domain, *domains) {
408                 if (is_localhost(*domain))
409                         log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
410                 else if (!hostname_is_valid(*domain)) {
411                         if (!streq(*domain, "*"))
412                                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
413                 } else
414                         continue;
415
416                 strv_remove(*domains, *domain);
417
418                 /* We removed one entry, make sure we don't skip the next one */
419                 domain--;
420         }
421
422         return 0;
423 }
424
425 int config_parse_tunnel(const char *unit,
426                         const char *filename,
427                         unsigned line,
428                         const char *section,
429                         unsigned section_line,
430                         const char *lvalue,
431                         int ltype,
432                         const char *rvalue,
433                         void *data,
434                         void *userdata) {
435         Network *network = userdata;
436         NetDev *netdev;
437         int r;
438
439         assert(filename);
440         assert(lvalue);
441         assert(rvalue);
442         assert(data);
443
444         r = netdev_get(network->manager, rvalue, &netdev);
445         if (r < 0) {
446                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
447                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
448                 return 0;
449         }
450
451         if (netdev->kind != NETDEV_KIND_IPIP &&
452             netdev->kind != NETDEV_KIND_SIT &&
453             netdev->kind != NETDEV_KIND_GRE &&
454             netdev->kind != NETDEV_KIND_VTI) {
455                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
456                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
457                 return 0;
458         }
459
460         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
461         if (r < 0) {
462                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
463                            "Can not add VLAN '%s' to network: %s",
464                            rvalue, strerror(-r));
465                 return 0;
466         }
467
468         netdev_ref(netdev);
469
470         return 0;
471 }
472
473 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
474         [DHCP_SUPPORT_NONE] = "none",
475         [DHCP_SUPPORT_BOTH] = "both",
476         [DHCP_SUPPORT_V4] = "v4",
477         [DHCP_SUPPORT_V6] = "v6",
478 };
479
480 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
481
482 int config_parse_dhcp(
483                 const char* unit,
484                 const char *filename,
485                 unsigned line,
486                 const char *section,
487                 unsigned section_line,
488                 const char *lvalue,
489                 int ltype,
490                 const char *rvalue,
491                 void *data,
492                 void *userdata) {
493
494         DHCPSupport *dhcp = data;
495         int k;
496
497         assert(filename);
498         assert(lvalue);
499         assert(rvalue);
500         assert(data);
501
502         /* Our enum shall be a superset of booleans, hence first try
503          * to parse as boolean, and then as enum */
504
505         k = parse_boolean(rvalue);
506         if (k > 0)
507                 *dhcp = DHCP_SUPPORT_BOTH;
508         else if (k == 0)
509                 *dhcp = DHCP_SUPPORT_NONE;
510         else {
511                 DHCPSupport s;
512
513                 s = dhcp_support_from_string(rvalue);
514                 if (s < 0){
515                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
516                         return 0;
517                 }
518
519                 *dhcp = s;
520         }
521
522         return 0;
523 }
524
525 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
526         [LLMNR_SUPPORT_NO] = "no",
527         [LLMNR_SUPPORT_YES] = "yes",
528         [LLMNR_SUPPORT_RESOLVE] = "resolve",
529 };
530
531 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
532
533 int config_parse_llmnr(
534                 const char* unit,
535                 const char *filename,
536                 unsigned line,
537                 const char *section,
538                 unsigned section_line,
539                 const char *lvalue,
540                 int ltype,
541                 const char *rvalue,
542                 void *data,
543                 void *userdata) {
544
545         LLMNRSupport *llmnr = data;
546         int k;
547
548         assert(filename);
549         assert(lvalue);
550         assert(rvalue);
551         assert(data);
552
553         /* Our enum shall be a superset of booleans, hence first try
554          * to parse as boolean, and then as enum */
555
556         k = parse_boolean(rvalue);
557         if (k > 0)
558                 *llmnr = LLMNR_SUPPORT_YES;
559         else if (k == 0)
560                 *llmnr = LLMNR_SUPPORT_NO;
561         else {
562                 LLMNRSupport s;
563
564                 s = llmnr_support_from_string(rvalue);
565                 if (s < 0){
566                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
567                         return 0;
568                 }
569
570                 *llmnr = s;
571         }
572
573         return 0;
574 }