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 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
118 netdev = hashmap_get(manager->netdevs, name);
129 static int netdev_enter_failed(NetDev *netdev) {
130 netdev->state = NETDEV_STATE_FAILED;
135 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
136 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
140 assert(netdev->state == NETDEV_STATE_READY);
141 assert(netdev->manager);
142 assert(netdev->manager->rtnl);
146 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
147 RTM_SETLINK, link->ifindex);
149 log_error_netdev(netdev,
150 "Could not allocate RTM_SETLINK message: %s",
155 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
157 log_error_netdev(netdev,
158 "Could not append IFLA_MASTER attribute: %s",
163 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
165 log_error_netdev(netdev,
166 "Could not send rtnetlink message: %s",
171 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
176 static int netdev_enter_ready(NetDev *netdev) {
177 netdev_enslave_callback *callback;
180 assert(netdev->name);
182 if (netdev->state != NETDEV_STATE_CREATING)
185 netdev->state = NETDEV_STATE_READY;
187 log_info_netdev(netdev, "netdev ready");
189 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
190 /* enslave the links that were attempted to be enslaved before the
192 netdev_enslave_ready(netdev, callback->link, callback->callback);
197 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
198 NetDev *netdev = userdata;
201 assert(netdev->state != _NETDEV_STATE_INVALID);
203 r = sd_rtnl_message_get_errno(m);
205 log_debug_netdev(netdev, "netdev exists, using existing");
207 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
208 netdev_enter_failed(netdev);
216 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
217 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
222 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
224 assert(netdev->name);
225 assert(netdev->manager);
226 assert(netdev->manager->rtnl);
228 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
230 log_error_netdev(netdev,
231 "Could not allocate RTM_NEWLINK message: %s",
237 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
239 log_error_netdev(netdev,
240 "Could not append IFLA_LINK attribute: %s",
246 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
248 log_error_netdev(netdev,
249 "Could not append IFLA_IFNAME attribute: %s",
254 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
256 log_error_netdev(netdev,
257 "Could not open IFLA_LINKINFO container: %s",
262 kind = netdev_kind_to_string(netdev->kind);
264 log_error_netdev(netdev, "Invalid kind");
268 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
270 log_error_netdev(netdev,
271 "Could not open IFLA_INFO_DATA container: %s",
276 if (netdev->vlanid <= VLANID_MAX) {
277 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
279 log_error_netdev(netdev,
280 "Could not append IFLA_VLAN_ID attribute: %s",
286 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
287 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
289 log_error_netdev(netdev,
290 "Could not append IFLA_MACVLAN_MODE attribute: %s",
296 r = sd_rtnl_message_close_container(req);
298 log_error_netdev(netdev,
299 "Could not close IFLA_INFO_DATA container %s",
304 r = sd_rtnl_message_close_container(req);
306 log_error_netdev(netdev,
307 "Could not close IFLA_LINKINFO container %s",
313 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
315 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
317 log_error_netdev(netdev,
318 "Could not send rtnetlink message: %s", strerror(-r));
322 log_debug_netdev(netdev, "creating netdev");
324 netdev->state = NETDEV_STATE_CREATING;
329 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
332 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
333 return netdev_create(netdev, link, callback);
335 if (netdev->state == NETDEV_STATE_READY) {
336 r = netdev_enslave_ready(netdev, link, callback);
340 /* the netdev is not yet read, save this request for when it is*/
341 netdev_enslave_callback *cb;
343 cb = new0(netdev_enslave_callback, 1);
347 cb->callback = callback;
350 LIST_PREPEND(callbacks, netdev->callbacks, cb);
356 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
366 r = sd_rtnl_message_get_type(message, &type);
368 log_error_netdev(netdev, "Could not get rtnl message type");
372 if (type != RTM_NEWLINK) {
373 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
377 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
379 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
380 netdev_enter_failed(netdev);
382 } else if (ifindex <= 0) {
383 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
384 netdev_enter_failed(netdev);
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);
396 /* ifindex already set to the same for this netdev */
400 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
402 log_error_netdev(netdev, "Could not get IFNAME");
406 if (!streq(netdev->name, received_name)) {
407 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
409 netdev_enter_failed(netdev);
413 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
415 log_error_netdev(netdev, "Could not get LINKINFO");
419 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
421 log_error_netdev(netdev, "Could not get KIND");
425 r = sd_rtnl_message_exit_container(message);
427 log_error_netdev(netdev, "Could not exit container");
431 kind = netdev_kind_to_string(netdev->kind);
433 log_error_netdev(netdev, "Could not get kind");
434 netdev_enter_failed(netdev);
438 if (!streq(kind, received_kind)) {
439 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
440 "expected %s", received_kind, kind);
441 netdev_enter_failed(netdev);
445 netdev->ifindex = ifindex;
447 netdev_enter_ready(netdev);
452 static int netdev_load_one(Manager *manager, const char *filename) {
453 _cleanup_netdev_unref_ NetDev *netdev = NULL;
454 _cleanup_fclose_ FILE *file = NULL;
460 if (null_or_empty_path(filename)) {
461 log_debug("skipping empty file: %s", filename);
465 file = fopen(filename, "re");
473 netdev = new0(NetDev, 1);
478 netdev->manager = manager;
479 netdev->state = _NETDEV_STATE_INVALID;
480 netdev->kind = _NETDEV_KIND_INVALID;
481 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
482 netdev->vlanid = VLANID_MAX + 1;
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);
488 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
492 if (netdev->kind == _NETDEV_KIND_INVALID) {
493 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
498 log_warning("NetDev without Name configured in %s. Ignoring", filename);
502 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
503 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
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);
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);
520 netdev->filename = strdup(filename);
521 if (!netdev->filename)
524 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
525 netdev->match_host, netdev->match_virt,
526 netdev->match_kernel, netdev->match_arch,
527 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
530 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
534 LIST_HEAD_INIT(netdev->callbacks);
536 if (netdev->kind != NETDEV_KIND_VLAN &&
537 netdev->kind != NETDEV_KIND_MACVLAN) {
538 r = netdev_create(netdev, NULL, NULL);
543 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
550 int netdev_load(Manager *manager) {
557 while ((netdev = hashmap_first(manager->netdevs)))
558 netdev_unref(netdev);
560 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
562 log_error("Failed to enumerate netdev files: %s", strerror(-r));
566 STRV_FOREACH_BACKWARDS(f, files) {
567 r = netdev_load_one(manager, *f);