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) {
328 r = sd_rtnl_message_get_type(message, &type);
330 log_error_netdev(netdev, "Could not get rtnl message type");
334 if (type != RTM_NEWLINK) {
335 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
339 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
341 log_error_netdev(netdev, "Could not get LINKINFO");
345 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
347 log_error_netdev(netdev, "Could not get KIND");
351 r = sd_rtnl_message_exit_container(message);
353 log_error_netdev(netdev, "Could not exit container");
357 kind = netdev_kind_to_string(netdev->kind);
359 log_error_netdev(netdev, "Could not get kind");
360 netdev_enter_failed(netdev);
364 if (!streq(kind, received_kind)) {
365 log_error_netdev(netdev, "Received newlink with wrong KIND");
366 netdev_enter_failed(netdev);
370 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
372 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
373 netdev_enter_failed(netdev);
375 } else if (ifindex <= 0) {
376 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
377 netdev_enter_failed(netdev);
381 if (netdev->ifindex > 0 && netdev->ifindex != ifindex) {
382 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
383 ifindex, netdev->ifindex);
384 netdev_enter_failed(netdev);
388 netdev->ifindex = ifindex;
390 netdev_enter_ready(netdev);
395 static int netdev_load_one(Manager *manager, const char *filename) {
396 _cleanup_netdev_free_ NetDev *netdev = NULL;
397 _cleanup_fclose_ FILE *file = NULL;
403 file = fopen(filename, "re");
411 netdev = new0(NetDev, 1);
415 netdev->manager = manager;
416 netdev->state = _NETDEV_STATE_INVALID;
417 netdev->kind = _NETDEV_KIND_INVALID;
418 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
419 netdev->vlanid = VLANID_MAX + 1;
421 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
422 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
423 false, false, netdev);
425 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
429 if (netdev->kind == _NETDEV_KIND_INVALID) {
430 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
435 log_warning("NetDev without Name configured in %s. Ignoring", filename);
439 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
440 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
444 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
445 log_warning("VLAN Id configured for a %s in %s. Ignoring",
446 netdev_kind_to_string(netdev->kind), filename);
450 if (netdev->kind != NETDEV_KIND_MACVLAN &&
451 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
452 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
453 netdev_kind_to_string(netdev->kind), filename);
457 netdev->filename = strdup(filename);
458 if (!netdev->filename)
461 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
462 netdev->match_host, netdev->match_virt,
463 netdev->match_kernel, netdev->match_arch,
464 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
467 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
471 LIST_HEAD_INIT(netdev->callbacks);
473 if (netdev->kind != NETDEV_KIND_VLAN &&
474 netdev->kind != NETDEV_KIND_MACVLAN) {
475 r = netdev_create(netdev, NULL, NULL);
485 int netdev_load(Manager *manager) {
492 while ((netdev = hashmap_first(manager->netdevs)))
495 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
497 log_error("Failed to enumerate netdev files: %s", strerror(-r));
501 STRV_FOREACH_BACKWARDS(f, files) {
502 r = netdev_load_one(manager, *f);