about summary refs log tree commit diff
path: root/src/libnm-platform
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
committerMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
commit34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch)
tree4e6220877828be4c6f261de09ec0cb2d80e32389 /src/libnm-platform
parentbba2e4b4de668db525cbfdfc35292e5a0b51671a (diff)
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'src/libnm-platform')
-rw-r--r--src/libnm-platform/README.md22
-rw-r--r--src/libnm-platform/nm-linux-platform.c69
-rw-r--r--src/libnm-platform/nm-netlink.c9
-rw-r--r--src/libnm-platform/nm-platform.c17
-rw-r--r--src/libnm-platform/nmp-object.c22
-rw-r--r--src/libnm-platform/wifi/nm-wifi-utils-nl80211.c94
6 files changed, 145 insertions, 88 deletions
diff --git a/src/libnm-platform/README.md b/src/libnm-platform/README.md
new file mode 100644
index 00000000..c8e7cf76
--- /dev/null
+++ b/src/libnm-platform/README.md
@@ -0,0 +1,22 @@
+libnm-platform
+==============
+
+A static helper library that provides `NMPlatform` and other utils.
+This is NetworkManager's internal netlink library, but also contains
+helpers for sysfs, ethtool and other kernel APIs.
+
+`NMPlaform` is also a cache of objects of the netlink API: `NMPCache`
+and `NMPObject`. These objects are used throughout NetworkManager
+also for generally tracking information about these types. For example,
+`NMPlatformIP4Address` (the public part of a certain type of `NMPObject`)
+is not only used to track platform addresses from netlink in the cache,
+but to track information about IPv4 addresses in general.
+
+This depends on the following helper libraries
+
+  - [../libnm-std-aux/](../libnm-std-aux/)
+  - [../libnm-base/](../libnm-base/)
+  - [../libnm-glib-aux/](../libnm-glib-aux/)
+  - [../libnm-udev-aux/](../libnm-udev-aux/)
+  - [../libnm-log-core/](../libnm-log-core/)
+  - [../linux-headers/](../linux-headers/)
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index 9ecac2d9..5b595a9b 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -3903,6 +3903,34 @@ _new_from_nl_addr(const struct nlmsghdr *nlh, gboolean id_only)
     return g_steal_pointer(&obj);
 }
 
+static gboolean
+ip_route_is_tracked(guint8 proto, guint8 type)
+{
+    if (proto > RTPROT_STATIC && !NM_IN_SET(proto, RTPROT_DHCP, RTPROT_RA)) {
+        /* We ignore certain rtm_protocol, because NetworkManager would only ever
+         * configure certain protocols. Other routes are not configured by NetworkManager
+         * and we don't track them in the platform cache.
+         *
+         * This is to help with the performance overhead of a huge number of
+         * routes, for example with the bird BGP software, that adds routes
+         * with RTPROT_BIRD protocol. */
+        return FALSE;
+    }
+
+    if (!NM_IN_SET(type,
+                   RTN_UNICAST,
+                   RTN_LOCAL,
+                   RTN_BLACKHOLE,
+                   RTN_UNREACHABLE,
+                   RTN_PROHIBIT,
+                   RTN_THROW)) {
+        /* Certain route types are ignored and not placed into the cache. */
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */
 static NMPObject *
 _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter)
@@ -3963,6 +3991,16 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
      * only handle ~supported~ routes.
      *****************************************************************/
 
+    /* If it's a route that we don't need to track, abort here to avoid unnecessary
+     * memory allocations to create the nmp_object. However, if the message has the
+     * NLM_F_REPLACE flag, it might be replacing a route that we were tracking so we
+     * have to stop tracking it. That means that we have to process all messages with
+     * NLM_F_REPLACE. See nmp_cache_update_netlink_route().
+     */
+    if (!ip_route_is_tracked(rtm->rtm_protocol, rtm->rtm_type)
+        && !(nlh->nlmsg_flags & NLM_F_REPLACE))
+        return NULL;
+
     addr_family = rtm->rtm_family;
 
     if (addr_family == AF_INET)
@@ -5519,39 +5557,18 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route)
 static gboolean
 ip_route_is_alive(const NMPlatformIPRoute *route)
 {
-    guint8 prot;
+    guint8 proto, type;
 
     nm_assert(route);
     nm_assert(route->rt_source >= NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC
               && route->rt_source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST);
 
-    prot = route->rt_source - 1;
-
-    nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source);
-
-    if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) {
-        /* We ignore certain rtm_protocol, because NetworkManager would only ever
-         * configure certain protocols. Other routes are not configured by NetworkManager
-         * and we don't track them in the platform cache.
-         *
-         * This is to help with the performance overhead of a huge number of
-         * routes, for example with the bird BGP software, that adds routes
-         * with RTPROT_BIRD protocol. */
-        return FALSE;
-    }
+    proto = route->rt_source - 1;
+    type  = nm_platform_route_type_uncoerce(route->type_coerced);
 
-    if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced),
-                   RTN_UNICAST,
-                   RTN_LOCAL,
-                   RTN_BLACKHOLE,
-                   RTN_UNREACHABLE,
-                   RTN_PROHIBIT,
-                   RTN_THROW)) {
-        /* Certain route types are ignored and not placed into the cache. */
-        return FALSE;
-    }
+    nm_assert(nmp_utils_ip_config_source_from_rtprot(proto) == route->rt_source);
 
-    return TRUE;
+    return ip_route_is_tracked(proto, type);
 }
 
 /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c
index 5684b8cd..6d153128 100644
--- a/src/libnm-platform/nm-netlink.c
+++ b/src/libnm-platform/nm-netlink.c
@@ -4,6 +4,7 @@
  */
 
 #include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
+#include "libnm-glib-aux/nm-random-utils.h"
 
 #include "nm-netlink.h"
 
@@ -1105,7 +1106,7 @@ nl_socket_new(struct nl_sock **out_sk,
 {
     nm_auto_nlsock struct nl_sock *sk = NULL;
     nm_auto_close int              fd = -1;
-    time_t                         t;
+    unsigned                       seq_init;
     int                            err;
     int                            nmerr;
     socklen_t                      addrlen;
@@ -1121,7 +1122,7 @@ nl_socket_new(struct nl_sock **out_sk,
     if (fd < 0)
         return -nm_errno_from_native(errno);
 
-    t = time(NULL);
+    nm_random_get_bytes(&seq_init, sizeof(seq_init));
 
     sk  = g_slice_new(struct nl_sock);
     *sk = (struct nl_sock){
@@ -1138,8 +1139,8 @@ nl_socket_new(struct nl_sock **out_sk,
                 .nl_family = AF_NETLINK,
                 .nl_groups = 0,
             },
-        .s_seq_expect = t,
-        .s_seq_next   = t,
+        .s_seq_expect = seq_init,
+        .s_seq_next   = seq_init,
         .s_bufsize    = 0,
         .s_msg_peek   = !NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK),
         .s_auto_ack   = TRUE,
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index b89b0359..cd5a54bb 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -6147,9 +6147,9 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
         link->initialized ? " init" : " not-init",
         link->inet6_addr_gen_mode_inv ? " addrgenmode " : "",
         link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str(
-            _nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
-            str_addrmode,
-            sizeof(str_addrmode))
+                                            _nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
+                                            str_addrmode,
+                                            sizeof(str_addrmode))
                                       : "",
         str_address[0] ? " addr " : "",
         str_address[0] ? str_address : "",
@@ -7385,11 +7385,12 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
                                                        route->lock_mtu ? "lock " : "",
                                                        route->mtu)
                                       : "",
-        route->rt_pref ? nm_sprintf_buf(
-            str_pref,
-            " pref %s",
-            nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
-                       : "");
+        route->rt_pref
+            ? nm_sprintf_buf(
+                  str_pref,
+                  " pref %s",
+                  nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
+            : "");
 
     return buf;
 }
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index 4090da71..cb4e9764 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -2988,6 +2988,13 @@ nmp_cache_update_netlink_route(NMPCache         *cache,
          * Since we don't cache all routes (see "route_is_alive"), we cannot know
          * with certainty which route was replaced.
          *
+         * For example, the kernel might have 3 similar routes (same WEAK_ID), one
+         * of which is not tracked by us so we don't have it into the cache. If we
+         * receive a route replace message, we don't know to what of the 3 routes
+         * it affects (one of the 3 we don't even know that exists). Moreover, if
+         * we only have one route on cache, we don't know if the replace is for a
+         * different one that we don't track.
+         *
          * Even if we would cache *all* routes (which we cannot, if kernel adds new
          * routing features that modify the known nmp_object_id_equal()), it would
          * be hard to find the right route that was replaced. Well, probably we
@@ -3002,15 +3009,14 @@ nmp_cache_update_netlink_route(NMPCache         *cache,
          * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860
          *
          * We need to resync.
+         *
+         * However, a resync is expensive. Think of a routing daemon that updates
+         * hundreds of routes per second, the performance penalty is huge. We can
+         * optimize it: if we don't have any matching route on cache (by WEAK_ID),
+         * we don't have anything to replace and we don't need a full resync, but
+         * only to add or discard the new route as usual.
          */
-        if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE
-            && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
-            /* For IPv4, we can do a small optimization. We skip the resync, if we have
-             * no conflicting routes (by weak-id).
-             *
-             * This optimization does not work for IPv6 (maybe should be fixed).
-             */
-        } else {
+        if (nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
             entry_replace   = NULL;
             resync_required = TRUE;
             goto out;
diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
index 6109849a..3c00898a 100644
--- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
+++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
@@ -567,6 +567,7 @@ struct nl80211_device_info {
     int                 phy;
     Nl80211Freq        *freqs;
     int                 num_freqs;
+    int                 num_freqs_alloc;
     guint32             freq;
     guint32             caps;
     gboolean            can_scan;
@@ -610,7 +611,6 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
     struct nlattr              *nl_freq;
     int                         rem_freq;
     int                         rem_band;
-    guint                       num_alloc;
 
 #ifdef NL80211_FREQUENCY_ATTR_NO_IR
     G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR
@@ -622,22 +622,16 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
     if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
         return NL_SKIP;
 
-    if (tb[NL80211_ATTR_WIPHY] == NULL || tb[NL80211_ATTR_WIPHY_BANDS] == NULL)
+    if (!tb[NL80211_ATTR_WIPHY])
         return NL_SKIP;
 
     info->phy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
 
     if (tb[NL80211_ATTR_WIPHY_FREQ])
         info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-    else
-        info->freq = 0;
 
-    if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) {
+    if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
         info->can_scan_ssid = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0;
-    } else {
-        /* old kernel that only had mac80211, so assume it can */
-        info->can_scan_ssid = TRUE;
-    }
 
     if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
         struct nlattr *nl_cmd;
@@ -664,51 +658,57 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
         }
     }
 
-    /* Read supported frequencies */
-    num_alloc       = 32;
-    info->num_freqs = 0;
-    info->freqs     = g_new(Nl80211Freq, num_alloc);
+    if (tb[NL80211_ATTR_WIPHY_BANDS]) {
+        /* Read supported frequencies */
 
-    nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-        if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
-            return NL_SKIP;
+        if (!info->freqs) {
+            info->num_freqs       = 0;
+            info->num_freqs_alloc = 32;
+            info->freqs           = g_new(Nl80211Freq, info->num_freqs_alloc);
+        }
 
-        nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-            Nl80211Freq *f;
+        nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+            if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0)
+                return NL_SKIP;
 
-            if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
+            if (!tb_band[NL80211_BAND_ATTR_FREQS])
                 continue;
 
-            if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                continue;
+            nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+                Nl80211Freq *f;
 
-            if (info->num_freqs >= num_alloc) {
-                num_alloc *= 2;
-                info->freqs = g_renew(Nl80211Freq, info->freqs, num_alloc);
-            }
+                if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
+                    continue;
+
+                if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                    continue;
 
-            f  = &info->freqs[info->num_freqs];
-            *f = (Nl80211Freq){
-                .freq     = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
-                .disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
-                .no_ir    = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
-            };
+                if (info->num_freqs >= info->num_freqs_alloc) {
+                    info->num_freqs_alloc *= 2;
+                    info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs_alloc);
+                }
 
-            info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
+                f  = &info->freqs[info->num_freqs];
+                *f = (Nl80211Freq){
+                    .freq     = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
+                    .disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
+                    .no_ir    = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
+                };
 
-            if (f->freq >= 2401 && f->freq <= 2495)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
-            if (f->freq >= 5150 && f->freq <= 5895)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
-            if (f->freq >= 5925 && f->freq <= 7125)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
+                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
 
-            info->num_freqs++;
+                if (f->freq >= 2401 && f->freq <= 2495)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
+                if (f->freq >= 5150 && f->freq < 5950)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
+                if (f->freq >= 5950 && f->freq <= 7125)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
+
+                info->num_freqs++;
+            }
         }
     }
 
-    info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs);
-
     /* Read security/encryption support */
     if (tb[NL80211_ATTR_CIPHER_SUITES]) {
         guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]);
@@ -874,7 +874,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
 
     self->phy = -1;
 
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0);
+    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, NLM_F_DUMP);
+    NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
+
+    device_info.can_scan_ssid = TRUE;
 
     device_info.self = self;
     if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) {
@@ -882,6 +885,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
         return NULL;
     }
 
+    if (device_info.freqs) {
+        device_info.freqs = g_renew(Nl80211Freq, device_info.freqs, device_info.num_freqs);
+    }
+
     if (!device_info.success) {
         _LOGD("NL80211_CMD_GET_WIPHY request indicated failure");
         return NULL;
@@ -915,4 +922,7 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
 
     _LOGD("using nl80211 for Wi-Fi device control");
     return (NMWifiUtils *) g_steal_pointer(&self);
+
+nla_put_failure:
+    g_return_val_if_reached(NULL);
 }