]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
networkd: netdev - cancel all callbacks when freeing
[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"
c6f7c917 23#include "network-internal.h"
02b59d57
TG
24#include "path-util.h"
25#include "conf-files.h"
26#include "conf-parser.h"
27#include "list.h"
28
672682a6
TG
29#define VLANID_MAX 4094
30
2c5859af 31static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
52433f6b 32 [NETDEV_KIND_BRIDGE] = "bridge",
54abf461
TG
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
fe6b2d55 35 [NETDEV_KIND_MACVLAN] = "macvlan",
52433f6b 36};
02b59d57 37
1a436809
TG
38DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
52433f6b 40
2c5859af 41static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
fe6b2d55
TG
42 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
46};
47
48DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
50
59cb64e6
TG
51static void netdev_cancel_callbacks(NetDev *netdev) {
52 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
52433f6b
TG
53 netdev_enslave_callback *callback;
54
55 if (!netdev)
02b59d57
TG
56 return;
57
59cb64e6
TG
58 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
59
52433f6b 60 while ((callback = netdev->callbacks)) {
59cb64e6
TG
61 if (m) {
62 assert(callback->link);
63 assert(callback->callback);
64 assert(netdev->manager);
65 assert(netdev->manager->rtnl);
66
67 callback->callback(netdev->manager->rtnl, m, link);
68 }
69
52433f6b 70 LIST_REMOVE(callbacks, netdev->callbacks, callback);
02b59d57
TG
71 free(callback);
72 }
59cb64e6
TG
73}
74
75static void netdev_free(NetDev *netdev) {
76 if (!netdev)
77 return;
78
79 netdev_cancel_callbacks(netdev);
02b59d57 80
52433f6b
TG
81 if (netdev->name)
82 hashmap_remove(netdev->manager->netdevs, netdev->name);
02b59d57 83
52433f6b 84 free(netdev->filename);
02b59d57 85
52433f6b
TG
86 free(netdev->description);
87 free(netdev->name);
02b59d57 88
79e16ce3
LP
89 condition_free_list(netdev->match_host);
90 condition_free_list(netdev->match_virt);
91 condition_free_list(netdev->match_kernel);
92 condition_free_list(netdev->match_arch);
93
52433f6b 94 free(netdev);
02b59d57
TG
95}
96
14b746f7
TG
97NetDev *netdev_unref(NetDev *netdev) {
98 if (netdev && (-- netdev->n_ref <= 0))
99 netdev_free(netdev);
100
101 return NULL;
102}
103
104NetDev *netdev_ref(NetDev *netdev) {
105 if (netdev)
106 assert_se(++ netdev->n_ref >= 2);
107
108 return netdev;
109}
110
1a436809
TG
111int netdev_get(Manager *manager, const char *name, NetDev **ret) {
112 NetDev *netdev;
02b59d57
TG
113
114 assert(manager);
115 assert(name);
116 assert(ret);
117
52433f6b
TG
118 netdev = hashmap_get(manager->netdevs, name);
119 if (!netdev) {
02b59d57
TG
120 *ret = NULL;
121 return -ENOENT;
122 }
123
52433f6b 124 *ret = netdev;
02b59d57
TG
125
126 return 0;
127}
128
1a436809 129static int netdev_enter_failed(NetDev *netdev) {
52433f6b 130 netdev->state = NETDEV_STATE_FAILED;
02b59d57
TG
131
132 return 0;
133}
134
1a436809 135static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
cf6a8911 136 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
02b59d57
TG
137 int r;
138
52433f6b
TG
139 assert(netdev);
140 assert(netdev->state == NETDEV_STATE_READY);
4fb7242c
TG
141 assert(netdev->manager);
142 assert(netdev->manager->rtnl);
02b59d57
TG
143 assert(link);
144 assert(callback);
145
151b9b96
LP
146 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
147 RTM_SETLINK, link->ifindex);
02b59d57 148 if (r < 0) {
52433f6b 149 log_error_netdev(netdev,
3333d748
ZJS
150 "Could not allocate RTM_SETLINK message: %s",
151 strerror(-r));
02b59d57
TG
152 return r;
153 }
154
50add290 155 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
02b59d57 156 if (r < 0) {
52433f6b 157 log_error_netdev(netdev,
3333d748
ZJS
158 "Could not append IFLA_MASTER attribute: %s",
159 strerror(-r));
02b59d57
TG
160 return r;
161 }
162
52433f6b 163 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 164 if (r < 0) {
52433f6b 165 log_error_netdev(netdev,
3333d748
ZJS
166 "Could not send rtnetlink message: %s",
167 strerror(-r));
02b59d57
TG
168 return r;
169 }
170
52433f6b 171 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 172
02b59d57
TG
173 return 0;
174}
175
1a436809 176static int netdev_enter_ready(NetDev *netdev) {
52433f6b 177 netdev_enslave_callback *callback;
02b59d57 178
52433f6b
TG
179 assert(netdev);
180 assert(netdev->name);
924fe430 181
ba5596ec
TG
182 if (netdev->state != NETDEV_STATE_CREATING)
183 return 0;
184
52433f6b 185 netdev->state = NETDEV_STATE_READY;
02b59d57 186
52433f6b 187 log_info_netdev(netdev, "netdev ready");
02b59d57 188
52433f6b 189 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
b226d99b 190 /* enslave the links that were attempted to be enslaved before the
02b59d57 191 * link was ready */
52433f6b 192 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
193 }
194
195 return 0;
196}
52433f6b 197static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
1a436809 198 NetDev *netdev = userdata;
172f6635 199 int r;
02b59d57 200
52433f6b 201 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
202
203 r = sd_rtnl_message_get_errno(m);
e09826dc 204 if (r == -EEXIST)
505f8da7
TG
205 log_debug_netdev(netdev, "netdev exists, using existing");
206 else if (r < 0) {
52433f6b
TG
207 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
208 netdev_enter_failed(netdev);
dd3efc09
TG
209
210 return 1;
02b59d57
TG
211 }
212
dd3efc09 213 return 1;
02b59d57
TG
214}
215
1a436809 216static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
cf6a8911 217 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 218 const char *kind;
02b59d57
TG
219 int r;
220
52433f6b 221 assert(netdev);
fe6b2d55
TG
222 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
223 (link && callback));
52433f6b
TG
224 assert(netdev->name);
225 assert(netdev->manager);
226 assert(netdev->manager->rtnl);
02b59d57 227
151b9b96 228 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
02b59d57 229 if (r < 0) {
52433f6b 230 log_error_netdev(netdev,
3333d748
ZJS
231 "Could not allocate RTM_NEWLINK message: %s",
232 strerror(-r));
02b59d57
TG
233 return r;
234 }
235
54abf461
TG
236 if (link) {
237 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
238 if (r < 0) {
239 log_error_netdev(netdev,
240 "Could not append IFLA_LINK attribute: %s",
241 strerror(-r));
242 return r;
243 }
244 }
245
52433f6b 246 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 247 if (r < 0) {
52433f6b 248 log_error_netdev(netdev,
3333d748
ZJS
249 "Could not append IFLA_IFNAME attribute: %s",
250 strerror(-r));
02b59d57
TG
251 return r;
252 }
253
ee3a6a51 254 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
02b59d57 255 if (r < 0) {
52433f6b 256 log_error_netdev(netdev,
3333d748
ZJS
257 "Could not open IFLA_LINKINFO container: %s",
258 strerror(-r));
02b59d57
TG
259 return r;
260 }
261
52433f6b
TG
262 kind = netdev_kind_to_string(netdev->kind);
263 if (!kind) {
264 log_error_netdev(netdev, "Invalid kind");
265 return -EINVAL;
266 }
267
d8e538ec 268 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
02b59d57 269 if (r < 0) {
52433f6b 270 log_error_netdev(netdev,
d8e538ec
TG
271 "Could not open IFLA_INFO_DATA container: %s",
272 strerror(-r));
02b59d57
TG
273 return r;
274 }
275
d8e538ec
TG
276 if (netdev->vlanid <= VLANID_MAX) {
277 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
54abf461
TG
278 if (r < 0) {
279 log_error_netdev(netdev,
d8e538ec 280 "Could not append IFLA_VLAN_ID attribute: %s",
54abf461
TG
281 strerror(-r));
282 return r;
283 }
d8e538ec 284 }
54abf461 285
d8e538ec
TG
286 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
287 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
288 if (r < 0) {
289 log_error_netdev(netdev,
290 "Could not append IFLA_MACVLAN_MODE attribute: %s",
291 strerror(-r));
54abf461
TG
292 return r;
293 }
294 }
295
d8e538ec
TG
296 r = sd_rtnl_message_close_container(req);
297 if (r < 0) {
298 log_error_netdev(netdev,
299 "Could not close IFLA_INFO_DATA container %s",
300 strerror(-r));
301 return r;
302 }
303
02b59d57
TG
304 r = sd_rtnl_message_close_container(req);
305 if (r < 0) {
52433f6b 306 log_error_netdev(netdev,
3333d748
ZJS
307 "Could not close IFLA_LINKINFO container %s",
308 strerror(-r));
02b59d57
TG
309 return r;
310 }
311
54abf461
TG
312 if (link)
313 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
314 else
315 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 316 if (r < 0) {
52433f6b 317 log_error_netdev(netdev,
3333d748 318 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
319 return r;
320 }
321
52433f6b 322 log_debug_netdev(netdev, "creating netdev");
02b59d57 323
52433f6b 324 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
325
326 return 0;
327}
328
1a436809 329int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
b226d99b
TG
330 int r;
331
fe6b2d55 332 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
54abf461
TG
333 return netdev_create(netdev, link, callback);
334
52433f6b 335 if (netdev->state == NETDEV_STATE_READY) {
b226d99b
TG
336 r = netdev_enslave_ready(netdev, link, callback);
337 if (r < 0)
338 return r;
02b59d57 339 } else {
52433f6b
TG
340 /* the netdev is not yet read, save this request for when it is*/
341 netdev_enslave_callback *cb;
02b59d57 342
52433f6b 343 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
344 if (!cb)
345 return log_oom();
346
347 cb->callback = callback;
348 cb->link = link;
349
52433f6b 350 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
351 }
352
353 return 0;
354}
355
d39edfc7 356int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
c3ebdce3 357 uint16_t type;
d39edfc7
TG
358 const char *kind;
359 char *received_kind;
c6315a7a 360 char *received_name;
d39edfc7
TG
361 int r, ifindex;
362
50add290 363 assert(netdev);
c3ebdce3 364 assert(message);
02b59d57 365
c3ebdce3 366 r = sd_rtnl_message_get_type(message, &type);
ba5596ec
TG
367 if (r < 0) {
368 log_error_netdev(netdev, "Could not get rtnl message type");
c3ebdce3 369 return r;
ba5596ec 370 }
c3ebdce3 371
ba5596ec
TG
372 if (type != RTM_NEWLINK) {
373 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
c3ebdce3 374 return -EINVAL;
ba5596ec 375 }
d39edfc7 376
a21df104
TG
377 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
378 if (r < 0) {
379 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
380 netdev_enter_failed(netdev);
381 return r;
382 } else if (ifindex <= 0) {
383 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
384 netdev_enter_failed(netdev);
385 return r;
386 }
387
388
389 if (netdev->ifindex > 0) {
390 if (netdev->ifindex != ifindex) {
391 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
392 ifindex, netdev->ifindex);
393 netdev_enter_failed(netdev);
394 return -EEXIST;
395 } else
396 /* ifindex already set to the same for this netdev */
397 return 0;
398 }
399
c6315a7a
TG
400 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
401 if (r < 0) {
402 log_error_netdev(netdev, "Could not get IFNAME");
403 return r;
404 }
405
406 if (!streq(netdev->name, received_name)) {
407 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
408 received_name);
409 netdev_enter_failed(netdev);
410 return r;
411 }
412
d39edfc7
TG
413 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
414 if (r < 0) {
415 log_error_netdev(netdev, "Could not get LINKINFO");
416 return r;
417 }
418
419 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
420 if (r < 0) {
421 log_error_netdev(netdev, "Could not get KIND");
422 return r;
423 }
424
505f8da7
TG
425 r = sd_rtnl_message_exit_container(message);
426 if (r < 0) {
427 log_error_netdev(netdev, "Could not exit container");
428 return r;
429 }
430
c3ebdce3
TG
431 kind = netdev_kind_to_string(netdev->kind);
432 if (!kind) {
433 log_error_netdev(netdev, "Could not get kind");
434 netdev_enter_failed(netdev);
435 return -EINVAL;
436 }
437
d39edfc7 438 if (!streq(kind, received_kind)) {
c6315a7a
TG
439 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
440 "expected %s", received_kind, kind);
d39edfc7
TG
441 netdev_enter_failed(netdev);
442 return r;
443 }
444
50add290 445 netdev->ifindex = ifindex;
52433f6b
TG
446
447 netdev_enter_ready(netdev);
02b59d57
TG
448
449 return 0;
450}
451
52433f6b 452static int netdev_load_one(Manager *manager, const char *filename) {
14b746f7 453 _cleanup_netdev_unref_ NetDev *netdev = NULL;
02b59d57
TG
454 _cleanup_fclose_ FILE *file = NULL;
455 int r;
456
bf1bc670
TA
457 assert(manager);
458 assert(filename);
459
6916ec29
TG
460 if (null_or_empty_path(filename)) {
461 log_debug("skipping empty file: %s", filename);
462 return 0;
463 }
464
02b59d57
TG
465 file = fopen(filename, "re");
466 if (!file) {
467 if (errno == ENOENT)
468 return 0;
469 else
ecb08ec6 470 return -errno;
02b59d57
TG
471 }
472
1a436809 473 netdev = new0(NetDev, 1);
52433f6b 474 if (!netdev)
02b59d57
TG
475 return log_oom();
476
14b746f7 477 netdev->n_ref = 1;
52433f6b
TG
478 netdev->manager = manager;
479 netdev->state = _NETDEV_STATE_INVALID;
480 netdev->kind = _NETDEV_KIND_INVALID;
fe6b2d55 481 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
672682a6 482 netdev->vlanid = VLANID_MAX + 1;
02b59d57 483
fe6b2d55
TG
484 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
485 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
486 false, false, netdev);
02b59d57
TG
487 if (r < 0) {
488 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
489 return r;
449f7554 490 }
02b59d57 491
52433f6b 492 if (netdev->kind == _NETDEV_KIND_INVALID) {
1a436809 493 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
52433f6b
TG
494 return 0;
495 }
496
497 if (!netdev->name) {
1a436809 498 log_warning("NetDev without Name configured in %s. Ignoring", filename);
02b59d57
TG
499 return 0;
500 }
501
672682a6
TG
502 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
503 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
54abf461
TG
504 return 0;
505 }
506
fe6b2d55
TG
507 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
508 log_warning("VLAN Id configured for a %s in %s. Ignoring",
509 netdev_kind_to_string(netdev->kind), filename);
510 return 0;
511 }
512
513 if (netdev->kind != NETDEV_KIND_MACVLAN &&
514 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
515 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
516 netdev_kind_to_string(netdev->kind), filename);
517 return 0;
518 }
519
52433f6b
TG
520 netdev->filename = strdup(filename);
521 if (!netdev->filename)
02b59d57
TG
522 return log_oom();
523
c0dda186 524 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
edbb03e9
TG
525 netdev->match_host, netdev->match_virt,
526 netdev->match_kernel, netdev->match_arch,
bf175aaf 527 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
c0dda186
TG
528 return 0;
529
52433f6b 530 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
531 if (r < 0)
532 return r;
533
52433f6b 534 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 535
fe6b2d55
TG
536 if (netdev->kind != NETDEV_KIND_VLAN &&
537 netdev->kind != NETDEV_KIND_MACVLAN) {
54abf461
TG
538 r = netdev_create(netdev, NULL, NULL);
539 if (r < 0)
540 return r;
541 }
02b59d57 542
69ceb044
TG
543 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
544
52433f6b 545 netdev = NULL;
02b59d57
TG
546
547 return 0;
548}
549
52433f6b 550int netdev_load(Manager *manager) {
1a436809 551 NetDev *netdev;
02b59d57
TG
552 char **files, **f;
553 int r;
554
555 assert(manager);
556
52433f6b 557 while ((netdev = hashmap_first(manager->netdevs)))
14b746f7 558 netdev_unref(netdev);
02b59d57 559
2ad8416d 560 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
561 if (r < 0) {
562 log_error("Failed to enumerate netdev files: %s", strerror(-r));
563 return r;
564 }
565
566 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 567 r = netdev_load_one(manager, *f);
02b59d57
TG
568 if (r < 0)
569 return r;
570 }
571
572 strv_free(files);
573
574 return 0;
575}