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