From 52433f6b65eccd1c54606dde999610640f3458ac Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 21 Jan 2014 21:58:08 +0100 Subject: networkd: add basic bonding support Refactor bridging support to be generic netdev support and extend it to cover bonding as well. --- src/network/networkd-bridge.c | 330 ------------------------------------ src/network/networkd-gperf.gperf | 6 +- src/network/networkd-link.c | 96 ++++++----- src/network/networkd-manager.c | 14 +- src/network/networkd-netdev.c | 354 +++++++++++++++++++++++++++++++++++++++ src/network/networkd-network.c | 49 +++++- src/network/networkd.h | 87 ++++++---- 7 files changed, 522 insertions(+), 414 deletions(-) delete mode 100644 src/network/networkd-bridge.c create mode 100644 src/network/networkd-netdev.c (limited to 'src') diff --git a/src/network/networkd-bridge.c b/src/network/networkd-bridge.c deleted file mode 100644 index ce48878991..0000000000 --- a/src/network/networkd-bridge.c +++ /dev/null @@ -1,330 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "networkd.h" -#include "net-util.h" -#include "path-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "list.h" - -void bridge_free(Bridge *bridge) { - bridge_join_callback *callback; - - if (!bridge) - return; - - while ((callback = bridge->callbacks)) { - LIST_REMOVE(callbacks, bridge->callbacks, callback); - free(callback); - } - - if (bridge->name) - hashmap_remove(bridge->manager->bridges, bridge->name); - - free(bridge->filename); - - free(bridge->description); - free(bridge->name); - - free(bridge); -} - -int bridge_get(Manager *manager, const char *name, Bridge **ret) { - Bridge *bridge; - - assert(manager); - assert(name); - assert(ret); - - bridge = hashmap_get(manager->bridges, name); - if (!bridge) { - *ret = NULL; - return -ENOENT; - } - - *ret = bridge; - - return 0; -} - -static int bridge_enter_failed(Bridge *bridge) { - bridge->state = BRIDGE_STATE_FAILED; - - return 0; -} - -static int bridge_join_ready(Bridge *bridge, Link* link, sd_rtnl_message_handler_t callback) { - _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; - int r; - - assert(bridge); - assert(bridge->state == BRIDGE_STATE_READY); - assert(link); - assert(callback); - - r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req); - if (r < 0) { - log_error_bridge(bridge, - "Could not allocate RTM_SETLINK message: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_message_append_u32(req, IFLA_MASTER, bridge->link->ifindex); - if (r < 0) { - log_error_bridge(bridge, - "Could not append IFLA_MASTER attribute: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_call_async(bridge->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error_bridge(bridge, - "Could not send rtnetlink message: %s", - strerror(-r)); - return r; - } - - log_debug_bridge(bridge, "joining link %s to bridge", link->ifname); - - return 0; -} - -static int bridge_enter_ready(Bridge *bridge) { - bridge_join_callback *callback; - - assert(bridge); - assert(bridge->name); - - bridge->state = BRIDGE_STATE_READY; - - log_info_bridge(bridge, "bridge ready"); - - LIST_FOREACH(callbacks, callback, bridge->callbacks) { - /* join the links that were attempted to be joined befor the - * link was ready */ - bridge_join_ready(bridge, callback->link, callback->callback); - } - - return 0; -} - -static int bridge_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Bridge *bridge = userdata; - int r; - - assert(bridge->state != _BRIDGE_STATE_INVALID); - - r = sd_rtnl_message_get_errno(m); - if (r < 0) { - log_warning_bridge(bridge, "bridge failed: %s", strerror(-r)); - bridge_enter_failed(bridge); - - return 1; - } - - return 1; -} - -static int bridge_create(Bridge *bridge) { - _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; - int r; - - assert(bridge); - assert(bridge->state == _BRIDGE_STATE_INVALID); - assert(bridge->name); - assert(bridge->manager); - assert(bridge->manager->rtnl); - - r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req); - if (r < 0) { - log_error_bridge(bridge, - "Could not allocate RTM_NEWLINK message: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_message_append_string(req, IFLA_IFNAME, bridge->name); - if (r < 0) { - log_error_bridge(bridge, - "Could not append IFLA_IFNAME attribute: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_message_open_container(req, IFLA_LINKINFO); - if (r < 0) { - log_error_bridge(bridge, - "Could not open IFLA_LINKINFO container: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, "bridge"); - if (r < 0) { - log_error_bridge(bridge, - "Could not append IFLA_INFO_KIND attribute: %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_message_close_container(req); - if (r < 0) { - log_error_bridge(bridge, - "Could not close IFLA_LINKINFO container %s", - strerror(-r)); - return r; - } - - r = sd_rtnl_call_async(bridge->manager->rtnl, req, &bridge_create_handler, bridge, 0, NULL); - if (r < 0) { - log_error_bridge(bridge, - "Could not send rtnetlink message: %s", strerror(-r)); - return r; - } - - log_debug_bridge(bridge, "creating bridge"); - - bridge->state = BRIDGE_STATE_CREATING; - - return 0; -} - -int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) { - if (bridge->state == BRIDGE_STATE_READY) { - bridge_join_ready(bridge, link, callback); - } else { - /* the bridge is not yet read, save this request for when it is*/ - bridge_join_callback *cb; - - cb = new0(bridge_join_callback, 1); - if (!cb) - return log_oom(); - - cb->callback = callback; - cb->link = link; - - LIST_PREPEND(callbacks, bridge->callbacks, cb); - } - - return 0; -} - -int bridge_set_link(Manager *m, Link *link) { - Bridge *bridge; - int r; - - r = bridge_get(m, link->ifname, &bridge); - if (r < 0) - return r; - - if (bridge->link && bridge->link != link) - return -EEXIST; - - bridge->link = link; - - bridge_enter_ready(bridge); - - return 0; -} - -static int bridge_load_one(Manager *manager, const char *filename) { - _cleanup_bridge_free_ Bridge *bridge = NULL; - _cleanup_fclose_ FILE *file = NULL; - int r; - - assert(manager); - assert(filename); - - file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - else - return errno; - } - - bridge = new0(Bridge, 1); - if (!bridge) - return log_oom(); - - bridge->manager = manager; - bridge->state = _BRIDGE_STATE_INVALID; - - r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup, - (void*) network_gperf_lookup, false, false, bridge); - if (r < 0) { - log_warning("Could not parse config file %s: %s", filename, strerror(-r)); - return r; - } - - if (!bridge->name) { - log_warning("Bridge without Name configured in %s. Ignoring", filename); - return 0; - } - - bridge->filename = strdup(filename); - if (!bridge->filename) - return log_oom(); - - r = hashmap_put(bridge->manager->bridges, bridge->name, bridge); - if (r < 0) - return r; - - LIST_HEAD_INIT(bridge->callbacks); - - r = bridge_create(bridge); - if (r < 0) - return r; - - bridge = NULL; - - return 0; -} - -int bridge_load(Manager *manager) { - Bridge *bridge; - char **files, **f; - int r; - - assert(manager); - - while ((bridge = hashmap_first(manager->bridges))) - bridge_free(bridge); - - r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); - if (r < 0) { - log_error("Failed to enumerate netdev files: %s", strerror(-r)); - return r; - } - - STRV_FOREACH_BACKWARDS(f, files) { - r = bridge_load_one(manager, *f); - if (r < 0) - return r; - } - - strv_free(files); - - return 0; -} diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index abf6a30975..7181cf856a 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -22,6 +22,7 @@ Match.Type, config_parse_string, 0, offsetof(Networ Match.Name, config_parse_ifname, 0, offsetof(Network, match_name) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_bridge, 0, offsetof(Network, bridge) +Network.Bond, config_parse_bond, 0, offsetof(Network, bond) Network.DHCP, config_parse_bool, 0, offsetof(Network, dhcp) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 @@ -34,5 +35,6 @@ DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Networ DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu) DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname) DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domainname) -Bridge.Description, config_parse_string, 0, offsetof(Bridge, description) -Bridge.Name, config_parse_ifname, 0, offsetof(Bridge, name) +Netdev.Description, config_parse_string, 0, offsetof(Netdev, description) +Netdev.Name, config_parse_ifname, 0, offsetof(Netdev, name) +Netdev.Kind, config_parse_netdev_kind, 0, offsetof(Netdev, kind) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2fa77f15ff..f746f2dcc4 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -95,7 +95,7 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) { Network *network; int r; uint64_t ifindex; - const char *devtype; + NetdevKind kind; assert(m); assert(device); @@ -113,9 +113,9 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) { *ret = link; - devtype = udev_device_get_devtype(device); - if (streq_ptr(devtype, "bridge")) { - r = bridge_set_link(m, link); + kind = netdev_kind_from_string(udev_device_get_devtype(device)); + if (kind != _NETDEV_KIND_INVALID) { + r = netdev_set_link(m, kind, link); if (r < 0 && r != -ENOENT) return r; } @@ -729,11 +729,11 @@ static int link_up(Link *link) { return 0; } -static int link_bridge_joined(Link *link) { +static int link_enslaved(Link *link) { int r; assert(link); - assert(link->state == LINK_STATE_JOINING_BRIDGE); + assert(link->state == LINK_STATE_ENSLAVING); assert(link->network); r = link_up(link); @@ -748,67 +748,87 @@ static int link_bridge_joined(Link *link) { return 0; } -static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { +static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { Link *link = userdata; int r; assert(link); - assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED); + assert(link->state == LINK_STATE_ENSLAVING || link->state == LINK_STATE_FAILED); assert(link->network); + link->enslaving --; + if (link->state == LINK_STATE_FAILED) return 1; r = sd_rtnl_message_get_errno(m); if (r < 0) { - log_struct_link(LOG_ERR, link, - "MESSAGE=%s: could not join bridge '%s': %s", - link->ifname, link->network->bridge->name, strerror(-r), - BRIDGE(link->network->bridge), - NULL); + log_error_link(link, "could not enslave: %s", + strerror(-r)); link_enter_failed(link); return 1; } - log_struct_link(LOG_DEBUG, link, - "MESSAGE=%s: joined bridge '%s'", - link->network->bridge->name, - BRIDGE(link->network->bridge), - NULL); + log_debug_link(link, "enslaved"); - link_bridge_joined(link); + if (link->enslaving == 0) + link_enslaved(link); return 1; } -static int link_enter_join_bridge(Link *link) { +static int link_enter_enslave(Link *link) { int r; assert(link); assert(link->network); assert(link->state == _LINK_STATE_INVALID); - link->state = LINK_STATE_JOINING_BRIDGE; + link->state = LINK_STATE_ENSLAVING; - if (!link->network->bridge) - return link_bridge_joined(link); + if (!link->network->bridge && !link->network->bond) + return link_enslaved(link); - log_struct_link(LOG_DEBUG, link, - "MESSAGE=%s: joining bridge '%s'", - link->network->bridge->name, - BRIDGE(link->network->bridge), - NULL); - log_debug_link(link, "joining bridge"); + if (link->network->bridge) { + log_struct_link(LOG_DEBUG, link, + "MESSAGE=%s: enslaving by '%s'", + link->network->bridge->name, + NETDEV(link->network->bridge), + NULL); - r = bridge_join(link->network->bridge, link, &bridge_handler); - if (r < 0) { - log_struct_link(LOG_WARNING, link, - "MESSAGE=%s: could not join bridge '%s': %s", - link->network->bridge->name, strerror(-r), - BRIDGE(link->network->bridge), + r = netdev_enslave(link->network->bridge, link, &enslave_handler); + if (r < 0) { + log_struct_link(LOG_WARNING, link, + "MESSAGE=%s: could not enslave by '%s': %s", + link->network->bridge->name, strerror(-r), + NETDEV(link->network->bridge), + NULL); + link_enter_failed(link); + return r; + } + + link->enslaving ++; + } + + if (link->network->bond) { + log_struct_link(LOG_DEBUG, link, + "MESSAGE=%s: enslaving by '%s'", + link->network->bond->name, + NETDEV(link->network->bond), NULL); - link_enter_failed(link); - return r; + + r = netdev_enslave(link->network->bond, link, &enslave_handler); + if (r < 0) { + log_struct_link(LOG_WARNING, link, + "MESSAGE=%s: could not enslave by '%s': %s", + link->network->bond->name, strerror(-r), + NETDEV(link->network->bond), + NULL); + link_enter_failed(link); + return r; + } + + link->enslaving ++; } return 0; @@ -875,7 +895,7 @@ int link_configure(Link *link) { return r; } - return link_enter_join_bridge(link); + return link_enter_enslave(link); } int link_update(Link *link, sd_rtnl_message *m) { diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 5ab9ba0baf..7b93c5b5e8 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -70,8 +70,8 @@ int manager_new(Manager **ret) { if (!m->links) return -ENOMEM; - m->bridges = hashmap_new(string_hash_func, string_compare_func); - if (!m->bridges) + m->netdevs = hashmap_new(string_hash_func, string_compare_func); + if (!m->netdevs) return -ENOMEM; LIST_HEAD_INIT(m->networks); @@ -84,7 +84,7 @@ int manager_new(Manager **ret) { void manager_free(Manager *m) { Network *network; - Bridge *bridge; + Netdev *netdev; Link *link; udev_monitor_unref(m->udev_monitor); @@ -100,9 +100,9 @@ void manager_free(Manager *m) { link_free(link); hashmap_free(m->links); - while ((bridge = hashmap_first(m->bridges))) - bridge_free(bridge); - hashmap_free(m->bridges); + while ((netdev = hashmap_first(m->netdevs))) + netdev_free(netdev); + hashmap_free(m->netdevs); sd_rtnl_unref(m->rtnl); @@ -115,7 +115,7 @@ int manager_load_config(Manager *m) { /* update timestamp */ paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true); - r = bridge_load(m); + r = netdev_load(m); if (r < 0) return r; diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c new file mode 100644 index 0000000000..2d1cef112b --- /dev/null +++ b/src/network/networkd-netdev.c @@ -0,0 +1,354 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "networkd.h" +#include "net-util.h" +#include "path-util.h" +#include "conf-files.h" +#include "conf-parser.h" +#include "list.h" + +static const char* const netdev_kind_table[] = { + [NETDEV_KIND_BRIDGE] = "bridge", + [NETDEV_KIND_BOND] = "bond" +}; + +DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetdevKind); +DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetdevKind, "Failed to parse netdev kind"); + +void netdev_free(Netdev *netdev) { + netdev_enslave_callback *callback; + + if (!netdev) + return; + + while ((callback = netdev->callbacks)) { + LIST_REMOVE(callbacks, netdev->callbacks, callback); + free(callback); + } + + if (netdev->name) + hashmap_remove(netdev->manager->netdevs, netdev->name); + + free(netdev->filename); + + free(netdev->description); + free(netdev->name); + + free(netdev); +} + +int netdev_get(Manager *manager, const char *name, Netdev **ret) { + Netdev *netdev; + + assert(manager); + assert(name); + assert(ret); + + netdev = hashmap_get(manager->netdevs, name); + if (!netdev) { + *ret = NULL; + return -ENOENT; + } + + *ret = netdev; + + return 0; +} + +static int netdev_enter_failed(Netdev *netdev) { + netdev->state = NETDEV_STATE_FAILED; + + return 0; +} + +static int netdev_enslave_ready(Netdev *netdev, Link* link, sd_rtnl_message_handler_t callback) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; + int r; + + assert(netdev); + assert(netdev->state == NETDEV_STATE_READY); + assert(link); + assert(callback); + + r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req); + if (r < 0) { + log_error_netdev(netdev, + "Could not allocate RTM_SETLINK message: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->link->ifindex); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_MASTER attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL); + if (r < 0) { + log_error_netdev(netdev, + "Could not send rtnetlink message: %s", + strerror(-r)); + return r; + } + + log_debug_netdev(netdev, "enslaving link '%s'", link->ifname); + + return 0; +} + +static int netdev_enter_ready(Netdev *netdev) { + netdev_enslave_callback *callback; + + assert(netdev); + assert(netdev->name); + + netdev->state = NETDEV_STATE_READY; + + log_info_netdev(netdev, "netdev ready"); + + LIST_FOREACH(callbacks, callback, netdev->callbacks) { + /* enslave the links that were attempted to be enslaved befor the + * link was ready */ + netdev_enslave_ready(netdev, callback->link, callback->callback); + } + + return 0; +} + +static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { + Netdev *netdev = userdata; + int r; + + assert(netdev->state != _NETDEV_STATE_INVALID); + + r = sd_rtnl_message_get_errno(m); + if (r < 0) { + log_warning_netdev(netdev, "netdev failed: %s", strerror(-r)); + netdev_enter_failed(netdev); + + return 1; + } + + return 1; +} + +static int netdev_create(Netdev *netdev) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; + const char *kind; + int r; + + assert(netdev); + assert(netdev->state == _NETDEV_STATE_INVALID); + assert(netdev->name); + assert(netdev->manager); + assert(netdev->manager->rtnl); + + r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req); + if (r < 0) { + log_error_netdev(netdev, + "Could not allocate RTM_NEWLINK message: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_IFNAME attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_open_container(req, IFLA_LINKINFO); + if (r < 0) { + log_error_netdev(netdev, + "Could not open IFLA_LINKINFO container: %s", + strerror(-r)); + return r; + } + + kind = netdev_kind_to_string(netdev->kind); + if (!kind) { + log_error_netdev(netdev, "Invalid kind"); + return -EINVAL; + } + + r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_INFO_KIND attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_close_container(req); + if (r < 0) { + log_error_netdev(netdev, + "Could not close IFLA_LINKINFO container %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL); + if (r < 0) { + log_error_netdev(netdev, + "Could not send rtnetlink message: %s", strerror(-r)); + return r; + } + + log_debug_netdev(netdev, "creating netdev"); + + netdev->state = NETDEV_STATE_CREATING; + + return 0; +} + +int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) { + if (netdev->state == NETDEV_STATE_READY) { + netdev_enslave_ready(netdev, link, callback); + } else { + /* the netdev is not yet read, save this request for when it is*/ + netdev_enslave_callback *cb; + + cb = new0(netdev_enslave_callback, 1); + if (!cb) + return log_oom(); + + cb->callback = callback; + cb->link = link; + + LIST_PREPEND(callbacks, netdev->callbacks, cb); + } + + return 0; +} + +int netdev_set_link(Manager *m, NetdevKind kind, Link *link) { + Netdev *netdev; + int r; + + r = netdev_get(m, link->ifname, &netdev); + if (r < 0) + return r; + + if (netdev->link && netdev->link != link) + return -EEXIST; + + if (netdev->kind != kind) + return -EINVAL; + + netdev->link = link; + + netdev_enter_ready(netdev); + + return 0; +} + +static int netdev_load_one(Manager *manager, const char *filename) { + _cleanup_netdev_free_ Netdev *netdev = NULL; + _cleanup_fclose_ FILE *file = NULL; + int r; + + assert(manager); + assert(filename); + + file = fopen(filename, "re"); + if (!file) { + if (errno == ENOENT) + return 0; + else + return errno; + } + + netdev = new0(Netdev, 1); + if (!netdev) + return log_oom(); + + netdev->manager = manager; + netdev->state = _NETDEV_STATE_INVALID; + netdev->kind = _NETDEV_KIND_INVALID; + + r = config_parse(NULL, filename, file, "Netdev\0", config_item_perf_lookup, + (void*) network_gperf_lookup, false, false, netdev); + if (r < 0) { + log_warning("Could not parse config file %s: %s", filename, strerror(-r)); + return r; + } + + if (netdev->kind == _NETDEV_KIND_INVALID) { + log_warning("Netdev without Kind configured in %s. Ignoring", filename); + return 0; + } + + if (!netdev->name) { + log_warning("Netdev without Name configured in %s. Ignoring", filename); + return 0; + } + + netdev->filename = strdup(filename); + if (!netdev->filename) + return log_oom(); + + r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev); + if (r < 0) + return r; + + LIST_HEAD_INIT(netdev->callbacks); + + r = netdev_create(netdev); + if (r < 0) + return r; + + netdev = NULL; + + return 0; +} + +int netdev_load(Manager *manager) { + Netdev *netdev; + char **files, **f; + int r; + + assert(manager); + + while ((netdev = hashmap_first(manager->netdevs))) + netdev_free(netdev); + + r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); + if (r < 0) { + log_error("Failed to enumerate netdev files: %s", strerror(-r)); + return r; + } + + STRV_FOREACH_BACKWARDS(f, files) { + r = netdev_load_one(manager, *f); + if (r < 0) + return r; + } + + strv_free(files); + + return 0; +} diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 40f6b23058..e703294731 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -223,7 +223,7 @@ int config_parse_bridge(const char *unit, void *data, void *userdata) { Network *network = userdata; - Bridge *bridge; + Netdev *netdev; int r; assert(filename); @@ -231,14 +231,57 @@ int config_parse_bridge(const char *unit, assert(rvalue); assert(data); - r = bridge_get(network->manager, rvalue, &bridge); + r = netdev_get(network->manager, rvalue, &netdev); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Bridge is invalid, ignoring assignment: %s", rvalue); return 0; } - network->bridge = bridge; + if (netdev->kind != NETDEV_KIND_BRIDGE) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Netdev is not a bridge, ignoring assignment: %s", rvalue); + return 0; + } + + network->bridge = netdev; + + return 0; +} + +int config_parse_bond(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + Netdev *netdev; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = netdev_get(network->manager, rvalue, &netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Bond is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (netdev->kind != NETDEV_KIND_BOND) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Netdev is not a bond, ignoring assignment: %s", rvalue); + return 0; + } + + network->bond = netdev; return 0; } diff --git a/src/network/networkd.h b/src/network/networkd.h index c684eb8a8f..2323e3db85 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -34,42 +34,50 @@ #include "hashmap.h" #include "list.h" -typedef struct Bridge Bridge; +typedef struct Netdev Netdev; typedef struct Network Network; typedef struct Link Link; typedef struct Address Address; typedef struct Route Route; typedef struct Manager Manager; -typedef struct bridge_join_callback bridge_join_callback; +typedef struct netdev_enslave_callback netdev_enslave_callback; -struct bridge_join_callback { +struct netdev_enslave_callback { sd_rtnl_message_handler_t callback; Link *link; - LIST_FIELDS(bridge_join_callback, callbacks); + LIST_FIELDS(netdev_enslave_callback, callbacks); }; -typedef enum BridgeState { - BRIDGE_STATE_FAILED, - BRIDGE_STATE_CREATING, - BRIDGE_STATE_READY, - _BRIDGE_STATE_MAX, - _BRIDGE_STATE_INVALID = -1, -} BridgeState; - -struct Bridge { +typedef enum NetdevKind { + NETDEV_KIND_BRIDGE, + NETDEV_KIND_BOND, + _NETDEV_KIND_MAX, + _NETDEV_KIND_INVALID = -1 +} NetdevKind; + +typedef enum NetdevState { + NETDEV_STATE_FAILED, + NETDEV_STATE_CREATING, + NETDEV_STATE_READY, + _NETDEV_STATE_MAX, + _NETDEV_STATE_INVALID = -1, +} NetdevState; + +struct Netdev { Manager *manager; char *filename; char *description; char *name; + NetdevKind kind; Link *link; - BridgeState state; + NetdevState state; - LIST_HEAD(bridge_join_callback, callbacks); + LIST_HEAD(netdev_enslave_callback, callbacks); }; struct Network { @@ -84,7 +92,8 @@ struct Network { char *match_name; char *description; - Bridge *bridge; + Netdev *bridge; + Netdev *bond; bool dhcp; bool dhcp_dns; bool dhcp_mtu; @@ -141,7 +150,7 @@ struct Route { }; typedef enum LinkState { - LINK_STATE_JOINING_BRIDGE, + LINK_STATE_ENSLAVING, LINK_STATE_SETTING_ADDRESSES, LINK_STATE_SETTING_ROUTES, LINK_STATE_CONFIGURED, @@ -170,6 +179,7 @@ struct Link { unsigned addr_messages; unsigned route_messages; + unsigned enslaving; sd_dhcp_client *dhcp; }; @@ -183,7 +193,7 @@ struct Manager { sd_event_source *udev_event_source; Hashmap *links; - Hashmap *bridges; + Hashmap *netdevs; LIST_HEAD(Network, networks); usec_t network_dirs_ts_usec; @@ -210,18 +220,23 @@ int manager_update_resolv_conf(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define _cleanup_manager_free_ _cleanup_(manager_freep) -/* Bridge */ +/* Netdev */ + +int netdev_load(Manager *manager); -int bridge_load(Manager *manager); +void netdev_free(Netdev *netdev); -void bridge_free(Bridge *bridge); +DEFINE_TRIVIAL_CLEANUP_FUNC(Netdev*, netdev_free); +#define _cleanup_netdev_free_ _cleanup_(netdev_freep) -DEFINE_TRIVIAL_CLEANUP_FUNC(Bridge*, bridge_free); -#define _cleanup_bridge_free_ _cleanup_(bridge_freep) +int netdev_get(Manager *manager, const char *name, Netdev **ret); +int netdev_set_link(Manager *m, NetdevKind kind, Link *link); +int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t cb); -int bridge_get(Manager *manager, const char *name, Bridge **ret); -int bridge_set_link(Manager *m, Link *link); -int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t cb); +const char *netdev_kind_to_string(NetdevKind d) _const_; +NetdevKind netdev_kind_from_string(const char *d) _pure_; + +int config_parse_netdev_kind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Network */ @@ -239,6 +254,10 @@ int config_parse_bridge(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + /* gperf */ const struct ConfigPerfItem* network_gperf_lookup(const char *key, unsigned length); @@ -307,16 +326,16 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); /* More macros which append INTERFACE= to the message */ -#define log_full_bridge(level, bridge, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", bridge->name, "%s: " fmt, bridge->name, ##__VA_ARGS__) -#define log_debug_bridge(bridge, ...) log_full_bridge(LOG_DEBUG, bridge, ##__VA_ARGS__) -#define log_info_bridge(bridge, ...) log_full_bridge(LOG_INFO, bridge, ##__VA_ARGS__) -#define log_notice_bridge(bridge, ...) log_full_bridge(LOG_NOTICE, bridge, ##__VA_ARGS__) -#define log_warning_bridge(bridge, ...) log_full_bridge(LOG_WARNING, bridge,## __VA_ARGS__) -#define log_error_bridge(bridge, ...) log_full_bridge(LOG_ERR, bridge, ##__VA_ARGS__) +#define log_full_netdev(level, netdev, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", netdev->name, "%s: " fmt, netdev->name, ##__VA_ARGS__) +#define log_debug_netdev(netdev, ...) log_full_netdev(LOG_DEBUG, netdev, ##__VA_ARGS__) +#define log_info_netdev(netdev, ...) log_full_netdev(LOG_INFO, netdev, ##__VA_ARGS__) +#define log_notice_netdev(netdev, ...) log_full_netdev(LOG_NOTICE, netdev, ##__VA_ARGS__) +#define log_warning_netdev(netdev, ...) log_full_netdev(LOG_WARNING, netdev,## __VA_ARGS__) +#define log_error_netdev(netdev, ...) log_full_netdev(LOG_ERR, netdev, ##__VA_ARGS__) -#define log_struct_bridge(level, bridge, ...) log_struct(level, "INTERFACE=%s", bridge->name, __VA_ARGS__) +#define log_struct_netdev(level, netdev, ...) log_struct(level, "INTERFACE=%s", netdev->name, __VA_ARGS__) -#define BRIDGE(bridge) "INTERFACE=%s", bridge->name +#define NETDEV(netdev) "INTERFACE=%s", netdev->name #define ADDRESS_FMT_VAL(address) \ (address).s_addr & 0xFF, \ ((address).s_addr >> 8) & 0xFF, \ -- cgit v1.2.3-54-g00ecf