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