From e24a6088c7fc666fb23e7d1493b5275a74263abb Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 24 Oct 2024 03:05:27 +0200 Subject: [PATCH 1/4] l3cfg: add helper function to fetch all the IPv4 configured addresses This function would be useful when performing operations related to the IPv4 addresses configured on the l3cfg. E.g this function will be used for getting the IPv4 to announce on a GARP on bonding-slb when one of the ports failover. (cherry picked from commit 69f3493670140a7a9be28412a97f71d685a14ce4) (cherry picked from commit bfe2047acc9d2b9146c19165d57d90caf050c052) --- src/core/nm-l3cfg.c | 24 ++++++++++++++++++++++++ src/core/nm-l3cfg.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 2c9779914c..86c5de1197 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -5390,6 +5390,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 diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 241fde0247..8890c0405f 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -441,6 +441,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); From 73aa5b47fa948f03c2482fc1b6d6070ef722f541 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Wed, 18 Dec 2024 11:23:25 +0100 Subject: [PATCH 2/4] glib-aux: add nm_ether_addr_hash() helper Add a hash generation helper for NMEtherAddr struct. This can be used for HashTables containing pointers to NMEtherAddr structs. (cherry picked from commit a63eec924cccf324b82a66fccc9d72d49bb2adfc) (cherry picked from commit 6371802087caf883e6880e4049c578ff1b7318e0) --- src/libnm-glib-aux/nm-shared-utils.c | 11 +++++++++++ src/libnm-glib-aux/nm-shared-utils.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 421e4d1bef..25c78fd31a 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -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); +} + /*****************************************************************************/ /** diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index ca9feb6080..70f1912e3c 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -240,6 +240,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; From ba3cff0ffdea1ae1990bda748064b44f4a38b170 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 7 Nov 2024 09:09:50 +0100 Subject: [PATCH 3/4] linux-platform: add helper function to query FDB table The function introduced queries the FDB table via netlink socket. It accepts a list of ifindexes to filter out the FDB content not related to it. It returns an array of MAC addresses. To cltarify this function is unusually exposed directly on nm-linux-platform.h as we don't want this be part of the whole NMPlatform object or cache. This, is an exception to the rule to simplify the integration of this functionality on NetworkManager. In addition, it also doesn't use the async mechanism that is widely used on netlink communication across nm-linux-platform. Again, the reason is to simplify its use, as async communication won't provide a benefit to the use cases we have planned for this, i.e balance-slb RARP announcing. (cherry picked from commit 00f47efcb21ccf0a5b0ff943e2d0ad51cf807865) (cherry picked from commit 8af7493627afd93f02edd63e9e73d74d98d9af88) --- src/libnm-platform/nm-linux-platform.c | 123 +++++++++++++++++++++++++ src/libnm-platform/nm-linux-platform.h | 3 + 2 files changed, 126 insertions(+) diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 53f678fc0c..02686f7261 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -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; @@ -10252,6 +10256,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, diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h index 08135a4acb..3f591b7f1a 100644 --- a/src/libnm-platform/nm-linux-platform.h +++ b/src/libnm-platform/nm-linux-platform.h @@ -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, From 8f9b2f22bba0a7c8722623228a7752ea8877ef21 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 7 Nov 2024 09:10:20 +0100 Subject: [PATCH 4/4] bonding: send ARP announcement on bonding-slb link/carrier down When a bond in balance-slb is created, the ports are enabled or disabled based on carrier and link state. If the link/carrier goes down, the port becomes disabled and we must make sure the MAC tables of the switches are updated properly so the traffic is redirected. In order to solve this, we send a GARP or RARP broadcast packet on the bond. This fix cover 3 different balance-slb scenarios. Scenario 1: The bond in balance-slb mode has IPv4 address configured and some ports connected. Here the bond is acting like active-backup as the packets will always have as source MAC the address of the bond interface. When a port goes down, NetworkManager will send a GARP broadcast announcing the address configured on the bond with the MAC address configured on the port. Scenario 2: The bond in balance-slb mode is connected to a bridge and has some ports connected. The bridge has IPv4 configured. When a port goes down, NetworkManager will send a GARP broadcast announcing the address configured on the bridge with the MAC address configured on the port. Scenario 3: The bond in balance-slb mode is connected to a bridge and has some ports connected. The bridge does not have IP configuration and therefore everything is L2. When a port goes down, NetworkManager will query the FDB table and filter the entries by the ones belonging to the bridge and the bond ifindexes. Then, it will send a RARP broadcast announcing every learned MAC address from FDB. Fixes: e9268e392418 ('firewall: add mlag firewall utils for multi chassis link aggregation (MLAG) for bonding-slb') (cherry picked from commit 3f2f922dd94347c4a38f6abb7fbf1926e62fb542) (cherry picked from commit e9e1768c37c54f5d869776de1965fb0d41b6f3a2) --- src/core/devices/nm-device-bond.c | 85 ++++++++++++++++++++++ src/core/devices/nm-device-bond.h | 3 + src/core/devices/nm-device.c | 34 +++++++++ src/core/nm-bond-manager.c | 114 ++++++++++++++++++++++++++++++ src/core/nm-bond-manager.h | 6 ++ 5 files changed, 242 insertions(+) diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 06a02d1a7a..f17bd852ac 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -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) { diff --git a/src/core/devices/nm-device-bond.h b/src/core/devices/nm-device-bond.h index 083189bb74..2a415843d4 100644 --- a/src/core/devices/nm-device-bond.h +++ b/src/core/devices/nm-device-bond.h @@ -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 */ diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index b534800a08..0940aa583d 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -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" @@ -7339,10 +7340,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; @@ -7425,6 +7428,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)) @@ -7441,6 +7446,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); diff --git a/src/core/nm-bond-manager.c b/src/core/nm-bond-manager.c index 71cd4ee732..f24c816377 100644 --- a/src/core/nm-bond-manager.c +++ b/src/core/nm-bond-manager.c @@ -6,8 +6,13 @@ #include +#include +#include +#include + #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 diff --git a/src/core/nm-bond-manager.h b/src/core/nm-bond-manager.h index 92a89f0b92..78ada15ba3 100644 --- a/src/core/nm-bond-manager.h +++ b/src/core/nm-bond-manager.h @@ -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);