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);
165 static int netdev_getlink_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
167 NetDev *netdev = userdata;
172 if (netdev->state == NETDEV_STATE_FAILED)
175 r = sd_rtnl_message_get_errno(m);
177 log_struct_netdev(LOG_ERR, netdev,
178 "MESSAGE=%s: could not get link: %s",
179 netdev->name, strerror(-r),
185 netdev_set_ifindex(netdev, m);
190 static int netdev_getlink(NetDev *netdev) {
191 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
194 assert(netdev->manager);
195 assert(netdev->manager->rtnl);
196 assert(netdev->name);
198 log_debug_netdev(netdev, "requesting netdev status");
200 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
203 log_error_netdev(netdev, "Could not allocate RTM_GETLINK message");
207 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
209 log_error_netdev(netdev, "Colud not append ifname to message: %s",
214 r = sd_rtnl_call_async(netdev->manager->rtnl, req, netdev_getlink_handler,
217 log_error_netdev(netdev,
218 "Could not send rtnetlink message: %s", strerror(-r));
225 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
226 NetDev *netdev = userdata;
229 assert(netdev->state != _NETDEV_STATE_INVALID);
231 r = sd_rtnl_message_get_errno(m);
233 r = netdev_getlink(netdev);
236 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
237 netdev_enter_failed(netdev);
245 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
246 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
251 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
253 assert(netdev->name);
254 assert(netdev->manager);
255 assert(netdev->manager->rtnl);
257 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
259 log_error_netdev(netdev,
260 "Could not allocate RTM_NEWLINK message: %s",
266 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
268 log_error_netdev(netdev,
269 "Could not append IFLA_LINK attribute: %s",
275 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
277 log_error_netdev(netdev,
278 "Could not append IFLA_IFNAME attribute: %s",
283 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
285 log_error_netdev(netdev,
286 "Could not open IFLA_LINKINFO container: %s",
291 kind = netdev_kind_to_string(netdev->kind);
293 log_error_netdev(netdev, "Invalid kind");
297 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
299 log_error_netdev(netdev,
300 "Could not open IFLA_INFO_DATA container: %s",
305 if (netdev->vlanid <= VLANID_MAX) {
306 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
308 log_error_netdev(netdev,
309 "Could not append IFLA_VLAN_ID attribute: %s",
315 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
316 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
318 log_error_netdev(netdev,
319 "Could not append IFLA_MACVLAN_MODE attribute: %s",
325 r = sd_rtnl_message_close_container(req);
327 log_error_netdev(netdev,
328 "Could not close IFLA_INFO_DATA container %s",
333 r = sd_rtnl_message_close_container(req);
335 log_error_netdev(netdev,
336 "Could not close IFLA_LINKINFO container %s",
342 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
344 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
346 log_error_netdev(netdev,
347 "Could not send rtnetlink message: %s", strerror(-r));
351 log_debug_netdev(netdev, "creating netdev");
353 netdev->state = NETDEV_STATE_CREATING;
358 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
359 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
360 return netdev_create(netdev, link, callback);
362 if (netdev->state == NETDEV_STATE_READY) {
363 netdev_enslave_ready(netdev, link, callback);
365 /* the netdev is not yet read, save this request for when it is*/
366 netdev_enslave_callback *cb;
368 cb = new0(netdev_enslave_callback, 1);
372 cb->callback = callback;
375 LIST_PREPEND(callbacks, netdev->callbacks, cb);
381 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
390 r = sd_rtnl_message_get_type(message, &type);
392 log_error_netdev(netdev, "Could not get rtnl message type");
396 if (type != RTM_NEWLINK) {
397 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
401 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
403 log_error_netdev(netdev, "Could not get LINKINFO");
407 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
409 log_error_netdev(netdev, "Could not get KIND");
413 kind = netdev_kind_to_string(netdev->kind);
415 log_error_netdev(netdev, "Could not get kind");
416 netdev_enter_failed(netdev);
420 if (!streq(kind, received_kind)) {
421 log_error_netdev(netdev, "Received newlink with wrong KIND");
422 netdev_enter_failed(netdev);
426 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
428 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
429 netdev_enter_failed(netdev);
431 } else if (ifindex <= 0) {
432 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
433 netdev_enter_failed(netdev);
437 if (netdev->ifindex > 0 && netdev->ifindex != ifindex) {
438 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
439 ifindex, netdev->ifindex);
440 netdev_enter_failed(netdev);
444 netdev->ifindex = ifindex;
446 netdev_enter_ready(netdev);
451 static int netdev_load_one(Manager *manager, const char *filename) {
452 _cleanup_netdev_free_ NetDev *netdev = NULL;
453 _cleanup_fclose_ FILE *file = NULL;
459 file = fopen(filename, "re");
467 netdev = new0(NetDev, 1);
471 netdev->manager = manager;
472 netdev->state = _NETDEV_STATE_INVALID;
473 netdev->kind = _NETDEV_KIND_INVALID;
474 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
475 netdev->vlanid = VLANID_MAX + 1;
477 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
478 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
479 false, false, netdev);
481 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
485 if (netdev->kind == _NETDEV_KIND_INVALID) {
486 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
491 log_warning("NetDev without Name configured in %s. Ignoring", filename);
495 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
496 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
500 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
501 log_warning("VLAN Id configured for a %s in %s. Ignoring",
502 netdev_kind_to_string(netdev->kind), filename);
506 if (netdev->kind != NETDEV_KIND_MACVLAN &&
507 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
508 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
509 netdev_kind_to_string(netdev->kind), filename);
513 netdev->filename = strdup(filename);
514 if (!netdev->filename)
517 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
518 netdev->match_host, netdev->match_virt,
519 netdev->match_kernel, netdev->match_arch,
520 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
523 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
527 LIST_HEAD_INIT(netdev->callbacks);
529 if (netdev->kind != NETDEV_KIND_VLAN &&
530 netdev->kind != NETDEV_KIND_MACVLAN) {
531 r = netdev_create(netdev, NULL, NULL);
541 int netdev_load(Manager *manager) {
548 while ((netdev = hashmap_first(manager->netdevs)))
551 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
553 log_error("Failed to enumerate netdev files: %s", strerror(-r));
557 STRV_FOREACH_BACKWARDS(f, files) {
558 r = netdev_load_one(manager, *f);