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[] = {
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[] = {
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_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
158 NetDev *netdev = userdata;
161 assert(netdev->state != _NETDEV_STATE_INVALID);
163 r = sd_rtnl_message_get_errno(m);
165 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
166 netdev_enter_failed(netdev);
174 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
175 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
180 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
182 assert(netdev->name);
183 assert(netdev->manager);
184 assert(netdev->manager->rtnl);
186 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
188 log_error_netdev(netdev,
189 "Could not allocate RTM_NEWLINK message: %s",
195 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
197 log_error_netdev(netdev,
198 "Could not append IFLA_LINK attribute: %s",
204 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
206 log_error_netdev(netdev,
207 "Could not append IFLA_IFNAME attribute: %s",
212 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
214 log_error_netdev(netdev,
215 "Could not open IFLA_LINKINFO container: %s",
220 kind = netdev_kind_to_string(netdev->kind);
222 log_error_netdev(netdev, "Invalid kind");
226 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
228 log_error_netdev(netdev,
229 "Could not append IFLA_INFO_KIND attribute: %s",
234 if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
235 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
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",
272 r = sd_rtnl_message_close_container(req);
274 log_error_netdev(netdev,
275 "Could not close IFLA_LINKINFO container %s",
281 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
283 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
285 log_error_netdev(netdev,
286 "Could not send rtnetlink message: %s", strerror(-r));
290 log_debug_netdev(netdev, "creating netdev");
292 netdev->state = NETDEV_STATE_CREATING;
297 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
298 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
299 return netdev_create(netdev, link, callback);
301 if (netdev->state == NETDEV_STATE_READY) {
302 netdev_enslave_ready(netdev, link, callback);
304 /* the netdev is not yet read, save this request for when it is*/
305 netdev_enslave_callback *cb;
307 cb = new0(netdev_enslave_callback, 1);
311 cb->callback = callback;
314 LIST_PREPEND(callbacks, netdev->callbacks, cb);
320 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
324 if (netdev->ifindex > 0) {
325 if (netdev->ifindex == ifindex)
331 netdev->ifindex = ifindex;
333 netdev_enter_ready(netdev);
338 static int netdev_load_one(Manager *manager, const char *filename) {
339 _cleanup_netdev_free_ NetDev *netdev = NULL;
340 _cleanup_fclose_ FILE *file = NULL;
346 file = fopen(filename, "re");
354 netdev = new0(NetDev, 1);
358 netdev->manager = manager;
359 netdev->state = _NETDEV_STATE_INVALID;
360 netdev->kind = _NETDEV_KIND_INVALID;
361 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
362 netdev->vlanid = VLANID_MAX + 1;
364 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
365 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
366 false, false, netdev);
368 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
372 if (netdev->kind == _NETDEV_KIND_INVALID) {
373 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
378 log_warning("NetDev without Name configured in %s. Ignoring", filename);
382 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
383 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
387 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
388 log_warning("VLAN Id configured for a %s in %s. Ignoring",
389 netdev_kind_to_string(netdev->kind), filename);
393 if (netdev->kind != NETDEV_KIND_MACVLAN &&
394 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
395 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
396 netdev_kind_to_string(netdev->kind), filename);
400 netdev->filename = strdup(filename);
401 if (!netdev->filename)
404 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
405 netdev->match_host, netdev->match_virt,
406 netdev->match_kernel, netdev->match_arch,
407 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
410 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
414 LIST_HEAD_INIT(netdev->callbacks);
416 if (netdev->kind != NETDEV_KIND_VLAN &&
417 netdev->kind != NETDEV_KIND_MACVLAN) {
418 r = netdev_create(netdev, NULL, NULL);
428 int netdev_load(Manager *manager) {
435 while ((netdev = hashmap_first(manager->netdevs)))
438 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
440 log_error("Failed to enumerate netdev files: %s", strerror(-r));
444 STRV_FOREACH_BACKWARDS(f, files) {
445 r = netdev_load_one(manager, *f);