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