chiark / gitweb /
networkd: unify handling of stacked netdevs
[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 "network-internal.h"
28 #include "path-util.h"
29 #include "conf-files.h"
30 #include "conf-parser.h"
31 #include "util.h"
32
33 static int network_load_one(Manager *manager, const char *filename) {
34         _cleanup_network_free_ Network *network = NULL;
35         _cleanup_fclose_ FILE *file = NULL;
36         Route *route;
37         Address *address;
38         int r;
39
40         assert(manager);
41         assert(filename);
42
43         file = fopen(filename, "re");
44         if (!file) {
45                 if (errno == ENOENT)
46                         return 0;
47                 else
48                         return -errno;
49         }
50
51         if (null_or_empty_fd(fileno(file))) {
52                 log_debug("Skipping empty file: %s", filename);
53                 return 0;
54         }
55
56         network = new0(Network, 1);
57         if (!network)
58                 return log_oom();
59
60         network->manager = manager;
61
62         LIST_HEAD_INIT(network->static_addresses);
63         LIST_HEAD_INIT(network->static_routes);
64
65         network->stacked_netdevs = hashmap_new(string_hash_func, string_compare_func);
66         if (!network->stacked_netdevs)
67                 return log_oom();
68
69         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
70         if (!network->addresses_by_section)
71                 return log_oom();
72
73         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
74         if (!network->routes_by_section)
75                 return log_oom();
76
77         network->filename = strdup(filename);
78         if (!network->filename)
79                 return log_oom();
80
81         network->ipv4ll_route = true;
82
83         network->dhcp_ntp = true;
84         network->dhcp_dns = true;
85         network->dhcp_hostname = true;
86         network->dhcp_domainname = true;
87         network->dhcp_routes = true;
88         network->dhcp_sendhost = true;
89
90         r = config_parse(NULL, filename, file,
91                          "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
92                          config_item_perf_lookup, network_network_gperf_lookup,
93                          false, false, true, network);
94         if (r < 0)
95                 return r;
96
97         LIST_PREPEND(networks, manager->networks, network);
98
99         LIST_FOREACH(routes, route, network->static_routes) {
100                 if (!route->family) {
101                         log_warning("Route section without Gateway field configured in %s. "
102                                     "Ignoring", filename);
103                         return 0;
104                 }
105         }
106
107         LIST_FOREACH(addresses, address, network->static_addresses) {
108                 if (!address->family) {
109                         log_warning("Address section without Address field configured in %s. "
110                                     "Ignoring", filename);
111                         return 0;
112                 }
113         }
114
115         network = NULL;
116
117         return 0;
118 }
119
120 int network_load(Manager *manager) {
121         Network *network;
122         _cleanup_strv_free_ char **files = NULL;
123         char **f;
124         int r;
125
126         assert(manager);
127
128         while ((network = manager->networks))
129                 network_free(network);
130
131         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
132         if (r < 0) {
133                 log_error("Failed to enumerate network files: %s", strerror(-r));
134                 return r;
135         }
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
169         netdev_unref(network->bridge);
170
171         netdev_unref(network->bond);
172
173         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i)
174                 netdev_unref(netdev);
175         hashmap_free(network->stacked_netdevs);
176
177         while ((route = network->static_routes))
178                 route_free(route);
179
180         while ((address = network->static_addresses))
181                 address_free(address);
182
183         hashmap_free(network->addresses_by_section);
184         hashmap_free(network->routes_by_section);
185
186         if (network->manager && network->manager->networks)
187                 LIST_REMOVE(networks, network->manager->networks, network);
188
189         condition_free_list(network->match_host);
190         condition_free_list(network->match_virt);
191         condition_free_list(network->match_kernel);
192         condition_free_list(network->match_arch);
193
194         free(network);
195 }
196
197 int network_get(Manager *manager, struct udev_device *device,
198                 const char *ifname, const struct ether_addr *address,
199                 Network **ret) {
200         Network *network;
201
202         assert(manager);
203         assert(ret);
204
205         LIST_FOREACH(networks, network, manager->networks) {
206                 if (net_match_config(network->match_mac, network->match_path,
207                                      network->match_driver, network->match_type,
208                                      network->match_name, network->match_host,
209                                      network->match_virt, network->match_kernel,
210                                      network->match_arch,
211                                      address,
212                                      udev_device_get_property_value(device, "ID_PATH"),
213                                      udev_device_get_driver(udev_device_get_parent(device)),
214                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
215                                      udev_device_get_devtype(device),
216                                      ifname)) {
217                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
218                                   network->filename);
219                         *ret = network;
220                         return 0;
221                 }
222         }
223
224         *ret = NULL;
225
226         return -ENOENT;
227 }
228
229 int network_apply(Manager *manager, Network *network, Link *link) {
230         int r;
231
232         link->network = network;
233
234         if (network->ipv4ll_route) {
235                 Route *route;
236
237                 r = route_new_static(network, 0, &route);
238                 if (r < 0)
239                         return r;
240
241                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
242                 if (r == 0)
243                         return -EINVAL;
244                 if (r < 0)
245                         return -errno;
246
247                 route->family = AF_INET;
248                 route->dst_prefixlen = 16;
249                 route->scope = RT_SCOPE_LINK;
250                 route->metrics = IPV4LL_ROUTE_METRIC;
251                 route->protocol = RTPROT_STATIC;
252         }
253
254         if (network->dns || network->ntp) {
255                 r = link_save(link);
256                 if (r < 0)
257                         return r;
258         }
259
260         return 0;
261 }
262
263 int config_parse_netdev(const char *unit,
264                 const char *filename,
265                 unsigned line,
266                 const char *section,
267                 unsigned section_line,
268                 const char *lvalue,
269                 int ltype,
270                 const char *rvalue,
271                 void *data,
272                 void *userdata) {
273         Network *network = userdata;
274         _cleanup_free_ char *kind_string = NULL;
275         char *p;
276         NetDev *netdev;
277         NetDevKind kind;
278         int r;
279
280         assert(filename);
281         assert(lvalue);
282         assert(rvalue);
283         assert(data);
284
285         kind_string = strdup(lvalue);
286         if (!kind_string)
287                 return log_oom();
288
289         /* the keys are CamelCase versions of the kind */
290         for (p = kind_string; *p; p++)
291                 *p = tolower(*p);
292
293         kind = netdev_kind_from_string(kind_string);
294         if (kind == _NETDEV_KIND_INVALID) {
295                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
296                            "Invalid NetDev kind: %s", lvalue);
297                 return 0;
298         }
299
300         r = netdev_get(network->manager, rvalue, &netdev);
301         if (r < 0) {
302                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
303                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
304                 return 0;
305         }
306
307         if (netdev->kind != kind) {
308                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
309                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
310                 return 0;
311         }
312
313         switch (kind) {
314         case NETDEV_KIND_BRIDGE:
315                 network->bridge = netdev;
316
317                 break;
318         case NETDEV_KIND_BOND:
319                 network->bond = netdev;
320
321                 break;
322         case NETDEV_KIND_VLAN:
323         case NETDEV_KIND_MACVLAN:
324         case NETDEV_KIND_VXLAN:
325                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326                 if (r < 0) {
327                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
328                                    "Can not add VLAN '%s' to network: %s",
329                                    rvalue, strerror(-r));
330                         return 0;
331                 }
332
333                 break;
334         default:
335                 assert_not_reached("Can not parse NetDev");
336         }
337
338         netdev_ref(netdev);
339
340         return 0;
341 }
342
343 int config_parse_tunnel(const char *unit,
344                         const char *filename,
345                         unsigned line,
346                         const char *section,
347                         unsigned section_line,
348                         const char *lvalue,
349                         int ltype,
350                         const char *rvalue,
351                         void *data,
352                         void *userdata) {
353         Network *network = userdata;
354         NetDev *netdev;
355         int r;
356
357         assert(filename);
358         assert(lvalue);
359         assert(rvalue);
360         assert(data);
361
362         r = netdev_get(network->manager, rvalue, &netdev);
363         if (r < 0) {
364                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
365                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
366                 return 0;
367         }
368
369         if (netdev->kind != NETDEV_KIND_IPIP &&
370             netdev->kind != NETDEV_KIND_SIT &&
371             netdev->kind != NETDEV_KIND_GRE &&
372             netdev->kind != NETDEV_KIND_VTI) {
373                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
374                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
375                 return 0;
376         }
377
378         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
379         if (r < 0) {
380                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
381                            "Can not add VLAN '%s' to network: %s",
382                            rvalue, strerror(-r));
383                 return 0;
384         }
385
386         netdev_ref(netdev);
387
388         return 0;
389 }