about summary refs log tree commit diff
path: root/src/libnm-platform
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-12-25 20:36:29 +0100
committerMichael Biebl <biebl@debian.org>2024-12-25 20:36:29 +0100
commite465722b908aa870bdc293b9a417d3c15294aa6d (patch)
tree7c959d3b73f427d6c40522a7464eaa4531ef2f05 /src/libnm-platform
parent56928734cbcf1d3a02fae4f152a541df1b04e04a (diff)
New upstream version 1.50.1 upstream/1.50.1
Diffstat (limited to 'src/libnm-platform')
-rw-r--r--src/libnm-platform/nm-linux-platform.c123
-rw-r--r--src/libnm-platform/nm-linux-platform.h3
-rw-r--r--src/libnm-platform/nm-platform.c86
-rw-r--r--src/libnm-platform/nm-platform.h5
-rw-r--r--src/libnm-platform/nmp-base.h10
5 files changed, 216 insertions, 11 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,
diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h
index 08135a4a..3f591b7f 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,
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index af04f29f..658efadb 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -61,6 +61,8 @@ G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == _NM_UTILS_H
 G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UTILS_HWADDR_LEN_MAX);
 G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX);
 
+static int _route_objs_cmp_values(gconstpointer a, gconstpointer b, gpointer user_data);
+
 static const char *
 _nmp_link_port_data_to_string(NMPortKind                    port_kind,
                               const NMPlatformLinkPortData *port_data,
@@ -4872,11 +4874,24 @@ nm_platform_ip_address_get_prune_list(NMPlatform            *self,
     return result;
 }
 
+static gboolean
+_route_obj_find_bsearch(GPtrArray *sorted_routes_objs, const NMPObject *route_obj)
+{
+    gssize pos =
+        nm_ptrarray_find_bsearch((gconstpointer *) sorted_routes_objs->pdata,
+                                 sorted_routes_objs->len,
+                                 route_obj,
+                                 _route_objs_cmp_values,
+                                 GINT_TO_POINTER((int) NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY));
+    return pos >= 0;
+}
+
 GPtrArray *
 nm_platform_ip_route_get_prune_list(NMPlatform            *self,
                                     int                    addr_family,
                                     int                    ifindex,
-                                    NMIPRouteTableSyncMode route_table_sync)
+                                    NMIPRouteTableSyncMode route_table_sync,
+                                    GPtrArray             *sorted_old_routes_objs)
 {
     NMPLookup                    lookup;
     GPtrArray                   *routes_prune = NULL;
@@ -4890,10 +4905,21 @@ nm_platform_ip_route_get_prune_list(NMPlatform            *self,
     nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
     nm_assert(NM_IN_SET(route_table_sync,
                         NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
-                        NM_IP_ROUTE_TABLE_SYNC_MODE_FULL,
+                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL,
+                        NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES,
                         NM_IP_ROUTE_TABLE_SYNC_MODE_ALL,
                         NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE));
 
+    if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES) {
+        nm_assert(sorted_old_routes_objs);
+        nm_assert(nm_utils_ptrarray_is_sorted(
+            (gconstpointer *) sorted_old_routes_objs->pdata,
+            sorted_old_routes_objs->len,
+            FALSE,
+            _route_objs_cmp_values,
+            GINT_TO_POINTER((int) NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)));
+    }
+
     nmp_lookup_init_object_by_ifindex(&lookup,
                                       NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)),
                                       ifindex);
@@ -4915,7 +4941,12 @@ nm_platform_ip_route_get_prune_list(NMPlatform            *self,
             if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx)))
                 continue;
             break;
-        case NM_IP_ROUTE_TABLE_SYNC_MODE_FULL:
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES:
+            if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx))
+                && !_route_obj_find_bsearch(sorted_old_routes_objs, obj))
+                continue;
+            break;
+        case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL:
             if (nm_platform_ip_route_get_effective_table(&rt->rx) == RT_TABLE_LOCAL)
                 continue;
             break;
@@ -5284,7 +5315,8 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
         routes_prune = nm_platform_ip_route_get_prune_list(self,
                                                            AF_INET,
                                                            ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE);
+                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE,
+                                                           NULL);
         success &= nm_platform_ip_route_sync(self, AF_INET, ifindex, NULL, routes_prune, NULL);
     }
     if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) {
@@ -5293,7 +5325,8 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
         routes_prune = nm_platform_ip_route_get_prune_list(self,
                                                            AF_INET6,
                                                            ifindex,
-                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE);
+                                                           NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE,
+                                                           NULL);
         success &= nm_platform_ip_route_sync(self, AF_INET6, ifindex, NULL, routes_prune, NULL);
     }
     return success;
@@ -6533,9 +6566,9 @@ nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *b
 const char *
 nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len)
 {
-    char  str_local[30];
+    char  str_local[30 + NM_INET_ADDRSTRLEN];
     char  str_local1[NM_INET_ADDRSTRLEN];
-    char  str_remote[30];
+    char  str_remote[30 + NM_INET_ADDRSTRLEN];
     char  str_remote1[NM_INET_ADDRSTRLEN];
     char  str_ttl[30];
     char  str_tclass[30];
@@ -8767,6 +8800,45 @@ nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformL
     return 0;
 }
 
+static int
+_route_objs_cmp_values(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+    const NMPObject         *a_obj    = a;
+    const NMPObject         *b_obj    = b;
+    NMPlatformIPRouteCmpType cmp_type = GPOINTER_TO_INT(user_data);
+
+    nm_assert(a_obj && b_obj);
+    nm_assert(NMP_OBJECT_CAST_IP_ROUTE(a_obj) && NMP_OBJECT_CAST_IP_ROUTE(b_obj));
+
+    if (NMP_OBJECT_GET_ADDR_FAMILY(a_obj) != NMP_OBJECT_GET_ADDR_FAMILY(b_obj)) {
+        return NMP_OBJECT_GET_ADDR_FAMILY(a_obj) == AF_INET ? 1 : -1;
+    } else if (NMP_OBJECT_GET_ADDR_FAMILY(a_obj) == AF_INET) {
+        return nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(a_obj),
+                                         NMP_OBJECT_CAST_IP4_ROUTE(b_obj),
+                                         cmp_type);
+    } else {
+        return nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(a_obj),
+                                         NMP_OBJECT_CAST_IP6_ROUTE(b_obj),
+                                         cmp_type);
+    }
+}
+
+static int
+_route_objs_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+    nm_assert(a && b);
+
+    return _route_objs_cmp_values(*((const NMPObject **) a), *((const NMPObject **) b), user_data);
+}
+
+void
+nm_platform_route_objs_sort(GPtrArray *routes_objs, NMPlatformIPRouteCmpType cmp_type)
+{
+    nm_assert(routes_objs);
+
+    g_ptr_array_sort_with_data(routes_objs, _route_objs_cmp, GINT_TO_POINTER((int) cmp_type));
+}
+
 void
 nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj,
                                        gboolean                      for_id,
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index e33be813..22bf0fdb 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -2389,7 +2389,8 @@ int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatf
 GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform            *self,
                                                int                    addr_family,
                                                int                    ifindex,
-                                               NMIPRouteTableSyncMode route_table_sync);
+                                               NMIPRouteTableSyncMode route_table_sync,
+                                               GPtrArray             *old_routes_objs);
 
 gboolean nm_platform_ip_route_sync(NMPlatform *self,
                                    int         addr_family,
@@ -2495,6 +2496,8 @@ int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatf
 
 GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex);
 
+void nm_platform_route_objs_sort(GPtrArray *routes_objs, NMPlatformIPRouteCmpType cmp_type);
+
 int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
                               const NMPlatformIP4Route *b,
                               NMPlatformIPRouteCmpType  cmp_type);
diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h
index c7d487e2..3784a78e 100644
--- a/src/libnm-platform/nmp-base.h
+++ b/src/libnm-platform/nmp-base.h
@@ -211,8 +211,11 @@ nmp_object_type_to_flags(NMPObjectType obj_type)
  * @NM_IP_ROUTE_TABLE_SYNC_MODE_NONE: indicate an invalid setting.
  * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: only the main table is synced. For all
  *   other tables, NM won't delete any extra routes.
- * @NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: NM will sync all tables, except the
- *   local table (255).
+ * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES: only the main table is synced,
+ *   plus individual routes in other tables added by NM, leaving routes that
+ *   were not added by NM untouched.
+ * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL: NM will sync all tables, except
+ *   the local table (255).
  * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL: NM will sync all tables, including the
  *   local table (255).
  * @NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE: NM will sync all tables (including
@@ -222,7 +225,8 @@ nmp_object_type_to_flags(NMPObjectType obj_type)
 typedef enum {
     NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
     NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
-    NM_IP_ROUTE_TABLE_SYNC_MODE_FULL,
+    NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN_AND_NM_ROUTES,
+    NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL,
     NM_IP_ROUTE_TABLE_SYNC_MODE_ALL,
     NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE,
 } NMIPRouteTableSyncMode;