chiark / gitweb /
networkd: introduce ipip tunnel
[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
24 #include "networkd.h"
25 #include "network-internal.h"
26 #include "path-util.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
29 #include "util.h"
30
31 static int network_load_one(Manager *manager, const char *filename) {
32         _cleanup_network_free_ Network *network = NULL;
33         _cleanup_fclose_ FILE *file = NULL;
34         Route *route;
35         Address *address;
36         int r;
37
38         assert(manager);
39         assert(filename);
40
41         file = fopen(filename, "re");
42         if (!file) {
43                 if (errno == ENOENT)
44                         return 0;
45                 else
46                         return -errno;
47         }
48
49         if (null_or_empty_path(filename)) {
50                 log_debug("skipping empty file: %s", filename);
51                 return 0;
52         }
53
54         network = new0(Network, 1);
55         if (!network)
56                 return log_oom();
57
58         network->manager = manager;
59
60         LIST_HEAD_INIT(network->static_addresses);
61         LIST_HEAD_INIT(network->static_routes);
62
63         network->vlans = hashmap_new(string_hash_func, string_compare_func);
64         if (!network->vlans)
65                 return log_oom();
66
67         network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
68         if (!network->macvlans)
69                 return log_oom();
70
71         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
72         if (!network->addresses_by_section)
73                 return log_oom();
74
75         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
76         if (!network->routes_by_section)
77                 return log_oom();
78
79         network->dns = set_new(NULL, NULL);
80         if (!network->dns)
81                 return log_oom();
82
83         network->filename = strdup(filename);
84         if (!network->filename)
85                 return log_oom();
86
87         network->dhcp_dns = true;
88         network->dhcp_hostname = true;
89         network->dhcp_domainname = true;
90
91         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
92                         (void*) network_network_gperf_lookup, false, false, network);
93         if (r < 0) {
94                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
95                 return r;
96         }
97
98         LIST_PREPEND(networks, manager->networks, network);
99
100         LIST_FOREACH(static_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(static_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
166         SET_FOREACH(address, network->dns, i)
167                 address_free(address);
168
169         set_free(network->dns);
170
171         netdev_unref(network->bridge);
172
173         netdev_unref(network->bond);
174
175         HASHMAP_FOREACH(netdev, network->vlans, i)
176                 netdev_unref(netdev);
177         hashmap_free(network->vlans);
178
179         HASHMAP_FOREACH(netdev, network->macvlans, i)
180                 netdev_unref(netdev);
181         hashmap_free(network->macvlans);
182
183         while ((route = network->static_routes))
184                 route_free(route);
185
186         while ((address = network->static_addresses))
187                 address_free(address);
188
189         hashmap_free(network->addresses_by_section);
190         hashmap_free(network->routes_by_section);
191
192         if (network->manager && network->manager->networks)
193                 LIST_REMOVE(networks, network->manager->networks, network);
194
195         condition_free_list(network->match_host);
196         condition_free_list(network->match_virt);
197         condition_free_list(network->match_kernel);
198         condition_free_list(network->match_arch);
199
200         free(network);
201 }
202
203 int network_get(Manager *manager, struct udev_device *device,
204                 const char *ifname, const struct ether_addr *address,
205                 Network **ret) {
206         Network *network;
207
208         assert(manager);
209         assert(ret);
210
211         LIST_FOREACH(networks, network, manager->networks) {
212                 if (net_match_config(network->match_mac, network->match_path,
213                                      network->match_driver, network->match_type,
214                                      network->match_name, network->match_host,
215                                      network->match_virt, network->match_kernel,
216                                      network->match_arch,
217                                      address,
218                                      udev_device_get_property_value(device, "ID_PATH"),
219                                      udev_device_get_driver(udev_device_get_parent(device)),
220                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
221                                      udev_device_get_devtype(device),
222                                      ifname)) {
223                         log_debug("%s: found matching network '%s'", ifname,
224                                   network->filename);
225                         *ret = network;
226                         return 0;
227                 }
228         }
229
230         *ret = NULL;
231
232         return -ENOENT;
233 }
234
235 int network_apply(Manager *manager, Network *network, Link *link) {
236         int r;
237
238         link->network = network;
239
240         if (network->dns) {
241                 r = manager_update_resolv_conf(manager);
242                 if (r < 0)
243                         return r;
244         }
245
246         return 0;
247 }
248
249 int config_parse_netdev(const char *unit,
250                 const char *filename,
251                 unsigned line,
252                 const char *section,
253                 unsigned section_line,
254                 const char *lvalue,
255                 int ltype,
256                 const char *rvalue,
257                 void *data,
258                 void *userdata) {
259         Network *network = userdata;
260         _cleanup_free_ char *kind_string = NULL;
261         char *p;
262         NetDev *netdev;
263         NetDevKind kind;
264         int r;
265
266         assert(filename);
267         assert(lvalue);
268         assert(rvalue);
269         assert(data);
270
271         kind_string = strdup(lvalue);
272         if (!kind_string)
273                 return log_oom();
274
275         /* the keys are CamelCase versions of the kind */
276         for (p = kind_string; *p; p++)
277                 *p = tolower(*p);
278
279         kind = netdev_kind_from_string(kind_string);
280         if (kind == _NETDEV_KIND_INVALID) {
281                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
282                            "Invalid NetDev kind: %s", lvalue);
283                 return 0;
284         }
285
286         r = netdev_get(network->manager, rvalue, &netdev);
287         if (r < 0) {
288                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
289                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
290                 return 0;
291         }
292
293         if (netdev->kind != kind) {
294                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
295                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
296                 return 0;
297         }
298
299         switch (kind) {
300         case NETDEV_KIND_BRIDGE:
301                 network->bridge = netdev;
302
303                 break;
304         case NETDEV_KIND_BOND:
305                 network->bond = netdev;
306
307                 break;
308         case NETDEV_KIND_VLAN:
309                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
310                 if (r < 0) {
311                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
312                                    "Can not add VLAN to network: %s", rvalue);
313                         return 0;
314                 }
315
316                 break;
317         case NETDEV_KIND_MACVLAN:
318                 r = hashmap_put(network->macvlans, netdev->name, netdev);
319                 if (r < 0) {
320                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
321                                    "Can not add MACVLAN to network: %s", rvalue);
322                         return 0;
323                 }
324
325                 break;
326         default:
327                 assert_not_reached("Can not parse NetDev");
328         }
329
330         netdev_ref(netdev);
331
332         return 0;
333 }
334
335 int config_parse_tunnel(const char *unit,
336                         const char *filename,
337                         unsigned line,
338                         const char *section,
339                         unsigned section_line,
340                         const char *lvalue,
341                         int ltype,
342                         const char *rvalue,
343                         void *data,
344                         void *userdata) {
345         Network *network = userdata;
346         NetDev *netdev;
347         int r;
348
349         assert(filename);
350         assert(lvalue);
351         assert(rvalue);
352         assert(data);
353
354         r = netdev_get(network->manager, rvalue, &netdev);
355         if (r < 0) {
356                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
357                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
358                 return 0;
359         }
360
361         if (netdev->kind != NETDEV_KIND_IPIP &&
362             netdev->kind != NETDEV_KIND_SIT &&
363             netdev->kind != NETDEV_KIND_GRE) {
364                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
365                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
366                 return 0;
367         }
368
369         network->tunnel = netdev;
370
371         return 0;
372 }