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 if (netdev->state != NETDEV_STATE_CREATING)
152 netdev->state = NETDEV_STATE_READY;
154 log_info_netdev(netdev, "netdev ready");
156 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
157 /* enslave the links that were attempted to be enslaved befor the
159 netdev_enslave_ready(netdev, callback->link, callback->callback);
164 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
165 NetDev *netdev = userdata;
168 assert(netdev->state != _NETDEV_STATE_INVALID);
170 r = sd_rtnl_message_get_errno(m);
172 log_debug_netdev(netdev, "netdev exists, using existing");
174 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
175 netdev_enter_failed(netdev);
183 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
184 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
189 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
191 assert(netdev->name);
192 assert(netdev->manager);
193 assert(netdev->manager->rtnl);
195 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
197 log_error_netdev(netdev,
198 "Could not allocate RTM_NEWLINK message: %s",
204 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
206 log_error_netdev(netdev,
207 "Could not append IFLA_LINK attribute: %s",
213 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
215 log_error_netdev(netdev,
216 "Could not append IFLA_IFNAME attribute: %s",
221 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
223 log_error_netdev(netdev,
224 "Could not open IFLA_LINKINFO container: %s",
229 kind = netdev_kind_to_string(netdev->kind);
231 log_error_netdev(netdev, "Invalid kind");
235 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
237 log_error_netdev(netdev,
238 "Could not open IFLA_INFO_DATA container: %s",
243 if (netdev->vlanid <= VLANID_MAX) {
244 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
246 log_error_netdev(netdev,
247 "Could not append IFLA_VLAN_ID attribute: %s",
253 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
254 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
256 log_error_netdev(netdev,
257 "Could not append IFLA_MACVLAN_MODE attribute: %s",
263 r = sd_rtnl_message_close_container(req);
265 log_error_netdev(netdev,
266 "Could not close IFLA_INFO_DATA container %s",
271 r = sd_rtnl_message_close_container(req);
273 log_error_netdev(netdev,
274 "Could not close IFLA_LINKINFO container %s",
280 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
282 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
284 log_error_netdev(netdev,
285 "Could not send rtnetlink message: %s", strerror(-r));
289 log_debug_netdev(netdev, "creating netdev");
291 netdev->state = NETDEV_STATE_CREATING;
296 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
297 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
298 return netdev_create(netdev, link, callback);
300 if (netdev->state == NETDEV_STATE_READY) {
301 netdev_enslave_ready(netdev, link, callback);
303 /* the netdev is not yet read, save this request for when it is*/
304 netdev_enslave_callback *cb;
306 cb = new0(netdev_enslave_callback, 1);
310 cb->callback = callback;
313 LIST_PREPEND(callbacks, netdev->callbacks, cb);
319 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
329 r = sd_rtnl_message_get_type(message, &type);
331 log_error_netdev(netdev, "Could not get rtnl message type");
335 if (type != RTM_NEWLINK) {
336 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
340 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
342 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
343 netdev_enter_failed(netdev);
345 } else if (ifindex <= 0) {
346 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
347 netdev_enter_failed(netdev);
352 if (netdev->ifindex > 0) {
353 if (netdev->ifindex != ifindex) {
354 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
355 ifindex, netdev->ifindex);
356 netdev_enter_failed(netdev);
359 /* ifindex already set to the same for this netdev */
363 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
365 log_error_netdev(netdev, "Could not get IFNAME");
369 if (!streq(netdev->name, received_name)) {
370 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
372 netdev_enter_failed(netdev);
376 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
378 log_error_netdev(netdev, "Could not get LINKINFO");
382 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
384 log_error_netdev(netdev, "Could not get KIND");
388 r = sd_rtnl_message_exit_container(message);
390 log_error_netdev(netdev, "Could not exit container");
394 kind = netdev_kind_to_string(netdev->kind);
396 log_error_netdev(netdev, "Could not get kind");
397 netdev_enter_failed(netdev);
401 if (!streq(kind, received_kind)) {
402 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
403 "expected %s", received_kind, kind);
404 netdev_enter_failed(netdev);
408 netdev->ifindex = ifindex;
410 netdev_enter_ready(netdev);
415 static int netdev_load_one(Manager *manager, const char *filename) {
416 _cleanup_netdev_free_ NetDev *netdev = NULL;
417 _cleanup_fclose_ FILE *file = NULL;
423 if (null_or_empty_path(filename)) {
424 log_debug("skipping empty file: %s", filename);
428 file = fopen(filename, "re");
436 netdev = new0(NetDev, 1);
440 netdev->manager = manager;
441 netdev->state = _NETDEV_STATE_INVALID;
442 netdev->kind = _NETDEV_KIND_INVALID;
443 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
444 netdev->vlanid = VLANID_MAX + 1;
446 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
447 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
448 false, false, netdev);
450 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
454 if (netdev->kind == _NETDEV_KIND_INVALID) {
455 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
460 log_warning("NetDev without Name configured in %s. Ignoring", filename);
464 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
465 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
469 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
470 log_warning("VLAN Id configured for a %s in %s. Ignoring",
471 netdev_kind_to_string(netdev->kind), filename);
475 if (netdev->kind != NETDEV_KIND_MACVLAN &&
476 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
477 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
478 netdev_kind_to_string(netdev->kind), filename);
482 netdev->filename = strdup(filename);
483 if (!netdev->filename)
486 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
487 netdev->match_host, netdev->match_virt,
488 netdev->match_kernel, netdev->match_arch,
489 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
492 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
496 LIST_HEAD_INIT(netdev->callbacks);
498 if (netdev->kind != NETDEV_KIND_VLAN &&
499 netdev->kind != NETDEV_KIND_MACVLAN) {
500 r = netdev_create(netdev, NULL, NULL);
510 int netdev_load(Manager *manager) {
517 while ((netdev = hashmap_first(manager->netdevs)))
520 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
522 log_error("Failed to enumerate netdev files: %s", strerror(-r));
526 STRV_FOREACH_BACKWARDS(f, files) {
527 r = netdev_load_one(manager, *f);