]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
networkd: add basic VLAN support
[thirdparty/systemd.git] / src / network / networkd-netdev.c
CommitLineData
02b59d57
TG
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 "list.h"
28
52433f6b
TG
29static const char* const netdev_kind_table[] = {
30 [NETDEV_KIND_BRIDGE] = "bridge",
54abf461
TG
31 [NETDEV_KIND_BOND] = "bond",
32 [NETDEV_KIND_VLAN] = "vlan",
52433f6b 33};
02b59d57 34
52433f6b
TG
35DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetdevKind);
36DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetdevKind, "Failed to parse netdev kind");
37
38void netdev_free(Netdev *netdev) {
39 netdev_enslave_callback *callback;
40
41 if (!netdev)
02b59d57
TG
42 return;
43
52433f6b
TG
44 while ((callback = netdev->callbacks)) {
45 LIST_REMOVE(callbacks, netdev->callbacks, callback);
02b59d57
TG
46 free(callback);
47 }
48
52433f6b
TG
49 if (netdev->name)
50 hashmap_remove(netdev->manager->netdevs, netdev->name);
02b59d57 51
52433f6b 52 free(netdev->filename);
02b59d57 53
52433f6b
TG
54 free(netdev->description);
55 free(netdev->name);
02b59d57 56
52433f6b 57 free(netdev);
02b59d57
TG
58}
59
52433f6b
TG
60int netdev_get(Manager *manager, const char *name, Netdev **ret) {
61 Netdev *netdev;
02b59d57
TG
62
63 assert(manager);
64 assert(name);
65 assert(ret);
66
52433f6b
TG
67 netdev = hashmap_get(manager->netdevs, name);
68 if (!netdev) {
02b59d57
TG
69 *ret = NULL;
70 return -ENOENT;
71 }
72
52433f6b 73 *ret = netdev;
02b59d57
TG
74
75 return 0;
76}
77
52433f6b
TG
78static int netdev_enter_failed(Netdev *netdev) {
79 netdev->state = NETDEV_STATE_FAILED;
02b59d57
TG
80
81 return 0;
82}
83
52433f6b 84static int netdev_enslave_ready(Netdev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
02b59d57
TG
85 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
86 int r;
87
52433f6b
TG
88 assert(netdev);
89 assert(netdev->state == NETDEV_STATE_READY);
02b59d57
TG
90 assert(link);
91 assert(callback);
92
fc25d7f8 93 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
02b59d57 94 if (r < 0) {
52433f6b 95 log_error_netdev(netdev,
3333d748
ZJS
96 "Could not allocate RTM_SETLINK message: %s",
97 strerror(-r));
02b59d57
TG
98 return r;
99 }
100
52433f6b 101 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->link->ifindex);
02b59d57 102 if (r < 0) {
52433f6b 103 log_error_netdev(netdev,
3333d748
ZJS
104 "Could not append IFLA_MASTER attribute: %s",
105 strerror(-r));
02b59d57
TG
106 return r;
107 }
108
52433f6b 109 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 110 if (r < 0) {
52433f6b 111 log_error_netdev(netdev,
3333d748
ZJS
112 "Could not send rtnetlink message: %s",
113 strerror(-r));
02b59d57
TG
114 return r;
115 }
116
52433f6b 117 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 118
02b59d57
TG
119 return 0;
120}
121
52433f6b
TG
122static int netdev_enter_ready(Netdev *netdev) {
123 netdev_enslave_callback *callback;
02b59d57 124
52433f6b
TG
125 assert(netdev);
126 assert(netdev->name);
924fe430 127
52433f6b 128 netdev->state = NETDEV_STATE_READY;
02b59d57 129
52433f6b 130 log_info_netdev(netdev, "netdev ready");
02b59d57 131
52433f6b
TG
132 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
133 /* enslave the links that were attempted to be enslaved befor the
02b59d57 134 * link was ready */
52433f6b 135 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
136 }
137
138 return 0;
139}
140
52433f6b
TG
141static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
142 Netdev *netdev = userdata;
02b59d57
TG
143 int r;
144
52433f6b 145 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
146
147 r = sd_rtnl_message_get_errno(m);
148 if (r < 0) {
52433f6b
TG
149 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
150 netdev_enter_failed(netdev);
dd3efc09
TG
151
152 return 1;
02b59d57
TG
153 }
154
dd3efc09 155 return 1;
02b59d57
TG
156}
157
54abf461 158static int netdev_create(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
02b59d57 159 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 160 const char *kind;
02b59d57
TG
161 int r;
162
52433f6b 163 assert(netdev);
54abf461 164 assert(!(netdev->kind == NETDEV_KIND_VLAN) || (link && callback && netdev->vlanid >= 0));
52433f6b
TG
165 assert(netdev->name);
166 assert(netdev->manager);
167 assert(netdev->manager->rtnl);
02b59d57 168
fc25d7f8 169 r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
02b59d57 170 if (r < 0) {
52433f6b 171 log_error_netdev(netdev,
3333d748
ZJS
172 "Could not allocate RTM_NEWLINK message: %s",
173 strerror(-r));
02b59d57
TG
174 return r;
175 }
176
54abf461
TG
177 if (link) {
178 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
179 if (r < 0) {
180 log_error_netdev(netdev,
181 "Could not append IFLA_LINK attribute: %s",
182 strerror(-r));
183 return r;
184 }
185 }
186
52433f6b 187 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 188 if (r < 0) {
52433f6b 189 log_error_netdev(netdev,
3333d748
ZJS
190 "Could not append IFLA_IFNAME attribute: %s",
191 strerror(-r));
02b59d57
TG
192 return r;
193 }
194
195 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
196 if (r < 0) {
52433f6b 197 log_error_netdev(netdev,
3333d748
ZJS
198 "Could not open IFLA_LINKINFO container: %s",
199 strerror(-r));
02b59d57
TG
200 return r;
201 }
202
52433f6b
TG
203 kind = netdev_kind_to_string(netdev->kind);
204 if (!kind) {
205 log_error_netdev(netdev, "Invalid kind");
206 return -EINVAL;
207 }
208
209 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
02b59d57 210 if (r < 0) {
52433f6b 211 log_error_netdev(netdev,
3333d748
ZJS
212 "Could not append IFLA_INFO_KIND attribute: %s",
213 strerror(-r));
02b59d57
TG
214 return r;
215 }
216
54abf461
TG
217 if (netdev->vlanid >= 0) {
218 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
219 if (r < 0) {
220 log_error_netdev(netdev,
221 "Could not open IFLA_INFO_DATA container: %s",
222 strerror(-r));
223 return r;
224 }
225
226 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
227 if (r < 0) {
228 log_error_netdev(netdev,
229 "Could not append IFLA_VLAN_ID attribute: %s",
230 strerror(-r));
231 return r;
232 }
233
234 r = sd_rtnl_message_close_container(req);
235 if (r < 0) {
236 log_error_netdev(netdev,
237 "Could not close IFLA_INFO_DATA container %s",
238 strerror(-r));
239 return r;
240 }
241 }
242
02b59d57
TG
243 r = sd_rtnl_message_close_container(req);
244 if (r < 0) {
52433f6b 245 log_error_netdev(netdev,
3333d748
ZJS
246 "Could not close IFLA_LINKINFO container %s",
247 strerror(-r));
02b59d57
TG
248 return r;
249 }
250
54abf461
TG
251 if (link)
252 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
253 else
254 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 255 if (r < 0) {
52433f6b 256 log_error_netdev(netdev,
3333d748 257 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
258 return r;
259 }
260
52433f6b 261 log_debug_netdev(netdev, "creating netdev");
02b59d57 262
52433f6b 263 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
264
265 return 0;
266}
267
52433f6b 268int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
54abf461
TG
269 if (netdev->kind == NETDEV_KIND_VLAN)
270 return netdev_create(netdev, link, callback);
271
52433f6b
TG
272 if (netdev->state == NETDEV_STATE_READY) {
273 netdev_enslave_ready(netdev, link, callback);
02b59d57 274 } else {
52433f6b
TG
275 /* the netdev is not yet read, save this request for when it is*/
276 netdev_enslave_callback *cb;
02b59d57 277
52433f6b 278 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
279 if (!cb)
280 return log_oom();
281
282 cb->callback = callback;
283 cb->link = link;
284
52433f6b 285 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
286 }
287
288 return 0;
289}
290
52433f6b
TG
291int netdev_set_link(Manager *m, NetdevKind kind, Link *link) {
292 Netdev *netdev;
924fe430 293 int r;
02b59d57 294
52433f6b 295 r = netdev_get(m, link->ifname, &netdev);
924fe430
TG
296 if (r < 0)
297 return r;
02b59d57 298
52433f6b 299 if (netdev->link && netdev->link != link)
02b59d57
TG
300 return -EEXIST;
301
52433f6b
TG
302 if (netdev->kind != kind)
303 return -EINVAL;
02b59d57 304
52433f6b
TG
305 netdev->link = link;
306
307 netdev_enter_ready(netdev);
02b59d57
TG
308
309 return 0;
310}
311
52433f6b
TG
312static int netdev_load_one(Manager *manager, const char *filename) {
313 _cleanup_netdev_free_ Netdev *netdev = NULL;
02b59d57
TG
314 _cleanup_fclose_ FILE *file = NULL;
315 int r;
316
bf1bc670
TA
317 assert(manager);
318 assert(filename);
319
02b59d57
TG
320 file = fopen(filename, "re");
321 if (!file) {
322 if (errno == ENOENT)
323 return 0;
324 else
325 return errno;
326 }
327
52433f6b
TG
328 netdev = new0(Netdev, 1);
329 if (!netdev)
02b59d57
TG
330 return log_oom();
331
52433f6b
TG
332 netdev->manager = manager;
333 netdev->state = _NETDEV_STATE_INVALID;
334 netdev->kind = _NETDEV_KIND_INVALID;
54abf461 335 netdev->vlanid = -1;
02b59d57 336
54abf461 337 r = config_parse(NULL, filename, file, "Netdev\0VLAN\0", config_item_perf_lookup,
52433f6b 338 (void*) network_gperf_lookup, false, false, netdev);
02b59d57
TG
339 if (r < 0) {
340 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
341 return r;
449f7554 342 }
02b59d57 343
52433f6b
TG
344 if (netdev->kind == _NETDEV_KIND_INVALID) {
345 log_warning("Netdev without Kind configured in %s. Ignoring", filename);
346 return 0;
347 }
348
349 if (!netdev->name) {
350 log_warning("Netdev without Name configured in %s. Ignoring", filename);
02b59d57
TG
351 return 0;
352 }
353
54abf461
TG
354 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid < 0) {
355 log_warning("VLAN without Id configured in %s. Ignoring", filename);
356 return 0;
357 }
358
52433f6b
TG
359 netdev->filename = strdup(filename);
360 if (!netdev->filename)
02b59d57
TG
361 return log_oom();
362
52433f6b 363 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
364 if (r < 0)
365 return r;
366
52433f6b 367 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 368
54abf461
TG
369 if (netdev->kind != NETDEV_KIND_VLAN) {
370 r = netdev_create(netdev, NULL, NULL);
371 if (r < 0)
372 return r;
373 }
02b59d57 374
52433f6b 375 netdev = NULL;
02b59d57
TG
376
377 return 0;
378}
379
52433f6b
TG
380int netdev_load(Manager *manager) {
381 Netdev *netdev;
02b59d57
TG
382 char **files, **f;
383 int r;
384
385 assert(manager);
386
52433f6b
TG
387 while ((netdev = hashmap_first(manager->netdevs)))
388 netdev_free(netdev);
02b59d57 389
2ad8416d 390 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
391 if (r < 0) {
392 log_error("Failed to enumerate netdev files: %s", strerror(-r));
393 return r;
394 }
395
396 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 397 r = netdev_load_one(manager, *f);
02b59d57
TG
398 if (r < 0)
399 return r;
400 }
401
402 strv_free(files);
403
404 return 0;
405}