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