1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
23 #include "network-internal.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
29 #define VLANID_MAX 4094
31 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
32 [NETDEV_KIND_BRIDGE] = "bridge",
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
35 [NETDEV_KIND_MACVLAN] = "macvlan",
38 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
41 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
42 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
48 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
51 static void netdev_cancel_callbacks(NetDev *netdev) {
52 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
53 netdev_enslave_callback *callback;
58 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
60 while ((callback = netdev->callbacks)) {
62 assert(callback->link);
63 assert(callback->callback);
64 assert(netdev->manager);
65 assert(netdev->manager->rtnl);
67 callback->callback(netdev->manager->rtnl, m, link);
70 LIST_REMOVE(callbacks, netdev->callbacks, callback);
75 static void netdev_free(NetDev *netdev) {
79 netdev_cancel_callbacks(netdev);
82 hashmap_remove(netdev->manager->netdevs, netdev->name);
84 free(netdev->filename);
86 free(netdev->description);
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);
97 NetDev *netdev_unref(NetDev *netdev) {
98 if (netdev && (-- netdev->n_ref <= 0))
104 NetDev *netdev_ref(NetDev *netdev) {
106 assert_se(++ netdev->n_ref >= 2);
111 void netdev_drop(NetDev *netdev) {
112 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
115 netdev->state = NETDEV_STATE_LINGER;
117 netdev_cancel_callbacks(netdev);
119 netdev_unref(netdev);
124 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
131 netdev = hashmap_get(manager->netdevs, name);
142 static int netdev_enter_failed(NetDev *netdev) {
143 netdev->state = NETDEV_STATE_FAILED;
148 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
149 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
153 assert(netdev->state == NETDEV_STATE_READY);
154 assert(netdev->manager);
155 assert(netdev->manager->rtnl);
159 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
160 RTM_SETLINK, link->ifindex);
162 log_error_netdev(netdev,
163 "Could not allocate RTM_SETLINK message: %s",
168 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
170 log_error_netdev(netdev,
171 "Could not append IFLA_MASTER attribute: %s",
176 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
178 log_error_netdev(netdev,
179 "Could not send rtnetlink message: %s",
184 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
189 static int netdev_enter_ready(NetDev *netdev) {
190 netdev_enslave_callback *callback;
193 assert(netdev->name);
195 if (netdev->state != NETDEV_STATE_CREATING)
198 netdev->state = NETDEV_STATE_READY;
200 log_info_netdev(netdev, "netdev ready");
202 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
203 /* enslave the links that were attempted to be enslaved before the
205 netdev_enslave_ready(netdev, callback->link, callback->callback);
210 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
211 NetDev *netdev = userdata;
214 assert(netdev->state != _NETDEV_STATE_INVALID);
216 r = sd_rtnl_message_get_errno(m);
218 log_debug_netdev(netdev, "netdev exists, using existing");
220 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
221 netdev_enter_failed(netdev);
229 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
230 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
235 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
237 assert(netdev->name);
238 assert(netdev->manager);
239 assert(netdev->manager->rtnl);
241 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
243 log_error_netdev(netdev,
244 "Could not allocate RTM_NEWLINK message: %s",
250 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
252 log_error_netdev(netdev,
253 "Could not append IFLA_LINK attribute: %s",
259 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
261 log_error_netdev(netdev,
262 "Could not append IFLA_IFNAME attribute: %s",
267 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
269 log_error_netdev(netdev,
270 "Could not open IFLA_LINKINFO container: %s",
275 kind = netdev_kind_to_string(netdev->kind);
277 log_error_netdev(netdev, "Invalid kind");
281 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
283 log_error_netdev(netdev,
284 "Could not open IFLA_INFO_DATA container: %s",
289 if (netdev->vlanid <= VLANID_MAX) {
290 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
292 log_error_netdev(netdev,
293 "Could not append IFLA_VLAN_ID attribute: %s",
299 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
300 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
302 log_error_netdev(netdev,
303 "Could not append IFLA_MACVLAN_MODE attribute: %s",
309 r = sd_rtnl_message_close_container(req);
311 log_error_netdev(netdev,
312 "Could not close IFLA_INFO_DATA container %s",
317 r = sd_rtnl_message_close_container(req);
319 log_error_netdev(netdev,
320 "Could not close IFLA_LINKINFO container %s",
326 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
328 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
330 log_error_netdev(netdev,
331 "Could not send rtnetlink message: %s", strerror(-r));
335 log_debug_netdev(netdev, "creating netdev");
337 netdev->state = NETDEV_STATE_CREATING;
342 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
345 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
346 return netdev_create(netdev, link, callback);
348 if (netdev->state == NETDEV_STATE_READY) {
349 r = netdev_enslave_ready(netdev, link, callback);
353 /* the netdev is not yet read, save this request for when it is*/
354 netdev_enslave_callback *cb;
356 cb = new0(netdev_enslave_callback, 1);
360 cb->callback = callback;
363 LIST_PREPEND(callbacks, netdev->callbacks, cb);
369 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
379 r = sd_rtnl_message_get_type(message, &type);
381 log_error_netdev(netdev, "Could not get rtnl message type");
385 if (type != RTM_NEWLINK) {
386 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
390 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
392 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
393 netdev_enter_failed(netdev);
395 } else if (ifindex <= 0) {
396 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
397 netdev_enter_failed(netdev);
402 if (netdev->ifindex > 0) {
403 if (netdev->ifindex != ifindex) {
404 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
405 ifindex, netdev->ifindex);
406 netdev_enter_failed(netdev);
409 /* ifindex already set to the same for this netdev */
413 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
415 log_error_netdev(netdev, "Could not get IFNAME");
419 if (!streq(netdev->name, received_name)) {
420 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
422 netdev_enter_failed(netdev);
426 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
428 log_error_netdev(netdev, "Could not get LINKINFO");
432 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
434 log_error_netdev(netdev, "Could not get KIND");
438 r = sd_rtnl_message_exit_container(message);
440 log_error_netdev(netdev, "Could not exit container");
444 kind = netdev_kind_to_string(netdev->kind);
446 log_error_netdev(netdev, "Could not get kind");
447 netdev_enter_failed(netdev);
451 if (!streq(kind, received_kind)) {
452 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
453 "expected %s", received_kind, kind);
454 netdev_enter_failed(netdev);
458 netdev->ifindex = ifindex;
460 netdev_enter_ready(netdev);
465 static int netdev_load_one(Manager *manager, const char *filename) {
466 _cleanup_netdev_unref_ NetDev *netdev = NULL;
467 _cleanup_fclose_ FILE *file = NULL;
473 if (null_or_empty_path(filename)) {
474 log_debug("skipping empty file: %s", filename);
478 file = fopen(filename, "re");
486 netdev = new0(NetDev, 1);
491 netdev->manager = manager;
492 netdev->state = _NETDEV_STATE_INVALID;
493 netdev->kind = _NETDEV_KIND_INVALID;
494 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
495 netdev->vlanid = VLANID_MAX + 1;
497 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
498 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
499 false, false, netdev);
501 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
505 if (netdev->kind == _NETDEV_KIND_INVALID) {
506 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
511 log_warning("NetDev without Name configured in %s. Ignoring", filename);
515 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
516 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
520 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
521 log_warning("VLAN Id configured for a %s in %s. Ignoring",
522 netdev_kind_to_string(netdev->kind), filename);
526 if (netdev->kind != NETDEV_KIND_MACVLAN &&
527 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
528 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
529 netdev_kind_to_string(netdev->kind), filename);
533 netdev->filename = strdup(filename);
534 if (!netdev->filename)
537 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
538 netdev->match_host, netdev->match_virt,
539 netdev->match_kernel, netdev->match_arch,
540 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
543 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
547 LIST_HEAD_INIT(netdev->callbacks);
549 if (netdev->kind != NETDEV_KIND_VLAN &&
550 netdev->kind != NETDEV_KIND_MACVLAN) {
551 r = netdev_create(netdev, NULL, NULL);
556 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
563 int netdev_load(Manager *manager) {
570 while ((netdev = hashmap_first(manager->netdevs)))
571 netdev_unref(netdev);
573 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
575 log_error("Failed to enumerate netdev files: %s", strerror(-r));
579 STRV_FOREACH_BACKWARDS(f, files) {
580 r = netdev_load_one(manager, *f);