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 void netdev_free(NetDev *netdev) {
52 netdev_enslave_callback *callback;
57 while ((callback = netdev->callbacks)) {
58 LIST_REMOVE(callbacks, netdev->callbacks, callback);
63 hashmap_remove(netdev->manager->netdevs, netdev->name);
65 free(netdev->filename);
67 free(netdev->description);
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);
78 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
85 netdev = hashmap_get(manager->netdevs, name);
96 static int netdev_enter_failed(NetDev *netdev) {
97 netdev->state = NETDEV_STATE_FAILED;
102 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
103 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
107 assert(netdev->state == NETDEV_STATE_READY);
108 assert(netdev->manager);
109 assert(netdev->manager->rtnl);
113 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
114 RTM_SETLINK, link->ifindex);
116 log_error_netdev(netdev,
117 "Could not allocate RTM_SETLINK message: %s",
122 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
124 log_error_netdev(netdev,
125 "Could not append IFLA_MASTER attribute: %s",
130 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
132 log_error_netdev(netdev,
133 "Could not send rtnetlink message: %s",
138 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
143 static int netdev_enter_ready(NetDev *netdev) {
144 netdev_enslave_callback *callback;
147 assert(netdev->name);
149 netdev->state = NETDEV_STATE_READY;
151 log_info_netdev(netdev, "netdev ready");
153 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
154 /* enslave the links that were attempted to be enslaved befor the
156 netdev_enslave_ready(netdev, callback->link, callback->callback);
162 static int netdev_getlink_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
164 NetDev *netdev = userdata;
169 if (netdev->state == NETDEV_STATE_FAILED)
172 r = sd_rtnl_message_get_errno(m);
174 log_struct_netdev(LOG_ERR, netdev,
175 "MESSAGE=%s: could not get link: %s",
176 netdev->name, strerror(-r),
182 netdev_set_ifindex(netdev, m);
187 static int netdev_getlink(NetDev *netdev) {
188 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
191 assert(netdev->manager);
192 assert(netdev->manager->rtnl);
193 assert(netdev->name);
195 log_debug_netdev(netdev, "requesting netdev status");
197 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
200 log_error_netdev(netdev, "Could not allocate RTM_GETLINK message");
204 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
206 log_error_netdev(netdev, "Colud not append ifname to message: %s",
211 r = sd_rtnl_call_async(netdev->manager->rtnl, req, netdev_getlink_handler,
214 log_error_netdev(netdev,
215 "Could not send rtnetlink message: %s", strerror(-r));
222 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
223 NetDev *netdev = userdata;
226 assert(netdev->state != _NETDEV_STATE_INVALID);
228 r = sd_rtnl_message_get_errno(m);
230 r = netdev_getlink(netdev);
233 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
234 netdev_enter_failed(netdev);
242 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
243 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
248 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
250 assert(netdev->name);
251 assert(netdev->manager);
252 assert(netdev->manager->rtnl);
254 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
256 log_error_netdev(netdev,
257 "Could not allocate RTM_NEWLINK message: %s",
263 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
265 log_error_netdev(netdev,
266 "Could not append IFLA_LINK attribute: %s",
272 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
274 log_error_netdev(netdev,
275 "Could not append IFLA_IFNAME attribute: %s",
280 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
282 log_error_netdev(netdev,
283 "Could not open IFLA_LINKINFO container: %s",
288 kind = netdev_kind_to_string(netdev->kind);
290 log_error_netdev(netdev, "Invalid kind");
294 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
296 log_error_netdev(netdev,
297 "Could not append IFLA_INFO_KIND attribute: %s",
302 if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
303 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
305 log_error_netdev(netdev,
306 "Could not open IFLA_INFO_DATA container: %s",
311 if (netdev->vlanid <= VLANID_MAX) {
312 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
314 log_error_netdev(netdev,
315 "Could not append IFLA_VLAN_ID attribute: %s",
321 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
322 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
324 log_error_netdev(netdev,
325 "Could not append IFLA_MACVLAN_MODE attribute: %s",
331 r = sd_rtnl_message_close_container(req);
333 log_error_netdev(netdev,
334 "Could not close IFLA_INFO_DATA container %s",
340 r = sd_rtnl_message_close_container(req);
342 log_error_netdev(netdev,
343 "Could not close IFLA_LINKINFO container %s",
349 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
351 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
353 log_error_netdev(netdev,
354 "Could not send rtnetlink message: %s", strerror(-r));
358 log_debug_netdev(netdev, "creating netdev");
360 netdev->state = NETDEV_STATE_CREATING;
365 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
366 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
367 return netdev_create(netdev, link, callback);
369 if (netdev->state == NETDEV_STATE_READY) {
370 netdev_enslave_ready(netdev, link, callback);
372 /* the netdev is not yet read, save this request for when it is*/
373 netdev_enslave_callback *cb;
375 cb = new0(netdev_enslave_callback, 1);
379 cb->callback = callback;
382 LIST_PREPEND(callbacks, netdev->callbacks, cb);
388 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
397 r = sd_rtnl_message_get_type(message, &type);
401 if (type != RTM_NEWLINK)
404 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
406 log_error_netdev(netdev, "Could not get LINKINFO");
410 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
412 log_error_netdev(netdev, "Could not get KIND");
416 kind = netdev_kind_to_string(netdev->kind);
418 log_error_netdev(netdev, "Could not get kind");
419 netdev_enter_failed(netdev);
423 if (!streq(kind, received_kind)) {
424 log_error_netdev(netdev, "Received newlink with wrong KIND");
425 netdev_enter_failed(netdev);
429 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
431 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
432 netdev_enter_failed(netdev);
434 } else if (ifindex <= 0) {
435 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
436 netdev_enter_failed(netdev);
440 if (netdev->ifindex > 0) {
441 if (netdev->ifindex == ifindex)
447 netdev->ifindex = ifindex;
449 netdev_enter_ready(netdev);
454 static int netdev_load_one(Manager *manager, const char *filename) {
455 _cleanup_netdev_free_ NetDev *netdev = NULL;
456 _cleanup_fclose_ FILE *file = NULL;
462 file = fopen(filename, "re");
470 netdev = new0(NetDev, 1);
474 netdev->manager = manager;
475 netdev->state = _NETDEV_STATE_INVALID;
476 netdev->kind = _NETDEV_KIND_INVALID;
477 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
478 netdev->vlanid = VLANID_MAX + 1;
480 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
481 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
482 false, false, netdev);
484 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
488 if (netdev->kind == _NETDEV_KIND_INVALID) {
489 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
494 log_warning("NetDev without Name configured in %s. Ignoring", filename);
498 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
499 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
503 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
504 log_warning("VLAN Id configured for a %s in %s. Ignoring",
505 netdev_kind_to_string(netdev->kind), filename);
509 if (netdev->kind != NETDEV_KIND_MACVLAN &&
510 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
511 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
512 netdev_kind_to_string(netdev->kind), filename);
516 netdev->filename = strdup(filename);
517 if (!netdev->filename)
520 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
521 netdev->match_host, netdev->match_virt,
522 netdev->match_kernel, netdev->match_arch,
523 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
526 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
530 LIST_HEAD_INIT(netdev->callbacks);
532 if (netdev->kind != NETDEV_KIND_VLAN &&
533 netdev->kind != NETDEV_KIND_MACVLAN) {
534 r = netdev_create(netdev, NULL, NULL);
544 int netdev_load(Manager *manager) {
551 while ((netdev = hashmap_first(manager->netdevs)))
554 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
556 log_error("Failed to enumerate netdev files: %s", strerror(-r));
560 STRV_FOREACH_BACKWARDS(f, files) {
561 r = netdev_load_one(manager, *f);