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 static 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 NetDev *netdev_unref(NetDev *netdev) {
79 if (netdev && (-- netdev->n_ref <= 0))
85 NetDev *netdev_ref(NetDev *netdev) {
87 assert_se(++ netdev->n_ref >= 2);
92 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
99 netdev = hashmap_get(manager->netdevs, name);
110 static int netdev_enter_failed(NetDev *netdev) {
111 netdev->state = NETDEV_STATE_FAILED;
116 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
117 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
121 assert(netdev->state == NETDEV_STATE_READY);
122 assert(netdev->manager);
123 assert(netdev->manager->rtnl);
127 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
128 RTM_SETLINK, link->ifindex);
130 log_error_netdev(netdev,
131 "Could not allocate RTM_SETLINK message: %s",
136 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
138 log_error_netdev(netdev,
139 "Could not append IFLA_MASTER attribute: %s",
144 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
146 log_error_netdev(netdev,
147 "Could not send rtnetlink message: %s",
152 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
157 static int netdev_enter_ready(NetDev *netdev) {
158 netdev_enslave_callback *callback;
161 assert(netdev->name);
163 if (netdev->state != NETDEV_STATE_CREATING)
166 netdev->state = NETDEV_STATE_READY;
168 log_info_netdev(netdev, "netdev ready");
170 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
171 /* enslave the links that were attempted to be enslaved befor the
173 netdev_enslave_ready(netdev, callback->link, callback->callback);
178 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
179 NetDev *netdev = userdata;
182 assert(netdev->state != _NETDEV_STATE_INVALID);
184 r = sd_rtnl_message_get_errno(m);
186 log_debug_netdev(netdev, "netdev exists, using existing");
188 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
189 netdev_enter_failed(netdev);
197 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
198 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
203 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
205 assert(netdev->name);
206 assert(netdev->manager);
207 assert(netdev->manager->rtnl);
209 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
211 log_error_netdev(netdev,
212 "Could not allocate RTM_NEWLINK message: %s",
218 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
220 log_error_netdev(netdev,
221 "Could not append IFLA_LINK attribute: %s",
227 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
229 log_error_netdev(netdev,
230 "Could not append IFLA_IFNAME attribute: %s",
235 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
237 log_error_netdev(netdev,
238 "Could not open IFLA_LINKINFO container: %s",
243 kind = netdev_kind_to_string(netdev->kind);
245 log_error_netdev(netdev, "Invalid kind");
249 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
251 log_error_netdev(netdev,
252 "Could not open IFLA_INFO_DATA container: %s",
257 if (netdev->vlanid <= VLANID_MAX) {
258 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
260 log_error_netdev(netdev,
261 "Could not append IFLA_VLAN_ID attribute: %s",
267 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
268 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
270 log_error_netdev(netdev,
271 "Could not append IFLA_MACVLAN_MODE attribute: %s",
277 r = sd_rtnl_message_close_container(req);
279 log_error_netdev(netdev,
280 "Could not close IFLA_INFO_DATA container %s",
285 r = sd_rtnl_message_close_container(req);
287 log_error_netdev(netdev,
288 "Could not close IFLA_LINKINFO container %s",
294 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
296 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
298 log_error_netdev(netdev,
299 "Could not send rtnetlink message: %s", strerror(-r));
303 log_debug_netdev(netdev, "creating netdev");
305 netdev->state = NETDEV_STATE_CREATING;
310 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
311 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
312 return netdev_create(netdev, link, callback);
314 if (netdev->state == NETDEV_STATE_READY) {
315 netdev_enslave_ready(netdev, link, callback);
317 /* the netdev is not yet read, save this request for when it is*/
318 netdev_enslave_callback *cb;
320 cb = new0(netdev_enslave_callback, 1);
324 cb->callback = callback;
327 LIST_PREPEND(callbacks, netdev->callbacks, cb);
333 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
343 r = sd_rtnl_message_get_type(message, &type);
345 log_error_netdev(netdev, "Could not get rtnl message type");
349 if (type != RTM_NEWLINK) {
350 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
354 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
356 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
357 netdev_enter_failed(netdev);
359 } else if (ifindex <= 0) {
360 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
361 netdev_enter_failed(netdev);
366 if (netdev->ifindex > 0) {
367 if (netdev->ifindex != ifindex) {
368 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
369 ifindex, netdev->ifindex);
370 netdev_enter_failed(netdev);
373 /* ifindex already set to the same for this netdev */
377 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
379 log_error_netdev(netdev, "Could not get IFNAME");
383 if (!streq(netdev->name, received_name)) {
384 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
386 netdev_enter_failed(netdev);
390 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
392 log_error_netdev(netdev, "Could not get LINKINFO");
396 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
398 log_error_netdev(netdev, "Could not get KIND");
402 r = sd_rtnl_message_exit_container(message);
404 log_error_netdev(netdev, "Could not exit container");
408 kind = netdev_kind_to_string(netdev->kind);
410 log_error_netdev(netdev, "Could not get kind");
411 netdev_enter_failed(netdev);
415 if (!streq(kind, received_kind)) {
416 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
417 "expected %s", received_kind, kind);
418 netdev_enter_failed(netdev);
422 netdev->ifindex = ifindex;
424 netdev_enter_ready(netdev);
429 static int netdev_load_one(Manager *manager, const char *filename) {
430 _cleanup_netdev_unref_ NetDev *netdev = NULL;
431 _cleanup_fclose_ FILE *file = NULL;
437 if (null_or_empty_path(filename)) {
438 log_debug("skipping empty file: %s", filename);
442 file = fopen(filename, "re");
450 netdev = new0(NetDev, 1);
455 netdev->manager = manager;
456 netdev->state = _NETDEV_STATE_INVALID;
457 netdev->kind = _NETDEV_KIND_INVALID;
458 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
459 netdev->vlanid = VLANID_MAX + 1;
461 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
462 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
463 false, false, netdev);
465 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
469 if (netdev->kind == _NETDEV_KIND_INVALID) {
470 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
475 log_warning("NetDev without Name configured in %s. Ignoring", filename);
479 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
480 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
484 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
485 log_warning("VLAN Id configured for a %s in %s. Ignoring",
486 netdev_kind_to_string(netdev->kind), filename);
490 if (netdev->kind != NETDEV_KIND_MACVLAN &&
491 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
492 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
493 netdev_kind_to_string(netdev->kind), filename);
497 netdev->filename = strdup(filename);
498 if (!netdev->filename)
501 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
502 netdev->match_host, netdev->match_virt,
503 netdev->match_kernel, netdev->match_arch,
504 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
507 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
511 LIST_HEAD_INIT(netdev->callbacks);
513 if (netdev->kind != NETDEV_KIND_VLAN &&
514 netdev->kind != NETDEV_KIND_MACVLAN) {
515 r = netdev_create(netdev, NULL, NULL);
520 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
527 int netdev_load(Manager *manager) {
534 while ((netdev = hashmap_first(manager->netdevs)))
535 netdev_unref(netdev);
537 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
539 log_error("Failed to enumerate netdev files: %s", strerror(-r));
543 STRV_FOREACH_BACKWARDS(f, files) {
544 r = netdev_load_one(manager, *f);