about summary refs log tree commit diff
path: root/src/libnm-platform/nm-linux-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-platform/nm-linux-platform.c')
-rw-r--r--src/libnm-platform/nm-linux-platform.c123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index bd495fe2..90a34102 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;
@@ -10352,6 +10356,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,