From 326cb4061a7d6ec4107f298b6f947d645512ad76 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Mon, 16 Jun 2014 11:54:33 +0530 Subject: [PATCH] networkd: introduce vxlan This patch enables netwokd to create vxlan Changes: Added: 1. File networkd networkd-vxlan.c 2. to netdev bool learning struct in_addr group uint64_t vxlanid; 3. VXLAN subsection and config parameters --- Makefile.am | 1 + src/network/networkd-link.c | 26 +++- src/network/networkd-netdev-gperf.gperf | 5 + src/network/networkd-netdev.c | 36 ++++- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 17 +++ src/network/networkd-vxlan.c | 162 +++++++++++++++++++++++ src/network/networkd.h | 7 + 8 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 src/network/networkd-vxlan.c diff --git a/Makefile.am b/Makefile.am index ec5f04d9e..479f7e2c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4358,6 +4358,7 @@ libsystemd_networkd_core_la_SOURCES = \ src/network/networkd-netdev.c \ src/network/networkd-tunnel.c \ src/network/networkd-veth.c \ + src/network/networkd-vxlan.c \ src/network/networkd-network.c \ src/network/networkd-address.c \ src/network/networkd-route.c \ diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index f496978cb..7c738fe81 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1492,7 +1492,7 @@ static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { } static int link_enter_enslave(Link *link) { - NetDev *vlan, *macvlan; + NetDev *vlan, *macvlan, *vxlan; Iterator i; int r; @@ -1508,7 +1508,8 @@ static int link_enter_enslave(Link *link) { !link->network->bond && !link->network->tunnel && hashmap_isempty(link->network->vlans) && - hashmap_isempty(link->network->macvlans)) + hashmap_isempty(link->network->macvlans) && + hashmap_isempty(link->network->vxlans)) return link_enslaved(link); if (link->network->bond) { @@ -1625,6 +1626,27 @@ static int link_enter_enslave(Link *link) { link->enslaving ++; } + HASHMAP_FOREACH(vxlan, link->network->vxlans, i) { + log_struct_link(LOG_DEBUG, link, + "MESSAGE=%*s: enslaving by '%s'", + IFNAMSIZ, + link->ifname, vxlan->ifname, NETDEV(vxlan), NULL); + + r = netdev_enslave(vxlan, link, &enslave_handler); + if (r < 0) { + log_struct_link(LOG_WARNING, link, + "MESSAGE=%*s: could not enslave by '%s': %s", + IFNAMSIZ, + link->ifname, vxlan->ifname, strerror(-r), + NETDEV(vxlan), NULL); + link_enter_failed(link); + return r; + } + + link_ref(link); + link->enslaving ++; + } + return 0; } diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 784f0f468..9125e1db4 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -33,3 +33,8 @@ Tunnel.TTL, config_parse_unsigned, 0, Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(NetDev, tunnel_pmtudisc) Peer.Name, config_parse_ifname, 0, offsetof(NetDev, ifname_peer) Peer.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(NetDev, vxlanid) +VXLAN.Group, config_parse_tunnel_address, 0, offsetof(NetDev, group) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(NetDev, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(NetDev, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(NetDev, learning) diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index 5f8a82cbd..5359eb179 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -36,6 +36,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BOND] = "bond", [NETDEV_KIND_VLAN] = "vlan", [NETDEV_KIND_MACVLAN] = "macvlan", + [NETDEV_KIND_VXLAN] = "vxlan", [NETDEV_KIND_IPIP] = "ipip", [NETDEV_KIND_GRE] = "gre", [NETDEV_KIND_SIT] = "sit", @@ -402,14 +403,20 @@ 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) { int r; - if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) + switch(netdev->kind) { + case NETDEV_KIND_VLAN: + case NETDEV_KIND_MACVLAN: return netdev_create(netdev, link, callback); - - if(netdev->kind == NETDEV_KIND_IPIP || - netdev->kind == NETDEV_KIND_GRE || - netdev->kind == NETDEV_KIND_SIT || - netdev->kind == NETDEV_KIND_VTI) + case NETDEV_KIND_VXLAN: + return netdev_create_vxlan(netdev, link, callback); + case NETDEV_KIND_IPIP: + case NETDEV_KIND_GRE: + case NETDEV_KIND_SIT: + case NETDEV_KIND_VTI: return netdev_create_tunnel(link, netdev_create_handler); + default: + break; + } if (netdev->state == NETDEV_STATE_READY) { r = netdev_enslave_ready(netdev, link, callback); @@ -606,9 +613,12 @@ static int netdev_load_one(Manager *manager, const char *filename) { netdev->kind = _NETDEV_KIND_INVALID; netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID; netdev->vlanid = VLANID_MAX + 1; + netdev->vxlanid = VXLAN_VID_MAX + 1; netdev->tunnel_pmtudisc = true; + netdev->learning = true; - r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0Tunnel\0Peer\0", + r = config_parse(NULL, filename, file, + "Match\0NetDev\0VLAN\0MACVLAN\0VXLAN\0Tunnel\0Peer\0", config_item_perf_lookup, (void*) network_netdev_gperf_lookup, false, false, netdev); if (r < 0) { @@ -631,12 +641,23 @@ static int netdev_load_one(Manager *manager, const char *filename) { return 0; } + if (netdev->kind == NETDEV_KIND_VXLAN && netdev->vxlanid > VXLAN_VID_MAX) { + log_warning("VXLAN without valid Id configured in %s. Ignoring", 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_VXLAN && netdev->vlanid <= VXLAN_VID_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", @@ -690,6 +711,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (netdev->kind != NETDEV_KIND_VLAN && netdev->kind != NETDEV_KIND_MACVLAN && + netdev->kind != NETDEV_KIND_VXLAN && netdev->kind != NETDEV_KIND_IPIP && netdev->kind != NETDEV_KIND_GRE && netdev->kind != NETDEV_KIND_SIT && diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7ef467eed..5e21805ee 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -29,6 +29,7 @@ Network.Bridge, config_parse_netdev, 0, Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) Network.VLAN, config_parse_netdev, 0, offsetof(Network, vlans) Network.MACVLAN, config_parse_netdev, 0, offsetof(Network, macvlans) +Network.VXLAN, config_parse_netdev, 0, offsetof(Network, vxlans) Network.DHCP, config_parse_bool, 0, offsetof(Network, dhcp) Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) Network.IPv4LL, config_parse_bool, 0, offsetof(Network, ipv4ll) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4e3817314..45c1ab0f4 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -69,6 +69,10 @@ static int network_load_one(Manager *manager, const char *filename) { if (!network->macvlans) return log_oom(); + network->vxlans = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!network->vxlans) + return log_oom(); + network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func); if (!network->addresses_by_section) return log_oom(); @@ -183,6 +187,10 @@ void network_free(Network *network) { netdev_unref(netdev); hashmap_free(network->macvlans); + HASHMAP_FOREACH(netdev, network->vxlans, i) + netdev_unref(netdev); + hashmap_free(network->vxlans); + while ((route = network->static_routes)) route_free(route); @@ -325,6 +333,15 @@ int config_parse_netdev(const char *unit, return 0; } + break; + case NETDEV_KIND_VXLAN: + r = hashmap_put(network->vxlans, netdev->ifname, netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Can not add VXLAN to network: %s", rvalue); + return 0; + } + break; default: assert_not_reached("Can not parse NetDev"); diff --git a/src/network/networkd-vxlan.c b/src/network/networkd-vxlan.c new file mode 100644 index 000000000..d6721222f --- /dev/null +++ b/src/network/networkd-vxlan.c @@ -0,0 +1,162 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + + 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 +#include +#include + +#include "sd-rtnl.h" +#include "networkd.h" + + +static int netdev_fill_vxlan_rtnl_message(NetDev *netdev, Link *link, sd_rtnl_message *m) { + int r; + + assert(link); + assert(link->network); + assert(m); + + r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_IFNAME, attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_LINKINFO attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, + netdev_kind_to_string(netdev->kind)); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_INFO_DATA attribute: %s", + strerror(-r)); + return r; + } + + if (netdev->vlanid <= VXLAN_VID_MAX) { + r = sd_rtnl_message_append_u32(m, IFLA_VXLAN_ID, netdev->vxlanid); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_ID attribute: %s", + strerror(-r)); + return r; + } + } + + r = sd_rtnl_message_append_in_addr(m, IFLA_VXLAN_GROUP, &netdev->group); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_GROUP attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_LINK attribute: %s", + strerror(-r)); + return r; + } + + if(netdev->ttl) { + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_TTL, netdev->ttl); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_TTL attribute: %s", + strerror(-r)); + return r; + } + } + + if(netdev->tos) { + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_TOS, netdev->tos); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_TOS attribute: %s", + strerror(-r)); + return r; + } + } + + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_LEARNING, netdev->learning); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_VXLAN_LEARNING attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_close_container(m); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_LINKINFO attribute: %s", + strerror(-r)); + return r; + } + + return r; +} + +int netdev_create_vxlan(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; + int r; + + assert(netdev); + assert(!(netdev->kind == NETDEV_KIND_VXLAN) || (link && callback)); + assert(netdev->ifname); + assert(netdev->manager); + assert(netdev->manager->rtnl); + + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) { + log_error_netdev(netdev, + "Could not allocate RTM_NEWLINK message: %s", + strerror(-r)); + return r; + } + + r = netdev_fill_vxlan_rtnl_message(netdev, link, m); + if(r < 0) + return r; + + r = sd_rtnl_call_async(netdev->manager->rtnl, m, 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, "Creating vxlan netdev: %s", + netdev_kind_to_string(netdev->kind)); + + netdev->state = NETDEV_STATE_CREATING; + + return 0; +} diff --git a/src/network/networkd.h b/src/network/networkd.h index 799fd5910..3b081d527 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -38,6 +38,7 @@ #include "condition-util.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU +#define VXLAN_VID_MAX (1u << 24) - 1 typedef struct NetDev NetDev; typedef struct Network Network; @@ -69,6 +70,7 @@ typedef enum NetDevKind { NETDEV_KIND_BOND, NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, + NETDEV_KIND_VXLAN, NETDEV_KIND_IPIP, NETDEV_KIND_GRE, NETDEV_KIND_SIT, @@ -108,16 +110,19 @@ struct NetDev { NetDevKind kind; uint64_t vlanid; + uint64_t vxlanid; int32_t macvlan_mode; int ifindex; NetDevState state; bool tunnel_pmtudisc; + bool learning; unsigned ttl; unsigned tos; struct in_addr local; struct in_addr remote; + struct in_addr group; LIST_HEAD(netdev_enslave_callback, callbacks); }; @@ -143,6 +148,7 @@ struct Network { NetDev *tunnel; Hashmap *vlans; Hashmap *macvlans; + Hashmap *vxlans; bool dhcp; bool dhcp_dns; bool dhcp_ntp; @@ -321,6 +327,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *newlink); int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t cb); int netdev_create_tunnel(Link *link, sd_rtnl_message_handler_t callback); int netdev_create_veth(NetDev *netdev, sd_rtnl_message_handler_t callback); +int netdev_create_vxlan(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback); const char *netdev_kind_to_string(NetDevKind d) _const_; NetDevKind netdev_kind_from_string(const char *d) _pure_; -- 2.30.2