chiark / gitweb /
bbc6475df059f272dba9be765366b681e17c75b4
[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)) {
229                         const char *attr;
230                         uint8_t name_assign_type = NET_NAME_UNKNOWN;
231
232                         if (network->match_name) {
233                                 attr = udev_device_get_sysattr_value(device, "name_assign_type");
234                                 (void)safe_atou8(attr, &name_assign_type);
235
236                                 if (name_assign_type == NET_NAME_ENUM)
237                                         log_warning("%-*s: found matching network '%s', based on potentially unstable ifname",
238                                                     IFNAMSIZ, ifname, network->filename);
239                                 else
240                                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
241                         } else
242                                 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
243
244                         *ret = network;
245                         return 0;
246                 }
247         }
248
249         *ret = NULL;
250
251         return -ENOENT;
252 }
253
254 int network_apply(Manager *manager, Network *network, Link *link) {
255         int r;
256
257         link->network = network;
258
259         if (network->ipv4ll_route) {
260                 Route *route;
261
262                 r = route_new_static(network, 0, &route);
263                 if (r < 0)
264                         return r;
265
266                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
267                 if (r == 0)
268                         return -EINVAL;
269                 if (r < 0)
270                         return -errno;
271
272                 route->family = AF_INET;
273                 route->dst_prefixlen = 16;
274                 route->scope = RT_SCOPE_LINK;
275                 route->metrics = IPV4LL_ROUTE_METRIC;
276                 route->protocol = RTPROT_STATIC;
277         }
278
279         if (network->dns || network->ntp) {
280                 r = link_save(link);
281                 if (r < 0)
282                         return r;
283         }
284
285         return 0;
286 }
287
288 int config_parse_netdev(const char *unit,
289                 const char *filename,
290                 unsigned line,
291                 const char *section,
292                 unsigned section_line,
293                 const char *lvalue,
294                 int ltype,
295                 const char *rvalue,
296                 void *data,
297                 void *userdata) {
298         Network *network = userdata;
299         _cleanup_free_ char *kind_string = NULL;
300         char *p;
301         NetDev *netdev;
302         NetDevKind kind;
303         int r;
304
305         assert(filename);
306         assert(lvalue);
307         assert(rvalue);
308         assert(data);
309
310         kind_string = strdup(lvalue);
311         if (!kind_string)
312                 return log_oom();
313
314         /* the keys are CamelCase versions of the kind */
315         for (p = kind_string; *p; p++)
316                 *p = tolower(*p);
317
318         kind = netdev_kind_from_string(kind_string);
319         if (kind == _NETDEV_KIND_INVALID) {
320                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
321                            "Invalid NetDev kind: %s", lvalue);
322                 return 0;
323         }
324
325         r = netdev_get(network->manager, rvalue, &netdev);
326         if (r < 0) {
327                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
328                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
329                 return 0;
330         }
331
332         if (netdev->kind != kind) {
333                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
334                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
335                 return 0;
336         }
337
338         switch (kind) {
339         case NETDEV_KIND_BRIDGE:
340                 network->bridge = netdev;
341
342                 break;
343         case NETDEV_KIND_BOND:
344                 network->bond = netdev;
345
346                 break;
347         case NETDEV_KIND_VLAN:
348         case NETDEV_KIND_MACVLAN:
349         case NETDEV_KIND_VXLAN:
350                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
351                 if (r < 0) {
352                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
353                                    "Can not add VLAN '%s' to network: %s",
354                                    rvalue, strerror(-r));
355                         return 0;
356                 }
357
358                 break;
359         default:
360                 assert_not_reached("Can not parse NetDev");
361         }
362
363         netdev_ref(netdev);
364
365         return 0;
366 }
367
368 int config_parse_domains(const char *unit,
369                          const char *filename,
370                          unsigned line,
371                          const char *section,
372                          unsigned section_line,
373                          const char *lvalue,
374                          int ltype,
375                          const char *rvalue,
376                          void *data,
377                          void *userdata) {
378         Network *network = userdata;
379         char ***domains = data;
380         char **domain;
381         int r;
382
383         r = config_parse_strv(unit, filename, line, section, section_line,
384                               lvalue, ltype, rvalue, domains, userdata);
385         if (r < 0)
386                 return r;
387
388         strv_uniq(*domains);
389         network->wildcard_domain = !!strv_find(*domains, "*");
390
391         STRV_FOREACH(domain, *domains) {
392                 if (is_localhost(*domain))
393                         log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
394                 else if (!hostname_is_valid(*domain)) {
395                         if (!streq(*domain, "*"))
396                                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
397                 } else
398                         continue;
399
400                 strv_remove(*domains, *domain);
401
402                 /* We removed one entry, make sure we don't skip the next one */
403                 domain--;
404         }
405
406         return 0;
407 }
408
409 int config_parse_tunnel(const char *unit,
410                         const char *filename,
411                         unsigned line,
412                         const char *section,
413                         unsigned section_line,
414                         const char *lvalue,
415                         int ltype,
416                         const char *rvalue,
417                         void *data,
418                         void *userdata) {
419         Network *network = userdata;
420         NetDev *netdev;
421         int r;
422
423         assert(filename);
424         assert(lvalue);
425         assert(rvalue);
426         assert(data);
427
428         r = netdev_get(network->manager, rvalue, &netdev);
429         if (r < 0) {
430                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
431                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
432                 return 0;
433         }
434
435         if (netdev->kind != NETDEV_KIND_IPIP &&
436             netdev->kind != NETDEV_KIND_SIT &&
437             netdev->kind != NETDEV_KIND_GRE &&
438             netdev->kind != NETDEV_KIND_VTI) {
439                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
440                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
441                 return 0;
442         }
443
444         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
445         if (r < 0) {
446                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
447                            "Can not add VLAN '%s' to network: %s",
448                            rvalue, strerror(-r));
449                 return 0;
450         }
451
452         netdev_ref(netdev);
453
454         return 0;
455 }
456
457 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
458         [DHCP_SUPPORT_NONE] = "none",
459         [DHCP_SUPPORT_BOTH] = "both",
460         [DHCP_SUPPORT_V4] = "v4",
461         [DHCP_SUPPORT_V6] = "v6",
462 };
463
464 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
465
466 int config_parse_dhcp(
467                 const char* unit,
468                 const char *filename,
469                 unsigned line,
470                 const char *section,
471                 unsigned section_line,
472                 const char *lvalue,
473                 int ltype,
474                 const char *rvalue,
475                 void *data,
476                 void *userdata) {
477
478         DHCPSupport *dhcp = data;
479         int k;
480
481         assert(filename);
482         assert(lvalue);
483         assert(rvalue);
484         assert(data);
485
486         /* Our enum shall be a superset of booleans, hence first try
487          * to parse as boolean, and then as enum */
488
489         k = parse_boolean(rvalue);
490         if (k > 0)
491                 *dhcp = DHCP_SUPPORT_BOTH;
492         else if (k == 0)
493                 *dhcp = DHCP_SUPPORT_NONE;
494         else {
495                 DHCPSupport s;
496
497                 s = dhcp_support_from_string(rvalue);
498                 if (s < 0){
499                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
500                         return 0;
501                 }
502
503                 *dhcp = s;
504         }
505
506         return 0;
507 }
508
509 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
510         [LLMNR_SUPPORT_NO] = "no",
511         [LLMNR_SUPPORT_YES] = "yes",
512         [LLMNR_SUPPORT_RESOLVE] = "resolve",
513 };
514
515 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
516
517 int config_parse_llmnr(
518                 const char* unit,
519                 const char *filename,
520                 unsigned line,
521                 const char *section,
522                 unsigned section_line,
523                 const char *lvalue,
524                 int ltype,
525                 const char *rvalue,
526                 void *data,
527                 void *userdata) {
528
529         LLMNRSupport *llmnr = data;
530         int k;
531
532         assert(filename);
533         assert(lvalue);
534         assert(rvalue);
535         assert(data);
536
537         /* Our enum shall be a superset of booleans, hence first try
538          * to parse as boolean, and then as enum */
539
540         k = parse_boolean(rvalue);
541         if (k > 0)
542                 *llmnr = LLMNR_SUPPORT_YES;
543         else if (k == 0)
544                 *llmnr = LLMNR_SUPPORT_NO;
545         else {
546                 LLMNRSupport s;
547
548                 s = llmnr_support_from_string(rvalue);
549                 if (s < 0){
550                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
551                         return 0;
552                 }
553
554                 *llmnr = s;
555         }
556
557         return 0;
558 }