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/>.
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);
73 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
80 netdev = hashmap_get(manager->netdevs, name);
91 static int netdev_enter_failed(NetDev *netdev) {
92 netdev->state = NETDEV_STATE_FAILED;
97 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
98 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
102 assert(netdev->state == NETDEV_STATE_READY);
103 assert(netdev->manager);
104 assert(netdev->manager->rtnl);
108 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
109 RTM_SETLINK, link->ifindex);
111 log_error_netdev(netdev,
112 "Could not allocate RTM_SETLINK message: %s",
117 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
119 log_error_netdev(netdev,
120 "Could not append IFLA_MASTER attribute: %s",
125 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
127 log_error_netdev(netdev,
128 "Could not send rtnetlink message: %s",
133 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
138 static int netdev_enter_ready(NetDev *netdev) {
139 netdev_enslave_callback *callback;
142 assert(netdev->name);
144 netdev->state = NETDEV_STATE_READY;
146 log_info_netdev(netdev, "netdev ready");
148 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
149 /* enslave the links that were attempted to be enslaved befor the
151 netdev_enslave_ready(netdev, callback->link, callback->callback);
157 static int netdev_getlink_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
159 NetDev *netdev = userdata;
164 if (netdev->state == NETDEV_STATE_FAILED)
167 r = sd_rtnl_message_get_errno(m);
169 log_struct_netdev(LOG_ERR, netdev,
170 "MESSAGE=%s: could not get link: %s",
171 netdev->name, strerror(-r),
177 r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
179 log_struct_netdev(LOG_ERR, netdev,
180 "MESSAGE=%s: could not get ifindex: %s",
181 netdev->name, strerror(-r),
187 r = netdev_set_ifindex(netdev, ifindex);
189 log_warning_netdev(netdev, "could not set ifindex to %d", ifindex);
194 static int netdev_getlink(NetDev *netdev) {
195 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
198 assert(netdev->manager);
199 assert(netdev->manager->rtnl);
200 assert(netdev->name);
202 log_debug_netdev(netdev, "requesting netdev status");
204 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
207 log_error_netdev(netdev, "Could not allocate RTM_GETLINK message");
211 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
213 log_error_netdev(netdev, "Colud not append ifname to message: %s",
218 r = sd_rtnl_call_async(netdev->manager->rtnl, req, netdev_getlink_handler,
221 log_error_netdev(netdev,
222 "Could not send rtnetlink message: %s", strerror(-r));
229 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
230 NetDev *netdev = userdata;
233 assert(netdev->state != _NETDEV_STATE_INVALID);
235 r = sd_rtnl_message_get_errno(m);
237 r = netdev_getlink(netdev);
240 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
241 netdev_enter_failed(netdev);
249 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
250 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
255 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
257 assert(netdev->name);
258 assert(netdev->manager);
259 assert(netdev->manager->rtnl);
261 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
263 log_error_netdev(netdev,
264 "Could not allocate RTM_NEWLINK message: %s",
270 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
272 log_error_netdev(netdev,
273 "Could not append IFLA_LINK attribute: %s",
279 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
281 log_error_netdev(netdev,
282 "Could not append IFLA_IFNAME attribute: %s",
287 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
289 log_error_netdev(netdev,
290 "Could not open IFLA_LINKINFO container: %s",
295 kind = netdev_kind_to_string(netdev->kind);
297 log_error_netdev(netdev, "Invalid kind");
301 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
303 log_error_netdev(netdev,
304 "Could not append IFLA_INFO_KIND attribute: %s",
309 if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
310 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
312 log_error_netdev(netdev,
313 "Could not open IFLA_INFO_DATA container: %s",
318 if (netdev->vlanid <= VLANID_MAX) {
319 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
321 log_error_netdev(netdev,
322 "Could not append IFLA_VLAN_ID attribute: %s",
328 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
329 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
331 log_error_netdev(netdev,
332 "Could not append IFLA_MACVLAN_MODE attribute: %s",
338 r = sd_rtnl_message_close_container(req);
340 log_error_netdev(netdev,
341 "Could not close IFLA_INFO_DATA container %s",
347 r = sd_rtnl_message_close_container(req);
349 log_error_netdev(netdev,
350 "Could not close IFLA_LINKINFO container %s",
356 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
358 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
360 log_error_netdev(netdev,
361 "Could not send rtnetlink message: %s", strerror(-r));
365 log_debug_netdev(netdev, "creating netdev");
367 netdev->state = NETDEV_STATE_CREATING;
372 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
373 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
374 return netdev_create(netdev, link, callback);
376 if (netdev->state == NETDEV_STATE_READY) {
377 netdev_enslave_ready(netdev, link, callback);
379 /* the netdev is not yet read, save this request for when it is*/
380 netdev_enslave_callback *cb;
382 cb = new0(netdev_enslave_callback, 1);
386 cb->callback = callback;
389 LIST_PREPEND(callbacks, netdev->callbacks, cb);
395 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
399 if (netdev->ifindex > 0) {
400 if (netdev->ifindex == ifindex)
406 netdev->ifindex = ifindex;
408 netdev_enter_ready(netdev);
413 static int netdev_load_one(Manager *manager, const char *filename) {
414 _cleanup_netdev_free_ NetDev *netdev = NULL;
415 _cleanup_fclose_ FILE *file = NULL;
421 file = fopen(filename, "re");
429 netdev = new0(NetDev, 1);
433 netdev->manager = manager;
434 netdev->state = _NETDEV_STATE_INVALID;
435 netdev->kind = _NETDEV_KIND_INVALID;
436 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
437 netdev->vlanid = VLANID_MAX + 1;
439 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
440 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
441 false, false, netdev);
443 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
447 if (netdev->kind == _NETDEV_KIND_INVALID) {
448 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
453 log_warning("NetDev without Name configured in %s. Ignoring", filename);
457 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
458 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
462 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
463 log_warning("VLAN Id configured for a %s in %s. Ignoring",
464 netdev_kind_to_string(netdev->kind), filename);
468 if (netdev->kind != NETDEV_KIND_MACVLAN &&
469 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
470 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
471 netdev_kind_to_string(netdev->kind), filename);
475 netdev->filename = strdup(filename);
476 if (!netdev->filename)
479 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
480 netdev->match_host, netdev->match_virt,
481 netdev->match_kernel, netdev->match_arch,
482 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
485 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
489 LIST_HEAD_INIT(netdev->callbacks);
491 if (netdev->kind != NETDEV_KIND_VLAN &&
492 netdev->kind != NETDEV_KIND_MACVLAN) {
493 r = netdev_create(netdev, NULL, NULL);
503 int netdev_load(Manager *manager) {
510 while ((netdev = hashmap_first(manager->netdevs)))
513 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
515 log_error("Failed to enumerate netdev files: %s", strerror(-r));
519 STRV_FOREACH_BACKWARDS(f, files) {
520 r = netdev_load_one(manager, *f);