chiark / gitweb /
networkd: introduce vxlan
[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
93         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
94                         (void*) network_network_gperf_lookup, false, false, network);
95         if (r < 0) {
96                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
97                 return r;
98         }
99
100         LIST_PREPEND(networks, manager->networks, network);
101
102         LIST_FOREACH(routes, route, network->static_routes) {
103                 if (!route->family) {
104                         log_warning("Route section without Gateway field configured in %s. "
105                                     "Ignoring", filename);
106                         return 0;
107                 }
108         }
109
110         LIST_FOREACH(addresses, address, network->static_addresses) {
111                 if (!address->family) {
112                         log_warning("Address section without Address field configured in %s. "
113                                     "Ignoring", filename);
114                         return 0;
115                 }
116         }
117
118         network = NULL;
119
120         return 0;
121 }
122
123 int network_load(Manager *manager) {
124         Network *network;
125         _cleanup_strv_free_ char **files = NULL;
126         char **f;
127         int r;
128
129         assert(manager);
130
131         while ((network = manager->networks))
132                 network_free(network);
133
134         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
135         if (r < 0) {
136                 log_error("Failed to enumerate network files: %s", strerror(-r));
137                 return r;
138         }
139
140         STRV_FOREACH_BACKWARDS(f, files) {
141                 r = network_load_one(manager, *f);
142                 if (r < 0)
143                         return r;
144         }
145
146         return 0;
147 }
148
149 void network_free(Network *network) {
150         NetDev *netdev;
151         Route *route;
152         Address *address;
153         Iterator i;
154
155         if (!network)
156                 return;
157
158         free(network->filename);
159
160         free(network->match_mac);
161         free(network->match_path);
162         free(network->match_driver);
163         free(network->match_type);
164         free(network->match_name);
165
166         free(network->description);
167
168         while ((address = network->ntp)) {
169                 LIST_REMOVE(addresses, network->ntp, address);
170                 address_free(address);
171         }
172
173         while ((address = network->dns)) {
174                 LIST_REMOVE(addresses, network->dns, address);
175                 address_free(address);
176         }
177
178         netdev_unref(network->bridge);
179
180         netdev_unref(network->bond);
181
182         HASHMAP_FOREACH(netdev, network->vlans, i)
183                 netdev_unref(netdev);
184         hashmap_free(network->vlans);
185
186         HASHMAP_FOREACH(netdev, network->macvlans, i)
187                 netdev_unref(netdev);
188         hashmap_free(network->macvlans);
189
190         HASHMAP_FOREACH(netdev, network->vxlans, i)
191                 netdev_unref(netdev);
192         hashmap_free(network->vxlans);
193
194         while ((route = network->static_routes))
195                 route_free(route);
196
197         while ((address = network->static_addresses))
198                 address_free(address);
199
200         hashmap_free(network->addresses_by_section);
201         hashmap_free(network->routes_by_section);
202
203         if (network->manager && network->manager->networks)
204                 LIST_REMOVE(networks, network->manager->networks, network);
205
206         condition_free_list(network->match_host);
207         condition_free_list(network->match_virt);
208         condition_free_list(network->match_kernel);
209         condition_free_list(network->match_arch);
210
211         free(network);
212 }
213
214 int network_get(Manager *manager, struct udev_device *device,
215                 const char *ifname, const struct ether_addr *address,
216                 Network **ret) {
217         Network *network;
218
219         assert(manager);
220         assert(ret);
221
222         LIST_FOREACH(networks, network, manager->networks) {
223                 if (net_match_config(network->match_mac, network->match_path,
224                                      network->match_driver, network->match_type,
225                                      network->match_name, network->match_host,
226                                      network->match_virt, network->match_kernel,
227                                      network->match_arch,
228                                      address,
229                                      udev_device_get_property_value(device, "ID_PATH"),
230                                      udev_device_get_driver(udev_device_get_parent(device)),
231                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
232                                      udev_device_get_devtype(device),
233                                      ifname)) {
234                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
235                                   network->filename);
236                         *ret = network;
237                         return 0;
238                 }
239         }
240
241         *ret = NULL;
242
243         return -ENOENT;
244 }
245
246 int network_apply(Manager *manager, Network *network, Link *link) {
247         int r;
248
249         link->network = network;
250
251         if (network->dns || network->ntp) {
252                 r = link_save(link);
253                 if (r < 0)
254                         return r;
255         }
256
257         return 0;
258 }
259
260 int config_parse_netdev(const char *unit,
261                 const char *filename,
262                 unsigned line,
263                 const char *section,
264                 unsigned section_line,
265                 const char *lvalue,
266                 int ltype,
267                 const char *rvalue,
268                 void *data,
269                 void *userdata) {
270         Network *network = userdata;
271         _cleanup_free_ char *kind_string = NULL;
272         char *p;
273         NetDev *netdev;
274         NetDevKind kind;
275         int r;
276
277         assert(filename);
278         assert(lvalue);
279         assert(rvalue);
280         assert(data);
281
282         kind_string = strdup(lvalue);
283         if (!kind_string)
284                 return log_oom();
285
286         /* the keys are CamelCase versions of the kind */
287         for (p = kind_string; *p; p++)
288                 *p = tolower(*p);
289
290         kind = netdev_kind_from_string(kind_string);
291         if (kind == _NETDEV_KIND_INVALID) {
292                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
293                            "Invalid NetDev kind: %s", lvalue);
294                 return 0;
295         }
296
297         r = netdev_get(network->manager, rvalue, &netdev);
298         if (r < 0) {
299                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
300                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
301                 return 0;
302         }
303
304         if (netdev->kind != kind) {
305                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
306                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
307                 return 0;
308         }
309
310         switch (kind) {
311         case NETDEV_KIND_BRIDGE:
312                 network->bridge = netdev;
313
314                 break;
315         case NETDEV_KIND_BOND:
316                 network->bond = netdev;
317
318                 break;
319         case NETDEV_KIND_VLAN:
320                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
321                 if (r < 0) {
322                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
323                                    "Can not add VLAN to network: %s", rvalue);
324                         return 0;
325                 }
326
327                 break;
328         case NETDEV_KIND_MACVLAN:
329                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
330                 if (r < 0) {
331                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
332                                    "Can not add MACVLAN to network: %s", rvalue);
333                         return 0;
334                 }
335
336                 break;
337         case NETDEV_KIND_VXLAN:
338                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
339                 if (r < 0) {
340                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
341                                    "Can not add VXLAN to network: %s", rvalue);
342                         return 0;
343                 }
344
345                 break;
346         default:
347                 assert_not_reached("Can not parse NetDev");
348         }
349
350         netdev_ref(netdev);
351
352         return 0;
353 }
354
355 int config_parse_tunnel(const char *unit,
356                         const char *filename,
357                         unsigned line,
358                         const char *section,
359                         unsigned section_line,
360                         const char *lvalue,
361                         int ltype,
362                         const char *rvalue,
363                         void *data,
364                         void *userdata) {
365         Network *network = userdata;
366         NetDev *netdev;
367         int r;
368
369         assert(filename);
370         assert(lvalue);
371         assert(rvalue);
372         assert(data);
373
374         r = netdev_get(network->manager, rvalue, &netdev);
375         if (r < 0) {
376                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
377                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
378                 return 0;
379         }
380
381         if (netdev->kind != NETDEV_KIND_IPIP &&
382             netdev->kind != NETDEV_KIND_SIT &&
383             netdev->kind != NETDEV_KIND_GRE &&
384             netdev->kind != NETDEV_KIND_VTI) {
385                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
386                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
387                 return 0;
388         }
389
390         network->tunnel = netdev;
391
392         return 0;
393 }