chiark / gitweb /
f60f7c812cc7eae5ce848974ac95ae6e64296130
[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
94         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
95                         (void*) network_network_gperf_lookup, false, false, network);
96         if (r < 0) {
97                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
98                 return r;
99         }
100
101         LIST_PREPEND(networks, manager->networks, network);
102
103         LIST_FOREACH(routes, route, network->static_routes) {
104                 if (!route->family) {
105                         log_warning("Route section without Gateway field configured in %s. "
106                                     "Ignoring", filename);
107                         return 0;
108                 }
109         }
110
111         LIST_FOREACH(addresses, address, network->static_addresses) {
112                 if (!address->family) {
113                         log_warning("Address section without Address field configured in %s. "
114                                     "Ignoring", filename);
115                         return 0;
116                 }
117         }
118
119         network = NULL;
120
121         return 0;
122 }
123
124 int network_load(Manager *manager) {
125         Network *network;
126         _cleanup_strv_free_ char **files = NULL;
127         char **f;
128         int r;
129
130         assert(manager);
131
132         while ((network = manager->networks))
133                 network_free(network);
134
135         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
136         if (r < 0) {
137                 log_error("Failed to enumerate network files: %s", strerror(-r));
138                 return r;
139         }
140
141         STRV_FOREACH_BACKWARDS(f, files) {
142                 r = network_load_one(manager, *f);
143                 if (r < 0)
144                         return r;
145         }
146
147         return 0;
148 }
149
150 void network_free(Network *network) {
151         NetDev *netdev;
152         Route *route;
153         Address *address;
154         Iterator i;
155
156         if (!network)
157                 return;
158
159         free(network->filename);
160
161         free(network->match_mac);
162         free(network->match_path);
163         free(network->match_driver);
164         free(network->match_type);
165         free(network->match_name);
166
167         free(network->description);
168
169         while ((address = network->ntp)) {
170                 LIST_REMOVE(addresses, network->ntp, address);
171                 address_free(address);
172         }
173
174         while ((address = network->dns)) {
175                 LIST_REMOVE(addresses, network->dns, address);
176                 address_free(address);
177         }
178
179         netdev_unref(network->bridge);
180
181         netdev_unref(network->bond);
182
183         netdev_unref(network->tunnel);
184
185         HASHMAP_FOREACH(netdev, network->vlans, i)
186                 netdev_unref(netdev);
187         hashmap_free(network->vlans);
188
189         HASHMAP_FOREACH(netdev, network->macvlans, i)
190                 netdev_unref(netdev);
191         hashmap_free(network->macvlans);
192
193         HASHMAP_FOREACH(netdev, network->vxlans, i)
194                 netdev_unref(netdev);
195         hashmap_free(network->vxlans);
196
197         while ((route = network->static_routes))
198                 route_free(route);
199
200         while ((address = network->static_addresses))
201                 address_free(address);
202
203         hashmap_free(network->addresses_by_section);
204         hashmap_free(network->routes_by_section);
205
206         if (network->manager && network->manager->networks)
207                 LIST_REMOVE(networks, network->manager->networks, network);
208
209         condition_free_list(network->match_host);
210         condition_free_list(network->match_virt);
211         condition_free_list(network->match_kernel);
212         condition_free_list(network->match_arch);
213
214         free(network);
215 }
216
217 int network_get(Manager *manager, struct udev_device *device,
218                 const char *ifname, const struct ether_addr *address,
219                 Network **ret) {
220         Network *network;
221
222         assert(manager);
223         assert(ret);
224
225         LIST_FOREACH(networks, network, manager->networks) {
226                 if (net_match_config(network->match_mac, network->match_path,
227                                      network->match_driver, network->match_type,
228                                      network->match_name, network->match_host,
229                                      network->match_virt, network->match_kernel,
230                                      network->match_arch,
231                                      address,
232                                      udev_device_get_property_value(device, "ID_PATH"),
233                                      udev_device_get_driver(udev_device_get_parent(device)),
234                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
235                                      udev_device_get_devtype(device),
236                                      ifname)) {
237                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
238                                   network->filename);
239                         *ret = network;
240                         return 0;
241                 }
242         }
243
244         *ret = NULL;
245
246         return -ENOENT;
247 }
248
249 int network_apply(Manager *manager, Network *network, Link *link) {
250         int r;
251
252         link->network = network;
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                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
324                 if (r < 0) {
325                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
326                                    "Can not add VLAN to network: %s", rvalue);
327                         return 0;
328                 }
329
330                 break;
331         case NETDEV_KIND_MACVLAN:
332                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
333                 if (r < 0) {
334                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
335                                    "Can not add MACVLAN to network: %s", rvalue);
336                         return 0;
337                 }
338
339                 break;
340         case NETDEV_KIND_VXLAN:
341                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
342                 if (r < 0) {
343                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
344                                    "Can not add VXLAN to network: %s", rvalue);
345                         return 0;
346                 }
347
348                 break;
349         default:
350                 assert_not_reached("Can not parse NetDev");
351         }
352
353         netdev_ref(netdev);
354
355         return 0;
356 }
357
358 int config_parse_tunnel(const char *unit,
359                         const char *filename,
360                         unsigned line,
361                         const char *section,
362                         unsigned section_line,
363                         const char *lvalue,
364                         int ltype,
365                         const char *rvalue,
366                         void *data,
367                         void *userdata) {
368         Network *network = userdata;
369         NetDev *netdev;
370         int r;
371
372         assert(filename);
373         assert(lvalue);
374         assert(rvalue);
375         assert(data);
376
377         r = netdev_get(network->manager, rvalue, &netdev);
378         if (r < 0) {
379                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
380                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
381                 return 0;
382         }
383
384         if (netdev->kind != NETDEV_KIND_IPIP &&
385             netdev->kind != NETDEV_KIND_SIT &&
386             netdev->kind != NETDEV_KIND_GRE &&
387             netdev->kind != NETDEV_KIND_VTI) {
388                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
389                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
390                 return 0;
391         }
392
393         network->tunnel = netdev;
394
395         return 0;
396 }