chiark / gitweb /
networkd: VLAN - allow multiple vlans to be created on a link
[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(uint64_hash_func, uint64_compare_func);
57         if (!network->vlans)
58                 return log_oom();
59
60         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
61         if (!network->addresses_by_section)
62                 return log_oom();
63
64         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
65         if (!network->routes_by_section)
66                 return log_oom();
67
68         network->filename = strdup(filename);
69         if (!network->filename)
70                 return log_oom();
71
72         network->dhcp_dns = true;
73         network->dhcp_hostname = true;
74         network->dhcp_domainname = true;
75
76         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
77                         (void*) network_gperf_lookup, false, false, network);
78         if (r < 0) {
79                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
80                 return r;
81         }
82
83         LIST_PREPEND(networks, manager->networks, network);
84
85         LIST_FOREACH(static_routes, route, network->static_routes) {
86                 if (!route->family) {
87                         log_warning("Route section without Gateway field configured in %s. "
88                                     "Ignoring", filename);
89                         return 0;
90                 }
91         }
92
93         LIST_FOREACH(static_addresses, address, network->static_addresses) {
94                 if (!address->family) {
95                         log_warning("Address section without Address field configured in %s. "
96                                     "Ignoring", filename);
97                         return 0;
98                 }
99         }
100
101         network = NULL;
102
103         return 0;
104 }
105
106 int network_load(Manager *manager) {
107         Network *network;
108         _cleanup_strv_free_ char **files = NULL;
109         char **f;
110         int r;
111
112         assert(manager);
113
114         while ((network = manager->networks))
115                 network_free(network);
116
117         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
118         if (r < 0) {
119                 log_error("Failed to enumerate network files: %s", strerror(-r));
120                 return r;
121         }
122
123         STRV_FOREACH_BACKWARDS(f, files) {
124                 r = network_load_one(manager, *f);
125                 if (r < 0)
126                         return r;
127         }
128
129         return 0;
130 }
131
132 void network_free(Network *network) {
133         Route *route;
134         Address *address;
135
136         if (!network)
137                 return;
138
139         free(network->filename);
140
141         free(network->match_mac);
142         free(network->match_path);
143         free(network->match_driver);
144         free(network->match_type);
145         free(network->match_name);
146
147         free(network->description);
148
149         address_free(network->dns);
150
151         hashmap_free(network->vlans);
152
153         while ((route = network->static_routes))
154                 route_free(route);
155
156         while ((address = network->static_addresses))
157                 address_free(address);
158
159         hashmap_free(network->addresses_by_section);
160         hashmap_free(network->routes_by_section);
161
162         if (network->manager && network->manager->networks)
163                 LIST_REMOVE(networks, network->manager->networks, network);
164
165         free(network);
166 }
167
168 int network_get(Manager *manager, struct udev_device *device, Network **ret) {
169         Network *network;
170
171         assert(manager);
172         assert(device);
173         assert(ret);
174
175         LIST_FOREACH(networks, network, manager->networks) {
176                 if (net_match_config(network->match_mac, network->match_path,
177                                         network->match_driver, network->match_type,
178                                         network->match_name,
179                                         udev_device_get_sysattr_value(device, "address"),
180                                         udev_device_get_property_value(device, "ID_PATH"),
181                                         udev_device_get_driver(device),
182                                         udev_device_get_devtype(device),
183                                         udev_device_get_sysname(device))) {
184                         log_debug("%s: found matching network '%s'",
185                                         udev_device_get_sysname(device),
186                                         network->filename);
187                         *ret = network;
188                         return 0;
189                 }
190         }
191
192         *ret = NULL;
193
194         return -ENOENT;
195 }
196
197 int network_apply(Manager *manager, Network *network, Link *link) {
198         int r;
199
200         link->network = network;
201
202         r = link_configure(link);
203         if (r < 0)
204                 return r;
205
206         if (network->dns) {
207                 r = manager_update_resolv_conf(manager);
208                 if (r < 0)
209                         return r;
210         }
211
212         return 0;
213 }
214
215 int config_parse_bridge(const char *unit,
216                 const char *filename,
217                 unsigned line,
218                 const char *section,
219                 unsigned section_line,
220                 const char *lvalue,
221                 int ltype,
222                 const char *rvalue,
223                 void *data,
224                 void *userdata) {
225         Network *network = userdata;
226         NetDev *netdev;
227         int r;
228
229         assert(filename);
230         assert(lvalue);
231         assert(rvalue);
232         assert(data);
233
234         r = netdev_get(network->manager, rvalue, &netdev);
235         if (r < 0) {
236                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
237                            "Bridge is invalid, ignoring assignment: %s", rvalue);
238                 return 0;
239         }
240
241         if (netdev->kind != NETDEV_KIND_BRIDGE) {
242                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
243                            "NetDev is not a bridge, ignoring assignment: %s", rvalue);
244                 return 0;
245         }
246
247         network->bridge = netdev;
248
249         return 0;
250 }
251
252 int config_parse_bond(const char *unit,
253                 const char *filename,
254                 unsigned line,
255                 const char *section,
256                 unsigned section_line,
257                 const char *lvalue,
258                 int ltype,
259                 const char *rvalue,
260                 void *data,
261                 void *userdata) {
262         Network *network = userdata;
263         NetDev *netdev;
264         int r;
265
266         assert(filename);
267         assert(lvalue);
268         assert(rvalue);
269         assert(data);
270
271         r = netdev_get(network->manager, rvalue, &netdev);
272         if (r < 0) {
273                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
274                            "Bond is invalid, ignoring assignment: %s", rvalue);
275                 return 0;
276         }
277
278         if (netdev->kind != NETDEV_KIND_BOND) {
279                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
280                            "NetDev is not a bond, ignoring assignment: %s", rvalue);
281                 return 0;
282         }
283
284         network->bond = netdev;
285
286         return 0;
287 }
288
289 int config_parse_vlan(const char *unit,
290                 const char *filename,
291                 unsigned line,
292                 const char *section,
293                 unsigned section_line,
294                 const char *lvalue,
295                 int ltype,
296                 const char *rvalue,
297                 void *data,
298                 void *userdata) {
299         Network *network = userdata;
300         NetDev *netdev;
301         int r;
302
303         assert(filename);
304         assert(lvalue);
305         assert(rvalue);
306         assert(data);
307
308         r = netdev_get(network->manager, rvalue, &netdev);
309         if (r < 0) {
310                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
311                            "VLAN is invalid, ignoring assignment: %s", rvalue);
312                 return 0;
313         }
314
315         if (netdev->kind != NETDEV_KIND_VLAN) {
316                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
317                            "NetDev is not a VLAN, ignoring assignment: %s", rvalue);
318                 return 0;
319         }
320
321         r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
322         if (r < 0) {
323                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
324                            "Can not add VLAN to network: %s", rvalue);
325                 return 0;
326         }
327
328         return 0;
329 }