chiark / gitweb /
networkd: add basic support for MACVLANs
[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 "networkd.h"
23 #include "net-util.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "util.h"
28
29 static int network_load_one(Manager *manager, const char *filename) {
30         _cleanup_network_free_ Network *network = NULL;
31         _cleanup_fclose_ FILE *file = NULL;
32         Route *route;
33         Address *address;
34         int r;
35
36         assert(manager);
37         assert(filename);
38
39         file = fopen(filename, "re");
40         if (!file) {
41                 if (errno == ENOENT)
42                         return 0;
43                 else
44                         return errno;
45         }
46
47         network = new0(Network, 1);
48         if (!network)
49                 return log_oom();
50
51         network->manager = manager;
52
53         LIST_HEAD_INIT(network->static_addresses);
54         LIST_HEAD_INIT(network->static_routes);
55
56         network->vlans = hashmap_new(string_hash_func, string_compare_func);
57         if (!network->vlans)
58                 return log_oom();
59
60         network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
61         if (!network->macvlans)
62                 return log_oom();
63
64         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
65         if (!network->addresses_by_section)
66                 return log_oom();
67
68         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
69         if (!network->routes_by_section)
70                 return log_oom();
71
72         network->filename = strdup(filename);
73         if (!network->filename)
74                 return log_oom();
75
76         network->dhcp_dns = true;
77         network->dhcp_hostname = true;
78         network->dhcp_domainname = true;
79
80         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
81                         (void*) network_network_gperf_lookup, false, false, network);
82         if (r < 0) {
83                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
84                 return r;
85         }
86
87         LIST_PREPEND(networks, manager->networks, network);
88
89         LIST_FOREACH(static_routes, route, network->static_routes) {
90                 if (!route->family) {
91                         log_warning("Route section without Gateway field configured in %s. "
92                                     "Ignoring", filename);
93                         return 0;
94                 }
95         }
96
97         LIST_FOREACH(static_addresses, address, network->static_addresses) {
98                 if (!address->family) {
99                         log_warning("Address section without Address field configured in %s. "
100                                     "Ignoring", filename);
101                         return 0;
102                 }
103         }
104
105         network = NULL;
106
107         return 0;
108 }
109
110 int network_load(Manager *manager) {
111         Network *network;
112         _cleanup_strv_free_ char **files = NULL;
113         char **f;
114         int r;
115
116         assert(manager);
117
118         while ((network = manager->networks))
119                 network_free(network);
120
121         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
122         if (r < 0) {
123                 log_error("Failed to enumerate network files: %s", strerror(-r));
124                 return r;
125         }
126
127         STRV_FOREACH_BACKWARDS(f, files) {
128                 r = network_load_one(manager, *f);
129                 if (r < 0)
130                         return r;
131         }
132
133         return 0;
134 }
135
136 void network_free(Network *network) {
137         Route *route;
138         Address *address;
139
140         if (!network)
141                 return;
142
143         free(network->filename);
144
145         free(network->match_mac);
146         free(network->match_path);
147         free(network->match_driver);
148         free(network->match_type);
149         free(network->match_name);
150
151         free(network->description);
152
153         address_free(network->dns);
154
155         hashmap_free(network->vlans);
156
157         hashmap_free(network->macvlans);
158
159         while ((route = network->static_routes))
160                 route_free(route);
161
162         while ((address = network->static_addresses))
163                 address_free(address);
164
165         hashmap_free(network->addresses_by_section);
166         hashmap_free(network->routes_by_section);
167
168         if (network->manager && network->manager->networks)
169                 LIST_REMOVE(networks, network->manager->networks, network);
170
171         free(network);
172 }
173
174 int network_get(Manager *manager, struct udev_device *device, Network **ret) {
175         Network *network;
176
177         assert(manager);
178         assert(device);
179         assert(ret);
180
181         LIST_FOREACH(networks, network, manager->networks) {
182                 if (net_match_config(network->match_mac, network->match_path,
183                                         network->match_driver, network->match_type,
184                                         network->match_name, network->match_host,
185                                         network->match_virt, network->match_kernel,
186                                         network->match_arch,
187                                         udev_device_get_sysattr_value(device, "address"),
188                                         udev_device_get_property_value(device, "ID_PATH"),
189                                         udev_device_get_driver(udev_device_get_parent(device)),
190                                         udev_device_get_property_value(device, "ID_NET_DRIVER"),
191                                         udev_device_get_devtype(device),
192                                         udev_device_get_sysname(device))) {
193                         log_debug("%s: found matching network '%s'",
194                                         udev_device_get_sysname(device),
195                                         network->filename);
196                         *ret = network;
197                         return 0;
198                 }
199         }
200
201         *ret = NULL;
202
203         return -ENOENT;
204 }
205
206 int network_apply(Manager *manager, Network *network, Link *link) {
207         int r;
208
209         link->network = network;
210
211         r = link_configure(link);
212         if (r < 0)
213                 return r;
214
215         if (network->dns) {
216                 r = manager_update_resolv_conf(manager);
217                 if (r < 0)
218                         return r;
219         }
220
221         return 0;
222 }
223
224 int config_parse_bridge(const char *unit,
225                 const char *filename,
226                 unsigned line,
227                 const char *section,
228                 unsigned section_line,
229                 const char *lvalue,
230                 int ltype,
231                 const char *rvalue,
232                 void *data,
233                 void *userdata) {
234         Network *network = userdata;
235         NetDev *netdev;
236         int r;
237
238         assert(filename);
239         assert(lvalue);
240         assert(rvalue);
241         assert(data);
242
243         r = netdev_get(network->manager, rvalue, &netdev);
244         if (r < 0) {
245                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
246                            "Bridge is invalid, ignoring assignment: %s", rvalue);
247                 return 0;
248         }
249
250         if (netdev->kind != NETDEV_KIND_BRIDGE) {
251                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
252                            "NetDev is not a bridge, ignoring assignment: %s", rvalue);
253                 return 0;
254         }
255
256         network->bridge = netdev;
257
258         return 0;
259 }
260
261 int config_parse_bond(const char *unit,
262                 const char *filename,
263                 unsigned line,
264                 const char *section,
265                 unsigned section_line,
266                 const char *lvalue,
267                 int ltype,
268                 const char *rvalue,
269                 void *data,
270                 void *userdata) {
271         Network *network = userdata;
272         NetDev *netdev;
273         int r;
274
275         assert(filename);
276         assert(lvalue);
277         assert(rvalue);
278         assert(data);
279
280         r = netdev_get(network->manager, rvalue, &netdev);
281         if (r < 0) {
282                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
283                            "Bond is invalid, ignoring assignment: %s", rvalue);
284                 return 0;
285         }
286
287         if (netdev->kind != NETDEV_KIND_BOND) {
288                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
289                            "NetDev is not a bond, ignoring assignment: %s", rvalue);
290                 return 0;
291         }
292
293         network->bond = netdev;
294
295         return 0;
296 }
297
298 int config_parse_vlan(const char *unit,
299                 const char *filename,
300                 unsigned line,
301                 const char *section,
302                 unsigned section_line,
303                 const char *lvalue,
304                 int ltype,
305                 const char *rvalue,
306                 void *data,
307                 void *userdata) {
308         Network *network = userdata;
309         NetDev *netdev;
310         int r;
311
312         assert(filename);
313         assert(lvalue);
314         assert(rvalue);
315         assert(data);
316
317         r = netdev_get(network->manager, rvalue, &netdev);
318         if (r < 0) {
319                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
320                            "VLAN is invalid, ignoring assignment: %s", rvalue);
321                 return 0;
322         }
323
324         if (netdev->kind != NETDEV_KIND_VLAN) {
325                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
326                            "NetDev is not a VLAN, ignoring assignment: %s", rvalue);
327                 return 0;
328         }
329
330         r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
331         if (r < 0) {
332                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
333                            "Can not add VLAN to network: %s", rvalue);
334                 return 0;
335         }
336
337         return 0;
338 }
339
340 int config_parse_macvlan(const char *unit,
341                 const char *filename,
342                 unsigned line,
343                 const char *section,
344                 unsigned section_line,
345                 const char *lvalue,
346                 int ltype,
347                 const char *rvalue,
348                 void *data,
349                 void *userdata) {
350         Network *network = userdata;
351         NetDev *netdev;
352         int r;
353
354         assert(filename);
355         assert(lvalue);
356         assert(rvalue);
357         assert(data);
358
359         r = netdev_get(network->manager, rvalue, &netdev);
360         if (r < 0) {
361                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
362                            "MACVLAN is invalid, ignoring assignment: %s", rvalue);
363                 return 0;
364         }
365
366         if (netdev->kind != NETDEV_KIND_MACVLAN) {
367                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
368                            "NetDev is not a MACVLAN, ignoring assignment: %s", rvalue);
369                 return 0;
370         }
371
372         r = hashmap_put(network->macvlans, netdev->name, netdev);
373         if (r < 0) {
374                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
375                            "Can not add MACVLAN to network: %s", rvalue);
376                 return 0;
377         }
378
379         return 0;
380 }