chiark / gitweb /
networkd: split out vlan and macvlan handling
[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 "network-internal.h"
27 #include "path-util.h"
28 #include "conf-files.h"
29 #include "conf-parser.h"
30 #include "util.h"
31
32 static int network_load_one(Manager *manager, const char *filename) {
33         _cleanup_network_free_ Network *network = NULL;
34         _cleanup_fclose_ FILE *file = NULL;
35         Route *route;
36         Address *address;
37         int r;
38
39         assert(manager);
40         assert(filename);
41
42         file = fopen(filename, "re");
43         if (!file) {
44                 if (errno == ENOENT)
45                         return 0;
46                 else
47                         return -errno;
48         }
49
50         if (null_or_empty_path(filename)) {
51                 log_debug("skipping empty file: %s", filename);
52                 return 0;
53         }
54
55         network = new0(Network, 1);
56         if (!network)
57                 return log_oom();
58
59         network->manager = manager;
60
61         LIST_HEAD_INIT(network->static_addresses);
62         LIST_HEAD_INIT(network->static_routes);
63
64         network->vlans = hashmap_new(string_hash_func, string_compare_func);
65         if (!network->vlans)
66                 return log_oom();
67
68         network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
69         if (!network->macvlans)
70                 return log_oom();
71
72         network->vxlans = hashmap_new(uint64_hash_func, uint64_compare_func);
73         if (!network->vxlans)
74                 return log_oom();
75
76         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
77         if (!network->addresses_by_section)
78                 return log_oom();
79
80         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
81         if (!network->routes_by_section)
82                 return log_oom();
83
84         network->filename = strdup(filename);
85         if (!network->filename)
86                 return log_oom();
87
88         network->dhcp_ntp = true;
89         network->dhcp_dns = true;
90         network->dhcp_hostname = true;
91         network->dhcp_domainname = true;
92         network->dhcp_routes = true;
93         network->dhcp_sendhost = true;
94
95         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
96                         (void*) network_network_gperf_lookup, false, false, network);
97         if (r < 0) {
98                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
99                 return r;
100         }
101
102         LIST_PREPEND(networks, manager->networks, network);
103
104         LIST_FOREACH(routes, route, network->static_routes) {
105                 if (!route->family) {
106                         log_warning("Route section without Gateway field configured in %s. "
107                                     "Ignoring", filename);
108                         return 0;
109                 }
110         }
111
112         LIST_FOREACH(addresses, address, network->static_addresses) {
113                 if (!address->family) {
114                         log_warning("Address section without Address field configured in %s. "
115                                     "Ignoring", filename);
116                         return 0;
117                 }
118         }
119
120         network = NULL;
121
122         return 0;
123 }
124
125 int network_load(Manager *manager) {
126         Network *network;
127         _cleanup_strv_free_ char **files = NULL;
128         char **f;
129         int r;
130
131         assert(manager);
132
133         while ((network = manager->networks))
134                 network_free(network);
135
136         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
137         if (r < 0) {
138                 log_error("Failed to enumerate network files: %s", strerror(-r));
139                 return r;
140         }
141
142         STRV_FOREACH_BACKWARDS(f, files) {
143                 r = network_load_one(manager, *f);
144                 if (r < 0)
145                         return r;
146         }
147
148         return 0;
149 }
150
151 void network_free(Network *network) {
152         NetDev *netdev;
153         Route *route;
154         Address *address;
155         Iterator i;
156
157         if (!network)
158                 return;
159
160         free(network->filename);
161
162         free(network->match_mac);
163         free(network->match_path);
164         free(network->match_driver);
165         free(network->match_type);
166         free(network->match_name);
167
168         free(network->description);
169
170         while ((address = network->ntp)) {
171                 LIST_REMOVE(addresses, network->ntp, address);
172                 address_free(address);
173         }
174
175         while ((address = network->dns)) {
176                 LIST_REMOVE(addresses, network->dns, address);
177                 address_free(address);
178         }
179
180         netdev_unref(network->bridge);
181
182         netdev_unref(network->bond);
183
184         netdev_unref(network->tunnel);
185
186         HASHMAP_FOREACH(netdev, network->vlans, i)
187                 netdev_unref(netdev);
188         hashmap_free(network->vlans);
189
190         HASHMAP_FOREACH(netdev, network->macvlans, i)
191                 netdev_unref(netdev);
192         hashmap_free(network->macvlans);
193
194         HASHMAP_FOREACH(netdev, network->vxlans, i)
195                 netdev_unref(netdev);
196         hashmap_free(network->vxlans);
197
198         while ((route = network->static_routes))
199                 route_free(route);
200
201         while ((address = network->static_addresses))
202                 address_free(address);
203
204         hashmap_free(network->addresses_by_section);
205         hashmap_free(network->routes_by_section);
206
207         if (network->manager && network->manager->networks)
208                 LIST_REMOVE(networks, network->manager->networks, network);
209
210         condition_free_list(network->match_host);
211         condition_free_list(network->match_virt);
212         condition_free_list(network->match_kernel);
213         condition_free_list(network->match_arch);
214
215         free(network);
216 }
217
218 int network_get(Manager *manager, struct udev_device *device,
219                 const char *ifname, const struct ether_addr *address,
220                 Network **ret) {
221         Network *network;
222
223         assert(manager);
224         assert(ret);
225
226         LIST_FOREACH(networks, network, manager->networks) {
227                 if (net_match_config(network->match_mac, network->match_path,
228                                      network->match_driver, network->match_type,
229                                      network->match_name, network->match_host,
230                                      network->match_virt, network->match_kernel,
231                                      network->match_arch,
232                                      address,
233                                      udev_device_get_property_value(device, "ID_PATH"),
234                                      udev_device_get_driver(udev_device_get_parent(device)),
235                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
236                                      udev_device_get_devtype(device),
237                                      ifname)) {
238                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
239                                   network->filename);
240                         *ret = network;
241                         return 0;
242                 }
243         }
244
245         *ret = NULL;
246
247         return -ENOENT;
248 }
249
250 int network_apply(Manager *manager, Network *network, Link *link) {
251         int r;
252
253         link->network = network;
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                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
325                 if (r < 0) {
326                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
327                                    "Can not add VLAN to network: %s", rvalue);
328                         return 0;
329                 }
330
331                 break;
332         case NETDEV_KIND_MACVLAN:
333                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
334                 if (r < 0) {
335                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
336                                    "Can not add MACVLAN to network: %s", rvalue);
337                         return 0;
338                 }
339
340                 break;
341         case NETDEV_KIND_VXLAN:
342                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
343                 if (r < 0) {
344                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
345                                    "Can not add VXLAN to network: %s", rvalue);
346                         return 0;
347                 }
348
349                 break;
350         default:
351                 assert_not_reached("Can not parse NetDev");
352         }
353
354         netdev_ref(netdev);
355
356         return 0;
357 }
358
359 int config_parse_tunnel(const char *unit,
360                         const char *filename,
361                         unsigned line,
362                         const char *section,
363                         unsigned section_line,
364                         const char *lvalue,
365                         int ltype,
366                         const char *rvalue,
367                         void *data,
368                         void *userdata) {
369         Network *network = userdata;
370         NetDev *netdev;
371         int r;
372
373         assert(filename);
374         assert(lvalue);
375         assert(rvalue);
376         assert(data);
377
378         r = netdev_get(network->manager, rvalue, &netdev);
379         if (r < 0) {
380                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
381                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
382                 return 0;
383         }
384
385         if (netdev->kind != NETDEV_KIND_IPIP &&
386             netdev->kind != NETDEV_KIND_SIT &&
387             netdev->kind != NETDEV_KIND_GRE &&
388             netdev->kind != NETDEV_KIND_VTI) {
389                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
390                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
391                 return 0;
392         }
393
394         network->tunnel = netdev;
395
396         return 0;
397 }