From 69f3493670140a7a9be28412a97f71d685a14ce4 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. --- 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 94d79da86..48a50a599 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -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 diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index f5ebc69ea..a352860d4 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -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); From a63eec924cccf324b82a66fccc9d72d49bb2adfc 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. --- 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 1ca3e304e..5f0e515a4 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 ff855784e..754c21aec 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -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; From 00f47efcb21ccf0a5b0ff943e2d0ad51cf807865 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. --- 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 f12032aff..3510e68a8 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; @@ -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, diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h index 08135a4ac..3f591b7f1 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 3f2f922dd94347c4a38f6abb7fbf1926e62fb542 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') --- 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 2d9b5598a..49ca156d5 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 083189bb7..2a415843d 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 27c784059..ba3e07c9a 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" @@ -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); diff --git a/src/core/nm-bond-manager.c b/src/core/nm-bond-manager.c index c33e043d1..2f7fe36c1 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 92a89f0b9..78ada15ba 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);