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