chiark / gitweb /
networkd: network - do reference counting on netdevs
[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         char *kind_string, *p;
261         NetDev *netdev;
262         NetDevKind kind;
263         int r;
264
265         assert(filename);
266         assert(lvalue);
267         assert(rvalue);
268         assert(data);
269
270         kind_string = strdup(lvalue);
271         if (!kind_string)
272                 return log_oom();
273
274         /* the keys are CamelCase versions of the kind */
275         for (p = kind_string; *p; p++)
276                 *p = tolower(*p);
277
278         kind = netdev_kind_from_string(kind_string);
279         if (kind == _NETDEV_KIND_INVALID) {
280                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
281                            "Invalid NetDev kind: %s", lvalue);
282                 return 0;
283         }
284
285         r = netdev_get(network->manager, rvalue, &netdev);
286         if (r < 0) {
287                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
288                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
289                 return 0;
290         }
291
292         if (netdev->kind != kind) {
293                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
294                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
295                 return 0;
296         }
297
298         switch (kind) {
299         case NETDEV_KIND_BRIDGE:
300                 network->bridge = netdev;
301
302                 break;
303         case NETDEV_KIND_BOND:
304                 network->bond = netdev;
305
306                 break;
307         case NETDEV_KIND_VLAN:
308                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
309                 if (r < 0) {
310                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
311                                    "Can not add VLAN to network: %s", rvalue);
312                         return 0;
313                 }
314
315                 break;
316         case NETDEV_KIND_MACVLAN:
317                 r = hashmap_put(network->macvlans, netdev->name, netdev);
318                 if (r < 0) {
319                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
320                                    "Can not add MACVLAN to network: %s", rvalue);
321                         return 0;
322                 }
323
324                 break;
325         default:
326                 assert_not_reached("Can not parse NetDev");
327         }
328
329         netdev_ref(netdev);
330
331         return 0;
332 }