]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
logind: add function session_jobs_reply() to unify the create reply
[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
50add290 101 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->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;
172f6635 143 int r;
02b59d57 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
50add290
TG
291int netdev_set_ifindex(Netdev *netdev, int ifindex) {
292 assert(netdev);
293 assert(ifindex > 0);
02b59d57 294
50add290
TG
295 if (netdev->ifindex > 0) {
296 if (netdev->ifindex == ifindex)
297 return 0;
298 else
299 return -EEXIST;
300 }
02b59d57 301
50add290 302 netdev->ifindex = ifindex;
52433f6b
TG
303
304 netdev_enter_ready(netdev);
02b59d57
TG
305
306 return 0;
307}
308
52433f6b
TG
309static int netdev_load_one(Manager *manager, const char *filename) {
310 _cleanup_netdev_free_ Netdev *netdev = NULL;
02b59d57
TG
311 _cleanup_fclose_ FILE *file = NULL;
312 int r;
313
bf1bc670
TA
314 assert(manager);
315 assert(filename);
316
02b59d57
TG
317 file = fopen(filename, "re");
318 if (!file) {
319 if (errno == ENOENT)
320 return 0;
321 else
322 return errno;
323 }
324
52433f6b
TG
325 netdev = new0(Netdev, 1);
326 if (!netdev)
02b59d57
TG
327 return log_oom();
328
52433f6b
TG
329 netdev->manager = manager;
330 netdev->state = _NETDEV_STATE_INVALID;
331 netdev->kind = _NETDEV_KIND_INVALID;
54abf461 332 netdev->vlanid = -1;
02b59d57 333
54abf461 334 r = config_parse(NULL, filename, file, "Netdev\0VLAN\0", config_item_perf_lookup,
52433f6b 335 (void*) network_gperf_lookup, false, false, netdev);
02b59d57
TG
336 if (r < 0) {
337 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
338 return r;
449f7554 339 }
02b59d57 340
52433f6b
TG
341 if (netdev->kind == _NETDEV_KIND_INVALID) {
342 log_warning("Netdev without Kind configured in %s. Ignoring", filename);
343 return 0;
344 }
345
346 if (!netdev->name) {
347 log_warning("Netdev without Name configured in %s. Ignoring", filename);
02b59d57
TG
348 return 0;
349 }
350
54abf461
TG
351 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid < 0) {
352 log_warning("VLAN without Id configured in %s. Ignoring", filename);
353 return 0;
354 }
355
52433f6b
TG
356 netdev->filename = strdup(filename);
357 if (!netdev->filename)
02b59d57
TG
358 return log_oom();
359
52433f6b 360 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
361 if (r < 0)
362 return r;
363
52433f6b 364 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 365
54abf461
TG
366 if (netdev->kind != NETDEV_KIND_VLAN) {
367 r = netdev_create(netdev, NULL, NULL);
368 if (r < 0)
369 return r;
370 }
02b59d57 371
52433f6b 372 netdev = NULL;
02b59d57
TG
373
374 return 0;
375}
376
52433f6b
TG
377int netdev_load(Manager *manager) {
378 Netdev *netdev;
02b59d57
TG
379 char **files, **f;
380 int r;
381
382 assert(manager);
383
52433f6b
TG
384 while ((netdev = hashmap_first(manager->netdevs)))
385 netdev_free(netdev);
02b59d57 386
2ad8416d 387 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
388 if (r < 0) {
389 log_error("Failed to enumerate netdev files: %s", strerror(-r));
390 return r;
391 }
392
393 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 394 r = netdev_load_one(manager, *f);
02b59d57
TG
395 if (r < 0)
396 return r;
397 }
398
399 strv_free(files);
400
401 return 0;
402}