merge: branch 'ff/garp_bond_slb'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2057
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user