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