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