chiark / gitweb /
sd-network: add support for wildcard domains
[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         Network *network = userdata;
355         char ***domains = data;
356         char **domain;
357         int r;
358
359         r = config_parse_strv(unit, filename, line, section, section_line,
360                               lvalue, ltype, rvalue, domains, userdata);
361         if (r < 0)
362                 return r;
363
364         strv_uniq(*domains);
365
366         if (strv_isempty(*domains))
367                 network->wildcard_domain = false;
368         else if (strv_find(*domains, "*"))
369                 network->wildcard_domain = true;
370
371         STRV_FOREACH(domain, *domains)
372                 if (is_localhost(*domain) || !hostname_is_valid(*domain))
373                         strv_remove(*domains, *domain);
374
375         return 0;
376 }
377
378 int config_parse_tunnel(const char *unit,
379                         const char *filename,
380                         unsigned line,
381                         const char *section,
382                         unsigned section_line,
383                         const char *lvalue,
384                         int ltype,
385                         const char *rvalue,
386                         void *data,
387                         void *userdata) {
388         Network *network = userdata;
389         NetDev *netdev;
390         int r;
391
392         assert(filename);
393         assert(lvalue);
394         assert(rvalue);
395         assert(data);
396
397         r = netdev_get(network->manager, rvalue, &netdev);
398         if (r < 0) {
399                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
400                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
401                 return 0;
402         }
403
404         if (netdev->kind != NETDEV_KIND_IPIP &&
405             netdev->kind != NETDEV_KIND_SIT &&
406             netdev->kind != NETDEV_KIND_GRE &&
407             netdev->kind != NETDEV_KIND_VTI) {
408                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
409                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
410                 return 0;
411         }
412
413         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
414         if (r < 0) {
415                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
416                            "Can not add VLAN '%s' to network: %s",
417                            rvalue, strerror(-r));
418                 return 0;
419         }
420
421         netdev_ref(netdev);
422
423         return 0;
424 }
425
426 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
427         [DHCP_SUPPORT_NONE] = "none",
428         [DHCP_SUPPORT_BOTH] = "both",
429         [DHCP_SUPPORT_V4] = "v4",
430         [DHCP_SUPPORT_V6] = "v6",
431 };
432
433 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
434
435 int config_parse_dhcp(
436                 const char* unit,
437                 const char *filename,
438                 unsigned line,
439                 const char *section,
440                 unsigned section_line,
441                 const char *lvalue,
442                 int ltype,
443                 const char *rvalue,
444                 void *data,
445                 void *userdata) {
446
447         DHCPSupport *dhcp = data;
448         int k;
449
450         assert(filename);
451         assert(lvalue);
452         assert(rvalue);
453         assert(data);
454
455         /* Our enum shall be a superset of booleans, hence first try
456          * to parse as boolean, and then as enum */
457
458         k = parse_boolean(rvalue);
459         if (k > 0)
460                 *dhcp = DHCP_SUPPORT_BOTH;
461         else if (k == 0)
462                 *dhcp = DHCP_SUPPORT_NONE;
463         else {
464                 DHCPSupport s;
465
466                 s = dhcp_support_from_string(rvalue);
467                 if (s < 0){
468                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
469                         return 0;
470                 }
471
472                 *dhcp = s;
473         }
474
475         return 0;
476 }
477
478 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
479         [LLMNR_SUPPORT_NO] = "no",
480         [LLMNR_SUPPORT_YES] = "yes",
481         [LLMNR_SUPPORT_RESOLVE] = "resolve",
482 };
483
484 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
485
486 int config_parse_llmnr(
487                 const char* unit,
488                 const char *filename,
489                 unsigned line,
490                 const char *section,
491                 unsigned section_line,
492                 const char *lvalue,
493                 int ltype,
494                 const char *rvalue,
495                 void *data,
496                 void *userdata) {
497
498         LLMNRSupport *llmnr = data;
499         int k;
500
501         assert(filename);
502         assert(lvalue);
503         assert(rvalue);
504         assert(data);
505
506         /* Our enum shall be a superset of booleans, hence first try
507          * to parse as boolean, and then as enum */
508
509         k = parse_boolean(rvalue);
510         if (k > 0)
511                 *llmnr = LLMNR_SUPPORT_YES;
512         else if (k == 0)
513                 *llmnr = LLMNR_SUPPORT_NO;
514         else {
515                 LLMNRSupport s;
516
517                 s = llmnr_support_from_string(rvalue);
518                 if (s < 0){
519                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
520                         return 0;
521                 }
522
523                 *llmnr = s;
524         }
525
526         return 0;
527 }