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