merge: branch 'ff/garp_bond_slb'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2057
This commit is contained in:
Fernando Fernandez Mancera
2024-12-18 15:47:43 +01:00
11 changed files with 407 additions and 0 deletions

View File

@@ -923,6 +923,91 @@ deactivate(NMDevice *device)
/*****************************************************************************/
gboolean
nm_device_bond_is_slb(NMDevice *device)
{
NMConnection *connection;
NMSettingBond *s_bond;
connection = nm_device_get_applied_connection(device);
if (!connection)
return FALSE;
s_bond = nm_connection_get_setting_bond(connection);
if (!s_bond)
return FALSE;
if (!_nm_setting_bond_opt_value_as_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB))
return FALSE;
return TRUE;
}
gboolean
nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port)
{
NMDeviceBond *self = NM_DEVICE_BOND(controller);
int port_ifindex = nm_device_get_ifindex(port);
int controller_ifindex = nm_device_get_ifindex(controller);
NML3Cfg *l3cfg = nm_device_get_l3cfg(controller);
NMDevice *bond_controller = nm_device_get_controller(controller);
NML3Cfg *bridge_l3cfg;
gs_free in_addr_t *addrs_array = NULL;
gsize addrs_len;
addrs_array = nm_l3cfg_get_configured_ip4_addresses(l3cfg, &addrs_len);
if (addrs_len > 0) {
/* the bond has IPs configured, it is not attached to a
* bridge then. */
if (!nm_bond_manager_send_arp(controller_ifindex,
-1,
nm_device_get_platform(port),
addrs_array,
addrs_len)) {
_LOGT(LOGD_BOND,
"failed to send gARP on port %s (ifindex %d)",
nm_device_get_iface(port),
port_ifindex);
return FALSE;
}
} else if (bond_controller
&& nm_device_get_device_type(bond_controller) == NM_DEVICE_TYPE_BRIDGE) {
/* the bond is attached to a bridge, firts let's check if the bridge has IP
* configuration. */
bridge_l3cfg = nm_device_get_l3cfg(bond_controller);
addrs_array = nm_l3cfg_get_configured_ip4_addresses(bridge_l3cfg, &addrs_len);
if (addrs_len > 0) {
/* the bridge has IPs configured, announcing them on the bond */
if (!nm_bond_manager_send_arp(controller_ifindex,
-1,
nm_device_get_platform(port),
addrs_array,
addrs_len)) {
_LOGT(LOGD_BOND,
"failed to send gARP on port %s (ifindex %d) on behalf of bridge",
nm_device_get_iface(port),
port_ifindex);
return FALSE;
}
}
/* we are going to ARP probe the content of the FDB table */
if (!nm_bond_manager_send_arp(controller_ifindex,
nm_device_get_ifindex(bond_controller),
nm_device_get_platform(port),
NULL,
0)) {
_LOGT(LOGD_BOND, "failed to send ARP probing with content of FDB table");
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
static void
nm_device_bond_init(NMDeviceBond *self)
{

View File

@@ -23,4 +23,7 @@ typedef struct _NMDeviceBondClass NMDeviceBondClass;
GType nm_device_bond_get_type(void);
gboolean nm_device_bond_is_slb(NMDevice *device);
gboolean nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port);
#endif /* NM_DEVICE_BOND_H */

View File

@@ -78,6 +78,7 @@
#include "nm-hostname-manager.h"
#include "nm-device-generic.h"
#include "nm-device-bond.h"
#include "nm-device-bridge.h"
#include "nm-device-loopback.h"
#include "nm-device-vlan.h"
@@ -7542,10 +7543,12 @@ device_link_changed(gpointer user_data)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
gboolean ip_ifname_changed = FALSE;
nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL;
NMDevice *controller;
const NMPlatformLink *pllink;
const char *str;
int ifindex;
gboolean was_up;
gboolean carrier_was_up;
gboolean update_unmanaged_specs = FALSE;
gboolean got_hw_addr = FALSE, had_hw_addr;
gboolean seen_down = priv->device_link_changed_down;
@@ -7628,6 +7631,8 @@ device_link_changed(gpointer user_data)
_LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
}
carrier_was_up = priv->carrier;
/* Update carrier from link event if applicable. */
if (nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT)
&& !nm_device_has_capability(self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
@@ -7644,6 +7649,35 @@ device_link_changed(gpointer user_data)
was_up = priv->up;
priv->up = NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP);
if ((was_up && !priv->up) || (carrier_was_up && !priv->carrier)) {
/* the link was up and now is down, or the carrier was up and now is down. We must
* check if this is a port of a bond and if that bond is in balance-slb mode to perform
* gARP on the controller's port.
*/
controller = nm_device_get_controller(self);
if (controller && nm_device_get_device_type(controller) == NM_DEVICE_TYPE_BOND
&& nm_device_bond_is_slb(controller)) {
NMDevicePrivate *controller_priv = NM_DEVICE_GET_PRIVATE(controller);
PortInfo *info;
_LOGT(
LOGD_CORE,
"controller %s is a bond in bonding-slb mode, redirecting traffic to another port",
nm_device_get_iface(controller));
c_list_for_each_entry (info, &controller_priv->ports, lst_port) {
if (info->port != self && NM_DEVICE_GET_PRIVATE(info->port)->carrier) {
_LOGT(LOGD_CORE,
"sending gARP on port %s (ifindex %d)",
nm_device_get_iface(info->port),
nm_device_get_ifindex(info->port));
if (nm_device_bond_announce_ports_on_slb(controller, info->port))
break;
}
}
}
}
if (pllink->initialized && nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) {
nm_device_set_unmanaged_by_user_udev(self);
nm_device_set_unmanaged_by_user_conf(self);

View File

@@ -6,8 +6,13 @@
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
#include "NetworkManagerUtils.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-platform/nm-linux-platform.h"
#include "libnm-glib-aux/nm-str-buf.h"
#include "libnm-platform/nm-platform.h"
#include "libnm-platform/nmp-object.h"
@@ -94,6 +99,32 @@ struct _NMBondManager {
/*****************************************************************************/
#define IP_ADDR_LEN 4
#define ARP_OP_GARP 0x0001
#define ARP_OP_RARP 0x0003
#define ARP_HW_TYPE_ETH 0x0001
#define ARP_PROTOCOL_IPV4 0x0800
typedef struct _nm_packed {
char s_addr[ETH_ALEN];
char d_addr[ETH_ALEN];
guint16 eth_type;
guint16 hw_type;
guint16 protocol;
guint8 addr_len;
guint8 ip_len;
guint16 op;
char s_hw_addr[ETH_ALEN];
char s_ip_addr[IP_ADDR_LEN];
char d_hw_addr[ETH_ALEN];
char d_ip_addr[IP_ADDR_LEN];
} ARPPacket;
/*****************************************************************************/
static void _nft_call(NMBondManager *self,
gboolean up,
const char *bond_ifname,
@@ -839,6 +870,89 @@ nm_bond_manager_reapply(NMBondManager *self)
_reconfigure_check(self, TRUE);
}
gboolean
nm_bond_manager_send_arp(int bond_ifindex,
int bridge_ifindex,
struct _NMPlatform *platform,
in_addr_t *addrs_array,
gsize addrs_len)
{
struct sockaddr_ll addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ARP),
.sll_ifindex = bond_ifindex,
};
ARPPacket data;
const guint8 *hwaddr;
gsize hwaddrlen = 0;
nm_auto_close int sockfd = -1;
bool announce_fdb = FALSE;
nm_assert(NM_IS_PLATFORM(platform));
nm_assert(bond_ifindex);
/* if the bridge_ifindex is specified is because we want to
* announce the FDB table content from the bridge */
if (bridge_ifindex > 0)
announce_fdb = TRUE;
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sockfd < 0)
return FALSE;
hwaddr = nm_platform_link_get_address(platform, bond_ifindex, &hwaddrlen);
/* infiniband interfaces not supported */
if (hwaddrlen > ETH_ALEN)
return FALSE;
/* common ARP options to be configured */
memset(data.d_addr, 0xff, ETH_ALEN);
data.eth_type = htons(ETH_P_ARP);
data.hw_type = htons(ARP_HW_TYPE_ETH);
data.protocol = htons(ARP_PROTOCOL_IPV4);
data.addr_len = ETH_ALEN;
data.ip_len = IP_ADDR_LEN;
if (announce_fdb) {
/* if we are announcing the FDB we do a RARP, we don't set the
* source/dest IPv4 address */
int ifindexes[] = {bridge_ifindex, bond_ifindex};
int i;
gs_free NMEtherAddr **fdb_addrs = NULL;
fdb_addrs = nm_linux_platform_get_link_fdb_table(platform, ifindexes, 2);
/* we want to send a Reverse ARP (RARP) packet */
data.op = htons(ARP_OP_RARP);
i = 0;
while (fdb_addrs[i] != NULL) {
NMEtherAddr *tmp_hwaddr = fdb_addrs[i];
memcpy(data.s_hw_addr, tmp_hwaddr, ETH_ALEN);
memcpy(data.d_hw_addr, tmp_hwaddr, ETH_ALEN);
memcpy(data.s_addr, tmp_hwaddr, ETH_ALEN);
g_free(tmp_hwaddr);
if (sendto(sockfd, &data, sizeof(data), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
return FALSE;
i++;
}
} else {
/* we want to send a Gratuitous ARP (GARP) packet */
data.op = htons(ARP_OP_GARP);
memcpy(data.s_addr, hwaddr, hwaddrlen);
memcpy(data.s_hw_addr, hwaddr, hwaddrlen);
for (int i = 0; i < addrs_len; i++) {
const in_addr_t tmp_addr = addrs_array[i];
unaligned_write_ne32(data.s_ip_addr, tmp_addr);
unaligned_write_ne32(data.d_ip_addr, tmp_addr);
if (sendto(sockfd, &data, sizeof(data), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
int

View File

@@ -23,6 +23,12 @@ NMBondManager *nm_bond_manager_new(struct _NMPlatform *platform,
void nm_bond_manager_reapply(NMBondManager *self);
gboolean nm_bond_manager_send_arp(int bond_ifindex,
int bridge_ifindex,
struct _NMPlatform *platform,
in_addr_t *addrs_array,
gsize addrs_len);
void nm_bond_manager_destroy(NMBondManager *self);
int nm_bond_manager_get_ifindex(NMBondManager *self);

View File

@@ -5552,6 +5552,30 @@ nm_l3cfg_get_best_default_route(NML3Cfg *self, int addr_family, gboolean get_com
return nm_l3_config_data_get_best_default_route(l3cd, addr_family);
}
in_addr_t *
nm_l3cfg_get_configured_ip4_addresses(NML3Cfg *self, gsize *out_len)
{
GArray *array = NULL;
NMDedupMultiIter iter;
const NMPObject *obj;
const NML3ConfigData *l3cd;
l3cd = nm_l3cfg_get_combined_l3cd(self, FALSE);
if (!l3cd)
return NULL;
array = g_array_new(FALSE, FALSE, sizeof(in_addr_t));
nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) {
in_addr_t tmp = NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address;
nm_g_array_append_simple(array, tmp);
}
*out_len = array->len;
return NM_CAST_ALIGN(in_addr_t, g_array_free(array, FALSE));
}
/*****************************************************************************/
gboolean

View File

@@ -437,6 +437,8 @@ const NML3ConfigData *nm_l3cfg_get_combined_l3cd(NML3Cfg *self, gboolean get_com
const NMPObject *
nm_l3cfg_get_best_default_route(NML3Cfg *self, int addr_family, gboolean get_commited);
in_addr_t *nm_l3cfg_get_configured_ip4_addresses(NML3Cfg *self, gsize *out_len);
/*****************************************************************************/
gboolean nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self);

View File

@@ -58,6 +58,17 @@ nm_ether_addr_from_string(NMEtherAddr *addr, const char *str)
return addr;
}
guint
nm_ether_addr_hash(const NMEtherAddr *a)
{
NMHashState h;
nm_hash_init(&h, 1947951703u);
nm_hash_update(&h, a, sizeof(NMEtherAddr));
return nm_hash_complete(&h);
}
/*****************************************************************************/
/**

View File

@@ -241,6 +241,8 @@ nm_ether_addr_is_zero(const NMEtherAddr *a)
return nm_memeq(a, &nm_ether_addr_zero, sizeof(NMEtherAddr));
}
guint nm_ether_addr_hash(const NMEtherAddr *a);
/*****************************************************************************/
struct ether_addr;

View File

@@ -323,6 +323,10 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
#define IFLA_VF_VLAN_INFO_UNSPEC 0
#define IFLA_VF_VLAN_INFO 1
/*****************************************************************************/
#define NDA_CONTROLLER NDA_MASTER
/* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */
struct _ifla_vf_setting {
guint32 vf;
@@ -10411,6 +10415,125 @@ link_get_driver_info(NMPlatform *platform,
/*****************************************************************************/
typedef struct {
int ifindexes_len;
int *ifindexes;
GHashTable *out_fdb_addrs;
} FdbData;
static int
parse_fdb_cb(const struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct ndmsg *ndmsg = NLMSG_DATA(nlh);
int from_ifindex = ndmsg->ndm_ifindex;
bool match = FALSE;
static const struct nla_policy policy[] = {
[NDA_LLADDR] = {.minlen = ETH_ALEN, .maxlen = ETH_ALEN},
[NDA_CONTROLLER] = {.type = NLA_U32},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
FdbData *data = arg;
int fdb_controller = -1;
if (nlmsg_parse_arr(nlh, sizeof(*ndmsg), tb, policy) < 0)
return NL_SKIP;
if (tb[NDA_CONTROLLER])
fdb_controller = nla_get_u32(tb[NDA_CONTROLLER]);
for (int i = 0; i < data->ifindexes_len; i++) {
int current_ifindex = data->ifindexes[i];
if (NM_IN_SET(current_ifindex, from_ifindex, fdb_controller)) {
match = TRUE;
break;
}
}
if (!match)
return NL_SKIP;
if (tb[NDA_LLADDR]) {
NMEtherAddr *hwaddr = g_new(NMEtherAddr, 1);
memcpy(hwaddr, nla_data(tb[NDA_LLADDR]), ETH_ALEN);
g_hash_table_add(data->out_fdb_addrs, hwaddr);
}
return NL_OK;
}
NMEtherAddr **
nm_linux_platform_get_link_fdb_table(NMPlatform *platform, int *ifindexes, guint ifindexes_len)
{
int nle;
struct nl_sock *sk = NULL;
nm_auto_nlmsg struct nl_msg *msg = NULL;
gs_unref_hashtable GHashTable *fdb_addrs = NULL;
FdbData data;
const struct ndmsg ndm = {
.ndm_family = AF_BRIDGE,
};
gpointer *ret = NULL;
nm_assert(ifindexes);
nm_assert(ifindexes_len >= 1);
fdb_addrs = g_hash_table_new_full((GHashFunc) nm_ether_addr_hash,
(GEqualFunc) nm_ether_addr_equal,
g_free,
NULL);
msg = nlmsg_alloc_new(0, RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
if (nlmsg_append_struct(msg, &ndm) < 0)
goto err;
nle = nl_socket_new(&sk, NETLINK_ROUTE, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, 0, 0);
if (nle < 0) {
_LOGD("get-link-fdb: error opening socket: %s (%d)", nm_strerror(nle), nle);
goto err;
}
nle = nl_send_auto(sk, msg);
if (nle < 0) {
_LOGD("get-link-fdb: failed sending request: %s (%d)", nm_strerror(nle), nle);
goto err;
}
data = ((FdbData) {
.ifindexes_len = ifindexes_len,
.ifindexes = ifindexes,
.out_fdb_addrs = fdb_addrs,
});
do {
nle = nl_recvmsgs(sk,
&((const struct nl_cb) {
.valid_cb = parse_fdb_cb,
.valid_arg = &data,
}));
} while (nle == -EAGAIN);
if (nle < 0) {
_LOGD("get-link-fdb: recv failed: %s (%d)", nm_strerror(nle), nle);
goto err;
}
ret = g_hash_table_get_keys_as_array(fdb_addrs, NULL);
g_hash_table_steal_all(fdb_addrs);
nl_socket_free(sk);
return NM_CAST_ALIGN(NMEtherAddr *, ret);
err:
if (sk)
nl_socket_free(sk);
return NULL;
}
/*****************************************************************************/
static gboolean
ip4_address_add(NMPlatform *platform,
int ifindex,

View File

@@ -25,6 +25,9 @@ GType nm_linux_platform_get_type(void);
struct _NMDedupMultiIndex;
NMEtherAddr **
nm_linux_platform_get_link_fdb_table(NMPlatform *platform, int *ifindexes, guint ifindexes_len);
NMPlatform *nm_linux_platform_new(struct _NMDedupMultiIndex *multi_idx,
gboolean log_with_ptr,
gboolean netns_support,