From fe6b2d55bcb379d01664ed28cea40634cb6b52e3 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 25 Feb 2014 21:16:17 +0100 Subject: [PATCH] networkd: add basic support for MACVLANs --- man/systemd.netdev.xml | 20 +++++++- man/systemd.network.xml | 7 +++ src/network/networkd-link.c | 23 ++++++++- src/network/networkd-netdev-gperf.gperf | 1 + src/network/networkd-netdev.c | 65 +++++++++++++++++++----- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 50 +++++++++++++++++- src/network/networkd.h | 21 ++++++++ 8 files changed, 170 insertions(+), 18 deletions(-) diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 26cc6158c..5d8b41a3b 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -142,8 +142,9 @@ Kind= The netdev kind. Currently, bridge, - bond and vlan - are supported. This option is compulsory. + bond, vlan and + macvlan are supported. This option + is compulsory. @@ -161,6 +162,21 @@ + The [MACVLAN] section only applies for netdevs of kind + macvlan, and accepts the following key: + + + + Mode= + + The MACVLAN mode to use. The supported options are + private, vepa, + bridge and passthru. + + + + + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 1ba4f4a97..fcf48c6d4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -235,6 +235,13 @@ may be specified more than once. + + MACVLAN= + + The name of a MACVLAN to create on the link. This option + may be specified more than once. + + An [Address] section accepts the following keys. diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1f495b38b..b217123ec 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -890,7 +890,7 @@ static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { } static int link_enter_enslave(Link *link) { - NetDev *vlan; + NetDev *vlan, *macvlan; Iterator i; int r; @@ -901,7 +901,8 @@ static int link_enter_enslave(Link *link) { link->state = LINK_STATE_ENSLAVING; if (!link->network->bridge && !link->network->bond && - hashmap_isempty(link->network->vlans)) + hashmap_isempty(link->network->vlans) && + hashmap_isempty(link->network->macvlans)) return link_enslaved(link); if (link->network->bridge) { @@ -943,6 +944,24 @@ static int link_enter_enslave(Link *link) { link->enslaving ++; } + HASHMAP_FOREACH(macvlan, link->network->macvlans, i) { + log_struct_link(LOG_DEBUG, link, + "MESSAGE=%s: enslaving by '%s'", + link->ifname, macvlan->name, NETDEV(macvlan), NULL); + + r = netdev_enslave(macvlan, link, &enslave_handler); + if (r < 0) { + log_struct_link(LOG_WARNING, link, + "MESSAGE=%s: could not enslave by '%s': %s", + link->ifname, macvlan->name, strerror(-r), + NETDEV(macvlan), NULL); + link_enter_failed(link); + return r; + } + + link->enslaving ++; + } + return 0; } diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 7dd47f971..2793d77a7 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -23,3 +23,4 @@ NetDev.Description, config_parse_string, 0, NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, name) NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) VLAN.Id, config_parse_uint64, 0, offsetof(NetDev, vlanid) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(NetDev, macvlan_mode) diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index 05f21fa87..9f580aeab 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -32,11 +32,22 @@ static const char* const netdev_kind_table[] = { [NETDEV_KIND_BRIDGE] = "bridge", [NETDEV_KIND_BOND] = "bond", [NETDEV_KIND_VLAN] = "vlan", + [NETDEV_KIND_MACVLAN] = "macvlan", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind"); +static const char* const macvlan_mode_table[] = { + [NETDEV_MACVLAN_MODE_PRIVATE] = "private", + [NETDEV_MACVLAN_MODE_VEPA] = "vepa", + [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge", + [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru", +}; + +DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode"); + void netdev_free(NetDev *netdev) { netdev_enslave_callback *callback; @@ -166,8 +177,8 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c int r; assert(netdev); - assert(!(netdev->kind == NETDEV_KIND_VLAN) || - (link && callback && netdev->vlanid <= VLANID_MAX)); + assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) || + (link && callback)); assert(netdev->name); assert(netdev->manager); assert(netdev->manager->rtnl); @@ -220,7 +231,7 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c return r; } - if (netdev->vlanid <= VLANID_MAX) { + if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA); if (r < 0) { log_error_netdev(netdev, @@ -229,12 +240,24 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c return r; } - r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid); - if (r < 0) { - log_error_netdev(netdev, - "Could not append IFLA_VLAN_ID attribute: %s", - strerror(-r)); - return r; + if (netdev->vlanid <= VLANID_MAX) { + r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VLAN_ID attribute: %s", + strerror(-r)); + return r; + } + } + + if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { + r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_MACVLAN_MODE attribute: %s", + strerror(-r)); + return r; + } } r = sd_rtnl_message_close_container(req); @@ -272,7 +295,7 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c } int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) { - if (netdev->kind == NETDEV_KIND_VLAN) + if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) return netdev_create(netdev, link, callback); if (netdev->state == NETDEV_STATE_READY) { @@ -335,10 +358,12 @@ static int netdev_load_one(Manager *manager, const char *filename) { netdev->manager = manager; netdev->state = _NETDEV_STATE_INVALID; netdev->kind = _NETDEV_KIND_INVALID; + netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID; netdev->vlanid = VLANID_MAX + 1; - r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0", config_item_perf_lookup, - (void*) network_netdev_gperf_lookup, false, false, netdev); + r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0", + config_item_perf_lookup, (void*) network_netdev_gperf_lookup, + false, false, netdev); if (r < 0) { log_warning("Could not parse config file %s: %s", filename, strerror(-r)); return r; @@ -359,6 +384,19 @@ static int netdev_load_one(Manager *manager, const char *filename) { return 0; } + if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) { + log_warning("VLAN Id configured for a %s in %s. Ignoring", + netdev_kind_to_string(netdev->kind), filename); + return 0; + } + + if (netdev->kind != NETDEV_KIND_MACVLAN && + netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { + log_warning("MACVLAN Mode configured for a %s in %s. Ignoring", + netdev_kind_to_string(netdev->kind), filename); + return 0; + } + netdev->filename = strdup(filename); if (!netdev->filename) return log_oom(); @@ -375,7 +413,8 @@ static int netdev_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(netdev->callbacks); - if (netdev->kind != NETDEV_KIND_VLAN) { + if (netdev->kind != NETDEV_KIND_VLAN && + netdev->kind != NETDEV_KIND_MACVLAN) { r = netdev_create(netdev, NULL, NULL); if (r < 0) return r; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 44aeb9c3a..6cc186f2a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -28,6 +28,7 @@ Network.Description, config_parse_string, 0, Network.Bridge, config_parse_bridge, 0, offsetof(Network, bridge) Network.Bond, config_parse_bond, 0, offsetof(Network, bond) Network.VLAN, config_parse_vlan, 0, offsetof(Network, vlans) +Network.MACVLAN, config_parse_macvlan, 0, offsetof(Network, macvlans) Network.DHCP, config_parse_bool, 0, offsetof(Network, dhcp) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 14fa92aea..ca79d4ff6 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -53,10 +53,14 @@ static int network_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(network->static_addresses); LIST_HEAD_INIT(network->static_routes); - network->vlans = hashmap_new(uint64_hash_func, uint64_compare_func); + network->vlans = hashmap_new(string_hash_func, string_compare_func); if (!network->vlans) return log_oom(); + network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!network->macvlans) + return log_oom(); + network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func); if (!network->addresses_by_section) return log_oom(); @@ -150,6 +154,8 @@ void network_free(Network *network) { hashmap_free(network->vlans); + hashmap_free(network->macvlans); + while ((route = network->static_routes)) route_free(route); @@ -330,3 +336,45 @@ int config_parse_vlan(const char *unit, return 0; } + +int config_parse_macvlan(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, + "MACVLAN is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (netdev->kind != NETDEV_KIND_MACVLAN) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "NetDev is not a MACVLAN, ignoring assignment: %s", rvalue); + return 0; + } + + r = hashmap_put(network->macvlans, netdev->name, netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Can not add MACVLAN to network: %s", rvalue); + return 0; + } + + return 0; +} diff --git a/src/network/networkd.h b/src/network/networkd.h index 8307bb5b1..85c300982 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -50,10 +50,20 @@ struct netdev_enslave_callback { LIST_FIELDS(netdev_enslave_callback, callbacks); }; +typedef enum MacVlanMode { + NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, + NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA, + NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE, + NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU, + _NETDEV_MACVLAN_MODE_MAX, + _NETDEV_MACVLAN_MODE_INVALID = -1 +} MacVlanMode; + typedef enum NetDevKind { NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VLAN, + NETDEV_KIND_MACVLAN, _NETDEV_KIND_MAX, _NETDEV_KIND_INVALID = -1 } NetDevKind; @@ -81,6 +91,7 @@ struct NetDev { NetDevKind kind; uint64_t vlanid; + int32_t macvlan_mode; int ifindex; NetDevState state; @@ -107,6 +118,7 @@ struct Network { NetDev *bridge; NetDev *bond; Hashmap *vlans; + Hashmap *macvlans; bool dhcp; bool dhcp_dns; bool dhcp_mtu; @@ -248,8 +260,13 @@ int netdev_enslave(NetDev *netdev, 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_; +const char *macvlan_mode_to_string(MacVlanMode d) _const_; +MacVlanMode macvlan_mode_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); +int config_parse_macvlan_mode(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_netdev_gperf_lookup(const char *key, unsigned length); @@ -277,6 +294,10 @@ int config_parse_vlan(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_macvlan(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_network_gperf_lookup(const char *key, unsigned length); -- 2.30.2