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