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