diff --git a/NEWS b/NEWS index 5759157b42..4b618a57d2 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ Overview of changes since NetworkManager-1.44 * Honor udev property ID_NET_MANAGED_BY to only manage an interface when set to "org.freedesktop.NetworkManager". * Drop build support with Python2. Python3 is now required. +* nmcli: limit number of printed addresses/routes in `nmcli` overview to 10. +* Limit number of exported IP addresses/routes on D-Bus to 100 to reduce + performance cost. Also, D-Bus updates for addresses/routes are now rate + limited to 3 per second. ============================================= NetworkManager-1.44 diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index 84379cdf95..e65f7ddae5 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -1558,7 +1558,8 @@ nm_utils_ip_addresses_to_dbus(int addr_family, char addr_str[NM_INET_ADDRSTRLEN]; NMDedupMultiIter iter; const NMPObject *obj; - guint i; + const gsize MAX_ADDRESSES = 100; + gsize i; nm_assert_addr_family(addr_family); @@ -1580,6 +1581,11 @@ nm_utils_ip_addresses_to_dbus(int addr_family, nm_platform_dedup_multi_iter_next_obj(&iter, &obj, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4))) { const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj); + if (i > MAX_ADDRESSES) { + /* Limited. The rest is hidden. */ + break; + } + if (out_address_data) { GVariantBuilder addr_builder; gconstpointer p; @@ -1669,6 +1675,8 @@ nm_utils_ip_routes_to_dbus(int addr_family, GVariantBuilder builder_data; GVariantBuilder builder_legacy; char addr_str[NM_INET_ADDRSTRLEN]; + const gsize MAX_ROUTES = 100; + gsize i; nm_assert_addr_family(addr_family); @@ -1681,6 +1689,7 @@ nm_utils_ip_routes_to_dbus(int addr_family, g_variant_builder_init(&builder_legacy, G_VARIANT_TYPE("a(ayuayu)")); } + i = 0; nm_dedup_multi_iter_init(&iter, head_entry); while (nm_platform_dedup_multi_iter_next_obj(&iter, &obj, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))) { const NMPlatformIPXRoute *r = NMP_OBJECT_CAST_IPX_ROUTE(obj); @@ -1699,6 +1708,13 @@ nm_utils_ip_routes_to_dbus(int addr_family, if (r->rx.type_coerced != nm_platform_route_type_coerce(RTN_UNICAST)) continue; + if (i >= MAX_ROUTES) { + /* Limited. The rest is hidden. */ + break; + } + + i++; + if (out_route_data) { GVariantBuilder route_builder; gconstpointer gateway; diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index bec67d9a46..48f59bd427 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -16,6 +16,10 @@ /*****************************************************************************/ +#define NOTIFY_PLATFORM_RATELIMIT_MSEC 333 + +/*****************************************************************************/ + GType nm_ip4_config_get_type(void); GType nm_ip6_config_get_type(void); @@ -67,6 +71,73 @@ _value_set_variant_as(GValue *value, const char *const *strv, guint len) /*****************************************************************************/ +static void +_notify_platform_handle(NMIPConfig *self, gint64 now_msec) +{ + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + guint32 obj_type_flags; + + nm_clear_g_source_inst(&priv->notify_platform_timeout_source); + + priv->notify_platform_rlimited_until_msec = now_msec + NOTIFY_PLATFORM_RATELIMIT_MSEC; + + obj_type_flags = nm_steal_int(&priv->notify_platform_obj_type_flags); + + nm_assert(obj_type_flags != 0u); + + _handle_platform_change(self, obj_type_flags, FALSE); +} + +static gboolean +_notify_platform_cb(gpointer user_data) +{ + _notify_platform_handle(user_data, nm_utils_get_monotonic_timestamp_msec()); + return G_SOURCE_CONTINUE; +} + +static void +_notify_platform(NMIPConfig *self, guint32 obj_type_flags) +{ + const int addr_family = nm_ip_config_get_addr_family(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gint64 now_msec; + + obj_type_flags &= (nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) + | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))); + + if (obj_type_flags == 0u) + return; + + priv->notify_platform_obj_type_flags |= obj_type_flags; + + if (priv->notify_platform_timeout_source) { + /* We are currently rate limited. Don't bother to check whether + * (now_msec < priv->notify_platform_rlimited_until_msec), just always + * delegate to the timeout handler. It is scheduled with a lower idle + * priority, so we want that additional backoff. */ + return; + } + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + + if (now_msec < priv->notify_platform_rlimited_until_msec) { + priv->notify_platform_timeout_source = nm_g_source_attach( + /* Schedule with a low G_PRIORITY_LOW. */ + nm_g_timeout_source_new(priv->notify_platform_rlimited_until_msec - now_msec, + G_PRIORITY_LOW - 10, + _notify_platform_cb, + self, + NULL), + NULL); + return; + } + + _notify_platform_handle(self, now_msec); +} + +/*****************************************************************************/ + static void _l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMIPConfig *self) { @@ -76,7 +147,7 @@ _l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMIPCo _handle_l3cd_changed(self, notify_data->l3cd_changed.l3cd_new); break; case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: - _handle_platform_change(self, notify_data->platform_change_on_idle.obj_type_flags, FALSE); + _notify_platform(self, notify_data->platform_change_on_idle.obj_type_flags); break; default: break; @@ -207,6 +278,8 @@ finalize(GObject *object) NMIPConfig *self = NM_IP_CONFIG(object); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + nm_clear_g_source_inst(&priv->notify_platform_timeout_source); + nm_clear_g_signal_handler(priv->l3cfg, &priv->l3cfg_notify_id); g_object_unref(priv->l3cfg); diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index 0dcea83b51..47a40bd223 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -32,7 +32,10 @@ struct _NMIPConfigPrivate { struct { const NMPObject *best_default_route; } v_gateway; - gulong l3cfg_notify_id; + GSource *notify_platform_timeout_source; + gint64 notify_platform_rlimited_until_msec; + gulong l3cfg_notify_id; + guint32 notify_platform_obj_type_flags; }; struct _NMIPConfig { diff --git a/src/nmcli/general.c b/src/nmcli/general.c index 0d8ddc47fe..6fc8184cb0 100644 --- a/src/nmcli/general.c +++ b/src/nmcli/general.c @@ -1411,36 +1411,57 @@ device_overview(NmCli *nmc, NMDevice *device) static void ac_overview(NmCli *nmc, NMActiveConnection *ac) { - GString *outbuf = g_string_sized_new(80); - NMIPConfig *ip; - nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); + nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE); + const guint MAX_ADDRESSES = 10; + const guint MAX_ROUTES = 10; + int IS_IPv4; if (nm_active_connection_get_controller(ac)) { - g_string_append_printf(outbuf, - "%s %s, ", - _("master"), - nm_device_get_iface(nm_active_connection_get_controller(ac))); + nm_str_buf_append_printf(&str, + "%s %s, ", + _("master"), + nm_device_get_iface(nm_active_connection_get_controller(ac))); } if (nm_active_connection_get_vpn(ac)) - g_string_append_printf(outbuf, "%s, ", _("VPN")); + nm_str_buf_append_printf(&str, "%s, ", _("VPN")); if (nm_active_connection_get_default(ac)) - g_string_append_printf(outbuf, "%s, ", _("ip4 default")); + nm_str_buf_append_printf(&str, "%s, ", _("ip4 default")); if (nm_active_connection_get_default6(ac)) - g_string_append_printf(outbuf, "%s, ", _("ip6 default")); - if (outbuf->len >= 2) { - g_string_truncate(outbuf, outbuf->len - 2); - nmc_print("\t%s\n", outbuf->str); + nm_str_buf_append_printf(&str, "%s, ", _("ip6 default")); + if (str.len >= 2) { + nm_str_buf_set_size(&str, str.len - 2u, TRUE, FALSE); + nmc_print("\t%s\n", nm_str_buf_get_str(&str)); } - ip = nm_active_connection_get_ip4_config(ac); - if (ip) { + nm_str_buf_reset(&str); + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + NMIPConfig *ip; const GPtrArray *p; - int i; + guint i; + + ip = IS_IPv4 ? nm_active_connection_get_ip4_config(ac) + : nm_active_connection_get_ip6_config(ac); + if (!ip) + continue; p = nm_ip_config_get_addresses(ip); for (i = 0; i < p->len; i++) { NMIPAddress *a = p->pdata[i]; - nmc_print("\tinet4 %s/%d\n", nm_ip_address_get_address(a), nm_ip_address_get_prefix(a)); + + nmc_print("\tinet%c %s/%d\n", + IS_IPv4 ? '4' : '6', + nm_ip_address_get_address(a), + nm_ip_address_get_prefix(a)); + + if (i >= MAX_ADDRESSES - 1u && p->len - i > 2u) { + /* Print always at least MAX_ADDRESSES fully. + * If there are MAX_ADDRESSES+1 addresses, print them all fully. + * If there are more addresses, print MAX_ADDRESSES fully, and a + * "more" line. */ + nmc_print("\tinet%c ... more\n", IS_IPv4 ? '4' : '6'); + break; + } } p = nm_ip_config_get_routes(ip); @@ -1450,33 +1471,14 @@ ac_overview(NmCli *nmc, NMActiveConnection *ac) nm_str_buf_reset(&str); _nm_ip_route_to_string(a, &str); - nmc_print("\troute4 %s\n", nm_str_buf_get_str(&str)); + nmc_print("\troute%c %s\n", IS_IPv4 ? '4' : '6', nm_str_buf_get_str(&str)); + + if (i >= MAX_ROUTES - 1u && p->len - i > 2u) { + nmc_print("\troute%c ... more\n", IS_IPv4 ? '4' : '6'); + break; + } } } - - ip = nm_active_connection_get_ip6_config(ac); - if (ip) { - const GPtrArray *p; - int i; - - p = nm_ip_config_get_addresses(ip); - for (i = 0; i < p->len; i++) { - NMIPAddress *a = p->pdata[i]; - nmc_print("\tinet6 %s/%d\n", nm_ip_address_get_address(a), nm_ip_address_get_prefix(a)); - } - - p = nm_ip_config_get_routes(ip); - for (i = 0; i < p->len; i++) { - NMIPRoute *a = p->pdata[i]; - - nm_str_buf_reset(&str); - _nm_ip_route_to_string(a, &str); - - nmc_print("\troute6 %s\n", nm_str_buf_get_str(&str)); - } - } - - g_string_free(outbuf, TRUE); } void