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