diff options
| author | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
| commit | 90e8691111889a7b5f3c812f5a41f15a8a058913 (patch) | |
| tree | f101a879eca27c34a9bfa5f3da52266b22539a36 /src | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src')
275 files changed, 29690 insertions, 20972 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index ecb1d8ae..596c5bb3 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -175,37 +175,78 @@ nm_utils_get_ip_config_method (NMConnection *connection, if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - if (nm_setting_connection_get_master (s_con)) + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - else { - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) - return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - method = nm_setting_ip_config_get_method (s_ip4); - g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - return method; - } + method = nm_setting_ip_config_get_method (s_ip4); + g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + return method; } else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); - if (nm_setting_connection_get_master (s_con)) + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - else { - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!s_ip6) - return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - method = nm_setting_ip_config_get_method (s_ip6); - g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); - - return method; - } + method = nm_setting_ip_config_get_method (s_ip6); + g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + + return method; } else g_assert_not_reached (); } +gboolean +nm_utils_connection_has_default_route (NMConnection *connection, + int addr_family, + gboolean *out_is_never_default) +{ + const char *method; + NMSettingIPConfig *s_ip; + gboolean is_never_default = FALSE; + gboolean has_default_route = FALSE; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); + + if (!connection) + goto out; + + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); + if (!s_ip) + goto out; + if (nm_setting_ip_config_get_never_default (s_ip)) { + is_never_default = TRUE; + goto out; + } + + if (addr_family == AF_INET) { + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (NM_IN_STRSET (method, NULL, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + goto out; + } else { + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (NM_IN_STRSET (method, NULL, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + goto out; + } + + has_default_route = TRUE; +out: + NM_SET_OUT (out_is_never_default, is_never_default); + return has_default_route; +} + +/*****************************************************************************/ + void nm_utils_complete_generic (NMPlatform *platform, NMConnection *connection, @@ -250,7 +291,7 @@ nm_utils_complete_generic (NMPlatform *platform, } /* Normalize */ - parameters = g_hash_table_new (g_str_hash, g_str_equal); + parameters = g_hash_table_new (nm_str_hash, g_str_equal); g_hash_table_insert (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD, default_enable_ipv6 ? NM_SETTING_IP6_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_IGNORE); nm_connection_normalize (connection, parameters, NULL, NULL); @@ -392,12 +433,14 @@ check_ip_routes (NMConnection *orig, gint64 default_metric, gboolean v4) { - gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL; + gs_free NMIPRoute **routes1 = NULL; + NMIPRoute **routes2; NMSettingIPConfig *s_ip1, *s_ip2; gint64 m; const char *s_name; GHashTable *props; - guint i, num; + guint i, i1, i2, num1, num2; + const guint8 PLEN = v4 ? 32 : 128; s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME : NM_SETTING_IP6_CONFIG_SETTING_NAME; @@ -414,27 +457,49 @@ check_ip_routes (NMConnection *orig, if (!s_ip1 || !s_ip2) return FALSE; - num = nm_setting_ip_config_get_num_routes (s_ip1); - if (num != nm_setting_ip_config_get_num_routes (s_ip2)) - return FALSE; + num1 = nm_setting_ip_config_get_num_routes (s_ip1); + num2 = nm_setting_ip_config_get_num_routes (s_ip2); - routes1 = g_new (NMIPRoute *, num); - routes2 = g_new (NMIPRoute *, num); + routes1 = g_new (NMIPRoute *, (gsize) num1 + num2); + routes2 = &routes1[num1]; - for (i = 0; i < num; i++) { + for (i = 0; i < num1; i++) routes1[i] = nm_setting_ip_config_get_route (s_ip1, i); + for (i = 0; i < num2; i++) routes2[i] = nm_setting_ip_config_get_route (s_ip2, i); - } m = nm_setting_ip_config_get_route_metric (s_ip2); if (m != -1) default_metric = m; - g_qsort_with_data (routes1, num, sizeof (NMIPRoute *), route_ptr_compare, &default_metric); - g_qsort_with_data (routes2, num, sizeof (NMIPRoute *), route_ptr_compare, &default_metric); + g_qsort_with_data (routes1, num1, sizeof (NMIPRoute *), route_ptr_compare, &default_metric); + g_qsort_with_data (routes2, num2, sizeof (NMIPRoute *), route_ptr_compare, &default_metric); + + for (i1 = 0, i2 = 0; i2 < num2; i1++) { + if (i1 >= num1) + return FALSE; + if (route_compare (routes1[i1], routes2[i2], default_metric) == 0) { + i2++; + continue; + } + + /* if @orig (@routes1) contains /32 routes that are missing in @candidate, + * we accept that. + * + * A /32 may have been added automatically, as a direct-route to the gateway. + * The generated connection (@orig) would contain that route, so we shall ignore + * it. + * + * Likeweise for /128 for IPv6. */ + if (nm_ip_route_get_prefix (routes1[i1]) == PLEN) + continue; + + return FALSE; + } - for (i = 0; i < num; i++) { - if (route_compare (routes1[i], routes2[i], default_metric)) + /* check that @orig has no left-over (except host routes that we ignore). */ + for (; i1 < num1; i1++) { + if (nm_ip_route_get_prefix (routes1[i1]) != PLEN) return FALSE; } diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index fe622bdf..e9bd41ae 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -227,7 +227,7 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl) return TRUE; error: - close (priv->brfd); + nm_close (priv->brfd); priv->brfd = -1; return FALSE; } @@ -474,6 +474,15 @@ act_stage3_ip4_config_start (NMDevice *device, } priv->ppp_manager = nm_ppp_manager_create (ppp_iface, &err); + + if (priv->ppp_manager) { + nm_ppp_manager_set_route_parameters (priv->ppp_manager, + nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_metric (device, AF_INET6)); + } + if ( !priv->ppp_manager || !nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_adsl_get_username (s_adsl), @@ -510,10 +519,8 @@ adsl_cleanup (NMDeviceAdsl *self) g_signal_handlers_disconnect_by_func (nm_device_get_platform (NM_DEVICE (self)), G_CALLBACK (link_changed_cb), self); - if (priv->brfd >= 0) { - close (priv->brfd); - priv->brfd = -1; - } + nm_close (priv->brfd); + priv->brfd = -1; nm_clear_g_source (&priv->nas_update_id); diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h index 6e97c3f5..d72bea81 100644 --- a/src/devices/bluetooth/nm-bluez-common.h +++ b/src/devices/bluetooth/nm-bluez-common.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2017 Red Hat, Inc. */ #ifndef __NETWORKMANAGER_BLUEZ_COMMON_H__ @@ -24,21 +24,23 @@ #define BLUETOOTH_CONNECT_DUN "dun" #define BLUETOOTH_CONNECT_NAP "nap" -#define BLUEZ_SERVICE "org.bluez" +#define NM_BLUEZ_SERVICE "org.bluez" -#define BLUEZ_MANAGER_PATH "/" -#define OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager" +#define NM_BLUEZ_MANAGER_PATH "/" +#define NM_OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager" -#define BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1" -#define BLUEZ5_DEVICE_INTERFACE "org.bluez.Device1" -#define BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1" +#define NM_BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1" +#define NM_BLUEZ5_DEVICE_INTERFACE "org.bluez.Device1" +#define NM_BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1" +#define NM_BLUEZ5_NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1" -#define BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager" -#define BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter" -#define BLUEZ4_DEVICE_INTERFACE "org.bluez.Device" -#define BLUEZ4_SERIAL_INTERFACE "org.bluez.Serial" -#define BLUEZ4_NETWORK_INTERFACE "org.bluez.Network" +#define NM_BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager" +#define NM_BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter" +#define NM_BLUEZ4_DEVICE_INTERFACE "org.bluez.Device" +#define NM_BLUEZ4_SERIAL_INTERFACE "org.bluez.Serial" +#define NM_BLUEZ4_NETWORK_INTERFACE "org.bluez.Network" #define NM_BLUEZ_MANAGER_BDADDR_ADDED "bdaddr-added" +#define NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED "network-server-added" #endif /* NM_BLUEZ_COMMON_H */ diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index 41ef74ca..182527d9 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -84,6 +84,7 @@ typedef struct { char *name; guint32 capabilities; gboolean connected; + gboolean paired; char *b4_iface; #if WITH_BLUEZ5_DUN @@ -278,10 +279,11 @@ check_emit_usable (NMBluezDevice *self) /* only expect the supported capabilities set. */ nm_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE ); - new_usable = (priv->initialized && priv->capabilities && priv->name && - ((priv->bluez_version == 4) || - (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered) ) && - priv->dbus_connection && priv->address && priv->adapter_address); + new_usable = ( priv->initialized && priv->capabilities + && priv->name && priv->paired + && ( (priv->bluez_version == 4) + || (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered)) + && priv->dbus_connection && priv->address && priv->adapter_address); if (!new_usable) goto END; @@ -343,6 +345,10 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection) return FALSE; bt_type = nm_setting_bluetooth_get_connection_type (s_bt); + + if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_NAP)) + return FALSE; + if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) && !(priv->capabilities & NM_BT_CAPABILITY_DUN)) return FALSE; @@ -465,7 +471,7 @@ nm_bluez_device_disconnect (NMBluezDevice *self) if (!priv->b4_iface) goto out; args = g_variant_new ("(s)", priv->b4_iface), - dbus_iface = BLUEZ4_SERIAL_INTERFACE; + dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE; } else if (priv->bluez_version == 5) { #if WITH_BLUEZ5_DUN nm_bluez5_dun_cleanup (priv->b5_dun_context); @@ -475,16 +481,16 @@ nm_bluez_device_disconnect (NMBluezDevice *self) } } else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) { if (priv->bluez_version == 4) - dbus_iface = BLUEZ4_NETWORK_INTERFACE; + dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE; else if (priv->bluez_version == 5) - dbus_iface = BLUEZ5_NETWORK_INTERFACE; + dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE; else g_assert_not_reached (); } else g_assert_not_reached (); g_dbus_connection_call (priv->dbus_connection, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, priv->path, dbus_iface, "Disconnect", @@ -577,13 +583,13 @@ nm_bluez_device_connect_async (NMBluezDevice *self, if (connection_bt_type == NM_BT_CAPABILITY_NAP) { connect_type = BLUETOOTH_CONNECT_NAP; if (priv->bluez_version == 4) - dbus_iface = BLUEZ4_NETWORK_INTERFACE; + dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE; else if (priv->bluez_version == 5) - dbus_iface = BLUEZ5_NETWORK_INTERFACE; + dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE; } else if (connection_bt_type == NM_BT_CAPABILITY_DUN) { connect_type = BLUETOOTH_CONNECT_DUN; if (priv->bluez_version == 4) - dbus_iface = BLUEZ4_SERIAL_INTERFACE; + dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE; else if (priv->bluez_version == 5) { #if WITH_BLUEZ5_DUN if (priv->b5_dun_context == NULL) @@ -602,7 +608,7 @@ nm_bluez_device_connect_async (NMBluezDevice *self, g_assert_not_reached (); g_dbus_connection_call (priv->dbus_connection, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, priv->path, dbus_iface, "Connect", @@ -795,6 +801,17 @@ _take_variant_property_connected (NMBluezDevice *self, GVariant *v) g_variant_unref (v); } +static void +_take_variant_property_paired (NMBluezDevice *self, GVariant *v) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) + priv->paired = g_variant_get_boolean (v); + + if (v) + g_variant_unref (v); +} static void adapter5_on_properties_changed (GDBusProxy *proxy, @@ -864,6 +881,8 @@ _take_one_variant_property (NMBluezDevice *self, const char *property, GVariant _take_variant_property_address (self, v); else if (!g_strcmp0 (property, "Connected")) _take_variant_property_connected (self, v); + else if (!g_strcmp0 (property, "Paired")) + _take_variant_property_paired (self, v); else if (!g_strcmp0 (property, "Name")) _take_variant_property_name (self, v); else if (!g_strcmp0 (property, "UUIDs")) @@ -963,6 +982,7 @@ query_properties (NMBluezDevice *self) g_object_freeze_notify (G_OBJECT (self)); _take_variant_property_address (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address")); _take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected")); + _take_variant_property_paired (self, g_dbus_proxy_get_cached_property (priv->proxy, "Paired")); _take_variant_property_name (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name")); _take_variant_property_uuids (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs")); g_object_thaw_notify (G_OBJECT (self)); @@ -972,9 +992,9 @@ query_properties (NMBluezDevice *self) g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, g_variant_get_string (v, NULL), - BLUEZ5_ADAPTER_INTERFACE, + NM_BLUEZ5_ADAPTER_INTERFACE, NULL, (GAsyncReadyCallback) adapter5_on_acquired, g_object_ref (self)); @@ -1134,17 +1154,17 @@ nm_bluez_device_new (const char *path, switch (priv->bluez_version) { case 4: - interface_name = BLUEZ4_DEVICE_INTERFACE; + interface_name = NM_BLUEZ4_DEVICE_INTERFACE; break; case 5: - interface_name = BLUEZ5_DEVICE_INTERFACE; + interface_name = NM_BLUEZ5_DEVICE_INTERFACE; break; } g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, priv->path, interface_name, NULL, @@ -1199,7 +1219,7 @@ dispose (GObject *object) if (to_delete) { nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path, nm_connection_get_id (to_delete), nm_connection_get_uuid (to_delete)); - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL, NULL); + nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL); g_object_unref (to_delete); } @@ -1274,7 +1294,7 @@ nm_bluez_device_class_init (NMBluezDeviceClass *config_class) g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - signals[INITIALIZED] = g_signal_new ("initialized", + signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h index e56d5d24..f8a1872f 100644 --- a/src/devices/bluetooth/nm-bluez-device.h +++ b/src/devices/bluetooth/nm-bluez-device.h @@ -39,6 +39,7 @@ #define NM_BLUEZ_DEVICE_CONNECTED "connected" /* Signals */ +#define NM_BLUEZ_DEVICE_INITIALIZED "initialized" #define NM_BLUEZ_DEVICE_REMOVED "removed" typedef struct _NMBluezDevice NMBluezDevice; @@ -61,8 +62,6 @@ const char *nm_bluez_device_get_address (NMBluezDevice *self); const char *nm_bluez_device_get_name (NMBluezDevice *self); -guint32 nm_bluez_device_get_class (NMBluezDevice *self); - guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self); gboolean nm_bluez_device_get_connected (NMBluezDevice *self); diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 2f0afa16..96e80245 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -26,6 +26,7 @@ #include <gmodule.h> #include "devices/nm-device-factory.h" +#include "devices/nm-device-bridge.h" #include "nm-setting-bluetooth.h" #include "settings/nm-settings.h" #include "nm-bluez4-manager.h" @@ -146,7 +147,7 @@ cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name) static void -manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr, +manager_bdaddr_added_cb (GObject *manager, NMBluezDevice *bt_device, const char *bdaddr, const char *name, @@ -180,6 +181,13 @@ manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr, } static void +manager_network_server_added_cb (GObject *manager, + gpointer user_data) +{ + nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), NULL); +} + +static void setup_version_number (NMBluezManager *self, int bluez_version) { NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); @@ -228,6 +236,10 @@ setup_bluez5 (NMBluezManager *self) NM_BLUEZ_MANAGER_BDADDR_ADDED, G_CALLBACK (manager_bdaddr_added_cb), self); + g_signal_connect (manager, + NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED, + G_CALLBACK (manager_network_server_added_cb), + self); nm_bluez5_manager_query_devices (manager); } @@ -264,7 +276,7 @@ check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, c cleanup_checking (self, FALSE); if (!priv->watch_name_id) { priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, G_BUS_NAME_WATCHER_FLAGS_NONE, watch_name_on_appeared, NULL, @@ -317,7 +329,7 @@ check_bluez_and_try_setup_do_introspect (GObject *source_object, /* might not be the best approach to detect the version, but it's good enough in practice. */ if (strstr (xml_data, "org.freedesktop.DBus.ObjectManager")) bluez_version = 5; - else if (strstr (xml_data, BLUEZ4_MANAGER_INTERFACE)) + else if (strstr (xml_data, NM_BLUEZ4_MANAGER_INTERFACE)) bluez_version = 4; else reason = "unexpected introspect result"; @@ -380,7 +392,7 @@ check_bluez_and_try_setup (NMBluezManager *self) g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, - BLUEZ_SERVICE, + NM_BLUEZ_SERVICE, "/", DBUS_INTERFACE_INTROSPECTABLE, priv->async_cancellable, @@ -406,6 +418,20 @@ create_device (NMDeviceFactory *factory, return NULL; } +static gboolean +match_connection (NMDeviceFactory *factory, + NMConnection *connection) +{ + const char *type = nm_connection_get_connection_type (connection); + + nm_assert (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME)); + + if (_nm_connection_get_setting_bluetooth_for_nap (connection)) + return FALSE; /* handled by the bridge factory */ + + return TRUE; +} + /*****************************************************************************/ static void @@ -427,7 +453,7 @@ dispose (GObject *object) g_clear_object (&priv->manager4); } if (priv->manager5) { - g_signal_handlers_disconnect_by_func (priv->manager5, manager_bdaddr_added_cb, self); + g_signal_handlers_disconnect_by_data (priv->manager5, self); g_clear_object (&priv->manager5); } @@ -450,5 +476,6 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass) factory_class->get_supported_types = get_supported_types; factory_class->create_device = create_device; + factory_class->match_connection = match_connection; factory_class->start = start; } diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c index c0c1be30..0f19f998 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.c +++ b/src/devices/bluetooth/nm-bluez4-adapter.c @@ -25,6 +25,7 @@ #include <string.h> #include "nm-dbus-interface.h" +#include "nm-utils/nm-hash-utils.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" #include "nm-core-internal.h" @@ -49,6 +50,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { char *path; GDBusProxy *proxy; + GCancellable *proxy_cancellable; gboolean initialized; char *address; @@ -73,6 +75,11 @@ G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT) /*****************************************************************************/ +#define _NMLOG_DOMAIN LOGD_BT +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-adapter", __VA_ARGS__) + +/*****************************************************************************/ + static void device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device); /*****************************************************************************/ @@ -119,8 +126,8 @@ nm_bluez4_adapter_get_devices (NMBluez4Adapter *self) static void emit_device_removed (NMBluez4Adapter *self, NMBluezDevice *device) { - nm_log_dbg (LOGD_BT, "(%s): bluez device now unusable", - nm_bluez_device_get_path (device)); + _LOGD ("(%s): bluez device now unusable", + nm_bluez_device_get_path (device)); g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); } @@ -130,9 +137,9 @@ device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data) NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data); if (nm_bluez_device_get_usable (device)) { - nm_log_dbg (LOGD_BT, "(%s): bluez device now usable (device address is %s)", - nm_bluez_device_get_path (device), - nm_bluez_device_get_address (device)); + _LOGD ("(%s): bluez device now usable (device address is %s)", + nm_bluez_device_get_path (device), + nm_bluez_device_get_address (device)); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); } else emit_device_removed (self, device); @@ -143,9 +150,9 @@ device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data) { NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data); - nm_log_dbg (LOGD_BT, "(%s): bluez device %s", - nm_bluez_device_get_path (device), - success ? "initialized" : "failed to initialize"); + _LOGD ("(%s): bluez device %s", + nm_bluez_device_get_path (device), + success ? "initialized" : "failed to initialize"); if (!success) device_do_remove (self, device); } @@ -174,11 +181,11 @@ device_created (GDBusProxy *proxy, const char *path, gpointer user_data) NMBluezDevice *device; device = nm_bluez_device_new (path, priv->address, priv->settings, 4); - g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self); - g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self); + g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self); + g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self); g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device); - nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path); + _LOGD ("(%s): new bluez device found", path); } static void @@ -188,7 +195,7 @@ device_removed (GDBusProxy *proxy, const char *path, gpointer user_data) NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); NMBluezDevice *device; - nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path); + _LOGD ("(%s): bluez device removed", path); device = g_hash_table_lookup (priv->devices, path); if (device) @@ -198,19 +205,28 @@ device_removed (GDBusProxy *proxy, const char *path, gpointer user_data) static void get_properties_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) { - NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data); - NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); - GError *err = NULL; + NMBluez4Adapter *self; + NMBluez4AdapterPrivate *priv; + gs_free_error GError *error = NULL; GVariant *ret, *properties; char **devices; int i; ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, - G_VARIANT_TYPE ("(a{sv})"), &err); + G_VARIANT_TYPE ("(a{sv})"), &error); + + if ( !ret + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_BLUEZ4_ADAPTER (user_data); + priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); + + g_clear_object (&priv->proxy_cancellable); + if (!ret) { - g_dbus_error_strip_remote_error (err); - nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s", err->message); - g_error_free (err); + g_dbus_error_strip_remote_error (error); + _LOGW ("bluez error getting adapter properties: %s", error->message); goto done; } @@ -233,15 +249,43 @@ done: } static void -query_properties (NMBluez4Adapter *self) +_proxy_new_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); + NMBluez4Adapter *self; + NMBluez4AdapterPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); + + if (!proxy) { + _LOGW ("bluez error creating D-Bus proxy: %s", error->message); + g_clear_object (&priv->proxy_cancellable); + g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized); + return; + } + + priv->proxy = proxy; + + _nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (device_created), self); + _nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (device_removed), self); g_dbus_proxy_call (priv->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - get_properties_cb, self); + priv->proxy_cancellable, + get_properties_cb, + self); } /*****************************************************************************/ @@ -297,7 +341,7 @@ nm_bluez4_adapter_init (NMBluez4Adapter *self) { NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); - priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); } @@ -316,19 +360,17 @@ nm_bluez4_adapter_new (const char *path, NMSettings *settings) priv->settings = g_object_ref (settings); - priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - BLUEZ_SERVICE, - priv->path, - BLUEZ4_ADAPTER_INTERFACE, - NULL, NULL); - _nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"), - G_CALLBACK (device_created), self); - _nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"), - G_CALLBACK (device_removed), self); + priv->proxy_cancellable = g_cancellable_new (); - query_properties (self); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_BLUEZ_SERVICE, + priv->path, + NM_BLUEZ4_ADAPTER_INTERFACE, + priv->proxy_cancellable, + _proxy_new_cb, + self); return self; } @@ -339,21 +381,28 @@ dispose (GObject *object) NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); NMBluezDevice *device; + nm_clear_g_cancellable (&priv->proxy_cancellable); + while ((device = g_hash_table_find (priv->devices, _find_all, NULL))) device_do_remove (self, device); + if (priv->proxy) { + g_signal_handlers_disconnect_by_data (priv->proxy, self); + g_clear_object (&priv->proxy); + } + G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->dispose (object); } static void finalize (GObject *object) { - NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE ((NMBluez4Adapter *) object); + NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (object); + NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self); g_hash_table_destroy (priv->devices); g_free (priv->address); g_free (priv->path); - g_object_unref (priv->proxy); G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->finalize (object); @@ -384,7 +433,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class) g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - signals[INITIALIZED] = g_signal_new ("initialized", + signals[INITIALIZED] = g_signal_new (NM_BLUEZ4_ADAPTER_INITIALIZED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, @@ -392,7 +441,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class) g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); - signals[DEVICE_ADDED] = g_signal_new ("device-added", + signals[DEVICE_ADDED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_ADDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, @@ -400,7 +449,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); - signals[DEVICE_REMOVED] = g_signal_new ("device-removed", + signals[DEVICE_REMOVED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_REMOVED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h index e240ec2c..0aa4ff91 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.h +++ b/src/devices/bluetooth/nm-bluez4-adapter.h @@ -30,9 +30,15 @@ #define NM_IS_BLUEZ4_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_ADAPTER)) #define NM_BLUEZ4_ADAPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass)) +/* Properties */ #define NM_BLUEZ4_ADAPTER_PATH "path" #define NM_BLUEZ4_ADAPTER_ADDRESS "address" +/* Signals */ +#define NM_BLUEZ4_ADAPTER_INITIALIZED "initialized" +#define NM_BLUEZ4_ADAPTER_DEVICE_ADDED "device-added" +#define NM_BLUEZ4_ADAPTER_DEVICE_REMOVED "device-removed" + typedef struct _NMBluez4Adapter NMBluez4Adapter; typedef struct _NMBluez4AdapterClass NMBluez4AdapterClass; diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c index a9079a2f..1fe02f18 100644 --- a/src/devices/bluetooth/nm-bluez4-manager.c +++ b/src/devices/bluetooth/nm-bluez4-manager.c @@ -47,6 +47,7 @@ typedef struct { NMSettings *settings; GDBusProxy *proxy; + GCancellable *proxy_cancellable; NMBluez4Adapter *adapter; } NMBluez4ManagerPrivate; @@ -66,6 +67,11 @@ G_DEFINE_TYPE (NMBluez4Manager, nm_bluez4_manager, G_TYPE_OBJECT) /*****************************************************************************/ +#define _NMLOG_DOMAIN LOGD_BT +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-manager", __VA_ARGS__) + +/*****************************************************************************/ + static void emit_bdaddr_added (NMBluez4Manager *self, NMBluezDevice *device) { @@ -119,8 +125,10 @@ adapter_initialized (NMBluez4Adapter *adapter, gboolean success, gpointer user_d emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); g_slist_free (devices); - g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self); - g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self); + g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_ADDED, + G_CALLBACK (device_added), self); + g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_REMOVED, + G_CALLBACK (device_removed), self); } else { g_object_unref (priv->adapter); priv->adapter = NULL; @@ -169,49 +177,70 @@ default_adapter_changed (GDBusProxy *proxy, const char *path, NMBluez4Manager *s /* Add the new default adapter */ if (path) { priv->adapter = nm_bluez4_adapter_new (path, priv->settings); - g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self); + g_signal_connect (priv->adapter, NM_BLUEZ4_ADAPTER_INITIALIZED, + G_CALLBACK (adapter_initialized), self); } } static void default_adapter_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) { - NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data); - NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); - GVariant *ret; - GError *err = NULL; + NMBluez4Manager *self; + NMBluez4ManagerPrivate *priv; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char *default_adapter; ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, - G_VARIANT_TYPE ("(o)"), &err); - if (ret) { - const char *default_adapter; + G_VARIANT_TYPE ("(o)"), &error); + if ( !ret + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - g_variant_get (ret, "(&o)", &default_adapter); - default_adapter_changed (priv->proxy, default_adapter, self); - g_variant_unref (ret); - } else { + self = NM_BLUEZ4_MANAGER (user_data); + priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->proxy_cancellable); + + if (!ret) { /* Ignore "No such adapter" errors; just means bluetooth isn't active */ - if ( !_nm_dbus_error_has_name (err, "org.bluez.Error.NoSuchAdapter") - && !_nm_dbus_error_has_name (err, "org.freedesktop.systemd1.LoadFailed") - && !g_error_matches (err, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { - g_dbus_error_strip_remote_error (err); - nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s", - err->message); + if ( !_nm_dbus_error_has_name (error, "org.bluez.Error.NoSuchAdapter") + && !_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed") + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + g_dbus_error_strip_remote_error (error); + _LOGW ("bluez error getting default adapter: %s", + error->message); } - g_error_free (err); + return; } + + g_variant_get (ret, "(&o)", &default_adapter); + default_adapter_changed (priv->proxy, default_adapter, self); } static void -query_default_adapter (NMBluez4Manager *self) +name_owner_changed (NMBluez4Manager *self) { NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); + gs_free char *owner = NULL; + + nm_clear_g_cancellable (&priv->proxy_cancellable); + + owner = g_dbus_proxy_get_name_owner (priv->proxy); + if (!owner) { + /* Throwing away the adapter removes all devices too */ + g_clear_object (&priv->adapter); + return; + } + + priv->proxy_cancellable = g_cancellable_new (); g_dbus_proxy_call (priv->proxy, "DefaultAdapter", NULL, G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - default_adapter_cb, self); + priv->proxy_cancellable, + default_adapter_cb, + self); } static void @@ -219,34 +248,35 @@ name_owner_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { - NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data); - NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); - char *owner; - - owner = g_dbus_proxy_get_name_owner (priv->proxy); - if (owner) { - query_default_adapter (self); - g_free (owner); - } else { - /* Throwing away the adapter removes all devices too */ - g_clear_object (&priv->adapter); - } + name_owner_changed (user_data); } -/*****************************************************************************/ - static void -nm_bluez4_manager_init (NMBluez4Manager *self) +_proxy_new_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); + NMBluez4Manager *self; + NMBluez4ManagerPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); + + if (!proxy) { + _LOGW ("bluez error creating D-Bus proxy: %s", error->message); + g_clear_object (&priv->proxy_cancellable); + return; + } + + priv->proxy = proxy; - priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - BLUEZ_SERVICE, - BLUEZ_MANAGER_PATH, - BLUEZ4_MANAGER_INTERFACE, - NULL, NULL); _nm_dbus_signal_connect (priv->proxy, "AdapterRemoved", G_VARIANT_TYPE ("(o)"), G_CALLBACK (adapter_removed), self); _nm_dbus_signal_connect (priv->proxy, "DefaultAdapterChanged", G_VARIANT_TYPE ("(o)"), @@ -254,7 +284,27 @@ nm_bluez4_manager_init (NMBluez4Manager *self) g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (name_owner_changed_cb), self); - query_default_adapter (self); + name_owner_changed (self); +} + +/*****************************************************************************/ + +static void +nm_bluez4_manager_init (NMBluez4Manager *self) +{ + NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); + + priv->proxy_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + NM_BLUEZ_SERVICE, + NM_BLUEZ_MANAGER_PATH, + NM_BLUEZ4_MANAGER_INTERFACE, + priv->proxy_cancellable, + _proxy_new_cb, + self); } NMBluez4Manager * @@ -275,7 +325,13 @@ dispose (GObject *object) NMBluez4Manager *self = NM_BLUEZ4_MANAGER (object); NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self); - g_clear_object (&priv->proxy); + nm_clear_g_cancellable (&priv->proxy_cancellable); + + if (priv->proxy) { + g_signal_handlers_disconnect_by_data (priv->proxy, self); + g_clear_object (&priv->proxy); + } + g_clear_object (&priv->adapter); G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object); diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c index aba3a0dd..ca09b276 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.c +++ b/src/devices/bluetooth/nm-bluez5-dun.c @@ -386,11 +386,11 @@ nm_bluez5_dun_cleanup (NMBluez5DunContext *context) ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req); context->rfcomm_id = -1; } - close (context->rfcomm_fd); + nm_close (context->rfcomm_fd); context->rfcomm_fd = -1; } - close (context->rfcomm_tty_fd); + nm_close (context->rfcomm_tty_fd); context->rfcomm_tty_fd = -1; } diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c index 88759301..8c93f2a5 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ b/src/devices/bluetooth/nm-bluez5-manager.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2007 - 2013 Red Hat, Inc. + * Copyright (C) 2007 - 2017 Red Hat, Inc. * Copyright (C) 2013 Intel Corporation. */ @@ -30,14 +30,17 @@ #include "nm-core-internal.h" +#include "nm-utils/c-list.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" +#include "devices/nm-device-bridge.h" #include "settings/nm-settings.h" /*****************************************************************************/ enum { BDADDR_ADDED, + NETWORK_SERVER_ADDED, LAST_SIGNAL, }; @@ -49,10 +52,13 @@ typedef struct { GDBusProxy *proxy; GHashTable *devices; + + CList network_servers; } NMBluez5ManagerPrivate; struct _NMBluez5Manager { GObject parent; + NMBtVTableNetworkServer network_server_vtable; NMBluez5ManagerPrivate _priv; }; @@ -64,6 +70,15 @@ G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT) #define NM_BLUEZ5_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez5Manager, NM_IS_BLUEZ5_MANAGER) +#define NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE(self) (&(self)->network_server_vtable) +#define NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER(vtable) \ + NM_BLUEZ5_MANAGER(((char *)(vtable)) - offsetof (struct _NMBluez5Manager, network_server_vtable)) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_BT +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez5", __VA_ARGS__) + /*****************************************************************************/ static void device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self); @@ -71,6 +86,182 @@ static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Man /*****************************************************************************/ +typedef struct { + char *path; + char *addr; + NMDevice *device; + CList lst_ns; +} NetworkServer; + +static NetworkServer * +_find_network_server (NMBluez5Manager *self, const char *path, NMDevice *device) +{ + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + NetworkServer *network_server; + + nm_assert (path || NM_IS_DEVICE (device)); + + c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) { + if (path && !nm_streq (network_server->path, path)) + continue; + if (device && network_server->device != device) + continue; + return network_server; + } + return NULL; +} + +static NetworkServer * +_find_network_server_for_addr (NMBluez5Manager *self, const char *addr) +{ + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + NetworkServer *network_server; + + c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) { + /* The address lookups need a server not assigned to a device + * and tolerate an empty address as a wildcard for "any". */ + if ( !network_server->device + && (!addr || nm_streq (network_server->addr, addr))) + return network_server; + } + return NULL; +} + +static void +_network_server_unregister (NMBluez5Manager *self, NetworkServer *network_server) +{ + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + + if (!network_server->device) { + /* Not connected. */ + return; + } + + _LOGI ("NAP: unregistering %s from %s", + nm_device_get_iface (network_server->device), + network_server->addr); + + g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy), + NM_BLUEZ_SERVICE, + network_server->path, + NM_BLUEZ5_NETWORK_SERVER_INTERFACE, + "Unregister", + g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); + + g_clear_object (&network_server->device); +} + +static void +_network_server_free (NMBluez5Manager *self, NetworkServer *network_server) +{ + _network_server_unregister (self, network_server); + c_list_unlink (&network_server->lst_ns); + g_free (network_server->path); + g_free (network_server->addr); + g_slice_free (NetworkServer, network_server); +} + +static gboolean +network_server_is_available (const NMBtVTableNetworkServer *vtable, + const char *addr) +{ + NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable); + + return !!_find_network_server_for_addr (self, addr); +} + +static gboolean +network_server_register_bridge (const NMBtVTableNetworkServer *vtable, + const char *addr, + NMDevice *device) +{ + NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable); + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + NetworkServer *network_server = _find_network_server_for_addr (self, addr); + + nm_assert (NM_IS_DEVICE (device)); + nm_assert (!_find_network_server (self, NULL, device)); + + if (!network_server) { + /* The device checked that a network server is available, before + * starting the activation, but for some reason it no longer is. + * Indicate that the activation should not proceed. */ + _LOGI ("NAP: %s is not available for %s", addr, nm_device_get_iface (device)); + return FALSE; + } + + _LOGI ("NAP: registering %s on %s", nm_device_get_iface (device), network_server->addr); + + g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy), + NM_BLUEZ_SERVICE, + network_server->path, + NM_BLUEZ5_NETWORK_SERVER_INTERFACE, + "Register", + g_variant_new ("(ss)", BLUETOOTH_CONNECT_NAP, nm_device_get_iface (device)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); + + network_server->device = g_object_ref (device); + + return TRUE; +} + +static gboolean +network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable, + NMDevice *device) +{ + NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable); + NetworkServer *network_server = _find_network_server (self, NULL, device); + + if (network_server) + _network_server_unregister (self, network_server); + + return TRUE; +} + +static void +network_server_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self) +{ + NetworkServer *network_server; + + network_server = _find_network_server (self, path, NULL); + if (!network_server) + return; + + if (network_server->device) { + nm_device_queue_state (network_server->device, NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_BT_FAILED); + } + _LOGI ("NAP: removed interface %s", network_server->addr); + _network_server_free (self, network_server); +} + +static void +network_server_added (GDBusProxy *proxy, const gchar *path, const char *addr, NMBluez5Manager *self) +{ + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + NetworkServer *network_server; + + /* If BlueZ messes up and announces a single network server twice, + * make sure we get rid of the older instance first. */ + network_server_removed (proxy, path, self); + + network_server = g_slice_new0 (NetworkServer); + network_server->path = g_strdup (path); + network_server->addr = g_strdup (addr); + c_list_link_before (&priv->network_servers, &network_server->lst_ns); + + _LOGI ("NAP: added interface %s", addr); + + g_signal_emit (self, signals[NETWORK_SERVER_ADDED], 0); +} + +/*****************************************************************************/ + static void emit_bdaddr_added (NMBluez5Manager *self, NMBluezDevice *device) { @@ -125,14 +316,14 @@ device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self) { gboolean usable = nm_bluez_device_get_usable (device); - nm_log_dbg (LOGD_BT, "(%s): bluez device now %s", - nm_bluez_device_get_path (device), - usable ? "usable" : "unusable"); + _LOGD ("(%s): bluez device now %s", + nm_bluez_device_get_path (device), + usable ? "usable" : "unusable"); if (usable) { - nm_log_dbg (LOGD_BT, "(%s): bluez device address %s", - nm_bluez_device_get_path (device), - nm_bluez_device_get_address (device)); + _LOGD ("(%s): bluez device address %s", + nm_bluez_device_get_path (device), + nm_bluez_device_get_address (device)); emit_bdaddr_added (self, device); } else g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED); @@ -143,9 +334,9 @@ device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *se { NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); - nm_log_dbg (LOGD_BT, "(%s): bluez device %s", - nm_bluez_device_get_path (device), - success ? "initialized" : "failed to initialize"); + _LOGD ("(%s): bluez device %s", + nm_bluez_device_get_path (device), + success ? "initialized" : "failed to initialize"); if (!success) g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device)); } @@ -157,11 +348,11 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self) NMBluezDevice *device; device = nm_bluez_device_new (path, NULL, priv->settings, 5); - g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self); - g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self); + g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self); + g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self); g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device); - nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path); + _LOGD ("(%s): new bluez device found", path); } static void @@ -170,7 +361,7 @@ device_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self) NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); NMBluezDevice *device; - nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path); + _LOGD ("(%s): bluez device removed", path); device = g_hash_table_lookup (priv->devices, path); if (device) { @@ -186,8 +377,16 @@ object_manager_interfaces_added (GDBusProxy *proxy, GVariant *dict, NMBluez5Manager *self) { - if (g_variant_lookup (dict, BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL)) + if (g_variant_lookup (dict, NM_BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL)) device_added (proxy, path, self); + if (g_variant_lookup (dict, NM_BLUEZ5_NETWORK_SERVER_INTERFACE, "a{sv}", NULL)) { + gs_unref_variant GVariant *adapter = g_variant_lookup_value (dict, NM_BLUEZ5_ADAPTER_INTERFACE, G_VARIANT_TYPE_DICTIONARY); + const char *address; + + if ( adapter + && g_variant_lookup (adapter, "Address", "&s", &address)) + network_server_added (proxy, path, address, self); + } } static void @@ -196,8 +395,10 @@ object_manager_interfaces_removed (GDBusProxy *proxy, const char **ifaces, NMBluez5Manager *self) { - if (ifaces && g_strv_contains (ifaces, BLUEZ5_DEVICE_INTERFACE)) + if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_DEVICE_INTERFACE)) device_removed (proxy, path, self); + if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) + network_server_removed (proxy, path, self); } static void @@ -215,20 +416,17 @@ get_managed_objects_cb (GDBusProxy *proxy, &error); if (!variant) { if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) - nm_log_warn (LOGD_BT, "Couldn't get managed objects: not running Bluez5?"); + _LOGW ("Couldn't get managed objects: not running Bluez5?"); else { g_dbus_error_strip_remote_error (error); - nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s", error->message); + _LOGW ("Couldn't get managed objects: %s", error->message); } g_clear_error (&error); return; } g_variant_iter_init (&i, g_variant_get_child_value (variant, 0)); while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) { - if (g_variant_lookup_value (ifaces, BLUEZ5_DEVICE_INTERFACE, - G_VARIANT_TYPE_DICTIONARY)) { - device_added (proxy, path, self); - } + object_manager_interfaces_added (proxy, path, ifaces, self); g_variant_unref (ifaces); } @@ -248,7 +446,7 @@ on_proxy_acquired (GObject *object, priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (!priv->proxy) { - nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s", error->message); + _LOGW ("Couldn't acquire object manager proxy: %s", error->message); g_clear_error (&error); return; } @@ -281,9 +479,9 @@ bluez_connect (NMBluez5Manager *self) g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, - BLUEZ_SERVICE, - BLUEZ_MANAGER_PATH, - OBJECT_MANAGER_INTERFACE, + NM_BLUEZ_SERVICE, + NM_BLUEZ_MANAGER_PATH, + NM_OBJECT_MANAGER_INTERFACE, NULL, (GAsyncReadyCallback) on_proxy_acquired, self); @@ -306,33 +504,26 @@ name_owner_changed_cb (GObject *object, } } -static void -bluez_cleanup (NMBluez5Manager *self, gboolean do_signal) -{ - NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); - - if (priv->proxy) { - g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self); - g_clear_object (&priv->proxy); - } - - if (do_signal) - remove_all_devices (self); - else - g_hash_table_remove_all (priv->devices); -} - /*****************************************************************************/ static void nm_bluez5_manager_init (NMBluez5Manager *self) { NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + NMBtVTableNetworkServer *network_server_vtable = NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE (self); bluez_connect (self); - priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); + + c_list_init (&priv->network_servers); + + nm_assert (!nm_bt_vtable_network_server); + network_server_vtable->is_available = network_server_is_available; + network_server_vtable->register_bridge = network_server_register_bridge; + network_server_vtable->unregister_bridge = network_server_unregister_bridge; + nm_bt_vtable_network_server = network_server_vtable; } NMBluez5Manager * @@ -351,8 +542,18 @@ static void dispose (GObject *object) { NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object); + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); + CList *iter, *safe; - bluez_cleanup (self, FALSE); + c_list_for_each_safe (iter, safe, &priv->network_servers) + _network_server_free (self, c_list_entry (iter, NetworkServer, lst_ns)); + + if (priv->proxy) { + g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self); + g_clear_object (&priv->proxy); + } + + g_hash_table_remove_all (priv->devices); G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object); } @@ -360,7 +561,8 @@ dispose (GObject *object) static void finalize (GObject *object) { - NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE ((NMBluez5Manager *) object); + NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object); + NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self); g_hash_table_destroy (priv->devices); @@ -384,4 +586,11 @@ nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT); + + signals[NETWORK_SERVER_ADDED] = + g_signal_new (NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); } diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 4ee71489..0d46be8f 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -40,15 +40,14 @@ #include "nm-bt-error.h" #include "platform/nm-platform.h" +#include "devices/wwan/nm-modem-manager.h" +#include "devices/wwan/nm-modem.h" + #include "introspection/org.freedesktop.NetworkManager.Device.Bluetooth.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceBt); -#define MM_DBUS_SERVICE "org.freedesktop.ModemManager1" -#define MM_DBUS_PATH "/org/freedesktop/ModemManager1" -#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager1" - /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( @@ -65,7 +64,8 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { - GDBusProxy *mm_proxy; + NMModemManager *modem_manager; + gboolean mm_running; NMBluezDevice *bt_device; @@ -647,7 +647,7 @@ component_added (NMDevice *device, GObject *component) NMDeviceState state; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - if (!NM_IS_MODEM (component)) + if (!component || !NM_IS_MODEM (component)) return FALSE; modem = NM_MODEM (component); @@ -967,9 +967,12 @@ is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) } static void -set_mm_running (NMDeviceBt *self, gboolean running) +set_mm_running (NMDeviceBt *self) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + gboolean running; + + running = (nm_modem_manager_name_owner_get (priv->modem_manager) != NULL); if (priv->mm_running != running) { _LOGD (LOGD_BT, "ModemManager now %s", @@ -983,18 +986,11 @@ set_mm_running (NMDeviceBt *self, gboolean running) } static void -mm_name_owner_changed (GObject *object, - GParamSpec *pspec, - NMDeviceBt *self) +mm_name_owner_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - char *owner; - - owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); - if (owner) { - set_mm_running (self, TRUE); - g_free (owner); - } else - set_mm_running (self, FALSE); + set_mm_running (user_data); } /*****************************************************************************/ @@ -1039,7 +1035,8 @@ set_property (GObject *object, guint prop_id, case PROP_BT_DEVICE: /* construct-only */ priv->bt_device = g_value_dup_object (value); - g_signal_connect (priv->bt_device, "removed", G_CALLBACK (bluez_device_removed), object); + if (!priv->bt_device) + g_return_if_reached (); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1052,46 +1049,42 @@ set_property (GObject *object, guint prop_id, static void nm_device_bt_init (NMDeviceBt *self) { - NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); - GError *error = NULL; - - priv->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - MM_DBUS_SERVICE, - MM_DBUS_PATH, - MM_DBUS_INTERFACE, - NULL, &error); - if (priv->mm_proxy) { - g_signal_connect (priv->mm_proxy, "notify::g-name-owner", - G_CALLBACK (mm_name_owner_changed), - self); - mm_name_owner_changed (G_OBJECT (priv->mm_proxy), NULL, self); - } else { - _LOGW (LOGD_MB, "Could not create proxy for '%s': %s", - MM_DBUS_SERVICE, error->message); - g_clear_error (&error); - } } static void constructed (GObject *object) { - NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object); + NMDeviceBt *self = NM_DEVICE_BT (object); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); const char *my_hwaddr; G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object); + priv->modem_manager = g_object_ref (nm_modem_manager_get ()); + + nm_modem_manager_name_owner_ref (priv->modem_manager); + + g_signal_connect (priv->modem_manager, + "notify::"NM_MODEM_MANAGER_NAME_OWNER, + G_CALLBACK (mm_name_owner_changed_cb), + self); + + if (priv->bt_device) { + /* Watch for BT device property changes */ + g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED, + G_CALLBACK (bluez_connected_changed), + object); + g_signal_connect (priv->bt_device, NM_BLUEZ_DEVICE_REMOVED, + G_CALLBACK (bluez_device_removed), object); + } + my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object)); - g_assert (my_hwaddr); - priv->bdaddr = g_strdup (my_hwaddr); + if (my_hwaddr) + priv->bdaddr = g_strdup (my_hwaddr); + else + g_warn_if_reached (); - /* Watch for BT device property changes */ - g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED, - G_CALLBACK (bluez_connected_changed), - object); + set_mm_running (self); } NMDevice * @@ -1129,9 +1122,10 @@ dispose (GObject *object) g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); - if (priv->mm_proxy) { - g_signal_handlers_disconnect_by_func (priv->mm_proxy, G_CALLBACK (mm_name_owner_changed), object); - g_clear_object (&priv->mm_proxy); + if (priv->modem_manager) { + g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), object); + nm_modem_manager_name_owner_unref (priv->modem_manager); + g_clear_object (&priv->modem_manager); } modem_cleanup (NM_DEVICE_BT (object)); diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h index 9bcf6ca8..b90dbd2a 100644 --- a/src/devices/bluetooth/nm-device-bt.h +++ b/src/devices/bluetooth/nm-device-bt.h @@ -24,8 +24,6 @@ #include "devices/nm-device.h" #include "nm-bluez-device.h" -#include "devices/wwan/nm-modem.h" - #define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ()) #define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt)) #define NM_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) @@ -52,8 +50,10 @@ NMDevice *nm_device_bt_new (NMBluezDevice *bt_device, guint32 nm_device_bt_get_capabilities (NMDeviceBt *device); +struct _NMModem; + gboolean nm_device_bt_modem_added (NMDeviceBt *device, - NMModem *modem, + struct _NMModem *modem, const char *driver); #endif /* __NETWORKMANAGER_DEVICE_BT_H__ */ diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index d9c2ca64..910dd0bf 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -58,24 +58,6 @@ get_generic_capabilities (NMDevice *dev) } static gboolean -is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) -{ - return TRUE; -} - -static gboolean -check_connection_available (NMDevice *device, - NMConnection *connection, - NMDeviceCheckConAvailableFlags flags, - const char *specific_object) -{ - /* Connections are always available because the carrier state is determined - * by the slave carrier states, not the bonds's state. - */ - return TRUE; -} - -static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { NMSettingBond *s_bond; @@ -501,7 +483,7 @@ create_and_realize (NMDevice *device, "Failed to create bond interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } return TRUE; @@ -622,6 +604,7 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n static void nm_device_bond_init (NMDeviceBond * self) { + nm_assert (nm_device_is_master (NM_DEVICE (self))); } static void @@ -631,10 +614,9 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND) + parent_class->is_master = TRUE; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->is_available = is_available; parent_class->check_connection_compatible = check_connection_compatible; - parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; parent_class->update_connection = update_connection; @@ -671,7 +653,6 @@ create_device (NMDeviceFactory *factory, NM_DEVICE_TYPE_DESC, "Bond", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_BOND, - NM_DEVICE_IS_MASTER, TRUE, NULL); } diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 01c4eb22..74689aef 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -49,6 +49,10 @@ G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE) /*****************************************************************************/ +const NMBtVTableNetworkServer *nm_bt_vtable_network_server = NULL; + +/*****************************************************************************/ + static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { @@ -56,20 +60,23 @@ get_generic_capabilities (NMDevice *dev) } static gboolean -is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) -{ - return TRUE; -} - -static gboolean check_connection_available (NMDevice *device, NMConnection *connection, NMDeviceCheckConAvailableFlags flags, const char *specific_object) { - /* Connections are always available because the carrier state is determined - * by the bridge port carrier states, not the bridge's state. - */ + NMSettingBluetooth *s_bt; + + if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object)) + return FALSE; + + s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection); + if (s_bt) { + return nm_bt_vtable_network_server + && nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server, + nm_setting_bluetooth_get_bdaddr (s_bt)); + } + return TRUE; } @@ -83,9 +90,17 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return FALSE; s_bridge = nm_connection_get_setting_bridge (connection); - if (!s_bridge || !nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) + if (!s_bridge) return FALSE; + if (!nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) { + if ( nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME) + && _nm_connection_get_setting_bluetooth_for_nap (connection)) { + /* a bluetooth NAP connection is handled by the bridge */ + } else + return FALSE; + } + mac_address = nm_setting_bridge_get_mac_address (s_bridge); if (mac_address && nm_device_is_real (device)) { const char *hw_addr; @@ -141,6 +156,7 @@ static const Option master_options[] = { { NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", TRUE, TRUE }, { NM_SETTING_BRIDGE_MAX_AGE, "max_age", TRUE, TRUE }, { NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", TRUE, TRUE }, + { NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "group_fwd_mask", TRUE, FALSE }, { NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", FALSE, FALSE }, { NULL, NULL } }; @@ -324,6 +340,40 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) return NM_ACT_STAGE_RETURN_SUCCESS; } +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMConnection *connection; + NMSettingBluetooth *s_bt; + + connection = nm_device_get_applied_connection (device); + + s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection); + if (s_bt) { + if ( !nm_bt_vtable_network_server + || !nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server, + nm_setting_bluetooth_get_bdaddr (s_bt), + device)) { + /* The HCI we could use is no longer present. */ + *out_failure_reason = NM_DEVICE_STATE_REASON_REMOVED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +deactivate (NMDevice *device) +{ + if (nm_bt_vtable_network_server) { + /* always call unregister. It does nothing if the device + * isn't registered as a hotspot bridge. */ + nm_bt_vtable_network_server->unregister_bridge (nm_bt_vtable_network_server, + device); + } +} + static gboolean enslave_slave (NMDevice *device, NMDevice *slave, @@ -384,20 +434,32 @@ create_and_realize (NMDevice *device, NMSettingBridge *s_bridge; const char *iface = nm_device_get_iface (device); const char *hwaddr; + gs_free char *hwaddr_cloned = NULL; guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX]; NMPlatformError plerr; - g_assert (iface); + nm_assert (iface); s_bridge = nm_connection_get_setting_bridge (connection); - g_assert (s_bridge); + nm_assert (s_bridge); + hwaddr = nm_setting_bridge_get_mac_address (s_bridge); + if ( !hwaddr + && nm_device_hw_addr_get_cloned (device, connection, FALSE, + &hwaddr_cloned, NULL, NULL)) { + /* The cloned MAC address might by dynamic, for example with stable-id="${RANDOM}". + * It's a bit odd that we first create the device with one dynamic address, + * and later on may reset it to another. That is, because we don't cache + * the dynamic address in @device, like we do during nm_device_hw_addr_set_cloned(). */ + hwaddr = hwaddr_cloned; + } + if (hwaddr) { if (!nm_utils_hwaddr_aton (hwaddr, mac_address, ETH_ALEN)) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "Invalid hardware address '%s'", hwaddr); - return FALSE; + g_return_val_if_reached (FALSE); } } @@ -411,7 +473,7 @@ create_and_realize (NMDevice *device, "Failed to create bridge interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -423,6 +485,7 @@ create_and_realize (NMDevice *device, static void nm_device_bridge_init (NMDeviceBridge * self) { + nm_assert (nm_device_is_master (NM_DEVICE (self))); } static void @@ -432,8 +495,8 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE) + parent_class->is_master = TRUE; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->is_available = is_available; parent_class->check_connection_compatible = check_connection_compatible; parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; @@ -443,6 +506,8 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) parent_class->create_and_realize = create_and_realize; parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->act_stage2_config = act_stage2_config; + parent_class->deactivate = deactivate; parent_class->enslave_slave = enslave_slave; parent_class->release_slave = release_slave; parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; @@ -470,12 +535,36 @@ create_device (NMDeviceFactory *factory, NM_DEVICE_TYPE_DESC, "Bridge", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_BRIDGE, - NM_DEVICE_IS_MASTER, TRUE, NULL); } +static gboolean +match_connection (NMDeviceFactory *factory, + NMConnection *connection) +{ + const char *type = nm_connection_get_connection_type (connection); + + if (nm_streq (type, NM_SETTING_BRIDGE_SETTING_NAME)) + return TRUE; + + nm_assert (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME)); + + if (!_nm_connection_get_setting_bluetooth_for_nap (connection)) + return FALSE; + + if (!g_type_from_name ("NMBluezManager")) { + /* bluetooth NAP connections are handled by bridge factory. However, + * it needs help from the bluetooth plugin, so if the plugin is not loaded, + * we claim not to support it. */ + return FALSE; + } + + return TRUE; +} + NM_DEVICE_FACTORY_DEFINE_INTERNAL (BRIDGE, Bridge, bridge, NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BRIDGE) - NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BRIDGE_SETTING_NAME), + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BLUETOOTH_SETTING_NAME), factory_class->create_device = create_device; + factory_class->match_connection = match_connection; ); diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h index f0fa1f4b..44b4ed72 100644 --- a/src/devices/nm-device-bridge.h +++ b/src/devices/nm-device-bridge.h @@ -35,4 +35,6 @@ typedef struct _NMDeviceBridgeClass NMDeviceBridgeClass; GType nm_device_bridge_get_type (void); +extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server; + #endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */ diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index dce4f7bc..085c44e6 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -112,7 +112,7 @@ create_and_realize (NMDevice *device, "Failed to create dummy interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 8a04d401..7807100f 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -108,14 +108,14 @@ typedef struct _NMDeviceEthernetPrivate { /* PPPoE */ NMPPPManager *ppp_manager; - NMIP4Config *pending_ip4_config; gint32 last_pppoe_time; guint pppoe_wait_id; /* DCB */ DcbWait dcb_wait; guint dcb_timeout_id; - gulong dcb_carrier_id; + + bool dcb_handle_carrier_changes:1; } NMDeviceEthernetPrivate; NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceEthernet, @@ -255,22 +255,6 @@ _update_s390_subchannels (NMDeviceEthernet *self) } static void -reset_8021x_autoconnect_retries (NMDevice *device) -{ - NMActRequest *req; - NMSettingsConnection *connection; - - req = nm_device_get_act_request (device); - if ( req - && nm_device_get_applied_setting (device, NM_TYPE_SETTING_802_1X)) { - connection = nm_act_request_get_settings_connection (req); - g_return_if_fail (connection); - /* Reset autoconnect retries on success, failure, or when deactivating */ - nm_settings_connection_reset_autoconnect_retries (connection); - } -} - -static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -278,12 +262,6 @@ device_state_changed (NMDevice *device, { if (new_state > NM_DEVICE_STATE_ACTIVATED) wired_secrets_cancel (NM_DEVICE_ETHERNET (device)); - - if (NM_IN_SET (new_state, - NM_DEVICE_STATE_ACTIVATED, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_DISCONNECTED)) - reset_8021x_autoconnect_retries (device); } static void @@ -294,7 +272,7 @@ nm_device_ethernet_init (NMDeviceEthernet *self) priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate); self->_priv = priv; - priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->s390_options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); } static NMDeviceCapabilities @@ -680,25 +658,20 @@ handle_auth_or_fail (NMDeviceEthernet *self, NMActRequest *req, gboolean new_secrets) { + NMDeviceEthernetPrivate *priv; const char *setting_name; NMConnection *applied_connection; - NMSettingsConnection *settings_connection; - int tries_left; - applied_connection = nm_act_request_get_applied_connection (req); - settings_connection = nm_act_request_get_settings_connection (req); + priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - tries_left = nm_settings_connection_get_autoconnect_retries (settings_connection); - if (tries_left == 0) + if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return NM_ACT_STAGE_RETURN_FAILURE; - if (tries_left > 0) - nm_settings_connection_set_autoconnect_retries (settings_connection, tries_left - 1); - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req)); + applied_connection = nm_act_request_get_applied_connection (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); if (setting_name) { wired_secrets_get_secrets (self, setting_name, @@ -1000,6 +973,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, static NMActStageReturn pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason) { + NMDevice *device = NM_DEVICE (self); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NMSettingPppoe *s_pppoe; NMActRequest *req; @@ -1011,9 +985,17 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_ s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); - priv->ppp_manager = nm_ppp_manager_create (nm_device_get_iface (NM_DEVICE (self)), + priv->ppp_manager = nm_ppp_manager_create (nm_device_get_iface (device), &err); + if (priv->ppp_manager) { + nm_ppp_manager_set_route_parameters (priv->ppp_manager, + nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_metric (device, AF_INET6)); + } + if ( !priv->ppp_manager || !nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_pppoe_get_username (s_pppoe), @@ -1133,7 +1115,7 @@ dcb_state (NMDevice *device, gboolean timeout) _LOGD (LOGD_DCB, "dcb_state() enabling DCB"); nm_clear_g_source (&priv->dcb_timeout_id); if (!dcb_enable (device)) { - nm_clear_g_signal_handler (device, &priv->dcb_carrier_id); + priv->dcb_handle_carrier_changes = FALSE; nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED); @@ -1157,7 +1139,7 @@ dcb_state (NMDevice *device, gboolean timeout) _LOGD (LOGD_DCB, "dcb_state() preconfig up configuring DCB"); nm_clear_g_source (&priv->dcb_timeout_id); if (!dcb_configure (device)) { - nm_clear_g_signal_handler (device, &priv->dcb_carrier_id); + priv->dcb_handle_carrier_changes = FALSE; nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED); @@ -1180,7 +1162,7 @@ dcb_state (NMDevice *device, gboolean timeout) if (timeout || carrier) { _LOGD (LOGD_DCB, "dcb_state() postconfig up starting IP"); nm_clear_g_source (&priv->dcb_timeout_id); - nm_clear_g_signal_handler (device, &priv->dcb_carrier_id); + priv->dcb_handle_carrier_changes = FALSE; priv->dcb_wait = DCB_WAIT_UNKNOWN; nm_device_activate_schedule_stage3_ip_config_start (device); } @@ -1190,20 +1172,6 @@ dcb_state (NMDevice *device, gboolean timeout) } } -static void -dcb_carrier_changed (NMDevice *device, GParamSpec *pspec, gpointer unused) -{ - NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - - g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); - - if (priv->dcb_timeout_id) { - _LOGD (LOGD_DCB, "carrier_changed() calling dcb_state()"); - dcb_state (device, FALSE); - } -} - /*****************************************************************************/ static gboolean @@ -1262,7 +1230,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); nm_clear_g_source (&priv->dcb_timeout_id); - nm_clear_g_signal_handler (device, &priv->dcb_carrier_id); + priv->dcb_handle_carrier_changes = FALSE; /* 802.1x has to run before any IP configuration since the 802.1x auth * process opens the port up for normal traffic. @@ -1296,13 +1264,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) priv->dcb_timeout_id = g_timeout_add_seconds (4, dcb_carrier_timeout, device); } - /* Watch carrier independently of NMDeviceClass::carrier_changed so - * we get instant notifications of disconnection that aren't deferred. - */ - priv->dcb_carrier_id = g_signal_connect (device, - "notify::" NM_DEVICE_CARRIER, - G_CALLBACK (dcb_carrier_changed), - NULL); + priv->dcb_handle_carrier_changes = TRUE; ret = NM_ACT_STAGE_RETURN_POSTPONE; } @@ -1367,16 +1329,8 @@ deactivate (NMDevice *device) NMSettingDcb *s_dcb; GError *error = NULL; - /* Clear wired secrets tries when deactivating */ - reset_8021x_autoconnect_retries (device); - nm_clear_g_source (&priv->pppoe_wait_id); - if (priv->pending_ip4_config) { - g_object_unref (priv->pending_ip4_config); - priv->pending_ip4_config = NULL; - } - if (priv->ppp_manager) { nm_ppp_manager_stop_sync (priv->ppp_manager); g_clear_object (&priv->ppp_manager); @@ -1386,7 +1340,7 @@ deactivate (NMDevice *device) priv->dcb_wait = DCB_WAIT_UNKNOWN; nm_clear_g_source (&priv->dcb_timeout_id); - nm_clear_g_signal_handler (device, &priv->dcb_carrier_id); + priv->dcb_handle_carrier_changes = FALSE; /* Tear down DCB/FCoE if it was enabled */ s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); @@ -1579,7 +1533,7 @@ update_connection (NMDevice *device, NMConnection *connection) } static void -get_link_speed (NMDevice *device) +link_speed_update (NMDevice *device) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); @@ -1591,16 +1545,28 @@ get_link_speed (NMDevice *device) return; priv->speed = speed; - _notify (self, PROP_SPEED); - _LOGD (LOGD_PLATFORM | LOGD_ETHER, "speed is now %d Mb/s", speed); + _notify (self, PROP_SPEED); } static void carrier_changed_notify (NMDevice *device, gboolean carrier) { + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (priv->dcb_handle_carrier_changes) { + nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); + + if (priv->dcb_timeout_id) { + _LOGD (LOGD_DCB, "carrier_changed() calling dcb_state()"); + dcb_state (device, FALSE); + } + } + if (carrier) - get_link_speed (device); + link_speed_update (device); + NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->carrier_changed_notify (device, carrier); } @@ -1682,7 +1648,6 @@ dispose (GObject *object) nm_clear_g_source (&priv->pppoe_wait_id); nm_clear_g_source (&priv->dcb_timeout_id); - nm_clear_g_signal_handler (self, &priv->dcb_carrier_id); G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object); } @@ -1810,8 +1775,24 @@ create_device (NMDeviceFactory *factory, NULL); } +static gboolean +match_connection (NMDeviceFactory *factory, NMConnection *connection) +{ + const char *type = nm_connection_get_connection_type (connection); + NMSettingPppoe *s_pppoe; + + if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME)) + return TRUE; + + nm_assert (nm_streq (type, NM_SETTING_PPPOE_SETTING_NAME)); + s_pppoe = nm_connection_get_setting_pppoe (connection); + + return !nm_setting_pppoe_get_parent (s_pppoe); +} + NM_DEVICE_FACTORY_DEFINE_INTERNAL (ETHERNET, Ethernet, ethernet, NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_ETHERNET) NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_PPPOE_SETTING_NAME), factory_class->create_device = create_device; + factory_class->match_connection = match_connection; ); diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index f512b8b2..97f011c5 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -30,6 +30,8 @@ #include "platform/nm-platform.h" #include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-setting-bluetooth.h" #define PLUGIN_PREFIX "libnm-device-plugin-" @@ -55,13 +57,12 @@ nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *compo gboolean consumed = FALSE; g_return_val_if_fail (NM_IS_DEVICE_FACTORY (factory), FALSE); - g_return_val_if_fail (G_IS_OBJECT (component), FALSE); g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed); return consumed; } -void +static void nm_device_factory_get_supported_types (NMDeviceFactory *factory, const NMLinkType **out_link_types, const char *const**out_setting_types) @@ -91,56 +92,26 @@ nm_device_factory_create_device (NMDeviceFactory *factory, GError **error) { NMDeviceFactoryClass *klass; - const NMLinkType *link_types = NULL; - const char *const*setting_types = NULL; - int i; NMDevice *device; gboolean ignore = FALSE; g_return_val_if_fail (factory, NULL); g_return_val_if_fail (iface && *iface, NULL); - g_return_val_if_fail (plink || connection, NULL); - g_return_val_if_fail (!plink || !connection, NULL); - - nm_device_factory_get_supported_types (factory, &link_types, &setting_types); - - NM_SET_OUT (out_ignore, FALSE); - if (plink) { + g_return_val_if_fail (!connection, NULL); g_return_val_if_fail (strcmp (iface, plink->name) == 0, NULL); - - for (i = 0; link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { - if (plink->type == link_types[i]) - break; - } - - if (link_types[i] == NM_LINK_TYPE_UNKNOWN) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, - "Device factory %s does not support link type %s (%d)", - G_OBJECT_TYPE_NAME (factory), - plink->kind, plink->type); - return NULL; - } - } else if (connection) { - for (i = 0; setting_types && setting_types[i]; i++) { - if (nm_connection_is_type (connection, setting_types[i])) - break; - } - - if (!setting_types[i]) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, - "Device factory %s does not support connection type %s", - G_OBJECT_TYPE_NAME (factory), - nm_connection_get_connection_type (connection)); - return NULL; - } - } + nm_assert (factory == nm_device_factory_manager_find_factory_for_link_type (plink->type)); + } else if (connection) + nm_assert (factory == nm_device_factory_manager_find_factory_for_connection (connection)); + else + g_return_val_if_reached (NULL); klass = NM_DEVICE_FACTORY_GET_CLASS (factory); if (!klass->create_device) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "Device factory %s cannot manage new devices", G_OBJECT_TYPE_NAME (factory)); + NM_SET_OUT (out_ignore, FALSE); return NULL; } @@ -253,51 +224,35 @@ _cleanup (void) g_clear_pointer (&factories_by_setting, g_hash_table_unref); } -static NMDeviceFactory * -find_factory (const NMLinkType *needle_link_types, - const char *const*needle_setting_types) -{ - NMDeviceFactory *found; - guint i; - - g_return_val_if_fail (factories_by_link, NULL); - g_return_val_if_fail (factories_by_setting, NULL); - - /* NMLinkType search */ - for (i = 0; needle_link_types && needle_link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { - found = g_hash_table_lookup (factories_by_link, GUINT_TO_POINTER (needle_link_types[i])); - if (found) - return found; - } - - /* NMSetting name search */ - for (i = 0; needle_setting_types && needle_setting_types[i]; i++) { - found = g_hash_table_lookup (factories_by_setting, needle_setting_types[i]); - if (found) - return found; - } - - return NULL; -} - NMDeviceFactory * nm_device_factory_manager_find_factory_for_link_type (NMLinkType link_type) { - const NMLinkType ltypes[2] = { link_type, NM_LINK_TYPE_NONE }; + g_return_val_if_fail (factories_by_link, NULL); - if (link_type == NM_LINK_TYPE_UNKNOWN) - return NULL; - g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, NULL); - return find_factory (ltypes, NULL); + return g_hash_table_lookup (factories_by_link, GUINT_TO_POINTER (link_type)); } NMDeviceFactory * nm_device_factory_manager_find_factory_for_connection (NMConnection *connection) { - const char *const stypes[2] = { nm_connection_get_connection_type (connection), NULL }; + NMDeviceFactoryClass *klass; + NMDeviceFactory *factory; + const char *type; + GSList *list; - g_assert (stypes[0]); - return find_factory (NULL, stypes); + g_return_val_if_fail (factories_by_setting, NULL); + + type = nm_connection_get_connection_type (connection); + list = g_hash_table_lookup (factories_by_setting, type); + + for (; list; list = g_slist_next (list)) { + factory = list->data; + klass = NM_DEVICE_FACTORY_GET_CLASS (factory); + if (!klass->match_connection || klass->match_connection (factory, connection)) + return factory; + } + + return NULL; } void @@ -318,9 +273,11 @@ nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc ca if (factories_by_setting) { g_hash_table_iter_init (&iter, factories_by_setting); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) { - if (!g_slist_find (list, factory)) - list = g_slist_prepend (list, factory); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &list_iter)) { + for (; list_iter; list_iter = g_slist_next (list_iter)) { + if (!g_slist_find (list, list_iter->data)) + list = g_slist_prepend (list, list_iter->data); + } } } @@ -332,36 +289,33 @@ nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc ca static gboolean _add_factory (NMDeviceFactory *factory, - gboolean check_duplicates, const char *path, NMDeviceFactoryManagerFactoryFunc callback, gpointer user_data) { - NMDeviceFactory *found = NULL; const NMLinkType *link_types = NULL; const char *const*setting_types = NULL; + GSList *list, *list2; int i; g_return_val_if_fail (factories_by_link, FALSE); g_return_val_if_fail (factories_by_setting, FALSE); nm_device_factory_get_supported_types (factory, &link_types, &setting_types); - if (check_duplicates) { - found = find_factory (link_types, setting_types); - if (found) { - nm_log_warn (LOGD_PLATFORM, "Loading device plugin failed: multiple plugins " - "for same type (using '%s' instead of '%s')", - (char *) g_object_get_qdata (G_OBJECT (found), plugin_path_quark ()), - path); - return FALSE; - } - } g_object_set_qdata_full (G_OBJECT (factory), plugin_path_quark (), g_strdup (path), g_free); for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) g_hash_table_insert (factories_by_link, GUINT_TO_POINTER (link_types[i]), g_object_ref (factory)); - for (i = 0; setting_types && setting_types[i]; i++) - g_hash_table_insert (factories_by_setting, (char *) setting_types[i], g_object_ref (factory)); + for (i = 0; setting_types && setting_types[i]; i++) { + list = g_hash_table_lookup (factories_by_setting, (char *) setting_types[i]); + if (list) { + list2 = g_slist_append (list, g_object_ref (factory)); + nm_assert (list == list2); + } else { + list = g_slist_append (list, g_object_ref (factory)); + g_hash_table_insert (factories_by_setting, (char *) setting_types[i], list); + } + } callback (factory, user_data); @@ -377,7 +331,13 @@ _load_internal_factory (GType factory_gtype, NMDeviceFactory *factory; factory = (NMDeviceFactory *) g_object_new (factory_gtype, NULL); - _add_factory (factory, FALSE, "internal", callback, user_data); + _add_factory (factory, "internal", callback, user_data); +} + +static void +factories_list_unref (GSList *list) +{ + g_slist_free_full (list, g_object_unref); } void @@ -392,7 +352,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call g_return_if_fail (factories_by_setting == NULL); factories_by_link = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); - factories_by_setting = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + factories_by_setting = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, (GDestroyNotify) factories_list_unref); #define _ADD_INTERNAL(get_type_fcn) \ G_STMT_START { \ @@ -409,6 +369,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call _ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type); _ADD_INTERNAL (nm_macsec_device_factory_get_type); _ADD_INTERNAL (nm_macvlan_device_factory_get_type); + _ADD_INTERNAL (nm_ppp_device_factory_get_type); _ADD_INTERNAL (nm_tun_device_factory_get_type); _ADD_INTERNAL (nm_veth_device_factory_get_type); _ADD_INTERNAL (nm_vlan_device_factory_get_type); @@ -452,7 +413,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call } g_clear_error (&error); - _add_factory (factory, TRUE, g_module_name (plugin), callback, user_data); + _add_factory (factory, g_module_name (plugin), callback, user_data); g_object_unref (factory); } diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index ff105da7..33b596e6 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -72,11 +72,19 @@ typedef struct { void (*start) (NMDeviceFactory *factory); /** + * match_connection: + * @connection: the #NMConnection + * + * Check if the factory supports the given connection. + */ + gboolean (*match_connection) (NMDeviceFactory *factory, NMConnection *connection); + + /** * get_connection_parent: * @factory: the #NMDeviceFactory * @connection: the #NMConnection to return the parent name for, if supported * - * Given a connection, returns the a parent interface name, parent connection + * Given a connection, returns the parent interface name, parent connection * UUID, or parent device permanent hardware address for @connection. * * Returns: the parent interface name, parent connection UUID, parent @@ -140,11 +148,15 @@ typedef struct { * @factory: the #NMDeviceFactory * @component: a new component which existing devices may wish to claim * - * The factory emits this signal when it finds a new component. For example, - * the WWAN factory may indicate that a new modem is available, which an - * existing Bluetooth device may wish to claim. If no device claims the - * component, the plugin is allowed to create a new #NMDevice instance for - * that component and emit the "device-added" signal. + * The factory emits this signal when an appearance of some component + * native to it could be interesting to some of the already existing devices. + * The devices then indicate if they took interest in claiming the component. + * + * For example, the WWAN factory may indicate that a new modem is available, + * which an existing Bluetooth device may wish to claim. It emits a signal + * passing the modem instance around to see if any device claims it. + * If no device claims the component, the plugin is allowed to create a new + * #NMDevice instance for that component and emit the "device-added" signal. * * Returns: %TRUE if the component was claimed by a device, %FALSE if not */ @@ -174,10 +186,6 @@ typedef NMDeviceFactory * (*NMDeviceFactoryCreateFunc) (GError **error); /*****************************************************************************/ -void nm_device_factory_get_supported_types (NMDeviceFactory *factory, - const NMLinkType **out_link_types, - const char *const**out_setting_types); - const char *nm_device_factory_get_connection_parent (NMDeviceFactory *factory, NMConnection *connection); diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 7e041270..09ad2855 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -269,13 +269,13 @@ create_and_realize (NMDevice *device, } if (!parent) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "InfiniBand partitions can not be created without a parent interface"); return FALSE; } if (!NM_IS_DEVICE_INFINIBAND (parent)) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "Parent interface %s must be an InfiniBand interface", nm_device_get_iface (parent)); return FALSE; @@ -283,7 +283,7 @@ create_and_realize (NMDevice *device, priv->parent_ifindex = nm_device_get_ifindex (parent); if (priv->parent_ifindex <= 0) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "failed to get InfiniBand parent %s ifindex", nm_device_get_iface (parent)); return FALSE; @@ -295,7 +295,7 @@ create_and_realize (NMDevice *device, "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", nm_device_get_iface (device), nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -324,7 +324,7 @@ unrealize (NMDevice *device, GError **error) g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to remove InfiniBand P_Key interface '%s': %s", nm_device_get_iface (device), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 2f505ef4..af3cfe4c 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -442,40 +442,6 @@ update_connection (NMDevice *device, NMConnection *connection) } static gboolean -match_parent (NMDevice *dev_parent, const char *setting_parent) -{ - g_return_val_if_fail (setting_parent, FALSE); - - if (!dev_parent) - return FALSE; - - if (nm_utils_is_uuid (setting_parent)) { - NMActRequest *parent_req; - NMConnection *parent_connection; - - /* If the parent is a UUID, the connection matches if our parent - * device has that connection activated. - */ - parent_req = nm_device_get_act_request (dev_parent); - if (!parent_req) - return FALSE; - - parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req)); - if (!parent_connection) - return FALSE; - - if (g_strcmp0 (setting_parent, nm_connection_get_uuid (parent_connection)) != 0) - return FALSE; - } else { - /* interface name */ - if (g_strcmp0 (setting_parent, nm_device_get_ip_iface (dev_parent)) != 0) - return FALSE; - } - - return TRUE; -} - -static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device); @@ -496,10 +462,8 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (nm_device_is_real (device)) { /* Check parent interface; could be an interface name or a UUID */ parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel); - if (parent) { - if (!match_parent (nm_device_parent_get_device (device), parent)) - return FALSE; - } + if (parent && !nm_device_match_parent (device, parent)) + return FALSE; if (!address_equal_pp (priv->addr_family, nm_setting_ip_tunnel_get_local (s_ip_tunnel), @@ -647,7 +611,7 @@ create_and_realize (NMDevice *device, "Failed to create GRE interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -670,10 +634,10 @@ create_and_realize (NMDevice *device, plerr = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, - "Failed to create SIT interface '%s' for '%s': %s", - iface, - nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + "Failed to create SIT interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -696,10 +660,10 @@ create_and_realize (NMDevice *device, plerr = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, - "Failed to create IPIP interface '%s' for '%s': %s", - iface, - nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + "Failed to create IPIP interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -728,7 +692,7 @@ create_and_realize (NMDevice *device, "Failed to create IPIP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } break; diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 8add3f6f..95587278 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -477,25 +477,20 @@ handle_auth_or_fail (NMDeviceMacsec *self, NMActRequest *req, gboolean new_secrets) { + NMDeviceMacsecPrivate *priv; const char *setting_name; - int tries_left; NMConnection *applied_connection; - NMSettingsConnection *settings_connection; - applied_connection = nm_act_request_get_applied_connection (req); - settings_connection = nm_act_request_get_settings_connection (req); + priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); - tries_left = nm_settings_connection_get_autoconnect_retries (settings_connection); - if (tries_left == 0) + if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return NM_ACT_STAGE_RETURN_FAILURE; - if (tries_left > 0) - nm_settings_connection_set_autoconnect_retries (settings_connection, tries_left - 1); - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req)); + applied_connection = nm_act_request_get_applied_connection (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); if (setting_name) { macsec_secrets_get_secrets (self, setting_name, @@ -692,7 +687,7 @@ create_and_realize (NMDevice *device, g_assert (s_macsec); if (!parent) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "MACsec devices can not be created without a parent interface"); return FALSE; } @@ -720,7 +715,7 @@ create_and_realize (NMDevice *device, "Failed to create macsec interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -739,21 +734,6 @@ link_changed (NMDevice *device, static void -reset_autoconnect_retries (NMDevice *device) -{ - NMActRequest *req; - NMSettingsConnection *connection; - - req = nm_device_get_act_request (device); - if (req) { - connection = nm_act_request_get_settings_connection (req); - g_return_if_fail (connection); - /* Reset autoconnect retries on success, failure, or when deactivating */ - nm_settings_connection_reset_autoconnect_retries (connection); - } -} - -static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -761,11 +741,6 @@ device_state_changed (NMDevice *device, { if (new_state > NM_DEVICE_STATE_ACTIVATED) macsec_secrets_cancel (NM_DEVICE_MACSEC (device)); - - if ( new_state == NM_DEVICE_STATE_ACTIVATED - || new_state == NM_DEVICE_STATE_FAILED - || new_state == NM_DEVICE_STATE_DISCONNECTED) - reset_autoconnect_retries (device); } /******************************************************************/ @@ -822,7 +797,7 @@ get_property (GObject *object, guint prop_id, } static void -nm_device_macsec_init (NMDeviceMacsec * self) +nm_device_macsec_init (NMDeviceMacsec *self) { } diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index cea2b984..2a461543 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -234,7 +234,7 @@ create_and_realize (NMDevice *device, parent_ifindex = parent ? nm_device_get_ifindex (parent) : 0; if (parent_ifindex <= 0) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "MACVLAN devices can not be created without a parent interface"); g_return_val_if_fail (!parent, FALSE); return FALSE; @@ -258,7 +258,7 @@ create_and_realize (NMDevice *device, lnk.tap ? "macvtap" : "macvlan", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -286,69 +286,6 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) /*****************************************************************************/ - -static gboolean -match_parent (NMDeviceMacvlan *self, const char *parent) -{ - NMDevice *parent_device; - - g_return_val_if_fail (parent != NULL, FALSE); - - parent_device = nm_device_parent_get_device (NM_DEVICE (self)); - if (!parent_device) - return FALSE; - - if (nm_utils_is_uuid (parent)) { - NMActRequest *parent_req; - NMConnection *parent_connection; - - /* If the parent is a UUID, the connection matches if our parent - * device has that connection activated. - */ - - parent_req = nm_device_get_act_request (parent_device); - if (!parent_req) - return FALSE; - - parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req)); - if (!parent_connection) - return FALSE; - - if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0) - return FALSE; - } else { - /* interface name */ - if (g_strcmp0 (parent, nm_device_get_ip_iface (parent_device)) != 0) - return FALSE; - } - - return TRUE; -} - -static gboolean -match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr) -{ - NMSettingWired *s_wired; - NMDevice *parent_device; - const char *setting_mac; - const char *parent_mac; - - s_wired = nm_connection_get_setting_wired (connection); - if (!s_wired) - return !fail_if_no_hwaddr; - - setting_mac = nm_setting_wired_get_mac_address (s_wired); - if (!setting_mac) - return !fail_if_no_hwaddr; - - parent_device = nm_device_parent_get_device (device); - if (!parent_device) - return !fail_if_no_hwaddr; - - parent_mac = nm_device_get_permanent_hw_address (parent_device); - return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); -} - static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -378,11 +315,11 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) /* Check parent interface; could be an interface name or a UUID */ parent = nm_setting_macvlan_get_parent (s_macvlan); if (parent) { - if (!match_parent (NM_DEVICE_MACVLAN (device), parent)) + if (!nm_device_match_parent (device, parent)) return FALSE; } else { /* Parent could be a MAC address in an NMSettingWired */ - if (!match_hwaddr (device, connection, TRUE)) + if (!nm_device_match_hwaddr (device, connection, TRUE)) return FALSE; } } @@ -419,7 +356,7 @@ complete_connection (NMDevice *device, * settings, then there's not enough information to complete the setting. */ if ( !nm_setting_macvlan_get_parent (s_macvlan) - && !match_hwaddr (device, connection, TRUE)) { + && !nm_device_match_hwaddr (device, connection, TRUE)) { g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "The 'macvlan' setting had no interface name, parent, or hardware address."); return FALSE; diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c new file mode 100644 index 00000000..8b3968d5 --- /dev/null +++ b/src/devices/nm-device-ppp.c @@ -0,0 +1,350 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ppp.h" + +#include "nm-act-request.h" +#include "nm-device-factory.h" +#include "nm-device-private.h" +#include "nm-manager.h" +#include "nm-setting-pppoe.h" +#include "platform/nm-platform.h" +#include "ppp/nm-ppp-manager.h" +#include "ppp/nm-ppp-manager-call.h" +#include "ppp/nm-ppp-status.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.Ppp.h" + +#include "nm-device-logging.h" +_LOG_DECLARE_SELF(NMDevicePpp); + +/*****************************************************************************/ + +typedef struct _NMDevicePppPrivate { + NMPPPManager *ppp_manager; + NMIP4Config *pending_ip4_config; + char *pending_ifname; +} NMDevicePppPrivate; + +struct _NMDevicePpp { + NMDevice parent; + NMDevicePppPrivate _priv; +}; + +struct _NMDevicePppClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE) + +#define NM_DEVICE_PPP_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDevicePpp, NM_IS_DEVICE_PPP) + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingPppoe *s_pppoe; + + if (!NM_DEVICE_CLASS (nm_device_ppp_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + if (!nm_streq0 (nm_connection_get_connection_type (connection), + NM_SETTING_PPPOE_SETTING_NAME)) + return FALSE; + + s_pppoe = nm_connection_get_setting_pppoe (connection); + nm_assert (s_pppoe); + + return !!nm_setting_pppoe_get_parent (s_pppoe); +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + +static void +ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (status) { + case NM_PPP_STATUS_DISCONNECT: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); + break; + case NM_PPP_STATUS_DEAD: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); + break; + case NM_PPP_STATUS_RUNNING: + nm_device_activate_schedule_stage3_ip_config_start (device); + break; + default: + break; + } +} + +static void +ppp_ip4_config (NMPPPManager *ppp_manager, + const char *iface, + NMIP4Config *config, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDevicePpp *self = NM_DEVICE_PPP (device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + gboolean renamed; + + _LOGT (LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd"); + + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + if (nm_device_activate_ip4_state_in_conf (device)) { + if (!nm_device_take_over_link (device, iface, &renamed)) { + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + if (renamed) + nm_manager_remove_device (nm_manager_get (), iface, NM_DEVICE_TYPE_PPP); + + nm_device_activate_schedule_ip4_config_result (device, config); + return; + } + } else { + if (priv->pending_ip4_config) + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = g_object_ref (config); + g_free (priv->pending_ifname); + priv->pending_ifname = g_strdup (iface); + } +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDevicePpp *self = NM_DEVICE_PPP (device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + NMSettingPppoe *s_pppoe; + NMActRequest *req; + GError *error = NULL; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); + + s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); + + g_clear_object (&priv->pending_ip4_config); + nm_clear_g_free (&priv->pending_ifname); + + priv->ppp_manager = nm_ppp_manager_create (nm_setting_pppoe_get_parent (s_pppoe), &error); + + if (priv->ppp_manager) { + nm_ppp_manager_set_route_parameters (priv->ppp_manager, + nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_metric (device, AF_INET6)); + } + + if ( !priv->ppp_manager + || !nm_ppp_manager_start (priv->ppp_manager, req, + nm_setting_pppoe_get_username (s_pppoe), + 30, 0, &error)) { + _LOGW (LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", error->message); + g_error_free (error); + + g_clear_object (&priv->ppp_manager); + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, + G_CALLBACK (ppp_state_changed), + self); + g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, + G_CALLBACK (ppp_ip4_config), + self); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + NMDevicePpp *self = NM_DEVICE_PPP (device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + gboolean renamed; + + if (priv->pending_ip4_config) { + if (!nm_device_take_over_link (device, priv->pending_ifname, &renamed)) + return NM_ACT_STAGE_RETURN_FAILURE; + if (renamed) + nm_manager_remove_device (nm_manager_get (), priv->pending_ifname, NM_DEVICE_TYPE_PPP); + if (out_config) + *out_config = g_steal_pointer (&priv->pending_ip4_config); + else + g_clear_object (&priv->pending_ip4_config); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + /* Wait IPCP termination */ + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + int parent_ifindex; + + if (!parent) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "PPP devices can not be created without a parent interface"); + return FALSE; + } + + parent_ifindex = nm_device_get_ifindex (parent); + g_warn_if_fail (parent_ifindex > 0); + + nm_device_parent_set_ifindex (device, parent_ifindex); + + /* The interface is created later */ + + return TRUE; +} + +static void +deactivate (NMDevice *device) +{ + NMDevicePpp *self = NM_DEVICE_PPP (device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + + if (priv->ppp_manager) { + nm_ppp_manager_stop_sync (priv->ppp_manager); + g_clear_object (&priv->ppp_manager); + } +} + +static void +nm_device_ppp_init (NMDevicePpp *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDevicePpp *self = NM_DEVICE_PPP (object); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + + g_clear_object (&priv->pending_ip4_config); + nm_clear_g_free (&priv->pending_ifname); + + G_OBJECT_CLASS (nm_device_ppp_parent_class)->dispose (object); +} + +static void +nm_device_ppp_class_init (NMDevicePppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_PPPOE_SETTING_NAME, NM_LINK_TYPE_PPP) + + object_class->dispose = dispose; + + parent_class->act_stage2_config = act_stage2_config; + parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + parent_class->check_connection_compatible = check_connection_compatible; + parent_class->create_and_realize = create_and_realize; + parent_class->deactivate = deactivate; + parent_class->get_generic_capabilities = get_generic_capabilities; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_PPP_SKELETON, + NULL); +} + +/*****************************************************************************/ + +#define NM_TYPE_PPP_DEVICE_FACTORY (nm_ppp_device_factory_get_type ()) +#define NM_PPP_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_DEVICE_FACTORY, NMPppDeviceFactory)) + +static NMDevice * +create_device (NMDeviceFactory *factory, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_PPP, + NM_DEVICE_IFACE, iface, + NM_DEVICE_TYPE_DESC, "Ppp", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_PPP, + NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_PPP, + NULL); +} + +static gboolean +match_connection (NMDeviceFactory *factory, NMConnection *connection) +{ + NMSettingPppoe *s_pppoe; + + s_pppoe = nm_connection_get_setting_pppoe (connection); + nm_assert (s_pppoe); + + return !!nm_setting_pppoe_get_parent (s_pppoe); +} + +static const char * +get_connection_parent (NMDeviceFactory *factory, NMConnection *connection) +{ + NMSettingPppoe *s_pppoe; + + nm_assert (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)); + + s_pppoe = nm_connection_get_setting_pppoe (connection); + nm_assert (s_pppoe); + + return nm_setting_pppoe_get_parent (s_pppoe); +} + +static char * +get_connection_iface (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface) +{ + nm_assert (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)); + + if (!parent_iface) + return NULL; + + return g_strdup (nm_connection_get_interface_name (connection)); +} + +NM_DEVICE_FACTORY_DEFINE_INTERNAL (PPP, Ppp, ppp, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_PPP) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_PPPOE_SETTING_NAME), + factory_class->get_connection_parent = get_connection_parent; + factory_class->get_connection_iface = get_connection_iface; + factory_class->create_device = create_device; + factory_class->match_connection = match_connection; +); diff --git a/src/devices/nm-device-ppp.h b/src/devices/nm-device-ppp.h new file mode 100644 index 00000000..aaa18b9b --- /dev/null +++ b/src/devices/nm-device-ppp.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_PPP_H__ +#define __NETWORKMANAGER_DEVICE_PPP_H__ + +#define NM_TYPE_DEVICE_PPP (nm_device_ppp_get_type ()) +#define NM_DEVICE_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_PPP, NMDevicePpp)) +#define NM_DEVICE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_PPP, NMDevicePppClass)) +#define NM_IS_DEVICE_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_PPP)) +#define NM_IS_DEVICE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_PPP)) +#define NM_DEVICE_PPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_PPP, NMDevicePppClass)) + +typedef struct _NMDevicePpp NMDevicePpp; +typedef struct _NMDevicePppClass NMDevicePppClass; + +GType nm_device_ppp_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_PPP_H__ */ diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 9eccafdc..f1486c54 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -57,6 +57,8 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar void nm_device_take_down (NMDevice *self, gboolean block); +gboolean nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed); + gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr, const char *detail, @@ -83,7 +85,6 @@ gboolean nm_device_activate_ip6_state_in_conf (NMDevice *device); gboolean nm_device_activate_ip6_state_in_wait (NMDevice *device); gboolean nm_device_activate_ip6_state_done (NMDevice *device); -void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout); void nm_device_set_dhcp_anycast_address (NMDevice *device, const char *addr); gboolean nm_device_dhcp4_renew (NMDevice *device, gboolean release); @@ -91,10 +92,6 @@ gboolean nm_device_dhcp6_renew (NMDevice *device, gboolean release); void nm_device_recheck_available_connections (NMDevice *device); -gboolean nm_device_get_enslaved (NMDevice *device); - -NMDevice *nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex); - void nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave, NMLogDomain log_domain); @@ -121,6 +118,8 @@ gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self, guint32 nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is_user_config); +void nm_device_commit_mtu (NMDevice *self); + /*****************************************************************************/ #define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \ @@ -135,4 +134,9 @@ gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setti #define nm_device_hash_check_invalid_keys(hash, setting_name, error, ...) \ _nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL })) +gboolean nm_device_match_parent (NMDevice *device, const char *parent); +gboolean nm_device_match_hwaddr (NMDevice *device, + NMConnection *connection, + gboolean fail_if_no_hwaddr); + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index b4af4416..a7d7c0bf 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -244,7 +244,7 @@ create_and_realize (NMDevice *device, "Failed to create TUN/TAP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index 11916c59..a8c4bcc8 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -37,12 +37,8 @@ _LOG_DECLARE_SELF(NMDeviceVeth); /*****************************************************************************/ -typedef struct { -} NMDeviceVethPrivate; - struct _NMDeviceVeth { NMDeviceEthernet parent; - NMDeviceVethPrivate _priv; }; struct _NMDeviceVethClass { @@ -57,8 +53,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceVeth, G_DEFINE_TYPE (NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET) -#define NM_DEVICE_VETH_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceVeth, NM_IS_DEVICE_VETH) - /*****************************************************************************/ static void diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index a74da8f2..e30dae74 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -51,6 +51,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceVlan, typedef struct { gulong parent_state_id; gulong parent_hwaddr_id; + gulong parent_mtu_id; guint vlan_id; } NMDeviceVlanPrivate; @@ -86,6 +87,17 @@ parent_state_changed (NMDevice *parent, } static void +parent_mtu_maybe_changed (NMDevice *parent, + GParamSpec *pspec, + gpointer user_data) +{ + /* the MTU of a VLAN device is limited by the parent's MTU. + * + * When the parent's MTU changes, try to re-set the MTU. */ + nm_device_commit_mtu (user_data); +} + +static void parent_hwaddr_maybe_changed (NMDevice *parent, GParamSpec *pspec, gpointer user_data) @@ -143,6 +155,7 @@ parent_changed_notify (NMDevice *device, * parent_changed_notify(). */ nm_clear_g_signal_handler (old_parent, &priv->parent_state_id); nm_clear_g_signal_handler (old_parent, &priv->parent_hwaddr_id); + nm_clear_g_signal_handler (old_parent, &priv->parent_mtu_id); if (new_parent) { priv->parent_state_id = g_signal_connect (new_parent, @@ -154,6 +167,10 @@ parent_changed_notify (NMDevice *device, G_CALLBACK (parent_hwaddr_maybe_changed), device); parent_hwaddr_maybe_changed (new_parent, NULL, self); + priv->parent_mtu_id = g_signal_connect (new_parent, "notify::" NM_DEVICE_MTU, + G_CALLBACK (parent_mtu_maybe_changed), device); + parent_mtu_maybe_changed (new_parent, NULL, self); + /* Set parent-dependent unmanaged flag */ nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PARENT, @@ -231,11 +248,20 @@ create_and_realize (NMDevice *device, g_assert (s_vlan); if (!parent) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, "VLAN devices can not be created without a parent interface"); return FALSE; } + parent_ifindex = nm_device_get_ifindex (parent); + if (parent_ifindex <= 0) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "cannot retrieve ifindex of interface %s (%s)", + nm_device_get_iface (parent), + nm_device_get_type_desc (parent)); + return FALSE; + } + if (!nm_device_supports_vlans (parent)) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "no support for VLANs on interface %s of type %s", @@ -244,9 +270,6 @@ create_and_realize (NMDevice *device, return FALSE; } - parent_ifindex = nm_device_get_ifindex (parent); - g_warn_if_fail (parent_ifindex > 0); - vlan_id = nm_setting_vlan_get_id (s_vlan); plerr = nm_platform_link_vlan_add (nm_device_get_platform (device), @@ -260,7 +283,7 @@ create_and_realize (NMDevice *device, "Failed to create VLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -309,68 +332,6 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) /*****************************************************************************/ static gboolean -match_parent (NMDeviceVlan *self, const char *parent) -{ - NMDevice *parent_device; - - g_return_val_if_fail (parent != NULL, FALSE); - - parent_device = nm_device_parent_get_device (NM_DEVICE (self)); - if (!parent_device) - return FALSE; - - if (nm_utils_is_uuid (parent)) { - NMActRequest *parent_req; - NMConnection *parent_connection; - - /* If the parent is a UUID, the connection matches if our parent - * device has that connection activated. - */ - - parent_req = nm_device_get_act_request (parent_device); - if (!parent_req) - return FALSE; - - parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req)); - if (!parent_connection) - return FALSE; - - if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0) - return FALSE; - } else { - /* interface name */ - if (g_strcmp0 (parent, nm_device_get_ip_iface (parent_device)) != 0) - return FALSE; - } - - return TRUE; -} - -static gboolean -match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr) -{ - NMSettingWired *s_wired; - NMDevice *parent_device; - const char *setting_mac; - const char *parent_mac; - - s_wired = nm_connection_get_setting_wired (connection); - if (!s_wired) - return !fail_if_no_hwaddr; - - setting_mac = nm_setting_wired_get_mac_address (s_wired); - if (!setting_mac) - return !fail_if_no_hwaddr; - - parent_device = nm_device_parent_get_device (device); - if (!parent_device) - return !fail_if_no_hwaddr; - - parent_mac = nm_device_get_permanent_hw_address (parent_device); - return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); -} - -static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE ((NMDeviceVlan *) device); @@ -392,11 +353,11 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) /* Check parent interface; could be an interface name or a UUID */ parent = nm_setting_vlan_get_parent (s_vlan); if (parent) { - if (!match_parent (NM_DEVICE_VLAN (device), parent)) + if (!nm_device_match_parent (device, parent)) return FALSE; } else { /* Parent could be a MAC address in an NMSettingWired */ - if (!match_hwaddr (device, connection, TRUE)) + if (!nm_device_match_hwaddr (device, connection, TRUE)) return FALSE; } } @@ -445,7 +406,7 @@ complete_connection (NMDevice *device, * settings, then there's not enough information to complete the setting. */ if ( !nm_setting_vlan_get_parent (s_vlan) - && !match_hwaddr (device, connection, TRUE)) { + && !nm_device_match_hwaddr (device, connection, TRUE)) { g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "The 'vlan' setting had no interface name, parent, or hardware address."); return FALSE; @@ -538,8 +499,10 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) /* Change MAC address to parent's one if needed */ parent_device = nm_device_parent_get_device (device); - if (parent_device) + if (parent_device) { parent_hwaddr_maybe_changed (parent_device, NULL, device); + parent_mtu_maybe_changed (parent_device, NULL, device); + } s_vlan = (NMSettingVlan *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_VLAN); if (s_vlan) { diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index d0b88874..d9efe840 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -223,7 +223,7 @@ create_and_realize (NMDevice *device, "Failed to create VXLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -231,43 +231,6 @@ create_and_realize (NMDevice *device, } static gboolean -match_parent (NMDeviceVxlan *self, const char *parent) -{ - NMDevice *parent_device; - - g_return_val_if_fail (parent != NULL, FALSE); - - parent_device = nm_device_parent_get_device (NM_DEVICE (self)); - if (!parent_device) - return FALSE; - - if (nm_utils_is_uuid (parent)) { - NMActRequest *parent_req; - NMConnection *parent_connection; - - /* If the parent is a UUID, the connection matches if our parent - * device has that connection activated. - */ - parent_req = nm_device_get_act_request (parent_device); - if (!parent_req) - return FALSE; - - parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req)); - if (!parent_connection) - return FALSE; - - if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0) - return FALSE; - } else { - /* interface name */ - if (g_strcmp0 (parent, nm_device_get_ip_iface (parent_device)) != 0) - return FALSE; - } - - return TRUE; -} - -static gboolean address_matches (const char *str, in_addr_t addr4, struct in6_addr *addr6) { in_addr_t new_addr4 = 0; @@ -302,8 +265,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (nm_device_is_real (device)) { parent = nm_setting_vxlan_get_parent (s_vxlan); - if ( parent - && !match_parent (NM_DEVICE_VXLAN (device), parent)) + if (parent && !nm_device_match_parent (device, parent)) return FALSE; if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan)) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index bacbfb33..e979b875 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -34,12 +34,16 @@ #include <arpa/inet.h> #include <fcntl.h> #include <linux/if_addr.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "nm-common-macros.h" #include "nm-device-private.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" #include "dhcp/nm-dhcp-manager.h" @@ -59,16 +63,16 @@ #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" +#include "nm-utils/c-list.h" #include "dns/nm-dns-manager.h" #include "nm-core-internal.h" -#include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" #include "nm-arping-manager.h" #include "nm-connectivity.h" #include "nm-dbus-interface.h" +#include "nm-device-vlan.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); @@ -82,6 +86,13 @@ _LOG_DECLARE_SELF (NMDevice); #define DHCP_NUM_TRIES_MAX 3 #define DEFAULT_AUTOCONNECT TRUE +#define CARRIER_WAIT_TIME_MS 5000 +#define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 + +#define NM_DEVICE_AUTH_RETRIES_UNSET -1 +#define NM_DEVICE_AUTH_RETRIES_INFINITY -2 +#define NM_DEVICE_AUTH_RETRIES_DEFAULT 3 + /*****************************************************************************/ typedef void (*ActivationHandleFunc) (NMDevice *self); @@ -106,6 +117,7 @@ typedef enum { } IpState; typedef struct { + CList lst_slave; NMDevice *slave; gulong watch_id; bool slave_is_enslaved; @@ -186,7 +198,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_IFINDEX, PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, - PROP_IS_MASTER, PROP_MASTER, PROP_PARENT, PROP_HW_ADDRESS, @@ -231,6 +242,8 @@ typedef struct _NMDevicePrivate { int parent_ifindex; + int auth_retries; + union { const guint8 hw_addr_len; /* read-only */ guint8 hw_addr_len_; @@ -312,6 +325,17 @@ typedef struct _NMDevicePrivate { guint32 mtu_initial; guint32 ip6_mtu_initial; + guint32 v4_route_table; + guint32 v6_route_table; + + /* when carrier goes away, we give a grace period of CARRIER_WAIT_TIME_MS + * until taking action. + * + * When changing MTU, the device might take longer then that. So, whenever + * NM changes the MTU it sets @carrier_wait_until_ms to CARRIER_WAIT_TIME_AFTER_MTU_MS + * in the future. This is used to extend the grace period in this particular case. */ + gint64 carrier_wait_until_ms; + bool carrier:1; bool ignore_carrier:1; @@ -322,10 +346,15 @@ typedef struct _NMDevicePrivate { bool v4_commit_first_time:1; bool v6_commit_first_time:1; + bool default_route_metric_penalty_ip4_has:1; + bool default_route_metric_penalty_ip6_has:1; + NMDeviceSysIfaceState sys_iface_state:2; + bool v4_route_table_initalized:1; + bool v6_route_table_initalized:1; + /* Generic DHCP stuff */ - guint32 dhcp_timeout; char * dhcp_anycast_address; char * current_stable_id; @@ -346,14 +375,7 @@ typedef struct _NMDevicePrivate { NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ GSList * vpn4_configs; /* VPNs which use this device */ - struct { - bool v4_has; - bool v4_is_assumed; - bool v6_has; - bool v6_is_assumed; - NMPlatformIP4Route v4; - NMPlatformIP6Route v6; - } default_route; + bool v4_has_shadowed_routes; const char *ip4_rp_filter; @@ -390,6 +412,7 @@ typedef struct _NMDevicePrivate { /* IPv4LL stuff */ sd_ipv4ll * ipv4ll; guint ipv4ll_timeout; + guint rt6_temporary_not_available_id; /* IPv4 DAD stuff */ struct { @@ -411,6 +434,8 @@ typedef struct _NMDevicePrivate { bool nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ NMIP6Config * dad6_ip6_config; + GHashTable * rt6_temporary_not_available; + NMNDisc * ndisc; gulong ndisc_changed_id; gulong ndisc_timeout_id; @@ -452,8 +477,7 @@ typedef struct _NMDevicePrivate { gulong master_ready_id; /* slave management */ - bool is_master; - GSList * slaves; /* list of SlaveInfo */ + CList slaves; /* list of SlaveInfo */ NMMetered metered; @@ -485,19 +509,18 @@ G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT) static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url); +static gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs); + static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, - guint32 default_route_metric, gboolean commit, - gboolean routes_full_sync); + GPtrArray *ip4_dev_route_blacklist); static gboolean ip4_config_merge_and_apply (NMDevice *self, - NMIP4Config *config, gboolean commit); static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit); @@ -514,7 +537,7 @@ static void nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect static void nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user); static const char *_activation_func_to_string (ActivationHandleFunc func); -static void activation_source_handle_cb (NMDevice *self, int family); +static void activation_source_handle_cb (NMDevice *self, int addr_family); static void _set_state_full (NMDevice *self, NMDeviceState state, @@ -525,7 +548,7 @@ static gboolean queued_ip4_config_change (gpointer user_data); static gboolean queued_ip6_config_change (gpointer user_data); static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data); static gboolean ip_config_valid (NMDeviceState state); -static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection); +static NMActStageReturn dhcp4_start (NMDevice *self); static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll); static void nm_device_start_ip_check (NMDevice *self); static void realize_start_setup (NMDevice *self, @@ -534,8 +557,9 @@ static void realize_start_setup (NMDevice *self, const char *assume_state_connection_uuid, gboolean set_nm_owned, NMUnmanFlagOp unmanaged_user_explicit); +static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); -static void dhcp_schedule_restart (NMDevice *self, int family, const char *reason); +static void dhcp_schedule_restart (NMDevice *self, int addr_family, const char *reason); static void _cancel_activation (NMDevice *self); /*****************************************************************************/ @@ -628,6 +652,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED, "parent-managed-changed"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"), ); #define reason_to_string(reason) \ @@ -647,12 +672,32 @@ nm_device_get_netns (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->netns; } +NMDedupMultiIndex * +nm_device_get_multi_index (NMDevice *self) +{ + return nm_netns_get_multi_idx (nm_device_get_netns (self)); +} + NMPlatform * nm_device_get_platform (NMDevice *self) { return nm_netns_get_platform (nm_device_get_netns (self)); } +static NMIP4Config * +_ip4_config_new (NMDevice *self) +{ + return nm_ip4_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + +static NMIP6Config * +_ip6_config_new (NMDevice *self) +{ + return nm_ip6_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState, @@ -714,6 +759,25 @@ nm_device_sys_iface_state_set (NMDevice *self, nm_assert (priv->sys_iface_state == sys_iface_state); } +static void +_active_connection_set_state_flags_full (NMDevice *self, + NMActivationStateFlags flags, + NMActivationStateFlags mask) +{ + NMActiveConnection *ac; + + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (self)); + if (ac) + nm_active_connection_set_state_flags_full (ac, flags, mask); +} + +static void +_active_connection_set_state_flags (NMDevice *self, + NMActivationStateFlags flags) +{ + _active_connection_set_state_flags_full (self, flags, flags); +} + /*****************************************************************************/ void @@ -796,26 +860,35 @@ nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *val NMPlatform *platform = nm_device_get_platform (self); gs_free char *value_to_free = NULL; const char *value_to_set; + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return FALSE; if (value) { value_to_set = value; } else { /* Set to a default value when we've got a NULL @value. */ value_to_free = nm_platform_sysctl_get (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path ("default", property))); + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, "default", property))); value_to_set = value_to_free; } return nm_platform_sysctl_set (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), value_to_set); } static guint32 nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return fallback; + return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), 10, 0, G_MAXUINT32, @@ -825,14 +898,24 @@ nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { - return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), value); + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return FALSE; + + return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), value); } static guint32 nm_device_ipv6_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return fallback; + return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), 10, 0, G_MAXUINT32, @@ -949,14 +1032,29 @@ _set_ip_state (NMDevice *self, int addr_family, IpState new_state) IpState *p; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert_addr_family (addr_family); - p = addr_family == AF_INET ? &priv->ip4_state_ : &priv->ip6_state_; + p = (addr_family == AF_INET) + ? &priv->ip4_state_ + : &priv->ip6_state_; if (*p != new_state) { - _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", addr_family == AF_INET ? '4' : '6', - (int) new_state, _ip_state_to_string (new_state)); + _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", + nm_utils_addr_family_to_char (addr_family), + (int) new_state, + _ip_state_to_string (new_state)); *p = new_state; + + if (new_state == IP_DONE) { + /* we only set the IPx_READY flag once we reach IP_DONE state. We don't + * ever clear it, even if we later enter IP_FAIL state. + * + * This is not documented/guaranteed behavior, but seems to make sense for now. */ + _active_connection_set_state_flags (self, + addr_family == AF_INET + ? NM_ACTIVATION_STATE_FLAG_IP4_READY + : NM_ACTIVATION_STATE_FLAG_IP6_READY); + } } } @@ -978,6 +1076,48 @@ nm_device_get_iface (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->iface; } +gboolean +nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const NMPlatformLink *plink; + NMPlatform *platform; + gboolean up, success = TRUE; + int ifindex; + + g_return_val_if_fail (priv->ifindex <= 0, FALSE); + g_return_val_if_fail (ifname, FALSE); + + NM_SET_OUT (renamed, FALSE); + + platform = nm_device_get_platform (self); + plink = nm_platform_link_get_by_ifname (platform, ifname); + if (!plink) + return FALSE; + + ifindex = plink->ifindex; + + if (!nm_streq (ifname, nm_device_get_iface (self))) { + up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + + /* Rename the link to the device ifname */ + if (up) + nm_platform_link_set_down (platform, ifindex); + success = nm_platform_link_set_name (platform, ifindex, nm_device_get_iface (self)); + if (up) + nm_platform_link_set_up (platform, ifindex, NULL); + + NM_SET_OUT (renamed, success); + } + + if (success) { + priv->ifindex = ifindex; + _notify (self, PROP_IFINDEX); + } + + return success; +} + int nm_device_get_ifindex (NMDevice *self) { @@ -1091,7 +1231,8 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) } if (priv->ip_ifindex > 0) { - if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ip_ifindex, TRUE); if (!nm_platform_link_is_up (nm_device_get_platform (self), priv->ip_ifindex)) @@ -1478,23 +1619,14 @@ nm_device_get_metered (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->metered; } -/** - * nm_device_get_priority(): - * @self: the #NMDevice - * - * Returns: the device's routing priority. Lower numbers means a "better" - * device, eg higher priority. - */ -int -nm_device_get_priority (NMDevice *self) +static guint32 +_get_route_metric_default (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (self), 1000); - /* Device 'priority' is used for the default route-metric and is based on * the device type. The settings ipv4.route-metric and ipv6.route-metric * can overwrite this default. * - * Currently for both IPv4 and IPv6 we use the same default values. + * For both IPv4 and IPv6 we use the same default values. * * The route-metric is used for the metric of the routes of device. * This also applies to the default route. Therefore it affects also @@ -1532,6 +1664,8 @@ nm_device_get_priority (NMDevice *self) return 425; case NM_DEVICE_TYPE_TUN: return 450; + case NM_DEVICE_TYPE_PPP: + return 460; case NM_DEVICE_TYPE_VXLAN: return 500; case NM_DEVICE_TYPE_DUMMY: @@ -1546,6 +1680,10 @@ nm_device_get_priority (NMDevice *self) return 700; case NM_DEVICE_TYPE_BT: return 750; + case NM_DEVICE_TYPE_OVS_BRIDGE: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + return 800; case NM_DEVICE_TYPE_GENERIC: return 950; case NM_DEVICE_TYPE_UNKNOWN: @@ -1558,29 +1696,40 @@ nm_device_get_priority (NMDevice *self) return 11000; } -static guint32 -route_metric_with_penalty (NMDevice *self, guint32 metric) +static gboolean +default_route_metric_penalty_detect (NMDevice *self) { #if WITH_CONCHECK NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const guint32 PENALTY = 20000; - - /* Beware: for IPv6, a metric of 0 effectively means 1024. - * Only pass a normalized IPv6 metric (nm_utils_ip6_route_metric_normalize). */ + /* currently we don't differentiate between IPv4 and IPv6 when detecting + * connectivity. */ if ( priv->connectivity_state != NM_CONNECTIVITY_FULL - && nm_connectivity_check_enabled (nm_connectivity_get ())) { - if (metric >= G_MAXUINT32 - PENALTY) - return G_MAXUINT32; - return metric + PENALTY; + && nm_connectivity_check_enabled (nm_connectivity_get ())) { + return TRUE; } #endif - return metric; + + return FALSE; } static guint32 -_get_ipx_route_metric (NMDevice *self, - gboolean is_v4) +default_route_metric_penalty_get (NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert_addr_family (addr_family); + + if ( addr_family == AF_INET + ? priv->default_route_metric_penalty_ip4_has + : priv->default_route_metric_penalty_ip6_has) + return 20000; + return 0; +} + +guint32 +nm_device_get_route_metric (NMDevice *self, + int addr_family) { char *value; gint64 route_metric; @@ -1588,10 +1737,11 @@ _get_ipx_route_metric (NMDevice *self, NMConnection *connection; g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), G_MAXUINT32); connection = nm_device_get_applied_connection (self); if (connection) { - s_ip = is_v4 + s_ip = addr_family == AF_INET ? nm_connection_get_setting_ip4_config (connection) : nm_connection_get_setting_ip6_config (connection); @@ -1610,7 +1760,7 @@ _get_ipx_route_metric (NMDevice *self, * Note that that means that the route-metric might change between SIGHUP. * You must cache the returned value if that is a problem. */ value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self); + addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric", self); if (value) { route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); g_free (value); @@ -1618,81 +1768,96 @@ _get_ipx_route_metric (NMDevice *self, if (route_metric >= 0) goto out; } - route_metric = nm_device_get_priority (self); + route_metric = _get_route_metric_default (self); out: - if (!is_v4) - route_metric = nm_utils_ip6_route_metric_normalize (route_metric); - return route_metric; + return nm_utils_ip_route_metric_normalize (addr_family, route_metric); } guint32 -nm_device_get_ip4_route_metric (NMDevice *self) +nm_device_get_route_table (NMDevice *self, + int addr_family, + gboolean fallback_main) { - return _get_ipx_route_metric (self, TRUE); -} + NMDevicePrivate *priv; + NMConnection *connection; + NMSettingIPConfig *s_ip; + guint32 route_table = 0; -guint32 -nm_device_get_ip6_route_metric (NMDevice *self) -{ - return _get_ipx_route_metric (self, FALSE); -} + nm_assert_addr_family (addr_family); -static void -_update_default_route (NMDevice *self, int addr_family, gboolean has, gboolean is_assumed) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - bool *p_has, *p_is_assumed; + g_return_val_if_fail (NM_IS_DEVICE (self), RT_TABLE_MAIN); - nm_assert (NM_IN_SET (addr_family, 0, AF_INET, AF_INET6)); + priv = NM_DEVICE_GET_PRIVATE (self); + /* the route table setting affects how we sync routes. We shall + * not change it while the device is active, hence, cache it. */ if (addr_family == AF_INET) { - p_has = &priv->default_route.v4_has; - p_is_assumed = &priv->default_route.v4_is_assumed; + if (priv->v4_route_table_initalized) + return priv->v4_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } else { - p_has = &priv->default_route.v6_has; - p_is_assumed = &priv->default_route.v6_is_assumed; + if (priv->v6_route_table_initalized) + return priv->v6_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } - if (*p_has == has && *p_is_assumed == is_assumed) - return; + connection = nm_device_get_applied_connection (self); + if (connection) { + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); - *p_has = has; - *p_is_assumed = is_assumed; + if (s_ip) + route_table = nm_setting_ip_config_get_route_table (s_ip); - if (addr_family == AF_INET) - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - else - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); -} - -const NMPlatformIP4Route * -nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed) -{ - NMDevicePrivate *priv; + /* we only lookup the global default if we also have an applied + * connection. Otherwise, the connection is not active, and the + * connection default doesn't matter. */ + if (route_table == 0) { + gs_free char *value = NULL; - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? "ipv4.route-table" + : "ipv6.route-table", + self); + route_table = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 0); + } + } - priv = NM_DEVICE_GET_PRIVATE (self); + if (addr_family == AF_INET) { + priv->v4_route_table_initalized = TRUE; + priv->v4_route_table = route_table; + } else { + priv->v6_route_table_initalized = TRUE; + priv->v6_route_table = route_table; + } - if (out_is_assumed) - *out_is_assumed = priv->default_route.v4_is_assumed; + _LOGT (LOGD_DEVICE, + "ipv%c.route-table = %u%s", + addr_family == AF_INET ? '4' : '6', + (guint) (route_table ?: RT_TABLE_MAIN), + route_table ? "" : " (policy routing not enabled)"); - return priv->default_route.v4_has ? &priv->default_route.v4 : NULL; + return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } -const NMPlatformIP6Route * -nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed) +const NMPObject * +nm_device_get_best_default_route (NMDevice *self, + int addr_family) { - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (out_is_assumed) - *out_is_assumed = priv->default_route.v6_is_assumed; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - return priv->default_route.v6_has ? &priv->default_route.v6 : NULL; + switch (addr_family) { + case AF_INET: + return priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL; + case AF_INET6: + return priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL; + case AF_UNSPEC: + return (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL) + ?: (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL); + default: + g_return_val_if_reached (NULL); + } } const char * @@ -1809,7 +1974,7 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) /* If the connectivity check is disabled, make an optimistic guess. */ if (state == NM_CONNECTIVITY_UNKNOWN) { if (priv->state == NM_DEVICE_STATE_ACTIVATED) { - if (priv->default_route.v4_has || priv->default_route.v6_has) + if (nm_device_get_best_default_route (self, AF_UNSPEC)) state = NM_CONNECTIVITY_FULL; else state = NM_CONNECTIVITY_LIMITED; @@ -1829,12 +1994,12 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) if ( priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external (self)) { - if ( priv->default_route.v4_has - && !ip4_config_merge_and_apply (self, NULL, TRUE)) - _LOGW (LOGD_IP4, "Failed to update IPv4 default route metric"); - if ( priv->default_route.v6_has + if ( nm_device_get_best_default_route (self, AF_INET) + && !ip4_config_merge_and_apply (self, TRUE)) + _LOGW (LOGD_IP4, "Failed to update IPv4 route metric"); + if ( nm_device_get_best_default_route (self, AF_INET6) && !ip6_config_merge_and_apply (self, TRUE)) - _LOGW (LOGD_IP6, "Failed to update IPv6 default route metric"); + _LOGW (LOGD_IP6, "Failed to update IPv6 route metric"); } } } @@ -1948,7 +2113,7 @@ concheck_periodic_update (NMDevice *self) gboolean check_enable; check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) - && (priv->default_route.v4_has || priv->default_route.v6_has); + && nm_device_get_best_default_route (self, AF_UNSPEC); if (check_enable && !priv->concheck_periodic_id) { /* We just gained a default route. Enable periodic checking. */ @@ -1976,11 +2141,11 @@ static SlaveInfo * find_slave_info (NMDevice *self, NMDevice *slave) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + CList *iter; SlaveInfo *info; - GSList *iter; - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - info = iter->data; + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); if (info->slave == slave) return info; } @@ -2100,13 +2265,19 @@ nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean co * Transfers ownership from slave_priv->master. */ self_free = self; - priv->slaves = g_slist_remove (priv->slaves, info); + c_list_unlink_init (&info->lst_slave); slave_priv->master = NULL; g_signal_handler_disconnect (slave, info->watch_id); g_object_unref (slave); g_slice_free (SlaveInfo, info); + if (c_list_is_empty (&priv->slaves)) { + _active_connection_set_state_flags_full (self, + 0, + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES); + } + /* Ensure the device's hardware address is up-to-date; it often changes * when slaves change. */ @@ -2140,7 +2311,8 @@ is_unmanaged_external_down (NMDevice *self, gboolean consider_can) /* Manage externally-created software interfaces only when they are IFF_UP */ if ( priv->ifindex <= 0 || !priv->up - || !(priv->slaves || nm_platform_link_can_assume (nm_device_get_platform (self), priv->ifindex))) + || !( !c_list_is_empty (&priv->slaves) + || nm_platform_link_can_assume (nm_device_get_platform (self), priv->ifindex))) return NM_UNMAN_FLAG_OP_SET_UNMANAGED; return NM_UNMAN_FLAG_OP_SET_MANAGED; @@ -2235,8 +2407,6 @@ carrier_changed (NMDevice *self, gboolean carrier) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); - if (priv->state <= NM_DEVICE_STATE_UNMANAGED) return; @@ -2246,21 +2416,22 @@ carrier_changed (NMDevice *self, gboolean carrier) if (priv->ignore_carrier && !carrier) return; - if (priv->is_master) { - /* Bridge/bond/team carrier does not affect its own activation, - * but when carrier comes on, if there are slaves waiting, - * it will restart them. - */ - if (!carrier) + if (nm_device_is_master (self)) { + if (carrier) { + /* Force master to retry getting ip addresses when carrier + * is restored. */ + if (priv->state == NM_DEVICE_STATE_ACTIVATED) + nm_device_update_dynamic_ip_setup (self); + else { + if (nm_device_activate_ip4_state_in_wait (self)) + nm_device_activate_stage3_ip4_start (self); + if (nm_device_activate_ip6_state_in_wait (self)) + nm_device_activate_stage3_ip6_start (self); + } return; - - if (nm_device_activate_ip4_state_in_wait (self)) - nm_device_activate_stage3_ip4_start (self); - if (nm_device_activate_ip6_state_in_wait (self)) - nm_device_activate_stage3_ip6_start (self); - - return; - } else if (nm_device_get_enslaved (self) && !carrier) { + } + /* fall-through and change state of device */ + } else if (priv->is_enslaved && !carrier) { /* Slaves don't deactivate when they lose carrier; for * bonds/teams in particular that would be actively * counterproductive. @@ -2306,7 +2477,7 @@ carrier_disconnected_action_cb (gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DEVICE, "link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id); + _LOGD (LOGD_DEVICE, "carrier: link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id); priv->carrier_defer_id = 0; carrier_changed (self, FALSE); @@ -2320,7 +2491,7 @@ carrier_disconnected_action_cancel (NMDevice *self) guint id = priv->carrier_defer_id; if (nm_clear_g_source (&priv->carrier_defer_id)) { - _LOGD (LOGD_DEVICE, "link disconnected (canceling deferred action) (id=%u)", + _LOGD (LOGD_DEVICE, "carrier: link disconnected (canceling deferred action) (id=%u)", id); } } @@ -2338,8 +2509,9 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) _notify (self, PROP_CARRIER); if (priv->carrier) { - _LOGI (LOGD_DEVICE, "link connected"); + _LOGI (LOGD_DEVICE, "carrier: link connected"); carrier_disconnected_action_cancel (self); + NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); carrier_changed (self, TRUE); if (priv->carrier_wait_id) { @@ -2349,14 +2521,15 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) } else { if (priv->carrier_wait_id) nm_device_add_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); + NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); if ( state <= NM_DEVICE_STATE_DISCONNECTED && !priv->queued_act_request) { - _LOGD (LOGD_DEVICE, "link disconnected"); + _LOGD (LOGD_DEVICE, "carrier: link disconnected"); carrier_changed (self, FALSE); } else { priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY, carrier_disconnected_action_cb, self); - _LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)", + _LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %d seconds) (id=%u)", LINK_DISCONNECT_DELAY, priv->carrier_defer_id); } } @@ -2388,12 +2561,27 @@ static void device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *master; + nm_auto_nmpobj const NMPObject *plink_master_keep_alive = NULL; + const NMPlatformLink *plink_master; g_return_if_fail (plink); if (plink->master <= 0) return; + master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); + plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master); + plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master)); + + if ( master == NULL + && plink_master + && g_strcmp0 (plink_master->name, "ovs-system") == 0 + && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) { + _LOGD (LOGD_DEVICE, "the device claimed by openvswitch"); + return; + } + if (priv->master) { if ( plink->master > 0 && plink->master == nm_device_get_ifindex (priv->master)) { @@ -2405,20 +2593,16 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (plink->master > 0) { - NMDevice *master; - master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); - if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) - nm_device_master_add_slave (master, self, FALSE); - else if (master) { - _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (master)); - } else { - _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", - plink->master, - nm_platform_link_get_name (nm_device_get_platform (self), plink->master)); - } + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) + nm_device_master_add_slave (master, self, FALSE); + else if (master) { + _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (master)); + } else { + _LOGW (LOGD_DEVICE, "enslaved to unknown device %d (%s%s%s)", + plink->master, + NM_PRINT_FMT_QUOTED (plink_master, "\"", plink_master->name, "\"", "??")); } } @@ -2429,16 +2613,19 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) gint32 now; GArray *addresses, *dns_servers, *dns_domains; guint len, i; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter ipconf_iter; if (nm_ndisc_get_node_type (ndisc) != NM_NDISC_NODE_TYPE_ROUTER) return; now = nm_utils_get_monotonic_timestamp_s (); - len = nm_ip6_config_get_num_addresses (priv->ip6_config); - addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress), len); - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *addr = nm_ip6_config_get_address (priv->ip6_config, i); + head_entry = nm_ip6_config_lookup_addresses (priv->ip6_config); + addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress), + head_entry ? head_entry->len : 0); + nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { + const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj); NMNDiscAddress *ndisc_addr; if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) @@ -2497,8 +2684,7 @@ device_link_changed (NMDevice *self) NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean ip_ifname_changed = FALSE; - const char *udi; - NMPlatformLink info; + nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL; const NMPlatformLink *pllink; int ifindex; gboolean was_up; @@ -2512,40 +2698,20 @@ device_link_changed (NMDevice *self) if (!pllink) return G_SOURCE_REMOVE; - info = *pllink; + pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink)); - udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { - /* Update UDI to what udev gives us */ - g_free (priv->udi); - priv->udi = g_strdup (udi); - _notify (self, PROP_UDI); - } - - if (!nm_streq0 (info.driver, priv->driver)) { - g_free (priv->driver); - priv->driver = g_strdup (info.driver); - _notify (self, PROP_DRIVER); - } - - if (priv->mtu != info.mtu) { - priv->mtu = info.mtu; - _notify (self, PROP_MTU); - } - - if (ifindex == nm_device_get_ip_ifindex (self)) - _stats_update_counters_from_pllink (self, &info); + nm_device_update_from_platform_link (self, pllink); had_hw_addr = (priv->hw_addr != NULL); nm_device_update_hw_address (self); got_hw_addr = (!had_hw_addr && priv->hw_addr); nm_device_update_permanent_hw_address (self, FALSE); - if (info.name[0] && strcmp (priv->iface, info.name) != 0) { + if (pllink->name[0] && strcmp (priv->iface, pllink->name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", - priv->ifindex, priv->iface, info.name); + priv->ifindex, priv->iface, pllink->name); g_free (priv->iface); - priv->iface = g_strdup (info.name); + priv->iface = g_strdup (pllink->name); /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; @@ -2568,8 +2734,8 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - if (priv->ndisc && info.inet6_token.id) { - if (nm_ndisc_set_iid (priv->ndisc, info.inet6_token)) + if (priv->ndisc && pllink->inet6_token.id) { + if (nm_ndisc_set_iid (priv->ndisc, pllink->inet6_token)) _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -2578,20 +2744,21 @@ device_link_changed (NMDevice *self) && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) nm_device_set_carrier (self, pllink->connected); - klass->link_changed (self, &info); + klass->link_changed (self, pllink); /* Update DHCP, etc, if needed */ if (ip_ifname_changed) nm_device_update_dynamic_ip_setup (self); was_up = priv->up; - priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP); + priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP); - if ( info.initialized + if ( pllink->initialized && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { NMDeviceStateReason reason; nm_device_set_unmanaged_by_user_udev (self); + nm_device_set_unmanaged_by_user_conf (self); reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; @@ -2616,13 +2783,13 @@ device_link_changed (NMDevice *self) set_unmanaged_external_down (self, FALSE); - device_recheck_slave_status (self, &info); + device_recheck_slave_status (self, pllink); if (priv->up && !was_up) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); } if (priv->ip6_state == IP_DONE) { @@ -2703,6 +2870,103 @@ link_changed_cb (NMPlatform *platform, } } +/*****************************************************************************/ + +typedef struct { + in_addr_t network; + guint8 plen; +} IP4RPFilterData; + +static guint +_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) +{ + NMHashState h; + + nm_hash_init (&h, 1105201169u); + nm_hash_update_vals (&h, + d->network, + d->plen); + return nm_hash_complete (&h); +} + +static gboolean +_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) +{ + return d1->network == d2->network && d1->plen == d2->plen; +} + +static gboolean +_v4_has_shadowed_routes_detect (NMDevice *self) +{ + NMPlatform *platform; + int ifindex; + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + guint data_len; + gs_unref_hashtable GHashTable *data_hash = NULL; + gs_free IP4RPFilterData *data_arr = NULL; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return FALSE; + + platform = nm_device_get_platform (self); + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)); + if (!head_entry) + return FALSE; + + /* first, create a lookup index @data_hash for all network/plen pairs. */ + data_len = 0; + data_arr = g_new (IP4RPFilterData, head_entry->len); + data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, + (GEqualFunc) _v4_has_shadowed_routes_detect_equal); + + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData *d; + + nm_assert (r->ifindex == ifindex); + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d = &data_arr[data_len++]; + d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d->plen = r->plen; + g_hash_table_add (data_hash, d); + } + + /* then, search if there is any route on another interface with the same + * network/plen destination. If yes, we consider this a multihoming + * setup. */ + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE)); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData d; + + if ( r->ifindex == ifindex + || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d.plen = r->plen; + if (g_hash_table_contains (data_hash, &d)) + return TRUE; + } + + return FALSE; +} + static void ip4_rp_filter_update (NMDevice *self) { @@ -2710,7 +2974,7 @@ ip4_rp_filter_update (NMDevice *self) const char *ip4_rp_filter; if ( priv->v4_has_shadowed_routes - || priv->default_route.v4_has) { + || nm_device_get_best_default_route (self, AF_INET)) { if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) { /* Don't touch the rp_filter if it's not strict. */ return; @@ -2729,20 +2993,6 @@ ip4_rp_filter_update (NMDevice *self) } static void -ip4_routes_changed_changed_cb (NMRouteManager *route_manager, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ip_ifindex (self); - - if (nm_device_sys_iface_state_is_external_or_assume (self)) - return; - - priv->v4_has_shadowed_routes = nm_route_manager_ip4_routes_shadowed (route_manager, - ifindex); - ip4_rp_filter_update (self); -} - -static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { /* stub implementation of virtual function to allow subclasses to chain up. */ @@ -2822,7 +3072,9 @@ nm_device_realize_start (NMDevice *self, gboolean *out_compatible, GError **error) { - NMPlatformLink plink_copy; + nm_auto_nmpobj const NMPObject *plink_keep_alive = NULL; + + nm_assert (!plink || NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); NM_SET_OUT (out_compatible, TRUE); @@ -2836,13 +3088,12 @@ nm_device_realize_start (NMDevice *self, if (!link_type_compatible (self, plink->type, out_compatible, error)) return FALSE; - } - if (plink) { - plink_copy = *plink; - plink = &plink_copy; + plink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); } - realize_start_setup (self, plink, + + realize_start_setup (self, + plink, assume_state_guess_assume, assume_state_connection_uuid, set_nm_owned, @@ -2868,8 +3119,8 @@ nm_device_create_and_realize (NMDevice *self, NMDevice *parent, GError **error) { + nm_auto_nmpobj const NMPObject *plink_keep_alive = NULL; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMPlatformLink plink_copy; const NMPlatformLink *plink = NULL; /* Must be set before device is realized */ @@ -2881,11 +3132,14 @@ nm_device_create_and_realize (NMDevice *self, if (NM_DEVICE_GET_CLASS (self)->create_and_realize) { if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error)) return FALSE; - plink_copy = *plink; - plink = &plink_copy; + if (plink) { + nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); + plink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); + } } - realize_start_setup (self, plink, + realize_start_setup (self, + plink, FALSE, /* assume_state_guess_assume */ NULL, /* assume_state_connection_uuid */ FALSE, NM_UNMAN_FLAG_OP_FORGET); @@ -2899,38 +3153,54 @@ nm_device_create_and_realize (NMDevice *self, return TRUE; } -static void -update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink) +void +nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *udi; + const char *str; + int ifindex; + guint32 mtu; - g_return_if_fail (plink != NULL); + g_return_if_fail (plink == NULL || link_type_compatible (self, plink->type, NULL, NULL)); - udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { + str = plink ? nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex) : NULL; + if (g_strcmp0 (str, priv->udi)) { g_free (priv->udi); - priv->udi = g_strdup (udi); + priv->udi = g_strdup (str); _notify (self, PROP_UDI); } - if (!g_strcmp0 (plink->name, priv->iface)) { + str = plink ? plink->name : NULL; + if (str && g_strcmp0 (str, priv->iface)) { g_free (priv->iface); - priv->iface = g_strdup (plink->name); + priv->iface = g_strdup (str); _notify (self, PROP_IFACE); } - if (priv->ifindex != plink->ifindex) { - priv->ifindex = plink->ifindex; - _notify (self, PROP_IFINDEX); - } - - priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); - if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) { + str = plink ? plink->driver : NULL; + if (g_strcmp0 (str, priv->driver) != 0) { g_free (priv->driver); - priv->driver = g_strdup (plink->driver); + priv->driver = g_strdup (str); _notify (self, PROP_DRIVER); } + + if (plink) { + priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + if (plink->ifindex == nm_device_get_ip_ifindex (self)) + _stats_update_counters_from_pllink (self, plink); + } else { + priv->up = FALSE; + } + + mtu = plink ? plink->mtu : 0; + _set_mtu (self, mtu); + + ifindex = plink ? plink->ifindex : 0; + if (priv->ifindex != ifindex) { + priv->ifindex = ifindex; + _notify (self, PROP_IFINDEX); + NM_DEVICE_GET_CLASS (self)->link_changed (self, plink); + } } static void @@ -2943,7 +3213,7 @@ device_init_sriov_num_vfs (NMDevice *self) if ( priv->ifindex > 0 && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)) { value = nm_config_data_get_device_config (NM_CONFIG_GET_DATA, - "sriov-num-vfs", + NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, self, NULL); num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1); @@ -3010,7 +3280,10 @@ realize_start_setup (NMDevice *self, NMDeviceCapabilities capabilities = 0; NMConfig *config; guint real_rate; - guint32 mtu; + + /* plink is a NMPlatformLink type, however, we require it to come from the platform + * cache (where else would it come from?). */ + nm_assert (!plink || NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); g_return_if_fail (NM_IS_DEVICE (self)); @@ -3035,20 +3308,14 @@ realize_start_setup (NMDevice *self, priv->mtu_initial = 0; priv->ip6_mtu_initial = 0; priv->ip6_mtu = 0; - if (priv->mtu) { - priv->mtu = 0; - _notify (self, PROP_MTU); - } + _set_mtu (self, 0); _assume_state_set (self, assume_state_guess_assume, assume_state_connection_uuid); nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); - if (plink) { - g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL)); - update_device_from_platform_link (self, plink); - _stats_update_counters_from_pllink (self, plink); - } + if (plink) + nm_device_update_from_platform_link (self, plink); if (priv->ifindex > 0) { priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex); @@ -3059,11 +3326,9 @@ realize_start_setup (NMDevice *self, if (nm_platform_link_is_software (nm_device_get_platform (self), priv->ifindex)) capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; - mtu = nm_platform_link_get_mtu (nm_device_get_platform (self), priv->ifindex); - if (priv->mtu != mtu) { - priv->mtu = mtu; - _notify (self, PROP_MTU); - } + _set_mtu (self, + nm_platform_link_get_mtu (nm_device_get_platform (self), + priv->ifindex)); nm_platform_link_get_driver_info (nm_device_get_platform (self), priv->ifindex, @@ -3075,7 +3340,8 @@ realize_start_setup (NMDevice *self, if (priv->firmware_version) _notify (self, PROP_FIRMWARE_VERSION); - if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex)) @@ -3147,6 +3413,7 @@ realize_start_setup (NMDevice *self, nm_device_set_unmanaged_flags (self, NM_UNMANAGED_LOOPBACK, priv->ifindex == 1); nm_device_set_unmanaged_by_user_udev (self); + nm_device_set_unmanaged_by_user_conf (self); nm_device_set_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT, plink && !plink->initialized); @@ -3257,7 +3524,6 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) g_return_val_if_fail (priv->iface != NULL, FALSE); g_return_val_if_fail (priv->real, FALSE); - g_object_freeze_notify (G_OBJECT (self)); ifindex = nm_device_get_ifindex (self); @@ -3274,6 +3540,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) } } + g_object_freeze_notify (G_OBJECT (self)); NM_DEVICE_GET_CLASS (self)->unrealize_notify (self); _parent_set_ifindex (self, 0, FALSE); @@ -3286,10 +3553,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) if (nm_clear_g_free (&priv->ip_iface)) _notify (self, PROP_IP_IFACE); - if (priv->mtu != 0) { - priv->mtu = 0; - _notify (self, PROP_MTU); - } + _set_mtu (self, 0); if (priv->driver_version) { g_clear_pointer (&priv->driver_version, g_free); @@ -3373,13 +3637,25 @@ gboolean nm_device_notify_component_added (NMDevice *self, GObject *component) { NMDeviceClass *klass; + NMDevicePrivate *priv; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - g_return_val_if_fail (G_IS_OBJECT (component), FALSE); + priv = NM_DEVICE_GET_PRIVATE (self); klass = NM_DEVICE_GET_CLASS (self); + + if (priv->state == NM_DEVICE_STATE_DISCONNECTED) { + /* A device could have stayed disconnected because it would + * want to register with a network server that now become + * available. */ + nm_device_recheck_available_connections (self); + if (g_hash_table_size (priv->available_connections) > 0) + nm_device_emit_recheck_auto_activate (self); + } + if (klass->component_added) return klass->component_added (self, component); + return FALSE; } @@ -3453,7 +3729,8 @@ slave_state_changed (NMDevice *slave, configure, reason); /* Bridge/bond/team interfaces are left up until manually deactivated */ - if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) + if ( c_list_is_empty (&priv->slaves) + && priv->state == NM_DEVICE_STATE_ACTIVATED) _LOGD (LOGD_DEVICE, "last slave removed; remaining activated"); } } @@ -3504,9 +3781,12 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) info->watch_id = g_signal_connect (slave, NM_DEVICE_STATE_CHANGED, G_CALLBACK (slave_state_changed), self); - priv->slaves = g_slist_append (priv->slaves, info); + c_list_link_tail (&priv->slaves, &info->lst_slave); slave_priv->master = g_object_ref (self); + _active_connection_set_state_flags (self, + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES); + /* no need to emit * * _notify (slave, PROP_MASTER); @@ -3527,46 +3807,6 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) } /** - * nm_device_master_get_slaves: - * @self: the master device - * - * Returns: any slaves of which @self is the master. Caller owns returned list. - */ -static GSList * -nm_device_master_get_slaves (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GSList *slaves = NULL, *iter; - - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) - slaves = g_slist_prepend (slaves, ((SlaveInfo *) iter->data)->slave); - - return slaves; -} - -/** - * nm_device_master_get_slave_by_ifindex: - * @self: the master device - * @ifindex: the slave's interface index - * - * Returns: the slave with the given @ifindex of which @self is the master, - * or %NULL if no device with @ifindex is a slave of @self. - */ -NMDevice * -nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex) -{ - GSList *iter; - - for (iter = NM_DEVICE_GET_PRIVATE (self)->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; - - if (nm_device_get_ip_ifindex (info->slave) == ifindex) - return info->slave; - } - return NULL; -} - -/** * nm_device_master_check_slave_physical_port: * @self: the master device * @slave: a slave device @@ -3582,14 +3822,14 @@ nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *slave_physical_port_id, *existing_physical_port_id; SlaveInfo *info; - GSList *iter; + CList *iter; slave_physical_port_id = nm_device_get_physical_port_id (slave); if (!slave_physical_port_id) return; - for (iter = priv->slaves; iter; iter = iter->next) { - info = iter->data; + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); if (info->slave == slave) continue; @@ -3615,6 +3855,7 @@ nm_device_master_release_slaves (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason reason; gboolean configure = TRUE; + CList *iter, *safe; /* Don't release the slaves if this connection doesn't belong to NM. */ if (nm_device_sys_iface_state_is_external (self)) @@ -3627,8 +3868,8 @@ nm_device_master_release_slaves (NMDevice *self) if (!nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) configure = FALSE; - while (priv->slaves) { - SlaveInfo *info = priv->slaves->data; + c_list_for_each_safe (iter, safe, &priv->slaves) { + SlaveInfo *info = c_list_entry (iter, SlaveInfo, lst_slave); nm_device_master_release_one_slave (self, info->slave, configure, reason); } @@ -3643,7 +3884,9 @@ nm_device_master_release_slaves (NMDevice *self) gboolean nm_device_is_master (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (self)->is_master; + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_CLASS (self)->is_master; } /** @@ -3669,6 +3912,113 @@ nm_device_get_master (NMDevice *self) return NULL; } +static gboolean +get_ip_config_may_fail (NMDevice *self, int addr_family) +{ + NMConnection *connection; + NMSettingIPConfig *s_ip = NULL; + + connection = nm_device_get_applied_connection (self); + + /* Fail the connection if the failed IP method is required to complete */ + switch (addr_family) { + case AF_INET: + s_ip = nm_connection_get_setting_ip4_config (connection); + break; + case AF_INET6: + s_ip = nm_connection_get_setting_ip6_config (connection); + break; + default: + nm_assert_not_reached (); + } + + return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); +} + +/* + * check_ip_state + * + * Transition the device from IP_CONFIG to the next state according to the + * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are + * called just after the initial configuration and thus IPv4/IPv6 are allowed to + * fail if the ipvx.may-fail properties say so, because the IP methods couldn't + * even be started. + */ +static void +check_ip_state (NMDevice *self, gboolean may_fail) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; + NMSettingIPConfig *s_ip4, *s_ip6; + NMDeviceState state; + + if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) + return; + + /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the + * master to enslave us. */ + if ( nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)) + && !priv->is_enslaved) + return; + + s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ip4_disabled = TRUE; + + s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), + NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + ip6_ignore = TRUE; + + if ( priv->ip4_state == IP_DONE + && priv->ip6_state == IP_DONE) { + /* Both method completed (or disabled), proceed with activation */ + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } + + if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) + && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { + /* Either both methods failed, or only one failed and the other is + * disabled */ + if (nm_device_sys_iface_state_is_external_or_assume (self)) { + /* We have assumed configuration, but couldn't redo it. No problem, + * move to check state. */ + _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET6, IP_DONE); + state = NM_DEVICE_STATE_IP_CHECK; + } else if ( may_fail + && get_ip_config_may_fail (self, AF_INET) + && get_ip_config_may_fail (self, AF_INET6)) { + /* Couldn't start either IPv6 and IPv4 autoconfiguration, + * but both are allowed to fail. */ + state = NM_DEVICE_STATE_SECONDARIES; + } else { + /* Autoconfiguration attempted without success. */ + state = NM_DEVICE_STATE_FAILED; + } + + nm_device_state_changed (self, + state, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + + /* If a method is still pending but required, wait */ + if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) + return; + if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) + return; + + /* If at least a method has completed, proceed with activation */ + if ( (priv->ip4_state == IP_DONE && !ip4_disabled) + || (priv->ip6_state == IP_DONE && !ip6_ignore)) { + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } +} + /** * nm_device_slave_notify_enslave: * @self: the slave device @@ -3705,10 +4055,8 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success) } if (activating) { - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); if (success) - nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + check_ip_state (self, FALSE); else nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); } else @@ -3767,19 +4115,6 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) } /** - * nm_device_get_enslaved: - * @self: the #NMDevice - * - * Returns: %TRUE if the device is enslaved to a master device (eg bridge or - * bond or team), %FALSE if not - */ -gboolean -nm_device_get_enslaved (NMDevice *self) -{ - return NM_DEVICE_GET_PRIVATE (self)->is_enslaved; -} - -/** * nm_device_removed: * @self: the #NMDevice * @unconfigure_ip_config: whether to clear the IP config objects @@ -3805,17 +4140,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) if (!unconfigure_ip_config) return; - /* Clean up IP configs; this does not actually deconfigure the - * interface, it just clears the configuration to which policy - * is reacting via NM_DEVICE_IP4_CONFIG_CHANGED/NM_DEVICE_IP6_CONFIG_CHANGED - * signal. As NMPolicy registered the NMIPxConfig instances in NMDnsManager, - * these would be leaked otherwise. */ - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); - nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE); - nm_device_set_ip6_config (self, NULL, FALSE, FALSE); + nm_device_set_ip4_config (self, NULL, FALSE, NULL); + nm_device_set_ip6_config (self, NULL, FALSE); } static gboolean @@ -3823,12 +4149,17 @@ is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->carrier || priv->ignore_carrier) + if ( priv->carrier + || priv->ignore_carrier) return TRUE; if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER)) return TRUE; + /* master types are always available even without carrier. */ + if (nm_device_is_master (self)) + return TRUE; + return FALSE; } @@ -3863,6 +4194,13 @@ nm_device_is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) } gboolean +nm_device_ignore_carrier_by_default (NMDevice *self) +{ + /* master types ignore-carrier by default. */ + return nm_device_is_master (self); +} + +gboolean nm_device_get_enabled (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -4057,7 +4395,8 @@ device_has_config (NMDevice *self) return TRUE; /* Master-slave relationship is also a configuration */ - if (priv->slaves || nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) > 0) + if ( !c_list_is_empty (&priv->slaves) + || nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) > 0) return TRUE; return FALSE; @@ -4179,6 +4518,8 @@ nm_device_generate_connection (NMDevice *self, s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); nm_connection_add_setting (connection, s_ip6); + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ifindex); if (pllink && pllink->inet6_token.id) { g_object_set (s_ip6, @@ -4206,7 +4547,7 @@ nm_device_generate_connection (NMDevice *self, if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) - && !priv->slaves) { + && c_list_is_empty (&priv->slaves)) { NM_SET_OUT (out_maybe_later, TRUE); g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "ignoring generated connection (no IP and not in master-slave relationship)"); @@ -4219,7 +4560,7 @@ nm_device_generate_connection (NMDevice *self, if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) - && !priv->slaves + && c_list_is_empty (&priv->slaves) && !nm_config_data_get_assume_ipv6ll_only (NM_CONFIG_GET_DATA, self)) { _LOGD (LOGD_DEVICE, "ignoring generated connection (IPv6LL-only and not in master-slave relationship)"); NM_SET_OUT (out_maybe_later, TRUE); @@ -4261,6 +4602,65 @@ nm_device_complete_connection (NMDevice *self, return success; } +gboolean +nm_device_match_parent (NMDevice *self, const char *parent) +{ + NMDevice *parent_device; + + g_return_val_if_fail (parent, FALSE); + + parent_device = nm_device_parent_get_device (self); + if (!parent_device) + return FALSE; + + if (nm_utils_is_uuid (parent)) { + NMConnection *connection; + + /* If the parent is a UUID, the connection matches when there is + * no connection active on the device or when a connection with + * that UUID is active. + */ + connection = nm_device_get_applied_connection (self); + if (!connection) + return TRUE; + + if (!nm_streq0 (parent, nm_connection_get_uuid (connection))) + return FALSE; + } else { + /* Interface name */ + if (!nm_streq0 (parent, nm_device_get_ip_iface (parent_device))) + return FALSE; + } + + return TRUE; +} + +gboolean +nm_device_match_hwaddr (NMDevice *device, + NMConnection *connection, + gboolean fail_if_no_hwaddr) +{ + NMSettingWired *s_wired; + NMDevice *parent_device; + const char *setting_mac; + const char *parent_mac; + + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) + return !fail_if_no_hwaddr; + + setting_mac = nm_setting_wired_get_mac_address (s_wired); + if (!setting_mac) + return !fail_if_no_hwaddr; + + parent_device = nm_device_parent_get_device (device); + if (!parent_device) + return !fail_if_no_hwaddr; + + parent_mac = nm_device_get_permanent_hw_address (parent_device); + return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); +} + static gboolean check_connection_compatible (NMDevice *self, NMConnection *connection) { @@ -4317,7 +4717,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->is_master) + if (!nm_device_is_master (self)) return FALSE; /* All masters should have connection type set */ @@ -4506,44 +4906,49 @@ activation_source_handle_cb6 (gpointer user_data) static ActivationHandleData * activation_source_get_by_family (NMDevice *self, - int family, + int addr_family, GSourceFunc *out_idle_func) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (family == AF_INET6) { + switch (addr_family) { + case AF_INET6: NM_SET_OUT (out_idle_func, activation_source_handle_cb6); return &priv->act_handle6; - } else { + case AF_INET: NM_SET_OUT (out_idle_func, activation_source_handle_cb4); - g_return_val_if_fail (family == AF_INET, &priv->act_handle4); return &priv->act_handle4; } + g_return_val_if_reached (NULL); } static void -activation_source_clear (NMDevice *self, int family) +activation_source_clear (NMDevice *self, + int addr_family) { ActivationHandleData *act_data; - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); if (act_data->id) { - _LOGD (LOGD_DEVICE, "activation-stage: clear %s,%d (id %u)", - _activation_func_to_string (act_data->func), family, act_data->id); + _LOGD (LOGD_DEVICE, "activation-stage: clear %s,v%c (id %u)", + _activation_func_to_string (act_data->func), + nm_utils_addr_family_to_char (addr_family), + act_data->id); nm_clear_g_source (&act_data->id); act_data->func = NULL; } } static void -activation_source_handle_cb (NMDevice *self, int family) +activation_source_handle_cb (NMDevice *self, + int addr_family) { ActivationHandleData *act_data, a; g_return_if_fail (NM_IS_DEVICE (self)); - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); g_return_if_fail (act_data->id); g_return_if_fail (act_data->func); @@ -4553,23 +4958,27 @@ activation_source_handle_cb (NMDevice *self, int family) act_data->func = NULL; act_data->id = 0; - _LOGD (LOGD_DEVICE, "activation-stage: invoke %s,%d (id %u)", - _activation_func_to_string (a.func), family, a.id); + _LOGD (LOGD_DEVICE, "activation-stage: invoke %s,v%c (id %u)", + _activation_func_to_string (a.func), + nm_utils_addr_family_to_char (addr_family), + a.id); a.func (self); - _LOGD (LOGD_DEVICE, "activation-stage: complete %s,%d (id %u)", - _activation_func_to_string (a.func), family, a.id); + _LOGD (LOGD_DEVICE, "activation-stage: complete %s,v%c (id %u)", + _activation_func_to_string (a.func), + nm_utils_addr_family_to_char (addr_family), + a.id); } static void -activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int family) +activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int addr_family) { ActivationHandleData *act_data; GSourceFunc source_func; guint new_id = 0; - act_data = activation_source_get_by_family (self, family, &source_func); + act_data = activation_source_get_by_family (self, addr_family, &source_func); if (act_data->id && act_data->func == func) { /* Don't bother rescheduling the same function that's about to @@ -4577,22 +4986,28 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil * streams of associate events before NM has had a chance to process * the first one. */ - _LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,%d (id %u)", - _activation_func_to_string (func), family, act_data->id); + _LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,v%c (id %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + act_data->id); return; } new_id = g_idle_add (source_func, self); if (act_data->id) { - _LOGW (LOGD_DEVICE, "activation-stage: schedule %s,%d which replaces %s,%d (id %u -> %u)", - _activation_func_to_string (func), family, - _activation_func_to_string (act_data->func), family, + _LOGW (LOGD_DEVICE, "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + _activation_func_to_string (act_data->func), + nm_utils_addr_family_to_char (addr_family), act_data->id, new_id); nm_clear_g_source (&act_data->id); } else { - _LOGD (LOGD_DEVICE, "activation-stage: schedule %s,%d (id %u)", - _activation_func_to_string (func), family, new_id); + _LOGD (LOGD_DEVICE, "activation-stage: schedule %s,v%c (id %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + new_id); } act_data->func = func; @@ -4600,42 +5015,18 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil } static gboolean -activation_source_is_scheduled (NMDevice *self, ActivationHandleFunc func, int family) +activation_source_is_scheduled (NMDevice *self, + ActivationHandleFunc func, + int addr_family) { ActivationHandleData *act_data; - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); return act_data->func == func; } /*****************************************************************************/ -static gboolean -get_ip_config_may_fail (NMDevice *self, int family) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; - - g_return_val_if_fail (self != NULL, TRUE); - - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - /* Fail the connection if the failed IP method is required to complete */ - switch (family) { - case AF_INET: - s_ip = nm_connection_get_setting_ip4_config (connection); - break; - case AF_INET6: - s_ip = nm_connection_get_setting_ip6_config (connection); - break; - default: - g_assert_not_reached (); - } - - return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); -} - static void master_ready (NMDevice *self, NMActiveConnection *active) @@ -4836,7 +5227,7 @@ activate_stage2_device_config (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret; gboolean no_firmware = FALSE; - GSList *iter; + CList *iter; nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); @@ -4863,8 +5254,8 @@ activate_stage2_device_config (NMDevice *self) } /* If we have slaves that aren't yet enslaved, do that now */ - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; + c_list_for_each (iter, &priv->slaves) { + SlaveInfo *info = c_list_entry (iter, SlaveInfo, lst_slave); NMDeviceState slave_state = nm_device_get_state (info->slave); if (slave_state == NM_DEVICE_STATE_IP_CONFIG) @@ -4930,97 +5321,21 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) activation_source_schedule (self, activate_stage2_device_config, AF_INET); } -/* - * check_ip_state - * - * Transition the device from IP_CONFIG to the next state according to the - * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are - * called just after the initial configuration and thus IPv4/IPv6 are allowed to - * fail if the ipvx.may-fail properties say so, because the IP methods couldn't - * even be started. - */ -static void -check_ip_state (NMDevice *self, gboolean may_fail) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; - NMSettingIPConfig *s_ip4, *s_ip6; - NMDeviceState state; - - if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) - return; - - s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ip4_disabled = TRUE; - - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) - ip6_ignore = TRUE; - - if ( priv->ip4_state == IP_DONE - && priv->ip6_state == IP_DONE) { - /* Both method completed (or disabled), proceed with activation */ - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } - - if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) - && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { - /* Either both methods failed, or only one failed and the other is - * disabled */ - if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* We have assumed configuration, but couldn't redo it. No problem, - * move to check state. */ - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); - state = NM_DEVICE_STATE_IP_CHECK; - } else if ( may_fail - && get_ip_config_may_fail (self, AF_INET) - && get_ip_config_may_fail (self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - nm_device_state_changed (self, - state, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* If a method is still pending but required, wait */ - if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) - return; - if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) - return; - - /* If at least a method has completed, proceed with activation */ - if ( (priv->ip4_state == IP_DONE && !ip4_disabled) - || (priv->ip6_state == IP_DONE && !ip6_ignore)) { - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } -} - void -nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason) +nm_device_ip_method_failed (NMDevice *self, + int addr_family, + NMDeviceStateReason reason) { NMDevicePrivate *priv; g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (family == AF_INET || family == AF_INET6); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); priv = NM_DEVICE_GET_PRIVATE (self); - _set_ip_state (self, family, IP_FAIL); + _set_ip_state (self, addr_family, IP_FAIL); - if (get_ip_config_may_fail (self, family)) + if (get_ip_config_may_fail (self, addr_family)) check_ip_state (self, FALSE); else nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); @@ -5077,7 +5392,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes NMIP4Config *empty; if (success) { - empty = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + empty = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, empty); g_object_unref (empty); } else { @@ -5091,17 +5406,17 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da { NMDevice *self; NMDevicePrivate *priv; + NMDedupMultiIter ipconf_iter; const NMPlatformIP4Address *address; gboolean result, success = TRUE; - int i, j; + int i; g_assert (data); self = data->device; priv = NM_DEVICE_GET_PRIVATE (self); for (i = 0; data->configs && data->configs[i]; i++) { - for (j = 0; j < nm_ip4_config_get_num_addresses (data->configs[i]); j++) { - address = nm_ip4_config_get_address (data->configs[i], j); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { result = nm_arping_manager_check_address (arping_manager, address->address); success &= result; @@ -5135,13 +5450,14 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMArpingManager *arping_manager; const NMPlatformIP4Address *address; + NMDedupMultiIter ipconf_iter; ArpingData *data; guint timeout; gboolean ret, addr_found; const guint8 *hw_addr; size_t hw_addr_len = 0; GError *error = NULL; - guint i, j; + guint i; g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (configs); @@ -5187,10 +5503,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) data->device = self; for (i = 0; configs[i]; i++) { - for (j = 0; j < nm_ip4_config_get_num_addresses (configs[i]); j++) { - address = nm_ip4_config_get_address (configs[i], j); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) nm_arping_manager_add_address (arping_manager, address->address); - } } g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED, @@ -5234,7 +5548,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) NMPlatformIP4Address address; NMPlatformIP4Route route; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); g_assert (config); memset (&address, 0, sizeof (address)); @@ -5247,8 +5561,9 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) route.network = htonl (0xE0000000L); route.plen = 4; route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; - route.metric = nm_device_get_ip4_route_metric (self); - nm_ip4_config_add_route (config, &route); + route.table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (self, AF_INET, TRUE)); + route.metric = nm_device_get_route_metric (self, AF_INET); + nm_ip4_config_add_route (config, &route, NULL); return config; } @@ -5304,7 +5619,9 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) nm_clear_g_source (&priv->ipv4ll_timeout); nm_device_activate_schedule_ip4_config_result (self, config); } else if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, config, TRUE)) { + g_clear_object (&priv->dev_ip4_config); + priv->dev_ip4_config = g_object_ref (config); + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); } @@ -5399,57 +5716,10 @@ ipv4ll_start (NMDevice *self) /*****************************************************************************/ -static gboolean -_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) -{ - gboolean success = FALSE; - int ifindex = nm_device_get_ip_ifindex (self); - GArray *routes; - - if (addr_family == AF_INET) - routes = nm_platform_ip4_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - else - routes = nm_platform_ip6_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - - if (routes) { - guint route_metric = G_MAXUINT32, m; - const NMPlatformIPRoute *route = NULL, *r; - guint i; - - /* if there are several default routes, find the one with the best metric */ - for (i = 0; i < routes->len; i++) { - if (addr_family == AF_INET) { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); - m = r->metric; - } else { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); - m = nm_utils_ip6_route_metric_normalize (r->metric); - } - if (!route || m < route_metric) { - route = r; - route_metric = m; - } - } - - if (route) { - if (addr_family == AF_INET) - *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); - else - *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); - success = TRUE; - } - g_array_free (routes, TRUE); - } - return success; -} - -/*****************************************************************************/ - static void ensure_con_ip4_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip4_config) @@ -5459,10 +5729,11 @@ ensure_con_ip4_config (NMDevice *self) if (!connection) return; - priv->con_ip4_config = nm_ip4_config_new (ip_ifindex); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* For assumed connections ignore all addresses and routes. */ @@ -5475,7 +5746,6 @@ static void ensure_con_ip6_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip6_config) @@ -5485,10 +5755,11 @@ ensure_con_ip6_config (NMDevice *self) if (!connection) return; - priv->con_ip6_config = nm_ip6_config_new (ip_ifindex); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, nm_connection_get_setting_ip6_config (connection), - nm_device_get_ip6_route_metric (self)); + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* For assumed connections ignore all addresses and routes. */ @@ -5527,38 +5798,19 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } -static void -_ip4_config_merge_default (gpointer value, gpointer user_data) -{ - NMIP4Config *src = (NMIP4Config *) value; - NMIP4Config *dst = (NMIP4Config *) user_data; - - nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); -} - static gboolean ip4_config_merge_and_apply (NMDevice *self, - NMIP4Config *config, gboolean commit) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; gboolean success; NMIP4Config *composite; - gboolean has_direct_route; - const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); - guint32 gateway; - gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; - - /* Merge all the configs into the composite config */ - if (config) { - g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); - } + gboolean ignore_default_routes = FALSE; + GSList *iter; + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -5569,35 +5821,38 @@ ip4_config_merge_and_apply (NMDevice *self, ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4); - if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_AUTO)) - auto_method = TRUE; + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + ignore_default_routes = nm_setting_ip_config_get_never_default (s_ip4) + || nm_setting_ip_config_get_gateway (s_ip4); } } - composite = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip4_config_new (self); init_ip4_config_dns_priority (self, composite); if (commit) { + if (priv->queued_ip4_config_id) + update_ext_ip_config (self, AF_INET, FALSE, FALSE); ensure_con_ip4_config (self); - if (priv->queued_ip4_config_id) { - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - FALSE); - } } + if (commit) + priv->default_route_metric_penalty_ip4_has = default_route_metric_penalty_detect (self); + if (priv->dev_ip4_config) { nm_ip4_config_merge (composite, priv->dev_ip4_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET)); } - g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite); + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); if (priv->ext_ip4_config) - nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. @@ -5605,107 +5860,23 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip4_config) { nm_ip4_config_merge (composite, priv->wwan_ip4_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET)); } /* Merge user overrides into the composite config. For assumed connections, * con_ip4_config is empty. */ - if (priv->con_ip4_config) - nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip4_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v4_has = FALSE; - priv->default_route.v4_is_assumed = TRUE; - - if (!commit) { - /* during a non-commit event, we always pickup whatever is configured. */ - goto END_ADD_DEFAULT_ROUTE; - } - - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) - goto END_ADD_DEFAULT_ROUTE; - - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - - connection_has_default_route - = nm_default_route_manager_ip4_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns), - connection, &connection_is_never_default); - - if ( !priv->v4_commit_first_time - && connection_is_never_default) { - /* If the connection is explicitly configured as never-default, we enforce the (absence of the) - * default-route only once. That allows the user to configure a connection as never-default, - * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ - goto END_ADD_DEFAULT_ROUTE; - } - - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v4_is_assumed = FALSE; - - if (!connection_has_default_route) - goto END_ADD_DEFAULT_ROUTE; - - if (!nm_ip4_config_get_num_addresses (composite)) { - /* without addresses we can have no default route. */ - goto END_ADD_DEFAULT_ROUTE; - } - - gateway = nm_ip4_config_get_gateway (composite); - if ( !nm_ip4_config_has_gateway (composite) - && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM) - goto END_ADD_DEFAULT_ROUTE; - - has_direct_route = ( gateway == 0 - || nm_ip4_config_destination_is_direct (composite, gateway, 32) - || nm_ip4_config_get_direct_route_for_host (composite, gateway)); - - priv->default_route.v4_has = TRUE; - memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); - priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v4.gateway = gateway; - priv->default_route.v4.metric = route_metric_with_penalty (self, default_route_metric); - priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP4Route r = priv->default_route.v4; - - /* add a direct route to the gateway */ - r.network = gateway; - r.plen = 32; - r.gateway = 0; - nm_ip4_config_add_route (composite, &r); + if (priv->con_ip4_config) { + nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, + default_route_metric_penalty_get (self, AF_INET)); } -END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v4_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4); + if (commit) { + nm_ip4_config_add_dependent_routes (composite, + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET), + &ip4_dev_route_blacklist); } if (commit) { @@ -5713,11 +5884,7 @@ END_ADD_DEFAULT_ROUTE: NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - routes_full_sync = commit - && priv->v4_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync); + success = nm_device_set_ip4_config (self, composite, commit, ip4_dev_route_blacklist); g_object_unref (composite); if (commit) @@ -5728,9 +5895,14 @@ END_ADD_DEFAULT_ROUTE: static gboolean dhcp4_lease_change (NMDevice *self, NMIP4Config *config) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + g_return_val_if_fail (config, FALSE); - if (!ip4_config_merge_and_apply (self, config, TRUE)) { + g_clear_object (&priv->dev_ip4_config); + priv->dev_ip4_config = g_object_ref (config); + + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); return FALSE; } @@ -5750,15 +5922,13 @@ dhcp4_restart_cb (gpointer user_data) { NMDevice *self = user_data; NMDevicePrivate *priv; - NMConnection *connection; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); priv->dhcp4.restart_id = 0; - connection = nm_device_get_applied_connection (self); - if (dhcp4_start (self, connection) == NM_ACT_STAGE_RETURN_FAILURE) + if (dhcp4_start (self) == NM_ACT_STAGE_RETURN_FAILURE) dhcp_schedule_restart (self, AF_INET, NULL); return FALSE; @@ -5825,7 +5995,7 @@ dhcp4_state_changed (NMDhcpClient *client, NMIP4Config *manual, **configs; NMConnection *connection; - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); + g_return_if_fail (nm_dhcp_client_get_addr_family (client) == AF_INET); g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config)); _LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state); @@ -5850,10 +6020,11 @@ dhcp4_state_changed (NMDhcpClient *client, connection = nm_device_get_applied_connection (self); g_assert (connection); - manual = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + manual = _ip4_config_new (self); nm_ip4_config_merge_setting (manual, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 3); configs[0] = manual; @@ -5885,36 +6056,60 @@ dhcp4_state_changed (NMDhcpClient *client, } static int -dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4) +get_dhcp_timeout (NMDevice *self, int addr_family) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gs_free char *value = NULL; - int timeout; + NMDeviceClass *klass; + NMConnection *connection; + NMSettingIPConfig *s_ip; + guint32 timeout; - timeout = nm_setting_ip_config_get_dhcp_timeout (NM_SETTING_IP_CONFIG (s_ip4)); - if (timeout) - return timeout; + nm_assert (NM_IS_DEVICE (self)); + nm_assert_addr_family (addr_family); - value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv4.dhcp-timeout", - self); - timeout = _nm_utils_ascii_str_to_int64 (value, 10, - 0, G_MAXINT32, 0); + connection = nm_device_get_applied_connection (self); + + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); + + timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip); if (timeout) return timeout; - return priv->dhcp_timeout; + { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? "ipv4.dhcp-timeout" + : "ipv6.dhcp-timeout", + self); + timeout = _nm_utils_ascii_str_to_int64 (value, 10, + 0, G_MAXINT32, 0); + if (timeout) + return timeout; + } + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->get_dhcp_timeout) + timeout = klass->get_dhcp_timeout (self, addr_family); + + return timeout ?: NM_DHCP_TIMEOUT_DEFAULT; } static NMActStageReturn -dhcp4_start (NMDevice *self, - NMConnection *connection) +dhcp4_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip4; const guint8 *hw_addr; size_t hw_addr_len = 0; GByteArray *tmp = NULL; + NMConnection *connection; + + connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, FALSE); s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -5931,16 +6126,18 @@ dhcp4_start (NMDevice *self, /* Begin DHCP on the interface */ g_warn_if_fail (priv->dhcp4.client == NULL); priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), + nm_netns_get_multi_idx (nm_device_get_netns (self)), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, nm_connection_get_uuid (connection), - nm_device_get_ip4_route_metric (self), + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET), nm_setting_ip_config_get_dhcp_send_hostname (s_ip4), nm_setting_ip_config_get_dhcp_hostname (s_ip4), nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)), nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)), - dhcp4_get_timeout (self, NM_SETTING_IP4_CONFIG (s_ip4)), + get_dhcp_timeout (self, AF_INET), priv->dhcp_anycast_address, NULL); @@ -5968,7 +6165,6 @@ gboolean nm_device_dhcp4_renew (NMDevice *self, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; g_return_val_if_fail (priv->dhcp4.client != NULL, FALSE); @@ -5977,11 +6173,8 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) /* Terminate old DHCP instance and release the old lease */ dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release); - connection = nm_device_get_applied_connection (self); - g_return_val_if_fail (connection, FALSE); - /* Start DHCP again on the interface */ - return dhcp4_start (self, connection) != NM_ACT_STAGE_RETURN_FAILURE; + return dhcp4_start (self) != NM_ACT_STAGE_RETURN_FAILURE; } /*****************************************************************************/ @@ -5989,66 +6182,63 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) static GHashTable *shared_ips = NULL; static void -release_shared_ip (gpointer data) +shared_ip_release (gpointer data) { g_hash_table_remove (shared_ips, data); + if (!g_hash_table_size (shared_ips)) + g_clear_pointer (&shared_ips, g_hash_table_unref); } -static gboolean -reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Address *address) +static NMIP4Config * +shared4_new_config (NMDevice *self, NMConnection *connection) { - if (G_UNLIKELY (shared_ips == NULL)) - shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + NMIP4Config *config = NULL; + gboolean is_generated = FALSE; + NMSettingIPConfig *s_ip4; + NMPlatformIP4Address address = { + .addr_source = NM_IP_CONFIG_SOURCE_SHARED, + }; - memset (address, 0, sizeof (*address)); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (connection, NULL); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) { /* Use the first user-supplied address */ NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0); in_addr_t a; - g_assert (user); nm_ip_address_get_address_binary (user, &a); - nm_platform_ip4_address_set_addr (address, a, nm_ip_address_get_prefix (user)); + nm_platform_ip4_address_set_addr (&address, a, nm_ip_address_get_prefix (user)); } else { /* Find an unused address in the 10.42.x.x range */ guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */ guint32 count = 0; - while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { - count += ntohl (0x100); - if (count > ntohl (0xFE00)) { - _LOGE (LOGD_SHARING, "ran out of shared IP addresses!"); - return FALSE; + if (G_UNLIKELY (!shared_ips)) + shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + else { + while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { + count += ntohl (0x100); + if (count > ntohl (0xFE00)) { + _LOGE (LOGD_SHARING, "ran out of shared IP addresses!"); + return FALSE; + } } } - nm_platform_ip4_address_set_addr (address, start + count, 24); - g_hash_table_add (shared_ips, GUINT_TO_POINTER (address->address)); + nm_platform_ip4_address_set_addr (&address, start + count, 24); + g_hash_table_add (shared_ips, GUINT_TO_POINTER (address.address)); + is_generated = TRUE; } - return TRUE; -} - -static NMIP4Config * -shared4_new_config (NMDevice *self, NMConnection *connection) -{ - NMIP4Config *config = NULL; - NMPlatformIP4Address address; - - g_return_val_if_fail (self != NULL, NULL); - - if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address)) - return NULL; - - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); - address.addr_source = NM_IP_CONFIG_SOURCE_SHARED; + config = _ip4_config_new (self); nm_ip4_config_add_address (config, &address); - - /* Remove the address lock when the object gets disposed */ - g_object_set_qdata_full (G_OBJECT (config), NM_CACHED_QUARK ("shared-ip"), - GUINT_TO_POINTER (address.address), - release_shared_ip); - + if (is_generated) { + /* Remove the address lock when the object gets disposed */ + g_object_set_qdata_full (G_OBJECT (config), NM_CACHED_QUARK ("shared-ip"), + GUINT_TO_POINTER (address.address), + shared_ip_release); + } return config; } @@ -6092,9 +6282,16 @@ static gboolean connection_requires_carrier (NMConnection *connection) { NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingConnection *s_con; gboolean ip4_carrier_wanted, ip6_carrier_wanted; gboolean ip4_used = FALSE, ip6_used = FALSE; + /* We can progress to IP_CONFIG now, so that we're enslaved. + * That may actually cause carrier to go up and thus continue acivation. */ + s_con = nm_connection_get_setting_connection (connection); + if (nm_setting_connection_get_master (s_con)) + return FALSE; + ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used); if (ip4_carrier_wanted) { /* If IPv4 wants a carrier and cannot fail, the whole connection @@ -6128,16 +6325,19 @@ connection_requires_carrier (NMConnection *connection) } static gboolean -have_any_ready_slaves (NMDevice *self, const GSList *slaves) +have_any_ready_slaves (NMDevice *self) { - const GSList *iter; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + SlaveInfo *info; + CList *iter; /* Any enslaved slave is "ready" in the generic case as it's * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2 * properties set up. */ - for (iter = slaves; iter; iter = g_slist_next (iter)) { - if (nm_device_get_enslaved (iter->data)) + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); + if (NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) return TRUE; } return FALSE; @@ -6161,29 +6361,23 @@ act_stage3_ip4_config_start (NMDevice *self, NMConnection *connection; NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; const char *method; - GSList *slaves; - gboolean ready_slaves; connection = nm_device_get_applied_connection (self); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if ( connection_ip4_method_requires_carrier (connection, NULL) - && priv->is_master + && nm_device_is_master (self) && !priv->carrier) { _LOGI (LOGD_IP4 | LOGD_DEVICE, "IPv4 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (priv->is_master && ip4_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip4_requires_slaves (connection)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv4 attempt, then postpone IPv4 addressing. */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { + if (!have_any_ready_slaves (self)) { _LOGI (LOGD_DEVICE | LOGD_IP4, "IPv4 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_IP_WAIT; @@ -6195,7 +6389,7 @@ act_stage3_ip4_config_start (NMDevice *self, /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { - ret = dhcp4_start (self, connection); + ret = dhcp4_start (self); if (ret == NM_ACT_STAGE_RETURN_FAILURE) NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) { @@ -6205,10 +6399,11 @@ act_stage3_ip4_config_start (NMDevice *self, } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) { NMIP4Config **configs, *config; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); nm_ip4_config_merge_setting (config, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 2); configs[0] = config; @@ -6266,15 +6461,6 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } -static void -_ip6_config_merge_default (gpointer value, gpointer user_data) -{ - NMIP6Config *src = (NMIP6Config *) value; - NMIP6Config *dst = (NMIP6Config *) user_data; - - nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); -} - static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit) @@ -6283,14 +6469,11 @@ ip6_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP6Config *composite; - gboolean has_direct_route; - const struct in6_addr *gateway; - gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; + gboolean ignore_default_routes = FALSE; const char *token = NULL; + GSList *iter; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -6303,17 +6486,17 @@ ip6_config_merge_and_apply (NMDevice *self, ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip6); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip6); + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + ignore_default_routes = nm_setting_ip_config_get_never_default (s_ip6) + || nm_setting_ip_config_get_gateway (s_ip6); + if (nm_setting_ip6_config_get_addr_gen_mode (ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) token = nm_setting_ip6_config_get_token (ip6); - - if (NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_DHCP)) - auto_method = TRUE; } } - composite = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip6_config_new (self); nm_ip6_config_set_privacy (composite, priv->ndisc ? priv->ndisc_use_tempaddr : @@ -6321,35 +6504,35 @@ ip6_config_merge_and_apply (NMDevice *self, init_ip6_config_dns_priority (self, composite); if (commit) { + if (priv->queued_ip6_config_id) + update_ext_ip_config (self, AF_INET6, FALSE, FALSE); ensure_con_ip6_config (self); - if (priv->queued_ip6_config_id) { - g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - FALSE, - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) - priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); - } } + if (commit) + priv->default_route_metric_penalty_ip6_has = default_route_metric_penalty_detect (self); + /* Merge all the IP configs into the composite config */ if (priv->ac_ip6_config) { nm_ip6_config_merge (composite, priv->ac_ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } if (priv->dhcp6.ip6_config) { nm_ip6_config_merge (composite, priv->dhcp6.ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } - g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite); + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); if (priv->ext_ip6_config) - nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. @@ -6357,108 +6540,34 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip6_config) { nm_ip6_config_merge (composite, priv->wwan_ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); - } - - /* Merge user overrides into the composite config. For assumed connections, - * con_ip6_config is empty. */ - if (priv->con_ip6_config) - nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); - - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip6_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v6_has = FALSE; - priv->default_route.v6_is_assumed = TRUE; - - if (!commit) { - /* during a non-commit event, we always pickup whatever is configured. */ - goto END_ADD_DEFAULT_ROUTE; + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) - goto END_ADD_DEFAULT_ROUTE; - - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - - connection_has_default_route - = nm_default_route_manager_ip6_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns), - connection, &connection_is_never_default); - - if ( !priv->v6_commit_first_time - && connection_is_never_default) { - /* If the connection is explicitly configured as never-default, we enforce the (absence of the) - * default-route only once. That allows the user to configure a connection as never-default, - * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ - goto END_ADD_DEFAULT_ROUTE; - } + if (priv->rt6_temporary_not_available) { + const NMPObject *o; + GHashTableIter hiter; - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v6_is_assumed = FALSE; - - if (!connection_has_default_route) - goto END_ADD_DEFAULT_ROUTE; - - if (!nm_ip6_config_get_num_addresses (composite)) { - /* without addresses we can have no default route. */ - goto END_ADD_DEFAULT_ROUTE; + g_hash_table_iter_init (&hiter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&hiter, (gpointer *) &o, NULL)) { + nm_ip6_config_add_route (composite, + NMP_OBJECT_CAST_IP6_ROUTE (o), + NULL); + } } - gateway = nm_ip6_config_get_gateway (composite); - if (!gateway) - goto END_ADD_DEFAULT_ROUTE; - - - has_direct_route = nm_ip6_config_get_direct_route_for_host (composite, gateway) != NULL; - - - - priv->default_route.v6_has = TRUE; - memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); - priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v6.gateway = *gateway; - priv->default_route.v6.metric = route_metric_with_penalty (self, - nm_device_get_ip6_route_metric (self)); - priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP6Route r = priv->default_route.v6; - - /* add a direct route to the gateway */ - r.network = *gateway; - r.plen = 128; - r.gateway = in6addr_any; - nm_ip6_config_add_route (composite, &r); + /* Merge user overrides into the composite config. For assumed connections, + * con_ip6_config is empty. */ + if (priv->con_ip6_config) { + nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, + default_route_metric_penalty_get (self, AF_INET6)); } -END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v6_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) &priv->default_route.v6); + if (commit) { + nm_ip6_config_add_dependent_routes (composite, + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); } /* Allow setting MTU etc */ @@ -6472,11 +6581,7 @@ END_ADD_DEFAULT_ROUTE: } } - routes_full_sync = commit - && priv->v6_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync); + success = nm_device_set_ip6_config (self, composite, commit); g_object_unref (composite); if (commit) priv->v6_commit_first_time = FALSE; @@ -6533,28 +6638,30 @@ dhcp6_restart_cb (gpointer user_data) } static void -dhcp_schedule_restart (NMDevice *self, int family, const char *reason) +dhcp_schedule_restart (NMDevice *self, + int addr_family, + const char *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean inet4; guint tries_left; - gs_free char *tries_str = NULL; + char tries_str[255]; - g_return_if_fail (family == AF_INET || family == AF_INET6); - inet4 = family == AF_INET; + nm_assert_addr_family (addr_family); - tries_left = inet4 ? priv->dhcp4.num_tries_left : priv->dhcp6.num_tries_left; - if (tries_left != DHCP_NUM_TRIES_MAX) - tries_str = g_strdup_printf (", %u tries left", tries_left + 1); + tries_left = (addr_family == AF_INET) + ? priv->dhcp4.num_tries_left + : priv->dhcp6.num_tries_left; - _LOGI (inet4 ? LOGD_DHCP4 : LOGD_DHCP6, + _LOGI ((addr_family == AF_INET) ? LOGD_DHCP4 : LOGD_DHCP6, "scheduling DHCPv%c restart in %u seconds%s%s%s%s", - inet4 ? '4' : '6', + nm_utils_addr_family_to_char (addr_family), DHCP_RESTART_TIMEOUT, - tries_str ? tries_str : "", + (tries_left != DHCP_NUM_TRIES_MAX) + ? nm_sprintf_buf (tries_str, ", %u tries left", tries_left + 1) + : "", NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", "")); - if (inet4) { + if (addr_family == AF_INET) { priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, dhcp4_restart_cb, self); } else { @@ -6631,9 +6738,8 @@ dhcp6_state_changed (NMDhcpClient *client, { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint i; - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); + g_return_if_fail (nm_dhcp_client_get_addr_family (client) == AF_INET6); g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config)); _LOGD (LOGD_DHCP6, "new DHCPv6 client state %d", state); @@ -6648,10 +6754,11 @@ dhcp6_state_changed (NMDhcpClient *client, && event_id && priv->dhcp6.event_id && !strcmp (event_id, priv->dhcp6.event_id)) { - for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) { - nm_ip6_config_add_address (priv->dhcp6.ip6_config, - nm_ip6_config_get_address (ip6_config, i)); - } + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *a; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a) + nm_ip6_config_add_address (priv->dhcp6.ip6_config, a); } else { g_clear_object (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); @@ -6741,15 +6848,17 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) } priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, &ll_addr->address, nm_connection_get_uuid (connection), - nm_device_get_ip6_route_metric (self), + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6), nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), nm_setting_ip_config_get_dhcp_hostname (s_ip6), - priv->dhcp_timeout, + get_dhcp_timeout (self, AF_INET6), priv->dhcp_anycast_address, (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), @@ -6769,7 +6878,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) } if (nm_device_sys_iface_state_is_external_or_assume (self)) - priv->dhcp4.was_active = TRUE; + priv->dhcp6.was_active = TRUE; return !!priv->dhcp6.client; } @@ -6869,7 +6978,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) NMPlatformIP6Address address = *subnet; if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); /* Assign a ::1 address in the subnet for us. */ address.address.s6_addr32[3] |= htonl (1); @@ -6899,7 +7008,7 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device) nm_ip6_config_reset_nameservers (priv->ac_ip6_config); nm_ip6_config_reset_searches (priv->ac_ip6_config); } else - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); if (from_device) from_config = nm_device_get_ip6_config (from_device); @@ -6990,7 +7099,6 @@ check_and_add_ipv6ll_addr (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ip_ifindex = nm_device_get_ip_ifindex (self); struct in6_addr lladdr; - guint i, n; NMConnection *connection; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; @@ -6999,11 +7107,10 @@ check_and_add_ipv6ll_addr (NMDevice *self) return; if (priv->ip6_config) { - n = nm_ip6_config_get_num_addresses (priv->ip6_config); - for (i = 0; i < n; i++) { - const NMPlatformIP6Address *addr; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *addr; - addr = nm_ip6_config_get_address (priv->ip6_config, i); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ip6_config, &addr) { if ( IN6_IS_ADDR_LINKLOCAL (&addr->address) && !(addr->n_ifa_flags & IFA_F_DADFAILED)) { /* Already have an LL address, nothing to do */ @@ -7152,6 +7259,26 @@ nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is_user_co /*****************************************************************************/ static void +_set_mtu (NMDevice *self, guint32 mtu) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->mtu == mtu) + return; + + priv->mtu = mtu; + _notify (self, PROP_MTU); + + if (priv->master) { + /* changing the MTU of a slave, might require the master to reset + * it's MTU. Note that the master usually cannot set a MTU larger + * then the slave's. Hence, when the slave increases the MTU, + * master might want to retry setting the MTU. */ + nm_device_commit_mtu (priv->master); + } +} + +static void _commit_mtu (NMDevice *self, const NMIP4Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -7170,8 +7297,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) return; if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* for assumed connections we don't tamper with the MTU. This is - * a bug and supposed to be fixed by the unmanaged/assumed rework. */ + /* for assumed connections we don't tamper with the MTU. */ return; } @@ -7271,6 +7397,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) }) if ( (mtu_desired && mtu_desired != mtu_plat) || (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ())) { + gboolean anticipated_failure = FALSE; if (!priv->mtu_initial && !priv->ip6_mtu_initial) { /* before touching any of the MTU paramters, record the @@ -7279,100 +7406,102 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) priv->ip6_mtu_initial = _IP6_MTU_SYS (); } - if (mtu_desired && mtu_desired != mtu_plat) - nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired); + if (mtu_desired && mtu_desired != mtu_plat) { + if (nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired) == NM_PLATFORM_ERROR_CANT_SET_MTU) { + anticipated_failure = TRUE; + _LOGW (LOGD_DEVICE, "mtu: failure to set MTU. %s", + NM_IS_DEVICE_VLAN (self) + ? "Is the parent's MTU size large enough?" + : (!c_list_is_empty (&priv->slaves) + ? "Are the MTU sizes of the slaves large enough?" + : "Did you configure the MTU correctly?")); + } + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; + } if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ()) { - nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu)); + if (!nm_device_ipv6_sysctl_set (self, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { + int errsv = errno; + + _NMLOG (anticipated_failure && errsv == EINVAL ? LOGL_DEBUG : LOGL_WARN, + LOGD_DEVICE, + "mtu: failure to set IPv6 MTU%s", + anticipated_failure && errsv == EINVAL + ? ": Is the underlying MTU value successfully set?" + : ""); + } + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; } } #undef _IP6_MTU_SYS } +void +nm_device_commit_mtu (NMDevice *self) +{ + NMDeviceState state; + + g_return_if_fail (NM_IS_DEVICE (self)); + + state = nm_device_get_state (self); + if ( state >= NM_DEVICE_STATE_CONFIG + && state < NM_DEVICE_STATE_DEACTIVATING) { + _LOGT (LOGD_DEVICE, "mtu: commit-mtu..."); + _commit_mtu (self, NM_DEVICE_GET_PRIVATE (self)->ip4_config); + } else + _LOGT (LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_str (state)); +} + static void ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, NMDevice *self) { NMNDiscConfigMap changed = changed_int; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int i; - int system_support; - guint32 ifa_flags = 0x00; - - /* - * Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self)); - - if (system_support) - ifa_flags = IFA_F_NOPREFIXROUTE; - if ( priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - || priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - { - /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ - ifa_flags |= IFA_F_MANAGETEMPADDR; - } + guint i; g_return_if_fail (priv->act_request); if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); - - if (changed & NM_NDISC_CONFIG_GATEWAYS) { - /* Use the first gateway as ordered in neighbor discovery cache. */ - if (rdata->gateways_n) - nm_ip6_config_set_gateway (priv->ac_ip6_config, &rdata->gateways[0].address); - else - nm_ip6_config_set_gateway (priv->ac_ip6_config, NULL); - } + priv->ac_ip6_config = _ip6_config_new (self); if (changed & NM_NDISC_CONFIG_ADDRESSES) { - /* Rebuild address list from neighbor discovery cache. */ - nm_ip6_config_reset_addresses (priv->ac_ip6_config); - - /* ndisc->addresses contains at most max_addresses entries. - * This is different from what the kernel does, which - * also counts static and temporary addresses when checking - * max_addresses. - **/ - for (i = 0; i < rdata->addresses_n; i++) { - const NMNDiscAddress *discovered_address = &rdata->addresses[i]; - NMPlatformIP6Address address; - - memset (&address, 0, sizeof (address)); - address.address = discovered_address->address; - address.plen = system_support ? 64 : 128; - address.timestamp = discovered_address->timestamp; - address.lifetime = discovered_address->lifetime; - address.preferred = discovered_address->preferred; - if (address.preferred > address.lifetime) - address.preferred = address.lifetime; - address.addr_source = NM_IP_CONFIG_SOURCE_NDISC; - address.n_ifa_flags = ifa_flags; + guint8 plen; + guint32 ifa_flags; + + /* Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. */ + ifa_flags = 0; + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + if (NM_IN_SET (priv->ndisc_use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; + plen = 64; + } else + plen = 128; - nm_ip6_config_add_address (priv->ac_ip6_config, &address); - } + nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config, + rdata->addresses, + rdata->addresses_n, + plen, + ifa_flags); } - if (changed & NM_NDISC_CONFIG_ROUTES) { - /* Rebuild route list from neighbor discovery cache. */ - nm_ip6_config_reset_routes (priv->ac_ip6_config); - - for (i = 0; i < rdata->routes_n; i++) { - const NMNDiscRoute *discovered_route = &rdata->routes[i]; - const NMPlatformIP6Route route = { - .network = discovered_route->network, - .plen = discovered_route->plen, - .gateway = discovered_route->gateway, - .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .metric = nm_device_get_ip6_route_metric (self), - }; - - nm_ip6_config_add_route (priv->ac_ip6_config, &route); - } + if (NM_FLAGS_ANY (changed, NM_NDISC_CONFIG_ROUTES + | NM_NDISC_CONFIG_GATEWAYS)) { + nm_ip6_config_reset_routes_ndisc (priv->ac_ip6_config, + rdata->gateways, + rdata->gateways_n, + rdata->routes, + rdata->routes_n, + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6), + nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); } if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { @@ -7465,8 +7594,10 @@ addrconf6_start_with_link_ready (NMDevice *self) } /* Apply any manual configuration before starting RA */ - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip6_config_merge_and_apply (self, TRUE)) { _LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration"); + g_clear_object (&priv->con_ip6_config); + } /* XXX: These sysctls would probably be better set by the lndp ndisc itself. */ switch (nm_ndisc_get_node_type (priv->ndisc)) { @@ -7537,6 +7668,9 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ac_ip6_config = NULL; } + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); g_assert (s_ip6); @@ -7559,7 +7693,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ndisc_use_tempaddr = use_tempaddr; if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - && !nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self))) { + && !nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { _LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " "IPv6 private addresses. This feature is not available"); } @@ -7590,6 +7725,8 @@ addrconf6_cleanup (NMDevice *self) nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE); g_clear_object (&priv->ac_ip6_config); + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); g_clear_object (&priv->ndisc); } @@ -7616,8 +7753,13 @@ save_ip6_properties (NMDevice *self) g_hash_table_remove_all (priv->ip6_saved_properties); + if (!nm_device_get_ip_ifindex (self)) + return; + for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]))); + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, ip6_properties_to_save[i]))); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -7657,7 +7799,8 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) int ifindex = nm_device_get_ip_ifindex (self); char *value; - if (!nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (!nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) return; priv->nm_ipv6ll = enable; @@ -7672,13 +7815,15 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) LOGD_IP6, "failed to %s userspace IPv6LL address handling (%s)", detail, - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); } if (enable) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ value = nm_platform_sysctl_get (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6"))); + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), "disable_ipv6"))); if (g_strcmp0 (value, "0") == 0) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); g_free (value); @@ -7739,6 +7884,9 @@ _ip6_privacy_get (NMDevice *self) if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) return ip6_privacy; + if (!nm_device_get_ip_ifindex (self)) + return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;; + /* 3.) No valid default-value configured. Fallback to reading sysctl. * * Instead of reading static config files in /etc, just read the current sysctl value. @@ -7775,29 +7923,23 @@ act_stage3_ip6_config_start (NMDevice *self, const char *method; NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; const char *ip6_privacy_str = "0"; - GSList *slaves; - gboolean ready_slaves; connection = nm_device_get_applied_connection (self); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if ( connection_ip6_method_requires_carrier (connection, NULL) - && priv->is_master + && nm_device_is_master (self) && !priv->carrier) { _LOGI (LOGD_IP6 | LOGD_DEVICE, "IPv6 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (priv->is_master && ip6_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip6_requires_slaves (connection)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv6 attempt, then postpone IPv6 addressing. */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { + if (!have_any_ready_slaves (self)) { _LOGI (LOGD_DEVICE | LOGD_IP6, "IPv6 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_IP_WAIT; @@ -7810,7 +7952,8 @@ act_stage3_ip6_config_start (NMDevice *self, method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { - if (!priv->master) { + if ( !priv->master + && !nm_device_sys_iface_state_is_external (self)) { gboolean old_nm_ipv6ll = priv->nm_ipv6ll; /* When activating an IPv6 'ignore' connection we need to revert back @@ -7847,7 +7990,8 @@ act_stage3_ip6_config_start (NMDevice *self, */ nm_platform_process_events (nm_device_get_platform (self)); g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), FALSE, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); @@ -7911,17 +8055,11 @@ nm_device_activate_stage3_ip4_start (NMDevice *self) g_assert (priv->ip4_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv4. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip4_config) - ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + ip4_config = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, ip4_config); g_object_unref (ip4_config); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { @@ -7958,17 +8096,11 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) g_assert (priv->ip6_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv6. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET6, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip6_config) - ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + ip6_config = _ip6_config_new (self); /* Here we get a static IPv6 config, like for Shared where it's * autogenerated or from modems where it comes from ModemManager. */ @@ -8002,39 +8134,18 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) static void activate_stage3_ip_config_start (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActiveConnection *master; - NMDevice *master_device; - _set_ip_state (self, AF_INET, IP_WAIT); _set_ip_state (self, AF_INET6, IP_WAIT); + _active_connection_set_state_flags (self, + NM_ACTIVATION_STATE_FLAG_LAYER2_READY); + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self))) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); - /* If the device is a slave, then we don't do any IP configuration but we - * use the IP config stage to indicate to the master we're ready for - * enslavement. If the master is already activating, it will have tried to - * enslave us when we changed state to IP_CONFIG, causing us to queue a - * transition to SECONDARIES (or FAILED if the enslavement failed), with - * our IP states set to IP_DONE either way. If the master isn't yet - * activating, then they'll still be in IP_WAIT. Either way, we bail out - * of IP config here. - */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)); - if (master) { - master_device = nm_active_connection_get_device (master); - if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) { - _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'", - nm_connection_get_id (nm_device_get_applied_connection (self)), - master_device ? nm_device_get_iface (master_device) : "(unknown)"); - } - return; - } - /* IPv4 */ if ( nm_device_activate_ip4_state_in_wait (self) && !nm_device_activate_stage3_ip4_start (self)) @@ -8309,18 +8420,20 @@ start_sharing (NMDevice *self, NMIP4Config *config) char str_addr[INET_ADDRSTRLEN + 1]; char str_mask[INET_ADDRSTRLEN + 1]; guint32 netmask, network; - const NMPlatformIP4Address *ip4_addr; + const NMPlatformIP4Address *ip4_addr = NULL; const char *ip_iface; g_return_val_if_fail (config != NULL, FALSE); ip_iface = nm_device_get_ip_iface (self); + if (!ip_iface) + return FALSE; - ip4_addr = nm_ip4_config_get_address (config, 0); + ip4_addr = nm_ip4_config_get_first_address (config); if (!ip4_addr || !ip4_addr->address) return FALSE; - netmask = nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); + netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask))) return FALSE; @@ -8420,7 +8533,7 @@ arp_announce (NMDevice *self) } static void -activate_stage5_ip4_config_commit (NMDevice *self) +activate_stage5_ip4_config_result (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; @@ -8442,7 +8555,7 @@ activate_stage5_ip4_config_commit (NMDevice *self) } /* NULL to use the existing priv->dev_ip4_config */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) { + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; @@ -8492,7 +8605,7 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); - activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET); + activation_source_schedule (self, activate_stage5_ip4_config_result, AF_INET); } gboolean @@ -8530,7 +8643,8 @@ dad6_get_pending_addresses (NMDevice *self) priv->wwan_ip6_config }; const NMPlatformIP6Address *addr, *pl_addr; NMIP6Config *dad6_config = NULL; - guint i, j, num; + NMDedupMultiIter ipconf_iter; + guint i; int ifindex; ifindex = nm_device_get_ip_ifindex (self); @@ -8541,13 +8655,11 @@ dad6_get_pending_addresses (NMDevice *self) */ for (i = 0; i < G_N_ELEMENTS (confs); i++) { if (confs[i]) { - num = nm_ip6_config_get_num_addresses (confs[i]); - for (j = 0; j < num; j++) { - addr = nm_ip6_config_get_address (confs[i], j); + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) { pl_addr = nm_platform_ip6_address_get (nm_device_get_platform (self), ifindex, - addr->address, - addr->plen); + addr->address); if ( pl_addr && NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_TENTATIVE) && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_DADFAILED) @@ -8556,7 +8668,7 @@ dad6_get_pending_addresses (NMDevice *self) nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); if (!dad6_config) - dad6_config = nm_ip6_config_new (ifindex); + dad6_config = _ip6_config_new (self); nm_ip6_config_add_address (dad6_config, pl_addr); } @@ -8584,6 +8696,8 @@ activate_stage5_ip6_config_commit (NMDevice *self) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); + g_return_if_fail (ip_ifindex); + if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex) && !nm_device_sys_iface_state_is_external_or_assume (self)) { nm_platform_link_set_up (nm_device_get_platform (self), ip_ifindex, NULL); if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex)) @@ -8756,16 +8870,15 @@ static void _update_ip4_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint32 addr; + const NMPlatformIP4Address *address; g_return_if_fail (NM_IS_DEVICE (self)); if ( priv->ip4_config && ip_config_valid (priv->state) - && nm_ip4_config_get_num_addresses (priv->ip4_config)) { - addr = nm_ip4_config_get_address (priv->ip4_config, 0)->address; - if (addr != priv->ip4_address) { - priv->ip4_address = addr; + && (address = nm_ip4_config_get_first_address (priv->ip4_config))) { + if (address->address != priv->ip4_address) { + priv->ip4_address = address->address; _notify (self, PROP_IP4_ADDRESS); } } @@ -8802,7 +8915,7 @@ delete_on_deactivate_link_delete (gpointer user_data) if (!nm_device_unrealize (data->device, TRUE, &error)) _LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message); - } else + } else if (data->ifindex > 0) nm_platform_link_delete (nm_device_get_platform (self), data->ifindex); g_free (data); @@ -8833,8 +8946,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); DeleteOnDeactivateData *data; - if (ifindex <= 0) - return; if (!priv->nm_owned) return; if (priv->queued_act_request) @@ -8905,7 +9016,7 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, #if NM_MORE_ASSERTS > 10 /* Assert that the keys are unique. */ { - gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); for (i = 0; argv[i]; i++) { if (!nm_g_hash_table_add (check_dups, (char *) argv[i])) @@ -8971,10 +9082,11 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (priv->ip4_state != IP_NONE) { g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->ext_ip4_config); - priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, s_ip4_new, - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); if (!force_restart) { method_old = s_ip4_old @@ -8992,7 +9104,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (!nm_device_activate_stage3_ip4_start (self)) _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); } else { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration"); } } @@ -9013,10 +9125,11 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (priv->ip6_state != IP_NONE) { g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ext_ip6_config); - priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, s_ip6_new, - nm_device_get_ip6_route_metric (self)); + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); if (!force_restart) { method_old = s_ip6_old @@ -9096,7 +9209,27 @@ can_reapply_change (NMDevice *self, const char *setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_PROXY_SETTING_NAME)) { - /* accept all */ + if (g_hash_table_contains (diffs, NM_SETTING_IP_CONFIG_ROUTE_TABLE)) { + /* changing the route-table setting is complicated, because it affects + * how we sync the routes. Don't support changing it without full + * re-activation. + * + * The problem is really that changing the setting also affects the sync + * mode. So, switching from NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN to + * NM_IP_ROUTE_TABLE_SYNC_MODE_FULL would somehow require us to get rid + * of additional routes, but we don't know which routes were added by NM + * and which should be removed. + * + * Note how nm_device_get_route_table() caches the value for the duration of the + * activation. */ + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply changes to '%s.%s' setting", + setting_name, + NM_SETTING_IP_CONFIG_ROUTE_TABLE); + return FALSE; + } return TRUE; } else { g_set_error (error, @@ -9484,6 +9617,97 @@ impl_device_get_applied_connection (NMDevice *self, /*****************************************************************************/ +typedef struct { + gint64 timestamp_ms; + bool dirty; +} IP6RoutesTemporaryNotAvailableData; + +static gboolean +_rt6_temporary_not_available_timeout (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->rt6_temporary_not_available_id = 0; + nm_device_activate_schedule_ip6_config_result (self); + + return G_SOURCE_REMOVE; +} + +static gboolean +_rt6_temporary_not_available_set (NMDevice *self, + GPtrArray *temporary_not_available) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + IP6RoutesTemporaryNotAvailableData *data; + GHashTableIter iter; + gint64 now_ms, oldest_ms; + const gint64 MAX_AGE_MS = 20000; + guint i; + gboolean success = TRUE; + + if ( !temporary_not_available + || !temporary_not_available->len) { + /* nothing outstanding. Clear tracking the routes. */ + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + return success; + } + + if (priv->rt6_temporary_not_available) { + g_hash_table_iter_init (&iter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) + data->dirty = TRUE; + } else { + priv->rt6_temporary_not_available = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn (IP6RoutesTemporaryNotAvailableData)); + } + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + oldest_ms = now_ms; + + for (i = 0; i < temporary_not_available->len; i++) { + const NMPObject *o = temporary_not_available->pdata[i]; + + data = g_hash_table_lookup (priv->rt6_temporary_not_available, o); + if (data) { + if (!data->dirty) + continue; + data->dirty = FALSE; + nm_assert (data->timestamp_ms > 0 && data->timestamp_ms <= now_ms); + if (now_ms > data->timestamp_ms + MAX_AGE_MS) { + /* timeout. Could not add this address. */ + _LOGW (LOGD_DEVICE, "failure to add IPv6 route: %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + success = FALSE; + } else + oldest_ms = MIN (data->timestamp_ms, oldest_ms); + continue; + } + + data = g_slice_new0 (IP6RoutesTemporaryNotAvailableData); + data->timestamp_ms = now_ms; + g_hash_table_insert (priv->rt6_temporary_not_available, (gpointer) nmp_object_ref (o), data); + } + + g_hash_table_iter_init (&iter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) { + if (data->dirty) + g_hash_table_iter_remove (&iter); + } + + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + priv->rt6_temporary_not_available_id = g_timeout_add (oldest_ms + MAX_AGE_MS - now_ms, + _rt6_temporary_not_available_timeout, + self); + + return success; +} + +/*****************************************************************************/ + static void disconnect_cb (NMDevice *self, GDBusMethodInvocation *context, @@ -9835,47 +10059,43 @@ nm_device_get_ip4_config (NMDevice *self) static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, - guint32 default_route_metric, gboolean commit, - gboolean routes_full_sync) + GPtrArray *ip4_dev_route_blacklist) { NMDevicePrivate *priv; NMIP4Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; - int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ({ + int ip_ifindex = nm_device_get_ip_ifindex (self); - if (new_config) { - config_ifindex = nm_ip4_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + ( ip_ifindex > 0 + && ip_ifindex == nm_ip4_config_get_ifindex (new_config)); + }))); + + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip4_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - gboolean assumed = nm_device_sys_iface_state_is_external_or_assume (self); - _commit_mtu (self, new_config); - /* For assumed devices we must not touch the kernel-routes, such as the device-route. - * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this - * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync, - assumed ? (gint64) -1 : (gint64) default_route_metric); + nm_device_get_route_table (self, AF_INET, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN); + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), + nm_ip4_config_get_ifindex (new_config), + ip4_dev_route_blacklist); } if (new_config) { @@ -9906,7 +10126,6 @@ nm_device_set_ip4_config (NMDevice *self, g_clear_object (&priv->dev_ip4_config); } - def_route_changed = nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); concheck_periodic_update (self); if (!nm_device_sys_iface_state_is_external_or_assume (self)) @@ -9940,9 +10159,6 @@ nm_device_set_ip4_config (NMDevice *self, } nm_device_queue_recheck_assume (self); - } else if (def_route_changed) { - _LOGD (LOGD_IP4, "ip4-config: default route changed"); - g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, priv->ip4_config); } return success; @@ -9986,11 +10202,16 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!old || NM_IS_IP4_CONFIG (old)); + nm_assert (!config || NM_IS_IP4_CONFIG (config)); + nm_assert (!old || nm_ip4_config_get_ifindex (old) == nm_device_get_ip_ifindex (self)); + nm_assert (!config || nm_ip4_config_get_ifindex (config) == nm_device_get_ip_ifindex (self)); + if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config)) return; /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed to set VPN routes for device"); } @@ -10007,47 +10228,53 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) priv->wwan_ip4_config = g_object_ref (config); /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration"); } static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP6Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; - int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ({ + int ip_ifindex = nm_device_get_ip_ifindex (self); - if (new_config) { - config_ifindex = nm_ip6_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + ( ip_ifindex > 0 + && ip_ifindex == nm_ip6_config_get_ifindex (new_config)); + }))); + + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip6_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { + gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; + _commit_mtu (self, priv->ip4_config); + success = nm_ip6_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync); + nm_device_get_route_table (self, AF_INET6, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + &temporary_not_available); + + if (!_rt6_temporary_not_available_set (self, temporary_not_available)) + success = FALSE; } if (new_config) { @@ -10077,8 +10304,6 @@ nm_device_set_ip6_config (NMDevice *self, nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } - def_route_changed = nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - if (has_changes) { NMSettingsConnection *settings_connection; @@ -10108,9 +10333,6 @@ nm_device_set_ip6_config (NMDevice *self, if (priv->ndisc) ndisc_set_router_config (priv->ndisc, self); - } else if (def_route_changed) { - _LOGD (LOGD_IP6, "ip6-config: default route changed"); - g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, priv->ip6_config); } return success; @@ -10121,6 +10343,11 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!old || NM_IS_IP6_CONFIG (old)); + nm_assert (!config || NM_IS_IP6_CONFIG (config)); + nm_assert (!old || nm_ip6_config_get_ifindex (old) == nm_device_get_ip_ifindex (self)); + nm_assert (!config || nm_ip6_config_get_ifindex (config) == nm_device_get_ip_ifindex (self)); + if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config)) return; @@ -10370,7 +10597,7 @@ nm_device_start_ip_check (NMDevice *self) NMSettingConnection *s_con; guint timeout = 0; const char *ping_binary = NULL; - char buf[INET6_ADDRSTRLEN] = { 0 }; + char buf[NM_UTILS_INET_ADDRSTRLEN]; NMLogDomain log_domain = LOGD_IP4; /* Shouldn't be any active ping here, since IP_CHECK happens after the @@ -10389,25 +10616,24 @@ nm_device_start_ip_check (NMDevice *self) g_assert (s_con); timeout = nm_setting_connection_get_gateway_ping_timeout (s_con); + buf[0] = '\0'; if (timeout) { - if (priv->ip4_config && priv->ip4_state == IP_DONE) { - guint gw = 0; + const NMPObject *gw; - ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL); - log_domain = LOGD_IP4; - - gw = nm_ip4_config_get_gateway (priv->ip4_config); - if (gw && !inet_ntop (AF_INET, &gw, buf, sizeof (buf))) - buf[0] = '\0'; + if (priv->ip4_config && priv->ip4_state == IP_DONE) { + gw = nm_ip4_config_best_default_route_get (priv->ip4_config); + if (gw) { + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (gw)->gateway, buf); + ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL); + log_domain = LOGD_IP4; + } } else if (priv->ip6_config && priv->ip6_state == IP_DONE) { - const struct in6_addr *gw = NULL; - - ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL); - log_domain = LOGD_IP6; - - gw = nm_ip6_config_get_gateway (priv->ip6_config); - if (gw && !inet_ntop (AF_INET6, gw, buf, sizeof (buf))) - buf[0] = '\0'; + gw = nm_ip6_config_best_default_route_get (priv->ip6_config); + if (gw) { + nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (gw)->gateway, buf); + ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL); + log_domain = LOGD_IP6; + } } } @@ -10507,6 +10733,8 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) * a timeout is reached. */ if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + gint64 now_ms, until_ms; + /* we start a grace period of 5 seconds during which we will schedule * a pending action whenever we have no carrier. * @@ -10515,7 +10743,10 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) nm_clear_g_source (&priv->carrier_wait_id); if (!priv->carrier) nm_device_add_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - priv->carrier_wait_id = g_timeout_add_seconds (5, carrier_wait_timeout, self); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + until_ms = NM_MAX (now_ms + CARRIER_WAIT_TIME_MS, priv->carrier_wait_until_ms); + priv->carrier_wait_id = g_timeout_add (until_ms - now_ms, carrier_wait_timeout, self); } /* Can only get HW address of some devices when they are up */ @@ -10525,7 +10756,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) /* when the link comes up, we must restore IP configuration if necessary. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up"); } if (priv->ip6_state == IP_DONE) { @@ -10608,20 +10839,28 @@ find_ip4_lease_config (NMDevice *self, g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), + AF_INET, ip_iface, ip_ifindex, nm_connection_get_uuid (connection), - FALSE, - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); for (liter = leases; liter && !found; liter = liter->next) { NMIP4Config *lease_config = liter->data; - const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); - guint32 gateway = nm_ip4_config_get_gateway (lease_config); + const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (lease_config); + const NMPObject *gw1, *gw2; g_assert (address); if (!nm_ip4_config_address_exists (ext_ip4_config, address)) continue; - if (gateway != nm_ip4_config_get_gateway (ext_ip4_config)) + gw1 = nm_ip4_config_best_default_route_get (lease_config); + if (!gw1) + continue; + gw2 = nm_ip4_config_best_default_route_get (ext_ip4_config); + if (!gw2) + continue; + if (NMP_OBJECT_CAST_IP4_ROUTE (gw1)->gateway != NMP_OBJECT_CAST_IP4_ROUTE (gw2)->gateway) continue; found = g_object_ref (lease_config); } @@ -10641,23 +10880,24 @@ capture_lease_config (NMDevice *self, NMSettingsConnection *const*connections; guint i; gboolean dhcp_used = FALSE; + NMDedupMultiIter ipconf_iter; /* Ensure at least one address on the device has a non-infinite lifetime, * otherwise DHCP cannot possibly be active on the device right now. */ if (ext_ip4_config && out_ip4_config) { - for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) { - const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i); + const NMPlatformIP4Address *addr; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ext_ip4_config, &addr) { if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { dhcp_used = TRUE; break; } } } else if (ext_ip6_config && out_ip6_config) { - for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) { - const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i); + const NMPlatformIP6Address *addr; + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ext_ip6_config, &addr) { if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { dhcp_used = TRUE; break; @@ -10695,184 +10935,161 @@ capture_lease_config (NMDevice *self, } } -static void -_ip4_config_intersect (gpointer value, gpointer user_data) -{ - NMIP4Config *dst = (NMIP4Config *) value; - NMIP4Config *src = (NMIP4Config *) user_data; - - nm_ip4_config_intersect (dst, src); -} - -static void -_ip4_config_subtract (gpointer value, gpointer user_data) -{ - NMIP4Config *dst = (NMIP4Config *) user_data; - NMIP4Config *src = (NMIP4Config *) value; - - nm_ip4_config_subtract (dst, src); -} - -static void -update_ip4_config (NMDevice *self, gboolean initial) +static gboolean +update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex; gboolean capture_resolv_conf; + GSList *iter; - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if ( !initial - && activation_source_is_scheduled (self, - activate_stage5_ip4_config_commit, - AF_INET)) { - priv->queued_ip4_config_pending = FALSE; - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); - _LOGT (LOGD_DEVICE, "IP4 update was postponed"); - return; - } + nm_assert_addr_family (addr_family); ifindex = nm_device_get_ip_ifindex (self); if (!ifindex) - return; + return FALSE; capture_resolv_conf = initial && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ()); - /* IPv4 */ - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), - ifindex, - capture_resolv_conf); - if (priv->ext_ip4_config) { - if (initial) { - g_clear_object (&priv->dev_ip4_config); - capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); - } - - /* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL. - * Hence, we will wrongly remove device-routes with metric=0 if they were added by - * the user on purpose. This should be fixed by also tracking and exposing - * kernel routes. */ - - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip4_config) - nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config); - if (priv->dev_ip4_config) - nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config); + if (addr_family == AF_INET) { - g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config); + g_clear_object (&priv->ext_ip4_config); + priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + ifindex, + capture_resolv_conf); + if (priv->ext_ip4_config) { + if (initial) { + g_clear_object (&priv->dev_ip4_config); + capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); + } - if (priv->wwan_ip4_config) - nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); + if (intersect_configs) { + /* This function was called upon external changes. Remove the configuration + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed + * by the user. */ + if (priv->con_ip4_config) { + nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->dev_ip4_config) { + nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->wwan_ip4_config) { + nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_intersect (iter->data, priv->ext_ip4_config, 0); + } - /* Remove parts from ext_ip4_config to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config); - if (priv->dev_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); + /* Remove parts from ext_ip4_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->dev_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->wwan_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_subtract (priv->ext_ip4_config, iter->data, 0); + } - g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config); + } else { + nm_assert (addr_family == AF_INET6); - if (priv->wwan_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); + g_clear_object (&priv->ext_ip6_config); + g_clear_object (&priv->ext_ip6_config_captured); + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + ifindex, + capture_resolv_conf, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + if (priv->ext_ip6_config_captured) { + + priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); + + if (intersect_configs) { + /* This function was called upon external changes. Remove the configuration + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed + * by the user. */ + if (priv->con_ip6_config) { + nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->ac_ip6_config) { + nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->dhcp6.ip6_config) { + nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->wwan_ip6_config) { + nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_intersect (iter->data, priv->ext_ip6_config, 0); + } - ip4_config_merge_and_apply (self, NULL, FALSE); + /* Remove parts from ext_ip6_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->ac_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->dhcp6.ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->wwan_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_subtract (priv->ext_ip6_config, iter->data, 0); + } } -} - -static void -_ip6_config_intersect (gpointer value, gpointer user_data) -{ - NMIP6Config *dst = (NMIP6Config *) value; - NMIP6Config *src = (NMIP6Config *) user_data; - - nm_ip6_config_intersect (dst, src); -} - -static void -_ip6_config_subtract (gpointer value, gpointer user_data) -{ - NMIP6Config *dst = (NMIP6Config *) user_data; - NMIP6Config *src = (NMIP6Config *) value; - nm_ip6_config_subtract (dst, src); + return TRUE; } static void -update_ip6_config (NMDevice *self, gboolean initial) +update_ip_config (NMDevice *self, int addr_family, gboolean initial) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex; - gboolean capture_resolv_conf; - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if ( !initial - && activation_source_is_scheduled (self, - activate_stage5_ip6_config_commit, - AF_INET6)) { - priv->queued_ip6_config_pending = FALSE; - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); - _LOGT (LOGD_DEVICE, "IP6 update was postponed"); - return; - } - - ifindex = nm_device_get_ip_ifindex (self); - if (!ifindex) - return; + nm_assert_addr_family (addr_family); - capture_resolv_conf = initial - && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ()); + if (update_ext_ip_config (self, addr_family, initial, TRUE)) { + if (addr_family == AF_INET) { + if (priv->ext_ip4_config) + ip4_config_merge_and_apply (self, FALSE); + } else { + if (priv->ext_ip6_config_captured) + ip6_config_merge_and_apply (self, FALSE); + } + } - /* IPv6 */ - g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) { - - priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); - - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip6_config) - nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config); - if (priv->ac_ip6_config) - nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config); - if (priv->dhcp6.ip6_config) - nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config); - if (priv->wwan_ip6_config) - nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); - g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config); - - /* Remove parts from ext_ip6_config to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config); - if (priv->ac_ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config); - if (priv->dhcp6.ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.ip6_config); - if (priv->wwan_ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config); - g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config); - - ip6_config_merge_and_apply (self, FALSE); - } - - if ( priv->linklocal6_timeout_id + if ( addr_family == AF_INET6 + && priv->linklocal6_timeout_id && priv->ext_ip6_config_captured && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) { /* linklocal6 is ready now, do the state transition... we are also @@ -10885,8 +11102,8 @@ update_ip6_config (NMDevice *self, gboolean initial) void nm_device_capture_initial_config (NMDevice *self) { - update_ip4_config (self, TRUE); - update_ip6_config (self, TRUE); + update_ip_config (self, AF_INET, TRUE); + update_ip_config (self, AF_INET6, TRUE); } static gboolean @@ -10906,10 +11123,27 @@ queued_ip4_config_change (gpointer user_data) return TRUE; priv->queued_ip4_config_id = 0; - update_ip4_config (self, FALSE); + + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if (activation_source_is_scheduled (self, + activate_stage5_ip4_config_result, + AF_INET)) { + priv->queued_ip4_config_pending = FALSE; + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + _LOGT (LOGD_DEVICE, "IP4 update was postponed"); + } else + update_ip_config (self, AF_INET, FALSE); set_unmanaged_external_down (self, TRUE); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { + priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; + ip4_rp_filter_update (self); + } + return FALSE; } @@ -10932,7 +11166,19 @@ queued_ip6_config_change (gpointer user_data) return TRUE; priv->queued_ip6_config_id = 0; - update_ip6_config (self, FALSE); + + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if (activation_source_is_scheduled (self, + activate_stage5_ip6_config_commit, + AF_INET6)) { + priv->queued_ip6_config_pending = FALSE; + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + _LOGT (LOGD_DEVICE, "IP6 update was postponed"); + } else + update_ip_config (self, AF_INET6, FALSE); if (priv->state < NM_DEVICE_STATE_DEACTIVATING && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) { @@ -10977,6 +11223,8 @@ queued_ip6_config_change (gpointer user_data) g_clear_object (&priv->dad6_ip6_config); _set_ip_state (self, AF_INET6, IP_DONE); check_ip_state (self, FALSE); + if (priv->rt6_temporary_not_available) + nm_device_activate_schedule_ip6_config_result (self); } } @@ -11052,6 +11300,7 @@ NM_UTILS_FLAGS2STR_DEFINE (nm_unmanaged_flags2str, NMUnmanagedFlags, NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_EXPLICIT, "user-explicit"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_BY_DEFAULT, "by-default"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_SETTINGS, "user-settings"), + NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_CONF, "user-conf"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_UDEV, "user-udev"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_EXTERNAL_DOWN, "external-down"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_IS_SLAVE, "is-slave"), @@ -11144,11 +11393,19 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo if (NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_UDEV)) { /* configuration from udev or nm-config overwrites the by-default flag - * which is based on the device type. */ - flags &= ~NM_UNMANAGED_BY_DEFAULT; + * which is based on the device type. + * configuration from udev overwrites external-down */ + flags &= ~( NM_UNMANAGED_BY_DEFAULT + | NM_UNMANAGED_EXTERNAL_DOWN); + } - /* configuration from udev overwrites external-down */ - flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; + if (NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_CONF)) { + /* configuration from NetworkManager.conf overwrites the by-default flag + * which is based on the device type. + * It also overwrites the udev configuration and external-down */ + flags &= ~( NM_UNMANAGED_BY_DEFAULT + | NM_UNMANAGED_USER_UDEV + | NM_UNMANAGED_EXTERNAL_DOWN); } if ( NM_FLAGS_HAS (mask, NM_UNMANAGED_IS_SLAVE) @@ -11160,9 +11417,9 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo if (NM_FLAGS_HAS (mask, NM_UNMANAGED_USER_EXPLICIT)) { /* if the device is managed by user-decision, certain other flags * are ignored. */ - flags &= ~( NM_UNMANAGED_BY_DEFAULT | NM_UNMANAGED_USER_UDEV + | NM_UNMANAGED_USER_CONF | NM_UNMANAGED_EXTERNAL_DOWN); } @@ -11469,6 +11726,35 @@ nm_device_set_unmanaged_by_user_udev (NMDevice *self) } void +nm_device_set_unmanaged_by_user_conf (NMDevice *self) +{ + gboolean value; + NMUnmanFlagOp set_op; + + value = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + self, + -1, + TRUE); + switch (value) { + case TRUE: + set_op = NM_UNMAN_FLAG_OP_SET_MANAGED; + break; + case FALSE: + set_op = NM_UNMAN_FLAG_OP_SET_UNMANAGED; + break; + default: + set_op = NM_UNMAN_FLAG_OP_FORGET; + break; + } + + nm_device_set_unmanaged_by_flags (self, + NM_UNMANAGED_USER_CONF, + set_op, + NM_DEVICE_STATE_REASON_USER_REQUESTED); +} + +void nm_device_set_unmanaged_by_quitting (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -11489,14 +11775,6 @@ nm_device_set_unmanaged_by_quitting (NMDevice *self) /*****************************************************************************/ void -nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout) -{ - g_return_if_fail (NM_IS_DEVICE (self)); - - NM_DEVICE_GET_PRIVATE (self)->dhcp_timeout = timeout; -} - -void nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) { NMDevicePrivate *priv; @@ -11766,6 +12044,12 @@ check_connection_available (NMDevice *self, return TRUE; } + /* master types are always available even without carrier. + * Making connection non-available would un-enslave slaves which + * is not desired. */ + if (nm_device_is_master (self)) + return TRUE; + return FALSE; } @@ -12030,7 +12314,8 @@ nm_device_has_pending_action (NMDevice *self) if (priv->pending_actions) return TRUE; - if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { + if ( nm_device_is_real (self) + && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { /* as long as the platform link is not yet initialized, we have a pending * action. */ return TRUE; @@ -12091,26 +12376,22 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { - _update_default_route (self, AF_INET, FALSE, FALSE); - _update_default_route (self, AF_INET6, FALSE, FALSE); - } else { - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - } - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; + priv->v4_route_table_initalized = FALSE; + priv->v6_route_table_initalized = FALSE; + + priv->default_route_metric_penalty_ip4_has = FALSE; + priv->default_route_metric_penalty_ip6_has = FALSE; + priv->linklocal6_dad_counter = 0; /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE); - nm_device_set_ip6_config (self, NULL, TRUE, TRUE); + nm_device_set_ip4_config (self, NULL, TRUE, NULL); + nm_device_set_ip6_config (self, NULL, TRUE); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); @@ -12125,6 +12406,9 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->ip6_config); g_clear_object (&priv->dad6_ip6_config); + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + g_slist_free_full (priv->vpn4_configs, g_object_unref); priv->vpn4_configs = NULL; g_slist_free_full (priv->vpn6_configs, g_object_unref); @@ -12198,18 +12482,24 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); + ifindex = nm_device_get_ip_ifindex (self); + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* master: release slaves */ nm_device_master_release_slaves (self); /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), ifindex); - nm_platform_address_flush (nm_device_get_platform (self), ifindex); + NMPlatform *platform = nm_device_get_platform (self); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); } } + if (ifindex > 0) + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL); + /* slave: mark no longer enslaved */ if ( priv->master && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) @@ -12241,8 +12531,10 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean && cleanup_type == CLEANUP_TYPE_DECONFIGURE) { _LOGT (LOGD_DEVICE, "mtu: reset device-mtu: %u, ipv6-mtu: %u, ifindex: %d", (guint) priv->mtu_initial, (guint) priv->ip6_mtu_initial, ifindex); - if (priv->mtu_initial) + if (priv->mtu_initial) { nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, priv->mtu_initial); + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; + } if (priv->ip6_mtu_initial) { char sbuf[64]; @@ -12267,15 +12559,13 @@ static char * find_dhcp4_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint i, n; + const NMPlatformIP4Address *a; + NMDedupMultiIter ipconf_iter; if (!priv->ip4_config) return NULL; - n = nm_ip4_config_get_num_addresses (priv->ip4_config); - for (i = 0; i < n; i++) { - const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i); - + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip4_config, &a) { if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); } @@ -12346,7 +12636,7 @@ nm_device_spawn_iface_helper (NMDevice *self) g_assert (s_ip4); g_ptr_array_add (argv, g_strdup ("--priority4")); - g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip4_route_metric (self))); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_route_metric (self, AF_INET))); g_ptr_array_add (argv, g_strdup ("--dhcp4")); g_ptr_array_add (argv, g_strdup (dhcp4_address)); @@ -12388,7 +12678,7 @@ nm_device_spawn_iface_helper (NMDevice *self) g_assert (s_ip6); g_ptr_array_add (argv, g_strdup ("--priority6")); - g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip6_route_metric (self))); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_route_metric (self, AF_INET6))); g_ptr_array_add (argv, g_strdup ("--slaac")); @@ -12555,7 +12845,7 @@ _set_state_full (NMDevice *self, if ( (priv->state == state) && ( state != NM_DEVICE_STATE_UNAVAILABLE || !priv->firmware_missing)) { - _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', internal state '%s'%s)", + _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)", nm_device_state_to_str (old_state), nm_device_state_to_str (state), reason_to_string (reason), @@ -12564,7 +12854,7 @@ _set_state_full (NMDevice *self, return; } - _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', internal state '%s')", + _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s')", nm_device_state_to_str (old_state), nm_device_state_to_str (state), reason_to_string (reason), @@ -12593,6 +12883,10 @@ _set_state_full (NMDevice *self, NM_DEVICE_SYS_IFACE_STATE_ASSUME)) nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_ACTIVATED) + priv->auth_retries = NM_DEVICE_AUTH_RETRIES_UNSET; + if (state > NM_DEVICE_STATE_DISCONNECTED) nm_device_assume_state_reset (self); @@ -12752,15 +13046,11 @@ _set_state_full (NMDevice *self, if ( priv->queued_act_request && !priv->queued_act_request_is_waiting_for_carrier) { NMActRequest *queued_req; - gboolean success; queued_req = priv->queued_act_request; priv->queued_act_request = NULL; - success = _device_activate (self, queued_req); + _device_activate (self, queued_req); g_object_unref (queued_req); - if (success) - break; - /* fall through */ } break; case NM_DEVICE_STATE_ACTIVATED: @@ -13380,7 +13670,7 @@ handle_fail: _NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)", operation, addr, detail, - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); } if (was_up) { @@ -13683,6 +13973,12 @@ nm_device_get_initial_hw_address (NMDevice *self) gboolean nm_device_spec_match_list (NMDevice *self, const GSList *specs) { + return nm_device_spec_match_list_full (self, specs, FALSE); +} + +int +nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_match_value) +{ NMDeviceClass *klass; NMMatchSpecMatchType m; @@ -13697,7 +13993,17 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs) nm_device_get_driver_version (self), nm_device_get_permanent_hw_address (self), klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL); - return m == NM_MATCH_SPEC_MATCH; + + switch (m) { + case NM_MATCH_SPEC_MATCH: + return TRUE; + case NM_MATCH_SPEC_NEG_MATCH: + return FALSE; + case NM_MATCH_SPEC_NO_MATCH: + return no_match_value; + } + nm_assert_not_reached (); + return no_match_value; } guint @@ -13727,6 +14033,54 @@ nm_device_get_supplicant_timeout (NMDevice *self) SUPPLICANT_DEFAULT_TIMEOUT); } +gboolean +nm_device_auth_retries_try_next (NMDevice *self) +{ + NMDevicePrivate *priv; + NMSettingConnection *s_con; + int auth_retries; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + auth_retries = priv->auth_retries; + + if (G_UNLIKELY (auth_retries == NM_DEVICE_AUTH_RETRIES_UNSET)) { + auth_retries = -1; + + s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION)); + if (s_con) + auth_retries = nm_setting_connection_get_auth_retries (s_con); + + if (auth_retries == -1) { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.auth-retries", + self); + auth_retries = _nm_utils_ascii_str_to_int64 (value, 10, -1, G_MAXINT32, -1); + } + + if (auth_retries == 0) + auth_retries = NM_DEVICE_AUTH_RETRIES_INFINITY; + else if (auth_retries == -1) + auth_retries = NM_DEVICE_AUTH_RETRIES_DEFAULT; + else + nm_assert (auth_retries > 0); + + priv->auth_retries = auth_retries; + } + + if (auth_retries == NM_DEVICE_AUTH_RETRIES_INFINITY) + return TRUE; + if (auth_retries <= 0) { + nm_assert (auth_retries == 0); + return FALSE; + } + priv->auth_retries--; + return TRUE; +} + /*****************************************************************************/ static const char * @@ -13742,7 +14096,7 @@ _activation_func_to_string (ActivationHandleFunc func) FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage3_ip_config_start); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip4_config_timeout); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip6_config_timeout); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_commit); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_result); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip6_config_commit); g_return_val_if_reached ("unknown"); } @@ -13758,23 +14112,22 @@ nm_device_init (NMDevice *self) self->_priv = priv; + c_list_init (&priv->slaves); + priv->netns = g_object_ref (NM_NETNS_GET); + priv->auth_retries = NM_DEVICE_AUTH_RETRIES_UNSET; priv->type = NM_DEVICE_TYPE_UNKNOWN; priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; priv->state = NM_DEVICE_STATE_UNMANAGED; priv->state_reason = NM_DEVICE_STATE_REASON_NONE; - priv->dhcp_timeout = 0; priv->rfkill_type = RFKILL_TYPE_UNKNOWN; priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT; priv->unmanaged_mask = priv->unmanaged_flags; priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); - priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + priv->ip6_saved_properties = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); priv->sys_iface_state = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - priv->default_route.v4_is_assumed = TRUE; - priv->default_route.v6_is_assumed = TRUE; - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; } @@ -13843,9 +14196,6 @@ constructed (GObject *object) g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); - g_signal_connect (nm_netns_get_route_manager (priv->netns), NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_CALLBACK (ip4_routes_changed_changed_cb), self); - priv->settings = g_object_ref (NM_SETTINGS_GET); g_assert (priv->settings); @@ -13886,9 +14236,6 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - g_signal_handlers_disconnect_by_func (nm_netns_get_route_manager (priv->netns), - G_CALLBACK (ip4_routes_changed_changed_cb), self); - g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); priv->arping.dad_list = NULL; @@ -13904,7 +14251,7 @@ dispose (GObject *object) _cleanup_generic_pre (self, CLEANUP_TYPE_KEEP); - g_warn_if_fail (priv->slaves == NULL); + g_warn_if_fail (c_list_is_empty (&priv->slaves)); g_assert (priv->master_ready_id == 0); /* Let the kernel manage IPv6LL again */ @@ -14083,10 +14430,6 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->rfkill_type = g_value_get_uint (value); break; - case PROP_IS_MASTER: - /* construct-only */ - priv->is_master = g_value_get_boolean (value); - break; case PROP_PERM_HW_ADDRESS: /* construct-only */ priv->hw_addr_perm = g_value_dup_string (value); @@ -14221,9 +14564,6 @@ get_property (GObject *object, guint prop_id, case PROP_PHYSICAL_PORT_ID: g_value_set_string (value, priv->physical_port_id); break; - case PROP_IS_MASTER: - g_value_set_boolean (value, priv->is_master); - break; case PROP_MASTER: g_value_set_object (value, nm_device_get_master (self)); break; @@ -14260,13 +14600,15 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, nm_device_is_real (self)); break; case PROP_SLAVES: { - GSList *slave_iter; + CList *slave_iter; char **slave_list; - guint i; + gsize i, n; - slave_list = g_new (char *, g_slist_length (priv->slaves) + 1); - for (slave_iter = priv->slaves, i = 0; slave_iter; slave_iter = slave_iter->next) { - SlaveInfo *info = slave_iter->data; + n = c_list_length (&priv->slaves); + slave_list = g_new (char *, n + 1); + i = 0; + c_list_for_each (slave_iter, &priv->slaves) { + SlaveInfo *info = c_list_entry (slave_iter, SlaveInfo, lst_slave); const char *path; if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) @@ -14275,6 +14617,7 @@ get_property (GObject *object, guint prop_id, if (path) slave_list[i++] = g_strdup (path); } + nm_assert (i <= n); slave_list[i] = NULL; g_value_take_boxed (value, slave_list); break; @@ -14323,7 +14666,6 @@ nm_device_class_init (NMDeviceClass *klass) klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; - klass->have_any_ready_slaves = have_any_ready_slaves; klass->get_type_description = get_type_description; klass->get_autoconnect_allowed = get_autoconnect_allowed; @@ -14484,11 +14826,6 @@ nm_device_class_init (NMDeviceClass *klass) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IS_MASTER] = - g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); obj_properties[PROP_MASTER] = g_param_spec_object (NM_DEVICE_MASTER, "", "", NM_TYPE_DEVICE, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 6d17d7c9..350a17b2 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -120,7 +120,6 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ #define NM_DEVICE_IFINDEX "ifindex" /* Internal only */ -#define NM_DEVICE_IS_MASTER "is-master" /* Internal only */ #define NM_DEVICE_MASTER "master" /* Internal only */ #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ @@ -196,6 +195,10 @@ typedef struct { const char *connection_type; const NMLinkType *link_types; + /* Whether the device type is a master-type. This depends purely on the + * type (NMDeviceClass), not the actual device instance. */ + bool is_master:1; + void (*state_changed) (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -364,9 +367,6 @@ typedef struct { NMDevice *slave, gboolean configure); - gboolean (* have_any_ready_slaves) (NMDevice *self, - const GSList *slaves); - void (* parent_changed_notify) (NMDevice *self, int old_ifindex, NMDevice *old_parent, @@ -378,10 +378,10 @@ typedef struct { * @self: the #NMDevice * @component: the component (device, modem, etc) which was added * - * Notifies @self that a new component was added to the Manager. This - * may include any kind of %GObject subclass, and the device is expected - * to match only specific components they care about, like %NMModem objects - * or %NMDevice objects. + * Notifies @self that a new component that a device might be interested + * in was detected by some device factory. It may include an object of + * %GObject subclass to help the devices decide whether it claims that + * particular object itself and the emitting factory should not. * * Returns: %TRUE if the component was claimed exclusively and no further * devices should be notified of the new component. %FALSE to indicate @@ -406,6 +406,9 @@ typedef struct { void (* reapply_connection) (NMDevice *self, NMConnection *con_old, NMConnection *con_new); + + guint32 (* get_dhcp_timeout) (NMDevice *self, + int addr_family); } NMDeviceClass; typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, @@ -416,6 +419,7 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, GType nm_device_get_type (void); +struct _NMDedupMultiIndex *nm_device_get_multi_index (NMDevice *self); NMNetns *nm_device_get_netns (NMDevice *self); NMPlatform *nm_device_get_platform (NMDevice *self); @@ -443,9 +447,8 @@ NMDeviceType nm_device_get_device_type (NMDevice *dev); NMLinkType nm_device_get_link_type (NMDevice *dev); NMMetered nm_device_get_metered (NMDevice *dev); -int nm_device_get_priority (NMDevice *dev); -guint32 nm_device_get_ip4_route_metric (NMDevice *dev); -guint32 nm_device_get_ip6_route_metric (NMDevice *dev); +guint32 nm_device_get_route_table (NMDevice *self, int addr_family, gboolean fallback_main); +guint32 nm_device_get_route_metric (NMDevice *dev, int addr_family); const char * nm_device_get_hw_address (NMDevice *dev); const char * nm_device_get_permanent_hw_address (NMDevice *self); @@ -493,6 +496,8 @@ NMSetting * nm_device_get_applied_setting (NMDevice *dev, GType setting_ty void nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config); +gboolean nm_device_ignore_carrier_by_default (NMDevice *self); + gboolean nm_device_is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags); gboolean nm_device_has_carrier (NMDevice *dev); @@ -522,6 +527,7 @@ gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnec gboolean nm_device_unmanage_on_quit (NMDevice *self); gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs); +int nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_match_value); gboolean nm_device_is_activating (NMDevice *dev); gboolean nm_device_autoconnect_allowed (NMDevice *self); @@ -561,6 +567,10 @@ void nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device); * the settings plugins, such as NM_CONTROLLED=no in ifcfg-rh), it cannot * be overruled and is authorative. That is because users may depend on * dropping a ifcfg-rh file to ensure the device is unmanaged. + * @NM_UNMANAGED_USER_CONF: %TRUE when unmanaged by user decision via + * the NetworkManager.conf ("unmanaged" in the [device] section). + * Contray to @NM_UNMANAGED_USER_SETTINGS, this can be overwritten via + * D-Bus. * @NM_UNMANAGED_BY_DEFAULT: %TRUE for certain device types where we unmanage * them by default * @NM_UNMANAGED_USER_UDEV: %TRUE when unmanaged by user decision (via UDev rule) @@ -585,6 +595,7 @@ typedef enum { /*< skip >*/ /* These flags can be non-effective and be overwritten * by other flags. */ NM_UNMANAGED_BY_DEFAULT = (1LL << 8), + NM_UNMANAGED_USER_CONF = (1LL << 9), NM_UNMANAGED_USER_UDEV = (1LL << 10), NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 11), NM_UNMANAGED_IS_SLAVE = (1LL << 12), @@ -615,6 +626,7 @@ void nm_device_set_unmanaged_by_flags_queue (NMDevice *self, NMDeviceStateReason reason); void nm_device_set_unmanaged_by_user_settings (NMDevice *self); void nm_device_set_unmanaged_by_user_udev (NMDevice *self); +void nm_device_set_unmanaged_by_user_conf (NMDevice *self); void nm_device_set_unmanaged_by_quitting (NMDevice *device); gboolean nm_device_is_nm_owned (NMDevice *device); @@ -648,6 +660,9 @@ gboolean nm_device_unrealize (NMDevice *device, gboolean remove_resources, GError **error); +void nm_device_update_from_platform_link (NMDevice *self, + const NMPlatformLink *plink); + gboolean nm_device_get_autoconnect (NMDevice *device); void nm_device_set_autoconnect_intern (NMDevice *device, gboolean autoconnect); void nm_device_emit_recheck_auto_activate (NMDevice *device); @@ -694,8 +709,8 @@ gboolean nm_device_owns_iface (NMDevice *device, const char *iface); NMConnection *nm_device_new_default_connection (NMDevice *self); -const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed); -const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed); +const NMPObject *nm_device_get_best_default_route (NMDevice *self, + int addr_family); void nm_device_spawn_iface_helper (NMDevice *self); @@ -720,6 +735,9 @@ void nm_device_update_initial_hw_address (NMDevice *self); void nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze); void nm_device_update_dynamic_ip_setup (NMDevice *self); guint nm_device_get_supplicant_timeout (NMDevice *self); + +gboolean nm_device_auth_retries_try_next (NMDevice *self); + gboolean nm_device_hw_addr_get_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi, @@ -735,6 +753,16 @@ void nm_device_check_connectivity (NMDevice *self, gpointer user_data); NMConnectivityState nm_device_get_connectivity_state (NMDevice *self); +typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer; +struct _NMBtVTableNetworkServer { + gboolean (*is_available) (const NMBtVTableNetworkServer *vtable, + const char *addr); + gboolean (*register_bridge) (const NMBtVTableNetworkServer *vtable, + const char *addr, + NMDevice *device); + gboolean (*unregister_bridge) (const NMBtVTableNetworkServer *vtable, + NMDevice *device); +}; const char *nm_device_state_to_str (NMDeviceState state); diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index bfd631f0..2ed2a7d9 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -274,13 +274,15 @@ static guint lldp_neighbor_id_hash (gconstpointer ptr) { const LldpNeighbor *neigh = ptr; - guint hash; - - hash = 23423423u + ((guint) (neigh->chassis_id ? g_str_hash (neigh->chassis_id) : 12321u)); - hash = (hash * 33u) + ((guint) (neigh->port_id ? g_str_hash (neigh->port_id) : 34342343u)); - hash = (hash * 33u) + ((guint) neigh->chassis_id_type); - hash = (hash * 33u) + ((guint) neigh->port_id_type); - return hash; + NMHashState h; + + nm_hash_init (&h, 23423423u); + nm_hash_update_str0 (&h, neigh->chassis_id); + nm_hash_update_str0 (&h, neigh->port_id); + nm_hash_update_vals (&h, + neigh->chassis_id_type, + neigh->port_id_type); + return nm_hash_complete (&h); } static int diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c new file mode 100644 index 00000000..53ea2b82 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-bridge.c @@ -0,0 +1,156 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-bridge.h" +#include "nm-device-ovs-port.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-bridge.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsBridge); + +/*****************************************************************************/ + +struct _NMDeviceOvsBridge { + NMDevice parent; +}; + +struct _NMDeviceOvsBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-bridge"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The actual backing resources will be created on enslavement by the port + * when it can identify the port and the bridge. */ + + return TRUE; +} + +static gboolean +unrealize (NMDevice *device, GError **error) +{ + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + const char *connection_type; + + if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + connection_type = nm_connection_get_connection_type (connection); + if (!nm_streq0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) + return FALSE; + + return TRUE; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static gboolean +enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +{ + if (!configure) + return TRUE; + + if (!NM_IS_DEVICE_OVS_PORT (slave)) + return FALSE; + + return TRUE; +} + +static void +release_slave (NMDevice *device, NMDevice *slave, gboolean configure) +{ +} + +/*****************************************************************************/ + +static void +nm_device_ovs_bridge_init (NMDeviceOvsBridge *self) +{ +} + +static void +nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->unrealize = unrealize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->check_connection_compatible = check_connection_compatible; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_BRIDGE_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-bridge.h b/src/devices/ovs/nm-device-ovs-bridge.h new file mode 100644 index 00000000..631b4754 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-bridge.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ +#define __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ + +#define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type ()) +#define NM_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge)) +#define NM_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) +#define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_IS_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_DEVICE_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) + +typedef struct _NMDeviceOvsBridge NMDeviceOvsBridge; +typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass; + +GType nm_device_ovs_bridge_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ */ diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c new file mode 100644 index 00000000..426521c5 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -0,0 +1,191 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-interface.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-port.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsInterface); + +/*****************************************************************************/ + +struct _NMDeviceOvsInterface { + NMDevice parent; +}; + +struct _NMDeviceOvsInterfaceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-interface"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The actual backing resources will be created once an interface is + * added to a port of ours, since there can be neither an empty port nor + * an empty bridge. */ + + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; +} + +static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + return TRUE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingOvsInterface *s_ovs_iface; + + if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); + if (!s_ovs_iface) + return FALSE; + if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), + "internal", "patch")) { + return FALSE; + } + + s_con = nm_connection_get_setting_connection (connection); + if (g_strcmp0 (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_OVS_INTERFACE_SETTING_NAME) != 0) { + return FALSE; + } + + return TRUE; +} + +static void +link_changed (NMDevice *device, + const NMPlatformLink *pllink) +{ + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + nm_device_bring_up (device, TRUE, NULL); + nm_device_activate_schedule_stage3_ip_config_start (device); + } +} + +static gboolean +_is_internal_interface (NMDevice *device) +{ + NMConnection *connection = nm_device_get_applied_connection (device); + NMSettingOvsInterface *s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); + + g_return_val_if_fail (s_ovs_iface, FALSE); + + return strcmp (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal") == 0; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + if (!_is_internal_interface (device)) + return NM_ACT_STAGE_RETURN_IP_FAIL; + + if (!nm_device_get_ip_ifindex (device)) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + if (!_is_internal_interface (device)) + return NM_ACT_STAGE_RETURN_IP_FAIL; + + if (!nm_device_get_ip_ifindex (device)) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); +} + +static gboolean +can_unmanaged_external_down (NMDevice *self) +{ + return FALSE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_interface_init (NMDeviceOvsInterface *self) +{ +} + +static void +nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH); + + device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->is_available = is_available; + device_class->check_connection_compatible = check_connection_compatible; + device_class->link_changed = link_changed; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->can_unmanaged_external_down = can_unmanaged_external_down; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_INTERFACE_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-interface.h b/src/devices/ovs/nm-device-ovs-interface.h new file mode 100644 index 00000000..a748e206 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-interface.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ +#define __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ + +#define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type ()) +#define NM_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface)) +#define NM_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) +#define NM_IS_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_IS_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_DEVICE_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) + +typedef struct _NMDeviceOvsInterface NMDeviceOvsInterface; +typedef struct _NMDeviceOvsInterfaceClass NMDeviceOvsInterfaceClass; + +GType nm_device_ovs_interface_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ */ diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c new file mode 100644 index 00000000..83199f2d --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -0,0 +1,201 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-port.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ovs-port.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsPort.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsPort); + +/*****************************************************************************/ + +struct _NMDeviceOvsPort { + NMDevice parent; +}; + +struct _NMDeviceOvsPortClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-port"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The port will be added to ovsdb when an interface is enslaved, + * because there's no such thing like an empty port. */ + + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingConnection *s_con; + const char *connection_type; + + if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!connection_type) + return FALSE; + + if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) + return TRUE; + + return FALSE; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static void +add_iface_cb (GError *error, gpointer user_data) +{ + NMDevice *slave = user_data; + + if (error) { + nm_log_warn (LOGD_DEVICE, "device %s could not be added to a ovs port: %s", + nm_device_get_iface (slave), error->message); + nm_device_state_changed (slave, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); + } + + g_object_unref (slave); +} + +static gboolean +enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +{ + NMActiveConnection *ac_port = NULL; + NMActiveConnection *ac_bridge = NULL; + + if (!configure) + return TRUE; + + + ac_port = NM_ACTIVE_CONNECTION (nm_device_get_act_request (device)); + ac_bridge = nm_active_connection_get_master (ac_port); + if (!ac_bridge) + ac_bridge = ac_port; + + nm_ovsdb_add_interface (nm_ovsdb_get (), + nm_active_connection_get_applied_connection (ac_bridge), + nm_device_get_applied_connection (device), + nm_device_get_applied_connection (slave), + add_iface_cb, g_object_ref (slave)); + + return TRUE; +} + +static void +del_iface_cb (GError *error, gpointer user_data) +{ + NMDevice *slave = user_data; + + if (error) { + nm_log_warn (LOGD_DEVICE, "device %s could not be removed from a ovs port: %s", + nm_device_get_iface (slave), error->message); + nm_device_state_changed (slave, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); + } + + g_object_unref (slave); +} + +static void +release_slave (NMDevice *device, NMDevice *slave, gboolean configure) +{ + nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave), + del_iface_cb, g_object_ref (slave)); +} + +/*****************************************************************************/ + +static void +nm_device_ovs_port_init (NMDeviceOvsPort *self) +{ +} + +static void +nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->check_connection_compatible = check_connection_compatible; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_PORT_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-port.h b/src/devices/ovs/nm-device-ovs-port.h new file mode 100644 index 00000000..5ccf1ec1 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-port.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_PORT_H__ +#define __NETWORKMANAGER_DEVICE_OVS_PORT_H__ + +#define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type ()) +#define NM_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort)) +#define NM_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) +#define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_IS_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_DEVICE_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) + +typedef struct _NMDeviceOvsPort NMDeviceOvsPort; +typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass; + +GType nm_device_ovs_port_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_PORT_H__ */ diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c new file mode 100644 index 00000000..830f94fc --- /dev/null +++ b/src/devices/ovs/nm-ovs-factory.c @@ -0,0 +1,195 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-manager.h" +#include "nm-ovsdb.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ovs-bridge.h" +#include "platform/nm-platform.h" +#include "nm-core-internal.h" +#include "devices/nm-device-factory.h" + +/*****************************************************************************/ + +typedef struct { + NMDeviceFactory parent; +} NMOvsFactory; + +typedef struct { + NMDeviceFactoryClass parent; +} NMOvsFactoryClass; + +#define NM_TYPE_OVS_FACTORY (nm_ovs_factory_get_type ()) +#define NM_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory)) +#define NM_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) +#define NM_IS_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVS_FACTORY)) +#define NM_IS_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVS_FACTORY)) +#define NM_OVS_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) + +static GType nm_ovs_factory_get_type (void); +G_DEFINE_TYPE (NMOvsFactory, nm_ovs_factory, NM_TYPE_DEVICE_FACTORY) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovs", __VA_ARGS__) + +/*****************************************************************************/ + +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_OVS_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME) +) + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_OVS_FACTORY, NULL); +} + +static NMDevice * +new_device_from_type (const char *name, NMDeviceType device_type) +{ + GType type; + const char *type_desc; + NMLinkType link_type = NM_LINK_TYPE_NONE; + + if (nm_manager_get_device (nm_manager_get (), name, device_type)) + return NULL; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) { + type = NM_TYPE_DEVICE_OVS_INTERFACE; + type_desc = "OpenVSwitch Interface"; + link_type = NM_LINK_TYPE_OPENVSWITCH; + } else if (device_type == NM_DEVICE_TYPE_OVS_PORT) { + type = NM_TYPE_DEVICE_OVS_PORT; + type_desc = "OpenVSwitch Port"; + } else if (device_type == NM_DEVICE_TYPE_OVS_BRIDGE) { + type = NM_TYPE_DEVICE_OVS_BRIDGE; + type_desc = "OpenVSwitch Bridge"; + } else { + return NULL; + } + + return g_object_new (type, + NM_DEVICE_IFACE, name, + NM_DEVICE_DRIVER, "openvswitch", + NM_DEVICE_DEVICE_TYPE, device_type, + NM_DEVICE_TYPE_DESC, type_desc, + NM_DEVICE_LINK_TYPE, link_type, + NULL); +} + +static void +ovsdb_device_added (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type, + NMDeviceFactory *self) +{ + NMDevice *device = NULL; + + device = new_device_from_type (name, device_type); + if (!device) + return; + + g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device); + g_object_unref (device); +} + +static void +ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type, + NMDeviceFactory *self) +{ + NMDevice *device; + NMDeviceState device_state; + + device = nm_manager_get_device (nm_manager_get (), name, device_type); + if (!device) + return; + + device_state = nm_device_get_state (device); + if ( device_type == NM_DEVICE_TYPE_OVS_INTERFACE + && device_state > NM_DEVICE_STATE_DISCONNECTED + && device_state < NM_DEVICE_STATE_DEACTIVATING) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_REMOVED); + } else if (device_state == NM_DEVICE_STATE_UNMANAGED) { + nm_device_unrealize (device, TRUE, NULL); + } +} + +static void +start (NMDeviceFactory *self) +{ + NMOvsdb *ovsdb; + + ovsdb = nm_ovsdb_get (); + + g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_ADDED, G_CALLBACK (ovsdb_device_added), self, (GConnectFlags) 0); + g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_REMOVED, G_CALLBACK (ovsdb_device_removed), self, (GConnectFlags) 0); +} + +static NMDevice * +create_device (NMDeviceFactory *self, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + NMDeviceType device_type = NM_DEVICE_TYPE_UNKNOWN; + const char *connection_type = NULL; + + if (g_strcmp0 (iface, "ovs-system") == 0) { + *out_ignore = TRUE; + return NULL; + } + + if (connection) + connection_type = nm_connection_get_connection_type (connection); + + if (plink) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_PORT; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_BRIDGE; + + return new_device_from_type (iface, device_type); +} + +static void +nm_ovs_factory_init (NMOvsFactory *self) +{ +} + +static void +nm_ovs_factory_class_init (NMOvsFactoryClass *klass) +{ + NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass); + + factory_class->get_supported_types = get_supported_types; + factory_class->start = start; + factory_class->create_device = create_device; +} diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c new file mode 100644 index 00000000..b44668c0 --- /dev/null +++ b/src/devices/ovs/nm-ovsdb.c @@ -0,0 +1,1607 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-ovsdb.h" + +#include <string.h> +#include <jansson.h> +#include <gmodule.h> +#include <gio/gunixsocketaddress.h> + +#include "devices/nm-device.h" +#include "platform/nm-platform.h" +#include "nm-core-internal.h" + +/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */ +#ifndef json_boolean +#define json_boolean(val) ((val) ? json_true() : json_false()) +#endif + +/* Added in Jansson v2.5 (released Sep 19 2013), but travis.ci has v2.2. */ +#ifndef json_array_foreach +#define json_array_foreach(array, index, value) \ + for (index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) +#endif + +/* Added in Jansson v2.3 (released Jan 27 2012) */ +#ifndef json_object_foreach +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) +#endif + +/*****************************************************************************/ + +typedef struct { + char *name; + char *connection_uuid; + GPtrArray *interfaces; /* interface uuids */ +} OpenvswitchPort; + +typedef struct { + char *name; + char *connection_uuid; + GPtrArray *ports; /* port uuids */ +} OpenvswitchBridge; + +typedef struct { + char *name; + char *type; + char *connection_uuid; +} OpenvswitchInterface; + +/*****************************************************************************/ + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + DEVICE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GSocketClient *client; + GSocketConnection *conn; + GCancellable *cancellable; + char buf[4096]; /* Input buffer */ + size_t bufp; /* Last decoded byte in the input buffer. */ + GString *input; /* JSON stream waiting for decoding. */ + GString *output; /* JSON stream to be sent. */ + gint64 seq; + GArray *calls; /* Method calls waiting for a response. */ + GHashTable *interfaces; /* interface uuid => OpenvswitchInterface */ + GHashTable *ports; /* port uuid => OpenvswitchPort */ + GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */ + const char *db_uuid; +} NMOvsdbPrivate; + +struct _NMOvsdb { + GObject parent; + NMOvsdbPrivate _priv; +}; + +struct _NMOvsdbClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMOvsdb, nm_ovsdb, G_TYPE_OBJECT) + +#define NM_OVSDB_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMOvsdb, NM_IS_OVSDB) + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovsdb", __VA_ARGS__) + +NM_DEFINE_SINGLETON_GETTER (NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB); + +/*****************************************************************************/ + +static void ovsdb_try_connect (NMOvsdb *self); +static void ovsdb_disconnect (NMOvsdb *self); +static void ovsdb_read (NMOvsdb *self); +static void ovsdb_write (NMOvsdb *self); +static void ovsdb_next_command (NMOvsdb *self); + +/*****************************************************************************/ + +/* ovsdb command abstraction. */ + +typedef void (*OvsdbMethodCallback) (NMOvsdb *self, json_t *response, + GError *error, gpointer user_data); + +typedef enum { + OVSDB_MONITOR, + OVSDB_ADD_INTERFACE, + OVSDB_DEL_INTERFACE, +} OvsdbCommand; + +typedef struct { + gint64 id; +#define COMMAND_PENDING -1 /* id not yet assigned */ + OvsdbCommand command; + OvsdbMethodCallback callback; + gpointer user_data; + union { + const char *ifname; + struct { + NMConnection *bridge; + NMConnection *port; + NMConnection *interface; + }; + }; +} OvsdbMethodCall; + +static void +_call_trace (const char *comment, OvsdbMethodCall *call, json_t *msg) +{ +#ifdef NM_MORE_LOGGING + char *str = NULL; + + if (msg) + str = json_dumps (msg, 0); + + switch (call->command) { + case OVSDB_MONITOR: + _LOGT ("%s: monitor%s%s", + comment, + msg ? ": " : "", + msg ? str : ""); + break; + case OVSDB_ADD_INTERFACE: + _LOGT ("%s: add-iface bridge=%s port=%s interface=%s%s%s", + comment, + nm_connection_get_interface_name (call->bridge), + nm_connection_get_interface_name (call->port), + nm_connection_get_interface_name (call->interface), + msg ? ": " : "", + msg ? str : ""); + break; + case OVSDB_DEL_INTERFACE: + _LOGT ("%s: del-iface interface=%s%s%s", + comment, call->ifname, + msg ? ": " : "", + msg ? str : ""); + break; + } + + if (msg) + g_free (str); +#endif +} + +/** + * ovsdb_call_method: + * + * Queues the ovsdb command. Eventually fires the command right away if + * there's no command pending completion. + */ +static void +ovsdb_call_method (NMOvsdb *self, OvsdbCommand command, + const char *ifname, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + OvsdbMethodCallback callback, gpointer user_data) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call; + + /* Ensure we're not unsynchronized before we queue the method call. */ + ovsdb_try_connect (self); + + g_array_set_size (priv->calls, priv->calls->len + 1); + call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1); + call->id = COMMAND_PENDING; + call->command = command; + call->callback = callback; + call->user_data = user_data; + + switch (call->command) { + case OVSDB_MONITOR: + break; + case OVSDB_ADD_INTERFACE: + call->bridge = nm_simple_connection_new_clone (bridge); + call->port = nm_simple_connection_new_clone (port); + call->interface = nm_simple_connection_new_clone (interface); + break; + case OVSDB_DEL_INTERFACE: + call->ifname = g_strdup (ifname); + break; + } + + _call_trace ("enqueue", call, NULL); + + ovsdb_next_command (self); +} + +/*****************************************************************************/ + +/* Create and process the JSON-RPC messages from ovsdb. */ + +/** + * _expect_ovs_bridges: + * + * Return a command that will fail the transaction if the actual set of + * bridges doesn't match @bridges. This is a way of detecting race conditions + * with other ovsdb clients that might be adding or removing bridges + * at the same time. + */ +static void +_expect_ovs_bridges (json_t *params, const char *db_uuid, json_t *bridges) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, [s, s]]]}", + "op", "wait", "table", "Open_vSwitch", + "timeout", 0, "columns", "bridges", + "until", "==", "rows", "bridges", "set", bridges, + "where", "_uuid", "==", "uuid", db_uuid) + ); +} + +/** + * _set_ovs_bridges: + * + * Return a command that will update the list of bridges in @db_uuid + * database to @new_bridges. + */ +static void +_set_ovs_bridges (json_t *params, const char *db_uuid, json_t *new_bridges) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, [s, s]]]}", + "op", "update", "table", "Open_vSwitch", + "row", "bridges", "set", new_bridges, + "where", "_uuid", "==", "uuid", db_uuid) + ); +} + +/** + * _expect_bridge_ports: + * + * Return a command that will fail the transaction if the actual set of + * ports in bridge @ifname doesn't match @ports. This is a way of detecting + * race conditions with other ovsdb clients that might be adding or removing + * bridge ports at the same time. + */ +static void +_expect_bridge_ports (json_t *params, const char *ifname, json_t *ports) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, s]]}", + "op", "wait", "table", "Bridge", + "timeout", 0, "columns", "ports", + "until", "==", "rows", "ports", "set", ports, + "where", "name", "==", ifname) + ); +} + +/** + * _set_bridge_ports: + * + * Return a command that will update the list of ports of bridge + * @ifname to @new_ports. + */ +static void +_set_bridge_ports (json_t *params, const char *ifname, json_t *new_ports) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}", + "op", "update", "table", "Bridge", + "row", "ports", "set", new_ports, + "where", "name", "==", ifname) + ); +} + +/** + * _expect_port_interfaces: + * + * Return a command that will fail the transaction if the actual set of + * interfaces in port @ifname doesn't match @interfaces. This is a way of + * detecting race conditions with other ovsdb clients that might be adding + * or removing port interfaces at the same time. + */ +static void +_expect_port_interfaces (json_t *params, const char *ifname, json_t *interfaces) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, s]]}", + "op", "wait", "table", "Port", + "timeout", 0, "columns", "interfaces", + "until", "==", "rows", "interfaces", "set", interfaces, + "where", "name", "==", ifname) + ); +} + +/** + * _set_port_interfaces: + * + * Return a command that will update the list of interfaces of port @ifname + * to @new_interfaces. + */ +static void +_set_port_interfaces (json_t *params, const char *ifname, json_t *new_interfaces) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}", + "op", "update", "table", "Port", + "row", "interfaces", "set", new_interfaces, + "where", "name", "==", ifname) + ); +} + +/** + * _insert_interface: + * + * Returns an commands that adds new interface from a given connection. + */ +static void +_insert_interface (json_t *params, NMConnection *interface) +{ + const char *type = NULL; + NMSettingOvsInterface *s_ovs_iface; + NMSettingOvsPatch *s_ovs_patch; + json_t *options = json_array (); + + s_ovs_iface = nm_connection_get_setting_ovs_interface (interface); + if (s_ovs_iface) + type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface); + + json_array_append (options, json_string ("map")); + s_ovs_patch = nm_connection_get_setting_ovs_patch (interface); + if (s_ovs_patch) { + json_array_append (options, json_pack ("[[s, s]]", + "peer", + nm_setting_ovs_patch_get_peer (s_ovs_patch))); + } else { + json_array_append (options, json_array ()); + } + + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:s, s:s, s:o, s:[s, [[s, s]]]}, s:s}", + "op", "insert", "table", "Interface", "row", + "name", nm_connection_get_interface_name (interface), + "type", type ? type : "", + "options", options, + "external_ids", "map", "NM.connection.uuid", nm_connection_get_uuid (interface), + "uuid-name", "rowInterface")); +} + +/** + * _insert_port: + * + * Returns an commands that adds new port from a given connection. + */ +static void +_insert_port (json_t *params, NMConnection *port, json_t *new_interfaces) +{ + NMSettingOvsPort *s_ovs_port; + const char *vlan_mode = NULL; + guint tag = 0; + const char *lacp = NULL; + const char *bond_mode = NULL; + guint bond_updelay = 0; + guint bond_downdelay = 0; + json_t *row; + + s_ovs_port = nm_connection_get_setting_ovs_port (port); + + row = json_object (); + + if (s_ovs_port) { + vlan_mode = nm_setting_ovs_port_get_vlan_mode (s_ovs_port); + tag = nm_setting_ovs_port_get_tag (s_ovs_port); + lacp = nm_setting_ovs_port_get_lacp (s_ovs_port); + bond_mode = nm_setting_ovs_port_get_bond_mode (s_ovs_port); + bond_updelay = nm_setting_ovs_port_get_bond_updelay (s_ovs_port); + bond_downdelay = nm_setting_ovs_port_get_bond_downdelay (s_ovs_port); + } + + if (vlan_mode) + json_object_set_new (row, "vlan_mode", json_string (vlan_mode)); + if (tag) + json_object_set_new (row, "tag", json_integer (tag)); + if (lacp) + json_object_set_new (row, "lacp", json_string (lacp)); + if (bond_mode) + json_object_set_new (row, "bond_mode", json_string (bond_mode)); + if (bond_updelay) + json_object_set_new (row, "bond_updelay", json_integer (bond_updelay)); + if (bond_downdelay) + json_object_set_new (row, "bond_downdelay", json_integer (bond_downdelay)); + + json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (port))); + json_object_set_new (row, "interfaces", json_pack ("[s, O]", "set", new_interfaces)); + json_object_set_new (row, "external_ids", + json_pack ("[s, [[s, s]]]", "map", + "NM.connection.uuid", nm_connection_get_uuid (port))); + + /* Create a new one. */ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Port", + "row", row, "uuid-name", "rowPort")); +} + +/** + * _insert_bridge: + * + * Returns an commands that adds new bridge from a given connection. + */ +static void +_insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports) +{ + NMSettingOvsBridge *s_ovs_bridge; + const char *fail_mode = NULL; + gboolean mcast_snooping_enable = FALSE; + gboolean rstp_enable = FALSE; + gboolean stp_enable = FALSE; + json_t *row; + + s_ovs_bridge = nm_connection_get_setting_ovs_bridge (bridge); + + row = json_object (); + + if (s_ovs_bridge) { + fail_mode = nm_setting_ovs_bridge_get_fail_mode (s_ovs_bridge); + mcast_snooping_enable = nm_setting_ovs_bridge_get_mcast_snooping_enable (s_ovs_bridge); + rstp_enable = nm_setting_ovs_bridge_get_rstp_enable (s_ovs_bridge); + stp_enable = nm_setting_ovs_bridge_get_stp_enable (s_ovs_bridge); + } + + if (fail_mode) + json_object_set_new (row, "fail_mode", json_string (fail_mode)); + if (mcast_snooping_enable) + json_object_set_new (row, "mcast_snooping_enable", json_boolean (mcast_snooping_enable)); + if (rstp_enable) + json_object_set_new (row, "rstp_enable", json_boolean (rstp_enable)); + if (stp_enable) + json_object_set_new (row, "stp_enable", json_boolean (stp_enable)); + + json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (bridge))); + json_object_set_new (row, "ports", json_pack ("[s, O]", "set", new_ports)); + json_object_set_new (row, "external_ids", + json_pack ("[s, [[s, s]]]", "map", + "NM.connection.uuid", nm_connection_get_uuid (bridge))); + + /* Create a new one. */ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Bridge", + "row", row, "uuid-name", "rowBridge")); +} + +/** + * _inc_next_cfg: + * + * Returns an mutate command that bumps next_cfg upon successful completion + * of the transaction it is in. + */ +static json_t * +_inc_next_cfg (const char *db_uuid) +{ + return json_pack ("{s:s, s:s, s:[[s, s, i]], s:[[s, s, [s, s]]]}", + "op", "mutate", "table", "Open_vSwitch", + "mutations", "next_cfg", "+=", 1, + "where", "_uuid", "==", "uuid", db_uuid); +} + +/** + * _add_interface: + * + * Adds an interface as specified by @interface connection, optionally creating + * a parent @port and @bridge if needed. + */ +static void +_add_interface (NMOvsdb *self, json_t *params, + NMConnection *bridge, NMConnection *port, NMConnection *interface) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GHashTableIter iter; + const char *bridge_uuid; + const char *port_uuid; + const char *interface_uuid; + OpenvswitchBridge *ovs_bridge = NULL; + OpenvswitchPort *ovs_port = NULL; + OpenvswitchInterface *ovs_interface = NULL; + int pi; + int ii; + json_t *bridges, *new_bridges; + json_t *ports, *new_ports; + json_t *interfaces, *new_interfaces; + gboolean has_interface = FALSE; + + bridges = json_array (); + ports = json_array (); + interfaces = json_array (); + new_bridges = json_array (); + new_ports = json_array (); + new_interfaces = json_array (); + + g_hash_table_iter_init (&iter, priv->bridges); + while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) { + json_array_append_new (bridges, json_pack ("[s, s]", "uuid", bridge_uuid)); + + if ( g_strcmp0 (ovs_bridge->name, nm_connection_get_interface_name (bridge)) != 0 + || g_strcmp0 (ovs_bridge->connection_uuid, nm_connection_get_uuid (bridge)) != 0) + continue; + + for (pi = 0; pi < ovs_bridge->ports->len; pi++) { + port_uuid = g_ptr_array_index (ovs_bridge->ports, pi); + ovs_port = g_hash_table_lookup (priv->ports, port_uuid); + + json_array_append_new (ports, json_pack ("[s, s]", "uuid", port_uuid)); + + if ( g_strcmp0 (ovs_port->name, nm_connection_get_interface_name (port)) != 0 + || g_strcmp0 (ovs_port->connection_uuid, nm_connection_get_uuid (port)) != 0) + continue; + + for (ii = 0; ii < ovs_port->interfaces->len; ii++) { + interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); + ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid); + + json_array_append_new (interfaces, json_pack ("[s, s]", "uuid", interface_uuid)); + + if ( g_strcmp0 (ovs_interface->name, nm_connection_get_interface_name (interface)) == 0 + && g_strcmp0 (ovs_interface->connection_uuid, nm_connection_get_uuid (interface)) == 0) + has_interface = TRUE; + } + + break; + } + + break; + } + + json_array_extend (new_bridges, bridges); + json_array_extend (new_ports, ports); + json_array_extend (new_interfaces, interfaces); + + if (json_array_size (interfaces) == 0) { + /* Need to create a port. */ + if (json_array_size (ports) == 0) { + /* Need to create a bridge. */ + _expect_ovs_bridges (params, priv->db_uuid, bridges); + json_array_append_new (new_bridges, json_pack ("[s, s]", "named-uuid", "rowBridge")); + _set_ovs_bridges (params, priv->db_uuid, new_bridges); + _insert_bridge (params, bridge, new_ports); + } else { + /* Bridge already exists. */ + g_return_if_fail (ovs_bridge); + _expect_bridge_ports (params, ovs_bridge->name, ports); + _set_bridge_ports (params, nm_connection_get_interface_name (bridge), new_ports); + } + + json_array_append_new (new_ports, json_pack ("[s, s]", "named-uuid", "rowPort")); + _insert_port (params, port, new_interfaces); + } else { + /* Port already exists */ + g_return_if_fail (ovs_port); + _expect_port_interfaces (params, ovs_port->name, interfaces); + _set_port_interfaces (params, nm_connection_get_interface_name (port), new_interfaces); + } + + if (!has_interface) { + _insert_interface (params, interface); + json_array_append_new (new_interfaces, json_pack ("[s, s]", "named-uuid", "rowInterface")); + } + + json_decref (interfaces); + json_decref (ports); + json_decref (bridges); + + json_decref (new_interfaces); + json_decref (new_ports); + json_decref (new_bridges); +} + +/** + * _delete_interface: + * + * Removes an interface of @ifname name, collecting empty ports and bridge + * if last item is removed from them. + */ +static void +_delete_interface (NMOvsdb *self, json_t *params, const char *ifname) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GHashTableIter iter; + char *bridge_uuid; + char *port_uuid; + char *interface_uuid; + OpenvswitchBridge *ovs_bridge; + OpenvswitchPort *ovs_port; + OpenvswitchInterface *ovs_interface; + int pi; + int ii; + json_t *bridges, *new_bridges; + json_t *ports, *new_ports; + json_t *interfaces, *new_interfaces; + gboolean bridges_changed; + gboolean ports_changed; + gboolean interfaces_changed; + + bridges = json_array (); + new_bridges = json_array (); + bridges_changed = FALSE; + + g_hash_table_iter_init (&iter, priv->bridges); + while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) { + json_array_append_new (bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + + ports = json_array (); + new_ports = json_array (); + ports_changed = FALSE; + + for (pi = 0; pi < ovs_bridge->ports->len; pi++) { + port_uuid = g_ptr_array_index (ovs_bridge->ports, pi); + ovs_port = g_hash_table_lookup (priv->ports, port_uuid); + + json_array_append_new (ports, json_pack ("[s,s]", "uuid", port_uuid)); + + interfaces = json_array (); + new_interfaces = json_array (); + interfaces_changed = FALSE; + + for (ii = 0; ii < ovs_port->interfaces->len; ii++) { + interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); + ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid); + + json_array_append_new (interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); + + if (strcmp (ovs_interface->name, ifname) == 0) { + /* skip the interface */ + interfaces_changed = TRUE; + continue; + } + + json_array_append_new (new_interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); + } + + if (json_array_size (new_interfaces) == 0) { + ports_changed = TRUE; + } else { + if (interfaces_changed) { + _expect_port_interfaces (params, ovs_port->name, interfaces); + _set_port_interfaces (params, ovs_port->name, new_interfaces); + } + json_array_append_new (new_ports, json_pack ("[s,s]", "uuid", port_uuid)); + } + + json_decref (interfaces); + json_decref (new_interfaces); + } + + if (json_array_size (new_ports) == 0) { + bridges_changed = TRUE; + } else { + if (ports_changed) { + _expect_bridge_ports (params, ovs_bridge->name, ports); + _set_bridge_ports (params, ovs_bridge->name, new_ports); + } + json_array_append_new (new_bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + } + + json_decref (ports); + json_decref (new_ports); + } + + if (bridges_changed) { + _expect_ovs_bridges (params, priv->db_uuid, bridges); + _set_ovs_bridges (params, priv->db_uuid, new_bridges); + } +} + +/** + * ovsdb_next_command: + * + * Translates a higher level operation (add/remove bridge/port) to a RFC 7047 + * command serialized into JSON ands sends it over to the database. + + * Only called when no command is waiting for a response, since the serialized + * command might depend on result of a previous one (add and remove need to + * include an up to date bridge list in their transactions to rule out races). + */ +static void +ovsdb_next_command (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call = NULL; + char *cmd; + json_t *msg = NULL; + json_t *params; + + if (!priv->conn) + return; + if (!priv->calls->len) + return; + call = &g_array_index (priv->calls, OvsdbMethodCall, 0); + if (call->id != COMMAND_PENDING) + return; + call->id = priv->seq++; + + switch (call->command) { + case OVSDB_MONITOR: + msg = json_pack ("{s:i, s:s, s:[s, n, {" + " s:[{s:[s, s, s]}]," + " s:[{s:[s, s, s]}]," + " s:[{s:[s, s, s]}]," + " s:[{s:[]}]" + "}]}", + "id", call->id, + "method", "monitor", "params", "Open_vSwitch", + "Bridge", "columns", "name", "ports", "external_ids", + "Port", "columns", "name", "interfaces", "external_ids", + "Interface", "columns", "name", "type", "external_ids", + "Open_vSwitch", "columns"); + break; + case OVSDB_ADD_INTERFACE: + params = json_array (); + json_array_append_new (params, json_string ("Open_vSwitch")); + json_array_append_new (params, _inc_next_cfg (priv->db_uuid)); + + _add_interface (self, params, call->bridge, call->port, call->interface); + + msg = json_pack ("{s:i, s:s, s:o}", + "id", call->id, + "method", "transact", "params", params); + break; + case OVSDB_DEL_INTERFACE: + params = json_array (); + json_array_append_new (params, json_string ("Open_vSwitch")); + json_array_append_new (params, _inc_next_cfg (priv->db_uuid)); + + _delete_interface (self, params, call->ifname); + + msg = json_pack ("{s:i, s:s, s:o}", + "id", call->id, + "method", "transact", "params", params); + break; + } + + g_return_if_fail (msg); + _call_trace ("send", call, msg); + cmd = json_dumps (msg, 0); + + g_string_append (priv->output, cmd); + json_decref (msg); + free (cmd); + + ovsdb_write (self); +} + +/** + * _uuids_to_array: + * + * This tidies up the somewhat non-straightforward way ovsdb represents an array + * of UUID elements. The single element is a tuple (called <atom> in RFC7047), + * + * [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ] + * + * while the list of multiple UUIDs are turned into a set of such tuples ("atoms"): + * + * [ "set", [ [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ], + * [ "uuid", "185c93f6-0b39-424e-8587-77d074aa7ce0" ], ... ] ] + */ +static void +_uuids_to_array (GPtrArray *array, const json_t *items) +{ + const char *key; + json_t *value; + size_t index = 0; + json_t *set_value; + size_t set_index; + + while (index < json_array_size (items)) { + key = json_string_value (json_array_get (items, index)); + index++; + value = json_array_get (items, index); + index++; + + if (!value) + return; + + if (g_strcmp0 (key, "uuid") == 0 && json_is_string (value)) { + g_ptr_array_add (array, g_strdup (json_string_value (value))); + } else if (g_strcmp0 (key, "set") == 0 && json_is_array (value)) { + json_array_foreach (value, set_index, set_value) { + _uuids_to_array (array, set_value); + } + } + } +} + +static char * +_connection_uuid_from_external_ids (json_t *external_ids) +{ + json_t *value; + size_t index; + + if (g_strcmp0 ("map", json_string_value (json_array_get (external_ids, 0))) != 0) + return NULL; + + json_array_foreach (json_array_get (external_ids, 1), index, value) { + if (g_strcmp0 ("NM.connection.uuid", json_string_value (json_array_get (value, 0))) == 0) + return g_strdup (json_string_value (json_array_get (value, 1))); + } + + return NULL; +} + +/** + * ovsdb_got_update: + * + * Called when we've got an "update" method call (we asked for it with the monitor + * command). We use it to maintain a consistent view of bridge list regardless of + * whether the changes are done by us or externally. + */ +static void +ovsdb_got_update (NMOvsdb *self, json_t *msg) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_t *ovs = NULL; + json_t *bridge = NULL; + json_t *port = NULL; + json_t *interface = NULL; + json_t *items; + json_t *external_ids; + json_error_t json_error = { 0, }; + void *iter; + const char *name; + const char *key; + const char *type; + json_t *value; + OpenvswitchBridge *ovs_bridge; + OpenvswitchPort *ovs_port; + OpenvswitchInterface *ovs_interface; + + if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:o, s?:o, s?:o}", + "Open_vSwitch", &ovs, + "Bridge", &bridge, + "Port", &port, + "Interface", &interface) == -1) { + /* This doesn't really have to be an error; the key might + * be missing if there really are no bridges present. */ + _LOGD ("Bad update: %s", json_error.text); + } + + if (ovs) { + iter = json_object_iter (ovs); + priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL); + } + + /* Interfaces */ + json_object_foreach (interface, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:s, s:o}}", "new", + "name", &name, + "type", &type, + "external_ids", &external_ids) == 0) + new = TRUE; + + if (old) { + ovs_interface = g_hash_table_lookup (priv->interfaces, key); + if (!new || g_strcmp0 (ovs_interface->name, name) != 0) { + old = FALSE; + _LOGT ("removed an '%s' interface: %s%s%s", + ovs_interface->type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + if (g_strcmp0 (ovs_interface->type, "internal") == 0) { + /* Currently the factory only creates NMDevices for + * internal interfaces. Ignore the rest. */ + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); + } + } + g_hash_table_remove (priv->interfaces, key); + } + + if (new) { + ovs_interface = g_slice_new (OpenvswitchInterface); + ovs_interface->name = g_strdup (name); + ovs_interface->type = g_strdup (type); + ovs_interface->connection_uuid = _connection_uuid_from_external_ids (external_ids); + if (old) { + _LOGT ("changed an '%s' interface: %s%s%s", type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + "ovs-interface", ovs_interface->name); + } else { + _LOGT ("added an '%s' interface: %s%s%s", + ovs_interface->type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + if (g_strcmp0 (ovs_interface->type, "internal") == 0) { + /* Currently the factory only creates NMDevices for + * internal interfaces. Ignore the rest. */ + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); + } + } + g_hash_table_insert (priv->interfaces, g_strdup (key), ovs_interface); + } + } + + /* Ports */ + json_object_foreach (port, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new", + "name", &name, + "external_ids", &external_ids, + "interfaces", &items) == 0) + new = TRUE; + + if (old) { + ovs_port = g_hash_table_lookup (priv->ports, key); + if (!new || g_strcmp0 (ovs_port->name, name) != 0) { + old = FALSE; + _LOGT ("removed a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + } + g_hash_table_remove (priv->ports, key); + } + + if (new) { + ovs_port = g_slice_new (OpenvswitchPort); + ovs_port->name = g_strdup (name); + ovs_port->connection_uuid = _connection_uuid_from_external_ids (external_ids); + ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free); + _uuids_to_array (ovs_port->interfaces, items); + if (old) { + _LOGT ("changed a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + NM_SETTING_OVS_PORT_SETTING_NAME, ovs_port->name); + } else { + _LOGT ("added a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + } + g_hash_table_insert (priv->ports, g_strdup (key), ovs_port); + } + } + + /* Bridges */ + json_object_foreach (bridge, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new", + "name", &name, + "external_ids", &external_ids, + "ports", &items) == 0) + new = TRUE; + + if (old) { + ovs_bridge = g_hash_table_lookup (priv->bridges, key); + if (!new || g_strcmp0 (ovs_bridge->name, name) != 0) { + old = FALSE; + _LOGT ("removed a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + } + g_hash_table_remove (priv->bridges, key); + } + + if (new) { + ovs_bridge = g_slice_new (OpenvswitchBridge); + ovs_bridge->name = g_strdup (name); + ovs_bridge->connection_uuid = _connection_uuid_from_external_ids (external_ids); + ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free); + _uuids_to_array (ovs_bridge->ports, items); + if (old) { + _LOGT ("changed a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + NM_SETTING_OVS_BRIDGE_SETTING_NAME, ovs_bridge->name); + } else { + _LOGT ("added a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + } + g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge); + } + } + +} + +/** + * ovsdb_got_echo: + * + * Only implemented because the specification mandates it. Actual ovsdb hasn't been + * seen doing this. + */ +static void +ovsdb_got_echo (NMOvsdb *self, json_int_t id, json_t *data) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_t *msg; + char *reply; + gboolean output_was_empty; + + output_was_empty = priv->output->len == 0; + + msg = json_pack ("{s:I, s:O}", "id", id, "result", data); + reply = json_dumps (msg, 0); + g_string_append (priv->output, reply); + json_decref (msg); + free (reply); + + if (output_was_empty) + ovsdb_write (self); +} + +/** + * ovsdb_got_msg:: + * + * Called when when a complete JSON object was seen and unmarshalled. + * Either finishes a method call or processes a method call. + */ +static void +ovsdb_got_msg (NMOvsdb *self, json_t *msg) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_error_t json_error = { 0, }; + json_t *json_id = NULL; + gint64 id = -1; + const char *method = NULL; + json_t *params = NULL; + json_t *result = NULL; + json_t *error = NULL; + OvsdbMethodCall *call = NULL; + OvsdbMethodCallback callback; + gpointer user_data; + GError *local = NULL; + + if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:s, s?:o, s?:o, s?:o}", + "id", &json_id, + "method", &method, + "params", ¶ms, + "result", &result, + "error", &error) == -1) { + _LOGW ("couldn't grok the message: %s", json_error.text); + ovsdb_disconnect (self); + return; + } + + if (json_is_number (json_id)) + id = json_integer_value (json_id); + + if (method) { + /* It's a method call! */ + if (!params) { + _LOGW ("a method call with no params: '%s'", method); + ovsdb_disconnect (self); + return; + } + + if (g_strcmp0 (method, "update") == 0) { + /* This is a update method call. */ + ovsdb_got_update (self, json_array_get (params, 1)); + } else if (g_strcmp0 (method, "echo") == 0) { + /* This is an echo request. */ + ovsdb_got_echo (self, id, params); + } else { + _LOGW ("got an unknown method call: '%s'", method); + } + return; + } + + if (id > -1) { + /* This is a response to a method call. */ + if (!priv->calls->len) { + _LOGE ("there are no queued calls expecting response %" G_GUINT64_FORMAT, id); + ovsdb_disconnect (self); + return; + } + call = &g_array_index (priv->calls, OvsdbMethodCall, 0); + if (call->id != id) { + _LOGE ("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT, call->id, id); + ovsdb_disconnect (self); + return; + } + /* Cool, we found a corresponsing call. Finish it. */ + + _call_trace ("response", call, msg); + + if (!json_is_null (error)) { + /* The response contains an error. */ + g_set_error (&local, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error call to OVSDB returned an error: %s", + json_string_value (error)); + } + + callback = call->callback; + user_data = call->user_data; + g_array_remove_index (priv->calls, 0); + callback (self, result, local, user_data); + + /* Don't progress further commands in case the callback hit an error + * and disconnected us. */ + if (!priv->conn) + return; + + /* Now we're free to serialize and send the next command, if any. */ + ovsdb_next_command (self); + + return; + } + + + /* This is a message we are not interested in. */ + _LOGW ("got an unknown message, ignoring"); +} + +/*****************************************************************************/ + +/* Lower level marshalling and demarshalling of the JSON-RPC traffic on the + * ovsdb socket. */ + +static size_t +_json_callback (void *buffer, size_t buflen, void *user_data) +{ + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + if (priv->bufp == priv->input->len) { + /* No more bytes buffered for decoding. */ + return 0; + } + + /* Pass one more byte to the JSON decoder. */ + *(char *)buffer = priv->input->str[priv->bufp]; + priv->bufp++; + + return (size_t)1; +} + +/** + * ovsdb_read_cb: + * + * Read out the data available from the ovsdb socket and try to deserialize + * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg(). + */ +static void +ovsdb_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GInputStream *stream = G_INPUT_STREAM (source_object); + GError *error = NULL; + gssize size; + json_t *msg; + json_error_t json_error = { 0, }; + + size = g_input_stream_read_finish (stream, res, &error); + if (size == -1) { + _LOGW ("short read from ovsdb: %s", error->message); + g_clear_error (&error); + ovsdb_disconnect (self); + return; + } + + g_string_append_len (priv->input, priv->buf, size); + do { + priv->bufp = 0; + /* The callback always eats up only up to a single byte. This makes + * it possible for us to identify complete JSON objects in spite of + * us not knowing the length in advance. */ + msg = json_load_callback (_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error); + if (msg) { + ovsdb_got_msg (self, msg); + g_string_erase (priv->input, 0, priv->bufp); + } + json_decref (msg); + } while (msg); + + if (!priv->conn) + return; + + if (size) + ovsdb_read (self); +} + +static void +ovsdb_read (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + g_input_stream_read_async (g_io_stream_get_input_stream (G_IO_STREAM (priv->conn)), + priv->buf, sizeof(priv->buf), + G_PRIORITY_DEFAULT, NULL, ovsdb_read_cb, self); +} + +static void +ovsdb_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GOutputStream *stream = G_OUTPUT_STREAM (source_object); + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GError *error = NULL; + gssize size; + + size = g_output_stream_write_finish (stream, res, &error); + if (size == -1) { + _LOGW ("short write to ovsdb: %s", error->message); + g_clear_error (&error); + ovsdb_disconnect (self); + return; + } + + if (!priv->conn) + return; + + g_string_erase (priv->output, 0, size); + + ovsdb_write (self); +} + +static void +ovsdb_write (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GOutputStream *stream; + + if (!priv->output->len) + return; + + stream = g_io_stream_get_output_stream (G_IO_STREAM (priv->conn)); + if (g_output_stream_has_pending (stream)) + return; + + g_output_stream_write_async (stream, + priv->output->str, priv->output->len, + G_PRIORITY_DEFAULT, NULL, ovsdb_write_cb, self); +} + +/*****************************************************************************/ + +/* Routines to maintain the ovsdb connection. */ + +/** + * ovsdb_disconnect: + * + * Clean up the internal state to the point equivalent to before connecting. + * Apart from clean shutdown this is a good response to unexpected trouble, + * since the next method call attempt a will trigger reconnect which hopefully + * puts us back in sync. + */ +static void +ovsdb_disconnect (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call; + OvsdbMethodCallback callback; + gpointer user_data; + GError *error; + + _LOGD ("disconnecting from ovsdb"); + + while (priv->calls->len) { + error = NULL; + call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1); + g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + + callback = call->callback; + user_data = call->user_data; + g_array_remove_index (priv->calls, priv->calls->len - 1); + callback (self, NULL, error, user_data); + } + + priv->bufp = 0; + g_string_truncate (priv->input, 0); + g_string_truncate (priv->output, 0); + g_clear_object (&priv->client); + g_clear_object (&priv->conn); + g_clear_pointer (&priv->db_uuid, g_free); +} + +static void +_monitor_bridges_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data) +{ + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + _LOGI ("%s", error->message); + ovsdb_disconnect (self); + } + + g_clear_error (&error); + return; + } + + /* Treat the first response the same as the subsequent "update" + * messages we eventually get. */ + ovsdb_got_update (self, result); +} + +static void +_client_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GSocketClient *client = G_SOCKET_CLIENT (source_object); + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv; + GError *error = NULL; + GSocketConnection *conn; + + conn = g_socket_client_connect_finish (client, res, &error); + if (conn == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOGI ("%s", error->message); + + ovsdb_disconnect (self); + g_clear_error (&error); + return; + } + + priv = NM_OVSDB_GET_PRIVATE (self); + priv->conn = conn; + g_clear_object (&priv->cancellable); + + ovsdb_read (self); + ovsdb_next_command (self); +} + +/** + * ovsdb_try_connect: + * + * Establish a connection to ovsdb unless it's already established or being + * established. Queues a monitor command as a very first one so that we're in + * sync when other commands are issued. + */ +static void +ovsdb_try_connect (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GSocketAddress *addr; + + if (priv->client) + return; + + /* XXX: This should probably be made configurable via NetworkManager.conf */ + addr = g_unix_socket_address_new (RUNSTATEDIR "/openvswitch/db.sock"); + + priv->client = g_socket_client_new (); + priv->cancellable = g_cancellable_new (); + g_socket_client_connect_async (priv->client, G_SOCKET_CONNECTABLE (addr), + priv->cancellable, _client_connect_cb, self); + g_object_unref (addr); + + /* Queue a monitor call before any other command, ensuring that we have an up + * to date view of existing bridged that we need for add and remove ops. */ + ovsdb_call_method (self, OVSDB_MONITOR, NULL, + NULL, NULL, NULL, _monitor_bridges_cb, NULL); +} + +/*****************************************************************************/ + +/* Public functions useful for NMDeviceOpenvswitch to maintain the life cycle of + * their ovsdb entries without having to deal with ovsdb complexities themselves. */ + +typedef struct { + NMOvsdbCallback callback; + gpointer user_data; +} OvsdbCall; + +static void +_transact_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data) +{ + OvsdbCall *call = user_data; + const char *err; + const char *err_details; + size_t index; + json_t *value; + + if (error) + goto out; + + json_array_foreach (result, index, value) { + if (json_unpack (value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error running the transaction: %s: %s", err, err_details); + goto out; + } + } + +out: + call->callback (error, call->user_data); + g_slice_free (OvsdbCall, call); +} + +void +nm_ovsdb_add_interface (NMOvsdb *self, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + NMOvsdbCallback callback, gpointer user_data) +{ + OvsdbCall *call; + + call = g_slice_new (OvsdbCall); + call->callback = callback; + call->user_data = user_data; + + ovsdb_call_method (self, OVSDB_ADD_INTERFACE, NULL, + bridge, port, interface, _transact_cb, call); +} + +void +nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname, + NMOvsdbCallback callback, gpointer user_data) +{ + OvsdbCall *call; + + call = g_slice_new (OvsdbCall); + call->callback = callback; + call->user_data = user_data; + + ovsdb_call_method (self, OVSDB_DEL_INTERFACE, ifname, + NULL, NULL, NULL, _transact_cb, call); +} + +/*****************************************************************************/ + +static void +_clear_call (gpointer data) +{ + OvsdbMethodCall *call = data; + + switch (call->command) { + case OVSDB_MONITOR: + break; + case OVSDB_ADD_INTERFACE: + g_clear_object (&call->bridge); + g_clear_object (&call->port); + g_clear_object (&call->interface); + break; + case OVSDB_DEL_INTERFACE: + g_clear_pointer (&call->ifname, g_free); + break; + } +} + +static void +_free_bridge (gpointer data) +{ + OpenvswitchBridge *ovs_bridge = data; + + g_free (ovs_bridge->name); + g_free (ovs_bridge->connection_uuid); + g_ptr_array_free (ovs_bridge->ports, TRUE); + g_slice_free (OpenvswitchBridge, ovs_bridge); +} + +static void +_free_port (gpointer data) +{ + OpenvswitchPort *ovs_port = data; + + g_free (ovs_port->name); + g_free (ovs_port->connection_uuid); + g_ptr_array_free (ovs_port->interfaces, TRUE); + g_slice_free (OpenvswitchPort, ovs_port); +} + +static void +_free_interface (gpointer data) +{ + OpenvswitchInterface *ovs_interface = data; + + g_free (ovs_interface->name); + g_free (ovs_interface->connection_uuid); + g_free (ovs_interface->type); + g_slice_free (OpenvswitchInterface, ovs_interface); +} + +static void +nm_ovsdb_init (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + priv->calls = g_array_new (FALSE, TRUE, sizeof (OvsdbMethodCall)); + g_array_set_clear_func (priv->calls, _clear_call); + priv->input = g_string_new (NULL); + priv->output = g_string_new (NULL); + priv->bridges = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_bridge); + priv->ports = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_port); + priv->interfaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_interface); + + ovsdb_try_connect (self); +} + +static void +dispose (GObject *object) +{ + NMOvsdb *self = NM_OVSDB (object); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + ovsdb_disconnect (self); + + g_string_free (priv->input, TRUE); + priv->input = NULL; + g_string_free (priv->output, TRUE); + priv->output = NULL; + + if (priv->calls) { + g_array_free (priv->calls, TRUE); + priv->calls = NULL; + } + + g_clear_pointer (&priv->bridges, g_hash_table_destroy); + g_clear_pointer (&priv->ports, g_hash_table_destroy); + g_clear_pointer (&priv->interfaces, g_hash_table_destroy); + + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + + G_OBJECT_CLASS (nm_ovsdb_parent_class)->dispose (object); +} + +static void +nm_ovsdb_class_init (NMOvsdbClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; + + signals[DEVICE_ADDED] = + g_signal_new (NM_OVSDB_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + + signals[DEVICE_REMOVED] = + g_signal_new (NM_OVSDB_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + + signals[DEVICE_CHANGED] = + g_signal_new (NM_OVSDB_DEVICE_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); +} diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h new file mode 100644 index 00000000..cf9fe2a2 --- /dev/null +++ b/src/devices/ovs/nm-ovsdb.h @@ -0,0 +1,50 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_OVSDB_H__ +#define __NETWORKMANAGER_OVSDB_H__ + +#define NM_TYPE_OVSDB (nm_ovsdb_get_type ()) +#define NM_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVSDB, NMOvsdb)) +#define NM_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVSDB, NMOvsdbClass)) +#define NM_IS_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVSDB)) +#define NM_IS_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVSDB)) +#define NM_OVSDB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVSDB, NMOvsdbClass)) + +#define NM_OVSDB_DEVICE_ADDED "device-added" +#define NM_OVSDB_DEVICE_REMOVED "device-removed" +#define NM_OVSDB_DEVICE_CHANGED "device-changed" + +typedef struct _NMOvsdb NMOvsdb; +typedef struct _NMOvsdbClass NMOvsdbClass; + +typedef void (*NMOvsdbCallback) (GError *error, gpointer user_data); + +NMOvsdb *nm_ovsdb_get (void); + +GType nm_ovsdb_get_type (void); + +void nm_ovsdb_add_interface (NMOvsdb *self, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + NMOvsdbCallback callback, gpointer user_data); + +void nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname, + NMOvsdbCallback callback, gpointer user_data); + +#endif /* __NETWORKMANAGER_OVSDB_H__ */ diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 1c4d2ef6..098cd437 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -56,6 +56,8 @@ typedef struct { guint teamd_read_timeout; guint teamd_dbus_watch; char *config; + gboolean kill_in_progress; + NMConnection *connection; } NMDeviceTeamPrivate; struct _NMDeviceTeam { @@ -84,24 +86,6 @@ get_generic_capabilities (NMDevice *device) } static gboolean -is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) -{ - return TRUE; -} - -static gboolean -check_connection_available (NMDevice *device, - NMConnection *connection, - NMDeviceCheckConAvailableFlags flags, - const char *specific_object) -{ - /* Connections are always available because the carrier state is determined - * by the team port carrier states, not the team's state. - */ - return TRUE; -} - -static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { NMSettingTeam *s_team; @@ -306,6 +290,26 @@ master_update_slave_connection (NMDevice *self, } /*****************************************************************************/ +static void +teamd_kill_cb (pid_t pid, gboolean success, int child_status, void *user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceTeam *self = (NMDeviceTeam *) device; + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self); + + priv->kill_in_progress = FALSE; + + if (priv->connection) { + _LOGT (LOGD_TEAM, "kill terminated, starting teamd..."); + if (!teamd_start (device, priv->connection)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); + } + g_clear_object (&priv->connection); + } + g_object_unref (device); +} static void teamd_cleanup (NMDevice *device, gboolean free_tdc) @@ -317,7 +321,12 @@ teamd_cleanup (NMDevice *device, gboolean free_tdc) nm_clear_g_source (&priv->teamd_read_timeout); if (priv->teamd_pid > 0) { - nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL); + priv->kill_in_progress = TRUE; + nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, + LOGD_TEAM, "teamd", + 2000, + teamd_kill_cb, + g_object_ref (device)); priv->teamd_pid = 0; } @@ -340,7 +349,7 @@ teamd_timeout_cb (gpointer user_data) if (priv->teamd_pid && !priv->tdc) { /* Timed out launching our own teamd process */ - _LOGW (LOGD_TEAM, "teamd timed out."); + _LOGW (LOGD_TEAM, "teamd timed out"); teamd_cleanup (device, TRUE); g_warn_if_fail (nm_device_is_activating (device)); @@ -568,7 +577,7 @@ teamd_start (NMDevice *device, NMConnection *connection) /* Inject the hwaddr property into the JSON configuration. * While doing so, detect potential conflicts */ - json = json_loads (config ?: "{}", 0, &jerror); + json = json_loads (config ?: "{}", JSON_REJECT_DUPLICATES, &jerror); g_return_val_if_fail (json, FALSE); hwaddr = json_object_get (json, "hwaddr"); @@ -663,6 +672,12 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) teamd_cleanup (device, TRUE); } + if (priv->kill_in_progress) { + _LOGT (LOGD_TEAM, "kill in progress, wait before starting teamd"); + priv->connection = g_object_ref (connection); + return NM_ACT_STAGE_RETURN_POSTPONE; + } + return teamd_start (device, connection) ? NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE; } @@ -679,6 +694,7 @@ deactivate (NMDevice *device) if (!priv->teamd_pid) teamd_kill (self, NULL, NULL); teamd_cleanup (device, TRUE); + g_clear_object (&priv->connection); } static gboolean @@ -792,7 +808,7 @@ create_and_realize (NMDevice *device, "Failed to create team master interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -822,6 +838,7 @@ get_property (GObject *object, guint prop_id, static void nm_device_team_init (NMDeviceTeam * self) { + nm_assert (nm_device_is_master (NM_DEVICE (self))); } static void @@ -854,7 +871,6 @@ nm_device_team_new (const char *iface) NM_DEVICE_TYPE_DESC, "Team", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TEAM, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_TEAM, - NM_DEVICE_IS_MASTER, TRUE, NULL); } @@ -887,11 +903,10 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) object_class->dispose = dispose; object_class->get_property = get_property; + parent_class->is_master = TRUE; parent_class->create_and_realize = create_and_realize; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->is_available = is_available; parent_class->check_connection_compatible = check_connection_compatible; - parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; parent_class->update_connection = update_connection; parent_class->master_update_slave_connection = master_update_slave_connection; diff --git a/src/devices/tests/test-arping.c b/src/devices/tests/test-arping.c index 59223f11..4b4642f3 100644 --- a/src/devices/tests/test-arping.c +++ b/src/devices/tests/test-arping.c @@ -40,9 +40,8 @@ static void fixture_setup (test_fixture *fixture, gconstpointer user_data) { /* create veth pair. */ - nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); - fixture->ifindex0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; - fixture->ifindex1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex; + fixture->ifindex0 = nmtstp_link_veth_add (NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; + fixture->ifindex1 = nmtstp_link_get_typed (NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex; g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); @@ -87,7 +86,7 @@ test_arping_common (test_fixture *fixture, TestInfo *info) g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED, G_CALLBACK (arping_manager_probe_terminated), loop); g_assert (nm_arping_manager_start_probe (manager, 100, NULL)); - g_assert (nmtst_main_loop_run (loop, 1000)); + g_assert (nmtst_main_loop_run (loop, 2000)); for (i = 0; info->addresses[i]; i++) { g_assert_cmpint (nm_arping_manager_check_address (manager, info->addresses[i]), diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 24811931..ac78757d 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -312,13 +312,13 @@ companion_state_changed_cb (NMDeviceWifi *companion, } static gboolean -companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +companion_scan_prohibited_cb (NMDeviceWifi *companion, gpointer user_data) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); /* Don't allow the companion to scan while configuring the mesh interface */ - return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); + return (state >= NM_DEVICE_STATE_PREPARE) && (state <= NM_DEVICE_STATE_IP_CONFIG); } static gboolean @@ -358,8 +358,8 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other) g_signal_connect (G_OBJECT (other), "notify::" NM_DEVICE_WIFI_SCANNING, G_CALLBACK (companion_notify_cb), self); - g_signal_connect (G_OBJECT (other), NM_DEVICE_WIFI_SCANNING_ALLOWED, - G_CALLBACK (companion_scan_allowed_cb), self); + g_signal_connect (G_OBJECT (other), NM_DEVICE_WIFI_SCANNING_PROHIBITED, + G_CALLBACK (companion_scan_prohibited_cb), self); g_signal_connect (G_OBJECT (other), NM_DEVICE_AUTOCONNECT_ALLOWED, G_CALLBACK (companion_autoconnect_allowed_cb), self); @@ -425,6 +425,13 @@ state_changed (NMDevice *device, find_companion (NM_DEVICE_OLPC_MESH (device)); } +static guint32 +get_dhcp_timeout (NMDevice *device, int addr_family) +{ + /* shorter timeout for mesh connectivity */ + return 20; +} + /*****************************************************************************/ static void @@ -465,11 +472,8 @@ constructed (GObject *object) priv->manager = g_object_ref (nm_manager_get ()); - g_signal_connect (priv->manager, "device-added", G_CALLBACK (device_added_cb), self); - g_signal_connect (priv->manager, "device-removed", G_CALLBACK (device_removed_cb), self); - - /* shorter timeout for mesh connectivity */ - nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); + g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added_cb), self); + g_signal_connect (priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK (device_removed_cb), self); } NMDevice * @@ -519,6 +523,7 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->act_stage2_config = act_stage2_config; parent_class->state_changed = state_changed; + parent_class->get_dhcp_timeout = get_dhcp_timeout; obj_properties[PROP_COMPANION] = g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, "", "", diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 20692ed9..8bfddbd9 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2012 Red Hat, Inc. + * Copyright (C) 2005 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -63,8 +63,6 @@ _LOG_DECLARE_SELF(NMDeviceWifi); #define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5 -static NM_CACHED_QUARK_FCN ("wireless-secrets-tries", wireless_secrets_tries_quark) - /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, @@ -79,7 +77,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, enum { ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED, - SCANNING_ALLOWED, + SCANNING_PROHIBITED, LAST_SIGNAL }; @@ -119,6 +117,8 @@ typedef struct { NMDeviceWifiCapabilities capabilities; gint32 hw_addr_scan_expire; + + guint wps_timeout_id; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -132,7 +132,7 @@ struct _NMDeviceWifiClass NMDeviceClass parent; /* Signals */ - gboolean (*scanning_allowed) (NMDeviceWifi *device); + gboolean (*scanning_prohibited) (NMDeviceWifi *device, gboolean periodic); }; /*****************************************************************************/ @@ -143,7 +143,7 @@ G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) /*****************************************************************************/ -static gboolean check_scanning_allowed (NMDeviceWifi *self); +static gboolean check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic); static void schedule_scan (NMDeviceWifi *self, gboolean backoff); @@ -169,6 +169,10 @@ static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, gboolean success, NMDeviceWifi * self); +static void supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self); + static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, GParamSpec * pspec, NMDeviceWifi * self); @@ -177,7 +181,10 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); -static void request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options); +static void request_wireless_scan (NMDeviceWifi *self, + gboolean periodic, + gboolean force_if_scanning, + const GPtrArray *ssids); static void ap_add_remove (NMDeviceWifi *self, guint signum, @@ -268,6 +275,10 @@ supplicant_interface_acquire (NMDeviceWifi *self) G_CALLBACK (supplicant_iface_scan_done_cb), self); g_signal_connect (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS, + G_CALLBACK (supplicant_iface_wps_credentials_cb), + self); + g_signal_connect (priv->sup_iface, "notify::"NM_SUPPLICANT_INTERFACE_SCANNING, G_CALLBACK (supplicant_iface_notify_scanning_cb), self); @@ -559,7 +570,7 @@ deactivate (NMDevice *device) /* Ensure we trigger a scan after deactivating a Hotspot */ if (old_mode == NM_802_11_MODE_AP) - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, FALSE, FALSE, NULL); } static void @@ -781,15 +792,18 @@ complete_connection (NMDevice *device, NMSettingWireless *s_wifi; const char *setting_mac; char *str_ssid = NULL; - NMWifiAP *ap = NULL; + NMWifiAP *ap; const GByteArray *ssid = NULL; GByteArray *tmp_ssid = NULL; GBytes *setting_ssid = NULL; gboolean hidden = FALSE; const char *perm_hw_addr; + const char *mode; s_wifi = nm_connection_get_setting_wireless (connection); + mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; + if (!specific_object) { /* If not given a specific object, we need at minimum an SSID */ if (!s_wifi) { @@ -809,19 +823,29 @@ complete_connection (NMDevice *device, return FALSE; } - /* Find a compatible AP in the scan list */ - ap = find_first_compatible_ap (self, connection, FALSE); + if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + /* Find a compatible AP in the scan list */ + ap = find_first_compatible_ap (self, connection, FALSE); - /* If we still don't have an AP, then the WiFI settings needs to be - * fully specified by the client. Might not be able to find an AP - * if the network isn't broadcasting the SSID for example. - */ - if (!ap) { + /* If we still don't have an AP, then the WiFI settings needs to be + * fully specified by the client. Might not be able to find an AP + * if the network isn't broadcasting the SSID for example. + */ + if (!ap) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + + hidden = TRUE; + } + } else { if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) return FALSE; - - hidden = TRUE; + ap = NULL; } + } else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + ap = NULL; } else { ap = get_ap_by_path (self, specific_object); if (!ap) { @@ -1169,23 +1193,65 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) } } +static GPtrArray * +ssids_options_to_ptrarray (GVariant *value, GError **error) +{ + GPtrArray *ssids = NULL; + GByteArray *ssid_array; + GVariant *v; + const guint8 *bytes; + gsize len; + int num_ssids, i; + + num_ssids = g_variant_n_children (value); + if (num_ssids > 32) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "too many SSIDs requested to scan"); + return NULL; + } + + if (num_ssids) { + ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref); + for (i = 0; i < num_ssids; i++) { + v = g_variant_get_child_value (value, i); + bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8)); + if (len > 32) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "SSID at index %d more than 32 bytes", i); + g_ptr_array_unref (ssids); + return NULL; + } + + ssid_array = g_byte_array_new (); + g_byte_array_append (ssid_array, bytes, len); + g_ptr_array_add (ssids, ssid_array); + } + } + return ssids; +} + static void -request_scan_cb (NMDevice *device, - GDBusMethodInvocation *context, - NMAuthSubject *subject, - GError *error, - gpointer user_data) +dbus_request_scan_cb (NMDevice *device, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + GError *error, + gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); NMDeviceWifiPrivate *priv; - gs_unref_variant GVariant *new_scan_options = user_data; + gs_unref_variant GVariant *scan_options = user_data; + gs_unref_ptrarray GPtrArray *ssids = NULL; if (error) { g_dbus_method_invocation_return_gerror (context, error); return; } - if (!check_scanning_allowed (self)) { + if (check_scanning_prohibited (self, FALSE)) { g_dbus_method_invocation_return_error_literal (context, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1195,7 +1261,29 @@ request_scan_cb (NMDevice *device, priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - request_wireless_scan (self, FALSE, new_scan_options); + if (scan_options) { + gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); + + if (val) { + gs_free_error GError *ssid_error = NULL; + + if (!g_variant_is_of_type (val, G_VARIANT_TYPE ("aay"))) { + g_dbus_method_invocation_return_error_literal (context, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "Invalid 'ssid' scan option"); + return; + } + + ssids = ssids_options_to_ptrarray (val, &ssid_error); + if (ssid_error) { + g_dbus_method_invocation_return_gerror (context, ssid_error); + return; + } + } + } + + request_wireless_scan (self, FALSE, FALSE, ssids); g_dbus_method_invocation_return_value (context, NULL); } @@ -1243,22 +1331,23 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, NULL, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, - request_scan_cb, + dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); } static gboolean -scanning_allowed (NMDeviceWifi *self) +scanning_prohibited (NMDeviceWifi *self, gboolean periodic) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSupplicantInterfaceState supplicant_state; - NMConnection *connection; - g_return_val_if_fail (priv->sup_iface != NULL, FALSE); + g_return_val_if_fail (priv->sup_iface != NULL, TRUE); - /* Scanning not done in AP mode */ - if (priv->mode == NM_802_11_MODE_AP) - return FALSE; + /* Don't scan when a an AP or Ad-Hoc connection is active as it will + * disrupt connected clients or peers. + */ + if (priv->mode == NM_802_11_MODE_ADHOC || priv->mode == NM_802_11_MODE_AP) + return TRUE; switch (nm_device_get_state (NM_DEVICE (self))) { case NM_DEVICE_STATE_UNKNOWN: @@ -1271,78 +1360,42 @@ scanning_allowed (NMDeviceWifi *self) case NM_DEVICE_STATE_IP_CHECK: case NM_DEVICE_STATE_SECONDARIES: case NM_DEVICE_STATE_DEACTIVATING: - /* Don't scan when unusable or activating */ - return FALSE; + /* Prohibit scans when unusable or activating */ + return TRUE; case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: /* Can always scan when disconnected */ - return TRUE; + return FALSE; case NM_DEVICE_STATE_ACTIVATED: - /* Need to do further checks when activated */ + /* Prohibit periodic scans when connected; we ask the supplicant to + * background scan for us, unless the connection is locked to a specifc + * BSSID. + */ + if (periodic) + return TRUE; break; } - /* Don't scan if the supplicant is busy */ + /* Prohibit scans if the supplicant is busy */ supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface); if ( supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE || nm_supplicant_interface_get_scanning (priv->sup_iface)) - return FALSE; - - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - if (connection) { - NMSettingWireless *s_wifi; - const char *ip4_method = NULL; - - /* Don't scan when a shared connection is active; it makes drivers mad */ - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (!strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) - return FALSE; - - /* Don't scan when the connection is locked to a specifc AP, since - * intra-ESS roaming (which requires periodic scanning) isn't being - * used due to the specific AP lock. (bgo #513820) - */ - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - if (nm_setting_wireless_get_bssid (s_wifi)) - return FALSE; - } - - return TRUE; -} + return TRUE; -static gboolean -scanning_allowed_accumulator (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, - gpointer data) -{ - if (!g_value_get_boolean (handler_return)) - g_value_set_boolean (return_accu, FALSE); - return TRUE; + /* Allow the scan */ + return FALSE; } static gboolean -check_scanning_allowed (NMDeviceWifi *self) +check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic) { - GValue instance = G_VALUE_INIT; - GValue retval = G_VALUE_INIT; - - g_value_init (&instance, G_TYPE_OBJECT); - g_value_take_object (&instance, self); - - g_value_init (&retval, G_TYPE_BOOLEAN); - g_value_set_boolean (&retval, TRUE); + gboolean prohibited = FALSE; - /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return - * value being changed if no handlers are connected */ - g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval); - - return g_value_get_boolean (&retval); + g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited); + return prohibited; } static gboolean @@ -1354,8 +1407,12 @@ hidden_filter_func (NMSettings *settings, if (!nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_WIRELESS_SETTING_NAME)) return FALSE; - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connection)); - return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; + s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection)); + if (!s_wifi) + return FALSE; + if (nm_streq0 (nm_setting_wireless_get_mode (s_wifi), NM_SETTING_WIRELESS_MODE_AP)) + return FALSE; + return nm_setting_wireless_get_hidden (s_wifi); } static GPtrArray * @@ -1410,32 +1467,11 @@ build_hidden_probe_list (NMDeviceWifi *self) return ssids; } -static GPtrArray * -ssids_options_to_ptrarray (GVariant *value) -{ - GPtrArray *ssids = NULL; - GByteArray *ssid_array; - GVariant *v; - const guint8 *bytes; - gsize len; - int num_ssids, i; - - num_ssids = g_variant_n_children (value); - if (num_ssids) { - ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref); - for (i = 0; i < num_ssids; i++) { - v = g_variant_get_child_value (value, i); - bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8)); - ssid_array = g_byte_array_new (); - g_byte_array_append (ssid_array, bytes, len); - g_ptr_array_add (ssids, ssid_array); - } - } - return ssids; -} - static void -request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options) +request_wireless_scan (NMDeviceWifi *self, + gboolean periodic, + gboolean force_if_scanning, + const GPtrArray *ssids) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); gboolean request_started = FALSE; @@ -1447,24 +1483,14 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant return; } - if (check_scanning_allowed (self)) { - gs_unref_ptrarray GPtrArray *ssids = NULL; + if (!check_scanning_prohibited (self, periodic)) { + gs_unref_ptrarray GPtrArray *hidden_ssids = NULL; _LOGD (LOGD_WIFI, "wifi-scan: scanning requested"); - if (scan_options) { - GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); - - if (val) { - if (g_variant_is_of_type (val, G_VARIANT_TYPE ("aay"))) - ssids = ssids_options_to_ptrarray (val); - else - _LOGD (LOGD_WIFI, "wifi-scan: ignoring invalid 'ssids' scan option"); - g_variant_unref (val); - } + if (!ssids) { + ssids = hidden_ssids = build_hidden_probe_list (self); } - if (!ssids) - ssids = build_hidden_probe_list (self); if (_LOGD_ENABLED (LOGD_WIFI)) { if (ssids) { @@ -1478,7 +1504,7 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant ? nm_utils_ssid_to_utf8 (ssid->data, ssid->len) : NULL; _LOGD (LOGD_WIFI, "wifi-scan: (%u) probe scanning SSID %s%s%s", - i, NM_PRINT_FMT_QUOTED (foo, "\"", foo, "\"", "<hidden>")); + i, NM_PRINT_FMT_QUOTED (foo, "\"", foo, "\"", "*any*")); g_free (foo); } } else @@ -1504,7 +1530,7 @@ request_wireless_scan_periodic (gpointer user_data) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); priv->pending_scan_id = 0; - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, TRUE, FALSE, NULL); return G_SOURCE_REMOVE; } @@ -1747,6 +1773,7 @@ cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); if (disconnect && priv->sup_iface) nm_supplicant_interface_disconnect (priv->sup_iface); } @@ -1789,9 +1816,19 @@ wifi_secrets_cb (NMActRequest *req, if (error) { _LOGW (LOGD_WIFI, "%s", error->message); - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); + + if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, + NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { + /* Don't wait for WPS timeout on an explicit cancel. */ + nm_clear_g_source (&priv->wps_timeout_id); + } + + if (!priv->wps_timeout_id) { + /* Fail the device only if the WPS period is over too. */ + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1807,6 +1844,78 @@ wifi_secrets_cancel (NMDeviceWifi *self) } static void +supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self) +{ + NMActRequest *req; + GVariant *val, *secrets = NULL; + const char *array; + gsize psk_len = 0; + GError *error = NULL; + + if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_NEED_AUTH) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: The connection can't be updated with credentials"); + return; + } + + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: Updating the connection with credentials"); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + val = g_variant_lookup_value (credentials, "Key", G_VARIANT_TYPE_BYTESTRING); + if (val) { + char psk[64]; + + array = g_variant_get_fixed_array (val, &psk_len, 1); + if (psk_len >= 8 && psk_len <= 63) { + memcpy (psk, array, psk_len); + psk[psk_len] = '\0'; + if (g_utf8_validate (psk, psk_len, NULL)) { + secrets = g_variant_new_parsed ("[{%s, [{%s, <%s>}]}]", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, psk); + g_variant_ref_sink (secrets); + } + } + if (!secrets) + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: ignore invalid PSK"); + g_variant_unref (val); + } + if (secrets) { + if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, &error)) { + wifi_secrets_cancel (self); + nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self)); + } else { + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Could not update the connection with credentials: %s", error->message); + g_error_free (error); + } + g_variant_unref (secrets); + } +} + +static gboolean +wps_timeout_cb (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + priv->wps_timeout_id = 0; + if (!priv->wifi_secrets_id) { + /* Fail only if the secrets are not being requested. */ + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } + + return G_SOURCE_REMOVE; +} + +static void wifi_secrets_get_secrets (NMDeviceWifi *self, const char *setting_name, NMSecretAgentGetSecretsFlags flags) @@ -2055,6 +2164,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); /* If this is the initial association during device activation, * schedule the next activation stage. @@ -2131,7 +2241,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, /* we would clear _requested_scan_set() and trigger a new scan. * However, we don't want to cancel the current pending action, so force * a new scan request. */ - request_wireless_scan (self, TRUE, NULL); + request_wireless_scan (self, FALSE, TRUE, NULL); break; default: break; @@ -2222,9 +2332,15 @@ handle_auth_or_fail (NMDeviceWifi *self, NMActRequest *req, gboolean new_secrets) { + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); const char *setting_name; - guint32 tries; NMConnection *applied_connection; + NMSettingWirelessSecurity *s_wsec; + const char *bssid = NULL; + NM80211ApFlags ap_flags; + NMSettingWirelessSecurityWpsMethod wps_method; + const char *type; + NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); @@ -2233,14 +2349,50 @@ handle_auth_or_fail (NMDeviceWifi *self, g_return_val_if_fail (req, FALSE); } - applied_connection = nm_act_request_get_applied_connection (req); - - tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark ())); - if (tries > 3) + if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return FALSE; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + applied_connection = nm_act_request_get_applied_connection (req); + s_wsec = nm_connection_get_setting_wireless_security (applied_connection); + wps_method = nm_setting_wireless_security_get_wps_method (s_wsec); + + /* Negotiate the WPS method */ + if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + wps_method = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO; + + if ( wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO + && priv->current_ap) { + /* Determine the method to use from AP capabilities. */ + ap_flags = nm_wifi_ap_get_flags (priv->current_ap); + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PBC) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PIN) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + if ( ap_flags & NM_802_11_AP_FLAGS_WPS + && wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO) { + /* The AP doesn't specify which methods are supported. Allow all. */ + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + } + } + + if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC) { + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE; + type = "pbc"; + } else if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) { + type = "pin"; + } else + type = NULL; + + if (type) { + priv->wps_timeout_id = g_timeout_add_seconds (30, wps_timeout_cb, self); + if (priv->current_ap) + bssid = nm_wifi_ap_get_address (priv->current_ap); + nm_supplicant_interface_enroll_wps (priv->sup_iface, type, bssid, NULL); + } + nm_act_request_clear_secrets (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); if (!setting_name) { @@ -2248,10 +2400,9 @@ handle_auth_or_fail (NMDeviceWifi *self, return FALSE; } - wifi_secrets_get_secrets (self, setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries)); + if (new_secrets) + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + wifi_secrets_get_secrets (self, setting_name, get_secret_flags); return TRUE; } @@ -2348,6 +2499,8 @@ build_supplicant_config (NMDeviceWifi *self, NMSupplicantConfig *config = NULL; NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wireless_sec; + NMSettingWirelessSecurityPmf pmf; + gs_free char *value = NULL; g_return_val_if_fail (priv->sup_iface, NULL); @@ -2370,6 +2523,11 @@ build_supplicant_config (NMDeviceWifi *self, goto error; } + if (!nm_supplicant_config_add_bgscan (config, connection, error)) { + g_prefix_error (error, "bgscan: "); + goto error; + } + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); if (s_wireless_sec) { NMSetting8021x *s_8021x; @@ -2378,12 +2536,46 @@ build_supplicant_config (NMDeviceWifi *self, nm_device_get_ifindex (NM_DEVICE (self))); g_assert (con_uuid); + + /* Configure PMF (802.11w) */ + pmf = nm_setting_wireless_security_get_pmf (s_wireless_sec); + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) { + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi-sec.pmf", + NM_DEVICE (self)); + pmf = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE, + NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED, + NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL); + } + + /* Don't try to enable PMF on non-WPA networks */ + if (!NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wireless_sec), + "wpa-eap", + "wpa-psk")) + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + + /* Check if we actually support PMF */ + if (nm_supplicant_interface_get_pmf_support (priv->sup_iface) != NM_SUPPLICANT_FEATURE_YES) { + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { + g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, + "Supplicant does not support PMF"); + goto error; + } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) { + /* To be on the safe side, assume no support if we can't determine + * capabilities. + */ + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + } + } + s_8021x = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_wireless_security (config, s_wireless_sec, s_8021x, con_uuid, mtu, + pmf, error)) { g_prefix_error (error, "802-11-wireless-security: "); goto error; @@ -2430,6 +2622,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + nm_supplicant_interface_cancel_wps (priv->sup_iface); + mode = nm_setting_wireless_get_mode (s_wireless); if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) priv->mode = NM_802_11_MODE_INFRA; @@ -2578,6 +2772,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); @@ -2873,9 +3068,6 @@ activation_success_handler (NMDevice *device) /* Clear any critical protocol notification in the wifi stack */ nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE); - /* Clear wireless secrets tries on success */ - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL); - /* There should always be a current AP, either a fake one because we haven't * seen a scan result for the activated AP yet, or a real one from the * supplicant's scan list. @@ -2922,21 +3114,6 @@ activation_success_handler (NMDevice *device) } static void -activation_failure_handler (NMDevice *device) -{ - NMConnection *applied_connection; - - applied_connection = nm_device_get_applied_connection (device); - g_assert (applied_connection); - - /* Clear wireless secrets tries on failure */ - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL); - - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE); -} - -static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -2990,12 +3167,13 @@ device_state_changed (NMDevice *device, activation_success_handler (device); break; case NM_DEVICE_STATE_FAILED: - activation_failure_handler (device); + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE); break; case NM_DEVICE_STATE_DISCONNECTED: /* Kick off a scan to get latest results */ priv->scan_interval = SCAN_INTERVAL_MIN; - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, FALSE, FALSE, NULL); break; default: break; @@ -3159,7 +3337,7 @@ nm_device_wifi_init (NMDeviceWifi *self) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); priv->mode = NM_802_11_MODE_INFRA; - priv->aps = g_hash_table_new (g_str_hash, g_str_equal); + priv->aps = g_hash_table_new (nm_str_hash, g_str_equal); } static void @@ -3261,7 +3439,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->state_changed = device_state_changed; - klass->scanning_allowed = scanning_allowed; + klass->scanning_prohibited = scanning_prohibited; obj_properties[PROP_MODE] = g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "", @@ -3322,13 +3500,13 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_TYPE_NONE, 1, NM_TYPE_WIFI_AP); - signals[SCANNING_ALLOWED] = - g_signal_new (NM_DEVICE_WIFI_SCANNING_ALLOWED, + signals[SCANNING_PROHIBITED] = + g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed), - scanning_allowed_accumulator, NULL, NULL, - G_TYPE_BOOLEAN, 0); + G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited), + NULL, NULL, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), NMDBUS_TYPE_DEVICE_WIFI_SKELETON, diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 024fe0ef..09707d4f 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -44,7 +44,7 @@ #define NM_DEVICE_WIFI_ACCESS_POINT_REMOVED "access-point-removed" /* internal signals */ -#define NM_DEVICE_WIFI_SCANNING_ALLOWED "scanning-allowed" +#define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" typedef struct _NMDeviceWifi NMDeviceWifi; typedef struct _NMDeviceWifiClass NMDeviceWifiClass; diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 7de0838f..bc823af0 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -71,7 +71,7 @@ typedef struct { /* Non-scanned attributes */ bool fake:1; /* Whether or not the AP is from a scan */ bool hotspot:1; /* Whether the AP is a local device's hotspot network */ - gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ + gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ } NMWifiAPPrivate; struct _NMWifiAP { @@ -79,7 +79,7 @@ struct _NMWifiAP { NMWifiAPPrivate _priv; }; -struct _NMWifiAPClass{ +struct _NMWifiAPClass { NMExportedObjectClass parent; }; @@ -376,6 +376,14 @@ nm_wifi_ap_set_fake (NMWifiAP *ap, gboolean fake) return FALSE; } +NM80211ApFlags +nm_wifi_ap_get_flags (const NMWifiAP *ap) +{ + g_return_val_if_fail (NM_IS_WIFI_AP (ap), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_AP_GET_PRIVATE (ap)->flags; +} + static gboolean nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen) { @@ -435,6 +443,326 @@ security_from_vardict (GVariant *security) return flags; } +/*****************************************************************************/ + +static guint32 +get_max_rate_ht_20 (int mcs) +{ + switch (mcs) { + case 0: return 6500000; + case 1: + case 8: return 13000000; + case 2: + case 16: return 19500000; + case 3: + case 9: + case 24: return 26000000; + case 4: + case 10: + case 17: return 39000000; + case 5: + case 11: + case 25: return 52000000; + case 6: + case 18: return 58500000; + case 7: return 65000000; + case 12: + case 19: + case 26: return 78000000; + case 13: + case 27: return 104000000; + case 14: + case 20: return 117000000; + case 15: return 130000000; + case 21: + case 28: return 156000000; + case 22: return 175500000; + case 23: return 195000000; + case 29: return 208000000; + case 30: return 234000000; + case 31: return 260000000; + } + return 0; +} + +static guint32 +get_max_rate_ht_40 (int mcs) +{ + switch (mcs) { + case 0: return 13500000; + case 1: + case 8: return 27000000; + case 2: return 40500000; + case 3: + case 9: + case 24: return 54000000; + case 4: + case 10: + case 17: return 81000000; + case 5: + case 11: + case 25: return 108000000; + case 6: + case 18: return 121500000; + case 7: return 135000000; + case 12: + case 19: + case 26: return 162000000; + case 13: + case 27: return 216000000; + case 14: + case 20: return 243000000; + case 15: return 270000000; + case 16: return 40500000; + case 21: + case 28: return 324000000; + case 22: return 364500000; + case 23: return 405000000; + case 29: return 432000000; + case 30: return 486000000; + case 31: return 540000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss1 (int mcs) +{ + switch (mcs) { + case 0: return 29300000; + case 1: return 58500000; + case 2: return 87800000; + case 3: return 117000000; + case 4: return 175500000; + case 5: return 234000000; + case 6: return 263300000; + case 7: return 292500000; + case 8: return 351000000; + case 9: return 390000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss2 (int mcs) +{ + switch (mcs) { + case 0: return 58500000; + case 1: return 117000000; + case 2: return 175500000; + case 3: return 234000000; + case 4: return 351000000; + case 5: return 468000000; + case 6: return 526500000; + case 7: return 585000000; + case 8: return 702000000; + case 9: return 780000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss3 (int mcs) +{ + switch (mcs) { + case 0: return 87800000; + case 1: return 175500000; + case 2: return 263300000; + case 3: return 351000000; + case 4: return 526500000; + case 5: return 702000000; + case 6: return 0; + case 7: return 877500000; + case 8: return 105300000; + case 9: return 117000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss1 (int mcs) +{ + switch (mcs) { + case 0: return 58500000; + case 1: return 117000000; + case 2: return 175500000; + case 3: return 234000000; + case 4: return 351000000; + case 5: return 468000000; + case 6: return 526500000; + case 7: return 585000000; + case 8: return 702000000; + case 9: return 780000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss2 (int mcs) +{ + switch (mcs) { + case 0: return 117000000; + case 1: return 234000000; + case 2: return 351000000; + case 3: return 468000000; + case 4: return 702000000; + case 5: return 936000000; + case 6: return 1053000000; + case 7: return 1170000000; + case 8: return 1404000000; + case 9: return 1560000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss3 (int mcs) +{ + switch (mcs) { + case 0: return 175500000; + case 1: return 351000000; + case 2: return 526500000; + case 3: return 702000000; + case 4: return 1053000000; + case 5: return 1404000000; + case 6: return 1579500000; + case 7: return 1755000000; + case 8: return 2106000000; + case 9: return 0; + } + return 0; +} + +static gboolean +get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate) +{ + guint32 mcs, i; + guint8 ht_cap_info; + const guint8 *supported_mcs_set; + guint32 rate; + + /* http://standards.ieee.org/getieee802/download/802.11-2012.pdf + * https://mrncciew.com/2014/10/19/cwap-ht-capabilities-ie/ + */ + + if (len != 26) + return FALSE; + + ht_cap_info = bytes[0]; + supported_mcs_set = &bytes[3]; + *out_maxrate = 0; + + /* Find the maximum supported mcs rate */ + mcs = -1; + for (i = 0; i <= 76; i++) { + unsigned int mcs_octet = i / 8; + unsigned int MCS_RATE_BIT = 1 << i % 8; + + if (supported_mcs_set[mcs_octet] & MCS_RATE_BIT) { + /* Check for 40Mhz wide channel support */ + if (ht_cap_info & (1 << 1)) + rate = get_max_rate_ht_40 (i); + else + rate = get_max_rate_ht_20 (i); + + if (rate > *out_maxrate) + *out_maxrate = rate; + } + } + + return TRUE; +} + +static gboolean +get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate) +{ + guint32 mcs, m; + guint8 vht_cap, tx_map; + + /* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html + * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */ + + if (len != 12) + return FALSE; + + vht_cap = bytes[0]; + tx_map = bytes[8]; + + /* Check for mcs rates 8 and 9 support */ + if (tx_map & 0x2a) + mcs = 9; + else if (tx_map & 0x15) + mcs = 8; + else + mcs = 7; + + /* Check for 160Mhz wide channel support and + * spatial stream support */ + if (vht_cap & (1 << 2)) { + if (tx_map & 0x30) + m = get_max_rate_vht_160_ss3 (mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_160_ss2 (mcs); + else + m = get_max_rate_vht_160_ss1 (mcs); + } else { + if (tx_map & 0x30) + m = get_max_rate_vht_80_ss3 (mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_80_ss2 (mcs); + else + m = get_max_rate_vht_80_ss1 (mcs); + } + + *out_maxrate = m; + return TRUE; +} + +/* Management Frame Information Element IDs, ieee80211_eid */ +#define WLAN_EID_HT_CAPABILITY 45 +#define WLAN_EID_VHT_CAPABILITY 191 + +static guint32 +get_max_rate (const guint8 *bytes, gsize len) +{ + guint8 id, elem_len; + guint32 max_rate = 0; + + while (len) { + guint32 m; + + if (len < 2) + return 0; + + id = *bytes++; + elem_len = *bytes++; + len -= 2; + + if (elem_len > len) + return 0; + + switch (id) { + case WLAN_EID_HT_CAPABILITY: + if (!get_max_rate_ht (bytes, elem_len, &m)) + return 0; + max_rate = NM_MAX (max_rate, m); + break; + case WLAN_EID_VHT_CAPABILITY: + if (!get_max_rate_vht (bytes, elem_len, &m)) + return 0; + max_rate = NM_MAX (max_rate, m); + break; + } + + len -= elem_len; + bytes += elem_len; + } + + return max_rate; +} + +/*****************************************************************************/ + gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, const char *supplicant_path, @@ -444,11 +772,13 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, const guint8 *bytes; GVariant *v; gsize len; + gsize i; gboolean b = FALSE; const char *s; gint16 i16; guint16 u16; gboolean changed = FALSE; + guint32 max_rate; g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); g_return_val_if_fail (properties, FALSE); @@ -460,6 +790,18 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, if (g_variant_lookup (properties, "Privacy", "b", &b) && b) changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY); + v = g_variant_lookup_value (properties, "WPS", G_VARIANT_TYPE_VARDICT); + if (v) { + if (g_variant_lookup (v, "Type", "&s", &s)) { + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS); + if (strcmp (s, "pbc") == 0) + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PBC); + else if (strcmp (s, "pin") == 0) + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PIN); + } + g_variant_unref (v); + } + if (g_variant_lookup (properties, "Mode", "&s", &s)) { if (!g_strcmp0 (s, "infrastructure")) changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); @@ -497,21 +839,23 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, g_variant_unref (v); } + max_rate = 0; v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); if (v) { const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32)); - guint32 maxrate = 0; - int i; - /* Find the max AP rate */ - for (i = 0; i < len; i++) { - if (rates[i] > maxrate) - maxrate = rates[i]; - } - if (maxrate) - changed |= nm_wifi_ap_set_max_bitrate (ap, maxrate / 1000); + for (i = 0; i < len; i++) + max_rate = NM_MAX (max_rate, rates[i]); + g_variant_unref (v); + } + v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + max_rate = NM_MAX (max_rate, get_max_rate (bytes, len)); g_variant_unref (v); } + if (max_rate) + changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000); v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT); if (v) { diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 5e64087c..dd5a4ad1 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -88,6 +88,7 @@ gboolean nm_wifi_ap_set_max_bitrate (NMWifiAP *ap, gboolean nm_wifi_ap_get_fake (const NMWifiAP *ap); gboolean nm_wifi_ap_set_fake (NMWifiAP *ap, gboolean fake); +NM80211ApFlags nm_wifi_ap_get_flags (const NMWifiAP *self); const char *nm_wifi_ap_to_string (const NMWifiAP *self, char *str_buf, diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index 06da92ce..3ff82004 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -777,10 +777,8 @@ nm_wifi_utils_level_to_quality (gint val) val = 100 - (int) ((100.0 * (double) val) / 60.0); } else { /* Assume signal is a "quality" percentage */ - val = CLAMP (val, 0, 100); } - g_assert (val >= 0); - return (guint32) val; + return CLAMP (val, 0, 100); } diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-general.c index 3e61c5f0..89eebb22 100644 --- a/src/devices/wifi/tests/test-general.c +++ b/src/devices/wifi/tests/test-general.c @@ -44,16 +44,16 @@ \ success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \ if (success == FALSE && DEBUG) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ + g_print ("\n- COMPLETED ---------------------------------\n"); \ nm_connection_dump (src); \ - g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ + g_print ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ nm_connection_dump (expected); \ - g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ + g_print ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ } \ g_assert (success == TRUE); \ } else { \ if (success) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ + g_print ("\n- COMPLETED ---------------------------------\n"); \ nm_connection_dump (src); \ } \ g_assert (success == FALSE); \ @@ -1334,6 +1334,28 @@ test_strength_wext (void) g_assert_cmpint (nm_wifi_utils_level_to_quality (215), ==, 99); } +#define _assert_strength_in_range(x) \ + ({ \ + guint32 _x = (x); \ + g_assert_cmpint (_x, >=, 0); \ + g_assert_cmpint (_x, <=, 100); \ + }) + +static void +test_strength_all (void) +{ + int val; + + for (val = -200; val < 300; val++) + _assert_strength_in_range (nm_wifi_utils_level_to_quality (val)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT32)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT32)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT16)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT16)); +} + /*****************************************************************************/ NMTST_DEFINE (); @@ -1497,6 +1519,8 @@ main (int argc, char **argv) test_strength_percent); g_test_add_func ("/wifi/strength/wext", test_strength_wext); + g_test_add_func ("/wifi/strength/all", + test_strength_all); return g_test_run (); } diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index eb577aaf..6efcb03f 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -20,7 +20,11 @@ global: nm_modem_get_type; nm_modem_get_uid; nm_modem_ip4_pre_commit; + nm_modem_manager_get; nm_modem_manager_get_type; + nm_modem_manager_name_owner_get; + nm_modem_manager_name_owner_ref; + nm_modem_manager_name_owner_unref; nm_modem_owns_port; nm_modem_set_mm_enabled; nm_modem_stage3_ip4_config_start; diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 4a4d2f2c..22fb8c67 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -364,9 +364,8 @@ device_state_changed (NMDevice *device, { NMDeviceModem *self = NM_DEVICE_MODEM (device); NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); - NMSettingsConnection *connection = nm_device_get_settings_connection (device); - g_assert (priv->modem); + g_return_if_fail (priv->modem); if (new_state == NM_DEVICE_STATE_UNAVAILABLE && old_state < NM_DEVICE_STATE_UNAVAILABLE) { @@ -374,30 +373,7 @@ device_state_changed (NMDevice *device, _LOGI (LOGD_MB, "modem state '%s'", nm_modem_state_to_string (nm_modem_get_state (priv->modem))); } - nm_modem_device_state_changed (priv->modem, new_state, old_state); - - switch (nm_device_state_reason_check (reason)) { - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: - case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: - case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: - case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: - case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - /* Block autoconnect of the just-failed connection for situations - * where a retry attempt would just fail again. - */ - if (connection) { - nm_settings_connection_set_autoconnect_blocked_reason (connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED); - } - break; - default: - break; - } } static NMDeviceCapabilities @@ -664,6 +640,16 @@ set_modem (NMDeviceModem *self, NMModem *modem) g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self); } +static guint32 +get_dhcp_timeout (NMDevice *device, int addr_family) +{ + /* DHCP is always done by the modem firmware, not by the network, and + * by the time we get around to DHCP the firmware should already know + * the IP addressing details. So the DHCP timeout can be much shorter. + */ + return 15; +} + /*****************************************************************************/ static void @@ -718,18 +704,6 @@ nm_device_modem_init (NMDeviceModem *self) { } -static void -constructed (GObject *object) -{ - G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object); - - /* DHCP is always done by the modem firmware, not by the network, and - * by the time we get around to DHCP the firmware should already know - * the IP addressing details. So the DHCP timeout can be much shorter. - */ - nm_device_set_dhcp_timeout (NM_DEVICE (object), 15); -} - NMDevice * nm_device_modem_new (NMModem *modem) { @@ -786,7 +760,6 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; - object_class->constructed = constructed; device_class->get_generic_capabilities = get_generic_capabilities; device_class->get_type_description = get_type_description; @@ -807,6 +780,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) device_class->is_available = is_available; device_class->get_ip_iface_identifier = get_ip_iface_identifier; device_class->get_configured_mtu = nm_modem_get_configured_mtu; + device_class->get_dhcp_timeout = get_dhcp_timeout; device_class->state_changed = device_state_changed; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 4b16fb14..6e5f10a0 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -90,6 +90,9 @@ typedef struct { MMBearerIpConfig *ipv4_config; MMBearerIpConfig *ipv6_config; + guint idle_id_ip4; + guint idle_id_ip6; + guint32 pin_tries; } NMModemBroadbandPrivate; @@ -860,21 +863,6 @@ set_mm_enabled (NMModem *_self, /* IPv4 method static */ static gboolean -ip4_string_to_num (const gchar *str, guint32 *out) -{ - guint32 addr = 0; - gboolean success = FALSE; - - if (!str || inet_pton (AF_INET, str, &addr) != 1) - addr = 0; - else - success = TRUE; - - *out = (guint32)addr; - return success; -} - -static gboolean static_stage3_ip4_done (NMModemBroadband *self) { GError *error = NULL; @@ -883,7 +871,7 @@ static_stage3_ip4_done (NMModemBroadband *self) const gchar *address_string; const gchar *gw_string; guint32 address_network; - guint32 gw; + guint32 gw = 0; NMPlatformIP4Address address; const gchar **dns; guint i; @@ -895,7 +883,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config); - if (!ip4_string_to_num (address_string, &address_network)) { + if (!nm_utils_parse_inaddr_bin (AF_INET, address_string, &address_network)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid address given '%s'", @@ -906,11 +894,20 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config); - ip4_string_to_num (gw_string, &gw); + if ( !gw_string + || !nm_utils_parse_inaddr_bin (AF_INET, gw_string, &gw)) { + error = g_error_new (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) retrieving IP4 configuration failed: invalid gateway address %s%s%s", + nm_modem_get_uid (NM_MODEM (self)), + NM_PRINT_FMT_QUOTE_STRING (gw_string)); + goto out; + } data_port = mm_bearer_get_interface (self->_priv.bearer); g_assert (data_port); - config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); + config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); memset (&address, 0, sizeof (address)); address.address = address_network; @@ -923,14 +920,30 @@ static_stage3_ip4_done (NMModemBroadband *self) _LOGI (" address %s/%d", address_string, address.plen); if (gw) { - nm_ip4_config_set_gateway (config, gw); - _LOGI (" gateway %s", gw_string); + guint32 ip4_route_table, ip4_route_metric; + + nm_modem_get_route_parameters (NM_MODEM (self), + &ip4_route_table, + &ip4_route_metric, + NULL, + NULL); + { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_WWAN, + .gateway = gw, + .table_coerced = nm_platform_route_table_coerce (ip4_route_table), + .metric = ip4_route_metric, + }; + + _LOGI (" gateway %s", gw_string); + nm_ip4_config_add_route (config, &r, NULL); + } } /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config); for (i = 0; dns && dns[i]; i++) { - if ( ip4_string_to_num (dns[i], &address_network) + if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], &address_network) && address_network > 0) { nm_ip4_config_add_nameserver (config, address_network); _LOGI (" DNS %s", dns[i]); @@ -944,15 +957,17 @@ out: } static NMActStageReturn -static_stage3_ip4_config_start (NMModem *_self, +static_stage3_ip4_config_start (NMModem *modem, NMActRequest *req, NMDeviceStateReason *out_failure_reason) { - NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); /* We schedule it in an idle just to follow the same logic as in the * generic modem implementation. */ - g_idle_add ((GSourceFunc) static_stage3_ip4_done, self); + nm_clear_g_source (&priv->idle_id_ip4); + priv->idle_id_ip4 = g_idle_add ((GSourceFunc) static_stage3_ip4_done, self); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -1004,7 +1019,8 @@ stage3_ip6_done (NMModemBroadband *self) data_port = mm_bearer_get_interface (self->_priv.bearer); g_assert (data_port); - config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv6_config); if (address.plen <= 128) @@ -1014,7 +1030,9 @@ stage3_ip6_done (NMModemBroadband *self) address_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv6_config); if (address_string) { - if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) { + guint32 ip6_route_table, ip6_route_metric; + + if (inet_pton (AF_INET6, address_string, &address.address) != 1) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'", @@ -1022,8 +1040,23 @@ stage3_ip6_done (NMModemBroadband *self) address_string); goto out; } - _LOGI (" gateway %s", address_string); - nm_ip6_config_set_gateway (config, &address.address); + + nm_modem_get_route_parameters (NM_MODEM (self), + NULL, + NULL, + &ip6_route_table, + &ip6_route_metric); + { + const NMPlatformIP6Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_WWAN, + .gateway = address.address, + .table_coerced = nm_platform_route_table_coerce (ip6_route_table), + .metric = ip6_route_metric, + }; + + _LOGI (" gateway %s", address_string); + nm_ip6_config_add_route (config, &r, NULL); + } } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { /* Gateway required for the 'static' method */ error = g_error_new (NM_DEVICE_ERROR, @@ -1052,13 +1085,15 @@ out: } static NMActStageReturn -stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *out_failure_reason) +stage3_ip6_config_request (NMModem *modem, NMDeviceStateReason *out_failure_reason) { - NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); /* We schedule it in an idle just to follow the same logic as in the * generic modem implementation. */ - g_idle_add ((GSourceFunc) stage3_ip6_done, self); + nm_clear_g_source (&priv->idle_id_ip6); + priv->idle_id_ip6 = g_idle_add ((GSourceFunc) stage3_ip6_done, self); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -1409,6 +1444,10 @@ static void dispose (GObject *object) { NMModemBroadband *self = NM_MODEM_BROADBAND (object); + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); + + nm_clear_g_source (&priv->idle_id_ip4); + nm_clear_g_source (&priv->idle_id_ip6); connect_context_clear (self); g_clear_object (&self->_priv.ipv4_config); diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c index b1f6d92e..59cd2bca 100644 --- a/src/devices/wwan/nm-modem-manager.c +++ b/src/devices/wwan/nm-modem-manager.c @@ -45,6 +45,10 @@ /*****************************************************************************/ +NM_GOBJECT_PROPERTIES_DEFINE (NMModemManager, + PROP_NAME_OWNER, +); + enum { MODEM_ADDED, LAST_SIGNAL, @@ -54,14 +58,38 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { GDBusConnection *dbus_connection; - MMManager *modem_manager; - guint mm_launch_id; - gulong mm_name_owner_changed_id; - gulong mm_object_added_id; - gulong mm_object_removed_id; + + /* used during g_bus_get() and later during mm_manager_new(). */ + GCancellable *main_cancellable; + + struct { + MMManager *manager; + GCancellable *poke_cancellable; + gulong handle_name_owner_changed_id; + gulong handle_object_added_id; + gulong handle_object_removed_id; + guint relaunch_id; + + /* this only has one use: that the <info> logging line about + * ModemManager available distinguishes between first-time + * and later name-owner-changed. */ + enum { + LOG_AVAILABLE_NOT_INITIALIZED = 0, + LOG_AVAILABLE_YES, + LOG_AVAILABLE_NO, + } log_available:3; + + GDBusProxy *proxy; + GCancellable *proxy_cancellable; + guint proxy_ref_count; + char *proxy_name_owner; + } modm; #if WITH_OFONO - GDBusProxy *ofono_proxy; + struct { + GDBusProxy *proxy; + GCancellable *cancellable; + } ofono; #endif GHashTable *modems; @@ -82,19 +110,35 @@ G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT) /*****************************************************************************/ +#define _NMLOG_DOMAIN LOGD_MB +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "modem-manager", __VA_ARGS__) + +/*****************************************************************************/ + +NM_DEFINE_SINGLETON_GETTER (NMModemManager, nm_modem_manager_get, NM_TYPE_MODEM_MANAGER); + +/*****************************************************************************/ + +static void modm_schedule_manager_relaunch (NMModemManager *self, + guint n_seconds); +static void modm_ensure_manager (NMModemManager *self); + +/*****************************************************************************/ + static void handle_new_modem (NMModemManager *self, NMModem *modem) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); const char *path; path = nm_modem_get_path (modem); - if (g_hash_table_lookup (self->_priv.modems, path)) { + if (g_hash_table_lookup (priv->modems, path)) { g_warn_if_reached (); return; } /* Track the new modem */ - g_hash_table_insert (self->_priv.modems, g_strdup (path), modem); + g_hash_table_insert (priv->modems, g_strdup (path), modem); g_signal_emit (self, signals[MODEM_ADDED], 0, modem); } @@ -105,22 +149,27 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data) return TRUE; } +/*****************************************************************************/ + static void -clear_modem_manager (NMModemManager *self) +modm_clear_manager (NMModemManager *self) { - if (!self->_priv.modem_manager) + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + if (!priv->modm.manager) return; - nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_name_owner_changed_id); - nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_object_added_id); - nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_object_removed_id); - g_clear_object (&self->_priv.modem_manager); + nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_name_owner_changed_id); + nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_object_added_id); + nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_object_removed_id); + g_clear_object (&priv->modm.manager); } static void -modem_object_added (MMManager *modem_manager, - MMObject *modem_object, - NMModemManager *self) +modm_handle_object_added (MMManager *modem_manager, + MMObject *modem_object, + NMModemManager *self) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); const gchar *path; MMModem *modem_iface; NMModem *modem; @@ -128,21 +177,21 @@ modem_object_added (MMManager *modem_manager, /* Ensure we don't have the same modem already */ path = mm_object_get_path (modem_object); - if (g_hash_table_lookup (self->_priv.modems, path)) { - nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path); + if (g_hash_table_lookup (priv->modems, path)) { + _LOGW ("modem with path %s already exists, ignoring", path); return; } /* Ensure we have the 'Modem' interface at least */ modem_iface = mm_object_peek_modem (modem_object); if (!modem_iface) { - nm_log_warn (LOGD_MB, "modem with path %s doesn't have the Modem interface, ignoring", path); + _LOGW ("modem with path %s doesn't have the Modem interface, ignoring", path); return; } /* Ensure we have a primary port reported */ if (!mm_modem_get_primary_port (modem_iface)) { - nm_log_warn (LOGD_MB, "modem with path %s has unknown primary port, ignoring", path); + _LOGW ("modem with path %s has unknown primary port, ignoring", path); return; } @@ -150,65 +199,68 @@ modem_object_added (MMManager *modem_manager, modem = nm_modem_broadband_new (G_OBJECT (modem_object), &error); if (modem) handle_new_modem (self, modem); - else { - nm_log_warn (LOGD_MB, "failed to create modem: %s", - error->message); - } + else + _LOGW ("failed to create modem: %s", error->message); g_clear_error (&error); } static void -modem_object_removed (MMManager *manager, - MMObject *modem_object, - NMModemManager *self) +modm_handle_object_removed (MMManager *manager, + MMObject *modem_object, + NMModemManager *self) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); NMModem *modem; const gchar *path; path = mm_object_get_path (modem_object); - modem = (NMModem *) g_hash_table_lookup (self->_priv.modems, path); + modem = (NMModem *) g_hash_table_lookup (priv->modems, path); if (!modem) return; nm_modem_emit_removed (modem); - g_hash_table_remove (self->_priv.modems, path); + g_hash_table_remove (priv->modems, path); } static void -modem_manager_available (NMModemManager *self) +modm_manager_available (NMModemManager *self) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); GList *modems, *l; - nm_log_info (LOGD_MB, "ModemManager available in the bus"); + if (priv->modm.log_available != LOG_AVAILABLE_YES) { + _LOGI ("ModemManager %savailable", priv->modm.log_available ? "now " : ""); + priv->modm.log_available = LOG_AVAILABLE_YES; + } /* Update initial modems list */ - modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->_priv.modem_manager)); + modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (priv->modm.manager)); for (l = modems; l; l = g_list_next (l)) - modem_object_added (self->_priv.modem_manager, MM_OBJECT (l->data), self); + modm_handle_object_added (priv->modm.manager, MM_OBJECT (l->data), self); g_list_free_full (modems, (GDestroyNotify) g_object_unref); } -static void schedule_modem_manager_relaunch (NMModemManager *self, - guint n_seconds); -static void ensure_modem_manager (NMModemManager *self); - static void -modem_manager_name_owner_changed (MMManager *modem_manager, - GParamSpec *pspec, - NMModemManager *self) +modm_handle_name_owner_changed (MMManager *modem_manager, + GParamSpec *pspec, + NMModemManager *self) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); gchar *name_owner; /* Quit poking, if any */ - nm_clear_g_source (&self->_priv.mm_launch_id); + nm_clear_g_source (&priv->modm.relaunch_id); name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager)); if (!name_owner) { - nm_log_info (LOGD_MB, "ModemManager disappeared from bus"); + if (priv->modm.log_available != LOG_AVAILABLE_NO) { + _LOGI ("ModemManager %savailable", priv->modm.log_available ? "no longer " : "not "); + priv->modm.log_available = LOG_AVAILABLE_NO; + } /* If not managed by systemd, schedule relaunch */ if (!sd_booted ()) - schedule_modem_manager_relaunch (self, 0); + modm_schedule_manager_relaunch (self, 0); return; } @@ -220,18 +272,322 @@ modem_manager_name_owner_changed (MMManager *modem_manager, * nor 'object-removed' if it was created while there was no ModemManager in * the bus. This hack avoids this issue until we get a GIO with the fix * included... */ - clear_modem_manager (self); - ensure_modem_manager (self); + modm_clear_manager (self); + modm_ensure_manager (self); /* Whenever GDBusObjectManagerClient is fixed, we can just do the following: - * modem_manager_available (self); + * modm_manager_available (self); */ } +static void +modm_manager_poke_cb (GObject *connection, + GAsyncResult *res, + gpointer user_data) +{ + NMModemManager *self; + NMModemManagerPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *result = NULL; + + result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (connection), res, &error); + + if ( !result + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->modm.poke_cancellable); + + if (error) { + _LOGW ("error poking ModemManager: %s", error->message); + + /* Don't reschedule poke is MM service doesn't exist. */ + if ( !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { + + /* Setup timeout to relaunch */ + modm_schedule_manager_relaunch (self, MODEM_POKE_INTERVAL); + } + } +} + +static void +modm_manager_poke (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + nm_clear_g_cancellable (&priv->modm.poke_cancellable); + priv->modm.poke_cancellable = g_cancellable_new (); + + /* If there is no current owner right away, ensure we poke to get one */ + g_dbus_connection_call (priv->dbus_connection, + NM_MODEM_MANAGER_MM_DBUS_SERVICE, + NM_MODEM_MANAGER_MM_DBUS_PATH, + DBUS_INTERFACE_PEER, + "Ping", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->modm.poke_cancellable, + modm_manager_poke_cb, + self); +} + +static void +modm_manager_check_name_owner (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + gs_free gchar *name_owner = NULL; + + name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->modm.manager)); + if (name_owner) { + modm_manager_available (self); + return; + } + + /* If the lifecycle is not managed by systemd, poke */ + if (!sd_booted ()) + modm_manager_poke (self); +} + +static void +modm_manager_new_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + NMModemManager *self; + NMModemManagerPrivate *priv; + gs_free_error GError *error = NULL; + MMManager *modem_manager; + + modem_manager = mm_manager_new_finish (res, &error); + if ( !modem_manager + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + nm_assert (!priv->modm.manager); + + g_clear_object (&priv->main_cancellable); + + if (!modem_manager) { + /* We're not really supposed to get any error here. If we do get one, + * though, just re-schedule the MMManager creation after some time. + * During this period, name-owner changes won't be followed. */ + _LOGW ("error creating ModemManager client: %s", error->message); + /* Setup timeout to relaunch */ + modm_schedule_manager_relaunch (self, MODEM_POKE_INTERVAL); + return; + } + + priv->modm.manager = modem_manager; + + /* Setup signals in the GDBusObjectManagerClient */ + priv->modm.handle_name_owner_changed_id = + g_signal_connect (priv->modm.manager, + "notify::name-owner", + G_CALLBACK (modm_handle_name_owner_changed), + self); + priv->modm.handle_object_added_id = + g_signal_connect (priv->modm.manager, + "object-added", + G_CALLBACK (modm_handle_object_added), + self); + priv->modm.handle_object_removed_id = + g_signal_connect (priv->modm.manager, + "object-removed", + G_CALLBACK (modm_handle_object_removed), + self); + + modm_manager_check_name_owner (self); +} + +static void +modm_ensure_manager (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_assert (priv->dbus_connection); + + /* Create the GDBusObjectManagerClient. We do not request to autostart, as + * we don't really want the MMManager creation to fail. We can always poke + * later on if we want to request the autostart */ + if (!priv->modm.manager) { + if (!priv->main_cancellable) + priv->main_cancellable = g_cancellable_new (); + mm_manager_new (priv->dbus_connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + priv->main_cancellable, + modm_manager_new_cb, + self); + return; + } + + /* If already available, recheck name owner! */ + modm_manager_check_name_owner (self); +} + +static gboolean +modm_schedule_manager_relaunch_cb (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + priv->modm.relaunch_id = 0; + modm_ensure_manager (self); + return G_SOURCE_REMOVE; +} + +static void +modm_schedule_manager_relaunch (NMModemManager *self, + guint n_seconds) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + /* No need to pass an extra reference to self; timeout/idle will be + * cancelled if the object gets disposed. */ + if (n_seconds) + priv->modm.relaunch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)modm_schedule_manager_relaunch_cb, self); + else + priv->modm.relaunch_id = g_idle_add ((GSourceFunc)modm_schedule_manager_relaunch_cb, self); +} + +/*****************************************************************************/ + +static void +modm_proxy_name_owner_reset (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + char *name = NULL; + + if (priv->modm.proxy) + name = g_dbus_proxy_get_name_owner (priv->modm.proxy); + + if (nm_streq0 (priv->modm.proxy_name_owner, name)) { + g_free (name); + return; + } + g_free (priv->modm.proxy_name_owner); + priv->modm.proxy_name_owner = name; + + _notify (self, PROP_NAME_OWNER); +} + +static void +modm_proxy_name_owner_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + modm_proxy_name_owner_reset (user_data); +} + +static void +modm_proxy_new_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + NMModemManager *self; + NMModemManagerPrivate *priv; + GDBusProxy *proxy; + gs_free_error GError *error = NULL; + + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->modm.proxy_cancellable); + + if (!proxy) { + _LOGW ("could not obtain D-Bus proxy for ModemManager: %s", error->message); + return; + } + + priv->modm.proxy = proxy; + g_signal_connect (priv->modm.proxy, "notify::g-name-owner", + G_CALLBACK (modm_proxy_name_owner_changed_cb), self); + + modm_proxy_name_owner_reset (self); +} + +void +nm_modem_manager_name_owner_ref (NMModemManager *self) +{ + NMModemManagerPrivate *priv; + + g_return_if_fail (NM_IS_MODEM_MANAGER (self)); + + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + if (priv->modm.proxy_ref_count++ > 0) { + /* only try once to create the proxy. If proxy creation + * for the first "ref" failed, it's unclear what to do. + * The proxy is hosed. */ + return; + } + + nm_assert (!priv->modm.proxy && !priv->modm.proxy_cancellable); + + priv->modm.proxy_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + NM_MODEM_MANAGER_MM_DBUS_SERVICE, + NM_MODEM_MANAGER_MM_DBUS_PATH, + NM_MODEM_MANAGER_MM_DBUS_INTERFACE, + priv->modm.proxy_cancellable, + modm_proxy_new_cb, + self); +} + +void +nm_modem_manager_name_owner_unref (NMModemManager *self) +{ + NMModemManagerPrivate *priv; + + g_return_if_fail (NM_IS_MODEM_MANAGER (self)); + + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_return_if_fail (priv->modm.proxy_ref_count > 0); + + if (--priv->modm.proxy_ref_count > 0) + return; + + nm_clear_g_cancellable (&priv->modm.proxy_cancellable); + g_clear_object (&priv->modm.proxy); + + modm_proxy_name_owner_reset (self); +} + +const char * +nm_modem_manager_name_owner_get (NMModemManager *self) +{ + g_return_val_if_fail (NM_IS_MODEM_MANAGER (self), NULL); + nm_assert (NM_MODEM_MANAGER_GET_PRIVATE (self)->modm.proxy_ref_count > 0); + + return NM_MODEM_MANAGER_GET_PRIVATE (self)->modm.proxy_name_owner; +} + +/*****************************************************************************/ + #if WITH_OFONO + static void ofono_create_modem (NMModemManager *self, const char *path) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); NMModem *modem = NULL; /* Ensure duplicate modems aren't created. Because we're not using the @@ -239,12 +595,12 @@ ofono_create_modem (NMModemManager *self, const char *path) * receive ModemAdded signals before GetModems() returns, so some of the * modems returned from GetModems() may already have been created. */ - if (!g_hash_table_lookup (self->_priv.modems, path)) { + if (!g_hash_table_lookup (priv->modems, path)) { modem = nm_modem_ofono_new (path); if (modem) handle_new_modem (self, modem); else - nm_log_warn (LOGD_MB, "Failed to create oFono modem for %s", path); + _LOGW ("Failed to create oFono modem for %s", path); } } @@ -256,80 +612,96 @@ ofono_signal_cb (GDBusProxy *proxy, gpointer user_data) { NMModemManager *self = NM_MODEM_MANAGER (user_data); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); gchar *object_path; NMModem *modem; if (g_strcmp0 (signal_name, "ModemAdded") == 0) { g_variant_get (parameters, "(oa{sv})", &object_path, NULL); - nm_log_info (LOGD_MB, "oFono modem appeared: %s", object_path); + _LOGI ("oFono modem appeared: %s", object_path); ofono_create_modem (NM_MODEM_MANAGER (user_data), object_path); g_free (object_path); } else if (g_strcmp0 (signal_name, "ModemRemoved") == 0) { g_variant_get (parameters, "(o)", &object_path); - nm_log_info (LOGD_MB, "oFono modem removed: %s", object_path); + _LOGI ("oFono modem removed: %s", object_path); - modem = (NMModem *) g_hash_table_lookup (self->_priv.modems, object_path); + modem = (NMModem *) g_hash_table_lookup (priv->modems, object_path); if (modem) { nm_modem_emit_removed (modem); - g_hash_table_remove (self->_priv.modems, object_path); + g_hash_table_remove (priv->modems, object_path); } else { - nm_log_warn (LOGD_MB, "could not remove modem %s, not found in table", - object_path); + _LOGW ("could not remove modem %s, not found in table", + object_path); } g_free (object_path); } } static void -ofono_enumerate_devices_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) +ofono_enumerate_devices_done (GObject *proxy, + GAsyncResult *res, + gpointer user_data) { - NMModemManager *manager = NM_MODEM_MANAGER (user_data); + NMModemManager *self; + NMModemManagerPrivate *priv; gs_free_error GError *error = NULL; GVariant *results; GVariantIter *iter; const char *path; - results = g_dbus_proxy_call_finish (proxy, res, &error); - if (results) { - g_variant_get (results, "(a(oa{sv}))", &iter); - while (g_variant_iter_loop (iter, "(&oa{sv})", &path, NULL)) - ofono_create_modem (manager, path); - g_variant_iter_free (iter); - g_variant_unref (results); - } + results = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error); + if ( !results + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - if (error) { - nm_log_warn (LOGD_MB, "failed to enumerate oFono devices: %s", - error->message); + self = NM_MODEM_MANAGER (user_data); + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->ofono.cancellable); + + if (!results) { + _LOGW ("failed to enumerate oFono devices: %s", + error->message); + return; } + + g_variant_get (results, "(a(oa{sv}))", &iter); + while (g_variant_iter_loop (iter, "(&oa{sv})", &path, NULL)) + ofono_create_modem (self, path); + g_variant_iter_free (iter); + g_variant_unref (results); } static void -ofono_check_name_owner (NMModemManager *self) +ofono_check_name_owner (NMModemManager *self, gboolean first_invocation) { + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); gs_free char *name_owner = NULL; - name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->_priv.ofono_proxy)); + name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->ofono.proxy)); if (name_owner) { - nm_log_info (LOGD_MB, "oFono is now available"); + _LOGI ("oFono is %savailable", first_invocation ? "" : "now "); + + nm_clear_g_cancellable (&priv->ofono.cancellable); + priv->ofono.cancellable = g_cancellable_new (); - g_dbus_proxy_call (self->_priv.ofono_proxy, + g_dbus_proxy_call (priv->ofono.proxy, "GetModems", NULL, G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - (GAsyncReadyCallback) ofono_enumerate_devices_done, - g_object_ref (self)); + priv->ofono.cancellable, + ofono_enumerate_devices_done, + self); } else { GHashTableIter iter; NMModem *modem; - nm_log_info (LOGD_MB, "oFono disappeared from bus"); + _LOGI ("oFono is %savailable", first_invocation ? "not " : "no longer "); /* Remove any oFono modems that might be left around */ - g_hash_table_iter_init (&iter, self->_priv.modems); + g_hash_table_iter_init (&iter, priv->modems); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &modem)) { if (NM_IS_MODEM_OFONO (modem)) { nm_modem_emit_removed (modem); @@ -344,219 +716,121 @@ ofono_name_owner_changed (GDBusProxy *ofono_proxy, GParamSpec *pspec, NMModemManager *self) { - ofono_check_name_owner (self); + ofono_check_name_owner (self, FALSE); } static void -ofono_proxy_new_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +ofono_proxy_new_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { - gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data); + NMModemManager *self; + NMModemManagerPrivate *priv; gs_free_error GError *error = NULL; + GDBusProxy *proxy; - self->_priv.ofono_proxy = g_dbus_proxy_new_finish (res, &error); - if (error) { - nm_log_warn (LOGD_MB, "error getting oFono bus proxy: %s", error->message); + proxy = g_dbus_proxy_new_finish (res, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_MANAGER (user_data); + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_clear_object (&priv->ofono.cancellable); + + if (!proxy) { + _LOGW ("error getting oFono bus proxy: %s", error->message); return; } - g_signal_connect (self->_priv.ofono_proxy, + priv->ofono.proxy = proxy; + + g_signal_connect (priv->ofono.proxy, "notify::g-name-owner", G_CALLBACK (ofono_name_owner_changed), self); - g_signal_connect (self->_priv.ofono_proxy, + g_signal_connect (priv->ofono.proxy, "g-signal", G_CALLBACK (ofono_signal_cb), self); - ofono_check_name_owner (self); + ofono_check_name_owner (self, TRUE); } static void -ensure_ofono_client (NMModemManager *self) +ofono_init_proxy (NMModemManager *self) { - g_assert (self->_priv.dbus_connection); - g_dbus_proxy_new (self->_priv.dbus_connection, + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + nm_assert (priv->dbus_connection); + nm_assert (!priv->ofono.cancellable); + + priv->ofono.cancellable = g_cancellable_new (); + + g_dbus_proxy_new (priv->dbus_connection, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, OFONO_DBUS_SERVICE, OFONO_DBUS_PATH, OFONO_DBUS_INTERFACE, - NULL, - (GAsyncReadyCallback) ofono_proxy_new_cb, - g_object_ref (self)); + priv->ofono.cancellable, + ofono_proxy_new_cb, + self); } #endif -static void -modem_manager_poke_cb (GDBusConnection *connection, - GAsyncResult *res, - NMModemManager *self) -{ - GError *error = NULL; - GVariant *result; - - result = g_dbus_connection_call_finish (connection, res, &error); - if (error) { - nm_log_warn (LOGD_MB, "error poking ModemManager: %s", - error ? error->message : ""); - - /* Don't reschedule poke is MM service doesn't exist. */ - if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) - && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { - - /* Setup timeout to relaunch */ - schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL); - } - - g_error_free (error); - } else - g_variant_unref (result); - - /* Balance refcount */ - g_object_unref (self); -} - -static void -modem_manager_poke (NMModemManager *self) -{ - /* If there is no current owner right away, ensure we poke to get one */ - g_dbus_connection_call (self->_priv.dbus_connection, - "org.freedesktop.ModemManager1", - "/org/freedesktop/ModemManager1", - DBUS_INTERFACE_PEER, - "Ping", - NULL, /* inputs */ - NULL, /* outputs */ - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* cancellable */ - (GAsyncReadyCallback)modem_manager_poke_cb, /* callback */ - g_object_ref (self)); /* user_data */ -} +/*****************************************************************************/ static void -modem_manager_check_name_owner (NMModemManager *self) +bus_get_ready (GObject *source, + GAsyncResult *res, + gpointer user_data) { - gs_free gchar *name_owner = NULL; + NMModemManager *self; + NMModemManagerPrivate *priv; + gs_free_error GError *error = NULL; + GDBusConnection *connection; - name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->_priv.modem_manager)); - if (name_owner) { - /* Available! */ - modem_manager_available (self); + connection = g_bus_get_finish (res, &error); + if ( !connection + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - } - - /* If the lifecycle is not managed by systemd, poke */ - if (!sd_booted ()) - modem_manager_poke (self); -} - -static void -manager_new_ready (GObject *source, - GAsyncResult *res, - NMModemManager *self) -{ - /* Note we always get an extra reference to self here */ - GError *error = NULL; + self = NM_MODEM_MANAGER (user_data); + priv = NM_MODEM_MANAGER_GET_PRIVATE (self); - g_return_if_fail (!self->_priv.modem_manager); - - self->_priv.modem_manager = mm_manager_new_finish (res, &error); - if (!self->_priv.modem_manager) { - /* We're not really supposed to get any error here. If we do get one, - * though, just re-schedule the MMManager creation after some time. - * During this period, name-owner changes won't be followed. */ - nm_log_warn (LOGD_MB, "error creating ModemManager client: %s", error->message); - g_error_free (error); - /* Setup timeout to relaunch */ - schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL); - } else { - /* Setup signals in the GDBusObjectManagerClient */ - self->_priv.mm_name_owner_changed_id = - g_signal_connect (self->_priv.modem_manager, - "notify::name-owner", - G_CALLBACK (modem_manager_name_owner_changed), - self); - self->_priv.mm_object_added_id = - g_signal_connect (self->_priv.modem_manager, - "object-added", - G_CALLBACK (modem_object_added), - self); - self->_priv.mm_object_removed_id = - g_signal_connect (self->_priv.modem_manager, - "object-removed", - G_CALLBACK (modem_object_removed), - self); - - modem_manager_check_name_owner (self); - } - - /* Balance refcount */ - g_object_unref (self); -} - -static void -ensure_modem_manager (NMModemManager *self) -{ - g_assert (self->_priv.dbus_connection); - - /* Create the GDBusObjectManagerClient. We do not request to autostart, as - * we don't really want the MMManager creation to fail. We can always poke - * later on if we want to request the autostart */ - if (!self->_priv.modem_manager) { - mm_manager_new (self->_priv.dbus_connection, - G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, - NULL, - (GAsyncReadyCallback)manager_new_ready, - g_object_ref (self)); + if (!connection) { + _LOGW ("error getting bus connection: %s", error->message); return; } - /* If already available, recheck name owner! */ - modem_manager_check_name_owner (self); -} + priv->dbus_connection = connection; -static gboolean -mm_launch_cb (NMModemManager *self) -{ - self->_priv.mm_launch_id = 0; - ensure_modem_manager (self); - return G_SOURCE_REMOVE; + modm_ensure_manager (self); +#if WITH_OFONO + ofono_init_proxy (self); +#endif } -static void -schedule_modem_manager_relaunch (NMModemManager *self, - guint n_seconds) -{ - /* No need to pass an extra reference to self; timeout/idle will be - * cancelled if the object gets disposed. */ - if (n_seconds) - self->_priv.mm_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)mm_launch_cb, self); - else - self->_priv.mm_launch_id = g_idle_add ((GSourceFunc)mm_launch_cb, self); -} +/*****************************************************************************/ static void -bus_get_ready (GObject *source, - GAsyncResult *res, - gpointer user_data) +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) { - gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data); - gs_free_error GError *error = NULL; - - self->_priv.dbus_connection = g_bus_get_finish (res, &error); - if (!self->_priv.dbus_connection) { - nm_log_warn (LOGD_MB, "error getting bus connection: %s", error->message); - return; + NMModemManager *self = NM_MODEM_MANAGER (object); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_NAME_OWNER: + g_value_set_string (value, priv->modm.proxy_name_owner); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - /* Got the bus, ensure clients */ - ensure_modem_manager (self); -#if WITH_OFONO - ensure_ofono_client (self); -#endif } /*****************************************************************************/ @@ -564,36 +838,50 @@ bus_get_ready (GObject *source, static void nm_modem_manager_init (NMModemManager *self) { - self->_priv.modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + priv->modems = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + + priv->main_cancellable = g_cancellable_new (); g_bus_get (G_BUS_TYPE_SYSTEM, - NULL, - (GAsyncReadyCallback)bus_get_ready, - g_object_ref (self)); + priv->main_cancellable, + bus_get_ready, + self); } static void dispose (GObject *object) { NMModemManager *self = NM_MODEM_MANAGER (object); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); - nm_clear_g_source (&self->_priv.mm_launch_id); + nm_clear_g_cancellable (&priv->main_cancellable); + nm_clear_g_cancellable (&priv->modm.poke_cancellable); - clear_modem_manager (self); + nm_clear_g_source (&priv->modm.relaunch_id); + + nm_clear_g_cancellable (&priv->modm.proxy_cancellable); + g_clear_object (&priv->modm.proxy); + nm_clear_g_free (&priv->modm.proxy_name_owner); + + modm_clear_manager (self); #if WITH_OFONO - if (self->_priv.ofono_proxy) { - g_signal_handlers_disconnect_by_func (self->_priv.ofono_proxy, ofono_name_owner_changed, self); - g_signal_handlers_disconnect_by_func (self->_priv.ofono_proxy, ofono_signal_cb, self); - g_clear_object (&self->_priv.ofono_proxy); + if (priv->ofono.proxy) { + g_signal_handlers_disconnect_by_func (priv->ofono.proxy, ofono_name_owner_changed, self); + g_signal_handlers_disconnect_by_func (priv->ofono.proxy, ofono_signal_cb, self); + g_clear_object (&priv->ofono.proxy); } + nm_clear_g_cancellable (&priv->ofono.cancellable); #endif - g_clear_object (&self->_priv.dbus_connection); + g_clear_object (&priv->dbus_connection); - if (self->_priv.modems) { - g_hash_table_foreach_remove (self->_priv.modems, remove_one_modem, object); - g_hash_table_destroy (self->_priv.modems); + if (priv->modems) { + g_hash_table_foreach_remove (priv->modems, remove_one_modem, object); + g_hash_table_destroy (priv->modems); + priv->modems = NULL; } G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object); @@ -605,6 +893,15 @@ nm_modem_manager_class_init (NMModemManagerClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = dispose; + object_class->get_property = get_property; + + obj_properties[PROP_NAME_OWNER] = + g_param_spec_string (NM_MODEM_MANAGER_NAME_OWNER, "", "", + NULL, + G_PARAM_READABLE + | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[MODEM_ADDED] = g_signal_new (NM_MODEM_MANAGER_MODEM_ADDED, diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h index 65594dfa..5f913083 100644 --- a/src/devices/wwan/nm-modem-manager.h +++ b/src/devices/wwan/nm-modem-manager.h @@ -34,9 +34,22 @@ #define NM_MODEM_MANAGER_MODEM_ADDED "modem-added" +#define NM_MODEM_MANAGER_NAME_OWNER "name-owner" + +#define NM_MODEM_MANAGER_MM_DBUS_SERVICE "org.freedesktop.ModemManager1" +#define NM_MODEM_MANAGER_MM_DBUS_PATH "/org/freedesktop/ModemManager1" +#define NM_MODEM_MANAGER_MM_DBUS_INTERFACE "org.freedesktop.ModemManager1" + typedef struct _NMModemManager NMModemManager; typedef struct _NMModemManagerClass NMModemManagerClass; GType nm_modem_manager_get_type (void); +NMModemManager *nm_modem_manager_get (void); + +void nm_modem_manager_name_owner_ref (NMModemManager *self); +void nm_modem_manager_name_owner_unref (NMModemManager *self); + +const char *nm_modem_manager_name_owner_get (NMModemManager *self); + #endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */ diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 52b335c7..8b3fc2e8 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -46,6 +46,11 @@ typedef struct { GDBusProxy *context_proxy; GDBusProxy *sim_proxy; + GCancellable *modem_proxy_cancellable; + GCancellable *connman_proxy_cancellable; + GCancellable *context_proxy_cancellable; + GCancellable *sim_proxy_cancellable; + GError *property_error; char *context_path; @@ -99,22 +104,6 @@ G_DEFINE_TYPE (NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM) /*****************************************************************************/ -static gboolean -ip_string_to_network_address (const gchar *str, - guint32 *out) -{ - guint32 addr = 0; - gboolean success = FALSE; - - if (!str || inet_pton (AF_INET, str, &addr) != 1) - addr = 0; - else - success = TRUE; - - *out = (guint32)addr; - return success; -} - static void get_capabilities (NMModem *_self, NMDeviceModemCapabilities *modem_caps, @@ -165,29 +154,17 @@ typedef struct { static void disconnect_context_complete (DisconnectContext *ctx) { - g_simple_async_result_complete_in_idle (ctx->result); if (ctx->cancellable) g_object_unref (ctx->cancellable); - g_object_unref (ctx->result); + if (ctx->result) { + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + } g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } static gboolean -disconnect_context_complete_if_cancelled (DisconnectContext *ctx) -{ - GError *error = NULL; - - if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - disconnect_context_complete (ctx); - return TRUE; - } - - return FALSE; -} - -static gboolean disconnect_finish (NMModem *self, GAsyncResult *result, GError **error) @@ -196,25 +173,25 @@ disconnect_finish (NMModem *self, } static void -disconnect_done (GDBusProxy *proxy, - GAsyncResult *result, - gpointer user_data) +disconnect_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { DisconnectContext *ctx = (DisconnectContext*) user_data; NMModemOfono *self = ctx->self; - GError *error = NULL; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v = NULL; - g_dbus_proxy_call_finish (proxy, result, &error); + v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - _LOGD ("disconnect cancelled"); + if (ctx->result) + g_simple_async_result_take_error (ctx->result, g_steal_pointer (&error)); + disconnect_context_complete (ctx); return; } - if (error) { - if (ctx->warn) - _LOGW ("failed to disconnect modem: %s", error->message); - g_clear_error (&error); - } + if (error && ctx->warn) + _LOGW ("failed to disconnect modem: %s", error->message); _LOGD ("modem disconnected"); @@ -233,18 +210,15 @@ disconnect (NMModem *modem, NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); DisconnectContext *ctx; NMModemState state = nm_modem_get_state (NM_MODEM (self)); + GError *error = NULL; _LOGD ("warn: %s modem_state: %s", warn ? "TRUE" : "FALSE", nm_modem_state_to_string (state)); - if (state != NM_MODEM_STATE_CONNECTED) - return; - - ctx = g_slice_new (DisconnectContext); + ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); ctx->warn = warn; - if (callback) { ctx->result = g_simple_async_result_new (G_OBJECT (self), callback, @@ -252,9 +226,28 @@ disconnect (NMModem *modem, disconnect); } - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - if (disconnect_context_complete_if_cancelled (ctx)) + if (state != NM_MODEM_STATE_CONNECTED) { + if (ctx->result) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + ("modem is currently not connected")); + g_simple_async_result_take_error (ctx->result, error); + } + disconnect_context_complete (ctx); return; + } + + if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { + if (ctx->result) + g_simple_async_result_take_error (ctx->result, error); + else + g_clear_error (&error); + disconnect_context_complete (ctx); + return; + } + + ctx->cancellable = nm_g_object_ref (cancellable); nm_modem_set_state (NM_MODEM (self), NM_MODEM_STATE_DISCONNECTING, @@ -267,8 +260,8 @@ disconnect (NMModem *modem, g_variant_new ("b", warn)), G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, - (GAsyncReadyCallback) disconnect_done, + ctx->cancellable, + disconnect_done, ctx); } @@ -375,22 +368,35 @@ sim_property_changed (GDBusProxy *proxy, } static void -sim_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +sim_get_properties_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - GError *error = NULL; - GVariant *v_properties, *v_dict, *v; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v_properties = NULL; + gs_unref_variant GVariant *v_dict = NULL; + GVariant *v; GVariantIter i; const char *property; - v_properties = _nm_dbus_proxy_call_finish (proxy, + v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, G_VARIANT_TYPE ("(a{sv})"), &error); + if ( !v_properties + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + g_clear_object (&priv->sim_proxy_cancellable); + if (!v_properties) { g_dbus_error_strip_remote_error (error); _LOGW ("error getting sim properties: %s", error->message); - g_error_free (error); return; } @@ -418,9 +424,49 @@ sim_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ handle_sim_property (NULL, property, v, self); g_variant_unref (v); } +} - g_variant_unref (v_dict); - g_variant_unref (v_properties); +static void +_sim_proxy_new_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + if (!proxy) { + _LOGW ("failed to create SimManager proxy: %s", error->message); + g_clear_object (&priv->sim_proxy_cancellable); + return; + } + + priv->sim_proxy = proxy; + + /* Watch for custom ofono PropertyChanged signals */ + _nm_dbus_signal_connect (priv->sim_proxy, + "PropertyChanged", + G_VARIANT_TYPE ("(sv)"), + G_CALLBACK (sim_property_changed), + self); + + g_dbus_proxy_call (priv->sim_proxy, + "GetProperties", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + priv->sim_proxy_cancellable, + sim_get_properties_done, + self); } static void @@ -430,47 +476,30 @@ handle_sim_iface (NMModemOfono *self, gboolean found) _LOGD ("SimManager interface %sfound", found ? "" : "not "); - if (!found && priv->sim_proxy) { + if (!found && (priv->sim_proxy || priv->sim_proxy_cancellable)) { _LOGI ("SimManager interface disappeared"); - g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self)); - g_clear_object (&priv->sim_proxy); + nm_clear_g_cancellable (&priv->sim_proxy_cancellable); + if (priv->sim_proxy) { + g_signal_handlers_disconnect_by_data (priv->sim_proxy, self); + g_clear_object (&priv->sim_proxy); + } g_clear_pointer (&priv->imsi, g_free); update_modem_state (self); - } else if (found && !priv->sim_proxy) { - GError *error = NULL; - + } else if (found && (!priv->sim_proxy && !priv->sim_proxy_cancellable)) { _LOGI ("found new SimManager interface"); - priv->sim_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES - | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, /* GDBusInterfaceInfo */ - OFONO_DBUS_SERVICE, - nm_modem_get_path (NM_MODEM (self)), - OFONO_DBUS_INTERFACE_SIM_MANAGER, - NULL, /* GCancellable */ - &error); - if (priv->sim_proxy == NULL) { - _LOGW ("failed to create SimManager proxy: %s", error->message); - g_error_free (error); - return; - } - - /* Watch for custom ofono PropertyChanged signals */ - _nm_dbus_signal_connect (priv->sim_proxy, - "PropertyChanged", - G_VARIANT_TYPE ("(sv)"), - G_CALLBACK (sim_property_changed), - self); - - g_dbus_proxy_call (priv->sim_proxy, - "GetProperties", - NULL, - G_DBUS_CALL_FLAGS_NONE, - 20000, - NULL, - (GAsyncReadyCallback) sim_get_properties_done, - g_object_ref (self)); + priv->sim_proxy_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, /* GDBusInterfaceInfo */ + OFONO_DBUS_SERVICE, + nm_modem_get_path (NM_MODEM (self)), + OFONO_DBUS_INTERFACE_SIM_MANAGER, + priv->sim_proxy_cancellable, /* GCancellable */ + _sim_proxy_new_cb, + self); } } @@ -514,22 +543,35 @@ connman_property_changed (GDBusProxy *proxy, } static void -connman_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +connman_get_properties_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - GError *error = NULL; - GVariant *v_properties, *v_dict, *v; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v_properties = NULL; + gs_unref_variant GVariant *v_dict = NULL; + GVariant *v; GVariantIter i; const char *property; - v_properties = _nm_dbus_proxy_call_finish (proxy, - result, - G_VARIANT_TYPE ("(a{sv})"), - &error); + v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), + result, + G_VARIANT_TYPE ("(a{sv})"), + &error); + if ( !v_properties + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + g_clear_object (&priv->connman_proxy_cancellable); + if (!v_properties) { g_dbus_error_strip_remote_error (error); _LOGW ("error getting connman properties: %s", error->message); - g_error_free (error); return; } @@ -549,9 +591,48 @@ connman_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer u handle_connman_property (NULL, property, v, self); g_variant_unref (v); } +} + +static void +_connman_proxy_new_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; - g_variant_unref (v_dict); - g_variant_unref (v_properties); + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + if (!proxy) { + _LOGW ("failed to create ConnectionManager proxy: %s", error->message); + g_clear_object (&priv->connman_proxy_cancellable); + return; + } + + priv->connman_proxy = proxy; + + _nm_dbus_signal_connect (priv->connman_proxy, + "PropertyChanged", + G_VARIANT_TYPE ("(sv)"), + G_CALLBACK (connman_property_changed), + self); + + g_dbus_proxy_call (priv->connman_proxy, + "GetProperties", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + priv->connman_proxy_cancellable, + connman_get_properties_done, + self); } static void @@ -561,11 +642,13 @@ handle_connman_iface (NMModemOfono *self, gboolean found) _LOGD ("ConnectionManager interface %sfound", found ? "" : "not "); - if (!found && priv->connman_proxy) { + if (!found && (priv->connman_proxy || priv->connman_proxy_cancellable)) { _LOGI ("ConnectionManager interface disappeared"); - - g_signal_handlers_disconnect_by_data (priv->connman_proxy, NM_MODEM_OFONO (self)); - g_clear_object (&priv->connman_proxy); + nm_clear_g_cancellable (&priv->connman_proxy_cancellable); + if (priv->connman_proxy) { + g_signal_handlers_disconnect_by_data (priv->connman_proxy, self); + g_clear_object (&priv->connman_proxy); + } /* The connection manager proxy disappeared, we should * consider the modem disabled. @@ -573,41 +656,21 @@ handle_connman_iface (NMModemOfono *self, gboolean found) priv->gprs_attached = FALSE; update_modem_state (self); - } else if (found && !priv->connman_proxy) { - GError *error = NULL; - + } else if (found && (!priv->connman_proxy && !priv->connman_proxy_cancellable)) { _LOGI ("found new ConnectionManager interface"); - priv->connman_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES - | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, /* GDBusInterfaceInfo */ - OFONO_DBUS_SERVICE, - nm_modem_get_path (NM_MODEM (self)), - OFONO_DBUS_INTERFACE_CONNECTION_MANAGER, - NULL, /* GCancellable */ - &error); - if (priv->connman_proxy == NULL) { - _LOGW ("failed to create ConnectionManager proxy: %s", error->message); - g_error_free (error); - return; - } - - /* Watch for custom ofono PropertyChanged signals */ - _nm_dbus_signal_connect (priv->connman_proxy, - "PropertyChanged", - G_VARIANT_TYPE ("(sv)"), - G_CALLBACK (connman_property_changed), - self); - - g_dbus_proxy_call (priv->connman_proxy, - "GetProperties", - NULL, - G_DBUS_CALL_FLAGS_NONE, - 20000, - NULL, - (GAsyncReadyCallback) connman_get_properties_done, - g_object_ref (self)); + priv->connman_proxy_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, /* GDBusInterfaceInfo */ + OFONO_DBUS_SERVICE, + nm_modem_get_path (NM_MODEM (self)), + OFONO_DBUS_INTERFACE_CONNECTION_MANAGER, + priv->connman_proxy_cancellable, + _connman_proxy_new_cb, + NULL); } } @@ -667,22 +730,35 @@ modem_property_changed (GDBusProxy *proxy, } static void -modem_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +modem_get_properties_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - GError *error = NULL; - GVariant *v_properties, *v_dict, *v; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v_properties = NULL; + gs_unref_variant GVariant *v_dict = NULL; + GVariant *v; GVariantIter i; const char *property; - v_properties = _nm_dbus_proxy_call_finish (proxy, + v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, G_VARIANT_TYPE ("(a{sv})"), &error); + if ( !v_properties + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + g_clear_object (&priv->modem_proxy_cancellable); + if (!v_properties) { g_dbus_error_strip_remote_error (error); _LOGW ("error getting modem properties: %s", error->message); - g_error_free (error); return; } @@ -706,21 +782,29 @@ modem_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer use handle_modem_property (NULL, property, v, self); g_variant_unref (v); } - - g_variant_unref (v_dict); - g_variant_unref (v_properties); } static void -stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +stage1_prepare_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); - GError *error = NULL; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v = NULL; + + v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + g_clear_object (&priv->context_proxy_cancellable); g_clear_pointer (&priv->connect_properties, g_hash_table_destroy); - g_dbus_proxy_call_finish (proxy, result, &error); if (error) { _LOGW ("connection failed: %s", error->message); @@ -732,8 +816,6 @@ stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data * leading to the connection being disabled, and a 5m * timeout... */ - - g_clear_error (&error); } } @@ -751,6 +833,7 @@ context_property_changed (GDBusProxy *proxy, const gchar *s, *addr_s; const gchar **array, **iter; guint32 address_network, gateway_network; + guint32 ip4_route_table, ip4_route_metric; guint prefix = 0; _LOGD ("PropertyChanged: %s", property); @@ -809,15 +892,20 @@ context_property_changed (GDBusProxy *proxy, * 'Interface'. * * This needs discussion with upstream. + * + * FIXME: it is no longer allowed to omit the ifindex for NMIP4Config instances. + * This is broken. */ - priv->ip4_config = nm_ip4_config_new (0); + priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + 0); /* TODO: simply if/else error logic! */ if (g_variant_lookup (v_dict, "Address", "&s", &addr_s)) { _LOGD ("Address: %s", addr_s); - if (ip_string_to_network_address (addr_s, &address_network)) { + if ( addr_s + && nm_utils_parse_inaddr_bin (AF_INET, addr_s, &address_network)) { addr.address = address_network; addr.addr_source = NM_IP_CONFIG_SOURCE_WWAN; } else { @@ -833,7 +921,8 @@ context_property_changed (GDBusProxy *proxy, if (g_variant_lookup (v_dict, "Netmask", "&s", &s)) { _LOGD ("Netmask: %s", s); - if (s && ip_string_to_network_address (s, &address_network)) { + if ( s + && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { prefix = nm_utils_ip4_netmask_to_prefix (address_network); if (prefix > 0) addr.plen = prefix; @@ -850,15 +939,30 @@ context_property_changed (GDBusProxy *proxy, nm_ip4_config_add_address (priv->ip4_config, &addr); - if (g_variant_lookup (v_dict, "Gateway", "&s", &s)) { - if (s && ip_string_to_network_address (s, &gateway_network)) { - _LOGI ("Gateway: %s", s); - nm_ip4_config_set_gateway (priv->ip4_config, gateway_network); - } else { + if ( g_variant_lookup (v_dict, "Gateway", "&s", &s) + && s) { + + if (!nm_utils_parse_inaddr_bin (AF_INET, s, &gateway_network)) { _LOGW ("invalid 'Gateway': %s", s); goto out; } - nm_ip4_config_set_gateway (priv->ip4_config, gateway_network); + + nm_modem_get_route_parameters (NM_MODEM (self), + &ip4_route_table, + &ip4_route_metric, + NULL, + NULL); + { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_WWAN, + .gateway = gateway_network, + .table_coerced = nm_platform_route_table_coerce (ip4_route_table), + .metric = ip4_route_metric, + }; + + _LOGI ("Gateway: %s", s); + nm_ip4_config_add_route (priv->ip4_config, &r, NULL); + } } else { _LOGW ("Settings 'Gateway' missing"); goto out; @@ -867,7 +971,8 @@ context_property_changed (GDBusProxy *proxy, if (g_variant_lookup (v_dict, "DomainNameServers", "^a&s", &array)) { if (array) { for (iter = array; *iter; iter++) { - if (ip_string_to_network_address (*iter, &address_network) && address_network > 0) { + if ( nm_utils_parse_inaddr_bin (AF_INET, *iter, &address_network) + && address_network) { _LOGI ("DNS: %s", *iter); nm_ip4_config_add_nameserver (priv->ip4_config, address_network); } else { @@ -889,16 +994,25 @@ context_property_changed (GDBusProxy *proxy, if (g_variant_lookup (v_dict, "MessageProxy", "&s", &s)) { _LOGI ("MessageProxy: %s", s); - if (s && ip_string_to_network_address (s, &address_network)) { - NMPlatformIP4Route mms_route; - - mms_route.network = address_network; - mms_route.plen = 32; - mms_route.gateway = gateway_network; - - mms_route.metric = 1; - - nm_ip4_config_add_route (priv->ip4_config, &mms_route); + if ( s + && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + nm_modem_get_route_parameters (NM_MODEM (self), + &ip4_route_table, + &ip4_route_metric, + NULL, + NULL); + + { + const NMPlatformIP4Route mms_route = { + .network = address_network, + .plen = 32, + .gateway = gateway_network, + .table_coerced = nm_platform_route_table_coerce (ip4_route_table), + .metric = ip4_route_metric, + }; + + nm_ip4_config_add_route (priv->ip4_config, &mms_route, NULL); + } } else { _LOGW ("invalid MessageProxy: %s", s); } @@ -946,21 +1060,33 @@ static_stage3_ip4_config_start (NMModem *modem, } static void -context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +context_proxy_new_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); - GError *error = NULL; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; - priv->context_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); - if (error) { + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + if (!proxy) { _LOGE ("failed to create ofono ConnectionContext DBus proxy: %s", error->message); + g_clear_object (&priv->context_proxy_cancellable); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY); return; } + priv->context_proxy = proxy; + if (!priv->gprs_attached) { + g_clear_object (&priv->context_proxy_cancellable); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER); return; @@ -972,7 +1098,6 @@ context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat */ g_clear_object (&priv->ip4_config); - /* Watch for custom ofono PropertyChanged signals */ _nm_dbus_signal_connect (priv->context_proxy, "PropertyChanged", G_VARIANT_TYPE ("(sv)"), @@ -986,9 +1111,9 @@ context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat g_variant_new ("b", TRUE)), G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, - (GAsyncReadyCallback) stage1_prepare_done, - g_object_ref (self)); + priv->context_proxy_cancellable, + stage1_prepare_done, + self); } static void @@ -998,16 +1123,20 @@ do_context_activate (NMModemOfono *self) g_return_if_fail (NM_IS_MODEM_OFONO (self)); + nm_clear_g_cancellable (&priv->context_proxy_cancellable); g_clear_object (&priv->context_proxy); + + priv->context_proxy_cancellable = g_cancellable_new (); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, OFONO_DBUS_SERVICE, priv->context_path, OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT, - NULL, - (GAsyncReadyCallback) context_proxy_new_cb, - g_object_ref (self)); + priv->context_proxy_cancellable, + context_proxy_new_cb, + self); } static GHashTable * @@ -1018,7 +1147,7 @@ create_connect_properties (NMConnection *connection) const char *str; setting = nm_connection_get_setting_gsm (connection); - properties = g_hash_table_new (g_str_hash, g_str_equal); + properties = g_hash_table_new (nm_str_hash, g_str_equal); str = nm_setting_gsm_get_apn (setting); if (str) @@ -1081,19 +1210,29 @@ act_stage1_prepare (NMModem *modem, } static void -modem_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +modem_proxy_new_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data); - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); - GError *error = NULL; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; - priv->modem_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); - if (error) { + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_MODEM_OFONO (user_data); + priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + if (!proxy) { _LOGE ("failed to create ofono modem DBus proxy: %s", error->message); + g_clear_object (&priv->modem_proxy_cancellable); return; } - /* Watch for custom ofono PropertyChanged signals */ + priv->modem_proxy = proxy; + _nm_dbus_signal_connect (priv->modem_proxy, "PropertyChanged", G_VARIANT_TYPE ("(sv)"), @@ -1105,9 +1244,9 @@ modem_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) NULL, G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, - (GAsyncReadyCallback) modem_get_properties_done, - g_object_ref (self)); + priv->modem_proxy_cancellable, + modem_get_properties_done, + self); } /*****************************************************************************/ @@ -1121,6 +1260,9 @@ static void constructed (GObject *object) { NMModemOfono *self = NM_MODEM_OFONO (object); + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); + + priv->modem_proxy_cancellable = g_cancellable_new (); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, @@ -1128,9 +1270,11 @@ constructed (GObject *object) OFONO_DBUS_SERVICE, nm_modem_get_path (NM_MODEM (self)), OFONO_DBUS_INTERFACE_MODEM, - NULL, - (GAsyncReadyCallback) modem_proxy_new_cb, - g_object_ref (self)); + priv->modem_proxy_cancellable, + modem_proxy_new_cb, + self); + + G_OBJECT_CLASS (nm_modem_ofono_parent_class)->constructed (object); } NMModem * @@ -1163,6 +1307,11 @@ dispose (GObject *object) NMModemOfono *self = NM_MODEM_OFONO (object); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); + nm_clear_g_cancellable (&priv->modem_proxy_cancellable); + nm_clear_g_cancellable (&priv->connman_proxy_cancellable); + nm_clear_g_cancellable (&priv->context_proxy_cancellable); + nm_clear_g_cancellable (&priv->sim_proxy_cancellable); + if (priv->connect_properties) { g_hash_table_destroy (priv->connect_properties); priv->connect_properties = NULL; @@ -1171,15 +1320,22 @@ dispose (GObject *object) g_clear_object (&priv->ip4_config); if (priv->modem_proxy) { - g_signal_handlers_disconnect_by_data (priv->modem_proxy, NM_MODEM_OFONO (self)); + g_signal_handlers_disconnect_by_data (priv->modem_proxy, self); g_clear_object (&priv->modem_proxy); } - g_clear_object (&priv->connman_proxy); - g_clear_object (&priv->context_proxy); + if (priv->connman_proxy) { + g_signal_handlers_disconnect_by_data (priv->connman_proxy, self); + g_clear_object (&priv->connman_proxy); + } + + if (priv->context_proxy) { + g_signal_handlers_disconnect_by_data (priv->context_proxy, self); + g_clear_object (&priv->context_proxy); + } if (priv->sim_proxy) { - g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self)); + g_signal_handlers_disconnect_by_data (priv->sim_proxy, self); g_clear_object (&priv->sim_proxy); } diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 6494b849..77495b62 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -26,13 +26,13 @@ #include <fcntl.h> #include <string.h> #include <termios.h> +#include <linux/rtnetlink.h> #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-setting-connection.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "nm-route-manager.h" #include "nm-netns.h" #include "nm-act-request.h" #include "nm-ip4-config.h" @@ -98,6 +98,11 @@ typedef struct _NMModemPrivate { guint32 mm_ip_timeout; + guint32 ip4_route_table; + guint32 ip4_route_metric; + guint32 ip6_route_table; + guint32 ip6_route_metric; + /* PPP stats */ guint32 in_bytes; guint32 out_bytes; @@ -108,6 +113,46 @@ G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) #define NM_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMModem, NM_IS_MODEM) /*****************************************************************************/ + +#define _NMLOG_PREFIX_BUFLEN 64 +#define _NMLOG_PREFIX_NAME "modem" +#define _NMLOG_DOMAIN LOGD_MB + +static const char * +_nmlog_prefix (char *prefix, NMModem *self) +{ + const char *uuid; + int c; + + if (!self) + return ""; + + uuid = nm_modem_get_uid (self); + + if (uuid) { + char pp[_NMLOG_PREFIX_BUFLEN - 5]; + + c = g_snprintf (prefix, _NMLOG_PREFIX_BUFLEN, "[%s]", + nm_strquote (pp, sizeof (pp), uuid)); + } else + c = g_snprintf (prefix, _NMLOG_PREFIX_BUFLEN, "[%p]", self); + nm_assert (c < _NMLOG_PREFIX_BUFLEN); + + return prefix; +} + +#define _NMLOG(level, ...) \ + G_STMT_START { \ + char _prefix[_NMLOG_PREFIX_BUFLEN]; \ + \ + nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + _nmlog_prefix (_prefix, (self)) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } G_STMT_END + +/*****************************************************************************/ /* State/enabled/connected */ static const char *state_table[] = { @@ -151,11 +196,10 @@ nm_modem_set_state (NMModem *self, priv->prev_state = NM_MODEM_STATE_UNKNOWN; if (new_state != old_state) { - nm_log_info (LOGD_MB, "(%s): modem state changed, '%s' --> '%s' (reason: %s)\n", - nm_modem_get_uid (self), - nm_modem_state_to_string (old_state), - nm_modem_state_to_string (new_state), - reason ? reason : "none"); + _LOGI ("modem state changed, '%s' --> '%s' (reason: %s)", + nm_modem_state_to_string (old_state), + nm_modem_state_to_string (new_state), + reason ? reason : "none"); priv->state = new_state; _notify (self, PROP_STATE); @@ -181,24 +225,20 @@ nm_modem_set_mm_enabled (NMModem *self, NMModemState prev_state = priv->state; if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) { - nm_log_dbg (LOGD_MB, "(%s): cannot enable modem: already enabled", - nm_modem_get_uid (self)); + _LOGD ("cannot enable modem: already enabled"); return; } if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) { - nm_log_dbg (LOGD_MB, "(%s): cannot disable modem: already disabled", - nm_modem_get_uid (self)); + _LOGD ("cannot disable modem: already disabled"); return; } if (priv->state <= NM_MODEM_STATE_INITIALIZING) { - nm_log_dbg (LOGD_MB, "(%s): cannot enable/disable modem: initializing or failed", - nm_modem_get_uid (self)); + _LOGD ("cannot enable/disable modem: initializing or failed"); return; } else if (priv->state == NM_MODEM_STATE_LOCKED) { /* Don't try to enable if the modem is locked since that will fail */ - nm_log_warn (LOGD_MB, "(%s): cannot enable/disable modem: locked", - nm_modem_get_uid (self)); + _LOGW ("cannot enable/disable modem: locked"); /* Try to unlock the modem if it's being enabled */ if (enabled) @@ -468,7 +508,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, } if (!num || dns_workaround) { - nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers"); + _LOGW ("compensating for invalid PPP-provided nameservers"); nm_ip4_config_reset_nameservers (config); nm_ip4_config_add_nameserver (config, good_dns1); nm_ip4_config_add_nameserver (config, good_dns2); @@ -561,9 +601,8 @@ ppp_stage3_ip_config_start (NMModem *self, /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, * use the default one (30s) */ if (priv->mm_ip_timeout > 0) { - nm_log_info (LOGD_PPP, "(%s): using modem-specified IP timeout: %u seconds", - nm_modem_get_uid (self), - priv->mm_ip_timeout); + _LOGI ("using modem-specified IP timeout: %u seconds", + priv->mm_ip_timeout); ip_timeout = priv->mm_ip_timeout; } @@ -577,12 +616,18 @@ ppp_stage3_ip_config_start (NMModem *self, priv->ppp_manager = nm_ppp_manager_create (priv->data_port, &error); + if (priv->ppp_manager) { + nm_ppp_manager_set_route_parameters (priv->ppp_manager, + priv->ip4_route_table, + priv->ip4_route_metric, + priv->ip6_route_table, + priv->ip6_route_metric); + } + if ( !priv->ppp_manager || !nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, ip_timeout, baud_override, &error)) { - nm_log_err (LOGD_PPP, "(%s): error starting PPP: %s", - nm_modem_get_uid (self), - error->message); + _LOGE ("error starting PPP: %s", error->message); g_error_free (error); g_clear_object (&priv->ppp_manager); @@ -621,7 +666,7 @@ nm_modem_stage3_ip4_config_start (NMModem *self, const char *method; NMActStageReturn ret; - nm_log_dbg (LOGD_MB, "ip4_config_start"); + _LOGD ("ip4_config_start"); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); @@ -640,9 +685,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self, return NM_ACT_STAGE_RETURN_SUCCESS; if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) { - nm_log_warn (LOGD_MB | LOGD_IP4, - "(%s): unhandled WWAN IPv4 method '%s'; will fail", - nm_modem_get_uid (self), method); + _LOGW ("unhandled WWAN IPv4 method '%s'; will fail", + method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -653,15 +697,15 @@ nm_modem_stage3_ip4_config_start (NMModem *self, ret = ppp_stage3_ip_config_start (self, req, out_failure_reason); break; case NM_MODEM_IP_METHOD_STATIC: - nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_STATIC"); + _LOGD ("MODEM_IP_METHOD_STATIC"); ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, out_failure_reason); break; case NM_MODEM_IP_METHOD_AUTO: - nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_AUTO"); + _LOGD ("MODEM_IP_METHOD_AUTO"); ret = device_class->act_stage3_ip4_config_start (device, NULL, out_failure_reason); break; default: - nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self)); + _LOGI ("IPv4 configuration disabled"); ret = NM_ACT_STAGE_RETURN_IP_FAIL; break; } @@ -676,13 +720,15 @@ nm_modem_ip4_pre_commit (NMModem *modem, { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (modem); + nm_modem_set_route_parameters_from_device (modem, device); + /* If the modem has an ethernet-type data interface (ie, not PPP and thus * not point-to-point) and IP config has a /32 prefix, then we assume that * ARP will be pointless and we turn it off. */ if ( priv->ip4_method == NM_MODEM_IP_METHOD_STATIC || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0); + const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (config); g_assert (address); if (address->plen == 32) @@ -698,7 +744,8 @@ nm_modem_emit_ip6_config_result (NMModem *self, GError *error) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); - guint i, num; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *addr; gboolean do_slaac = TRUE; if (error) { @@ -710,11 +757,7 @@ nm_modem_emit_ip6_config_result (NMModem *self, /* If the IPv6 configuration only included a Link-Local address, then * we have to run SLAAC to get the full IPv6 configuration. */ - num = nm_ip6_config_get_num_addresses (config); - g_assert (num > 0); - for (i = 0; i < num; i++) { - const NMPlatformIP6Address * addr = nm_ip6_config_get_address (config, i); - + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, config, &addr) { if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { if (!priv->iid.id) priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1]; @@ -757,9 +800,8 @@ nm_modem_stage3_ip6_config_start (NMModem *self, return NM_ACT_STAGE_RETURN_IP_DONE; if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) { - nm_log_warn (LOGD_MB | LOGD_IP6, - "(%s): unhandled WWAN IPv6 method '%s'; will fail", - nm_modem_get_uid (self), method); + _LOGW ("unhandled WWAN IPv6 method '%s'; will fail", + method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -778,7 +820,7 @@ nm_modem_stage3_ip6_config_start (NMModem *self, ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, out_failure_reason); break; default: - nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self)); + _LOGI ("IPv6 configuration disabled"); ret = NM_ACT_STAGE_RETURN_IP_FAIL; break; } @@ -854,7 +896,7 @@ modem_secrets_cb (NMActRequest *req, return; if (error) - nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message); + _LOGW ("modem-secrets: %s", error->message); g_signal_emit (self, signals[AUTH_RESULT], 0, error); } @@ -975,17 +1017,15 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) str = nm_setting_gsm_get_device_id (s_gsm); if (str) { if (!priv->device_id) { - nm_log_dbg (LOGD_MB, "(%s): %s/%s has device-id, device does not", - priv->uid, - nm_connection_get_uuid (connection), - nm_connection_get_id (connection)); + _LOGD ("%s/%s has device-id, device does not", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); return FALSE; } if (strcmp (str, priv->device_id)) { - nm_log_dbg (LOGD_MB, "(%s): %s/%s device-id mismatch", - priv->uid, - nm_connection_get_uuid (connection), - nm_connection_get_id (connection)); + _LOGD ("%s/%s device-id mismatch", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); return FALSE; } } @@ -998,10 +1038,9 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) str = nm_setting_gsm_get_sim_id (s_gsm); if (str && priv->sim_id) { if (strcmp (str, priv->sim_id)) { - nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-id mismatch", - priv->uid, - nm_connection_get_uuid (connection), - nm_connection_get_id (connection)); + _LOGD ("%s/%s sim-id mismatch", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); return FALSE; } } @@ -1009,10 +1048,9 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) str = nm_setting_gsm_get_sim_operator_id (s_gsm); if (str && priv->sim_operator_id) { if (strcmp (str, priv->sim_operator_id)) { - nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-operator-id mismatch", - priv->uid, - nm_connection_get_uuid (connection), - nm_connection_get_id (connection)); + _LOGD ("%s/%s sim-operator-id mismatch", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); return FALSE; } } @@ -1069,10 +1107,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device) priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { ifindex = nm_device_get_ip_ifindex (device); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (nm_device_get_netns (device)), - ifindex); - nm_platform_address_flush (nm_device_get_platform (device), ifindex); - nm_platform_link_set_down (nm_device_get_platform (device), ifindex); + NMPlatform *platform = nm_device_get_platform (device); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); + nm_platform_link_set_down (platform, ifindex); } } } @@ -1149,12 +1188,12 @@ ppp_manager_stop_ready (NMPPPManager *ppp_manager, GAsyncResult *res, DeactivateContext *ctx) { + NMModem *self = ctx->self; GError *error = NULL; if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) { - nm_log_warn (LOGD_MB, "(%s): cannot stop PPP manager: %s", - nm_modem_get_uid (ctx->self), - error->message); + _LOGW ("cannot stop PPP manager: %s", + error->message); g_simple_async_result_take_error (ctx->result, error); deactivate_context_complete (ctx); return; @@ -1168,7 +1207,8 @@ ppp_manager_stop_ready (NMPPPManager *ppp_manager, static void deactivate_step (DeactivateContext *ctx) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (ctx->self); + NMModem *self = ctx->self; + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); GError *error = NULL; /* Check cancellable in each step */ @@ -1187,7 +1227,7 @@ deactivate_step (DeactivateContext *ctx) if (priv->ppp_manager) ctx->ppp_manager = g_object_ref (priv->ppp_manager); /* Run cleanup */ - NM_MODEM_GET_CLASS (ctx->self)->deactivate_cleanup (ctx->self, ctx->device); + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device); ctx->step++; /* fall through */ case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP: @@ -1203,16 +1243,15 @@ deactivate_step (DeactivateContext *ctx) /* fall through */ case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT: /* Disconnect asynchronously */ - NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self, - FALSE, - ctx->cancellable, - (GAsyncReadyCallback) disconnect_ready, - ctx); + NM_MODEM_GET_CLASS (self)->disconnect (self, + FALSE, + ctx->cancellable, + (GAsyncReadyCallback) disconnect_ready, + ctx); return; case DEACTIVATE_CONTEXT_STEP_LAST: - nm_log_dbg (LOGD_MB, "(%s): modem deactivation finished", - nm_modem_get_uid (ctx->self)); + _LOGD ("modem deactivation finished"); deactivate_context_complete (ctx); return; } @@ -1377,6 +1416,76 @@ nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid) /*****************************************************************************/ void +nm_modem_get_route_parameters (NMModem *self, + guint32 *out_ip4_route_table, + guint32 *out_ip4_route_metric, + guint32 *out_ip6_route_table, + guint32 *out_ip6_route_metric) +{ + NMModemPrivate *priv; + + g_return_if_fail (NM_IS_MODEM (self)); + + priv = NM_MODEM_GET_PRIVATE (self); + NM_SET_OUT (out_ip4_route_table, priv->ip4_route_table); + NM_SET_OUT (out_ip4_route_metric, priv->ip4_route_metric); + NM_SET_OUT (out_ip6_route_table, priv->ip6_route_table); + NM_SET_OUT (out_ip6_route_metric, priv->ip6_route_metric); +} + +void +nm_modem_set_route_parameters (NMModem *self, + guint32 ip4_route_table, + guint32 ip4_route_metric, + guint32 ip6_route_table, + guint32 ip6_route_metric) +{ + NMModemPrivate *priv; + + g_return_if_fail (NM_IS_MODEM (self)); + + priv = NM_MODEM_GET_PRIVATE (self); + if ( priv->ip4_route_table != ip4_route_table + || priv->ip4_route_metric != ip4_route_metric + || priv->ip6_route_table != ip6_route_table + || priv->ip6_route_metric != ip6_route_metric) { + priv->ip4_route_table = ip4_route_table; + priv->ip4_route_metric = ip4_route_metric; + priv->ip6_route_table = ip6_route_table; + priv->ip6_route_metric = ip6_route_metric; + + _LOGT ("route-parameters: table-v4: %u, metric-v4: %u, table-v6: %u, metric-v6: %u", + priv->ip4_route_table, + priv->ip4_route_metric, + priv->ip6_route_table, + priv->ip6_route_metric); + } + + if (priv->ppp_manager) { + nm_ppp_manager_set_route_parameters (priv->ppp_manager, + priv->ip4_route_table, + priv->ip4_route_metric, + priv->ip6_route_table, + priv->ip6_route_metric); + } +} + +void +nm_modem_set_route_parameters_from_device (NMModem *self, + NMDevice *device) +{ + g_return_if_fail (NM_IS_DEVICE (device)); + + nm_modem_set_route_parameters (self, + nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_metric (device, AF_INET6)); +} + +/*****************************************************************************/ + +void nm_modem_get_capabilities (NMModem *self, NMDeviceModemCapabilities *modem_caps, NMDeviceModemCapabilities *current_caps) @@ -1451,6 +1560,7 @@ set_property (GObject *object, guint prop_id, case PROP_PATH: /* construct-only */ priv->path = g_value_dup_string (value); + g_return_if_fail (priv->path); break; case PROP_DRIVER: /* construct-only */ @@ -1509,40 +1619,27 @@ set_property (GObject *object, guint prop_id, static void nm_modem_init (NMModem *self) { + NMModemPrivate *priv; + self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM, NMModemPrivate); + priv = self->_priv; + + priv->ip4_route_table = RT_TABLE_MAIN; + priv->ip4_route_metric = 700; + priv->ip6_route_table = RT_TABLE_MAIN; + priv->ip6_route_metric = 700; } -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) +static void +constructed (GObject *object) { - GObject *object; NMModemPrivate *priv; - object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type, - n_construct_params, - construct_params); - if (!object) - return NULL; - - priv = NM_MODEM_GET_PRIVATE ((NMModem *) object); + G_OBJECT_CLASS (nm_modem_parent_class)->constructed (object); - if (!priv->data_port && !priv->control_port) { - nm_log_err (LOGD_PLATFORM, "neither modem command nor data interface provided"); - goto err; - } + priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object)); - if (!priv->path) { - nm_log_err (LOGD_PLATFORM, "D-Bus path not provided"); - goto err; - } - - return object; - -err: - g_object_unref (object); - return NULL; + g_return_if_fail (priv->data_port || priv->control_port); } /*****************************************************************************/ @@ -1552,10 +1649,7 @@ dispose (GObject *object) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object); - if (priv->act_request) { - g_object_unref (priv->act_request); - priv->act_request = NULL; - } + g_clear_object (&priv->act_request); G_OBJECT_CLASS (nm_modem_parent_class)->dispose (object); } @@ -1584,7 +1678,7 @@ nm_modem_class_init (NMModemClass *klass) g_type_class_add_private (object_class, sizeof (NMModemPrivate)); - object_class->constructor = constructor; + object_class->constructed = constructed; object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index a50727a9..9546e4a1 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -105,10 +105,12 @@ typedef enum { /*< underscore_name=nm_modem_state >*/ struct _NMModemPrivate; -typedef struct { +struct _NMModem { GObject parent; struct _NMModemPrivate *_priv; -} NMModem; +}; + +typedef struct _NMModem NMModem; typedef struct { GObjectClass parent; @@ -185,6 +187,21 @@ gboolean nm_modem_complete_connection (NMModem *self, const GSList *existing_connections, GError **error); +void nm_modem_get_route_parameters (NMModem *self, + guint32 *out_ip4_route_table, + guint32 *out_ip4_route_metric, + guint32 *out_ip6_route_table, + guint32 *out_ip6_route_metric); + +void nm_modem_set_route_parameters (NMModem *self, + guint32 ip4_route_table, + guint32 ip4_route_metric, + guint32 ip6_route_table, + guint32 ip6_route_metric); + +void nm_modem_set_route_parameters_from_device (NMModem *modem, + NMDevice *device); + NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem, NMActRequest *req, NMDeviceStateReason *out_failure_reason); diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index fa4c8dbb..663102de 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -127,8 +127,8 @@ start (NMDeviceFactory *factory) NMWwanFactory *self = NM_WWAN_FACTORY (factory); NMWwanFactoryPrivate *priv = NM_WWAN_FACTORY_GET_PRIVATE (self); - priv->mm = g_object_new (NM_TYPE_MODEM_MANAGER, NULL); - g_assert (priv->mm); + priv->mm = g_object_ref (nm_modem_manager_get ()); + g_signal_connect (priv->mm, NM_MODEM_MANAGER_MODEM_ADDED, G_CALLBACK (modem_added_cb), diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h index 1047a7d7..1ed47170 100644 --- a/src/dhcp/nm-dhcp-client-logging.h +++ b/src/dhcp/nm-dhcp-client-logging.h @@ -23,6 +23,23 @@ #include "nm-dhcp-client.h" +static inline NMLogDomain +_nm_dhcp_client_get_domain (NMDhcpClient *self) +{ + if (self) { + switch (nm_dhcp_client_get_addr_family (self)) { + case AF_INET: + return LOGD_DHCP4; + case AF_INET6: + return LOGD_DHCP6; + default: + nm_assert_not_reached (); + break; + } + } + return LOGD_DHCP; +} + #define _NMLOG_PREFIX_NAME "dhcp" #define _NMLOG_DOMAIN LOGD_DHCP #define _NMLOG(level, ...) \ @@ -38,9 +55,7 @@ if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \ NMDhcpClient *_self = (NMDhcpClient *) (self); \ const char *__ifname = _self ? nm_dhcp_client_get_iface (_self) : NULL; \ - const NMLogDomain _domain = !_self \ - ? LOGD_DHCP \ - : (nm_dhcp_client_get_ipv6 (_self) ? LOGD_DHCP6 : LOGD_DHCP4); \ + const NMLogDomain _domain = _nm_dhcp_client_get_domain (_self); \ \ nm_log (_level, _domain, __ifname, NULL, \ "%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 0906f5be..20ea092f 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -29,6 +29,10 @@ #include <stdio.h> #include <stdlib.h> #include <uuid/uuid.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-random-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" @@ -48,33 +52,36 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_MULTI_IDX, + PROP_ADDR_FAMILY, PROP_IFACE, PROP_IFINDEX, PROP_HWADDR, - PROP_IPV6, PROP_UUID, - PROP_PRIORITY, + PROP_ROUTE_TABLE, + PROP_ROUTE_METRIC, PROP_TIMEOUT, ); typedef struct _NMDhcpClientPrivate { + NMDedupMultiIndex *multi_idx; char * iface; - int ifindex; GByteArray * hwaddr; - gboolean ipv6; char * uuid; - guint32 priority; - guint32 timeout; GByteArray * duid; GBytes * client_id; char * hostname; - gboolean use_fqdn; - - NMDhcpState state; pid_t pid; guint timeout_id; guint watch_id; - gboolean info_only; + int addr_family; + int ifindex; + guint32 route_table; + guint32 route_metric; + guint32 timeout; + NMDhcpState state; + bool info_only:1; + bool use_fqdn:1; } NMDhcpClientPrivate; G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) @@ -91,6 +98,14 @@ nm_dhcp_client_get_pid (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid; } +NMDedupMultiIndex * +nm_dhcp_client_get_multi_idx (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->multi_idx; +} + const char * nm_dhcp_client_get_iface (NMDhcpClient *self) { @@ -107,12 +122,12 @@ nm_dhcp_client_get_ifindex (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->ifindex; } -gboolean -nm_dhcp_client_get_ipv6 (NMDhcpClient *self) +int +nm_dhcp_client_get_addr_family (NMDhcpClient *self) { - g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), AF_UNSPEC); - return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6; + return NM_DHCP_CLIENT_GET_PRIVATE (self)->addr_family; } const char * @@ -140,11 +155,19 @@ nm_dhcp_client_get_hw_addr (NMDhcpClient *self) } guint32 -nm_dhcp_client_get_priority (NMDhcpClient *self) +nm_dhcp_client_get_route_table (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), RT_TABLE_MAIN); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->route_table; +} + +guint32 +nm_dhcp_client_get_route_metric (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), G_MAXUINT32); - return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority; + return NM_DHCP_CLIENT_GET_PRIVATE (self)->route_metric; } guint32 @@ -303,8 +326,8 @@ nm_dhcp_client_set_state (NMDhcpClient *self, watch_cleanup (self); if (new_state == NM_DHCP_STATE_BOUND) { - g_assert ( (priv->ipv6 && NM_IS_IP6_CONFIG (ip_config)) - || (!priv->ipv6 && NM_IS_IP4_CONFIG (ip_config))); + g_assert ( (priv->addr_family == AF_INET && NM_IS_IP4_CONFIG (ip_config)) + || (priv->addr_family == AF_INET6 && NM_IS_IP6_CONFIG (ip_config))); g_assert (options); } else { g_assert (ip_config == NULL); @@ -319,7 +342,8 @@ nm_dhcp_client_set_state (NMDhcpClient *self, if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND)) return; - if (priv->ipv6 && new_state == NM_DHCP_STATE_BOUND) { + if ( priv->addr_family == AF_INET6 + && new_state == NM_DHCP_STATE_BOUND) { char *start, *iaid; iaid = g_hash_table_lookup (options, "iaid"); @@ -392,6 +416,10 @@ nm_dhcp_client_start_timeout (NMDhcpClient *self) /* Set up a timeout on the transaction to kill it after the timeout */ g_assert (priv->timeout_id == 0); + + if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) + return; + priv->timeout_id = g_timeout_add_seconds (priv->timeout, transaction_timeout, self); @@ -426,10 +454,13 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->pid == -1, FALSE); - g_return_val_if_fail (priv->ipv6 == FALSE, FALSE); + g_return_val_if_fail (priv->addr_family == AF_INET, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); - _LOGI ("activation: beginning transaction (timeout in %d seconds)", priv->timeout); + if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) + _LOGI ("activation: beginning transaction (no timeout)"); + else + _LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); if (dhcp_client_id) tmp = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id); @@ -451,8 +482,6 @@ generate_duid_from_machine_id (void) gsize sumlen = sizeof (buffer); const guint16 duid_type = g_htons (4); uuid_t uuid; - GRand *generator; - guint i; gs_free char *machine_id_s = NULL; gs_free char *str = NULL; @@ -468,10 +497,7 @@ generate_duid_from_machine_id (void) "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate " "DHCPv6 DUID; creating non-persistent random DUID."); - generator = g_rand_new (); - for (i = 0; i < sizeof (buffer) / sizeof (guint32); i++) - ((guint32 *) buffer)[i] = g_rand_int (generator); - g_rand_free (generator); + nm_utils_random_bytes (buffer, sizeof (buffer)); } /* Generate a DHCP Unique Identifier for DHCPv6 using the @@ -528,7 +554,7 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->pid == -1, FALSE); - g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); + g_return_val_if_fail (priv->addr_family == AF_INET6, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); /* If we don't have one yet, read the default DUID for this DHCPv6 client @@ -544,8 +570,10 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, priv->info_only = info_only; - _LOGI ("activation: beginning transaction (timeout in %d seconds)", - priv->timeout); + if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) + _LOGI ("activation: beginning transaction (no timeout)"); + else + _LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, dhcp_anycast_addr, @@ -744,7 +772,7 @@ nm_dhcp_client_handle_event (gpointer unused, GVariant *value; /* Copy options */ - str_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + str_options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); g_variant_iter_init (&iter, options); while (g_variant_iter_next (&iter, "{&sv}", &name, &value)) { maybe_add_option (self, str_options, name, value); @@ -763,18 +791,20 @@ nm_dhcp_client_handle_event (gpointer unused, /* Create the IP config */ g_warn_if_fail (g_hash_table_size (str_options)); if (g_hash_table_size (str_options)) { - if (priv->ipv6) { - prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options); - ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->ifindex, + if (priv->addr_family == AF_INET) { + ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (nm_dhcp_client_get_multi_idx (self), + priv->ifindex, priv->iface, str_options, - priv->priority, - priv->info_only); + priv->route_table, + priv->route_metric); } else { - ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (priv->ifindex, + prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options); + ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (nm_dhcp_client_get_multi_idx (self), + priv->ifindex, priv->iface, str_options, - priv->priority); + priv->info_only); } } } @@ -822,14 +852,14 @@ get_property (GObject *object, guint prop_id, case PROP_HWADDR: g_value_set_boxed (value, priv->hwaddr); break; - case PROP_IPV6: - g_value_set_boolean (value, priv->ipv6); + case PROP_ADDR_FAMILY: + g_value_set_int (value, priv->addr_family); break; case PROP_UUID: g_value_set_string (value, priv->uuid); break; - case PROP_PRIORITY: - g_value_set_uint (value, priv->priority); + case PROP_ROUTE_METRIC: + g_value_set_uint (value, priv->route_metric); break; case PROP_TIMEOUT: g_value_set_uint (value, priv->timeout); @@ -847,6 +877,13 @@ set_property (GObject *object, guint prop_id, NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFACE: /* construct-only */ priv->iface = g_value_dup_string (value); @@ -860,19 +897,26 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->hwaddr = g_value_dup_boxed (value); break; - case PROP_IPV6: + case PROP_ADDR_FAMILY: /* construct-only */ - priv->ipv6 = g_value_get_boolean (value); + priv->addr_family = g_value_get_int (value); + if (!NM_IN_SET (priv->addr_family, AF_INET, AF_INET6)) + g_return_if_reached (); break; case PROP_UUID: /* construct-only */ priv->uuid = g_value_dup_string (value); break; - case PROP_PRIORITY: + case PROP_ROUTE_TABLE: + /* construct-only */ + priv->route_table = g_value_get_uint (value); + break; + case PROP_ROUTE_METRIC: /* construct-only */ - priv->priority = g_value_get_uint (value); + priv->route_metric = g_value_get_uint (value); break; case PROP_TIMEOUT: + /* construct-only */ priv->timeout = g_value_get_uint (value); break; default: @@ -924,6 +968,8 @@ dispose (GObject *object) } G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); + + priv->multi_idx = nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -940,6 +986,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) client_class->stop = stop; client_class->get_duid = get_duid; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_DHCP_CLIENT_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IFACE] = g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, "", "", NULL, @@ -958,11 +1010,11 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IPV6] = - g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, "", "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ADDR_FAMILY] = + g_param_spec_int (NM_DHCP_CLIENT_ADDR_FAMILY, "", "", + 0, G_MAXINT, AF_UNSPEC, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_UUID] = g_param_spec_string (NM_DHCP_CLIENT_UUID, "", "", @@ -970,15 +1022,21 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_PRIORITY] = - g_param_spec_uint (NM_DHCP_CLIENT_PRIORITY, "", "", + obj_properties[PROP_ROUTE_TABLE] = + g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "", + 0, G_MAXUINT32, RT_TABLE_MAIN, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ROUTE_METRIC] = + g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_METRIC, "", "", 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_TIMEOUT] = g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, "", "", - 0, G_MAXUINT, 45, + 1, G_MAXINT32, NM_DHCP_TIMEOUT_DEFAULT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index e41a59a2..02804002 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -24,6 +24,9 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" +#define NM_DHCP_TIMEOUT_DEFAULT ((guint32) 45) /* default DHCP timeout, in seconds */ +#define NM_DHCP_TIMEOUT_INFINITY G_MAXINT32 + #define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ()) #define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient)) #define NM_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) @@ -32,16 +35,19 @@ #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) #define NM_DHCP_CLIENT_INTERFACE "iface" +#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" #define NM_DHCP_CLIENT_IFINDEX "ifindex" #define NM_DHCP_CLIENT_HWADDR "hwaddr" -#define NM_DHCP_CLIENT_IPV6 "ipv6" #define NM_DHCP_CLIENT_UUID "uuid" -#define NM_DHCP_CLIENT_PRIORITY "priority" +#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" +#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" #define NM_DHCP_CLIENT_TIMEOUT "timeout" +#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed" #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated" + typedef enum { NM_DHCP_STATE_UNKNOWN = 0, NM_DHCP_STATE_BOUND, /* new lease or lease changed */ @@ -101,21 +107,25 @@ typedef struct { GType nm_dhcp_client_get_type (void); +struct _NMDedupMultiIndex *nm_dhcp_client_get_multi_idx (NMDhcpClient *self); + pid_t nm_dhcp_client_get_pid (NMDhcpClient *self); +int nm_dhcp_client_get_addr_family (NMDhcpClient *self); + const char *nm_dhcp_client_get_iface (NMDhcpClient *self); int nm_dhcp_client_get_ifindex (NMDhcpClient *self); -gboolean nm_dhcp_client_get_ipv6 (NMDhcpClient *self); - const char *nm_dhcp_client_get_uuid (NMDhcpClient *self); const GByteArray *nm_dhcp_client_get_duid (NMDhcpClient *self); const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self); -guint32 nm_dhcp_client_get_priority (NMDhcpClient *self); +guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self); + +guint32 nm_dhcp_client_get_route_metric (NMDhcpClient *self); guint32 nm_dhcp_client_get_timeout (NMDhcpClient *self); @@ -173,13 +183,16 @@ typedef struct { GType (*get_type)(void); const char *name; const char *(*get_path) (void); - GSList *(*get_lease_ip_configs) (const char *iface, + GSList *(*get_lease_ip_configs) (struct _NMDedupMultiIndex *multi_idx, + int addr_family, + const char *iface, int ifindex, const char *uuid, - gboolean ipv6, - guint32 default_route_metric); + guint32 route_table, + guint32 route_metric); } NMDhcpClientFactory; +extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd; extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal; diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 6a1b6865..e63e6a86 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -25,6 +25,8 @@ #include <ctype.h> #include <arpa/inet.h> +#include "nm-utils/nm-dedup-multi.h" + #include "nm-dhcp-utils.h" #include "nm-ip4-config.h" #include "nm-utils.h" @@ -162,13 +164,9 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean static void add_hostname6 (GString *str, const char *hostname) { - /* dhclient only supports the fqdn.fqdn for DHCPv6 and requires a fully- - * qualified name for this option, so we must require one here too. - */ - if (hostname && strchr (hostname, '.')) { + if (hostname) { g_string_append_printf (str, FQDN_FORMAT "\n", hostname); g_string_append (str, - "send fqdn.encoded on;\n" "send fqdn.server-update on;\n"); g_string_append_c (str, '\n'); } @@ -261,7 +259,7 @@ read_interface (const char *line, char *interface, guint size) char * nm_dhcp_dhclient_create_config (const char *interface, - gboolean is_ip6, + int addr_family, GBytes *client_id, const char *anycast_addr, const char *hostname, @@ -277,6 +275,7 @@ nm_dhcp_dhclient_create_config (const char *interface, int i; g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL); new_contents = g_string_new (_("# Created by NetworkManager\n")); fqdn_opts = g_ptr_array_sized_new (5); @@ -397,18 +396,18 @@ nm_dhcp_dhclient_create_config (const char *interface, g_string_append_printf (new_contents, "timeout %u;\n", timeout); } - if (is_ip6) { - add_hostname6 (new_contents, hostname); - add_request (reqs, "dhcp6.name-servers"); - add_request (reqs, "dhcp6.domain-search"); - add_request (reqs, "dhcp6.client-id"); - } else { + if (addr_family == AF_INET) { add_ip4_config (new_contents, client_id, hostname, use_fqdn); add_request (reqs, "rfc3442-classless-static-routes"); add_request (reqs, "ms-classless-static-routes"); add_request (reqs, "static-routes"); add_request (reqs, "wpad"); add_request (reqs, "ntp-servers"); + } else { + add_hostname6 (new_contents, hostname); + add_request (reqs, "dhcp6.name-servers"); + add_request (reqs, "dhcp6.domain-search"); + add_request (reqs, "dhcp6.client-id"); } if (reset_reqlist) @@ -686,24 +685,30 @@ lease_validity_span (const char *str_expire, GDateTime *now) /** * nm_dhcp_dhclient_read_lease_ip_configs: + * @multi_idx: the multi index instance for the ip config object + * @addr_family: whether to read IPv4 or IPv6 leases * @iface: the interface name to match leases with * @ifindex: interface index of @iface + * @route_table: the route table for the default route. + * @route_metric: the route metric for the default route. * @contents: the contents of a dhclient leasefile - * @ipv6: whether to read IPv4 or IPv6 leases * @now: the current UTC date/time; pass %NULL to automatically use current * UTC time. Testcases may need a different value for 'now' * * Reads dhclient leases from @contents and parses them into either - * #NMIP4Config or #NMIP6Config objects depending on the value of @ipv6. + * #NMIP4Config or #NMIP6Config objects depending on the value of @addr_family. * - * Returns: a #GSList of #NMIP4Config objects (if @ipv6 is %FALSE) or a list of - * #NMIP6Config objects (if @ipv6 is %TRUE) containing the lease data. + * Returns: a #GSList of #NMIP4Config objects (if @addr_family is %AF_INET) or a list of + * #NMIP6Config objects (if @addr_family is %AF_INET6) containing the lease data. */ GSList * -nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, +nm_dhcp_dhclient_read_lease_ip_configs (NMDedupMultiIndex *multi_idx, + int addr_family, + const char *iface, int ifindex, + guint32 route_table, + guint32 route_metric, const char *contents, - gboolean ipv6, GDateTime *now) { GSList *parsed = NULL, *iter, *leases = NULL; @@ -712,6 +717,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, gint32 now_monotonic_ts; g_return_val_if_fail (contents != NULL, NULL); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); split = g_strsplit_set (contents, "\n\r", -1); if (!split) @@ -733,7 +739,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, g_hash_table_destroy (hash); } - hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); } else if (hash && strlen (*line)) add_lease_option (hash, *line); } @@ -804,15 +810,25 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, /* Get default netmask for the IP according to appropriate class. */ if (!address.plen) - address.plen = nm_utils_ip4_get_default_prefix (address.address); + address.plen = _nm_utils_ip4_get_default_prefix (address.address); address.timestamp = now_monotonic_ts; address.lifetime = address.preferred = expiry; address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - ip4 = nm_ip4_config_new (ifindex); + ip4 = nm_ip4_config_new (multi_idx, ifindex); nm_ip4_config_add_address (ip4, &address); - nm_ip4_config_set_gateway (ip4, gw); + + { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gw, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + + nm_ip4_config_add_route (ip4, &r, NULL); + } value = g_hash_table_lookup (hash, "option domain-name-servers"); if (value) { diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index 2268890b..94de1963 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -23,7 +23,7 @@ #include "nm-setting-ip6-config.h" char *nm_dhcp_dhclient_create_config (const char *interface, - gboolean is_ip6, + int addr_family, GBytes *client_id, const char *anycast_addr, const char *hostname, @@ -43,10 +43,13 @@ gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, const char *escaped_duid, GError **error); -GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, +GSList *nm_dhcp_dhclient_read_lease_ip_configs (struct _NMDedupMultiIndex *multi_idx, + int addr_family, + const char *iface, int ifindex, + guint32 route_table, + guint32 route_metric, const char *contents, - gboolean ipv6, GDateTime *now); GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path); diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index f20158c6..74d920a8 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -38,6 +38,8 @@ #include <arpa/inet.h> #include <ctype.h> +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" #include "nm-dhcp-dhclient-utils.h" #include "nm-dhcp-manager.h" @@ -47,6 +49,15 @@ /*****************************************************************************/ +static const char * +_addr_family_to_path_part (int addr_family) +{ + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + return (addr_family == AF_INET6) ? "6" : ""; +} + +/*****************************************************************************/ + #define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type ()) #define NM_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient)) #define NM_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass)) @@ -92,9 +103,9 @@ nm_dhcp_dhclient_get_path (void) /** * get_dhclient_leasefile(): + * @addr_family: AF_INET or AF_INET6 * @iface: the interface name of the device on which DHCP will be done * @uuid: the connection UUID to which the returned lease should belong - * @ipv6: %TRUE for IPv6, %FALSE for IPv4 * @out_preferred_path: on return, the "most preferred" leasefile path * * Returns the path of an existing leasefile (if any) for this interface and @@ -104,16 +115,16 @@ nm_dhcp_dhclient_get_path (void) * Returns: an existing leasefile, or %NULL if no matching leasefile could be found */ static char * -get_dhclient_leasefile (const char *iface, +get_dhclient_leasefile (int addr_family, + const char *iface, const char *uuid, - gboolean ipv6, char **out_preferred_path) { char *path; /* /var/lib/NetworkManager is the preferred leasefile path */ path = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s-%s.lease", - ipv6 ? "6" : "", + _addr_family_to_path_part (addr_family), uuid, iface); if (out_preferred_path) @@ -131,14 +142,14 @@ get_dhclient_leasefile (const char *iface, /* Old Debian, SUSE, and Mandriva location */ g_free (path); path = g_strdup_printf (LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease", - ipv6 ? "6" : "", uuid, iface); + _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; /* Old Red Hat and Fedora location */ g_free (path); path = g_strdup_printf (LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease", - ipv6 ? "6" : "", uuid, iface); + _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; @@ -148,37 +159,36 @@ get_dhclient_leasefile (const char *iface, } static GSList * -nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, +nm_dhcp_dhclient_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, + int addr_family, + const char *iface, int ifindex, const char *uuid, - gboolean ipv6, - guint32 default_route_metric) + guint32 route_table, + guint32 route_metric) { - char *contents = NULL; - char *leasefile; - GSList *leases = NULL; + gs_free char *contents = NULL; + gs_free char *leasefile = NULL; - leasefile = get_dhclient_leasefile (iface, uuid, FALSE, NULL); + leasefile = get_dhclient_leasefile (addr_family, iface, uuid, NULL); if (!leasefile) return NULL; if ( g_file_test (leasefile, G_FILE_TEST_EXISTS) && g_file_get_contents (leasefile, &contents, NULL, NULL) && contents - && contents[0]) - leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, ifindex, contents, ipv6, NULL); - - g_free (leasefile); - g_free (contents); - - return leases; + && contents[0]) { + return nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, addr_family, iface, ifindex, + route_table, route_metric, contents, NULL); + } + return NULL; } static gboolean merge_dhclient_config (NMDhcpDhclient *self, + int addr_family, const char *iface, const char *conf_file, - gboolean is_ip6, GBytes *client_id, const char *anycast_addr, const char *hostname, @@ -204,10 +214,7 @@ merge_dhclient_config (NMDhcpDhclient *self, } } - if (is_ip6 && hostname && !strchr (hostname, '.')) - _LOGW ("hostname is not a FQDN, it will be ignored"); - - new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, timeout, + new = nm_dhcp_dhclient_create_config (iface, addr_family, client_id, anycast_addr, hostname, timeout, use_fqdn, orig_path, orig, out_new_client_id); g_assert (new); success = g_file_set_contents (conf_file, new, -1, error); @@ -218,7 +225,7 @@ merge_dhclient_config (NMDhcpDhclient *self, } static char * -find_existing_config (NMDhcpDhclient *self, const char *iface, const char *uuid, gboolean ipv6) +find_existing_config (NMDhcpDhclient *self, int addr_family, const char *iface, const char *uuid) { char *path; @@ -227,20 +234,20 @@ find_existing_config (NMDhcpDhclient *self, const char *iface, const char *uuid, * or generic. */ if (uuid) { - path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", uuid); + path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", _addr_family_to_path_part (addr_family), uuid); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_free (path); } - path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface); + path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", _addr_family_to_path_part (addr_family), iface); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_free (path); - path = g_strdup_printf (NMCONFDIR "/dhclient%s.conf", ipv6 ? "6" : ""); + path = g_strdup_printf (NMCONFDIR "/dhclient%s.conf", _addr_family_to_path_part (addr_family)); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; @@ -254,25 +261,25 @@ find_existing_config (NMDhcpDhclient *self, const char *iface, const char *uuid, * which is then used by many other distributions. Some distributions * (including Fedora) don't even provide a default configuration file. */ - path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s-%s.conf", ipv6 ? "6" : "", iface); + path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s-%s.conf", _addr_family_to_path_part (addr_family), iface); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_free (path); - path = g_strdup_printf (SYSCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface); + path = g_strdup_printf (SYSCONFDIR "/dhclient%s-%s.conf", _addr_family_to_path_part (addr_family), iface); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_free (path); - path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s.conf", ipv6 ? "6" : ""); + path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s.conf", _addr_family_to_path_part (addr_family)); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; g_free (path); - path = g_strdup_printf (SYSCONFDIR "/dhclient%s.conf", ipv6 ? "6" : ""); + path = g_strdup_printf (SYSCONFDIR "/dhclient%s.conf", _addr_family_to_path_part (addr_family)); _LOGD ("looking for existing config %s", path); if (g_file_test (path, G_FILE_TEST_EXISTS)) return path; @@ -290,8 +297,8 @@ find_existing_config (NMDhcpDhclient *self, const char *iface, const char *uuid, */ static char * create_dhclient_config (NMDhcpDhclient *self, + int addr_family, const char *iface, - gboolean is_ip6, const char *uuid, GBytes *client_id, const char *dhcp_anycast_addr, @@ -306,17 +313,17 @@ create_dhclient_config (NMDhcpDhclient *self, g_return_val_if_fail (iface != NULL, NULL); - new = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s.conf", is_ip6 ? "6" : "", iface); + new = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s.conf", _addr_family_to_path_part (addr_family), iface); _LOGD ("creating composite dhclient config %s", new); - orig = find_existing_config (self, iface, uuid, is_ip6); + orig = find_existing_config (self, addr_family, iface, uuid); if (orig) _LOGD ("merging existing dhclient config %s", orig); else _LOGD ("no existing dhclient configuration to merge"); error = NULL; - success = merge_dhclient_config (self, iface, new, is_ip6, client_id, dhcp_anycast_addr, + success = merge_dhclient_config (self, addr_family, iface, new, client_id, dhcp_anycast_addr, hostname, timeout, use_fqdn, orig, out_new_client_id, &error); if (!success) { _LOGW ("error creating dhclient configuration: %s", error->message); @@ -343,14 +350,15 @@ dhclient_start (NMDhcpClient *client, GError *error = NULL; const char *iface, *uuid, *system_bus_address, *dhclient_path = NULL; char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL; - gboolean ipv6, success; + int addr_family; + gboolean success; char *escaped, *preferred_leasefile_path = NULL; g_return_val_if_fail (priv->pid_file == NULL, FALSE); iface = nm_dhcp_client_get_iface (client); uuid = nm_dhcp_client_get_uuid (client); - ipv6 = nm_dhcp_client_get_ipv6 (client); + addr_family = nm_dhcp_client_get_addr_family (client); dhclient_path = nm_dhcp_dhclient_get_path (); if (!dhclient_path) { @@ -359,8 +367,8 @@ dhclient_start (NMDhcpClient *client, } pid_file = g_strdup_printf (RUNSTATEDIR "/dhclient%s-%s.pid", - ipv6 ? "6" : "", - iface); + _addr_family_to_path_part (addr_family), + iface); /* Kill any existing dhclient from the pidfile */ binary_name = g_path_get_basename (dhclient_path); @@ -374,7 +382,7 @@ dhclient_start (NMDhcpClient *client, } g_free (priv->lease_file); - priv->lease_file = get_dhclient_leasefile (iface, uuid, ipv6, &preferred_leasefile_path); + priv->lease_file = get_dhclient_leasefile (addr_family, iface, uuid, &preferred_leasefile_path); if (!priv->lease_file) { /* No existing leasefile, dhclient will create one at the preferred path */ priv->lease_file = g_strdup (preferred_leasefile_path); @@ -400,7 +408,7 @@ dhclient_start (NMDhcpClient *client, g_free (preferred_leasefile_path); /* Save the DUID to the leasefile dhclient will actually use */ - if (ipv6) { + if (addr_family == AF_INET6) { escaped = nm_dhcp_dhclient_escape_duid (duid); success = nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error); g_free (escaped); @@ -424,7 +432,7 @@ dhclient_start (NMDhcpClient *client, if (release) g_ptr_array_add (argv, (gpointer) "-r"); - if (ipv6) { + if (addr_family == AF_INET6) { g_ptr_array_add (argv, (gpointer) "-6"); if (mode_opt) g_ptr_array_add (argv, (gpointer) mode_opt); @@ -507,7 +515,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last timeout = nm_dhcp_client_get_timeout (client); use_fqdn = nm_dhcp_client_get_use_fqdn (client); - priv->conf_file = create_dhclient_config (self, iface, FALSE, uuid, client_id, dhcp_anycast_addr, + priv->conf_file = create_dhclient_config (self, AF_INET, iface, uuid, client_id, dhcp_anycast_addr, hostname, timeout, use_fqdn, &new_client_id); if (priv->conf_file) { if (new_client_id) @@ -538,7 +546,7 @@ ip6_start (NMDhcpClient *client, hostname = nm_dhcp_client_get_hostname (client); timeout = nm_dhcp_client_get_timeout (client); - priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr, + priv->conf_file = create_dhclient_config (self, AF_INET6, iface, uuid, NULL, dhcp_anycast_addr, hostname, timeout, TRUE, NULL); if (!priv->conf_file) { _LOGW ("error creating dhclient configuration file"); @@ -605,9 +613,9 @@ get_duid (NMDhcpClient *client) GError *error = NULL; /* Look in interface-specific leasefile first for backwards compat */ - leasefile = get_dhclient_leasefile (nm_dhcp_client_get_iface (client), + leasefile = get_dhclient_leasefile (AF_INET6, + nm_dhcp_client_get_iface (client), nm_dhcp_client_get_uuid (client), - TRUE, NULL); if (leasefile) { _LOGD ("looking for DUID in '%s'", leasefile); diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c new file mode 100644 index 00000000..d7ddd194 --- /dev/null +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -0,0 +1,272 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-dhcpcanon.c - dhcpcanon specific hooks for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 juga <juga at riseup dot net> + */ + +#include "nm-default.h" + +#if WITH_DHCPCANON + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include "nm-utils.h" +#include "nm-dhcp-manager.h" +#include "NetworkManagerUtils.h" +#include "nm-dhcp-listener.h" +#include "nm-dhcp-client-logging.h" + +#define NM_TYPE_DHCP_DHCPCANON (nm_dhcp_dhcpcanon_get_type ()) +#define NM_DHCP_DHCPCANON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon)) +#define NM_DHCP_DHCPCANON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass)) +#define NM_IS_DHCP_DHCPCANON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCANON)) +#define NM_IS_DHCP_DHCPCANON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_DHCPCANON)) +#define NM_DHCP_DHCPCANON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass)) + +typedef struct _NMDhcpDhcpcanon NMDhcpDhcpcanon; +typedef struct _NMDhcpDhcpcanonClass NMDhcpDhcpcanonClass; + +static GType nm_dhcp_dhcpcanon_get_type (void); + +/*****************************************************************************/ + +typedef struct { + char *conf_file; + const char *def_leasefile; + char *lease_file; + char *pid_file; + NMDhcpListener *dhcp_listener; +} NMDhcpDhcpcanonPrivate; + +struct _NMDhcpDhcpcanon { + NMDhcpClient parent; + NMDhcpDhcpcanonPrivate _priv; +}; + +struct _NMDhcpDhcpcanonClass { + NMDhcpClientClass parent; +}; + +G_DEFINE_TYPE (NMDhcpDhcpcanon, nm_dhcp_dhcpcanon, NM_TYPE_DHCP_CLIENT) + +#define NM_DHCP_DHCPCANON_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcpDhcpcanon, NM_IS_DHCP_DHCPCANON) + +/*****************************************************************************/ + +static const char * +nm_dhcp_dhcpcanon_get_path (void) +{ + return nm_utils_find_helper ("dhcpcanon", DHCPCANON_PATH, NULL); +} + + +static gboolean +dhcpcanon_start (NMDhcpClient *client, + const char *mode_opt, + const GByteArray *duid, + gboolean release, + pid_t *out_pid, + int prefixes) +{ + NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); + NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); + GPtrArray *argv = NULL; + pid_t pid; + GError *error = NULL; + const char *iface, *system_bus_address, *dhcpcanon_path = NULL; + char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL; + int addr_family; + + g_return_val_if_fail (priv->pid_file == NULL, FALSE); + + iface = nm_dhcp_client_get_iface (client); + addr_family = nm_dhcp_client_get_addr_family (client); + dhcpcanon_path = nm_dhcp_dhcpcanon_get_path (); + _LOGD ("dhcpcanon_path: %s", dhcpcanon_path); + if (!dhcpcanon_path) { + _LOGW ("dhcpcanon could not be found"); + return FALSE; + } + + pid_file = g_strdup_printf (RUNSTATEDIR "/dhcpcanon%c-%s.pid", + nm_utils_addr_family_to_char (addr_family), + iface); + _LOGD ("pid_file: %s", pid_file); + + /* Kill any existing dhcpcanon from the pidfile */ + binary_name = g_path_get_basename (dhcpcanon_path); + nm_dhcp_client_stop_existing (pid_file, binary_name); + g_free (binary_name); + + argv = g_ptr_array_new (); + g_ptr_array_add (argv, (gpointer) dhcpcanon_path); + + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path); + + if (pid_file) { + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) pid_file); + } + + if (priv->conf_file) { + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) priv->conf_file); + } + + /* Usually the system bus address is well-known; but if it's supposed + * to be something else, we need to push it to dhcpcanon, since dhcpcanon + * sanitizes the environment it gives the action scripts. + */ + system_bus_address = getenv ("DBUS_SYSTEM_BUS_ADDRESS"); + if (system_bus_address) { + system_bus_address_env = g_strdup_printf ("DBUS_SYSTEM_BUS_ADDRESS=%s", system_bus_address); + g_ptr_array_add (argv, (gpointer) "-e"); + g_ptr_array_add (argv, (gpointer) system_bus_address_env); + } + + + g_ptr_array_add (argv, (gpointer) iface); + g_ptr_array_add (argv, NULL); + + cmd_str = g_strjoinv (" ", (gchar **) argv->pdata); + g_free (cmd_str); + + if (g_spawn_async (NULL, (char **) argv->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + nm_utils_setpgid, NULL, &pid, &error)) { + g_assert (pid > 0); + _LOGI ("dhcpcanon started with pid %d", pid); + nm_dhcp_client_watch_child (client, pid); + priv->pid_file = pid_file; + } else { + _LOGW ("dhcpcanon failed to start: '%s'", error->message); + g_error_free (error); + g_free (pid_file); + } + + g_ptr_array_free (argv, TRUE); + g_free (system_bus_address_env); + return pid > 0 ? TRUE : FALSE; +} + +static gboolean +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address) +{ + gboolean success = FALSE; + success = dhcpcanon_start (client, NULL, NULL, FALSE, NULL, 0); + return success; +} + +static gboolean +ip6_start (NMDhcpClient *client, + const char *dhcp_anycast_addr, + const struct in6_addr *ll_addr, + gboolean info_only, + NMSettingIP6ConfigPrivacy privacy, + const GByteArray *duid, + guint needed_prefixes) +{ + NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); + + _LOGW ("the dhcpcd backend does not support IPv6"); + return FALSE; +} +static void +stop (NMDhcpClient *client, gboolean release, const GByteArray *duid) +{ + NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); + NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); + + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release, duid); + + if (priv->pid_file) { + if (remove (priv->pid_file) == -1) + _LOGD ("could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno)); + g_free (priv->pid_file); + priv->pid_file = NULL; + } +} + +static void +state_changed (NMDhcpClient *client, + NMDhcpState state, + GObject *ip_config, + GHashTable *options) +{ + if (nm_dhcp_client_get_client_id (client)) + return; + if (state != NM_DHCP_STATE_BOUND) + return; +} + +/*****************************************************************************/ + +static void +nm_dhcp_dhcpcanon_init (NMDhcpDhcpcanon *self) +{ + NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); + + priv->dhcp_listener = g_object_ref (nm_dhcp_listener_get ()); + g_signal_connect (priv->dhcp_listener, + NM_DHCP_LISTENER_EVENT, + G_CALLBACK (nm_dhcp_client_handle_event), + self); +} + +static void +dispose (GObject *object) +{ + NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE ((NMDhcpDhcpcanon *) object); + + if (priv->dhcp_listener) { + g_signal_handlers_disconnect_by_func (priv->dhcp_listener, + G_CALLBACK (nm_dhcp_client_handle_event), + NM_DHCP_DHCPCANON (object)); + g_clear_object (&priv->dhcp_listener); + } + + nm_clear_g_free (&priv->pid_file); + + G_OBJECT_CLASS (nm_dhcp_dhcpcanon_parent_class)->dispose (object); +} + +static void +nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) +{ + NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcanon_class); + GObjectClass *object_class = G_OBJECT_CLASS (dhcpcanon_class); + + object_class->dispose = dispose; + + client_class->ip4_start = ip4_start; + client_class->ip6_start = ip6_start; + client_class->stop = stop; + client_class->state_changed = state_changed; +} + +const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { + .name = "dhcpcanon", + .get_type = nm_dhcp_dhcpcanon_get_type, + .get_path = nm_dhcp_dhcpcanon_get_path, + .get_lease_ip_configs = NULL, +}; + +#endif /* WITH_DHCPCANON */ diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c index ca697ab3..a0449816 100644 --- a/src/dhcp/nm-dhcp-listener.c +++ b/src/dhcp/nm-dhcp-listener.c @@ -31,6 +31,7 @@ #include "nm-dhcp-helper-api.h" #include "nm-dhcp-client.h" +#include "nm-dhcp-manager.h" #include "nm-core-internal.h" #include "nm-bus-manager.h" #include "NetworkManagerUtils.h" @@ -40,10 +41,13 @@ /*****************************************************************************/ -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[3] = { +const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { /* the order here matters, as we will try the plugins in this order to find * the first available plugin. */ +#if WITH_DHCPCANON + &_nm_dhcp_client_factory_dhcpcanon, +#endif #if WITH_DHCLIENT &_nm_dhcp_client_factory_dhclient, #endif @@ -135,49 +139,35 @@ get_option (GVariant *options, const char *key) } static void -_method_call (GDBusConnection *connection, - const char *sender, - const char *object_path, - const char *interface_name, - const char *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) +_method_call_handle (NMDhcpListener *self, + GVariant *parameters) { - NMDhcpListener *self = NM_DHCP_LISTENER (user_data); - char *iface = NULL; - char *pid_str = NULL; - char *reason = NULL; - gint pid; + gs_free char *iface = NULL; + gs_free char *pid_str = NULL; + gs_free char *reason = NULL; + gs_unref_variant GVariant *options; + int pid; gboolean handled = FALSE; - GVariant *options; - - if (!nm_streq0 (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME)) - g_return_if_reached (); - if (!nm_streq0 (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY)) - g_return_if_reached (); - if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) - g_return_if_reached (); g_variant_get (parameters, "(@a{sv})", &options); iface = get_option (options, "interface"); if (iface == NULL) { _LOGW ("dhcp-event: didn't have associated interface."); - goto out; + return; } pid_str = get_option (options, "pid"); pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1); if (pid == -1) { _LOGW ("dhcp-event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)"); - goto out; + return; } reason = get_option (options, "reason"); if (reason == NULL) { _LOGW ("dhcp-event: (pid %d) DHCP event didn't have a reason", pid); - goto out; + return; } g_signal_emit (self, signals[EVENT], 0, iface, pid, options, reason, &handled); @@ -188,12 +178,29 @@ _method_call (GDBusConnection *connection, } else _LOGW ("dhcp-event: (pid %d) unhandled DHCP event for interface %s", pid, iface); } +} + +static void +_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMDhcpListener *self = NM_DHCP_LISTENER (user_data); + + if (!nm_streq0 (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME)) + g_return_if_reached (); + if (!nm_streq0 (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY)) + g_return_if_reached (); + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) + g_return_if_reached (); + + _method_call_handle (self, parameters); -out: - g_free (iface); - g_free (pid_str); - g_free (reason); - g_variant_unref (options); g_dbus_method_invocation_return_value (invocation, NULL); } diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index fff9f9ec..f5c7c84b 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -34,11 +34,11 @@ #include <fcntl.h> #include <stdio.h> +#include "nm-utils/nm-dedup-multi.h" + #include "nm-config.h" #include "NetworkManagerUtils.h" -#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */ - /*****************************************************************************/ typedef struct { @@ -95,7 +95,7 @@ _client_factory_available (const NMDhcpClientFactory *client_factory) /*****************************************************************************/ static NMDhcpClient * -get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6) +get_client_for_ifindex (NMDhcpManager *manager, int addr_family, int ifindex) { NMDhcpManagerPrivate *priv; GHashTableIter iter; @@ -111,7 +111,7 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6) NMDhcpClient *candidate = NM_DHCP_CLIENT (value); if ( nm_dhcp_client_get_ifindex (candidate) == ifindex - && nm_dhcp_client_get_ipv6 (candidate) == ip6) + && nm_dhcp_client_get_addr_family (candidate) == addr_family) return candidate; } @@ -152,12 +152,14 @@ client_state_changed (NMDhcpClient *client, static NMDhcpClient * client_start (NMDhcpManager *self, + int addr_family, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, const char *uuid, - guint32 priority, - gboolean ipv6, + guint32 route_table, + guint32 route_metric, const struct in6_addr *ipv6_ll_addr, const char *dhcp_client_id, guint32 timeout, @@ -185,7 +187,7 @@ client_start (NMDhcpManager *self, return NULL; /* Kill any old client instance */ - client = get_client_for_ifindex (self, ifindex, ipv6); + client = get_client_for_ifindex (self, addr_family, ifindex); if (client) { g_object_ref (client); remove_client (self, client); @@ -195,21 +197,23 @@ client_start (NMDhcpManager *self, /* And make a new one */ client = g_object_new (priv->client_factory->get_type (), + NM_DHCP_CLIENT_MULTI_IDX, multi_idx, + NM_DHCP_CLIENT_ADDR_FAMILY, addr_family, NM_DHCP_CLIENT_INTERFACE, iface, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, - NM_DHCP_CLIENT_IPV6, ipv6, NM_DHCP_CLIENT_UUID, uuid, - NM_DHCP_CLIENT_PRIORITY, priority, - NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT, + NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, + NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric, + NM_DHCP_CLIENT_TIMEOUT, (guint) timeout, NULL); g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client)); g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self); - if (ipv6) - success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes); - else + if (addr_family == AF_INET) success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, hostname_use_fqdn, last_ip4_address); + else + success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes); if (!success) { remove_client (self, client); @@ -222,11 +226,13 @@ client_start (NMDhcpManager *self, /* Caller owns a reference to the NMDhcpClient on return */ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, const char *uuid, - guint32 priority, + guint32 route_table, + guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, const char *dhcp_fqdn, @@ -267,7 +273,8 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, } } - return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL, + return client_start (self, AF_INET, multi_idx, iface, ifindex, hwaddr, uuid, + route_table, route_metric, NULL, dhcp_client_id, timeout, dhcp_anycast_addr, hostname, use_fqdn, FALSE, 0, last_ip_address, 0); } @@ -275,12 +282,14 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, /* Caller owns a reference to the NMDhcpClient on return */ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, const struct in6_addr *ll_addr, const char *uuid, - guint32 priority, + guint32 route_table, + guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, guint32 timeout, @@ -299,8 +308,9 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, /* Always prefer the explicit dhcp-hostname if given */ hostname = dhcp_hostname ? dhcp_hostname : priv->default_hostname; } - return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE, - ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, TRUE, info_only, + return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, + route_table, route_metric, ll_addr, + NULL, timeout, dhcp_anycast_addr, hostname, TRUE, info_only, privacy, NULL, needed_prefixes); } @@ -320,11 +330,13 @@ nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostna GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, + int addr_family, const char *iface, int ifindex, const char *uuid, - gboolean ipv6, - guint32 default_route_metric) + guint32 route_table, + guint32 route_metric) { NMDhcpManagerPrivate *priv; @@ -332,11 +344,12 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (ifindex >= -1, NULL); g_return_val_if_fail (uuid != NULL, NULL); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL); priv = NM_DHCP_MANAGER_GET_PRIVATE (self); if ( priv->client_factory && priv->client_factory->get_lease_ip_configs) - return priv->client_factory->get_lease_ip_configs (iface, ifindex, uuid, ipv6, default_route_metric); + return priv->client_factory->get_lease_ip_configs (multi_idx, addr_family, iface, ifindex, uuid, route_table, route_metric); return NULL; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 66fdd145..078117ff 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -46,11 +46,13 @@ void nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostname); NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, + struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, const char *uuid, - guint32 priority, + guint32 route_table, + guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, const char *dhcp_fqdn, @@ -60,12 +62,14 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, const char *last_ip_address); NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, + struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, const struct in6_addr *ll_addr, const char *uuid, - guint32 priority, + guint32 route_table, + guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, guint32 timeout, @@ -75,15 +79,17 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, guint needed_prefixes); GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, + struct _NMDedupMultiIndex *multi_idx, + int addr_family, const char *iface, int ifindex, const char *uuid, - gboolean ipv6, - guint32 default_route_metric); + guint32 route_table, + guint32 route_metric); /* For testing only */ extern const char* nm_dhcp_helper_path; -extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[3]; +extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; #endif /* __NETWORKMANAGER_DHCP_MANAGER_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index aa902701..9b1a4433 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -28,6 +28,8 @@ #include <ctype.h> #include <net/if_arp.h> +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" #include "nm-dhcp-utils.h" #include "NetworkManagerUtils.h" @@ -79,9 +81,6 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) #define DHCP_OPTION_NIS_DOMAIN 40 #define DHCP_OPTION_NIS_SERVERS 41 -#define DHCP_OPTION_DOMAIN_SEARCH 119 -#define DHCP_OPTION_MS_ROUTES 249 -#define DHCP_OPTION_WPAD 252 /* Internal values */ #define DHCP_OPTION_IP_ADDRESS 1024 @@ -105,53 +104,53 @@ typedef struct { #define REQPREFIX "requested_" static const ReqOption dhcp4_requests[] = { - { SD_DHCP_OPTION_SUBNET_MASK, REQPREFIX "subnet_mask", TRUE }, - { SD_DHCP_OPTION_TIME_OFFSET, REQPREFIX "time_offset", TRUE }, - { SD_DHCP_OPTION_ROUTER, REQPREFIX "routers", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, REQPREFIX "domain_name_servers", TRUE }, - { SD_DHCP_OPTION_HOST_NAME, REQPREFIX "host_name", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME, REQPREFIX "domain_name", TRUE }, - { SD_DHCP_OPTION_INTERFACE_MTU, REQPREFIX "interface_mtu", TRUE }, - { SD_DHCP_OPTION_BROADCAST, REQPREFIX "broadcast_address", TRUE }, - { SD_DHCP_OPTION_STATIC_ROUTE, REQPREFIX "static_routes", TRUE }, - { DHCP_OPTION_NIS_DOMAIN, REQPREFIX "nis_domain", TRUE }, - { DHCP_OPTION_NIS_SERVERS, REQPREFIX "nis_servers", TRUE }, - { SD_DHCP_OPTION_NTP_SERVER, REQPREFIX "ntp_servers", TRUE }, - { SD_DHCP_OPTION_SERVER_IDENTIFIER, REQPREFIX "dhcp_server_identifier", TRUE }, - { DHCP_OPTION_DOMAIN_SEARCH, REQPREFIX "domain_search", TRUE }, - { SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE }, - { DHCP_OPTION_MS_ROUTES, REQPREFIX "ms_classless_static_routes", TRUE }, - { DHCP_OPTION_WPAD, REQPREFIX "wpad", TRUE }, + { SD_DHCP_OPTION_SUBNET_MASK, REQPREFIX "subnet_mask", TRUE }, + { SD_DHCP_OPTION_TIME_OFFSET, REQPREFIX "time_offset", TRUE }, + { SD_DHCP_OPTION_ROUTER, REQPREFIX "routers", TRUE }, + { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, REQPREFIX "domain_name_servers", TRUE }, + { SD_DHCP_OPTION_HOST_NAME, REQPREFIX "host_name", TRUE }, + { SD_DHCP_OPTION_DOMAIN_NAME, REQPREFIX "domain_name", TRUE }, + { SD_DHCP_OPTION_INTERFACE_MTU, REQPREFIX "interface_mtu", TRUE }, + { SD_DHCP_OPTION_BROADCAST, REQPREFIX "broadcast_address", TRUE }, + { SD_DHCP_OPTION_STATIC_ROUTE, REQPREFIX "static_routes", TRUE }, + { DHCP_OPTION_NIS_DOMAIN, REQPREFIX "nis_domain", TRUE }, + { DHCP_OPTION_NIS_SERVERS, REQPREFIX "nis_servers", TRUE }, + { SD_DHCP_OPTION_NTP_SERVER, REQPREFIX "ntp_servers", TRUE }, + { SD_DHCP_OPTION_SERVER_IDENTIFIER, REQPREFIX "dhcp_server_identifier", TRUE }, + { SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, REQPREFIX "domain_search", TRUE }, + { SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE }, + { SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, REQPREFIX "ms_classless_static_routes", TRUE }, + { SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, REQPREFIX "wpad", TRUE }, /* Internal values */ - { SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry", FALSE }, - { SD_DHCP_OPTION_CLIENT_IDENTIFIER, REQPREFIX "dhcp_client_identifier", FALSE }, - { DHCP_OPTION_IP_ADDRESS, REQPREFIX "ip_address", FALSE }, + { SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry", FALSE }, + { SD_DHCP_OPTION_CLIENT_IDENTIFIER, REQPREFIX "dhcp_client_identifier", FALSE }, + { DHCP_OPTION_IP_ADDRESS, REQPREFIX "ip_address", FALSE }, { 0, NULL, FALSE } }; static const ReqOption dhcp6_requests[] = { - { SD_DHCP6_OPTION_CLIENTID, REQPREFIX "dhcp6_client_id", TRUE }, + { SD_DHCP6_OPTION_CLIENTID, REQPREFIX "dhcp6_client_id", TRUE }, /* Don't request server ID by default; some servers don't reply to * Information Requests that request the Server ID. */ - { SD_DHCP6_OPTION_SERVERID, REQPREFIX "dhcp6_server_id", FALSE }, + { SD_DHCP6_OPTION_SERVERID, REQPREFIX "dhcp6_server_id", FALSE }, - { SD_DHCP6_OPTION_DNS_SERVERS, REQPREFIX "dhcp6_name_servers", TRUE }, - { SD_DHCP6_OPTION_DOMAIN_LIST, REQPREFIX "dhcp6_domain_search", TRUE }, - { SD_DHCP6_OPTION_SNTP_SERVERS, REQPREFIX "dhcp6_sntp_servers", TRUE }, + { SD_DHCP6_OPTION_DNS_SERVERS, REQPREFIX "dhcp6_name_servers", TRUE }, + { SD_DHCP6_OPTION_DOMAIN_LIST, REQPREFIX "dhcp6_domain_search", TRUE }, + { SD_DHCP6_OPTION_SNTP_SERVERS, REQPREFIX "dhcp6_sntp_servers", TRUE }, /* Internal values */ - { DHCP6_OPTION_IP_ADDRESS, REQPREFIX "ip6_address", FALSE }, - { DHCP6_OPTION_PREFIXLEN, REQPREFIX "ip6_prefixlen", FALSE }, - { DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life", FALSE }, - { DHCP6_OPTION_MAX_LIFE, REQPREFIX "max_life", FALSE }, - { DHCP6_OPTION_STARTS, REQPREFIX "starts", FALSE }, - { DHCP6_OPTION_LIFE_STARTS, REQPREFIX "life_starts", FALSE }, - { DHCP6_OPTION_RENEW, REQPREFIX "renew", FALSE }, - { DHCP6_OPTION_REBIND, REQPREFIX "rebind", FALSE }, - { DHCP6_OPTION_IAID, REQPREFIX "iaid", FALSE }, + { DHCP6_OPTION_IP_ADDRESS, REQPREFIX "ip6_address", FALSE }, + { DHCP6_OPTION_PREFIXLEN, REQPREFIX "ip6_prefixlen", FALSE }, + { DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life", FALSE }, + { DHCP6_OPTION_MAX_LIFE, REQPREFIX "max_life", FALSE }, + { DHCP6_OPTION_STARTS, REQPREFIX "starts", FALSE }, + { DHCP6_OPTION_LIFE_STARTS, REQPREFIX "life_starts", FALSE }, + { DHCP6_OPTION_RENEW, REQPREFIX "renew", FALSE }, + { DHCP6_OPTION_REBIND, REQPREFIX "rebind", FALSE }, + { DHCP6_OPTION_IAID, REQPREFIX "iaid", FALSE }, { 0, NULL, FALSE } }; @@ -212,16 +211,18 @@ add_requests_to_options (GHashTable *options, const ReqOption *requests) #define LOG_LEASE(domain, ...) \ G_STMT_START { \ if (log_lease) { \ - _LOG2I ((domain), (iface), __VA_ARGS__); \ + _LOG2I ((domain), (iface), " "__VA_ARGS__); \ } \ } G_STMT_END static NMIP4Config * -lease_to_ip4_config (const char *iface, +lease_to_ip4_config (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, sd_dhcp_lease *lease, GHashTable *options, - guint32 default_priority, + guint32 route_table, + guint32 route_metric, gboolean log_lease, GError **error) { @@ -229,11 +230,12 @@ lease_to_ip4_config (const char *iface, struct in_addr tmp_addr; const struct in_addr *addr_list; char buf[INET_ADDRSTRLEN]; - const char *str; + const char *s; guint32 lifetime = 0, i; NMPlatformIP4Address address; - GString *l; + nm_auto_free_gstring GString *str = NULL; gs_free sd_dhcp_route **routes = NULL; + const char *const*search_domains = NULL; guint16 mtu; int r, num; guint64 end_time; @@ -241,24 +243,26 @@ lease_to_ip4_config (const char *iface, gsize data_len; gboolean metered = FALSE; gboolean static_default_gateway = FALSE; + gboolean gateway_has = FALSE; + in_addr_t gateway = 0; g_return_val_if_fail (lease != NULL, NULL); - ip4_config = nm_ip4_config_new (ifindex); + ip4_config = nm_ip4_config_new (multi_idx, ifindex); /* Address */ sd_dhcp_lease_get_address (lease, &tmp_addr); memset (&address, 0, sizeof (address)); address.address = tmp_addr.s_addr; address.peer_address = tmp_addr.s_addr; - str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, " address %s", str); - add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str); + s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); + LOG_LEASE (LOGD_DHCP4, "address %s", s); + add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, s); /* Prefix/netmask */ sd_dhcp_lease_get_netmask (lease, &tmp_addr); address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); - LOG_LEASE (LOGD_DHCP4, " plen %d", address.plen); + LOG_LEASE (LOGD_DHCP4, "plen %d", address.plen); add_option (options, dhcp4_requests, SD_DHCP_OPTION_SUBNET_MASK, @@ -269,7 +273,7 @@ lease_to_ip4_config (const char *iface, address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = address.preferred = lifetime; end_time = (guint64) time (NULL) + lifetime; - LOG_LEASE (LOGD_DHCP4, " expires in %" G_GUINT32_FORMAT " seconds", lifetime); + LOG_LEASE (LOGD_DHCP4, "expires in %" G_GUINT32_FORMAT " seconds", lifetime); add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, @@ -281,47 +285,58 @@ lease_to_ip4_config (const char *iface, /* DNS Servers */ num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { - l = g_string_sized_new (30); + nm_gstring_prepare (&str); for (i = 0; i < num; i++) { if (addr_list[i].s_addr) { nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); - str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, " nameserver '%s'", str); - g_string_append_printf (l, "%s%s", l->len ? " " : "", str); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); + LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", s); + g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } } - if (l->len) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, l->str); - g_string_free (l, TRUE); + if (str->len) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, str->str); + } + + /* Search domains */ + num = sd_dhcp_lease_get_search_domains (lease, (char ***) &search_domains); + if (num > 0) { + nm_gstring_prepare (&str); + for (i = 0; i < num; i++) { + nm_ip4_config_add_search (ip4_config, search_domains[i]); + g_string_append_printf (str, "%s%s", str->len ? " " : "", search_domains[i]); + LOG_LEASE (LOGD_DHCP4, "domain search '%s'", search_domains[i]); + } + add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, str->str); } /* Domain Name */ - r = sd_dhcp_lease_get_domainname (lease, &str); + r = sd_dhcp_lease_get_domainname (lease, &s); if (r == 0) { /* Multiple domains sometimes stuffed into option 15 "Domain Name". * As systemd escapes such characters, split them at \\032. */ - char **domains = g_strsplit (str, "\\032", 0); - char **s; + char **domains = g_strsplit (s, "\\032", 0); + char **d; - for (s = domains; *s; s++) { - LOG_LEASE (LOGD_DHCP4, " domain name '%s'", *s); - nm_ip4_config_add_domain (ip4_config, *s); + for (d = domains; *d; d++) { + LOG_LEASE (LOGD_DHCP4, "domain name '%s'", *d); + nm_ip4_config_add_domain (ip4_config, *d); } g_strfreev (domains); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, str); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, s); } /* Hostname */ - r = sd_dhcp_lease_get_hostname (lease, &str); + r = sd_dhcp_lease_get_hostname (lease, &s); if (r == 0) { - LOG_LEASE (LOGD_DHCP4, " hostname '%s'", str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, str); + LOG_LEASE (LOGD_DHCP4, "hostname '%s'", s); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, s); } /* Routes */ num = sd_dhcp_lease_get_routes (lease, &routes); if (num > 0) { - l = g_string_sized_new (30); + nm_gstring_prepare (&str); for (i = 0; i < num; i++) { NMPlatformIP4Route route = { 0 }; const char *gw_str; @@ -330,12 +345,13 @@ lease_to_ip4_config (const char *iface, if (sd_dhcp_route_get_destination (routes[i], &a) < 0) continue; - route.network = a.s_addr; if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0 || plen > 32) continue; + route.plen = plen; + route.network = nm_utils_ip4_address_clear_host_address (a.s_addr, plen); if (sd_dhcp_route_get_gateway (routes[i], &a) < 0) continue; @@ -343,28 +359,29 @@ lease_to_ip4_config (const char *iface, if (route.plen) { route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = default_priority; - nm_ip4_config_add_route (ip4_config, &route); + route.metric = route_metric; + route.table_coerced = nm_platform_route_table_coerce (route_table); + nm_ip4_config_add_route (ip4_config, &route, NULL); - str = nm_utils_inet4_ntop (route.network, buf); + s = nm_utils_inet4_ntop (route.network, buf); gw_str = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, " static route %s/%d gw %s", str, route.plen, gw_str); + LOG_LEASE (LOGD_DHCP4, "static route %s/%d gw %s", s, route.plen, gw_str); - g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str); + g_string_append_printf (str, "%s%s/%d %s", str->len ? " " : "", s, route.plen, gw_str); } else { if (!static_default_gateway) { static_default_gateway = TRUE; - nm_ip4_config_set_gateway (ip4_config, route.gateway); + gateway_has = TRUE; + gateway = route.gateway; - str = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, " gateway %s", str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str); + s = nm_utils_inet4_ntop (route.gateway, NULL); + LOG_LEASE (LOGD_DHCP4, "gateway %s", s); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); } } } - if (l->len) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, l->str); - g_string_free (l, TRUE); + if (str->len) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str->str); } /* If the DHCP server returns both a Classless Static Routes option and a @@ -376,32 +393,43 @@ lease_to_ip4_config (const char *iface, if (!static_default_gateway) { r = sd_dhcp_lease_get_router (lease, &tmp_addr); if (r == 0) { - nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr); - str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, " gateway %s", str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str); + gateway_has = TRUE; + gateway = tmp_addr.s_addr; + s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); + LOG_LEASE (LOGD_DHCP4, "gateway %s", s); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); } } + if (gateway_has) { + const NMPlatformIP4Route rt = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + + nm_ip4_config_add_route (ip4_config, &rt, NULL); + } + /* MTU */ r = sd_dhcp_lease_get_mtu (lease, &mtu); if (r == 0 && mtu) { nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); - LOG_LEASE (LOGD_DHCP4, " mtu %u", mtu); + LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); } /* NTP servers */ num = sd_dhcp_lease_get_ntp (lease, &addr_list); if (num > 0) { - l = g_string_sized_new (30); + nm_gstring_prepare (&str); for (i = 0; i < num; i++) { - str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); - LOG_LEASE (LOGD_DHCP4, " ntp server '%s'", str); - g_string_append_printf (l, "%s%s", l->len ? " " : "", str); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); + LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", s); + g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } - add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, l->str); - g_string_free (l, TRUE); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str); } r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len); @@ -415,20 +443,22 @@ lease_to_ip4_config (const char *iface, /*****************************************************************************/ static char * -get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6) +get_leasefile_path (int addr_family, const char *iface, const char *uuid) { return g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease", - ipv6 ? "6" : "", + addr_family == AF_INET6 ? "6" : "", uuid, iface); } static GSList * -nm_dhcp_systemd_get_lease_ip_configs (const char *iface, +nm_dhcp_systemd_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, + int addr_family, + const char *iface, int ifindex, const char *uuid, - gboolean ipv6, - guint32 default_route_metric) + guint32 route_table, + guint32 route_metric) { GSList *leases = NULL; gs_free char *path = NULL; @@ -436,13 +466,13 @@ nm_dhcp_systemd_get_lease_ip_configs (const char *iface, NMIP4Config *ip4_config; int r; - if (ipv6) + if (addr_family != AF_INET) return NULL; - path = get_leasefile_path (iface, uuid, FALSE); + path = get_leasefile_path (addr_family, iface, uuid); r = dhcp_lease_load (&lease, path); if (r == 0 && lease) { - ip4_config = lease_to_ip4_config (iface, ifindex, lease, NULL, default_route_metric, FALSE, NULL); + ip4_config = lease_to_ip4_config (multi_idx, iface, ifindex, lease, NULL, route_table, route_metric, FALSE, NULL); if (ip4_config) leases = g_slist_append (leases, ip4_config); sd_dhcp_lease_unref (lease); @@ -495,12 +525,14 @@ bound4_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - ip4_config = lease_to_ip4_config (iface, + options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); + ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), + iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, - nm_dhcp_client_get_priority (NM_DHCP_CLIENT (self)), + nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)), + nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)), TRUE, &error); if (ip4_config) { @@ -589,9 +621,9 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last g_assert (priv->client6 == NULL); g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), FALSE); + priv->lease_file = get_leasefile_path (AF_INET, iface, nm_dhcp_client_get_uuid (client)); - r = sd_dhcp_client_new (&priv->client4); + r = sd_dhcp_client_new (&priv->client4, FALSE); if (r < 0) { _LOGW ("failed to create client (%d)", r); return FALSE; @@ -716,7 +748,8 @@ error: } static NMIP6Config * -lease_to_ip6_config (const char *iface, +lease_to_ip6_config (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, sd_dhcp6_lease *lease, GHashTable *options, @@ -729,17 +762,17 @@ lease_to_ip6_config (const char *iface, NMIP6Config *ip6_config; const char *addr_str; char **domains; - GString *str; + nm_auto_free_gstring GString *str = NULL; int num, i; gint32 ts; g_return_val_if_fail (lease, NULL); - ip6_config = nm_ip6_config_new (ifindex); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); ts = nm_utils_get_monotonic_timestamp_s (); - str = g_string_sized_new (30); /* Addresses */ sd_dhcp6_lease_reset_address_iter (lease); + nm_gstring_prepare (&str); while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { NMPlatformIP6Address address = { .plen = 128, @@ -756,17 +789,14 @@ lease_to_ip6_config (const char *iface, g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); LOG_LEASE (LOGD_DHCP6, - " address %s", + "address %s", nm_platform_ip6_address_to_string (&address, NULL, 0)); }; - if (str->len) { + if (str->len) add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); - g_string_set_size (str , 0); - } if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { - g_string_free (str, TRUE); g_object_unref (ip6_config); g_set_error_literal (error, NM_MANAGER_ERROR, @@ -778,30 +808,28 @@ lease_to_ip6_config (const char *iface, /* DNS servers */ num = sd_dhcp6_lease_get_dns (lease, &dns); if (num > 0) { + nm_gstring_prepare (&str); for (i = 0; i < num; i++) { nm_ip6_config_add_nameserver (ip6_config, &dns[i]); addr_str = nm_utils_inet6_ntop (&dns[i], NULL); g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); - LOG_LEASE (LOGD_DHCP6, " nameserver %s", addr_str); + LOG_LEASE (LOGD_DHCP6, "nameserver %s", addr_str); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); - g_string_set_size (str, 0); } /* Search domains */ num = sd_dhcp6_lease_get_domains (lease, &domains); if (num > 0) { + nm_gstring_prepare (&str); for (i = 0; i < num; i++) { nm_ip6_config_add_search (ip6_config, domains[i]); g_string_append_printf (str, "%s%s", str->len ? " " : "", domains[i]); - LOG_LEASE (LOGD_DHCP6, " domain name '%s'", domains[i]); + LOG_LEASE (LOGD_DHCP6, "domain name '%s'", domains[i]); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); - g_string_set_size (str, 0); } - g_string_free (str, TRUE); - return ip6_config; } @@ -825,8 +853,9 @@ bound6_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - ip6_config = lease_to_ip6_config (iface, + options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); + ip6_config = lease_to_ip6_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), + iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, @@ -893,7 +922,7 @@ ip6_start (NMDhcpClient *client, g_return_val_if_fail (duid != NULL, FALSE); g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), TRUE); + priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client)); priv->info_only = info_only; r = sd_dhcp6_client_new (&priv->client6); diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index e55a21b4..4b2d57b9 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -24,6 +24,8 @@ #include <unistd.h> #include <arpa/inet.h> +#include "nm-utils/nm-dedup-multi.h" + #include "nm-dhcp-utils.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" @@ -36,7 +38,8 @@ static gboolean ip4_process_dhcpcd_rfc3442_routes (const char *iface, const char *str, - guint32 priority, + guint32 route_table, + guint32 route_metric, NMIP4Config *ip4_config, guint32 *gwaddr) { @@ -84,12 +87,13 @@ ip4_process_dhcpcd_rfc3442_routes (const char *iface, } else { _LOG2I (LOGD_DHCP4, iface, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1)); memset (&route, 0, sizeof (route)); - route.network = rt_addr; + route.network = nm_utils_ip4_address_clear_host_address (rt_addr, rt_cidr); route.plen = rt_cidr; route.gateway = rt_route; route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = priority; - nm_ip4_config_add_route (ip4_config, &route); + route.metric = route_metric; + route.table_coerced = nm_platform_route_table_coerce (route_table); + nm_ip4_config_add_route (ip4_config, &route, NULL); } } @@ -142,8 +146,7 @@ process_dhclient_rfc3442_route (const char **octets, goto error; } g_free (str_addr); - tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp); - route->network = tmp_addr; + route->network = nm_utils_ip4_address_clear_host_address (tmp_addr, tmp); } /* Handle next hop */ @@ -165,7 +168,8 @@ error: static gboolean ip4_process_dhclient_rfc3442_routes (const char *iface, const char *str, - guint32 priority, + guint32 route_table, + guint32 route_metric, NMIP4Config *ip4_config, guint32 *gwaddr) { @@ -197,8 +201,9 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, /* normal route */ route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = priority; - nm_ip4_config_add_route (ip4_config, &route); + route.metric = route_metric; + route.table_coerced = nm_platform_route_table_coerce (route_table); + nm_ip4_config_add_route (ip4_config, &route, NULL); _LOG2I (LOGD_DHCP4, iface, " classless static route %s/%d gw %s", nm_utils_inet4_ntop (route.network, addr), route.plen, @@ -214,7 +219,8 @@ out: static gboolean ip4_process_classless_routes (const char *iface, GHashTable *options, - guint32 priority, + guint32 route_table, + guint32 route_metric, NMIP4Config *ip4_config, guint32 *gwaddr) { @@ -270,16 +276,17 @@ ip4_process_classless_routes (const char *iface, if (strchr (str, '/')) { /* dhcpcd format */ - return ip4_process_dhcpcd_rfc3442_routes (iface, str, priority, ip4_config, gwaddr); + return ip4_process_dhcpcd_rfc3442_routes (iface, str, route_table, route_metric, ip4_config, gwaddr); } - return ip4_process_dhclient_rfc3442_routes (iface, str, priority, ip4_config, gwaddr); + return ip4_process_dhclient_rfc3442_routes (iface, str, route_table, route_metric, ip4_config, gwaddr); } static void process_classful_routes (const char *iface, GHashTable *options, - guint32 priority, + guint32 route_table, + guint32 route_metric, NMIP4Config *ip4_config) { const char *str; @@ -316,16 +323,19 @@ process_classful_routes (const char *iface, The Static Routes option (option 33) does not provide a subnet mask for each route - it is assumed that the subnet mask is implicit in whatever network number is specified in each route entry */ - route.plen = nm_utils_ip4_get_default_prefix (rt_addr); - if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) { + route.plen = _nm_utils_ip4_get_default_prefix (rt_addr); + if (rt_addr & ~_nm_utils_ip4_prefix_to_netmask (route.plen)) { /* RFC 943: target not "this network"; using host routing */ route.plen = 32; } route.gateway = rt_route; route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = priority; + route.metric = route_metric; + route.table_coerced = nm_platform_route_table_coerce (route_table); + + route.network = nm_utils_ip4_address_clear_host_address (route.network, route.plen); - nm_ip4_config_add_route (ip4_config, &route); + nm_ip4_config_add_route (ip4_config, &route, NULL); _LOG2I (LOGD_DHCP, iface, " static route %s", nm_platform_ip4_route_to_string (&route, NULL, 0)); } @@ -383,22 +393,25 @@ ip4_add_domain_search (gpointer data, gpointer user_data) } NMIP4Config * -nm_dhcp_utils_ip4_config_from_options (int ifindex, +nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, - guint32 priority) + guint32 route_table, + guint32 route_metric) { NMIP4Config *ip4_config = NULL; guint32 tmp_addr; in_addr_t addr; NMPlatformIP4Address address; char *str = NULL; - guint32 gwaddr = 0; + gboolean gateway_has = FALSE; + guint32 gateway = 0; guint8 plen = 0; g_return_val_if_fail (options != NULL, NULL); - ip4_config = nm_ip4_config_new (ifindex); + ip4_config = nm_ip4_config_new (multi_idx, ifindex); memset (&address, 0, sizeof (address)); address.timestamp = nm_utils_get_monotonic_timestamp_s (); @@ -414,7 +427,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, _LOG2I (LOGD_DHCP4, iface, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ - plen = nm_utils_ip4_get_default_prefix (addr); + plen = _nm_utils_ip4_get_default_prefix (addr); _LOG2I (LOGD_DHCP4, iface, " plen %d (default)", plen); } nm_platform_ip4_address_set_addr (&address, addr, plen); @@ -422,12 +435,12 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ - if (!ip4_process_classless_routes (iface, options, priority, ip4_config, &gwaddr)) - process_classful_routes (iface, options, priority, ip4_config); + if (!ip4_process_classless_routes (iface, options, route_table, route_metric, ip4_config, &gateway)) + process_classful_routes (iface, options, route_table, route_metric, ip4_config); - if (gwaddr) { - _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gwaddr, NULL)); - nm_ip4_config_set_gateway (ip4_config, gwaddr); + if (gateway) { + _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gateway, NULL)); + gateway_has = TRUE; } else { /* If the gateway wasn't provided as a classless static route with a * subnet length of 0, try to find it using the old-style 'routers' option. @@ -439,9 +452,9 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, for (s = routers; *s; s++) { /* FIXME: how to handle multiple routers? */ - if (inet_pton (AF_INET, *s, &gwaddr) > 0) { - nm_ip4_config_set_gateway (ip4_config, gwaddr); + if (inet_pton (AF_INET, *s, &gateway) > 0) { _LOG2I (LOGD_DHCP4, iface, " gateway %s", *s); + gateway_has = TRUE; break; } else _LOG2W (LOGD_DHCP4, iface, "ignoring invalid gateway '%s'", *s); @@ -450,6 +463,17 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, } } + if (gateway_has) { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + + nm_ip4_config_add_route (ip4_config, &r, NULL); + } + str = g_hash_table_lookup (options, "dhcp_lease_time"); if (str) { address.lifetime = address.preferred = strtoul (str, NULL, 10); @@ -616,10 +640,10 @@ nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options) } NMIP6Config * -nm_dhcp_utils_ip6_config_from_options (int ifindex, +nm_dhcp_utils_ip6_config_from_options (NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, - guint32 priority, gboolean info_only) { NMIP6Config *ip6_config = NULL; @@ -633,7 +657,7 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex, address.plen = 128; address.timestamp = nm_utils_get_monotonic_timestamp_s (); - ip6_config = nm_ip6_config_new (ifindex); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); str = g_hash_table_lookup (options, "max_life"); if (str) { diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 05982b16..32140f48 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -24,15 +24,17 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" -NMIP4Config *nm_dhcp_utils_ip4_config_from_options (int ifindex, +NMIP4Config *nm_dhcp_utils_ip4_config_from_options (struct _NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, - guint priority); + guint32 route_table, + guint32 route_metric); -NMIP6Config *nm_dhcp_utils_ip6_config_from_options (int ifindex, +NMIP6Config *nm_dhcp_utils_ip6_config_from_options (struct _NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, - guint priority, gboolean info_only); NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options); diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index 5816932b..f2e1f321 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -23,6 +23,9 @@ #include <string.h> #include <unistd.h> #include <arpa/inet.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "NetworkManagerUtils.h" #include "dhcp/nm-dhcp-dhclient-utils.h" @@ -35,10 +38,14 @@ #define DEBUG 1 +static const int IFINDEX = 5; +static const guint32 ROUTE_TABLE = RT_TABLE_MAIN; +static const guint32 ROUTE_METRIC = 100; + static void test_config (const char *orig, const char *expected, - gboolean ipv6, + int addr_family, const char *hostname, guint32 timeout, gboolean use_fqdn, @@ -57,7 +64,7 @@ test_config (const char *orig, } new = nm_dhcp_dhclient_create_config (iface, - ipv6, + addr_family, client_id, anycast_addr, hostname, @@ -106,7 +113,7 @@ static const char *orig_missing_expected = \ static void test_orig_missing (void) { - test_config (NULL, orig_missing_expected, FALSE, NULL, 0, FALSE, NULL, NULL, "eth0", NULL); + test_config (NULL, orig_missing_expected, AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", NULL); } /*****************************************************************************/ @@ -135,7 +142,7 @@ static void test_override_client_id (void) { test_config (override_client_id_orig, override_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, "11:22:33:44:55:66", NULL, "eth0", @@ -164,7 +171,7 @@ static void test_quote_client_id (void) { test_config (NULL, quote_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, "1234", NULL, "eth0", @@ -193,7 +200,7 @@ static void test_ascii_client_id (void) { test_config (NULL, ascii_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, "qb:cd:ef:12:34:56", NULL, "eth0", @@ -222,7 +229,7 @@ static void test_hex_single_client_id (void) { test_config (NULL, hex_single_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, "ab:cd:e:12:34:56", NULL, "eth0", @@ -259,7 +266,7 @@ test_existing_hex_client_id (void) new_client_id = g_bytes_new (bytes, sizeof (bytes)); test_config (existing_hex_client_id_orig, existing_hex_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, new_client_id, "eth0", @@ -299,7 +306,7 @@ test_existing_ascii_client_id (void) memcpy (buf + 1, EACID, NM_STRLEN (EACID)); new_client_id = g_bytes_new (buf, sizeof (buf)); test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, new_client_id, "eth0", @@ -328,7 +335,7 @@ static void test_fqdn (void) { test_config (NULL, fqdn_expected, - FALSE, "foo.bar.com", 0, + AF_INET, "foo.bar.com", 0, TRUE, NULL, NULL, "eth0", @@ -368,7 +375,7 @@ test_fqdn_options_override (void) { test_config (fqdn_options_override_orig, fqdn_options_override_expected, - FALSE, "example2.com", 0, + AF_INET, "example2.com", 0, TRUE, NULL, NULL, "eth0", @@ -401,7 +408,7 @@ static void test_override_hostname (void) { test_config (override_hostname_orig, override_hostname_expected, - FALSE, "blahblah", 0, FALSE, + AF_INET, "blahblah", 0, FALSE, NULL, NULL, "eth0", @@ -418,7 +425,6 @@ static const char *override_hostname6_expected = \ "# Merged from /path/to/dhclient.conf\n" "\n" "send fqdn.fqdn \"blahblah.local\"; # added by NetworkManager\n" - "send fqdn.encoded on;\n" "send fqdn.server-update on;\n" "\n" "also request dhcp6.name-servers;\n" @@ -430,7 +436,7 @@ static void test_override_hostname6 (void) { test_config (override_hostname6_orig, override_hostname6_expected, - TRUE, "blahblah.local", 0, TRUE, + AF_INET6, "blahblah.local", 0, TRUE, NULL, NULL, "eth0", @@ -442,6 +448,9 @@ test_override_hostname6 (void) static const char *nonfqdn_hostname6_expected = \ "# Created by NetworkManager\n" "\n" + "send fqdn.fqdn \"blahblah\"; # added by NetworkManager\n" + "send fqdn.server-update on;\n" + "\n" "also request dhcp6.name-servers;\n" "also request dhcp6.domain-search;\n" "also request dhcp6.client-id;\n" @@ -450,9 +459,9 @@ static const char *nonfqdn_hostname6_expected = \ static void test_nonfqdn_hostname6 (void) { - /* Non-FQDN hostname can't be used with dhclient */ + /* Non-FQDN hostname can now be used with dhclient */ test_config (NULL, nonfqdn_hostname6_expected, - TRUE, "blahblah", 0, TRUE, + AF_INET6, "blahblah", 0, TRUE, NULL, NULL, "eth0", @@ -487,7 +496,7 @@ static void test_existing_alsoreq (void) { test_config (existing_alsoreq_orig, existing_alsoreq_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", @@ -525,7 +534,7 @@ static void test_existing_req (void) { test_config (existing_req_orig, existing_req_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", @@ -564,7 +573,7 @@ static void test_existing_multiline_alsoreq (void) { test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", @@ -778,7 +787,7 @@ static void test_interface1 (void) { test_config (interface1_orig, interface1_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", @@ -823,7 +832,7 @@ static void test_interface2 (void) { test_config (interface2_orig, interface2_expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth1", @@ -877,7 +886,7 @@ test_config_req_intf (void) "\n"; test_config (orig, expected, - FALSE, NULL, 0, FALSE, + AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", @@ -889,6 +898,7 @@ test_config_req_intf (void) static void test_read_lease_ip4_config_basic (void) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -905,7 +915,7 @@ test_read_lease_ip4_config_basic (void) /* Date from before the least expiration */ now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); g_assert_cmpint (g_slist_length (leases), ==, 2); /* IP4Config #1 */ @@ -914,19 +924,19 @@ test_read_lease_ip4_config_basic (void) /* Address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); - g_assert (inet_aton ("192.168.1.180", (struct in_addr *) &expected_addr)); - addr = nm_ip4_config_get_address (config, 0); + expected_addr = nmtst_inet4_from_string ("192.168.1.180"); + addr = _nmtst_ip4_config_get_address (config, 0); g_assert_cmpint (addr->address, ==, expected_addr); g_assert_cmpint (addr->peer_address, ==, expected_addr); g_assert_cmpint (addr->plen, ==, 24); /* Gateway */ - g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr)); - g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr); + expected_addr = nmtst_inet4_from_string ("192.168.1.1"); + g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr); /* DNS */ g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 1); - g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr)); + expected_addr = nmtst_inet4_from_string ("192.168.1.1"); g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 0); @@ -937,21 +947,21 @@ test_read_lease_ip4_config_basic (void) /* Address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); - g_assert (inet_aton ("10.77.52.141", (struct in_addr *) &expected_addr)); - addr = nm_ip4_config_get_address (config, 0); + expected_addr = nmtst_inet4_from_string ("10.77.52.141"); + addr = _nmtst_ip4_config_get_address (config, 0); g_assert_cmpint (addr->address, ==, expected_addr); g_assert_cmpint (addr->peer_address, ==, expected_addr); g_assert_cmpint (addr->plen, ==, 8); /* Gateway */ - g_assert (inet_aton ("10.77.52.254", (struct in_addr *) &expected_addr)); - g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr); + expected_addr = nmtst_inet4_from_string ("10.77.52.254"); + g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr); /* DNS */ g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 2); - g_assert (inet_aton ("8.8.8.8", (struct in_addr *) &expected_addr)); + expected_addr = nmtst_inet4_from_string ("8.8.8.8"); g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); - g_assert (inet_aton ("8.8.4.4", (struct in_addr *) &expected_addr)); + expected_addr = nmtst_inet4_from_string ("8.8.4.4"); g_assert_cmpint (nm_ip4_config_get_nameserver (config, 1), ==, expected_addr); /* Domains */ @@ -966,6 +976,7 @@ test_read_lease_ip4_config_basic (void) static void test_read_lease_ip4_config_expired (void) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -979,7 +990,7 @@ test_read_lease_ip4_config_expired (void) /* Date from *after* the lease expiration */ now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); g_assert (leases == NULL); g_date_time_unref (now); @@ -989,6 +1000,7 @@ test_read_lease_ip4_config_expired (void) static void test_read_lease_ip4_config_expect_failure (gconstpointer user_data) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -1001,7 +1013,7 @@ test_read_lease_ip4_config_expect_failure (gconstpointer user_data) /* Date from before the least expiration */ now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); g_assert (leases == NULL); g_date_time_unref (now); diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index ffd63493..72f31191 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -22,7 +22,9 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> +#include <linux/rtnetlink.h> +#include "nm-utils/nm-dedup-multi.h" #include "nm-utils.h" #include "dhcp/nm-dhcp-utils.h" @@ -30,6 +32,20 @@ #include "nm-test-utils-core.h" +static NMIP4Config * +_ip4_config_from_options (int ifindex, + const char *iface, + GHashTable *options, + guint32 route_metric) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + NMIP4Config *config; + + config = nm_dhcp_utils_ip4_config_from_options (multi_idx, ifindex, iface, options, RT_TABLE_MAIN, route_metric); + g_assert (config); + return config; +} + typedef struct { const char *name; const char *value; @@ -41,7 +57,7 @@ fill_table (const Option *test_options, GHashTable *table) const Option *opt; if (!table) - table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + table = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); for (opt = test_options; opt->name; opt++) g_hash_table_insert (table, (gpointer) opt->name, (gpointer) opt->value); return table; @@ -86,12 +102,11 @@ test_generic_options (void) const char *expected_route2_gw = "10.1.1.1"; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); - address = nm_ip4_config_get_address (ip4_config, 0); + address = _nmtst_ip4_config_get_address (ip4_config, 0); g_assert (inet_pton (AF_INET, expected_addr, &tmp) > 0); g_assert (address->address == tmp); g_assert (address->peer_address == tmp); @@ -99,7 +114,7 @@ test_generic_options (void) /* Gateway */ g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0); - g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp); + g_assert (nmtst_ip4_config_get_gateway (ip4_config) == tmp); g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 0); @@ -118,10 +133,10 @@ test_generic_options (void) g_assert (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp); /* Routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); /* Route #1 */ - route = nm_ip4_config_get_route (ip4_config, 0); + route = _nmtst_ip4_config_get_route (ip4_config, 0); g_assert (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0); g_assert (route->network == tmp); g_assert (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0); @@ -130,14 +145,18 @@ test_generic_options (void) g_assert_cmpint (route->metric, ==, 0); /* Route #2 */ - route = nm_ip4_config_get_route (ip4_config, 1); - g_assert (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0); - g_assert (route->network == tmp); - g_assert (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0); - g_assert (route->gateway == tmp); + route = _nmtst_ip4_config_get_route (ip4_config, 1); + g_assert (route->network == nmtst_inet4_from_string (expected_route2_dest)); + g_assert (route->gateway == nmtst_inet4_from_string (expected_route2_gw)); g_assert_cmpint (route->plen, ==, 32); g_assert_cmpint (route->metric, ==, 0); + route = _nmtst_ip4_config_get_route (ip4_config, 2); + g_assert (route->network == nmtst_inet4_from_string ("0.0.0.0")); + g_assert (route->gateway == nmtst_inet4_from_string ("192.168.1.1")); + g_assert_cmpint (route->plen, ==, 0); + g_assert_cmpint (route->metric, ==, 0); + g_hash_table_destroy (options); } @@ -157,12 +176,11 @@ test_wins_options (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); - address = nm_ip4_config_get_address (ip4_config, 0); + address = _nmtst_ip4_config_get_address (ip4_config, 0); g_assert (address); g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 2); g_assert (inet_pton (AF_INET, expected_wins1, &tmp) > 0); @@ -184,16 +202,14 @@ test_vendor_option_metered (void) }; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert (nm_ip4_config_get_metered (ip4_config) == FALSE); g_hash_table_destroy (options); g_clear_object (&ip4_config); options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert (nm_ip4_config_get_metered (ip4_config) == TRUE); g_hash_table_destroy (options); } @@ -210,7 +226,7 @@ ip4_test_route (NMIP4Config *ip4_config, g_assert (expected_prefix <= 32); - route = nm_ip4_config_get_route (ip4_config, route_num); + route = _nmtst_ip4_config_get_route (ip4_config, route_num); g_assert (inet_pton (AF_INET, expected_dest, &tmp) > 0); g_assert (route->network == tmp); g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0); @@ -226,7 +242,7 @@ ip4_test_gateway (NMIP4Config *ip4_config, const char *expected_gw) g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0); - g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp); + g_assert (nmtst_ip4_config_get_gateway (ip4_config) == tmp); } static void @@ -246,13 +262,13 @@ test_classless_static_routes_1 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8); + ip4_test_route (ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); g_hash_table_destroy (options); } @@ -274,13 +290,13 @@ test_classless_static_routes_2 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8); + ip4_test_route (ip4_config, 2, "0.0.0.0", expected_route1_gw, 0); g_hash_table_destroy (options); } @@ -303,13 +319,13 @@ test_fedora_dhclient_classless_static_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 25); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 7); + ip4_test_route (ip4_config, 2, "0.0.0.0", expected_route1_gw, 0); /* Gateway */ ip4_test_gateway (ip4_config, expected_gateway); @@ -335,13 +351,13 @@ test_dhclient_invalid_classless_routes_1 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route (ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); g_hash_table_destroy (options); } @@ -366,16 +382,16 @@ test_dhcpcd_invalid_classless_routes_1 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static * routes are invalid. */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route (ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); g_hash_table_destroy (options); } @@ -399,16 +415,16 @@ test_dhclient_invalid_classless_routes_2 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static * routes are invalid. */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route (ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); g_hash_table_destroy (options); } @@ -432,8 +448,7 @@ test_dhcpcd_invalid_classless_routes_2 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static @@ -441,9 +456,10 @@ test_dhcpcd_invalid_classless_routes_2 (void) */ /* Routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 3); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route (ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); g_hash_table_destroy (options); } @@ -465,13 +481,13 @@ test_dhclient_invalid_classless_routes_3 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route (ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); g_hash_table_destroy (options); } @@ -493,13 +509,13 @@ test_dhcpcd_invalid_classless_routes_3 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*DHCP provided invalid classless static route*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route (ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); g_hash_table_destroy (options); } @@ -519,12 +535,12 @@ test_dhclient_gw_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route (ip4_config, 1, "0.0.0.0", "192.2.3.4", 0); /* Gateway */ ip4_test_gateway (ip4_config, expected_gateway); @@ -547,12 +563,12 @@ test_dhcpcd_gw_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ - g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); + g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route (ip4_config, 1, "0.0.0.0", "192.2.3.4", 0); /* Gateway */ ip4_test_gateway (ip4_config, expected_gateway); @@ -575,8 +591,7 @@ test_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* domain searches */ g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 3); @@ -602,8 +617,7 @@ test_invalid_escaped_domain_searches (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*invalid domain search*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* domain searches */ @@ -623,11 +637,10 @@ test_ip4_missing_prefix (const char *ip, guint32 expected_prefix) g_hash_table_insert (options, "ip_address", (gpointer) ip); g_hash_table_remove (options, "subnet_mask"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); - address = nm_ip4_config_get_address (ip4_config, 0); + address = _nmtst_ip4_config_get_address (ip4_config, 0); g_assert (address); g_assert_cmpint (address->plen, ==, expected_prefix); @@ -668,11 +681,10 @@ test_ip4_prefix_classless (void) g_hash_table_insert (options, "ip_address", "172.16.54.22"); g_hash_table_insert (options, "subnet_mask", "255.255.252.0"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); - address = nm_ip4_config_get_address (ip4_config, 0); + address = _nmtst_ip4_config_get_address (ip4_config, 0); g_assert (address); g_assert_cmpint (address->plen, ==, 22); diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index a6204ae4..3ec1fd2d 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -80,22 +80,20 @@ get_ip4_rdns_domains (NMIP4Config *ip4) { char **strv; GPtrArray *domains = NULL; - int i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; g_return_val_if_fail (ip4 != NULL, NULL); domains = g_ptr_array_sized_new (5); - for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4, i); - + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); - } - - for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (ip4, i); - nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); } /* Terminating NULL so we can use g_strfreev() to free it */ @@ -112,22 +110,20 @@ get_ip6_rdns_domains (NMIP6Config *ip6) { char **strv; GPtrArray *domains = NULL; - int i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; g_return_val_if_fail (ip6 != NULL, NULL); domains = g_ptr_array_sized_new (5); - for (i = 0; i < nm_ip6_config_get_num_addresses (ip6); i++) { - const NMPlatformIP6Address *address = nm_ip6_config_get_address (ip6, i); - + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); - } - - for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (ip6, i); - nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); } /* Terminating NULL so we can use g_strfreev() to free it */ diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 952468e3..d5392b7d 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -497,7 +497,7 @@ dispatch_netconfig (NMDnsManager *self, g_free (str); } - close (fd); + nm_close (fd); /* Wait until the process exits */ if (!nm_utils_kill_child_sync (pid, 0, LOGD_DNS, "netconfig", &status, 1000, 0)) { @@ -1167,9 +1167,16 @@ update_dns (NMDnsManager *self, * but only uses the local caching nameserver. */ if (caching) { + const char *lladdr = "127.0.0.1"; + + if (NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin)) { + /* systemd-resolved uses a different link-local address */ + lladdr = "127.0.0.53"; + } + g_strfreev (nameservers); - nameservers = g_new0 (char*, 2); - nameservers[0] = g_strdup ("127.0.0.1"); + nameservers = g_new0 (char *, 2); + nameservers[0] = g_strdup (lladdr); } if (update) { @@ -1427,6 +1434,7 @@ nm_dns_manager_set_initial_hostname (NMDnsManager *self, { NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + g_free (priv->hostname); priv->hostname = g_strdup (hostname); } @@ -1625,7 +1633,7 @@ _check_resconf_immutable (NMDnsManagerResolvConfManager rc_manager) if (fd != -1) { if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1) immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL); - close (fd); + nm_close (fd); } return immutable ? NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE : rc_manager; } @@ -1634,32 +1642,68 @@ _check_resconf_immutable (NMDnsManagerResolvConfManager rc_manager) static gboolean _resolvconf_resolved_managed (void) { - static const char *const resolved_paths[] = { + static const char *const RESOLVED_PATHS[] = { "/run/systemd/resolve/resolv.conf", "/lib/systemd/resolv.conf", "/usr/lib/systemd/resolv.conf", }; - GFile *f; - GFileInfo *info; - gboolean ret = FALSE; + struct stat st, st_test; + guint i; - f = g_file_new_for_path (_PATH_RESCONF); - info = g_file_query_info (f, - G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","\ - G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, NULL); + if (lstat (_PATH_RESCONF, &st) != 0) + return FALSE; - if (info && g_file_info_get_is_symlink (info)) { - ret = nm_utils_strv_find_first ((gchar **) resolved_paths, - G_N_ELEMENTS (resolved_paths), - g_file_info_get_symlink_target (info)) >= 0; - } + if (S_ISLNK (st.st_mode)) { + gs_free char *full_path = NULL; + nm_auto_free char *real_path = NULL; + + /* see if resolv.conf is a symlink with a target that is + * exactly like one of the candidates. + * + * This check will work for symlinks, even if the target + * does not exist and realpath() cannot resolve anything. + * + * We want to handle that, because systemd-resolved might not + * have started yet. */ + full_path = g_file_read_link (_PATH_RESCONF, NULL); + if (nm_utils_strv_find_first ((char **) RESOLVED_PATHS, + G_N_ELEMENTS (RESOLVED_PATHS), + full_path) >= 0) + return TRUE; + + /* see if resolv.conf is a symlink that resolves exactly one + * of the candidate paths. + * + * This check will work for symlinks that can be resolved + * to a realpath, but the actual file might not exist. + * + * We want to handle that, because systemd-resolved might not + * have started yet. */ + real_path = realpath (_PATH_RESCONF, NULL); + if (nm_utils_strv_find_first ((char **) RESOLVED_PATHS, + G_N_ELEMENTS (RESOLVED_PATHS), + real_path) >= 0) + return TRUE; - g_clear_object(&info); - g_clear_object(&f); + /* fall-through and resolve the symlink, to check the file + * it points to (below). + * + * This check is the most reliable, but it only works if + * systemd-resolved already started and created the file. */ + if (stat (_PATH_RESCONF, &st) != 0) + return FALSE; + } + + /* see if resolv.conf resolves to one of the candidate + * paths (or whether it is hard-linked). */ + for (i = 0; i < G_N_ELEMENTS (RESOLVED_PATHS); i++) { + if ( stat (RESOLVED_PATHS[i], &st_test) == 0 + && st.st_dev == st_test.st_dev + && st.st_ino == st_test.st_ino) + return TRUE; + } - return ret; + return FALSE; } static void diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index fce1fef1..315f2c4d 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -136,81 +136,75 @@ add_interface_configuration (NMDnsSystemdResolved *self, } static void -add_domain (GVariantBuilder *domains, - const char *domain, - gboolean never_default) -{ - /* If this link is never the default (e.g. only used for resources on this - * network) add a routing domain. */ - g_variant_builder_add (domains, "(sb)", domain, never_default); -} - -static void -update_add_ip6_config (NMDnsSystemdResolved *self, - GVariantBuilder *dns, - GVariantBuilder *domains, - const NMIP6Config *config) +update_add_ip_config (NMDnsSystemdResolved *self, + GVariantBuilder *dns, + GVariantBuilder *domains, + gpointer config) { + int addr_family; + gsize addr_size; guint i, n; + gboolean route_only; - n = nm_ip6_config_get_num_nameservers (config); - for (i = 0 ; i < n; i++) { - const struct in6_addr *ip; - - g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)")); - g_variant_builder_add (dns, "i", AF_INET6); - ip = nm_ip6_config_get_nameserver (config, i), - - g_variant_builder_add_value (dns, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, ip, 16, 1)); - g_variant_builder_close (dns); - } - - n = nm_ip6_config_get_num_searches (config); - if (n > 0) { - for (i = 0; i < n; i++) { - add_domain (domains, nm_ip6_config_get_search (config, i), - nm_ip6_config_get_never_default (config)); - } - } else { - n = nm_ip6_config_get_num_domains (config); - for (i = 0; i < n; i++) { - add_domain (domains, nm_ip6_config_get_domain (config, i), - nm_ip6_config_get_never_default (config)); - } - } -} + if (NM_IS_IP4_CONFIG (config)) + addr_family = AF_INET; + else if (NM_IS_IP6_CONFIG (config)) + addr_family = AF_INET6; + else + g_return_if_reached (); -static void -update_add_ip4_config (NMDnsSystemdResolved *self, - GVariantBuilder *dns, - GVariantBuilder *domains, - const NMIP4Config *config) -{ - guint i, n; + addr_size = nm_utils_addr_family_to_size (addr_family); - n = nm_ip4_config_get_num_nameservers (config); + n = addr_family == AF_INET + ? nm_ip4_config_get_num_nameservers (config) + : nm_ip6_config_get_num_nameservers (config); for (i = 0 ; i < n; i++) { - guint32 ns; + in_addr_t ns4; + gconstpointer ns; - g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)")); - g_variant_builder_add (dns, "i", AF_INET); - ns = nm_ip4_config_get_nameserver (config, i), + if (addr_family == AF_INET) { + ns4 = nm_ip4_config_get_nameserver (config, i); + ns = &ns4; + } else + ns = nm_ip6_config_get_nameserver (config, i); - g_variant_builder_add_value (dns, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &ns, 4, 1)); + g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)")); + g_variant_builder_add (dns, "i", addr_family); + g_variant_builder_add_value (dns, + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + ns, + addr_size, + 1)); g_variant_builder_close (dns); } - n = nm_ip4_config_get_num_searches (config); + /* If this link is never the default (e.g. only used for resources on this + * network) add a routing domain. */ + route_only = addr_family == AF_INET + ? !nm_ip4_config_best_default_route_get (config) + : !nm_ip6_config_best_default_route_get (config); + + n = addr_family == AF_INET + ? nm_ip4_config_get_num_searches (config) + : nm_ip6_config_get_num_searches (config); if (n > 0) { for (i = 0; i < n; i++) { - add_domain (domains, nm_ip4_config_get_search (config, i), - nm_ip4_config_get_never_default (config)); + g_variant_builder_add (domains, "(sb)", + addr_family == AF_INET + ? nm_ip4_config_get_search (config, i) + : nm_ip6_config_get_search (config, i), + route_only); } } else { - n = nm_ip4_config_get_num_domains (config); + n = addr_family == AF_INET + ? nm_ip4_config_get_num_domains (config) + : nm_ip6_config_get_num_domains (config); for (i = 0; i < n; i++) { - add_domain (domains, nm_ip4_config_get_domain (config, i), - nm_ip4_config_get_never_default (config)); + g_variant_builder_add (domains, "(sb)", + addr_family == AF_INET + ? nm_ip4_config_get_domain (config, i) + : nm_ip6_config_get_domain (config, i), + route_only); } } } @@ -243,14 +237,9 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_add (&domains, "i", ic->ifindex); g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)")); - for (l = ic->configs ; l != NULL ; l = g_list_next (l)) { - if (NM_IS_IP4_CONFIG (l->data)) - update_add_ip4_config (self, &dns, &domains, l->data); - else if (NM_IS_IP6_CONFIG (l->data)) - update_add_ip6_config (self, &dns, &domains, l->data); - else - g_assert_not_reached (); - } + for (l = ic->configs; l; l = l->next) + update_add_ip_config (self, &dns, &domains, l->data); + g_variant_builder_close (&dns); g_variant_builder_close (&domains); diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c index f76c983d..4ac1e7c3 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.c +++ b/src/dnsmasq/nm-dnsmasq-manager.c @@ -162,7 +162,7 @@ create_dm_cmd_line (const char *iface, const NMPlatformIP4Address *listen_address; guint i, n; - listen_address = nm_ip4_config_get_address (ip4_config, 0); + listen_address = nm_ip4_config_get_first_address (ip4_config); g_return_val_if_fail (listen_address, NULL); dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, error); @@ -221,7 +221,7 @@ create_dm_cmd_line (const char *iface, nm_cmd_line_add_string (cmd, s->str); g_string_truncate (s, 0); - if (!nm_ip4_config_get_never_default (ip4_config)) { + if (nm_ip4_config_best_default_route_get (ip4_config)) { g_string_append (s, "--dhcp-option=option:router,"); g_string_append (s, localaddr); nm_cmd_line_add_string (cmd, s->str); diff --git a/src/dnsmasq/nm-dnsmasq-utils.c b/src/dnsmasq/nm-dnsmasq-utils.c index e4f4324b..382b3aeb 100644 --- a/src/dnsmasq/nm-dnsmasq-utils.c +++ b/src/dnsmasq/nm-dnsmasq-utils.c @@ -35,11 +35,12 @@ nm_dnsmasq_utils_get_range (const NMPlatformIP4Address *addr, { guint32 host = addr->address; guint8 prefix = addr->plen; - guint32 netmask = nm_utils_ip4_prefix_to_netmask (prefix); - guint32 first, last, reserved; + guint32 netmask; + guint32 first, last, mid, reserved; + const guint32 NUM = 256; - g_return_val_if_fail (out_first != NULL, FALSE); - g_return_val_if_fail (out_last != NULL, FALSE); + g_return_val_if_fail (out_first, FALSE); + g_return_val_if_fail (out_last, FALSE); if (prefix > 30) { if (out_error_desc) @@ -47,29 +48,69 @@ nm_dnsmasq_utils_get_range (const NMPlatformIP4Address *addr, return FALSE; } - /* Find the first available address *after* the local machine's IP */ - first = (host & netmask) + htonl (1); + if (prefix < 24) { + /* if the subnet is larger then /24, we partition it and treat it + * like it would be a /24. + * + * Hence, the resulting range will always be between x.x.x.1/24 + * and x.x.x.254/24, with x.x.x.0 being the network address of the + * host. + * + * In this case, only a /24 portion of the subnet is used. + * No particular reason for that, but it's unlikely that a user + * would use NetworkManager's shared method when having hundered + * of DHCP clients. So, restrict the range to the same /24 in + * which the host address lies. + */ + prefix = 24; + } + + netmask = _nm_utils_ip4_prefix_to_netmask (prefix); + + /* treat addresses in host-order from here on. */ + netmask = ntohl (netmask); + host = ntohl (host); - /* Shortcut: allow a max of 253 addresses; the - htonl(1) here is to assure - * that we don't set 'last' to the broadcast address of the network. */ - if (prefix < 24) - last = (host | ~nm_utils_ip4_prefix_to_netmask (24)) - htonl (1); - else - last = (host | ~netmask) - htonl(1); + /* if host is the network or broadcast address, coerce it to + * one above or below. Usually, we wouldn't expect the user + * to pick such an address. */ + if (host == (host & netmask)) + host++; + else if (host == (host | ~netmask)) + host--; - /* Figure out which range (either above the host address or below it) - * has more addresses. Reserve some addresses for static IPs. + /* Exclude the network and broadcast address. */ + first = (host & netmask) + 1; + last = (host | ~netmask) - 1; + + /* Depending on whether host is above or below the middle of + * the subnet, the larger part if handed out. + * + * If the host is in the lower half, the range starts + * at the lower end with the host (plus reserved), until the + * broadcast address + * + * If the host is in the upper half, the range starts above + * the network-address and goes up until the host (except reserved). + * + * reserved is up to 8 addresses, 10% of the determined range. */ - if (ntohl (host) - ntohl (first) > ntohl (last) - ntohl (host)) { - /* Range below the host's IP address */ - reserved = (guint32) ((ntohl (host) - ntohl (first)) / 10); - last = host - htonl (MIN (reserved, 8)) - htonl (1); + mid = (host & netmask) | (((first + last) / 2) & ~netmask); + if (host > mid) { + /* use lower range */ + reserved = NM_MIN (((host - first) / 10), 8); + last = host - 1 - reserved; + first = NM_MAX (first, last > NUM ? last - NUM : 0); } else { - /* Range above host's IP address */ - reserved = (guint32) ((ntohl (last) - ntohl (host)) / 10); - first = host + htonl (MIN (reserved, 8)) + htonl (1); + /* use upper range */ + reserved = NM_MIN (((last - host) / 10), 8); + first = host + 1 + reserved; + last = NM_MIN (last, first < 0xFFFFFFFF - NUM ? first + NUM : 0xFFFFFFFF); } + first = htonl (first); + last = htonl (last); + nm_utils_inet4_ntop (first, out_first); nm_utils_inet4_ntop (last, out_last); diff --git a/src/dnsmasq/tests/test-dnsmasq-utils.c b/src/dnsmasq/tests/test-dnsmasq-utils.c index 00ff6610..b311ccb4 100644 --- a/src/dnsmasq/tests/test-dnsmasq-utils.c +++ b/src/dnsmasq/tests/test-dnsmasq-utils.c @@ -29,58 +29,138 @@ static void test_address_ranges (void) { - NMPlatformIP4Address addr; - char first[INET_ADDRSTRLEN]; - char last[INET_ADDRSTRLEN]; - char *error_desc = NULL; - - addr = *nmtst_platform_ip4_address ("192.168.0.1", NULL, 24); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "192.168.0.10"); - g_assert_cmpstr (last, ==, "192.168.0.254"); - - addr = *nmtst_platform_ip4_address ("192.168.0.99", NULL, 24); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "192.168.0.108"); - g_assert_cmpstr (last, ==, "192.168.0.254"); - - addr = *nmtst_platform_ip4_address ("192.168.0.254", NULL, 24); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "192.168.0.1"); - g_assert_cmpstr (last, ==, "192.168.0.245"); - - /* Smaller networks */ - addr = *nmtst_platform_ip4_address ("1.2.3.1", NULL, 30); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "1.2.3.2"); - g_assert_cmpstr (last, ==, "1.2.3.2"); - - addr = *nmtst_platform_ip4_address ("1.2.3.1", NULL, 29); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "1.2.3.2"); - g_assert_cmpstr (last, ==, "1.2.3.6"); - - addr = *nmtst_platform_ip4_address ("1.2.3.1", NULL, 28); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "1.2.3.3"); - g_assert_cmpstr (last, ==, "1.2.3.14"); - - addr = *nmtst_platform_ip4_address ("1.2.3.1", NULL, 26); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); - g_assert (error_desc == NULL); - g_assert_cmpstr (first, ==, "1.2.3.8"); - g_assert_cmpstr (last, ==, "1.2.3.62"); - - addr = *nmtst_platform_ip4_address ("1.2.3.1", NULL, 31); - g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc) == FALSE); - g_assert (error_desc); - g_free (error_desc); +#define _test_address_range(addr, plen, expected_first, expected_last) \ + G_STMT_START { \ + char *_error_desc = NULL; \ + char _first[INET_ADDRSTRLEN]; \ + char _last[INET_ADDRSTRLEN]; \ + \ + if (!nm_dnsmasq_utils_get_range (nmtst_platform_ip4_address ((addr""), NULL, (plen)), \ + _first, _last, &_error_desc)) \ + g_assert_not_reached (); \ + g_assert (!_error_desc); \ + g_assert_cmpstr (_first, ==, (expected_first"")); \ + g_assert_cmpstr (_last, ==, (expected_last"")); \ + g_assert_cmpint ((ntohl (nmtst_inet4_from_string (_last)) - ntohl (nmtst_inet4_from_string (_first))), <=, 244); \ + } G_STMT_END + +#define _test_address_range_fail(addr, plen) \ + G_STMT_START { \ + char *_error_desc = NULL; \ + char _first[INET_ADDRSTRLEN]; \ + char _last[INET_ADDRSTRLEN]; \ + \ + if (nm_dnsmasq_utils_get_range (nmtst_platform_ip4_address ((addr""), NULL, (plen)), \ + _first, _last, &_error_desc)) \ + g_assert_not_reached (); \ + g_assert (_error_desc); \ + g_free (_error_desc); \ + } G_STMT_END + + _test_address_range_fail ("1.2.3.1", 31); + + _test_address_range ("0.0.0.0", 30, "0.0.0.2", "0.0.0.2"); + _test_address_range ("0.0.0.1", 30, "0.0.0.2", "0.0.0.2"); + _test_address_range ("0.0.0.2", 30, "0.0.0.1", "0.0.0.1"); + _test_address_range ("0.0.0.3", 30, "0.0.0.1", "0.0.0.1"); + _test_address_range ("1.2.3.0", 30, "1.2.3.2", "1.2.3.2"); + _test_address_range ("1.2.3.1", 30, "1.2.3.2", "1.2.3.2"); + _test_address_range ("1.2.3.2", 30, "1.2.3.1", "1.2.3.1"); + _test_address_range ("1.2.3.3", 30, "1.2.3.1", "1.2.3.1"); + _test_address_range ("1.2.3.4", 30, "1.2.3.6", "1.2.3.6"); + _test_address_range ("1.2.3.5", 30, "1.2.3.6", "1.2.3.6"); + _test_address_range ("1.2.3.6", 30, "1.2.3.5", "1.2.3.5"); + _test_address_range ("1.2.3.7", 30, "1.2.3.5", "1.2.3.5"); + _test_address_range ("1.2.3.8", 30, "1.2.3.10", "1.2.3.10"); + _test_address_range ("1.2.3.9", 30, "1.2.3.10", "1.2.3.10"); + _test_address_range ("255.255.255.0", 30, "255.255.255.2", "255.255.255.2"); + _test_address_range ("255.255.255.1", 30, "255.255.255.2", "255.255.255.2"); + _test_address_range ("255.255.255.2", 30, "255.255.255.1", "255.255.255.1"); + _test_address_range ("255.255.255.3", 30, "255.255.255.1", "255.255.255.1"); + _test_address_range ("255.255.255.248", 30, "255.255.255.250", "255.255.255.250"); + _test_address_range ("255.255.255.249", 30, "255.255.255.250", "255.255.255.250"); + _test_address_range ("255.255.255.250", 30, "255.255.255.249", "255.255.255.249"); + _test_address_range ("255.255.255.251", 30, "255.255.255.249", "255.255.255.249"); + _test_address_range ("255.255.255.252", 30, "255.255.255.254", "255.255.255.254"); + _test_address_range ("255.255.255.253", 30, "255.255.255.254", "255.255.255.254"); + _test_address_range ("255.255.255.254", 30, "255.255.255.253", "255.255.255.253"); + _test_address_range ("255.255.255.255", 30, "255.255.255.253", "255.255.255.253"); + + _test_address_range ("0.0.0.0", 29, "0.0.0.2", "0.0.0.6"); + _test_address_range ("0.0.0.1", 29, "0.0.0.2", "0.0.0.6"); + _test_address_range ("0.0.0.2", 29, "0.0.0.3", "0.0.0.6"); + _test_address_range ("0.0.0.3", 29, "0.0.0.4", "0.0.0.6"); + _test_address_range ("0.0.0.4", 29, "0.0.0.1", "0.0.0.3"); + _test_address_range ("0.0.0.5", 29, "0.0.0.1", "0.0.0.4"); + _test_address_range ("0.0.0.6", 29, "0.0.0.1", "0.0.0.5"); + _test_address_range ("0.0.0.7", 29, "0.0.0.1", "0.0.0.5"); + _test_address_range ("0.0.0.8", 29, "0.0.0.10", "0.0.0.14"); + _test_address_range ("0.0.0.9", 29, "0.0.0.10", "0.0.0.14"); + _test_address_range ("1.2.3.0", 29, "1.2.3.2", "1.2.3.6"); + _test_address_range ("1.2.3.1", 29, "1.2.3.2", "1.2.3.6"); + _test_address_range ("1.2.3.2", 29, "1.2.3.3", "1.2.3.6"); + _test_address_range ("1.2.3.3", 29, "1.2.3.4", "1.2.3.6"); + _test_address_range ("1.2.3.4", 29, "1.2.3.1", "1.2.3.3"); + _test_address_range ("1.2.3.5", 29, "1.2.3.1", "1.2.3.4"); + _test_address_range ("1.2.3.6", 29, "1.2.3.1", "1.2.3.5"); + _test_address_range ("1.2.3.7", 29, "1.2.3.1", "1.2.3.5"); + _test_address_range ("1.2.3.8", 29, "1.2.3.10", "1.2.3.14"); + _test_address_range ("1.2.3.9", 29, "1.2.3.10", "1.2.3.14"); + _test_address_range ("255.255.255.248", 29, "255.255.255.250", "255.255.255.254"); + _test_address_range ("255.255.255.249", 29, "255.255.255.250", "255.255.255.254"); + _test_address_range ("255.255.255.250", 29, "255.255.255.251", "255.255.255.254"); + _test_address_range ("255.255.255.251", 29, "255.255.255.252", "255.255.255.254"); + _test_address_range ("255.255.255.252", 29, "255.255.255.249", "255.255.255.251"); + _test_address_range ("255.255.255.253", 29, "255.255.255.249", "255.255.255.252"); + _test_address_range ("255.255.255.254", 29, "255.255.255.249", "255.255.255.253"); + _test_address_range ("255.255.255.255", 29, "255.255.255.249", "255.255.255.253"); + + _test_address_range ("1.2.3.1", 29, "1.2.3.2", "1.2.3.6"); + _test_address_range ("1.2.3.1", 28, "1.2.3.3", "1.2.3.14"); + _test_address_range ("1.2.3.1", 26, "1.2.3.8", "1.2.3.62"); + + _test_address_range ("192.167.255.255", 24, "192.167.255.1", "192.167.255.245"); + _test_address_range ("192.168.0.0", 24, "192.168.0.10", "192.168.0.254"); + _test_address_range ("192.168.0.1", 24, "192.168.0.10", "192.168.0.254"); + _test_address_range ("192.168.0.2", 24, "192.168.0.11", "192.168.0.254"); + _test_address_range ("192.168.0.99", 24, "192.168.0.108", "192.168.0.254"); + _test_address_range ("192.168.0.126", 24, "192.168.0.135", "192.168.0.254"); + _test_address_range ("192.168.0.127", 24, "192.168.0.136", "192.168.0.254"); + _test_address_range ("192.168.0.128", 24, "192.168.0.1", "192.168.0.119"); + _test_address_range ("192.168.0.129", 24, "192.168.0.1", "192.168.0.120"); + _test_address_range ("192.168.0.130", 24, "192.168.0.1", "192.168.0.121"); + _test_address_range ("192.168.0.254", 24, "192.168.0.1", "192.168.0.245"); + _test_address_range ("192.168.0.255", 24, "192.168.0.1", "192.168.0.245"); + _test_address_range ("192.168.1.0", 24, "192.168.1.10", "192.168.1.254"); + _test_address_range ("192.168.1.1", 24, "192.168.1.10", "192.168.1.254"); + _test_address_range ("192.168.1.2", 24, "192.168.1.11", "192.168.1.254"); + _test_address_range ("192.168.1.10", 24, "192.168.1.19", "192.168.1.254"); + _test_address_range ("192.168.15.253", 24, "192.168.15.1", "192.168.15.244"); + _test_address_range ("192.168.15.254", 24, "192.168.15.1", "192.168.15.245"); + _test_address_range ("192.168.15.255", 24, "192.168.15.1", "192.168.15.245"); + _test_address_range ("192.168.16.0", 24, "192.168.16.10", "192.168.16.254"); + _test_address_range ("192.168.16.1", 24, "192.168.16.10", "192.168.16.254"); + + _test_address_range ("192.167.255.255", 20, "192.167.255.1", "192.167.255.245"); + _test_address_range ("192.168.0.0", 20, "192.168.0.10", "192.168.0.254"); + _test_address_range ("192.168.0.1", 20, "192.168.0.10", "192.168.0.254"); + _test_address_range ("192.168.0.2", 20, "192.168.0.11", "192.168.0.254"); + _test_address_range ("192.168.0.126", 20, "192.168.0.135", "192.168.0.254"); + _test_address_range ("192.168.0.127", 20, "192.168.0.136", "192.168.0.254"); + _test_address_range ("192.168.0.128", 20, "192.168.0.1", "192.168.0.119"); + _test_address_range ("192.168.0.129", 20, "192.168.0.1", "192.168.0.120"); + _test_address_range ("192.168.0.130", 20, "192.168.0.1", "192.168.0.121"); + _test_address_range ("192.168.0.254", 20, "192.168.0.1", "192.168.0.245"); + _test_address_range ("192.168.0.255", 20, "192.168.0.1", "192.168.0.245"); + _test_address_range ("192.168.1.0", 20, "192.168.1.10", "192.168.1.254"); + _test_address_range ("192.168.1.1", 20, "192.168.1.10", "192.168.1.254"); + _test_address_range ("192.168.1.2", 20, "192.168.1.11", "192.168.1.254"); + _test_address_range ("192.168.1.10", 20, "192.168.1.19", "192.168.1.254"); + _test_address_range ("192.168.15.253", 20, "192.168.15.1", "192.168.15.244"); + _test_address_range ("192.168.15.254", 20, "192.168.15.1", "192.168.15.245"); + _test_address_range ("192.168.15.255", 20, "192.168.15.1", "192.168.15.245"); + _test_address_range ("192.168.16.0", 20, "192.168.16.10", "192.168.16.254"); + _test_address_range ("192.168.16.1", 20, "192.168.16.10", "192.168.16.254"); } /*****************************************************************************/ diff --git a/src/main.c b/src/main.c index 52f0b7c8..d59da052 100644 --- a/src/main.c +++ b/src/main.c @@ -110,6 +110,7 @@ _init_nm_debug (NMConfig *config) flags = nm_utils_parse_debug_string (env, keys, G_N_ELEMENTS (keys)); flags |= nm_utils_parse_debug_string (debug, keys, G_N_ELEMENTS (keys)); +#if ! defined (__SANITIZE_ADDRESS__) if (NM_FLAGS_HAS (flags, D_RLIMIT_CORE)) { /* only enable this, if explicitly requested, because it might * expose sensitive data. */ @@ -120,6 +121,8 @@ _init_nm_debug (NMConfig *config) }; setrlimit (RLIMIT_CORE, &limit); } +#endif + if (NM_FLAGS_HAS (flags, D_FATAL_WARNINGS)) _set_g_fatal_warnings (); } @@ -194,7 +197,7 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli) N_("Log domains separated by ',': any combination of [%s]"), "PLATFORM,RFKILL,WIFI" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL }, - { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_(NM_DEFAULT_PID_FILE) }, + { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), NM_DEFAULT_PID_FILE }, { "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL }, { "print-config", 0, 0, G_OPTION_ARG_NONE, &global_opt.print_config, N_("Print NetworkManager configuration and exit"), NULL }, {NULL} @@ -354,11 +357,15 @@ main (int argc, char *argv[]) /* Set up unix signal handling - before creating threads, but after daemonizing! */ nm_main_utils_setup_signals (main_loop); - nm_logging_syslog_openlog (nm_config_data_get_value_cached (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_LOGGING, - NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY), - nm_config_get_is_debug (config)); + { + gs_free char *v = NULL; + + v = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + nm_logging_syslog_openlog (v, nm_config_get_is_debug (config)); + } nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s)", nm_config_get_first_start (config) ? "for the first time" : "after a restart"); diff --git a/src/ndisc/nm-fake-ndisc.c b/src/ndisc/nm-fake-ndisc.c index 7a9fb110..15abee88 100644 --- a/src/ndisc/nm-fake-ndisc.c +++ b/src/ndisc/nm-fake-ndisc.c @@ -51,7 +51,7 @@ typedef struct { guint32 timestamp; guint32 lifetime; guint32 preferred; - NMNDiscPreference preference; + NMIcmpv6RouterPref preference; } FakePrefix; /*****************************************************************************/ @@ -145,7 +145,7 @@ nm_fake_ndisc_add_gateway (NMFakeNDisc *self, const char *addr, guint32 timestamp, guint32 lifetime, - NMNDiscPreference preference) + NMIcmpv6RouterPref preference) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE (self); FakeRa *ra = find_ra (priv->ras, ra_id); @@ -169,7 +169,7 @@ nm_fake_ndisc_add_prefix (NMFakeNDisc *self, guint32 timestamp, guint32 lifetime, guint32 preferred, - NMNDiscPreference preference) + NMIcmpv6RouterPref preference) { NMFakeNDiscPrivate *priv = NM_FAKE_NDISC_GET_PRIVATE (self); FakeRa *ra = find_ra (priv->ras, ra_id); @@ -250,7 +250,7 @@ receive_ra (gpointer user_data) NMNDiscDataInternal *rdata = ndisc->rdata; FakeRa *ra = priv->ras->data; NMNDiscConfigMap changed = 0; - guint32 now = nm_utils_get_monotonic_timestamp_s (); + gint32 now = nm_utils_get_monotonic_timestamp_s (); guint i; NMNDiscDHCPLevel dhcp_level; diff --git a/src/ndisc/nm-fake-ndisc.h b/src/ndisc/nm-fake-ndisc.h index 2544c456..3266dc89 100644 --- a/src/ndisc/nm-fake-ndisc.h +++ b/src/ndisc/nm-fake-ndisc.h @@ -50,7 +50,7 @@ void nm_fake_ndisc_add_gateway (NMFakeNDisc *self, const char *addr, guint32 timestamp, guint32 lifetime, - NMNDiscPreference preference); + NMIcmpv6RouterPref preference); void nm_fake_ndisc_add_prefix (NMFakeNDisc *self, guint ra_id, @@ -60,7 +60,7 @@ void nm_fake_ndisc_add_prefix (NMFakeNDisc *self, guint32 timestamp, guint32 lifetime, guint32 preferred, - NMNDiscPreference preference); + NMIcmpv6RouterPref preference); void nm_fake_ndisc_add_dns_server (NMFakeNDisc *self, guint ra_id, diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 3bc1590e..70200ed3 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -93,12 +93,20 @@ send_rs (NMNDisc *ndisc, GError **error) return TRUE; } -_NM_UTILS_LOOKUP_DEFINE (static, translate_preference, enum ndp_route_preference, NMNDiscPreference, - NM_UTILS_LOOKUP_DEFAULT (NM_NDISC_PREFERENCE_INVALID), - NM_UTILS_LOOKUP_ITEM (NDP_ROUTE_PREF_LOW, NM_NDISC_PREFERENCE_LOW), - NM_UTILS_LOOKUP_ITEM (NDP_ROUTE_PREF_MEDIUM, NM_NDISC_PREFERENCE_MEDIUM), - NM_UTILS_LOOKUP_ITEM (NDP_ROUTE_PREF_HIGH, NM_NDISC_PREFERENCE_HIGH), -); +static NMIcmpv6RouterPref +_route_preference_coerce (enum ndp_route_preference pref) +{ + switch (pref) { + case NDP_ROUTE_PREF_LOW: + return NM_ICMPV6_ROUTER_PREF_LOW; + case NDP_ROUTE_PREF_MEDIUM: + return NM_ICMPV6_ROUTER_PREF_MEDIUM; + case NDP_ROUTE_PREF_HIGH: + return NM_ICMPV6_ROUTER_PREF_HIGH; + } + /* unexpected value must be treated as MEDIUM (RFC 4191). */ + return NM_ICMPV6_ROUTER_PREF_MEDIUM; +} static int receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) @@ -108,7 +116,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) NMNDiscConfigMap changed = 0; struct ndp_msgra *msgra = ndp_msgra (msg); struct in6_addr gateway_addr; - guint32 now = nm_utils_get_monotonic_timestamp_s (); + gint32 now = nm_utils_get_monotonic_timestamp_s (); int offset; int hop_limit; @@ -123,7 +131,11 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) * single time when the configuration is finished and updates can * come at any time. */ - _LOGD ("received router advertisement at %u", now); + _LOGD ("received router advertisement at %d", (int) now); + + gateway_addr = *ndp_msg_addrto (msg); + if (IN6_IS_ADDR_UNSPECIFIED (&gateway_addr)) + g_return_val_if_reached (0); /* DHCP level: * @@ -159,13 +171,12 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) * on the network. We should present all of them in router preference * order. */ - gateway_addr = *ndp_msg_addrto (msg); { - NMNDiscGateway gateway = { + const NMNDiscGateway gateway = { .address = gateway_addr, .timestamp = now, .lifetime = ndp_msgra_router_lifetime (msgra), - .preference = translate_preference (ndp_msgra_route_preference (msgra)), + .preference = _route_preference_coerce (ndp_msgra_route_preference (msgra)), }; if (nm_ndisc_add_gateway (ndisc, &gateway)) @@ -218,7 +229,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) .plen = ndp_msg_opt_route_prefix_len (msg, offset), .timestamp = now, .lifetime = ndp_msg_opt_route_lifetime (msg, offset), - .preference = translate_preference (ndp_msg_opt_route_preference (msg, offset)), + .preference = _route_preference_coerce (ndp_msg_opt_route_preference (msg, offset)), }; if (route.plen == 0 || route.plen > 128) @@ -337,7 +348,7 @@ send_ra (NMNDisc *ndisc, GError **error) { NMLndpNDiscPrivate *priv = NM_LNDP_NDISC_GET_PRIVATE ((NMLndpNDisc *) ndisc); NMNDiscDataInternal *rdata = ndisc->rdata; - guint32 now = nm_utils_get_monotonic_timestamp_s (); + gint32 now = nm_utils_get_monotonic_timestamp_s (); int errsv; struct in6_addr *addr; struct ndp_msg *msg; @@ -367,14 +378,14 @@ send_ra (NMNDisc *ndisc, GError **error) * whose prefixes are suitable for delegating. Let's announce them. */ for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); - guint32 age = now - address->timestamp; + guint32 age = NM_CLAMP ((gint64) now - (gint64) address->timestamp, 0, G_MAXUINT32 - 1); guint32 lifetime = address->lifetime; guint32 preferred = address->preferred; /* Clamp the life times if they're not forever. */ - if (lifetime != 0xffffffff) + if (lifetime != NM_NDISC_INFINITY) lifetime = lifetime > age ? lifetime - age : 0; - if (preferred != 0xffffffff) + if (preferred != NM_NDISC_INFINITY) preferred = preferred > age ? preferred - age : 0; prefix = _ndp_msg_add_option (msg, sizeof(*prefix)); @@ -520,8 +531,10 @@ start (NMNDisc *ndisc) static inline int ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, int min, int max, int defval) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + return (int) nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, property)), 10, min, max, diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h index 10bcc64f..bbecb01a 100644 --- a/src/ndisc/nm-ndisc-private.h +++ b/src/ndisc/nm-ndisc-private.h @@ -36,7 +36,7 @@ struct _NMNDiscDataInternal { typedef struct _NMNDiscDataInternal NMNDiscDataInternal; -void nm_ndisc_ra_received (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed); +void nm_ndisc_ra_received (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed); void nm_ndisc_rs_received (NMNDisc *ndisc); gboolean nm_ndisc_add_gateway (NMNDisc *ndisc, const NMNDiscGateway *new); diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index a50bbe43..6b44a96c 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -106,6 +106,24 @@ static void _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed); /*****************************************************************************/ +static guint8 +_preference_to_priority (NMIcmpv6RouterPref pref) +{ + switch (pref) { + case NM_ICMPV6_ROUTER_PREF_LOW: + return 1; + case NM_ICMPV6_ROUTER_PREF_MEDIUM: + return 2; + case NM_ICMPV6_ROUTER_PREF_HIGH: + return 3; + case NM_ICMPV6_ROUTER_PREF_INVALID: + break; + } + return 0; +} + +/*****************************************************************************/ + NMPNetns * nm_ndisc_netns_get (NMNDisc *self) { @@ -160,9 +178,43 @@ nm_ndisc_get_node_type (NMNDisc *self) /*****************************************************************************/ +static void +_ASSERT_data_gateways (const NMNDiscDataInternal *data) +{ +#if NM_MORE_ASSERTS > 10 + guint i, j; + const NMNDiscGateway *item_prev = NULL; + + if (!data->gateways->len) + return; + + for (i = 0; i < data->gateways->len; i++) { + const NMNDiscGateway *item = &g_array_index (data->gateways, NMNDiscGateway, i); + + nm_assert (!IN6_IS_ADDR_UNSPECIFIED (&item->address)); + nm_assert (item->timestamp > 0 && item->timestamp <= G_MAXINT32); + for (j = 0; j < i; j++) { + const NMNDiscGateway *item2 = &g_array_index (data->gateways, NMNDiscGateway, j); + + nm_assert (!IN6_ARE_ADDR_EQUAL (&item->address, &item2->address)); + } + + nm_assert (item->lifetime > 0); + if (i > 0) + nm_assert (_preference_to_priority (item_prev->preference) >= _preference_to_priority (item->preference)); + + item_prev = item; + } +#endif +} + +/*****************************************************************************/ + static const NMNDiscData * _data_complete (NMNDiscDataInternal *data) { + _ASSERT_data_gateways (data); + #define _SET(data, field) \ G_STMT_START { \ if ((data->public.field##_n = data->field->len) > 0) \ @@ -194,33 +246,45 @@ gboolean nm_ndisc_add_gateway (NMNDisc *ndisc, const NMNDiscGateway *new) { NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; - int i, insert_idx = -1; + guint i; + guint insert_idx = G_MAXUINT; - for (i = 0; i < rdata->gateways->len; i++) { + for (i = 0; i < rdata->gateways->len; ) { NMNDiscGateway *item = &g_array_index (rdata->gateways, NMNDiscGateway, i); if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { if (new->lifetime == 0) { - g_array_remove_index (rdata->gateways, i--); + g_array_remove_index (rdata->gateways, i); + _ASSERT_data_gateways (rdata); return TRUE; } if (item->preference != new->preference) { - g_array_remove_index (rdata->gateways, i--); + g_array_remove_index (rdata->gateways, i); continue; } - memcpy (item, new, sizeof (*new)); + *item = *new; + _ASSERT_data_gateways (rdata); return FALSE; } /* Put before less preferable gateways. */ - if (item->preference < new->preference && insert_idx < 0) + if ( _preference_to_priority (item->preference) < _preference_to_priority (new->preference) + && insert_idx == G_MAXUINT) insert_idx = i; + + i++; } - if (new->lifetime) - g_array_insert_val (rdata->gateways, MAX (insert_idx, 0), *new); + if (new->lifetime) { + g_array_insert_val (rdata->gateways, + insert_idx == G_MAXUINT + ? rdata->gateways->len + : insert_idx, + *new); + } + _ASSERT_data_gateways (rdata); return !!new->lifetime; } @@ -283,7 +347,7 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc); NMNDiscDataInternal *rdata = &priv->rdata; - int i; + guint i; for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); @@ -292,7 +356,7 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) gboolean changed; if (new->lifetime == 0) { - g_array_remove_index (rdata->addresses, i--); + g_array_remove_index (rdata->addresses, i); return TRUE; } @@ -307,11 +371,12 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) * what the kernel does, because it considers *all* addresses (including * static and other temporary addresses). **/ - if (priv->max_addresses && rdata->addresses->len >= priv->max_addresses) + if ( priv->max_addresses + && rdata->addresses->len >= priv->max_addresses) return FALSE; if (new->lifetime) - g_array_insert_val (rdata->addresses, i, *new); + g_array_append_val (rdata->addresses, *new); return !!new->lifetime; } @@ -329,7 +394,8 @@ nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new) { NMNDiscPrivate *priv; NMNDiscDataInternal *rdata; - int i, insert_idx = -1; + guint i; + guint insert_idx = G_MAXUINT; if (new->plen == 0 || new->plen > 128) { /* Only expect non-default routes. The router has no idea what the @@ -345,17 +411,17 @@ nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new) priv = NM_NDISC_GET_PRIVATE (ndisc); rdata = &priv->rdata; - for (i = 0; i < rdata->routes->len; i++) { + for (i = 0; i < rdata->routes->len; ) { NMNDiscRoute *item = &g_array_index (rdata->routes, NMNDiscRoute, i); if (IN6_ARE_ADDR_EQUAL (&item->network, &new->network) && item->plen == new->plen) { if (new->lifetime == 0) { - g_array_remove_index (rdata->routes, i--); + g_array_remove_index (rdata->routes, i); return TRUE; } if (item->preference != new->preference) { - g_array_remove_index (rdata->routes, i--); + g_array_remove_index (rdata->routes, i); continue; } @@ -364,12 +430,20 @@ nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new) } /* Put before less preferable routes. */ - if (item->preference < new->preference && insert_idx < 0) + if ( _preference_to_priority (item->preference) < _preference_to_priority (new->preference) + && insert_idx == G_MAXUINT) insert_idx = i; + + i++; } - if (new->lifetime) - g_array_insert_val (rdata->routes, CLAMP (insert_idx, 0, G_MAXINT), *new); + if (new->lifetime) { + g_array_insert_val (rdata->routes, + insert_idx == G_MAXUINT + ? 0u + : insert_idx, + *new); + } return !!new->lifetime; } @@ -378,7 +452,7 @@ nm_ndisc_add_dns_server (NMNDisc *ndisc, const NMNDiscDNSServer *new) { NMNDiscPrivate *priv; NMNDiscDataInternal *rdata; - int i; + guint i; priv = NM_NDISC_GET_PRIVATE (ndisc); rdata = &priv->rdata; @@ -400,7 +474,7 @@ nm_ndisc_add_dns_server (NMNDisc *ndisc, const NMNDiscDNSServer *new) } if (new->lifetime) - g_array_insert_val (rdata->dns_servers, i, *new); + g_array_append_val (rdata->dns_servers, *new); return !!new->lifetime; } @@ -411,7 +485,7 @@ nm_ndisc_add_dns_domain (NMNDisc *ndisc, const NMNDiscDNSDomain *new) NMNDiscPrivate *priv; NMNDiscDataInternal *rdata; NMNDiscDNSDomain *item; - int i; + guint i; priv = NM_NDISC_GET_PRIVATE (ndisc); rdata = &priv->rdata; @@ -438,8 +512,10 @@ nm_ndisc_add_dns_domain (NMNDisc *ndisc, const NMNDiscDNSDomain *new) } if (new->lifetime) { - g_array_insert_val (rdata->dns_domains, i, *new); - item = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i); + g_array_append_val (rdata->dns_domains, *new); + item = &g_array_index (rdata->dns_domains, + NMNDiscDNSDomain, + rdata->dns_domains->len - 1); item->domain = g_strdup (new->domain); } return !!new->lifetime; @@ -498,7 +574,8 @@ static void solicit_routers (NMNDisc *ndisc) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc); - gint64 next, now; + gint32 now, next; + gint64 t; if (priv->send_rs_id) return; @@ -506,9 +583,9 @@ solicit_routers (NMNDisc *ndisc) now = nm_utils_get_monotonic_timestamp_s (); priv->solicitations_left = priv->router_solicitations; - next = (((gint64) priv->last_rs) + priv->router_solicitation_interval) - now; - next = CLAMP (next, 0, G_MAXINT32); - _LOGD ("scheduling explicit router solicitation request in %" G_GINT64_FORMAT " seconds.", + t = (((gint64) priv->last_rs) + priv->router_solicitation_interval) - now; + next = CLAMP (t, 0, G_MAXINT32); + _LOGD ("scheduling explicit router solicitation request in %" G_GINT32_FORMAT " seconds.", next); priv->send_rs_id = g_timeout_add_seconds ((guint32) next, (GSourceFunc) send_rs_timeout, ndisc); } @@ -600,7 +677,7 @@ nm_ndisc_set_config (NMNDisc *ndisc, const GArray *dns_servers, const GArray *dns_domains) { - int changed = FALSE; + gboolean changed = FALSE; guint i; for (i = 0; i < addresses->len; i++) { @@ -718,21 +795,23 @@ void nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address) { NMNDiscDataInternal *rdata; - int i; + guint i; gboolean changed = FALSE; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->addresses->len; i++) { + for (i = 0; i < rdata->addresses->len; ) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); - if (!IN6_ARE_ADDR_EQUAL (&item->address, address)) - continue; - - _LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, NULL)); - if (!complete_address (ndisc, item)) - g_array_remove_index (rdata->addresses, i--); - changed = TRUE; + if (IN6_ARE_ADDR_EQUAL (&item->address, address)) { + _LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, NULL)); + changed = TRUE; + if (!complete_address (ndisc, item)) { + g_array_remove_index (rdata->addresses, i); + continue; + } + } + i++; } if (changed) @@ -774,16 +853,49 @@ dhcp_level_to_string (NMNDiscDHCPLevel dhcp_level) } } -#define expiry(item) (item->timestamp + item->lifetime) +static gint32 +get_expiry_time (guint32 timestamp, guint32 lifetime) +{ + gint64 t; + + /* timestamp is supposed to come from nm_utils_get_monotonic_timestamp_s(). + * It is expected to be within a certain range. */ + nm_assert (timestamp > 0); + nm_assert (timestamp <= G_MAXINT32); + + if (lifetime == NM_NDISC_INFINITY) + return G_MAXINT32; + + t = (gint64) timestamp + (gint64) lifetime; + return CLAMP (t, 0, G_MAXINT32 - 1); +} + +#define get_expiry(item) \ + ({ \ + typeof (item) _item = (item); \ + nm_assert (_item); \ + get_expiry_time ((_item->timestamp), (_item->lifetime)); \ + }) + +#define get_expiry_half(item) \ + ({ \ + typeof (item) _item = (item); \ + nm_assert (_item); \ + get_expiry_time ((_item->timestamp),\ + (_item->lifetime) == NM_NDISC_INFINITY \ + ? NM_NDISC_INFINITY \ + : (_item->lifetime) / 2); \ + }) static void _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) { NMNDiscPrivate *priv; NMNDiscDataInternal *rdata; - int i; + guint i; char changedstr[CONFIG_MAP_MAX_STR]; char addrstr[INET6_ADDRSTRLEN]; + char str_pref[35]; if (!_LOGD_ENABLED ()) return; @@ -798,165 +910,188 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) NMNDiscGateway *gateway = &g_array_index (rdata->gateways, NMNDiscGateway, i); inet_ntop (AF_INET6, &gateway->address, addrstr, sizeof (addrstr)); - _LOGD (" gateway %s pref %d exp %u", addrstr, gateway->preference, expiry (gateway)); + _LOGD (" gateway %s pref %s exp %d", addrstr, + nm_icmpv6_router_pref_to_string (gateway->preference, str_pref, sizeof (str_pref)), + get_expiry (gateway)); } for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); - _LOGD (" address %s exp %u", addrstr, expiry (address)); + _LOGD (" address %s exp %d", addrstr, get_expiry (address)); } for (i = 0; i < rdata->routes->len; i++) { NMNDiscRoute *route = &g_array_index (rdata->routes, NMNDiscRoute, i); inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr)); - _LOGD (" route %s/%d via %s pref %d exp %u", addrstr, (int) route->plen, - nm_utils_inet6_ntop (&route->gateway, NULL), route->preference, - expiry (route)); + _LOGD (" route %s/%u via %s pref %s exp %d", addrstr, (guint) route->plen, + nm_utils_inet6_ntop (&route->gateway, NULL), + nm_icmpv6_router_pref_to_string (route->preference, str_pref, sizeof (str_pref)), + get_expiry (route)); } for (i = 0; i < rdata->dns_servers->len; i++) { NMNDiscDNSServer *dns_server = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i); inet_ntop (AF_INET6, &dns_server->address, addrstr, sizeof (addrstr)); - _LOGD (" dns_server %s exp %u", addrstr, expiry (dns_server)); + _LOGD (" dns_server %s exp %d", addrstr, get_expiry (dns_server)); } for (i = 0; i < rdata->dns_domains->len; i++) { NMNDiscDNSDomain *dns_domain = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i); - _LOGD (" dns_domain %s exp %u", dns_domain->domain, expiry (dns_domain)); + _LOGD (" dns_domain %s exp %d", dns_domain->domain, get_expiry (dns_domain)); } } static void -clean_gateways (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint32 *nextevent) +clean_gateways (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) { NMNDiscDataInternal *rdata; guint i; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->gateways->len; i++) { + for (i = 0; i < rdata->gateways->len; ) { NMNDiscGateway *item = &g_array_index (rdata->gateways, NMNDiscGateway, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - if (item->lifetime == G_MAXUINT32) - continue; + if (item->lifetime != NM_NDISC_INFINITY) { + gint32 expiry = get_expiry (item); - if (now >= expiry) { - g_array_remove_index (rdata->gateways, i--); - *changed |= NM_NDISC_CONFIG_GATEWAYS; - } else if (*nextevent > expiry) - *nextevent = expiry; + if (now >= expiry) { + g_array_remove_index (rdata->gateways, i); + *changed |= NM_NDISC_CONFIG_GATEWAYS; + continue; + } + if (*nextevent > expiry) + *nextevent = expiry; + } + i++; } + + _ASSERT_data_gateways (rdata); } static void -clean_addresses (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint32 *nextevent) +clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) { NMNDiscDataInternal *rdata; guint i; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->addresses->len; i++) { + for (i = 0; i < rdata->addresses->len; ) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - if (item->lifetime == G_MAXUINT32) - continue; + if (item->lifetime != NM_NDISC_INFINITY) { + gint32 expiry = get_expiry (item); - if (now >= expiry) { - g_array_remove_index (rdata->addresses, i--); - *changed |= NM_NDISC_CONFIG_ADDRESSES; - } else if (*nextevent > expiry) - *nextevent = expiry; + if (now >= expiry) { + g_array_remove_index (rdata->addresses, i); + *changed |= NM_NDISC_CONFIG_ADDRESSES; + continue; + } + if (*nextevent > expiry) + *nextevent = expiry; + } + i++; } } static void -clean_routes (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint32 *nextevent) +clean_routes (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) { NMNDiscDataInternal *rdata; guint i; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->routes->len; i++) { + for (i = 0; i < rdata->routes->len; ) { NMNDiscRoute *item = &g_array_index (rdata->routes, NMNDiscRoute, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - if (item->lifetime == G_MAXUINT32) - continue; + if (item->lifetime != NM_NDISC_INFINITY) { + gint32 expiry = get_expiry (item); - if (now >= expiry) { - g_array_remove_index (rdata->routes, i--); - *changed |= NM_NDISC_CONFIG_ROUTES; - } else if (*nextevent > expiry) - *nextevent = expiry; + if (now >= expiry) { + g_array_remove_index (rdata->routes, i); + *changed |= NM_NDISC_CONFIG_ROUTES; + continue; + } + if (*nextevent > expiry) + *nextevent = expiry; + } + i++; } } static void -clean_dns_servers (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint32 *nextevent) +clean_dns_servers (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) { NMNDiscDataInternal *rdata; guint i; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->dns_servers->len; i++) { + for (i = 0; i < rdata->dns_servers->len; ) { NMNDiscDNSServer *item = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; - if (item->lifetime == G_MAXUINT32) - continue; + if (item->lifetime != NM_NDISC_INFINITY) { + gint32 expiry = get_expiry (item); + gint32 refresh; - if (now >= expiry) { - g_array_remove_index (rdata->dns_servers, i--); - *changed |= NM_NDISC_CONFIG_DNS_SERVERS; - } else if (now >= refresh) - solicit_routers (ndisc); - else if (*nextevent > refresh) - *nextevent = refresh; + if (now >= expiry) { + g_array_remove_index (rdata->dns_servers, i); + *changed |= NM_NDISC_CONFIG_DNS_SERVERS; + continue; + } + + refresh = get_expiry_half (item); + if (now >= refresh) + solicit_routers (ndisc); + else if (*nextevent > refresh) + *nextevent = refresh; + } + i++; } } static void -clean_dns_domains (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint32 *nextevent) +clean_dns_domains (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *nextevent) { NMNDiscDataInternal *rdata; guint i; rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; - for (i = 0; i < rdata->dns_domains->len; i++) { + for (i = 0; i < rdata->dns_domains->len; ) { NMNDiscDNSDomain *item = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; - if (item->lifetime == G_MAXUINT32) - continue; + if (item->lifetime != NM_NDISC_INFINITY) { + gint32 expiry = get_expiry (item); + gint32 refresh; - if (now >= expiry) { - g_array_remove_index (rdata->dns_domains, i--); - *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; - } else if (now >= refresh) - solicit_routers (ndisc); - else if (*nextevent > refresh) - *nextevent = refresh; + if (now >= expiry) { + g_array_remove_index (rdata->dns_domains, i); + *changed |= NM_NDISC_CONFIG_DNS_DOMAINS; + continue; + } + + refresh = get_expiry_half (item); + if (now >= refresh) + solicit_routers (ndisc); + else if (*nextevent > refresh) + *nextevent = refresh; + } + i++; } } static gboolean timeout_cb (gpointer user_data); static void -check_timestamps (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed) +check_timestamps (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc); /* Use a magic date in the distant future (~68 years) */ - guint32 never = G_MAXINT32; - guint32 nextevent = never; + gint32 nextevent = G_MAXINT32; nm_clear_g_source (&priv->timeout_id); @@ -969,10 +1104,11 @@ check_timestamps (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed) if (changed) _emit_config_change (ndisc, changed); - if (nextevent != never) { - g_return_if_fail (nextevent > now); - _LOGD ("scheduling next now/lifetime check: %u seconds", - nextevent - now); + if (nextevent != G_MAXINT32) { + if (nextevent <= now) + g_return_if_reached (); + _LOGD ("scheduling next now/lifetime check: %d seconds", + (int) (nextevent - now)); priv->timeout_id = g_timeout_add_seconds (nextevent - now, timeout_cb, ndisc); } } @@ -988,7 +1124,7 @@ timeout_cb (gpointer user_data) } void -nm_ndisc_ra_received (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed) +nm_ndisc_ra_received (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc); diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 7c67289d..b66c2289 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -55,36 +55,34 @@ typedef enum { NM_NDISC_DHCP_LEVEL_MANAGED } NMNDiscDHCPLevel; -typedef enum { - NM_NDISC_PREFERENCE_INVALID, - NM_NDISC_PREFERENCE_LOW, - NM_NDISC_PREFERENCE_MEDIUM, - NM_NDISC_PREFERENCE_HIGH -} NMNDiscPreference; +#define NM_NDISC_INFINITY G_MAXUINT32 -typedef struct { +struct _NMNDiscGateway { struct in6_addr address; guint32 timestamp; guint32 lifetime; - NMNDiscPreference preference; -} NMNDiscGateway; + NMIcmpv6RouterPref preference; +}; +typedef struct _NMNDiscGateway NMNDiscGateway; -typedef struct { +struct _NMNDiscAddress { struct in6_addr address; guint8 dad_counter; guint32 timestamp; guint32 lifetime; guint32 preferred; -} NMNDiscAddress; +}; +typedef struct _NMNDiscAddress NMNDiscAddress; -typedef struct { +struct _NMNDiscRoute { struct in6_addr network; guint8 plen; struct in6_addr gateway; guint32 timestamp; guint32 lifetime; - NMNDiscPreference preference; -} NMNDiscRoute; + NMIcmpv6RouterPref preference; +}; +typedef struct _NMNDiscRoute NMNDiscRoute; typedef struct { struct in6_addr address; diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c index 006aea7f..e99d2fc5 100644 --- a/src/ndisc/tests/test-ndisc-fake.c +++ b/src/ndisc/tests/test-ndisc-fake.c @@ -46,7 +46,7 @@ ndisc_new (void) } static void -match_gateway (const NMNDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt, NMNDiscPreference pref) +match_gateway (const NMNDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt, NMIcmpv6RouterPref pref) { const NMNDiscGateway *gw; char buf[INET6_ADDRSTRLEN]; @@ -82,7 +82,7 @@ match_address (const NMNDiscData *rdata, guint idx, const char *addr, guint32 ts } static void -match_route (const NMNDiscData *rdata, guint idx, const char *nw, int plen, const char *gw, guint32 ts, guint32 lt, NMNDiscPreference pref) +match_route (const NMNDiscData *rdata, guint idx, const char *nw, int plen, const char *gw, guint32 ts, guint32 lt, NMIcmpv6RouterPref pref) { const NMNDiscRoute *route; char buf[INET6_ADDRSTRLEN]; @@ -158,7 +158,7 @@ test_simple_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int NM_NDISC_CONFIG_HOP_LIMIT | NM_NDISC_CONFIG_MTU); g_assert_cmpint (rdata->dhcp_level, ==, NM_NDISC_DHCP_LEVEL_OTHERCONF); - match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_NDISC_PREFERENCE_MEDIUM); + match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10); match_dns_server (rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10); @@ -179,7 +179,7 @@ test_simple (void) id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_OTHERCONF, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10); nm_fake_ndisc_add_dns_server (ndisc, id, "2001:db8:c:c::1", now, 10); nm_fake_ndisc_add_dns_domain (ndisc, id, "foobar.com", now, 10); @@ -219,7 +219,7 @@ test_everything_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed NM_NDISC_CONFIG_DNS_DOMAINS | NM_NDISC_CONFIG_HOP_LIMIT | NM_NDISC_CONFIG_MTU); - match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_NDISC_PREFERENCE_MEDIUM); + match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10); match_dns_server (rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10); @@ -232,7 +232,7 @@ test_everything_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed NM_NDISC_CONFIG_DNS_DOMAINS); g_assert_cmpint (rdata->gateways_n, ==, 1); - match_gateway (rdata, 0, "fe80::2", data->timestamp1, 10, NM_NDISC_PREFERENCE_MEDIUM); + match_gateway (rdata, 0, "fe80::2", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); g_assert_cmpint (rdata->addresses_n, ==, 1); match_address (rdata, 0, "2001:db8:a:b::1", data->timestamp1, 10, 10); g_assert_cmpint (rdata->routes_n, ==, 1); @@ -260,7 +260,7 @@ test_everything (void) id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 10); nm_fake_ndisc_add_dns_server (ndisc, id, "2001:db8:c:c::1", now, 10); nm_fake_ndisc_add_dns_domain (ndisc, id, "foobar.com", now, 10); @@ -268,13 +268,13 @@ test_everything (void) /* expire everything from the first RA in the second */ id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 0, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 0, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 0, 0, 0); nm_fake_ndisc_add_dns_server (ndisc, id, "2001:db8:c:c::1", now, 0); nm_fake_ndisc_add_dns_domain (ndisc, id, "foobar.com", now, 0); /* and add some new stuff */ - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::2", now, 10, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::2", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); nm_fake_ndisc_add_dns_server (ndisc, id, "2001:db8:c:c::2", now, 10); nm_fake_ndisc_add_dns_domain (ndisc, id, "foobar2.com", now, 10); @@ -298,7 +298,67 @@ test_everything (void) } static void -test_preference_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) +test_preference_order_cb (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) +{ + NMNDiscConfigMap changed = changed_int; + + if (data->counter == 1) { + g_assert_cmpint (changed, ==, NM_NDISC_CONFIG_GATEWAYS | + NM_NDISC_CONFIG_ADDRESSES | + NM_NDISC_CONFIG_ROUTES); + + g_assert_cmpint (rdata->gateways_n, ==, 2); + match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_HIGH); + match_gateway (rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_LOW); + g_assert_cmpint (rdata->addresses_n, ==, 2); + match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); + match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); + g_assert_cmpint (rdata->routes_n, ==, 2); + match_route (rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10); + match_route (rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5); + + g_assert (nm_fake_ndisc_done (NM_FAKE_NDISC (ndisc))); + g_main_loop_quit (data->loop); + } + + data->counter++; +} + +static void +test_preference_order (void) +{ + NMFakeNDisc *ndisc = ndisc_new (); + guint32 now = nm_utils_get_monotonic_timestamp_s (); + TestData data = { g_main_loop_new (NULL, FALSE), 0, 0, now }; + guint id; + + /* Test insertion order of gateways */ + + id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); + g_assert (id); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_HIGH); + nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 5); + + id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); + g_assert (id); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::2", ++now, 10, NM_ICMPV6_ROUTER_PREF_LOW); + nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); + + g_signal_connect (ndisc, + NM_NDISC_CONFIG_RECEIVED, + G_CALLBACK (test_preference_order_cb), + &data); + + nm_ndisc_start (NM_NDISC (ndisc)); + g_main_loop_run (data.loop); + g_assert_cmpint (data.counter, ==, 2); + + g_object_unref (ndisc); + g_main_loop_unref (data.loop); +} + +static void +test_preference_changed_cb (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) { NMNDiscConfigMap changed = changed_int; @@ -307,8 +367,8 @@ test_preference_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed NM_NDISC_CONFIG_ADDRESSES | NM_NDISC_CONFIG_ROUTES); g_assert_cmpint (rdata->gateways_n, ==, 2); - match_gateway (rdata, 0, "fe80::2", data->timestamp1 + 1, 10, NM_NDISC_PREFERENCE_MEDIUM); - match_gateway (rdata, 1, "fe80::1", data->timestamp1, 10, NM_NDISC_PREFERENCE_LOW); + match_gateway (rdata, 0, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); + match_gateway (rdata, 1, "fe80::1", data->timestamp1, 10, NM_ICMPV6_ROUTER_PREF_LOW); g_assert_cmpint (rdata->addresses_n, ==, 2); match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10); match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); @@ -321,8 +381,8 @@ test_preference_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed NM_NDISC_CONFIG_ROUTES); g_assert_cmpint (rdata->gateways_n, ==, 2); - match_gateway (rdata, 0, "fe80::1", data->timestamp1 + 2, 10, NM_NDISC_PREFERENCE_HIGH); - match_gateway (rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_NDISC_PREFERENCE_MEDIUM); + match_gateway (rdata, 0, "fe80::1", data->timestamp1 + 2, 10, NM_ICMPV6_ROUTER_PREF_HIGH); + match_gateway (rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); g_assert_cmpint (rdata->addresses_n, ==, 2); match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1 + 2, 10, 10); match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10); @@ -338,7 +398,7 @@ test_preference_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed } static void -test_preference (void) +test_preference_changed (void) { NMFakeNDisc *ndisc = ndisc_new (); guint32 now = nm_utils_get_monotonic_timestamp_s (); @@ -352,22 +412,22 @@ test_preference (void) id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_NDISC_PREFERENCE_LOW); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_LOW); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 5); id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::2", ++now, 10, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::2", ++now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:b::", 64, "fe80::2", now, 10, 10, 10); id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", ++now, 10, NM_NDISC_PREFERENCE_HIGH); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", ++now, 10, NM_ICMPV6_ROUTER_PREF_HIGH); nm_fake_ndisc_add_prefix (ndisc, id, "2001:db8:a:a::", 64, "fe80::1", now, 10, 10, 15); g_signal_connect (ndisc, NM_NDISC_CONFIG_RECEIVED, - G_CALLBACK (test_preference_changed), + G_CALLBACK (test_preference_changed_cb), &data); nm_ndisc_start (NM_NDISC (ndisc)); @@ -411,7 +471,7 @@ test_dns_solicit_loop_rs_sent (NMFakeNDisc *ndisc, TestData *data) */ id = nm_fake_ndisc_add_ra (ndisc, 0, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_NDISC_PREFERENCE_MEDIUM); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_MEDIUM); nm_fake_ndisc_emit_new_ras (ndisc); } else if (data->rs_counter >= 6) { @@ -440,7 +500,7 @@ test_dns_solicit_loop (void) id = nm_fake_ndisc_add_ra (ndisc, 1, NM_NDISC_DHCP_LEVEL_NONE, 4, 1500); g_assert (id); - nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_NDISC_PREFERENCE_LOW); + nm_fake_ndisc_add_gateway (ndisc, id, "fe80::1", now, 10, NM_ICMPV6_ROUTER_PREF_LOW); nm_fake_ndisc_add_dns_server (ndisc, id, "2001:db8:c:c::1", now, 6); g_signal_connect (ndisc, @@ -476,7 +536,8 @@ main (int argc, char **argv) g_test_add_func ("/ndisc/simple", test_simple); g_test_add_func ("/ndisc/everything-changed", test_everything); - g_test_add_func ("/ndisc/preference-changed", test_preference); + g_test_add_func ("/ndisc/preference-order", test_preference_order); + g_test_add_func ("/ndisc/preference-changed", test_preference_changed); g_test_add_func ("/ndisc/dns-solicit-loop", test_dns_solicit_loop); return g_test_run (); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 862754f9..002f11f7 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -44,6 +44,8 @@ typedef struct _NMActiveConnectionPrivate { char *pending_activation_id; + NMActivationStateFlags state_flags; + NMActiveConnectionState state; bool is_default:1; bool is_default6:1; @@ -73,6 +75,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, PROP_SPECIFIC_OBJECT, PROP_DEVICES, PROP_STATE, + PROP_STATE_FLAGS, PROP_DEFAULT, PROP_IP4_CONFIG, PROP_DHCP4_CONFIG, @@ -120,12 +123,11 @@ static void _set_activation_type_managed (NMActiveConnection *self); #define _NMLOG(level, ...) \ G_STMT_START { \ char _sbuf[64]; \ - NMDevice *_device = (self) ? NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->device : NULL; \ - NMConnection *_applied_connection = _device ? NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->applied_connection : NULL; \ + NMActiveConnectionPrivate *_priv = self ? NM_ACTIVE_CONNECTION_GET_PRIVATE (self) : NULL; \ \ nm_log ((level), _NMLOG_DOMAIN, \ - (_device) ? nm_device_get_iface (_device) : NULL, \ - (_applied_connection) ? nm_connection_get_uuid (_applied_connection) : NULL, \ + (_priv && _priv->device) ? nm_device_get_iface (_priv->device) : NULL, \ + (_priv && _priv->applied_connection) ? nm_connection_get_uuid (_priv->applied_connection) : NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ self ? nm_sprintf_buf (_sbuf, "[%p]", self) : "" \ @@ -144,6 +146,16 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, NMActiveConnectionState, ); #define state_to_string(state) NM_UTILS_LOOKUP_STR (_state_to_string, state) +NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags, + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"), +); + /*****************************************************************************/ static void @@ -280,6 +292,33 @@ nm_active_connection_set_state (NMActiveConnection *self, } } +NMActivationStateFlags +nm_active_connection_get_state_flags (NMActiveConnection *self) +{ + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->state_flags; +} + +void +nm_active_connection_set_state_flags_full (NMActiveConnection *self, + NMActivationStateFlags state_flags, + NMActivationStateFlags mask) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + NMActivationStateFlags f; + + f = (priv->state_flags & ~mask) | (state_flags & mask); + if (f != priv->state_flags) { + char buf1[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; + char buf2[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; + + _LOGD ("set state-flags %s (was %s)", + _state_flags_to_string (f, buf1, sizeof (buf1)), + _state_flags_to_string (priv->state_flags, buf2, sizeof (buf2))); + priv->state_flags = f; + _notify (self, PROP_STATE_FLAGS); + } +} + const char * nm_active_connection_get_settings_connection_id (NMActiveConnection *self) { @@ -331,6 +370,39 @@ nm_active_connection_get_applied_connection (NMActiveConnection *self) return con; } +static void +_set_applied_connection_take (NMActiveConnection *self, + NMConnection *applied_connection) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + NMSettingConnection *s_con; + NMActivationStateFlags flags_val = 0; + + nm_assert (NM_IS_CONNECTION (applied_connection)); + nm_assert (!priv->applied_connection); + + /* we take ownership of @applied_connection. Ensure to pass in a reference. */ + priv->applied_connection = applied_connection; + nm_connection_clear_secrets (priv->applied_connection); + + /* we determine whether the connection is a master/slave, based solely + * on the connection properties itself. */ + s_con = nm_connection_get_setting_connection (priv->applied_connection); + if (nm_setting_connection_get_master (s_con)) + flags_val |= NM_ACTIVATION_STATE_FLAG_IS_SLAVE; + + if (NM_IN_STRSET (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME)) + flags_val |= NM_ACTIVATION_STATE_FLAG_IS_MASTER; + + nm_active_connection_set_state_flags_full (self, + flags_val, + NM_ACTIVATION_STATE_FLAG_IS_MASTER + | NM_ACTIVATION_STATE_FLAG_IS_SLAVE); +} + void nm_active_connection_set_settings_connection (NMActiveConnection *self, NMSettingsConnection *connection) @@ -355,8 +427,9 @@ nm_active_connection_set_settings_connection (NMActiveConnection *self, g_return_if_fail (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self))); _set_settings_connection (self, connection); - priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection)); - nm_connection_clear_secrets (priv->applied_connection); + + _set_applied_connection_take (self, + nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection))); } gboolean @@ -1091,6 +1164,9 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, NM_ACTIVE_CONNECTION_STATE_ACTIVATING); } break; + case PROP_STATE_FLAGS: + g_value_set_uint (value, priv->state_flags); + break; case PROP_DEFAULT: g_value_set_boolean (value, priv->is_default); break; @@ -1151,8 +1227,15 @@ set_property (GObject *object, guint prop_id, case PROP_INT_APPLIED_CONNECTION: /* construct-only */ acon = g_value_get_object (value); - if (acon) + if (acon) { + /* we don't call _set_applied_connection_take() yet, because the instance + * is not yet fully initialized. We are currently in the process of setting + * the constructor properties. + * + * For now, just piggyback the connection, but call _set_applied_connection_take() + * in constructed(). */ priv->applied_connection = g_object_ref (acon); + } break; case PROP_INT_DEVICE: /* construct-only */ @@ -1220,17 +1303,25 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object); - if (!priv->applied_connection && priv->settings_connection) + if ( !priv->applied_connection + && priv->settings_connection) priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection)); - if (priv->applied_connection) - nm_connection_clear_secrets (priv->applied_connection); - _LOGD ("constructed (%s, version-id %llu, type %s)", G_OBJECT_TYPE_NAME (self), (unsigned long long) priv->version_id, nm_activation_type_to_string (priv->activation_type)); + if (priv->applied_connection) { + /* priv->applied_connection was set during the construction of the object. + * It's not yet fully initialized, so do that now. + * + * We delayed that, because we may log in _set_applied_connection_take(), and the + * first logging line should be "constructed" above). */ + _set_applied_connection_take (self, + g_steal_pointer (&priv->applied_connection)); + } + g_return_if_fail (priv->subject); } @@ -1330,6 +1421,12 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_STATE_FLAGS] = + g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE_FLAGS, "", "", + 0, G_MAXUINT32, NM_ACTIVATION_STATE_FLAG_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_DEFAULT] = g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "", "", FALSE, diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 8d3478c7..5562b42f 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -39,6 +39,7 @@ #define NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT "specific-object" #define NM_ACTIVE_CONNECTION_DEVICES "devices" #define NM_ACTIVE_CONNECTION_STATE "state" +#define NM_ACTIVE_CONNECTION_STATE_FLAGS "state-flags" #define NM_ACTIVE_CONNECTION_DEFAULT "default" #define NM_ACTIVE_CONNECTION_IP4_CONFIG "ip4-config" #define NM_ACTIVE_CONNECTION_DHCP4_CONFIG "dhcp4-config" @@ -145,6 +146,19 @@ void nm_active_connection_set_state (NMActiveConnection *self, NMActiveConnectionState state, NMActiveConnectionStateReason reason); +NMActivationStateFlags nm_active_connection_get_state_flags (NMActiveConnection *self); + +void nm_active_connection_set_state_flags_full (NMActiveConnection *self, + NMActivationStateFlags state_flags, + NMActivationStateFlags mask); + +static inline void +nm_active_connection_set_state_flags (NMActiveConnection *self, + NMActivationStateFlags state_flags) +{ + nm_active_connection_set_state_flags_full (self, state_flags, state_flags); +} + NMDevice * nm_active_connection_get_device (NMActiveConnection *self); gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device); diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index b8e64ceb..f1aff430 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -24,6 +24,7 @@ #include <string.h> +#include "nm-utils/nm-hash-utils.h" #include "nm-setting-connection.h" #include "nm-auth-subject.h" #include "nm-auth-manager.h" @@ -131,7 +132,7 @@ nm_auth_chain_new_subject (NMAuthSubject *subject, self = g_slice_new0 (NMAuthChain); self->refcount = 1; - self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, chain_data_free); + self->data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, chain_data_free); self->done_func = done_func; self->user_data = user_data; self->context = context ? g_object_ref (context) : NULL; diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c index ca07e3d9..f6b86e90 100644 --- a/src/nm-bus-manager.c +++ b/src/nm-bus-manager.c @@ -171,7 +171,10 @@ close_connection_in_idle (gpointer user_data) g_hash_table_iter_init (&iter, server->obj_managers); while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) { - if (g_dbus_object_manager_server_get_connection (manager) == info->connection) { + gs_unref_object GDBusConnection *connection = NULL; + + connection = g_dbus_object_manager_server_get_connection (manager); + if (connection == info->connection) { g_hash_table_iter_remove (&iter); break; } @@ -250,6 +253,7 @@ private_server_manager_destroy (GDBusObjectManagerServer *manager) g_dbus_connection_close (connection, NULL, NULL, NULL); g_dbus_object_manager_server_set_connection (manager, NULL); g_object_unref (manager); + g_object_unref (connection); } static gboolean @@ -368,7 +372,10 @@ private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connecti g_hash_table_iter_init (&iter, s->obj_managers); while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &owner)) { - if (g_dbus_object_manager_server_get_connection (manager) == connection) + gs_unref_object GDBusConnection *c = NULL; + + c = g_dbus_object_manager_server_get_connection (manager); + if (c == connection) return owner; } return NULL; @@ -606,7 +613,10 @@ nm_bus_manager_get_unix_user (NMBusManager *self, /* Check if it's a private connection sender, which we fake */ for (iter = priv->private_servers; iter; iter = iter->next) { - if (private_server_get_connection_by_owner (iter->data, sender)) { + gs_unref_object GDBusConnection *connection = NULL; + + connection = private_server_get_connection_by_owner (iter->data, sender); + if (connection) { *out_uid = 0; return TRUE; } diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 7f9aa398..033c11cc 100644 --- a/src/nm-checkpoint-manager.c +++ b/src/nm-checkpoint-manager.c @@ -283,7 +283,7 @@ nm_checkpoint_manager_new (NMManager *manager) * of NMManager shall surpass the lifetime of the NMCheckpointManager * instance. */ self->_manager = manager; - self->checkpoints = g_hash_table_new_full (g_str_hash, g_str_equal, + self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, checkpoint_destroy); return self; diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index 04f00e89..c3a2e743 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -259,8 +259,8 @@ activate: nm_connection_replace_settings_from_connection (NM_CONNECTION (connection), dev_checkpoint->settings_connection); nm_settings_connection_commit_changes (connection, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, NULL); } } else { @@ -343,7 +343,7 @@ next_dev: nm_settings_connection_get_uuid (con))) { _LOGD ("rollback: deleting new connection %s", nm_settings_connection_get_uuid (con)); - nm_settings_connection_delete (con, NULL, NULL); + nm_settings_connection_delete (con, NULL); } } } @@ -506,7 +506,7 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time priv->flags = flags; if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { - priv->connection_uuids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) { g_hash_table_add (priv->connection_uuids, g_strdup (nm_settings_connection_get_uuid (*con))); diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 8f4d1121..5b06e00a 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -65,6 +65,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( PROP_CONFIG_DESCRIPTION, PROP_KEYFILE_USER, PROP_KEYFILE_INTERN, + PROP_CONNECTIVITY_ENABLED, PROP_CONNECTIVITY_URI, PROP_CONNECTIVITY_INTERVAL, PROP_CONNECTIVITY_RESPONSE, @@ -88,6 +89,7 @@ typedef struct { MatchSectionInfo *device_infos; struct { + gboolean enabled; char *uri; char *response; guint interval; @@ -106,9 +108,6 @@ typedef struct { char *rc_manager; NMGlobalDnsConfig *global_dns; - - /* mutable field */ - char *value_cached; } NMConfigDataPrivate; struct _NMConfigData { @@ -169,22 +168,6 @@ nm_config_data_get_value (const NMConfigData *self, const char *group, const cha return nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags); } -const char *nm_config_data_get_value_cached (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) -{ - const NMConfigDataPrivate *priv; - - g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); - g_return_val_if_fail (group && *group, NULL); - g_return_val_if_fail (key && *key, NULL); - - priv = NM_CONFIG_DATA_GET_PRIVATE (self); - - /* we modify @value_cached. In C++ jargon, the field is mutable. */ - g_free (((NMConfigDataPrivate *) priv)->value_cached); - ((NMConfigDataPrivate *) priv)->value_cached = nm_config_keyfile_get_value (priv->keyfile, group, key, flags); - return priv->value_cached; -} - gboolean nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) { @@ -209,7 +192,7 @@ nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, c g_return_val_if_fail (key && *key, default_value); /* when parsing the boolean, base it on the raw value from g_key_file_get_value(). */ - str = g_key_file_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, NULL); + str = nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, NM_CONFIG_GET_VALUE_RAW); if (str) { value = nm_config_parse_boolean (str, default_value); g_free (str); @@ -217,6 +200,28 @@ nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, c return value; } +gint64 +nm_config_data_get_value_int64 (const NMConfigData *self, const char *group, const char *key, guint base, gint64 min, gint64 max, gint64 fallback) +{ + int errsv; + gint64 val; + char *str; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), fallback); + g_return_val_if_fail (group && *group, fallback); + g_return_val_if_fail (key && *key, fallback); + + str = nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, NM_CONFIG_GET_VALUE_NONE); + val = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback); + if (str) { + /* preserve errno from the parsing. */ + errsv = errno; + g_free (str); + errno = errsv; + } + return val; +} + char ** nm_config_data_get_plugins (const NMConfigData *self, gboolean allow_default) { @@ -238,6 +243,14 @@ nm_config_data_get_plugins (const NMConfigData *self, gboolean allow_default) return _nm_utils_strv_cleanup (list, TRUE, TRUE, TRUE); } +gboolean +nm_config_data_get_connectivity_enabled (const NMConfigData *self) +{ + g_return_val_if_fail (self, FALSE); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->connectivity.enabled; +} + const char * nm_config_data_get_connectivity_uri (const NMConfigData *self) { @@ -304,15 +317,22 @@ nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) { gs_free char *value = NULL; gboolean has_match; + int m; g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); value = nm_config_data_get_device_config (self, NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, device, &has_match); if (has_match) - return nm_config_parse_boolean (value, FALSE); + m = nm_config_parse_boolean (value, -1); + else + m = nm_device_spec_match_list_full (device, NM_CONFIG_DATA_GET_PRIVATE (self)->ignore_carrier, -1); + + if (NM_IN_SET (m, TRUE, FALSE)) + return m; - return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->ignore_carrier); + /* if ignore-carrier is not explicitly configed, then it depends on the device (type). */ + return nm_device_ignore_carrier_by_default (device); } gboolean @@ -858,7 +878,7 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) return NULL; conf = g_malloc0 (sizeof (NMGlobalDnsConfig)); - conf->domains = g_hash_table_new_full (g_str_hash, g_str_equal, + conf->domains = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) global_dns_domain_free); strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL); @@ -1060,7 +1080,7 @@ nm_global_dns_config_from_dbus (const GValue *value, GError **error) } dns_config = g_malloc0 (sizeof (NMGlobalDnsConfig)); - dns_config->domains = g_hash_table_new_full (g_str_hash, g_str_equal, + dns_config->domains = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) global_dns_domain_free); g_variant_iter_init (&iter, variant); @@ -1373,7 +1393,8 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data) || g_strcmp0 (nm_config_data_get_config_description (old_data), nm_config_data_get_config_description (new_data)) != 0) changes |= NM_CONFIG_CHANGE_CONFIG_FILES; - if ( nm_config_data_get_connectivity_interval (old_data) != nm_config_data_get_connectivity_interval (new_data) + if ( nm_config_data_get_connectivity_enabled (old_data) != nm_config_data_get_connectivity_enabled (new_data) + || nm_config_data_get_connectivity_interval (old_data) != nm_config_data_get_connectivity_interval (new_data) || g_strcmp0 (nm_config_data_get_connectivity_uri (old_data), nm_config_data_get_connectivity_uri (new_data)) || g_strcmp0 (nm_config_data_get_connectivity_response (old_data), nm_config_data_get_connectivity_response (new_data))) changes |= NM_CONFIG_CHANGE_CONNECTIVITY; @@ -1413,6 +1434,9 @@ get_property (GObject *object, case PROP_CONFIG_DESCRIPTION: g_value_set_string (value, nm_config_data_get_config_description (self)); break; + case PROP_CONNECTIVITY_ENABLED: + g_value_set_boolean (value, nm_config_data_get_connectivity_enabled (self)); + break; case PROP_CONNECTIVITY_URI: g_value_set_string (value, nm_config_data_get_connectivity_uri (self)); break; @@ -1510,6 +1534,7 @@ constructed (GObject *object) priv->connection_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); priv->device_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + priv->connectivity.enabled = nm_config_keyfile_get_boolean (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "enabled", TRUE); priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL)); priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL); @@ -1614,8 +1639,6 @@ finalize (GObject *gobject) g_key_file_unref (priv->keyfile_intern); G_OBJECT_CLASS (nm_config_data_parent_class)->finalize (gobject); - - g_free (priv->value_cached); } static void @@ -1656,6 +1679,12 @@ nm_config_data_class_init (NMConfigDataClass *config_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONNECTIVITY_ENABLED] = + g_param_spec_string (NM_CONFIG_DATA_CONNECTIVITY_ENABLED, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONNECTIVITY_URI] = g_param_spec_string (NM_CONFIG_DATA_CONNECTIVITY_URI, "", "", NULL, diff --git a/src/nm-config-data.h b/src/nm-config-data.h index 98c66751..3efe4259 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -32,6 +32,7 @@ #define NM_CONFIG_DATA_CONFIG_DESCRIPTION "config-description" #define NM_CONFIG_DATA_KEYFILE_USER "keyfile-user" #define NM_CONFIG_DATA_KEYFILE_INTERN "keyfile-intern" +#define NM_CONFIG_DATA_CONNECTIVITY_ENABLED "connectivity-enabled" #define NM_CONFIG_DATA_CONNECTIVITY_URI "connectivity-uri" #define NM_CONFIG_DATA_CONNECTIVITY_INTERVAL "connectivity-interval" #define NM_CONFIG_DATA_CONNECTIVITY_RESPONSE "connectivity-response" @@ -155,10 +156,11 @@ const char *nm_config_data_get_config_description (const NMConfigData *config_da gboolean nm_config_data_has_group (const NMConfigData *self, const char *group); gboolean nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags); char *nm_config_data_get_value (const NMConfigData *config_data, const char *group, const char *key, NMConfigGetValueFlags flags); -const char *nm_config_data_get_value_cached (const NMConfigData *config_data, const char *group, const char *key, NMConfigGetValueFlags flags); gint nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, const char *key, gint default_value); +gint64 nm_config_data_get_value_int64 (const NMConfigData *self, const char *group, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); char **nm_config_data_get_plugins (const NMConfigData *config_data, gboolean allow_default); +gboolean nm_config_data_get_connectivity_enabled (const NMConfigData *config_data); const char *nm_config_data_get_connectivity_uri (const NMConfigData *config_data); guint nm_config_data_get_connectivity_interval (const NMConfigData *config_data); const char *nm_config_data_get_connectivity_response (const NMConfigData *config_data); diff --git a/src/nm-config.c b/src/nm-config.c index 91c21de7..de727c98 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -483,20 +483,20 @@ nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli, { GOptionEntry config_options[] = { - { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_(DEFAULT_CONFIG_MAIN_FILE) }, - { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) }, - { "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), N_(DEFAULT_SYSTEM_CONFIG_DIR) }, - { "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), N_(DEFAULT_INTERN_CONFIG_FILE) }, - { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), N_(DEFAULT_STATE_FILE) }, - { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) }, - { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(NM_CONFIG_DEFAULT_MAIN_PLUGINS) }, + { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), DEFAULT_CONFIG_MAIN_FILE }, + { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), DEFAULT_CONFIG_DIR }, + { "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), DEFAULT_SYSTEM_CONFIG_DIR }, + { "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), DEFAULT_INTERN_CONFIG_FILE }, + { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), DEFAULT_STATE_FILE }, + { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), DEFAULT_NO_AUTO_DEFAULT_FILE }, + { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), NM_CONFIG_DEFAULT_MAIN_PLUGINS }, { "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &cli->is_debug, N_("Don't become a daemon, and log to stderr"), NULL }, /* These three are hidden for now, and should eventually just go away. */ { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" }, { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) }, - { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), N_(NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE) }, + { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE }, { 0 }, }; @@ -1590,6 +1590,31 @@ done: return TRUE; } +/*****************************************************************************/ + +void nm_config_set_connectivity_check_enabled (NMConfig *self, + gboolean enabled) +{ + NMConfigPrivate *priv; + GKeyFile *keyfile; + + g_return_if_fail (NM_IS_CONFIG (self)); + + priv = NM_CONFIG_GET_PRIVATE (self); + g_return_if_fail (priv->config_data); + + keyfile = nm_config_data_clone_keyfile_intern (priv->config_data); + + /* Remove existing groups */ + g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, NULL); + + g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + "enabled", enabled ? "true" : "false"); + + nm_config_set_values (self, keyfile, TRUE, FALSE); + g_key_file_unref (keyfile); +} + /** * nm_config_set_values: * @self: the NMConfig instance diff --git a/src/nm-config.h b/src/nm-config.h index c5ff7c67..47e92988 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -75,6 +75,7 @@ #define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_AUDIT "audit" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" #define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs" @@ -179,6 +180,8 @@ void _nm_config_sort_groups (char **groups, gsize ngroups); gboolean nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error); +void nm_config_set_connectivity_check_enabled (NMConfig *self, gboolean enabled); + /* internal defines ... */ extern guint _nm_config_match_nm_version; extern char *_nm_config_match_env; diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index b895a82b..4ccc5719 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -35,6 +35,7 @@ typedef struct { char *uri; char *response; + gboolean enabled; guint interval; NMConfig *config; guint periodic_check_id; @@ -124,6 +125,7 @@ finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state) g_object_unref (cb_data->simple); curl_slist_free_all (cb_data->request_headers); g_free (cb_data->response); + g_free (cb_data->msg); g_free (cb_data->ifspec); g_source_remove (cb_data->timeout_id); g_slice_free (ConCheckCbData, cb_data); @@ -343,7 +345,7 @@ nm_connectivity_check_async (NMConnectivity *self, simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, nm_connectivity_check_async); - if (priv->uri && priv->interval && priv->curl_mhandle) + if (priv->enabled) ehandle = curl_easy_init (); if (ehandle) { @@ -401,7 +403,7 @@ nm_connectivity_check_enabled (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - return (priv->uri && priv->interval && priv->curl_mhandle); + return priv->enabled; } /*****************************************************************************/ @@ -419,6 +421,7 @@ update_config (NMConnectivity *self, NMConfigData *config_data) NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); const char *uri, *response; guint interval; + gboolean enabled; gboolean changed = FALSE; /* Set the URI. */ @@ -454,6 +457,18 @@ update_config (NMConnectivity *self, NMConfigData *config_data) changed = TRUE; } + /* Set enabled flag. */ + enabled = nm_config_data_get_connectivity_enabled (config_data); + /* connectivity checking also requires a valid URI, interval and + * curl_mhandle */ + if (!(priv->uri && priv->interval && priv->curl_mhandle)) { + enabled = FALSE; + } + if (priv->enabled != enabled) { + priv->enabled = enabled; + changed = TRUE; + } + /* Set the response. */ response = nm_config_data_get_connectivity_response (config_data); if (g_strcmp0 (response, priv->response) != 0) { diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index aaaf7b6c..a8ff3513 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -26,7 +26,6 @@ #include <errno.h> #include <fcntl.h> #include <string.h> -#include <poll.h> #include <unistd.h> #include <stdlib.h> #include <resolv.h> @@ -37,6 +36,7 @@ #include <linux/if_infiniband.h> #include <net/ethernet.h> +#include "nm-utils/nm-random-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -110,10 +110,6 @@ _nm_utils_set_testing (NMUtilsTestFlags flags) /*****************************************************************************/ -const NMIPAddr nm_ip_addr_zero = NMIPAddrInit; - -/*****************************************************************************/ - static GSList *_singletons = NULL; static gboolean _singletons_shutdown = FALSE; @@ -265,7 +261,7 @@ nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen) { - return addr & nm_utils_ip4_prefix_to_netmask (plen); + return addr & _nm_utils_ip4_prefix_to_netmask (plen); } /* nm_utils_ip6_address_clear_host_address: @@ -274,15 +270,17 @@ nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen) * @plen: prefix length of network * * Note: this function is self assignment safe, to update @src inplace, set both - * @dst and @src to the same destination. + * @dst and @src to the same destination or set @src NULL. */ const struct in6_addr * nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen) { g_return_val_if_fail (plen <= 128, NULL); - g_return_val_if_fail (src, NULL); g_return_val_if_fail (dst, NULL); + if (!src) + src = dst; + if (plen < 128) { guint nbytes = plen / 8; guint nbits = plen % 8; @@ -301,28 +299,28 @@ nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_ return dst; } -gboolean -nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen) +int +nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen) { int nbytes; - guint8 t, m; + guint8 va, vb, m; if (plen >= 128) - return memcmp (addr_a, addr_b, sizeof (struct in6_addr)) == 0; - - nbytes = plen / 8; - if (nbytes) { - if (memcmp (addr_a, addr_b, nbytes) != 0) - return FALSE; + NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, sizeof (struct in6_addr)); + else { + nbytes = plen / 8; + if (nbytes) + NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, nbytes); + + plen = plen % 8; + if (plen != 0) { + m = ~((1 << (8 - plen)) - 1); + va = ((((const guint8 *) addr_a))[nbytes]) & m; + vb = ((((const guint8 *) addr_b))[nbytes]) & m; + NM_CMP_DIRECT (va, vb); + } } - - plen = plen % 8; - if (plen == 0) - return TRUE; - - m = ~((1 << (8 - plen)) - 1); - t = ((((const guint8 *) addr_a))[nbytes]) ^ ((((const guint8 *) addr_b))[nbytes]); - return (t & m) == 0; + return 0; } /*****************************************************************************/ @@ -1820,81 +1818,6 @@ nm_match_spec_join (GSList *specs) /*****************************************************************************/ -char _nm_utils_to_string_buffer[]; - -void -nm_utils_to_string_buffer_init (char **buf, gsize *len) -{ - if (!*buf) { - *buf = _nm_utils_to_string_buffer; - *len = sizeof (_nm_utils_to_string_buffer); - } -} - -gboolean -nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len) -{ - nm_utils_to_string_buffer_init (buf, len); - if (!obj) { - g_strlcpy (*buf, "(null)", *len); - return FALSE; - } - return TRUE; -} - -const char * -nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, - gsize n_descs, - unsigned flags, - char *buf, - gsize len) -{ - gsize i; - char *p; - -#if NM_MORE_ASSERTS > 10 - nm_assert (descs); - nm_assert (n_descs > 0); - for (i = 0; i < n_descs; i++) { - gsize j; - - nm_assert (descs[i].flag && nm_utils_is_power_of_two (descs[i].flag)); - nm_assert (descs[i].name && descs[i].name[0]); - for (j = 0; j < i; j++) - nm_assert (descs[j].flag != descs[i].flag); - } -#endif - - nm_utils_to_string_buffer_init (&buf, &len); - - if (!len) - return buf; - - buf[0] = '\0'; - if (!flags) { - return buf; - } - - p = buf; - for (i = 0; flags && i < n_descs; i++) { - if (NM_FLAGS_HAS (flags, descs[i].flag)) { - flags &= ~descs[i].flag; - - if (buf[0] != '\0') - nm_utils_strbuf_append_c (&p, &len, ','); - nm_utils_strbuf_append_str (&p, &len, descs[i].name); - } - } - if (flags) { - if (buf[0] != '\0') - nm_utils_strbuf_append_c (&p, &len, ','); - nm_utils_strbuf_append (&p, &len, "0x%x", flags); - } - return buf; -}; - -/*****************************************************************************/ - char * nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id) { @@ -1952,101 +1875,109 @@ nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key) return name; } -/** - * nm_utils_read_resolv_conf_nameservers(): - * @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf - * - * Reads all nameservers out of @rc_contents or /etc/resolv.conf and returns - * them. - * - * Returns: a #GPtrArray of 'char *' elements of each nameserver line from - * @contents or resolv.conf - */ -GPtrArray * -nm_utils_read_resolv_conf_nameservers (const char *rc_contents) -{ - GPtrArray *nameservers = NULL; - char *contents = NULL; - char **lines, **iter; - char *p; - - if (rc_contents) - contents = g_strdup (rc_contents); - else { - if (!g_file_get_contents (_PATH_RESCONF, &contents, NULL, NULL)) - return NULL; - } - - nameservers = g_ptr_array_new_full (3, g_free); - - lines = g_strsplit_set (contents, "\r\n", -1); - for (iter = lines; *iter; iter++) { - if (!g_str_has_prefix (*iter, "nameserver")) - continue; - p = *iter + strlen ("nameserver"); - if (!g_ascii_isspace (*p++)) - continue; - /* Skip intermediate whitespace */ - while (g_ascii_isspace (*p)) - p++; - g_strchomp (p); +/*****************************************************************************/ - g_ptr_array_add (nameservers, g_strdup (p)); - } - g_strfreev (lines); - g_free (contents); +gboolean +nm_utils_resolve_conf_parse (int addr_family, + const char *rc_contents, + GArray *nameservers, + GPtrArray *dns_options) +{ + guint i; + gboolean changed = FALSE; + gs_free const char **lines = NULL; + gsize l; + + g_return_val_if_fail (rc_contents, FALSE); + g_return_val_if_fail (nameservers, FALSE); + g_return_val_if_fail ( ( addr_family == AF_INET + && g_array_get_element_size (nameservers) == sizeof (in_addr_t)) + || ( addr_family == AF_INET6 + && g_array_get_element_size (nameservers) == sizeof (struct in6_addr)), FALSE); + + lines = nm_utils_strsplit_set (rc_contents, "\r\n"); + if (!lines) + return FALSE; - return nameservers; -} +/* like glibc's MATCH() macro in resolv/res_init.c. */ +#define RC_MATCH(line, option, out_arg) \ + ({ \ + const char *const _line = (line); \ + gboolean _match = FALSE; \ + \ + if ( (strncmp (_line, option, NM_STRLEN (option)) == 0) \ + && (NM_IN_SET (_line[NM_STRLEN (option)], ' ', '\t'))) { \ + _match = TRUE;\ + (out_arg) = &_line[NM_STRLEN (option) + 1]; \ + } \ + _match; \ + }) + + for (l = 0; lines[l]; l++) { + const char *const line = lines[l]; + const char *s = NULL; + + if (RC_MATCH (line, "nameserver", s)) { + gs_free char *s_cpy = NULL; + NMIPAddr ns; + + s = nm_strstrip_avoid_copy (s, &s_cpy); + if (inet_pton (addr_family, s, &ns) != 1) + continue; -/** - * nm_utils_read_resolv_conf_dns_options(): - * @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf - * - * Reads all dns options out of @rc_contents or /etc/resolv.conf and returns - * them. - * - * Returns: a #GPtrArray of 'char *' elements of each option - */ -GPtrArray * -nm_utils_read_resolv_conf_dns_options (const char *rc_contents) -{ - GPtrArray *options = NULL; - char *contents = NULL; - char **lines, **line_iter; - char **tokens, **token_iter; - char *p; - - if (rc_contents) - contents = g_strdup (rc_contents); - else { - if (!g_file_get_contents (_PATH_RESCONF, &contents, NULL, NULL)) - return NULL; - } + if (addr_family == AF_INET) { + if (!ns.addr4) + continue; + for (i = 0; i < nameservers->len; i++) { + if (g_array_index (nameservers, guint32, i) == ns.addr4) + break; + } + } else { + if (IN6_IS_ADDR_UNSPECIFIED (&ns.addr6)) + continue; + for (i = 0; i < nameservers->len; i++) { + struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, i); - options = g_ptr_array_new_full (3, g_free); + if (IN6_ARE_ADDR_EQUAL (t, &ns.addr6)) + break; + } + } - lines = g_strsplit_set (contents, "\r\n", -1); - for (line_iter = lines; *line_iter; line_iter++) { - if (!g_str_has_prefix (*line_iter, "options")) - continue; - p = *line_iter + strlen ("options"); - if (!g_ascii_isspace (*p++)) + if (i == nameservers->len) { + g_array_append_val (nameservers, ns); + changed = TRUE; + } continue; + } - tokens = g_strsplit (p, " ", 0); - for (token_iter = tokens; token_iter && *token_iter; token_iter++) { - g_strstrip (*token_iter); - if (!*token_iter[0]) + if (RC_MATCH (line, "options", s)) { + if (!dns_options) continue; - g_ptr_array_add (options, g_strdup (*token_iter)); + + s = nm_str_skip_leading_spaces (s); + if (s[0]) { + gs_free const char **tokens = NULL; + gsize i_tokens; + + tokens = nm_utils_strsplit_set (s, " \t"); + nm_assert (tokens); + for (i_tokens = 0; tokens[i_tokens]; i_tokens++) { + gs_free char *t = g_strstrip (g_strdup (tokens[i_tokens])); + + if ( _nm_utils_dns_option_validate (t, NULL, NULL, + addr_family == AF_INET6, + _nm_utils_dns_option_descs) + && _nm_utils_dns_option_find_idx (dns_options, t) < 0) { + g_ptr_array_add (dns_options, g_steal_pointer (&t)); + changed = TRUE; + } + } + } + continue; } - g_strfreev (tokens); } - g_strfreev (lines); - g_free (contents); - return options; + return changed; } /*****************************************************************************/ @@ -2131,7 +2062,7 @@ monotonic_timestamp_get (struct timespec *tp) break; case 2: /* fallback, return CLOCK_MONOTONIC. Kernels prior to 2.6.39 - * don't support CLOCK_BOOTTIME. */ + * (released on 18 May, 2011) don't support CLOCK_BOOTTIME. */ err = clock_gettime (CLOCK_MONOTONIC, tp); break; } @@ -2292,7 +2223,7 @@ _log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b) { const LogConnectionSettingData *v1 = a; const LogConnectionSettingData *v2 = b; - guint32 p1, p2; + NMSettingPriority p1, p2; NMSetting *s1, *s2; s1 = v1->setting ? v1->setting : v1->diff_base_setting; @@ -2432,12 +2363,26 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, if (!name) name = ""; - connection_diff_are_same = nm_connection_diff (connection, diff_base, NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, &connection_diff); + connection_diff_are_same = nm_connection_diff (connection, diff_base, + NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, + &connection_diff); if (connection_diff_are_same) { - if (diff_base) - nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s and %p/%s): no difference", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base)); - else - nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s): no properties set", prefix, name, connection, G_OBJECT_TYPE_NAME (connection)); + const char *t1, *t2; + + t1 = nm_connection_get_connection_type (connection); + if (diff_base) { + t2 = nm_connection_get_connection_type (diff_base); + nm_log (level, domain, NULL, NULL, + "%sconnection '%s' (%p/%s/%s%s%s and %p/%s/%s%s%s): no difference", + prefix, name, + connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), + diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2)); + } else { + nm_log (level, domain, NULL, NULL, + "%sconnection '%s' (%p/%s/%s%s%s): no properties set", + prefix, name, + connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1)); + } g_assert (!connection_diff); return; } @@ -2471,12 +2416,20 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, if (print_header) { GError *err_verify = NULL; const char *path = nm_connection_get_path (connection); + const char *t1, *t2; + t1 = nm_connection_get_connection_type (connection); if (diff_base) { - nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s < %p/%s)%s%s%s:", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base), + t2 = nm_connection_get_connection_type (diff_base); + nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s < %p/%s/%s%s%s)%s%s%s:", + prefix, name, + connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), + diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2), NM_PRINT_FMT_QUOTED (path, " [", path, "]", "")); } else { - nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), + nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s):%s%s%s", + prefix, name, + connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), NM_PRINT_FMT_QUOTED (path, " [", path, "]", "")); } print_header = FALSE; @@ -2572,55 +2525,88 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_ #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/" #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/" G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR)); +G_STATIC_ASSERT (NM_STRLEN (IPV6_PROPERTY_DIR) + IFNAMSIZ + 60 == NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE); -static const char * -_get_property_path (const char *ifname, - const char *property, - gboolean ipv6) +/** + * nm_utils_sysctl_ip_conf_path: + * @addr_family: either AF_INET or AF_INET6. + * @buf: the output buffer where to write the path. It + * must be at least NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE bytes + * long. + * @ifname: an interface name + * @property: a property name + * + * Returns: the path to IPv6 property @property on @ifname. Note that + * this returns the input argument @buf. + */ +const char * +nm_utils_sysctl_ip_conf_path (int addr_family, char *buf, const char *ifname, const char *property) { - static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32]; int len; - ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname); + nm_assert (buf); + nm_assert_addr_family (addr_family); + + g_assert (nm_utils_is_valid_iface_name (ifname, NULL)); property = NM_ASSERT_VALID_PATH_COMPONENT (property); - len = g_snprintf (path, - sizeof (path), + len = g_snprintf (buf, + NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE, "%s%s/%s", - ipv6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR, + addr_family == AF_INET6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR, ifname, property); - g_assert (len < sizeof (path) - 1); - - return path; + g_assert (len < NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE - 1); + return buf; } -/** - * nm_utils_ip6_property_path: - * @ifname: an interface name - * @property: a property name - * - * Returns the path to IPv6 property @property on @ifname. Note that - * this uses a static buffer. - */ -const char * -nm_utils_ip6_property_path (const char *ifname, const char *property) +gboolean +nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *ifname, const char *property) { - return _get_property_path (ifname, property, TRUE); -} + g_return_val_if_fail (path, FALSE); + NM_ASSERT_VALID_PATH_COMPONENT (property); + g_assert (!ifname || nm_utils_is_valid_iface_name (ifname, NULL)); -/** - * nm_utils_ip4_property_path: - * @ifname: an interface name - * @property: a property name - * - * Returns the path to IPv4 property @property on @ifname. Note that - * this uses a static buffer. - */ -const char * -nm_utils_ip4_property_path (const char *ifname, const char *property) -{ - return _get_property_path (ifname, property, FALSE); + if (addr_family == AF_INET) { + if (!g_str_has_prefix (path, IPV4_PROPERTY_DIR)) + return FALSE; + path += NM_STRLEN (IPV4_PROPERTY_DIR); + } else if (addr_family == AF_INET6) { + if (!g_str_has_prefix (path, IPV6_PROPERTY_DIR)) + return FALSE; + path += NM_STRLEN (IPV6_PROPERTY_DIR); + } else + g_return_val_if_reached (FALSE); + + if (ifname) { + if (!g_str_has_prefix (path, ifname)) + return FALSE; + path += strlen (ifname); + if (path[0] != '/') + return FALSE; + path++; + } else { + const char *slash; + char buf[IFNAMSIZ]; + gsize l; + + slash = strchr (path, '/'); + if (!slash) + return FALSE; + l = slash - path; + if (l >= IFNAMSIZ) + return FALSE; + memcpy (buf, path, l); + buf[l] = '\0'; + if (!nm_utils_is_valid_iface_name (buf, NULL)) + return FALSE; + path = slash + 1; + } + + if (!nm_streq (path, property)) + return FALSE; + + return TRUE; } gboolean @@ -2733,99 +2719,6 @@ nm_utils_machine_id_read (void) /*****************************************************************************/ -/* taken from systemd's fd_wait_for_event(). Note that the timeout - * is here in nano-seconds, not micro-seconds. */ -int -nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) -{ - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - struct timespec ts, *pts; - int r; - - if (timeout_ns < 0) - pts = NULL; - else { - ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND); - ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND); - pts = &ts; - } - - r = ppoll (&pollfd, 1, pts, NULL); - if (r < 0) - return -errno; - if (r == 0) - return 0; - return pollfd.revents; -} - -/* taken from systemd's loop_read() */ -ssize_t -nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) -{ - uint8_t *p = buf; - ssize_t n = 0; - - g_return_val_if_fail (fd >= 0, -EINVAL); - g_return_val_if_fail (buf, -EINVAL); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read (fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1); - continue; - } - - return n > 0 ? n : -errno; - } - - if (k == 0) - return n; - - g_assert ((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -/* taken from systemd's loop_read_exact() */ -int -nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) -{ - ssize_t n; - - n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - _nm_printf (3, 4) static int _get_contents_error (GError **error, int errsv, const char *format, ...) @@ -2856,6 +2749,8 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) * nm_utils_fd_get_contents: * @fd: open file descriptor to read. The fd will not be closed, * but don't rely on it's state afterwards. + * @close_fd: if %TRUE, @fd will be closed by the function. + * Passing %TRUE here might safe a syscall for dup(). * @max_length: allocate at most @max_length bytes. If the * file is larger, reading will fail. Set to zero to use * a very large default. @@ -2883,11 +2778,13 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) */ int nm_utils_fd_get_contents (int fd, + gboolean close_fd, gsize max_length, char **contents, gsize *length, GError **error) { + nm_auto_close int fd_keeper = close_fd ? fd : -1; struct stat stat_buf; gs_free char *str = NULL; @@ -2933,9 +2830,20 @@ nm_utils_fd_get_contents (int fd, nm_auto_fclose FILE *f = NULL; char buf[4096]; gsize n_have, n_alloc; + int fd2; + + if (fd_keeper >= 0) + fd2 = nm_steal_fd (&fd_keeper); + else { + fd2 = dup (fd); + if (fd2 < 0) + return _get_contents_error (error, 0, "error during dup"); + } - if (!(f = fdopen (fd, "r"))) + if (!(f = fdopen (fd2, "r"))) { + close (fd2); return _get_contents_error (error, 0, "failure during fdopen"); + } n_have = 0; n_alloc = 0; @@ -3025,7 +2933,7 @@ nm_utils_file_get_contents (int dirfd, gsize *length, GError **error) { - nm_auto_close int fd = -1; + int fd; int errsv; g_return_val_if_fail (filename && filename[0], -EINVAL); @@ -3058,6 +2966,7 @@ nm_utils_file_get_contents (int dirfd, } } return nm_utils_fd_get_contents (fd, + TRUE, max_length, contents, length, @@ -3066,30 +2975,6 @@ nm_utils_file_get_contents (int dirfd, /*****************************************************************************/ -/* taken from systemd's dev_urandom(). */ -int -nm_utils_read_urandom (void *p, size_t nbytes) -{ - int fd = -1; - int r; - -again: - fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd < 0) { - r = errno; - if (r == EINTR) - goto again; - return r == ENOENT ? -ENOSYS : -r; - } - - r = nm_utils_fd_read_loop_exact (fd, p, nbytes, TRUE); - close (fd); - - return r; -} - -/*****************************************************************************/ - guint8 * nm_utils_secret_key_read (gsize *out_key_len, GError **error) { @@ -3108,7 +2993,6 @@ nm_utils_secret_key_read (gsize *out_key_len, GError **error) key_len = 0; } } else { - int r; mode_t key_mask; /* RFC7217 mandates the key SHOULD be at least 128 bits. @@ -3116,10 +3000,9 @@ nm_utils_secret_key_read (gsize *out_key_len, GError **error) key_len = 32; secret_key = g_malloc (key_len); - r = nm_utils_read_urandom (secret_key, key_len); - if (r < 0) { + if (!nm_utils_random_bytes (secret_key, key_len)) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't read /dev/urandom: %s", strerror (-r)); + "Can't get random data to generate secret key"); key_len = 0; goto out; } @@ -3373,8 +3256,7 @@ nm_utils_stable_id_random (void) { char buf[15]; - if (nm_utils_read_urandom (buf, sizeof (buf)) < 0) - g_return_val_if_reached (nm_utils_uuid_generate ()); + nm_utils_random_bytes (buf, sizeof (buf)); return g_base64_encode ((guchar *) buf, sizeof (buf)); } @@ -3437,7 +3319,7 @@ nm_utils_stable_id_parse (const char *stable_id, g_return_val_if_fail (out_generated, NM_UTILS_STABLE_TYPE_RANDOM); if (!stable_id) { - out_generated = NULL; + *out_generated = NULL; return NM_UTILS_STABLE_TYPE_UUID; } @@ -3505,7 +3387,7 @@ nm_utils_stable_id_parse (const char *stable_id, _stable_id_append (str, bootid ?: nm_utils_get_boot_id ()); else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) { /* RANDOM makes not so much sense for cloned-mac-address - * as the result is simmilar to specifing "cloned-mac-address=random". + * as the result is simmilar to specyifing "cloned-mac-address=random". * It makes however sense for RFC 7217 Stable Privacy IPv6 addresses * where this is effectively the only way to generate a different * (random) host identifier for each connect. @@ -3758,8 +3640,7 @@ nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, { struct ether_addr bin_addr; - if (nm_utils_read_urandom (&bin_addr, ETH_ALEN) < 0) - return NULL; + nm_utils_random_bytes (&bin_addr, ETH_ALEN); _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN); } @@ -4451,6 +4332,13 @@ nm_utils_format_con_diff_for_audit (GHashTable *diff) /*****************************************************************************/ +NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref, + NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_LOW, "low"), + NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"), + NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_HIGH, "high"), + NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), +); + NM_UTILS_LOOKUP_STR_DEFINE (nm_activation_type_to_string, NMActivationType, NM_UTILS_LOOKUP_DEFAULT_WARN ("(unknown)"), NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_MANAGED, "managed"), diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 0f37bd20..cc784724 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -25,6 +25,8 @@ #include <stdio.h> #include <arpa/inet.h> +#include "nm-utils/nm-hash-utils.h" + #include "nm-connection.h" /*****************************************************************************/ @@ -90,31 +92,59 @@ GETTER (void) \ /*****************************************************************************/ -typedef struct { - union { - guint8 addr_ptr[1]; - in_addr_t addr4; - struct in6_addr addr6; - - /* NMIPAddr is really a union for IP addresses. - * However, as ethernet addresses fit in here nicely, use - * it also for an ethernet MAC address. */ - guint8 addr_eth[6 /*ETH_ALEN*/]; - }; -} NMIPAddr; - -extern const NMIPAddr nm_ip_addr_zero; - -#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT } - -/*****************************************************************************/ - gboolean nm_ethernet_address_is_valid (gconstpointer addr, gssize len); gconstpointer nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen); in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen); const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen); -gboolean nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen); + +static inline int +nm_utils_ip4_address_same_prefix_cmp (in_addr_t addr_a, in_addr_t addr_b, guint8 plen) +{ + NM_CMP_DIRECT (htonl (nm_utils_ip4_address_clear_host_address (addr_a, plen)), + htonl (nm_utils_ip4_address_clear_host_address (addr_b, plen))); + return 0; +} + +int nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen); + +static inline gboolean +nm_utils_ip4_address_same_prefix (in_addr_t addr_a, in_addr_t addr_b, guint8 plen) +{ + return nm_utils_ip4_address_same_prefix_cmp (addr_a, addr_b, plen) == 0; +} + +static inline gboolean +nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen) +{ + return nm_utils_ip6_address_same_prefix_cmp (addr_a, addr_b, plen) == 0; +} + +#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \ + NM_CMP_RETURN (nm_utils_ip4_address_same_prefix_cmp ((a), (b), (plen))) + +#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \ + NM_CMP_RETURN (nm_utils_ip6_address_same_prefix_cmp ((a), (b), (plen))) + +static inline void +nm_hash_update_in6addr (NMHashState *h, const struct in6_addr *addr) +{ + nm_assert (addr); + + nm_hash_update (h, addr, sizeof (*addr)); +} + +static inline void +nm_hash_update_in6addr_prefix (NMHashState *h, const struct in6_addr *addr, guint8 plen) +{ + struct in6_addr a; + + nm_assert (addr); + + nm_utils_ip6_address_clear_host_address (&a, addr, plen); + /* we don't hash plen itself. The caller may want to do that.*/ + nm_hash_update_in6addr (h, &a); +} double nm_utils_exp10 (gint16 e); @@ -133,6 +163,21 @@ nm_utils_ip6_route_metric_normalize (guint32 metric) return metric ? metric : 1024 /*NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6*/; } +static inline guint32 +nm_utils_ip_route_metric_normalize (int addr_family, guint32 metric) +{ + return addr_family == AF_INET6 ? nm_utils_ip6_route_metric_normalize (metric) : metric; +} + +static inline guint32 +nm_utils_ip_route_metric_penalize (int addr_family, guint32 metric, guint32 penalty) +{ + metric = nm_utils_ip_route_metric_normalize (addr_family, metric); + if (metric < G_MAXUINT32 - penalty) + return metric + penalty; + return G_MAXUINT32; +} + int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED; guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid); @@ -174,135 +219,27 @@ NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, GSList *nm_match_spec_split (const char *value); char *nm_match_spec_join (GSList *specs); -extern char _nm_utils_to_string_buffer[2096]; - -void nm_utils_to_string_buffer_init (char **buf, gsize *len); -gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len); - -/*****************************************************************************/ - -typedef struct { - unsigned flag; - const char *name; -} NMUtilsFlags2StrDesc; - -#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, } - -#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \ -scope const char * \ -fcn_name (flags_type flags, char *buf, gsize len) \ -{ \ - static const NMUtilsFlags2StrDesc descs[] = { \ - __VA_ARGS__ \ - }; \ - G_STATIC_ASSERT (sizeof (flags_type) <= sizeof (unsigned)); \ - return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); \ -}; - -#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \ - _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__) -#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \ - _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__) - -const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, - gsize n_descs, - unsigned flags, - char *buf, - gsize len); - -/*****************************************************************************/ - -#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0 -#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0 - -#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \ -scope const char * \ -fcn_name (lookup_type val, char *buf, gsize len) \ -{ \ - nm_utils_to_string_buffer_init (&buf, &len); \ - if (len) { \ - const char *s = NULL; \ - switch (val) { \ - (void) 0, \ - __VA_ARGS__ \ - (void) 0; \ - }; \ - if (s) \ - g_strlcpy (buf, s, len); \ - else \ - g_snprintf (buf, len, "(%"int_fmt")", val); \ - } \ - return buf; \ -} - -#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \ - _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__) -#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \ - _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__) - -/*****************************************************************************/ - -#define NM_UTILS_LOOKUP_DEFAULT(v) return (v) -#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached (v) -#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) { nm_assert_not_reached (); return (v); } -#define NM_UTILS_LOOKUP_ITEM(v, n) (void) 0; case v: return (n); (void) 0 -#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, ""n"") -#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) (void) 0; case v: break; (void) 0 -#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() (void) 0; default: break; (void) 0 - -#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \ -scope result_type \ -fcn_name (lookup_type val) \ -{ \ - switch (val) { \ - (void) 0, \ - __VA_ARGS__ \ - (void) 0; \ - }; \ - { unknown_val; } \ -} - -#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ - _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) -#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \ - _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) - -/* Call the string-lookup-table function @fcn_name. If the function returns - * %NULL, the numeric index is converted to string using a alloca() buffer. - * Beware: this macro uses alloca(). */ -#define NM_UTILS_LOOKUP_STR(fcn_name, idx) \ - ({ \ - typeof (idx) _idx = (idx); \ - const char *_s; \ - \ - _s = fcn_name (_idx); \ - if (!_s) { \ - _s = g_alloca (30); \ - \ - g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \ - } \ - _s; \ - }) - /*****************************************************************************/ const char *nm_utils_get_ip_config_method (NMConnection *connection, GType ip_setting_type); +gboolean nm_utils_connection_has_default_route (NMConnection *connection, + int addr_family, + gboolean *out_is_never_default); + char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id); const char *nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key); -GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents); -GPtrArray *nm_utils_read_resolv_conf_dns_options (const char *rc_contents); +gboolean nm_utils_resolve_conf_parse (int addr_family, + const char *rc_contents, + GArray *nameservers, + GPtrArray *dns_options); int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b); void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); -#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) -#define NM_UTILS_NS_PER_MSEC ((gint64) 1000000) -#define NM_UTILS_NS_TO_MSEC_CEIL(nsec) (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC) - gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); gint64 nm_utils_get_monotonic_timestamp_ms (void); @@ -311,16 +248,17 @@ gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timest gboolean nm_utils_is_valid_path_component (const char *name); const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name); -const char *nm_utils_ip6_property_path (const char *ifname, const char *property); -const char *nm_utils_ip4_property_path (const char *ifname, const char *property); -gboolean nm_utils_is_specific_hostname (const char *name); +#define NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE 100 + +const char *nm_utils_sysctl_ip_conf_path (int addr_family, char *buf, const char *ifname, const char *property); -int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns); -ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll); -int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll); +gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *ifname, const char *property); + +gboolean nm_utils_is_specific_hostname (const char *name); int nm_utils_fd_get_contents (int fd, + gboolean close_fd, gsize max_length, char **contents, gsize *length, @@ -339,8 +277,6 @@ gboolean nm_utils_file_set_contents (const gchar *filename, mode_t mode, GError **error); -int nm_utils_read_urandom (void *p, size_t n); - char *nm_utils_machine_id_read (void); gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); @@ -348,7 +284,7 @@ guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error); const char *nm_utils_get_boot_id (void); -/* IPv6 Interface Identifer helpers */ +/* IPv6 Interface Identifier helpers */ /** * NMUtilsIPv6IfaceId: @@ -494,6 +430,19 @@ gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError * char **nm_utils_read_plugin_paths (const char *dirname, const char *prefix); char *nm_utils_format_con_diff_for_audit (GHashTable *diff); +/*****************************************************************************/ + +/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>, + * the values for netlink attribute RTA_PREF) and "enum ndp_route_preference" + * from <ndp.h>. */ +typedef enum { + NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */ + NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */ + NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */ + NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */ +} NMIcmpv6RouterPref; + +const char *nm_icmpv6_router_pref_to_string (NMIcmpv6RouterPref pref, char *buf, gsize len); /*****************************************************************************/ diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c deleted file mode 100644 index 1ab8f02d..00000000 --- a/src/nm-default-route-manager.c +++ /dev/null @@ -1,1611 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 Red Hat, Inc. - */ - - -#include "nm-default.h" - -#include "nm-default-route-manager.h" - -#include <string.h> - -#include "devices/nm-device.h" -#include "vpn/nm-vpn-connection.h" -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "nm-manager.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "nm-act-request.h" - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_LOG_WITH_PTR, - PROP_PLATFORM, -); - -typedef struct { - GPtrArray *entries_ip4; - GPtrArray *entries_ip6; - - NMPlatform *platform; - - struct { - guint guard; - guint backoff_wait_time_ms; - guint idle_handle; - gboolean has_v4_changes; - gboolean has_v6_changes; - } resync; - - /* During disposing, we unref the sources of all entries. This happens usually - * during shutdown, which might call the final deletion of the object. That - * again might cause calls back into NMDefaultRouteManager, which finds dangling - * pointers. - * Guard every publicly accessible function to return early if the instance - * is already disposing. */ - bool disposed; - - bool log_with_ptr; -} NMDefaultRouteManagerPrivate; - -struct _NMDefaultRouteManager { - GObject parent; - NMDefaultRouteManagerPrivate _priv; -}; - -struct _NMDefaultRouteManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT) - -#define NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDefaultRouteManager, NM_IS_DEFAULT_ROUTE_MANAGER) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "default-route" -#undef _NMLOG_ENABLED -#define _NMLOG_ENABLED(level, addr_family) \ - ({ \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - nm_logging_enabled (__level, __domain); \ - }) -#define _NMLOG(level, addr_family, ...) \ - G_STMT_START { \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __prefix_buf[100]; \ - \ - _nm_log (__level, __domain, 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \ - ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \ - _NMLOG2_PREFIX_NAME, \ - __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \ - self) \ - : _NMLOG2_PREFIX_NAME \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -#define _NMLOG2_PREFIX_NAME _NMLOG_PREFIX_NAME -#undef _NMLOG2_ENABLED -#define _NMLOG2_ENABLED _NMLOG_ENABLED -#define _NMLOG2(level, vtable, entry_idx, entry, ...) \ - G_STMT_START { \ - const int __addr_family = (vtable)->vt->addr_family; \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __prefix_buf[100]; \ - guint __entry_idx = (entry_idx); \ - const Entry *const __entry = (entry); \ - \ - _nm_log (__level, __domain, 0, NULL, NULL, \ - "%s: entry[%u/%s:%p:%s:%chas:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \ - ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \ - _NMLOG2_PREFIX_NAME, \ - __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \ - self) \ - : _NMLOG2_PREFIX_NAME, \ - __entry_idx, \ - NM_IS_DEVICE (__entry->source.pointer) ? "dev" : "vpn", \ - __entry->source.pointer, \ - NM_IS_DEVICE (__entry->source.pointer) ? nm_device_get_iface (__entry->source.device) : nm_active_connection_get_settings_connection_id (NM_ACTIVE_CONNECTION (__entry->source.vpn)), \ - (__entry->never_default ? '-' : '+'), \ - (__entry->synced ? '+' : '-') \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static void _resync_idle_cancel (NMDefaultRouteManager *self); - -/*****************************************************************************/ - -typedef struct { - union { - void *pointer; - GObject *object; - NMDevice *device; - NMVpnConnection *vpn; - } source; - NMPlatformIPXRoute route; - - /* Whether the route is synced to platform and has a default route. - * - * ( synced && !never_default): the interface gets a default route that - * is enforced and managed by NMDefaultRouteManager. - * - * (!synced && !never_default): the interface has this route, but it is assumed. - * Assumed interfaces are those that have no tracked entry or that only have - * (!synced && !never_default) entries. NMDefaultRouteManager will not touch - * default routes on these interfaces. - * This combination makes only sense for device sources. - * They are tracked so that assumed devices can also be the best device. - * - * ( synced && never_default): entries of this kind are a placeholder - * to indicate that the ifindex is managed but has no default-route. - * Missing entries also indicate that a certain ifindex has no default-route. - * The difference is that missing entries are considered assumed while on - * (synced && never_default) entries the absence of the default route - * is enforced. NMDefaultRouteManager will actively remove any default - * route on such ifindexes. - * Also, for VPN sources in addition we track them so that a never-default - * VPN connection can be choosen by get_best_config() to receive the DNS configuration. - * - * (!synced && never_default): this combination makes no sense. - */ - gboolean synced; - gboolean never_default; - - guint32 effective_metric; -} Entry; - -typedef struct { - const NMPlatformVTableRoute *vt; - GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv); -} VTableIP; - -static const VTableIP vtable_ip4, vtable_ip6; - -static NMPlatformIPRoute * -_vt_route_index (const VTableIP *vtable, GArray *routes, guint index) -{ - if (vtable->vt->is_ip4) - return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, index); - else - return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, index); -} - -static gboolean -_vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry) -{ - guint i; - NMPlatformIPXRoute route = entry->route; - - route.rx.metric = entry->effective_metric; - - if (vtable->vt->is_ip4) { - for (i = 0; i < routes->len; i++) { - NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i); - - route.rx.rt_source = r->rt_source; - if (nm_platform_ip4_route_cmp (r, &route.r4) == 0) - return TRUE; - } - } else { - for (i = 0; i < routes->len; i++) { - NMPlatformIP6Route *r = &g_array_index (routes, NMPlatformIP6Route, i); - - route.rx.rt_source = r->rt_source; - if (nm_platform_ip6_route_cmp (r, &route.r6) == 0) - return TRUE; - } - } - return FALSE; -} - -static void -_entry_free (Entry *entry) -{ - if (entry) { - g_object_unref (entry->source.object); - g_slice_free (Entry, entry); - } -} - -static Entry * -_entry_find_by_source (GPtrArray *entries, gpointer source, guint *out_idx) -{ - guint i; - - for (i = 0; i < entries->len; i++) { - Entry *e = g_ptr_array_index (entries, i); - - if (e->source.pointer == source) { - if (out_idx) - *out_idx = i; - return e; - } - } - - if (out_idx) - *out_idx = G_MAXUINT; - return NULL; -} - -static gboolean -_platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, guint32 metric) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries = vtable->get_entries (priv); - char buf1[sizeof (_nm_utils_to_string_buffer)]; - char buf2[sizeof (_nm_utils_to_string_buffer)]; - guint i; - Entry *entry_unsynced = NULL; - Entry *entry = NULL; - gboolean success; - - /* Find the entries for the given metric. - * The effective metric for synced entries is choosen in a way that it - * is unique (except for G_MAXUINT32, where a clash is not solvable). */ - for (i = 0; i < entries->len; i++) { - Entry *e = g_ptr_array_index (entries, i); - - if (e->never_default) - continue; - - if (e->effective_metric != metric) - continue; - - if (e->synced) { - g_assert (!entry || metric == G_MAXUINT32); - if (!entry) - entry = e; - } else - entry_unsynced = e; - } - - /* We don't expect to have an unsynced *and* a synced entry for the same metric. - * Unless, (a) their metric is G_MAXUINT32, in which case we could not find an unused effective metric, - * or (b) if we have an unsynced and a synced entry for the same ifindex. - * The latter case happens for example when activating an openvpn connection (synced) and - * assuming the corresponding tun0 interface (unsynced). */ - g_assert (!entry || !entry_unsynced || (entry->route.rx.ifindex == entry_unsynced->route.rx.ifindex) || metric == G_MAXUINT32); - - /* we only add the route, if we have an (to be synced) entry for it. */ - if (!entry) - return FALSE; - - if (vtable->vt->is_ip4) { - NMPlatformIP4Route rt = entry->route.r4; - const NMPlatformIP4Route *plat_rt; - - rt.network = 0; - rt.plen = 0; - rt.metric = entry->effective_metric; - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - - plat_rt = nm_platform_ip4_route_get (priv->platform, - entry->route.r4.ifindex, - 0, - 0, - entry->effective_metric); - if (plat_rt && nm_platform_ip4_route_cmp (plat_rt, &rt) == 0) { - _LOGt (AF_INET, "already exists: %s", - nm_platform_ip4_route_to_string (&rt, NULL, 0)); - return FALSE; - } - - rt.rt_source = entry->route.r4.rt_source; - - if (plat_rt) { - _LOGt (AF_INET, "update platform route: %s; with route: %s", - nm_platform_ip4_route_to_string (plat_rt, buf1, sizeof (buf1)), - nm_platform_ip4_route_to_string (&rt, buf2, sizeof (buf2))); - } - - success = nm_platform_ip4_route_add (priv->platform, &rt); - } else { - NMPlatformIP6Route rt = entry->route.r6; - const NMPlatformIP6Route *plat_rt; - - rt.network = in6addr_any; - rt.plen = 0; - rt.metric = entry->effective_metric; - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - - plat_rt = nm_platform_ip6_route_get (priv->platform, - entry->route.r6.ifindex, - in6addr_any, - 0, - entry->effective_metric); - if (plat_rt && nm_platform_ip6_route_cmp (plat_rt, &rt) == 0) { - _LOGt (AF_INET6, "already exists: %s", - nm_platform_ip6_route_to_string (&rt, NULL, 0)); - return FALSE; - } - - rt.rt_source = entry->route.r6.rt_source; - - if (plat_rt) { - _LOGt (AF_INET, "update platform route: %s; with route: %s", - nm_platform_ip6_route_to_string (plat_rt, buf1, sizeof (buf1)), - nm_platform_ip6_route_to_string (&rt, buf2, sizeof (buf2))); - } - - success = nm_platform_ip6_route_add (priv->platform, &rt); - } - - if (!success) { - _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); - } - return TRUE; -} - -static gboolean -_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, int ifindex_to_flush) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries = vtable->get_entries (priv); - GArray *routes; - guint i, j; - gboolean changed = FALSE; - - /* prune all other default routes from this device. */ - routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - - for (i = 0; i < routes->len; i++) { - const NMPlatformIPRoute *route; - gboolean has_ifindex_synced = FALSE; - Entry *entry = NULL; - - route = _vt_route_index (vtable, routes, i); - - /* look at all entries and see if the route for this ifindex pair is - * a known entry. */ - for (j = 0; j < entries->len; j++) { - Entry *e = g_ptr_array_index (entries, j); - - if ( e->route.rx.ifindex == route->ifindex - && e->synced) { - has_ifindex_synced = TRUE; - if ( !e->never_default - && e->effective_metric == route->metric) - entry = e; - } - } - - /* we only delete the route if we don't have a matching entry, - * and there is at least one entry that references this ifindex - * (indicating that the ifindex is managed by us -- not assumed). - * - * Otherwise, don't delete the route because it's configured - * externally (and will be assumed -- or already is assumed). - */ - if ( !entry - && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) { - vtable->vt->route_delete_default (priv->platform, route->ifindex, route->metric); - changed = TRUE; - } - } - g_array_free (routes, TRUE); - return changed; -} - -static int -_sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data) -{ - guint32 m_a, m_b; - const Entry *e_a = *((const Entry **) a); - const Entry *e_b = *((const Entry **) b); - - /* when comparing routes, we consider the (original) metric. */ - m_a = e_a->route.rx.metric; - m_b = e_b->route.rx.metric; - - /* we normalize route.metric already in _ipx_update_default_route(). - * so we can just compare the metrics numerically */ - - if (m_a != m_b) - return (m_a < m_b) ? -1 : 1; - - /* If the metrics are equal, we prefer the one that is !never_default */ - if (!!e_a->never_default != !!e_b->never_default) - return e_a->never_default ? 1 : -1; - - /* If the metrics are equal, we prefer the one that is assumed (!synced). - * Entries that we sync, can be modified so that only the best - * entry has a (deterministically) lowest metric. - * With assumed devices we cannot increase/change the metric. - * For example: two devices, both metric 0. One is assumed the other is - * synced. - * If we would choose the synced entry as best, we cannot - * increase the metric of the assumed one and we would have non-determinism. - * If we instead prefer the assumed device, we can increase the metric - * of the synced device and the assumed device is (deterministically) - * prefered. - * If both devices are assumed, we also have non-determinism, but also - * we don't reorder either. - */ - if (!!e_a->synced != !!e_b->synced) - return e_a->synced ? 1 : -1; - - /* otherwise, do not reorder */ - return 0; -} - -static GHashTable * -_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, GArray *routes) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries; - guint i, j; - GHashTable *result; - - /* create a list of all metrics that are currently assigned on an interface - * that is *not* already covered by one of our synced entries. - * IOW, returns the metrics that are in use by assumed interfaces - * that we want to preserve. */ - - entries = vtable->get_entries (priv); - - result = g_hash_table_new (NULL, NULL); - - for (i = 0; i < routes->len; i++) { - gboolean ifindex_has_synced_entry = FALSE; - const NMPlatformIPRoute *route; - - route = _vt_route_index (vtable, routes, i); - - for (j = 0; j < entries->len; j++) { - Entry *e = g_ptr_array_index (entries, j); - - if ( e->synced - && e->route.rx.ifindex == route->ifindex) { - ifindex_has_synced_entry = TRUE; - break; - } - } - - if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); - } - - /* also add all non-synced metrics from our entries list. We might have there some metrics that - * we track as non-synced but that are no longer part of platform routes. Anyway, for now - * we still want to treat them as assumed. */ - for (i = 0; i < entries->len; i++) { - gboolean ifindex_has_synced_entry = FALSE; - Entry *e_i = g_ptr_array_index (entries, i); - - if (e_i->synced) - continue; - - for (j = 0; j < entries->len; j++) { - Entry *e_j = g_ptr_array_index (entries, j); - - if ( j != i - && (e_j->synced && e_j->route.rx.ifindex == e_i->route.rx.ifindex)) { - ifindex_has_synced_entry = TRUE; - break; - } - } - - if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (e_i->route.rx.metric))); - } - - return result; -} - -static gboolean -_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - guint i, j; - gint64 last_metric = -1; - guint32 expected_metric; - GPtrArray *entries; - GArray *changed_metrics = g_array_new (FALSE, FALSE, sizeof (guint32)); - GHashTable *assumed_metrics; - GArray *routes; - gboolean changed = FALSE; - int ifindex_to_flush = 0; - - g_assert (priv->resync.guard == 0); - priv->resync.guard++; - - if (!external_change) { - if (vtable->vt->is_ip4) - priv->resync.has_v4_changes = FALSE; - else - priv->resync.has_v6_changes = FALSE; - if (!priv->resync.has_v4_changes && !priv->resync.has_v6_changes) - _resync_idle_cancel (self); - } - - entries = vtable->get_entries (priv); - - routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - - assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); - - if (old_entry && old_entry->synced && !old_entry->never_default) { - /* The old version obviously changed. */ - g_array_append_val (changed_metrics, old_entry->effective_metric); - } - - /* first iterate over all entries and adjust the effective metrics. */ - for (i = 0; i < entries->len; i++) { - entry = g_ptr_array_index (entries, i); - - if (entry->never_default) - continue; - - if (!entry->synced) { - gboolean has_synced_entry = FALSE; - - /* A non synced entry is completely ignored, if we have - * a synced entry for the same if index. - * Otherwise the metric of the entry is still remembered as - * last_metric to avoid reusing it. */ - for (j = 0; j < entries->len; j++) { - const Entry *e = g_ptr_array_index (entries, j); - - if ( e->synced - && e->route.rx.ifindex == entry->route.rx.ifindex) { - has_synced_entry = TRUE; - break; - } - } - if (!has_synced_entry) - last_metric = MAX (last_metric, (gint64) entry->effective_metric); - continue; - } - - expected_metric = entry->route.rx.metric; - if ((gint64) expected_metric <= last_metric) - expected_metric = last_metric == G_MAXUINT32 ? G_MAXUINT32 : last_metric + 1; - - while ( expected_metric < G_MAXUINT32 - && g_hash_table_contains (assumed_metrics, GUINT_TO_POINTER (expected_metric))) { - gboolean has_metric_for_ifindex = FALSE; - - /* Check if there are assumed devices that have default routes with this metric. - * If there are any, we have to pick another effective_metric. */ - - /* However, if there is a matching route (ifindex+metric) for our current entry, we are done. */ - for (j = 0; j < routes->len; j++) { - const NMPlatformIPRoute *r = _vt_route_index (vtable, routes, i); - - if ( r->metric == expected_metric - && r->ifindex == entry->route.rx.ifindex) { - has_metric_for_ifindex = TRUE; - break; - } - } - if (has_metric_for_ifindex) - break; - expected_metric++; - } - - if (changed_entry == entry) { - /* for the changed entry, the previous metric was either old_entry->effective_metric, - * or none. Hence, we only have to remember what is going to change. */ - g_array_append_val (changed_metrics, expected_metric); - if (!old_entry) { - _LOG2D (vtable, i, entry, "sync:add %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric); - } else if (old_entry != changed_entry) { - _LOG2D (vtable, i, entry, "sync:update %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) old_entry->effective_metric, - (guint) expected_metric); - } else { - _LOG2D (vtable, i, entry, "sync:resync %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric); - } - } else if (entry->effective_metric != expected_metric) { - g_array_append_val (changed_metrics, entry->effective_metric); - g_array_append_val (changed_metrics, expected_metric); - _LOG2D (vtable, i, entry, "sync:metric %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, - (guint) expected_metric); - } else { - if (!_vt_routes_has_entry (vtable, routes, entry)) { - g_array_append_val (changed_metrics, entry->effective_metric); - _LOG2D (vtable, i, entry, "sync:re-add %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, - (guint) entry->effective_metric); - } - } - - if (entry->effective_metric != expected_metric) { - entry->effective_metric = expected_metric; - changed = TRUE; - } - last_metric = expected_metric; - } - - g_array_free (routes, TRUE); - - g_array_sort_with_data (changed_metrics, nm_cmp_uint32_p_with_data, NULL); - last_metric = -1; - for (j = 0; j < changed_metrics->len; j++) { - expected_metric = g_array_index (changed_metrics, guint32, j); - - if (last_metric == (gint64) expected_metric) { - /* skip duplicates. */ - continue; - } - changed |= _platform_route_sync_add (vtable, self, expected_metric); - last_metric = expected_metric; - } - - if ( old_entry - && !changed_entry - && old_entry->synced - && !old_entry->never_default) { - /* If we entriely remove an entry that was synced before, we must make - * sure to flush routes for this ifindex too. Otherwise they linger - * around as "assumed" routes */ - ifindex_to_flush = old_entry->route.rx.ifindex; - } - - changed |= _platform_route_sync_flush (vtable, self, ifindex_to_flush); - - g_array_free (changed_metrics, TRUE); - g_hash_table_unref (assumed_metrics); - - priv->resync.guard--; - return changed; -} - -static gboolean -_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - GPtrArray *entries; - - entries = vtable->get_entries (priv); - g_assert (entry_idx < entries->len); - - entry = g_ptr_array_index (entries, entry_idx); - - g_assert ( !old_entry - || (entry->source.pointer == old_entry->source.pointer && entry->route.rx.ifindex == old_entry->route.rx.ifindex)); - - if (!entry->synced && !entry->never_default) - entry->effective_metric = entry->route.rx.metric; - - _LOG2D (vtable, entry_idx, entry, "%s %s (%"G_GUINT32_FORMAT")", - old_entry - ? (entry != old_entry - ? "record:update" - : "record:resync") - : "record:add ", - vtable->vt->route_to_string (&entry->route, NULL, 0), - entry->effective_metric); - - g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); - - return _resync_all (vtable, self, entry, old_entry, FALSE); -} - -static gboolean -_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - GPtrArray *entries; - gboolean ret; - - entries = vtable->get_entries (priv); - - g_assert (entry_idx < entries->len); - - entry = g_ptr_array_index (entries, entry_idx); - - _LOG2D (vtable, entry_idx, entry, "record:remove %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); - - /* Remove the entry from the list (but don't free it yet) */ - g_ptr_array_index (entries, entry_idx) = NULL; - g_ptr_array_remove_index (entries, entry_idx); - - ret = _resync_all (vtable, self, NULL, entry, FALSE); - _entry_free (entry); - - return ret; -} - -/*****************************************************************************/ - -static gboolean -_ipx_update_default_route (const VTableIP *vtable, - NMDefaultRouteManager *self, - gpointer source) -{ - NMDefaultRouteManagerPrivate *priv; - Entry *entry; - guint entry_idx; - const NMPlatformIPRoute *default_route = NULL; - NMPlatformIPXRoute rt; - int ip_ifindex; - GPtrArray *entries; - NMDevice *device = NULL; - NMVpnConnection *vpn = NULL; - gboolean never_default = FALSE; - gboolean synced = FALSE, ret; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return FALSE; - - if (NM_IS_DEVICE (source)) - device = source; - else if (NM_IS_VPN_CONNECTION (source)) - vpn = source; - else - g_return_val_if_reached (FALSE); - - if (device) - ip_ifindex = nm_device_get_ip_ifindex (device); - else - ip_ifindex = nm_vpn_connection_get_ip_ifindex (vpn, TRUE); - - entries = vtable->get_entries (priv); - entry = _entry_find_by_source (entries, source, &entry_idx); - - if ( entry - && entry->route.rx.ifindex != ip_ifindex) { - /* Strange... the ifindex changed... Remove the device and start again. */ - _LOG2D (vtable, entry_idx, entry, "ifindex changed: %d -> %d", - entry->route.rx.ifindex, ip_ifindex); - - g_object_freeze_notify (G_OBJECT (self)); - _entry_at_idx_remove (vtable, self, entry_idx); - g_assert (!_entry_find_by_source (entries, source, NULL)); - ret = _ipx_update_default_route (vtable, self, source); - g_object_thaw_notify (G_OBJECT (self)); - return ret; - } - - /* get the @default_route from the device. */ - if (ip_ifindex > 0) { - if (device) { - gboolean is_assumed = FALSE; - - if (vtable->vt->is_ip4) - default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device, &is_assumed); - else - default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device, &is_assumed); - if (!default_route && !is_assumed) { - /* the device has no default route, but it is not assumed. That means, NMDefaultRouteManager - * enforces that the device has no default route. - * - * Hence we have to keep track of this entry, otherwise a missing entry tells us - * that the interface is assumed and NM would not remove the default routes on - * the device. */ - memset (&rt, 0, sizeof (rt)); - rt.rx.ifindex = ip_ifindex; - rt.rx.rt_source = NM_IP_CONFIG_SOURCE_UNKNOWN; - rt.rx.metric = G_MAXUINT32; - default_route = &rt.rx; - - never_default = TRUE; - } - synced = !is_assumed; - } else { - NMConnection *connection = nm_active_connection_get_applied_connection ((NMActiveConnection *) vpn); - - if ( connection - && nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) { - - memset (&rt, 0, sizeof (rt)); - if (vtable->vt->is_ip4) { - NMIP4Config *vpn_config; - - vpn_config = nm_vpn_connection_get_ip4_config (vpn); - if (vpn_config) { - never_default = nm_ip4_config_get_never_default (vpn_config); - rt.r4.ifindex = ip_ifindex; - rt.r4.rt_source = NM_IP_CONFIG_SOURCE_VPN; - rt.r4.gateway = nm_ip4_config_get_gateway (vpn_config); - rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn); - rt.r4.mss = nm_ip4_config_get_mss (vpn_config); - default_route = &rt.rx; - } - } else { - NMIP6Config *vpn_config; - - vpn_config = nm_vpn_connection_get_ip6_config (vpn); - if (vpn_config) { - const struct in6_addr *int_gw = nm_ip6_config_get_gateway (vpn_config); - - never_default = nm_ip6_config_get_never_default (vpn_config); - rt.r6.ifindex = ip_ifindex; - rt.r6.rt_source = NM_IP_CONFIG_SOURCE_VPN; - rt.r6.gateway = int_gw ? *int_gw : in6addr_any; - rt.r6.metric = nm_vpn_connection_get_ip6_route_metric (vpn); - rt.r6.mss = nm_ip6_config_get_mss (vpn_config); - default_route = &rt.rx; - } - } - } - if (nm_vpn_connection_get_ip_ifindex (vpn, FALSE) > 0) - synced = TRUE; - else { - /* a VPN connection without tunnel device cannot have a non-synced, missing default route. - * Either it has a default route (which is synced), or it has no entry. */ - synced = default_route && !never_default; - } - } - } - - g_assert (!default_route || default_route->plen == 0); - - if (!synced && never_default) { - /* having a non-synced, never-default entry is non-sensical. Unset - * @default_route so that we don't add such an entry below. */ - default_route = NULL; - } - - if (!entry && !default_route) { - /* nothing to do */ - return FALSE; - } else if (!entry) { - /* add */ - entry = g_slice_new0 (Entry); - entry->source.object = g_object_ref (source); - - if (vtable->vt->is_ip4) - entry->route.r4 = *((const NMPlatformIP4Route *) default_route); - else - entry->route.r6 = *((const NMPlatformIP6Route *) default_route); - - /* only use normalized metrics */ - entry->route.rx.metric = vtable->vt->metric_normalize (entry->route.rx.metric); - entry->route.rx.ifindex = ip_ifindex; - entry->never_default = never_default; - entry->effective_metric = entry->route.rx.metric; - entry->synced = synced; - - g_ptr_array_add (entries, entry); - return _entry_at_idx_update (vtable, self, entries->len - 1, NULL); - } else if (default_route) { - /* update */ - Entry old_entry, new_entry; - - new_entry = *entry; - if (vtable->vt->is_ip4) - new_entry.route.r4 = *((const NMPlatformIP4Route *) default_route); - else - new_entry.route.r6 = *((const NMPlatformIP6Route *) default_route); - /* only use normalized metrics */ - new_entry.route.rx.metric = vtable->vt->metric_normalize (new_entry.route.rx.metric); - new_entry.route.rx.ifindex = ip_ifindex; - new_entry.never_default = never_default; - new_entry.synced = synced; - - if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0) { - if (!synced) { - /* the internal book-keeping doesn't change, so don't do a full - * sync of the configured routes. */ - return FALSE; - } - return _entry_at_idx_update (vtable, self, entry_idx, entry); - } else { - old_entry = *entry; - *entry = new_entry; - return _entry_at_idx_update (vtable, self, entry_idx, &old_entry); - } - } else { - /* delete */ - return _entry_at_idx_remove (vtable, self, entry_idx); - } -} - -gboolean -nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, - gpointer source) -{ - return _ipx_update_default_route (&vtable_ip4, self, source); -} - -gboolean -nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, - gpointer source) -{ - return _ipx_update_default_route (&vtable_ip6, self, source); -} - -/*****************************************************************************/ - -static gboolean -_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) -{ - const char *method; - NMSettingIPConfig *s_ip; - gboolean is_never_default = FALSE; - gboolean has_default_route = FALSE; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - - if (!connection) - goto out; - - if (vtable->vt->is_ip4) - s_ip = nm_connection_get_setting_ip4_config (connection); - else - s_ip = nm_connection_get_setting_ip6_config (connection); - if (!s_ip) - goto out; - if (nm_setting_ip_config_get_never_default (s_ip)) { - is_never_default = TRUE; - goto out; - } - - if (vtable->vt->is_ip4) { - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if ( !method - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - goto out; - } else { - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if ( !method - || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) - || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - goto out; - } - - has_default_route = TRUE; -out: - if (out_is_never_default) - *out_is_never_default = is_never_default; - return has_default_route; -} - -gboolean -nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) -{ - return _ipx_connection_has_default_route (&vtable_ip4, self, connection, out_is_never_default); -} - -gboolean -nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) -{ - return _ipx_connection_has_default_route (&vtable_ip6, self, connection, out_is_never_default); -} - -/*****************************************************************************/ - -static NMDevice * -_ipx_get_best_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices) -{ - NMDefaultRouteManagerPrivate *priv; - GPtrArray *entries; - guint i; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - if (!devices) - return NULL; - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - entries = vtable->get_entries (priv); - - for (i = 0; i < entries->len; i++) { - Entry *entry = g_ptr_array_index (entries, i); - NMDeviceState state; - - if (!NM_IS_DEVICE (entry->source.pointer)) - continue; - - if (entry->never_default) - continue; - - state = nm_device_get_state (entry->source.device); - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) { - /* FIXME: we also track unmanaged devices with assumed default routes. - * Skip them, they are (currently) no candidates for best-device. - * - * Later we also want to properly assume connections for unmanaged devices. - * - * Also, we don't want to have DEACTIVATING devices returned as best_device(). */ - continue; - } - - if (g_slist_find ((GSList *) devices, entry->source.device)) { - g_return_val_if_fail (nm_device_get_act_request (entry->source.pointer), entry->source.pointer); - return entry->source.pointer; - } - } - return NULL; -} - -/** _ipx_get_best_activating_device: - * @vtable: the virtual table - * @self: #NMDefaultRouteManager - * @devices: list of devices to be searched. Only devices from this list will be considered - * @fully_activated: if #TRUE, only search for devices that are fully activated. Otherwise, - * search if there is a best device going to be activated. In the latter case, this will - * return NULL if the best device is already activated. - * @preferred_device: if not-NULL, this device is preferred if there are more devices with - * the same priority. - **/ -static NMDevice * -_ipx_get_best_activating_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices, NMDevice *preferred_device) -{ - NMDefaultRouteManagerPrivate *priv; - const GSList *iter; - NMDevice *best_device = NULL; - guint32 best_prio = G_MAXUINT32; - NMDevice *best_activated_device; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - - best_activated_device = _ipx_get_best_device (vtable, self, devices); - - for (iter = devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = NM_DEVICE (iter->data); - guint32 prio; - Entry *entry; - - entry = _entry_find_by_source (vtable->get_entries (priv), device, NULL); - - if (entry) { - /* of all the device that have an entry, we already know that best_activated_device - * is the best. entry cannot be better. */ - if (entry->source.device != best_activated_device) - continue; - prio = entry->effective_metric; - } else { - NMDeviceState state = nm_device_get_state (device); - - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) - continue; - - if (!_ipx_connection_has_default_route (vtable, self, nm_device_get_applied_connection (device), NULL)) - continue; - - prio = nm_device_get_ip4_route_metric (device); - } - prio = vtable->vt->metric_normalize (prio); - - if ( !best_device - || prio < best_prio - || (prio == best_prio && preferred_device == device)) { - best_device = device; - best_prio = prio; - } - } - - /* There's only a best activating device if the best device - * among all activating and already-activated devices is a - * still-activating one. - */ - if (best_device && nm_device_get_state (best_device) >= NM_DEVICE_STATE_SECONDARIES) - return NULL; - return best_device; -} - -NMDevice * -nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device) -{ - if (fully_activated) - return _ipx_get_best_device (&vtable_ip4, self, devices); - else - return _ipx_get_best_activating_device (&vtable_ip4, self, devices, preferred_device); -} - -NMDevice * -nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device) -{ - if (fully_activated) - return _ipx_get_best_device (&vtable_ip6, self, devices); - else - return _ipx_get_best_activating_device (&vtable_ip6, self, devices, preferred_device); -} - -/*****************************************************************************/ - -static gpointer -_ipx_get_best_config (const VTableIP *vtable, - NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - NMDefaultRouteManagerPrivate *priv; - GPtrArray *entries; - guint i; - gpointer config_result = NULL; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - if (out_ip_iface) - *out_ip_iface = NULL; - if (out_ac) - *out_ac = NULL; - if (out_device) - *out_device = NULL; - if (out_vpn) - *out_vpn = NULL; - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - entries = vtable->get_entries (priv); - - for (i = 0; i < entries->len; i++) { - Entry *entry = g_ptr_array_index (entries, i); - - if (!NM_IS_DEVICE (entry->source.pointer)) { - NMVpnConnection *vpn = NM_VPN_CONNECTION (entry->source.vpn); - - if (entry->never_default && !ignore_never_default) - continue; - - if (vtable->vt->is_ip4) - config_result = nm_vpn_connection_get_ip4_config (vpn); - else - config_result = nm_vpn_connection_get_ip6_config (vpn); - g_assert (config_result); - - if (out_vpn) - *out_vpn = vpn; - if (out_ac) - *out_ac = NM_ACTIVE_CONNECTION (vpn); - if (out_ip_iface) - *out_ip_iface = nm_vpn_connection_get_ip_iface (vpn, TRUE); - } else { - NMDevice *device = entry->source.device; - NMActRequest *req; - NMDeviceState state; - - if (entry->never_default) - continue; - - state = nm_device_get_state (device); - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) { - /* FIXME: the device has a default route, but we ignore it due to - * unexpected state. That happens for example for unmanaged devices. - * - * In the future, we want unmanaged devices also assume a connection - * if they are activated externally. - * - * Also, we don't want to have DEACTIVATING devices returned as best_config(). */ - continue; - } - - if (vtable->vt->is_ip4) - config_result = nm_device_get_ip4_config (device); - else - config_result = nm_device_get_ip6_config (device); - g_assert (config_result); - req = nm_device_get_act_request (device); - g_assert (req); - - if (out_device) - *out_device = device; - if (out_ac) - *out_ac = NM_ACTIVE_CONNECTION (req); - if (out_ip_iface) - *out_ip_iface = nm_device_get_ip_iface (device); - } - break; - } - - return config_result; -} - -NMIP4Config * -nm_default_route_manager_ip4_get_best_config (NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return _ipx_get_best_config (&vtable_ip4, - self, - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - -NMIP6Config * -nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return _ipx_get_best_config (&vtable_ip6, - self, - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - -/*****************************************************************************/ - -static GPtrArray * -_v4_get_entries (NMDefaultRouteManagerPrivate *priv) -{ - return priv->entries_ip4; -} - -static GPtrArray * -_v6_get_entries (NMDefaultRouteManagerPrivate *priv) -{ - return priv->entries_ip6; -} - -static const VTableIP vtable_ip4 = { - .vt = &nm_platform_vtable_route_v4, - .get_entries = _v4_get_entries, -}; - -static const VTableIP vtable_ip6 = { - .vt = &nm_platform_vtable_route_v6, - .get_entries = _v6_get_entries, -}; - -/*****************************************************************************/ - -static gboolean -_resync_now (NMDefaultRouteManager *self) -{ - gboolean has_v4_changes, has_v6_changes; - gboolean changed = FALSE; - - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - has_v4_changes = priv->resync.has_v4_changes; - has_v6_changes = priv->resync.has_v6_changes; - - _LOGD (0, "resync: sync now (%u) (IPv4 changes: %s, IPv6 changes: %s)", priv->resync.idle_handle, - has_v4_changes ? "yes" : "no", has_v6_changes ? "yes" : "no"); - - priv->resync.has_v4_changes = FALSE; - priv->resync.has_v6_changes = FALSE; - nm_clear_g_source (&priv->resync.idle_handle); - priv->resync.backoff_wait_time_ms = - priv->resync.backoff_wait_time_ms == 0 - ? 100 - : priv->resync.backoff_wait_time_ms * 2; - - if (has_v4_changes) - changed |= _resync_all (&vtable_ip4, self, NULL, NULL, TRUE); - - if (has_v6_changes) - changed |= _resync_all (&vtable_ip6, self, NULL, NULL, TRUE); - - if (!changed) { - /* Nothing changed: reset the backoff wait time */ - _resync_idle_cancel (self); - } - - return changed; -} - -/** - * nm_default_route_manager_resync: - * @self: the #NMDefaultRouteManager instance - * @af_family: the address family to resync, can be - * AF_INET, AF_INET6 or AF_UNSPEC to sync both. - * - * #NMDefaultRouteManager keeps an internal list of configured - * routes. Usually, it configures routes in the system only - * - when that internal list changes due to - * nm_default_route_manager_ip4_update_default_route() or - * nm_default_route_manager_ip6_update_default_route(). - * - when platform notifies about changes, via _resync_idle_now(). - * This forces a resync to update the internal bookkeeping - * with what is currently configured in the system, but also - * reconfigure the system with all non-assumed default routes. - * - * Returns: %TRUE if anything changed during resync. - */ -gboolean -nm_default_route_manager_resync (NMDefaultRouteManager *self, - int af_family) -{ - NMDefaultRouteManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - g_return_val_if_fail (NM_IN_SET (af_family, AF_INET, AF_INET6, AF_UNSPEC), FALSE); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->disposed) - return FALSE; - - switch (af_family) { - case AF_INET: - priv->resync.has_v4_changes = TRUE; - break; - case AF_INET6: - priv->resync.has_v6_changes = TRUE; - break; - default: - priv->resync.has_v4_changes = TRUE; - priv->resync.has_v6_changes = TRUE; - break; - } - - return _resync_now (self); -} - -static gboolean -_resync_idle_now (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->resync.idle_handle = 0; - _resync_now (self); - return G_SOURCE_REMOVE; -} - -static void -_resync_idle_cancel (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->resync.idle_handle) { - _LOGD (0, "resync: cancelled (%u)", priv->resync.idle_handle); - g_source_remove (priv->resync.idle_handle); - priv->resync.idle_handle = 0; - } - priv->resync.backoff_wait_time_ms = 0; - priv->resync.has_v4_changes = FALSE; - priv->resync.has_v6_changes = FALSE; -} - -static void -_resync_idle_reschedule (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - /* since we react on external changes and re-add/remove default routes for - * the interfaces we manage, there could be the erroneous situation where two applications - * fight over a certain default route. - * Avoid this, by increasingly wait longer to touch the system (backoff wait time). */ - - if (priv->resync.backoff_wait_time_ms == 0) { - /* for scheduling idle, always reschedule (to process all other events first) */ - if (priv->resync.idle_handle) - g_source_remove (priv->resync.idle_handle); - else - _LOGD (0, "resync: schedule on idle"); - /* Schedule this at low priority so that on an external change to platform - * a NMDevice has a chance to picks up the changes first. */ - priv->resync.idle_handle = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) _resync_idle_now, self, NULL); - } else if (!priv->resync.idle_handle) { - priv->resync.idle_handle = g_timeout_add (priv->resync.backoff_wait_time_ms, (GSourceFunc) _resync_idle_now, self); - _LOGD (0, "resync: schedule in %u.%03u seconds (%u)", priv->resync.backoff_wait_time_ms/1000, - priv->resync.backoff_wait_time_ms%1000, priv->resync.idle_handle); - } -} - -static void -_platform_changed_cb (NMPlatform *platform, - int obj_type_i, - int ifindex, - gpointer platform_object, - int change_type_i, - NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv; - const NMPObjectType obj_type = obj_type_i; - const VTableIP *vtable; - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - vtable = &vtable_ip4; - break; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - vtable = &vtable_ip6; - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object)) - return; - vtable = &vtable_ip4; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object)) - return; - vtable = &vtable_ip6; - break; - default: - g_return_if_reached (); - } - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->resync.guard) { - /* callbacks while executing _resync_all() are ignored. */ - return; - } - - if (vtable->vt->is_ip4) - priv->resync.has_v4_changes = TRUE; - else - priv->resync.has_v6_changes = TRUE; - - _resync_idle_reschedule (self); -} - -/*****************************************************************************/ - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_LOG_WITH_PTR: - /* construct-only */ - priv->log_with_ptr = g_value_get_boolean (value); - break; - case PROP_PLATFORM: - /* construct-only */ - priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET; - if (!priv->platform) - g_return_if_reached (); - g_object_ref (priv->platform); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_default_route_manager_init (NMDefaultRouteManager *self) -{ -} - -static void -constructed (GObject *object) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->entries_ip4 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free); - priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free); - - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); -} - -NMDefaultRouteManager * -nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform) -{ - return g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER, - NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr, - NM_DEFAULT_ROUTE_MANAGER_PLATFORM, platform, - NULL); -} - -static void -dispose (GObject *object) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->disposed = TRUE; - - if (priv->platform) { - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_platform_changed_cb), self); - g_clear_object (&priv->platform); - } - - _resync_idle_cancel (self); - - /* g_ptr_array_free() invokes the free function for all entries without actually - * removing them and having dangling pointers in the process. _entry_free() - * will unref the source, which might cause the destruction of the object, which - * might trigger calling into @self again. This is guarded by priv->dispose. - * If you remove priv->dispose, you must refactor the lines below to remove enties - * one-by-one. - */ - if (priv->entries_ip4) { - g_ptr_array_free (priv->entries_ip4, TRUE); - priv->entries_ip4 = NULL; - } - if (priv->entries_ip6) { - g_ptr_array_free (priv->entries_ip6, TRUE); - priv->entries_ip6 = NULL; - } - - G_OBJECT_CLASS (nm_default_route_manager_parent_class)->dispose (object); -} - -static void -nm_default_route_manager_class_init (NMDefaultRouteManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = constructed; - object_class->dispose = dispose; - object_class->set_property = set_property; - - obj_properties[PROP_LOG_WITH_PTR] = - g_param_spec_boolean (NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, "", "", - TRUE, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PLATFORM] = - g_param_spec_object (NM_DEFAULT_ROUTE_MANAGER_PLATFORM, "", "", - NM_TYPE_PLATFORM, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h deleted file mode 100644 index bac8a6eb..00000000 --- a/src/nm-default-route-manager.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ -#define __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ - -#include "nm-connection.h" - -#define NM_TYPE_DEFAULT_ROUTE_MANAGER (nm_default_route_manager_get_type ()) -#define NM_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManager)) -#define NM_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) -#define NM_IS_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER)) -#define NM_IS_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER)) -#define NM_DEFAULT_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) - -#define NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr" -#define NM_DEFAULT_ROUTE_MANAGER_PLATFORM "platform" - -typedef struct _NMDefaultRouteManagerClass NMDefaultRouteManagerClass; - -GType nm_default_route_manager_get_type (void); - -NMDefaultRouteManager *nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform); - -gboolean nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source); -gboolean nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source); - -gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default); -gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default); - -NMDevice *nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); -NMDevice *nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); - -NMIP4Config *nm_default_route_manager_ip4_get_best_config (NMDefaultRouteManager *manager, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn); -NMIP6Config *nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager *manager, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn); - -gboolean nm_default_route_manager_resync (NMDefaultRouteManager *self, - int af_family); - -#endif /* NM_DEFAULT_ROUTE_MANAGER_H */ diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 0d482e0c..237afdef 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -113,6 +113,8 @@ static void dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) { GVariantBuilder int_builder; + NMDedupMultiIter ipconf_iter; + gboolean first; guint n, i; const NMPlatformIP4Address *addr; const NMPlatformIP4Route *route; @@ -120,15 +122,20 @@ dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) /* Addresses */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau")); - n = nm_ip4_config_get_num_addresses (ip4); - for (i = 0; i < n; i++) { - addr = nm_ip4_config_get_address (ip4, i); + first = TRUE; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &addr) { + const NMPObject *default_route; + array[0] = addr->address; array[1] = addr->plen; - array[2] = (i == 0) ? nm_ip4_config_get_gateway (ip4) : 0; + array[2] = ( first + && (default_route = nm_ip4_config_best_default_route_get (ip4))) + ? NMP_OBJECT_CAST_IP4_ROUTE (default_route)->gateway + : (guint32) 0; g_variant_builder_add (&int_builder, "@au", g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, array, 3, sizeof (guint32))); + first = FALSE; } g_variant_builder_add (builder, "{sv}", "addresses", @@ -163,9 +170,9 @@ dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau")); - n = nm_ip4_config_get_num_routes (ip4); - for (i = 0; i < n; i++) { - route = nm_ip4_config_get_route (ip4, i); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; array[0] = route->network; array[1] = route->plen; array[2] = route->gateway; @@ -183,25 +190,31 @@ static void dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder) { GVariantBuilder int_builder; + NMDedupMultiIter ipconf_iter; guint n, i; + gboolean first; const NMPlatformIP6Address *addr; - const struct in6_addr *gw_bytes; const NMPlatformIP6Route *route; GVariant *ip, *gw; /* Addresses */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuay)")); - n = nm_ip6_config_get_num_addresses (ip6); - for (i = 0; i < n; i++) { - addr = nm_ip6_config_get_address (ip6, i); - gw_bytes = nm_ip6_config_get_gateway (ip6); + + first = TRUE; + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &addr) { + const NMPObject *default_route; + ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &addr->address, sizeof (struct in6_addr), 1); gw = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - (i == 0 && gw_bytes) ? gw_bytes : &in6addr_any, + ( first + && (default_route = nm_ip6_config_best_default_route_get (ip6))) + ? &NMP_OBJECT_CAST_IP6_ROUTE (default_route)->gateway + : &in6addr_any, sizeof (struct in6_addr), 1); g_variant_builder_add (&int_builder, "(@ayu@ay)", ip, addr->plen, gw); + first = FALSE; } g_variant_builder_add (builder, "{sv}", "addresses", @@ -231,9 +244,9 @@ dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuayu)")); - n = nm_ip6_config_get_num_routes (ip6); - for (i = 0; i < n; i++) { - route = nm_ip6_config_get_route (ip6, i); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &route->network, sizeof (struct in6_addr), 1); diff --git a/src/nm-exported-object.c b/src/nm-exported-object.c index 0e903b89..94264caa 100644 --- a/src/nm-exported-object.c +++ b/src/nm-exported-object.c @@ -272,7 +272,7 @@ nm_exported_object_class_add_interface (NMExportedObjectClass *object_class, classinfo = g_slice_new (NMExportedObjectClassInfo); classinfo->skeleton_types = NULL; classinfo->methods = g_array_new (FALSE, FALSE, sizeof (NMExportedObjectDBusMethodImpl)); - classinfo->properties = g_hash_table_new (g_str_hash, g_str_equal); + classinfo->properties = g_hash_table_new (nm_str_hash, g_str_equal); g_type_set_qdata (G_TYPE_FROM_CLASS (object_class), nm_exported_object_class_info_quark (), classinfo); } @@ -579,7 +579,7 @@ _create_export_path (NMExportedObjectClass *klass) p = strchr (class_export_path, '%'); if (p) { if (G_UNLIKELY (!prefix_counters)) - prefix_counters = g_hash_table_new (g_str_hash, g_str_equal); + prefix_counters = g_hash_table_new (nm_str_hash, g_str_equal); nm_assert (p[1] == 'l'); nm_assert (p[2] == 'l'); diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c index 4a887b79..b9878592 100644 --- a/src/nm-firewall-manager.c +++ b/src/nm-firewall-manager.c @@ -25,6 +25,7 @@ #include <string.h> #include "NetworkManagerUtils.h" +#include "nm-utils/c-list.h" /*****************************************************************************/ @@ -39,7 +40,7 @@ typedef struct { GDBusProxy *proxy; GCancellable *proxy_cancellable; - GHashTable *pending_calls; + CList pending_calls; bool running; } NMFirewallManagerPrivate; @@ -76,6 +77,7 @@ typedef enum { } CBInfoMode; struct _NMFirewallManagerCallId { + CList lst; NMFirewallManager *self; CBInfoOpsType ops_type; union { @@ -176,8 +178,7 @@ _cb_info_create (NMFirewallManager *self, } else info->mode_mutable = CB_INFO_MODE_IDLE; - if (!nm_g_hash_table_add (priv->pending_calls, info)) - nm_assert_not_reached (); + c_list_link_tail (&priv->pending_calls, &info->lst); return info; } @@ -185,6 +186,7 @@ _cb_info_create (NMFirewallManager *self, static void _cb_info_free (CBInfo *info) { + c_list_unlink (&info->lst); if (info->mode != CB_INFO_MODE_IDLE) { if (info->dbus.arg) g_variant_unref (info->dbus.arg); @@ -209,8 +211,9 @@ _cb_info_complete_normal (CBInfo *info, GError *error) { NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (info->self); - if (!g_hash_table_remove (priv->pending_calls, info)) - g_return_if_reached (); + nm_assert (c_list_contains (&priv->pending_calls, &info->lst)); + + c_list_unlink_init (&info->lst); _cb_info_callback (info, error); _cb_info_free (info); @@ -423,8 +426,9 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call) self = info->self; priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->pending_calls, info)) - g_return_if_reached (); + nm_assert (c_list_contains (&priv->pending_calls, &info->lst)); + + c_list_unlink_init (&info->lst); nm_utils_error_set_cancelled (&error, FALSE, "NMFirewallManager"); @@ -488,8 +492,8 @@ _proxy_new_cb (GObject *source_object, NMFirewallManagerPrivate *priv; GDBusProxy *proxy; gs_free_error GError *error = NULL; - GHashTableIter iter; CBInfo *info; + CList *iter; proxy = g_dbus_proxy_new_for_bus_finish (result, &error); if ( !proxy @@ -513,8 +517,9 @@ _proxy_new_cb (GObject *source_object, _LOGD (NULL, "firewall %s", "initialized (not running)"); again: - g_hash_table_iter_init (&iter, priv->pending_calls); - while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL)) { + c_list_for_each (iter, &priv->pending_calls) { + info = c_list_entry (iter, CBInfo, lst); + if (info->mode != CB_INFO_MODE_DBUS_WAITING) continue; if (priv->running) { @@ -522,7 +527,7 @@ again: _handle_dbus_start (self, info); } else { _LOGD (info, "complete: fake success"); - g_hash_table_iter_remove (&iter); + c_list_unlink_init (&info->lst); _cb_info_callback (info, NULL); _cb_info_free (info); goto again; @@ -541,7 +546,7 @@ nm_firewall_manager_init (NMFirewallManager * self) { NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self); - priv->pending_calls = g_hash_table_new (g_direct_hash, g_direct_equal); + c_list_init (&priv->pending_calls); } static void @@ -572,13 +577,9 @@ dispose (GObject *object) NMFirewallManager *self = NM_FIREWALL_MANAGER (object); NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self); - if (priv->pending_calls) { - /* as every pending operation takes a reference to the manager, - * we don't expect pending operations at this point. */ - g_assert (g_hash_table_size (priv->pending_calls) == 0); - g_hash_table_unref (priv->pending_calls); - priv->pending_calls = NULL; - } + /* as every pending operation takes a reference to the manager, + * we don't expect pending operations at this point. */ + nm_assert (c_list_is_empty (&priv->pending_calls)); nm_clear_g_cancellable (&priv->proxy_cancellable); g_clear_object (&priv->proxy); diff --git a/src/nm-hostname-manager.c b/src/nm-hostname-manager.c new file mode 100644 index 00000000..f569ab86 --- /dev/null +++ b/src/nm-hostname-manager.c @@ -0,0 +1,664 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-hostname-manager.h" + +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +#if HAVE_SELINUX +#include <selinux/selinux.h> +#endif + +#include "nm-common-macros.h" +#include "nm-dbus-interface.h" +#include "nm-connection.h" +#include "nm-utils.h" +#include "nm-core-internal.h" + +#include "NetworkManagerUtils.h" + +/*****************************************************************************/ + +#define HOSTNAMED_SERVICE_NAME "org.freedesktop.hostname1" +#define HOSTNAMED_SERVICE_PATH "/org/freedesktop/hostname1" +#define HOSTNAMED_SERVICE_INTERFACE "org.freedesktop.hostname1" + +#define HOSTNAME_FILE_DEFAULT "/etc/hostname" +#define HOSTNAME_FILE_UCASE_HOSTNAME "/etc/HOSTNAME" +#define HOSTNAME_FILE_GENTOO "/etc/conf.d/hostname" + +#define IFCFG_DIR SYSCONFDIR "/sysconfig/network" +#define CONF_DHCP IFCFG_DIR "/dhcp" + +#if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1 +#error "Can only define one of HOSTNAME_PERSIST_*" +#endif + +#if defined(HOSTNAME_PERSIST_SUSE) +#define HOSTNAME_FILE HOSTNAME_FILE_UCASE_HOSTNAME +#elif defined(HOSTNAME_PERSIST_SLACKWARE) +#define HOSTNAME_FILE HOSTNAME_FILE_UCASE_HOSTNAME +#elif defined(HOSTNAME_PERSIST_GENTOO) +#define HOSTNAME_FILE HOSTNAME_FILE_GENTOO +#else +#define HOSTNAME_FILE HOSTNAME_FILE_DEFAULT +#endif + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMHostnameManager, + PROP_HOSTNAME, +); + +typedef struct { + char *current_hostname; + GFileMonitor *monitor; + GFileMonitor *dhcp_monitor; + gulong monitor_id; + gulong dhcp_monitor_id; + GDBusProxy *hostnamed_proxy; +} NMHostnameManagerPrivate; + +struct _NMHostnameManager { + GObject parent; + NMHostnameManagerPrivate _priv; +}; + +struct _NMHostnameManagerClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMHostnameManager, nm_hostname_manager, G_TYPE_OBJECT); + +#define NM_HOSTNAME_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMHostnameManager, NM_IS_HOSTNAME_MANAGER) + +NM_DEFINE_SINGLETON_GETTER (NMHostnameManager, nm_hostname_manager_get, NM_TYPE_HOSTNAME_MANAGER); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "hostname", __VA_ARGS__) + +/*****************************************************************************/ + +#if defined(HOSTNAME_PERSIST_GENTOO) +static gchar * +read_hostname_gentoo (const char *path) +{ + gs_free char *contents = NULL; + gs_strfreev char **all_lines = NULL; + const char *tmp; + guint i; + + if (!g_file_get_contents (path, &contents, NULL, NULL)) + return NULL; + + all_lines = g_strsplit (contents, "\n", 0); + for (i = 0; all_lines[i]; i++) { + g_strstrip (all_lines[i]); + if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') + continue; + if (g_str_has_prefix (all_lines[i], "hostname=")) { + tmp = &all_lines[i][NM_STRLEN ("hostname=")]; + return g_shell_unquote (tmp, NULL); + } + } + return NULL; +} +#endif + +#if defined(HOSTNAME_PERSIST_SLACKWARE) +static gchar * +read_hostname_slackware (const char *path) +{ + gs_free char *contents = NULL; + gs_strfreev char **all_lines = NULL; + char *tmp; + guint i, j = 0; + + if (!g_file_get_contents (path, &contents, NULL, NULL)) + return NULL; + + all_lines = g_strsplit (contents, "\n", 0); + for (i = 0; all_lines[i]; i++) { + g_strstrip (all_lines[i]); + if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') + continue; + tmp = &all_lines[i][0]; + /* We only want up to the first '.' -- the rest of the */ + /* fqdn is defined in /etc/hosts */ + while (tmp[j] != '\0') { + if (tmp[j] == '.') { + tmp[j] = '\0'; + break; + } + j++; + } + return g_shell_unquote (tmp, NULL); + } + return NULL; +} +#endif + +#if defined(HOSTNAME_PERSIST_SUSE) +static gboolean +hostname_is_dynamic (void) +{ + GIOChannel *channel; + char *str = NULL; + gboolean dynamic = FALSE; + + channel = g_io_channel_new_file (CONF_DHCP, "r", NULL); + if (!channel) + return dynamic; + + while (g_io_channel_read_line (channel, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) { + if (str) { + g_strstrip (str); + if (g_str_has_prefix (str, "DHCLIENT_SET_HOSTNAME=")) + dynamic = strcmp (&str[NM_STRLEN ("DHCLIENT_SET_HOSTNAME=")], "\"yes\"") == 0; + g_free (str); + } + } + + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + + return dynamic; +} +#endif + +/* Returns an allocated string which the caller owns and must eventually free */ +char * +nm_hostname_manager_read_hostname (NMHostnameManager *self) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + char *hostname = NULL; + + if (priv->hostnamed_proxy) { + hostname = g_strdup (priv->current_hostname); + goto out; + } + +#if defined(HOSTNAME_PERSIST_SUSE) + if (priv->dhcp_monitor_id && hostname_is_dynamic ()) + return NULL; +#endif + +#if defined(HOSTNAME_PERSIST_GENTOO) + hostname = read_hostname_gentoo (HOSTNAME_FILE); +#elif defined(HOSTNAME_PERSIST_SLACKWARE) + hostname = read_hostname_slackware (HOSTNAME_FILE); +#else + if (g_file_get_contents (HOSTNAME_FILE, &hostname, NULL, NULL)) + g_strchomp (hostname); +#endif + +out: + if (hostname && !hostname[0]) { + g_free (hostname); + return NULL; + } + + return hostname; +} + +/*****************************************************************************/ + +const char * +nm_hostname_manager_get_hostname (NMHostnameManager *self) +{ + g_return_val_if_fail (NM_IS_HOSTNAME_MANAGER (self), NULL); + return NM_HOSTNAME_MANAGER_GET_PRIVATE (self)->current_hostname; +} + +static void +_set_hostname_take (NMHostnameManager *self, char *hostname) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + + _LOGI ("hostname changed from %s%s%s to %s%s%s", + NM_PRINT_FMT_QUOTED (priv->current_hostname, "\"", priv->current_hostname, "\"", "(none)"), + NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)")); + + g_free (priv->current_hostname); + priv->current_hostname = hostname; + _notify (self, PROP_HOSTNAME); +} + +static void +_set_hostname (NMHostnameManager *self, const char *hostname) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + + hostname = nm_str_not_empty (hostname); + if (!nm_streq0 (hostname, priv->current_hostname)) + _set_hostname_take (self, g_strdup (hostname)); +} + +static void +_set_hostname_read (NMHostnameManager *self) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + char *hostname; + + if (priv->hostnamed_proxy) { + /* read-hostname returns the current hostname with hostnamed. */ + return; + } + + hostname = nm_hostname_manager_read_hostname (self); + + if (nm_streq0 (hostname, priv->current_hostname)) { + g_free (hostname); + return; + } + + _set_hostname_take (self, hostname); +} + +/*****************************************************************************/ + +typedef struct { + char *hostname; + NMHostnameManagerSetHostnameCb cb; + gpointer user_data; +} SetHostnameInfo; + +static void +set_transient_hostname_done (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + gs_free SetHostnameInfo *info = user_data; + gs_unref_variant GVariant *result = NULL; + gs_free_error GError *error = NULL; + + result = g_dbus_proxy_call_finish (proxy, res, &error); + + if (error) { + _LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s", + info->hostname, error->message); + } + + info->cb (info->hostname, !error, info->user_data); + g_free (info->hostname); +} + +void +nm_hostname_manager_set_transient_hostname (NMHostnameManager *self, + const char *hostname, + NMHostnameManagerSetHostnameCb cb, + gpointer user_data) +{ + NMHostnameManagerPrivate *priv; + SetHostnameInfo *info; + + g_return_if_fail (NM_IS_HOSTNAME_MANAGER (self)); + + priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + + if (!priv->hostnamed_proxy) { + cb (hostname, FALSE, user_data); + return; + } + + info = g_new0 (SetHostnameInfo, 1); + info->hostname = g_strdup (hostname); + info->cb = cb; + info->user_data = user_data; + + g_dbus_proxy_call (priv->hostnamed_proxy, + "SetHostname", + g_variant_new ("(sb)", hostname, FALSE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + set_transient_hostname_done, + info); +} + +gboolean +nm_hostname_manager_get_transient_hostname (NMHostnameManager *self, char **hostname) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + GVariant *v_hostname; + + if (!priv->hostnamed_proxy) + return FALSE; + + v_hostname = g_dbus_proxy_get_cached_property (priv->hostnamed_proxy, + "Hostname"); + if (!v_hostname) { + _LOGT ("transient hostname retrieval failed"); + return FALSE; + } + + *hostname = g_variant_dup_string (v_hostname, NULL); + g_variant_unref (v_hostname); + + return TRUE; +} + +gboolean +nm_hostname_manager_write_hostname (NMHostnameManager *self, const char *hostname) +{ + NMHostnameManagerPrivate *priv; + char *hostname_eol; + gboolean ret; + gs_free_error GError *error = NULL; + const char *file = HOSTNAME_FILE; + gs_free char *link_path = NULL; + gs_unref_variant GVariant *var = NULL; + struct stat file_stat; +#if HAVE_SELINUX + security_context_t se_ctx_prev = NULL, se_ctx = NULL; + mode_t st_mode = 0; +#endif + + g_return_val_if_fail (NM_IS_HOSTNAME_MANAGER (self), FALSE); + + priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + + if (priv->hostnamed_proxy) { + var = g_dbus_proxy_call_sync (priv->hostnamed_proxy, + "SetStaticHostname", + g_variant_new ("(sb)", hostname, FALSE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) + _LOGW ("could not set hostname: %s", error->message); + + return !error; + } + + /* If the hostname file is a symbolic link, follow it to find where the + * real file is located, otherwise g_file_set_contents will attempt to + * replace the link with a plain file. + */ + if ( lstat (file, &file_stat) == 0 + && S_ISLNK (file_stat.st_mode) + && (link_path = nm_utils_read_link_absolute (file, NULL))) + file = link_path; + +#if HAVE_SELINUX + /* Get default context for hostname file and set it for fscreate */ + if (stat (file, &file_stat) == 0) + st_mode = file_stat.st_mode; + matchpathcon (file, st_mode, &se_ctx); + matchpathcon_fini (); + getfscreatecon (&se_ctx_prev); + setfscreatecon (se_ctx); +#endif + +#if defined (HOSTNAME_PERSIST_GENTOO) + hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n" + "hostname=\"%s\"\n", hostname); +#else + hostname_eol = g_strdup_printf ("%s\n", hostname); +#endif + + ret = g_file_set_contents (file, hostname_eol, -1, &error); + +#if HAVE_SELINUX + /* Restore previous context and cleanup */ + setfscreatecon (se_ctx_prev); + freecon (se_ctx); + freecon (se_ctx_prev); +#endif + + g_free (hostname_eol); + + if (!ret) { + _LOGW ("could not save hostname to %s: %s", file, error->message); + return FALSE; + } + + return TRUE; +} + +gboolean +nm_hostname_manager_validate_hostname (const char *hostname) +{ + const char *p; + gboolean dot = TRUE; + + if (!hostname || !hostname[0]) + return FALSE; + + for (p = hostname; *p; p++) { + if (*p == '.') { + if (dot) + return FALSE; + dot = TRUE; + } else { + if (!g_ascii_isalnum (*p) && (*p != '-') && (*p != '_')) + return FALSE; + dot = FALSE; + } + } + + if (dot) + return FALSE; + + return (p - hostname <= HOST_NAME_MAX); +} + +static void +hostname_file_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + _set_hostname_read (user_data); +} + +/*****************************************************************************/ + +static void +hostnamed_properties_changed (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMHostnameManager *self = user_data; + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + GVariant *v_hostname; + + v_hostname = g_dbus_proxy_get_cached_property (priv->hostnamed_proxy, + "StaticHostname"); + if (v_hostname) { + _set_hostname (self, g_variant_get_string (v_hostname, NULL)); + g_variant_unref (v_hostname); + } +} + +static void +setup_hostname_file_monitors (NMHostnameManager *self) +{ + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + GFileMonitor *monitor; + const char *path = HOSTNAME_FILE; + char *link_path = NULL; + struct stat file_stat; + GFile *file; + + /* resolve the path to the hostname file if it is a symbolic link */ + if ( lstat(path, &file_stat) == 0 + && S_ISLNK (file_stat.st_mode) + && (link_path = nm_utils_read_link_absolute (path, NULL))) { + path = link_path; + if ( lstat(link_path, &file_stat) == 0 + && S_ISLNK (file_stat.st_mode)) { + _LOGW ("only one level of symbolic link indirection is allowed when monitoring " + HOSTNAME_FILE); + } + } + + /* monitor changes to hostname file */ + file = g_file_new_for_path (path); + monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); + g_object_unref (file); + g_free(link_path); + if (monitor) { + priv->monitor_id = g_signal_connect (monitor, "changed", + G_CALLBACK (hostname_file_changed_cb), + self); + priv->monitor = monitor; + } + +#if defined (HOSTNAME_PERSIST_SUSE) + /* monitor changes to dhcp file to know whether the hostname is valid */ + file = g_file_new_for_path (CONF_DHCP); + monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); + g_object_unref (file); + if (monitor) { + priv->dhcp_monitor_id = g_signal_connect (monitor, "changed", + G_CALLBACK (hostname_file_changed_cb), + self); + priv->dhcp_monitor = monitor; + } +#endif + + _set_hostname_read (self); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMHostnameManager *self = NM_HOSTNAME_MANAGER (object); + + switch (prop_id) { + case PROP_HOSTNAME: + g_value_set_string (value, nm_hostname_manager_get_hostname (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_hostname_manager_init (NMHostnameManager *self) +{ +} + +static void +constructed (GObject *object) +{ + NMHostnameManager *self = NM_HOSTNAME_MANAGER (object); + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + GDBusProxy *proxy; + GVariant *variant; + gs_free_error GError *error = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, + HOSTNAMED_SERVICE_NAME, HOSTNAMED_SERVICE_PATH, + HOSTNAMED_SERVICE_INTERFACE, NULL, &error); + if (proxy) { + variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname"); + if (variant) { + _LOGI ("hostname: using hostnamed"); + priv->hostnamed_proxy = proxy; + g_signal_connect (proxy, "g-properties-changed", + G_CALLBACK (hostnamed_properties_changed), self); + hostnamed_properties_changed (proxy, NULL, NULL, self); + g_variant_unref (variant); + } else { + _LOGI ("hostname: couldn't get property from hostnamed"); + g_object_unref (proxy); + } + } else { + _LOGI ("hostname: hostnamed not used as proxy creation failed with: %s", + error->message); + g_clear_error (&error); + } + + if (!priv->hostnamed_proxy) + setup_hostname_file_monitors (self); + + G_OBJECT_CLASS (nm_hostname_manager_parent_class)->constructed (object); +} + +static void +dispose (GObject *object) +{ + NMHostnameManager *self = NM_HOSTNAME_MANAGER (object); + NMHostnameManagerPrivate *priv = NM_HOSTNAME_MANAGER_GET_PRIVATE (self); + + if (priv->hostnamed_proxy) { + g_signal_handlers_disconnect_by_func (priv->hostnamed_proxy, + G_CALLBACK (hostnamed_properties_changed), + self); + g_clear_object (&priv->hostnamed_proxy); + } + + if (priv->monitor) { + if (priv->monitor_id) + g_signal_handler_disconnect (priv->monitor, priv->monitor_id); + + g_file_monitor_cancel (priv->monitor); + g_clear_object (&priv->monitor); + } + + if (priv->dhcp_monitor) { + if (priv->dhcp_monitor_id) + g_signal_handler_disconnect (priv->dhcp_monitor, + priv->dhcp_monitor_id); + + g_file_monitor_cancel (priv->dhcp_monitor); + g_clear_object (&priv->dhcp_monitor); + } + + nm_clear_g_free (&priv->current_hostname); + + G_OBJECT_CLASS (nm_hostname_manager_parent_class)->dispose (object); +} + +static void +nm_hostname_manager_class_init (NMHostnameManagerClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + + obj_properties[PROP_HOSTNAME] = + g_param_spec_string (NM_HOSTNAME_MANAGER_HOSTNAME, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/nm-hostname-manager.h b/src/nm-hostname-manager.h new file mode 100644 index 00000000..a837e9b9 --- /dev/null +++ b/src/nm-hostname-manager.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager + * + * Søren Sandmann <sandmann@daimi.au.dk> + * Dan Williams <dcbw@redhat.com> + * Tambet Ingo <tambet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2011, 2017 Red Hat, Inc. + * (C) Copyright 2008 Novell, Inc. + */ + +#ifndef __NM_HOSTNAME_MANAGER_H__ +#define __NM_HOSTNAME_MANAGER_H__ + +#define NM_TYPE_HOSTNAME_MANAGER (nm_hostname_manager_get_type ()) +#define NM_HOSTNAME_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManager)) +#define NM_HOSTNAME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManagerClass)) +#define NM_IS_HOSTNAME_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_HOSTNAME_MANAGER)) +#define NM_IS_HOSTNAME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_HOSTNAME_MANAGER)) +#define NM_HOSTNAME_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManagerClass)) + +#define NM_HOSTNAME_MANAGER_HOSTNAME "hostname" + +typedef struct _NMHostnameManager NMHostnameManager; +typedef struct _NMHostnameManagerClass NMHostnameManagerClass; + +typedef void (*NMHostnameManagerSetHostnameCb) (const char *name, gboolean result, gpointer user_data); + +GType nm_hostname_manager_get_type (void); + +NMHostnameManager *nm_hostname_manager_get (void); + +const char *nm_hostname_manager_get_hostname (NMHostnameManager *self); + +char *nm_hostname_manager_read_hostname (NMHostnameManager *self); + +gboolean nm_hostname_manager_write_hostname (NMHostnameManager *self, const char *hostname); + +void nm_hostname_manager_set_transient_hostname (NMHostnameManager *self, + const char *hostname, + NMHostnameManagerSetHostnameCb cb, + gpointer user_data); + +gboolean nm_hostname_manager_get_transient_hostname (NMHostnameManager *self, + char **hostname); + +gboolean nm_hostname_manager_validate_hostname (const char *hostname); + +#endif /* __NM_HOSTNAME_MANAGER_H__ */ diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index f8df2b9a..1493ef19 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -31,6 +31,7 @@ #include <sys/resource.h> #include <sys/stat.h> #include <signal.h> +#include <linux/rtnetlink.h> #include "main-utils.h" #include "NetworkManagerUtils.h" @@ -42,7 +43,6 @@ #include "nm-utils.h" #include "nm-setting-ip6-config.h" #include "systemd/nm-sd.h" -#include "nm-route-manager.h" #if !defined(NM_DIST_VERSION) # define NM_DIST_VERSION VERSION @@ -98,12 +98,6 @@ static struct { /*****************************************************************************/ -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - static void dhcp4_state_changed (NMDhcpClient *client, NMDhcpState state, @@ -114,6 +108,7 @@ dhcp4_state_changed (NMDhcpClient *client, { static NMIP4Config *last_config = NULL; NMIP4Config *existing; + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config)); @@ -122,17 +117,31 @@ dhcp4_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: g_assert (ip4_config); - existing = nm_ip4_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE); - if (last_config) - nm_ip4_config_subtract (existing, last_config); + g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex); - nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip4_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE, global_opt.priority_v4)) + existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), + NM_PLATFORM_GET, gl.ifindex, FALSE); + if (last_config) + nm_ip4_config_subtract (existing, last_config, 0); + + nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); + nm_ip4_config_add_dependent_routes (existing, + RT_TABLE_MAIN, + global_opt.priority_v4, + &ip4_dev_route_blacklist); + if (!nm_ip4_config_commit (existing, + NM_PLATFORM_GET, + NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN)) _LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config"); + nm_platform_ip4_dev_route_blacklist_set (NM_PLATFORM_GET, + gl.ifindex, + ip4_dev_route_blacklist); + if (last_config) g_object_unref (last_config); - last_config = nm_ip4_config_new (nm_dhcp_client_get_ifindex (client)); + last_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_dhcp_client_get_ifindex (client)); nm_ip4_config_replace (last_config, ip4_config, NULL); break; case NM_DHCP_STATE_TIMEOUT: @@ -155,86 +164,53 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in NMNDiscConfigMap changed = changed_int; static NMIP6Config *ndisc_config = NULL; NMIP6Config *existing; - int system_support; - guint32 ifa_flags = 0x00; - int i; - - /* - * Check, whether kernel is recent enough, to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. - * The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - - if (system_support) - ifa_flags = IFA_F_NOPREFIXROUTE; - if (global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - || global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - { - /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ - ifa_flags |= IFA_F_MANAGETEMPADDR; - } - existing = nm_ip6_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); + existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), + NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); if (ndisc_config) - nm_ip6_config_subtract (existing, ndisc_config); - else - ndisc_config = nm_ip6_config_new (gl.ifindex); - - if (changed & NM_NDISC_CONFIG_GATEWAYS) { - /* Use the first gateway as ordered in neighbor discovery cache. */ - if (rdata->gateways_n) - nm_ip6_config_set_gateway (ndisc_config, &rdata->gateways[0].address); - else - nm_ip6_config_set_gateway (ndisc_config, NULL); + nm_ip6_config_subtract (existing, ndisc_config, 0); + else { + ndisc_config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + gl.ifindex); } if (changed & NM_NDISC_CONFIG_ADDRESSES) { - /* Rebuild address list from neighbor discovery cache. */ - nm_ip6_config_reset_addresses (ndisc_config); - - /* ndisc->addresses contains at most max_addresses entries. - * This is different from what the kernel does, which - * also counts static and temporary addresses when checking - * max_addresses. - **/ - for (i = 0; i < rdata->addresses_n; i++) { - const NMNDiscAddress *discovered_address = &rdata->addresses[i]; - NMPlatformIP6Address address; - - memset (&address, 0, sizeof (address)); - address.address = discovered_address->address; - address.plen = system_support ? 64 : 128; - address.timestamp = discovered_address->timestamp; - address.lifetime = discovered_address->lifetime; - address.preferred = discovered_address->preferred; - if (address.preferred > address.lifetime) - address.preferred = address.lifetime; - address.addr_source = NM_IP_CONFIG_SOURCE_NDISC; - address.n_ifa_flags = ifa_flags; - - nm_ip6_config_add_address (ndisc_config, &address); - } + guint8 plen; + guint32 ifa_flags; + + /* Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. */ + ifa_flags = 0; + if (nm_platform_check_kernel_support (NM_PLATFORM_GET, + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + if (NM_IN_SET (global_opt.tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; + plen = 64; + } else + plen = 128; + + nm_ip6_config_reset_addresses_ndisc (ndisc_config, + rdata->addresses, + rdata->addresses_n, + plen, + ifa_flags); } - if (changed & NM_NDISC_CONFIG_ROUTES) { - /* Rebuild route list from neighbor discovery cache. */ - nm_ip6_config_reset_routes (ndisc_config); - - for (i = 0; i < rdata->routes_n; i++) { - const NMNDiscRoute *discovered_route = &rdata->routes[i]; - const NMPlatformIP6Route route = { - .network = discovered_route->network, - .plen = discovered_route->plen, - .gateway = discovered_route->gateway, - .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .metric = global_opt.priority_v6, - }; - - nm_ip6_config_add_route (ndisc_config, &route); - } + if (NM_FLAGS_ANY (changed, NM_NDISC_CONFIG_ROUTES + | NM_NDISC_CONFIG_GATEWAYS)) { + nm_ip6_config_reset_routes_ndisc (ndisc_config, + rdata->gateways, + rdata->gateways_n, + rdata->routes, + rdata->routes_n, + RT_TABLE_MAIN, + global_opt.priority_v6, + nm_platform_check_kernel_support (NM_PLATFORM_GET, + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); } if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { @@ -246,13 +222,20 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in if (changed & NM_NDISC_CONFIG_MTU) { char val[16]; + char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; g_snprintf (val, sizeof (val), "%d", rdata->mtu); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "mtu")), val); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "mtu")), val); } - nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE)) + nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); + nm_ip6_config_add_dependent_routes (existing, + RT_TABLE_MAIN, + global_opt.priority_v6); + if (!nm_ip6_config_commit (existing, + NM_PLATFORM_GET, + NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + NULL)) _LOGW (LOGD_IP6, "failed to apply IPv6 config"); } @@ -362,6 +345,7 @@ main (int argc, char *argv[]) gconstpointer tmp; gs_free NMUtilsIPv6IfaceId *iid = NULL; guint sd_id; + char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; nm_g_type_init (); @@ -466,19 +450,21 @@ main (int argc, char *argv[]) } if (global_opt.dhcp4_address) { - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries")), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, sysctl_path_buf, global_opt.ifname, "promote_secondaries")), "1"); dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), + nm_platform_get_multi_idx (NM_PLATFORM_GET), global_opt.ifname, gl.ifindex, hwaddr, global_opt.uuid, + RT_TABLE_MAIN, global_opt.priority_v4, !!global_opt.dhcp4_hostname, global_opt.dhcp4_hostname, global_opt.dhcp4_fqdn, global_opt.dhcp4_clientid, - 45, + NM_DHCP_TIMEOUT_DEFAULT, NULL, global_opt.dhcp4_address); g_assert (dhcp4_client); @@ -513,10 +499,10 @@ main (int argc, char *argv[]) if (iid) nm_ndisc_set_iid (ndisc, *iid); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra")), "1"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr")), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo")), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref")), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra")), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_defrtr")), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_pinfo")), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_rtr_pref")), "0"); g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, @@ -551,7 +537,7 @@ main (int argc, char *argv[]) /*****************************************************************************/ -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[3] = { +const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { &_nm_dhcp_client_factory_internal, }; diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index ae6af9c3..e2f99e24 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -25,12 +25,16 @@ #include <string.h> #include <arpa/inet.h> +#include <resolv.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "nm-utils.h" +#include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "introspection/org.freedesktop.NetworkManager.IP4Config.h" @@ -43,7 +47,232 @@ G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF); /*****************************************************************************/ +static gboolean +_route_valid (const NMPlatformIP4Route *r) +{ + return r + && r->plen <= 32 + && r->network == nm_utils_ip4_address_clear_host_address (r->network, r->plen); +} + +/*****************************************************************************/ + +static void +_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) +{ + nmp_object_id_hash_update ((NMPObject *) obj, h); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return nmp_object_id_equal ((NMPObject *) obj_a, (NMPObject *) obj_b); +} + +void +nm_ip_config_dedup_multi_idx_type_init (NMIPConfigDedupMultiIdxType *idx_type, + NMPObjectType obj_type) +{ + static const NMDedupMultiIdxTypeClass idx_type_class = { + .idx_obj_id_hash_update = _idx_obj_id_hash_update, + .idx_obj_id_equal = _idx_obj_id_equal, + }; + + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &idx_type_class); + idx_type->obj_type = obj_type; +} + +/*****************************************************************************/ + +gboolean +_nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, + NMIPConfigDedupMultiIdxType *idx_type, + int ifindex, + const NMPObject *obj_new, + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force, + const NMPObject **out_obj_old /* returns a reference! */, + const NMPObject **out_obj_new /* does not return a reference */) +{ + NMPObject obj_new_stackinit; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (ifindex > 0); + + /* we go through extra lengths to accept a full obj_new object. That one, + * can be reused by increasing the ref-count. */ + if (!obj_new) { + nm_assert (pl_new); + obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new); + obj_new_stackinit.object.ifindex = ifindex; + } else { + nm_assert (!pl_new); + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + if (obj_new->object.ifindex != ifindex) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.object.ifindex = ifindex; + } + } + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + nm_assert (nmp_object_is_alive (obj_new)); + + entry_old = nm_dedup_multi_index_lookup_obj (multi_idx, &idx_type->parent, obj_new); + + if (entry_old) { + gboolean modified = FALSE; + const NMPObject *obj_old = entry_old->obj; + + if (nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + + /* if @merge, we merge the new object with the existing one. + * Otherwise, we replace it entirely. */ + if (merge) { + switch (idx_type->obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + /* for addresses that we read from the kernel, we keep the timestamps as defined + * by the previous source (item_old). The reason is, that the other source configured the lifetimes + * with "what should be" and the kernel values are "what turned out after configuring it". + * + * For other sources, the longer lifetime wins. */ + if ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL + && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) + || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp; + obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime; + obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred; + modified = TRUE; + } + + /* keep the maximum addr_source. */ + if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; + modified = TRUE; + } + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + /* keep the maximum rt_source. */ + if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; + modified = TRUE; + } + break; + default: + nm_assert_not_reached (); + break; + } + + if ( modified + && nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + } + } + + if (!nm_dedup_multi_index_add_full (multi_idx, + &idx_type->parent, + obj_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + out_obj_old)) { + nm_assert_not_reached (); + NM_SET_OUT (out_obj_new, NULL); + return FALSE; + } + + NM_SET_OUT (out_obj_new, entry_new->obj); + return TRUE; + +append_force_and_out: + NM_SET_OUT (out_obj_old, nmp_object_ref (entry_old->obj)); + NM_SET_OUT (out_obj_new, entry_old->obj); + if (append_force) { + if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE)) + return TRUE; + } + return FALSE; +} + +/** + * _nm_ip_config_lookup_ip_route: + * @multi_idx: + * @idx_type: + * @needle: + * @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only + * return the result, if it compares equal to @needle according to this @cmp_type. + * Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing + * that compare-type means not to filter any further. + * + * Returns: the found entry or %NULL. + */ +const NMDedupMultiEntry * +_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + if (!entry) + return NULL; + + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_assert ( ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE + && nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0) + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE + && nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), NMP_OBJECT_CAST_IP6_ROUTE (needle), cmp_type) == 0)); + } else { + if (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP4_ROUTE (needle), + cmp_type) != 0) + return NULL; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP6_ROUTE (needle), + cmp_type) != 0) + return NULL; + } + } + return entry; +} + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, + PROP_MULTI_IDX, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, @@ -59,18 +288,11 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, ); typedef struct { - bool never_default:1; bool metered:1; - bool has_gateway:1; - guint32 gateway; - guint32 mss; guint32 mtu; int ifindex; NMIPConfigSource mtu_source; gint dns_priority; - gint64 route_metric; - GArray *addresses; - GArray *routes; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; @@ -80,6 +302,18 @@ typedef struct { GArray *wins; GVariant *address_data_variant; GVariant *addresses_variant; + GVariant *route_data_variant; + GVariant *routes_variant; + NMDedupMultiIndex *multi_idx; + const NMPObject *best_default_route; + union { + NMIPConfigDedupMultiIdxType idx_ip4_addresses_; + NMDedupMultiIdxType idx_ip4_addresses; + }; + union { + NMIPConfigDedupMultiIdxType idx_ip4_routes_; + NMDedupMultiIdxType idx_ip4_routes; + }; } NMIP4ConfigPrivate; struct _NMIP4Config { @@ -97,10 +331,24 @@ G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) /*****************************************************************************/ +static void _add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new); +static void _add_route (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Route *new, const NMPObject **out_obj_new); +static const NMDedupMultiEntry *_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); + +/*****************************************************************************/ + int -nm_ip4_config_get_ifindex (const NMIP4Config *config) +nm_ip4_config_get_ifindex (const NMIP4Config *self) +{ + return NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex; +} + +NMDedupMultiIndex * +nm_ip4_config_get_multi_idx (const NMIP4Config *self) { - return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex; + return NM_IP4_CONFIG_GET_PRIVATE (self)->multi_idx; } /*****************************************************************************/ @@ -114,83 +362,167 @@ _ipv4_is_zeronet (in_addr_t network) /*****************************************************************************/ -/** - * nm_ip4_config_capture_resolv_conf(): - * @nameservers: array of guint32 - * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf - * - * Reads all resolv.conf IPv4 nameservers and adds them to @nameservers. - * - * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged - */ -gboolean -nm_ip4_config_capture_resolv_conf (GArray *nameservers, - GPtrArray *dns_options, - const char *rc_contents) +const NMDedupMultiHeadEntry * +nm_ip4_config_lookup_addresses (const NMIP4Config *self) { - GPtrArray *read_ns, *read_options; - guint i, j; - gboolean changed = FALSE; + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - g_return_val_if_fail (nameservers != NULL, FALSE); + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip4_addresses, + NULL); +} - read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents); - if (!read_ns) - return FALSE; +void +nm_ip_config_iter_ip4_address_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip4_config_lookup_addresses (self)); +} - for (i = 0; i < read_ns->len; i++) { - const char *s = g_ptr_array_index (read_ns, i); - guint32 ns = 0; +/*****************************************************************************/ - if (!inet_pton (AF_INET, s, (void *) &ns) || !ns) - continue; +const NMDedupMultiHeadEntry * +nm_ip4_config_lookup_routes (const NMIP4Config *self) +{ + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - /* Ignore duplicates */ - for (j = 0; j < nameservers->len; j++) { - if (g_array_index (nameservers, guint32, j) == ns) - break; - } + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip4_routes, + NULL); +} - if (j == nameservers->len) { - g_array_append_val (nameservers, ns); - changed = TRUE; +void +nm_ip_config_iter_ip4_route_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip4_config_lookup_routes (self)); +} + +/*****************************************************************************/ + +const NMPObject * +_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp) +{ + int addr_family; + int c; + guint metric_cur, metric_cmp; + + nm_assert ( !obj_cur + || NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cur), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert ( !obj_cmp + || ( !obj_cur + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) + || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp)); + nm_assert ( !obj_cur + || nm_ip_config_best_default_route_is (obj_cur)); + + /* assumes that @obj_cur is already the best default route (or NULL). It checks whether + * @obj_cmp is also a default route and returns the best of both. */ + if ( obj_cmp + && nm_ip_config_best_default_route_is (obj_cmp)) { + if (!obj_cur) + return obj_cmp; + + addr_family = NMP_OBJECT_GET_CLASS (obj_cmp)->addr_family; + metric_cur = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cur)->metric); + metric_cmp = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cmp)->metric); + + if (metric_cmp < metric_cur) + return obj_cmp; + + if (metric_cmp == metric_cur) { + /* Routes have the same metric. We still want to deterministically + * prefer one or the other. It's important to consistently choose one + * or the other, so that the order doesn't matter how routes are added + * (and merged). */ + c = nmp_object_cmp (obj_cur, obj_cmp); + if (c != 0) + return c < 0 ? obj_cur : obj_cmp; + + /* as last resort, compare pointers. */ + if (obj_cmp < obj_cur) + return obj_cmp; } } - g_ptr_array_unref (read_ns); + return obj_cur; +} - if (dns_options) { - read_options = nm_utils_read_resolv_conf_dns_options (rc_contents); - if (!read_options) - return changed; +gboolean +_nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + if (new_candidate == *best_default_route) + return FALSE; + nmp_object_ref (new_candidate); + nm_clear_nmp_object (best_default_route); + *best_default_route = new_candidate; + return TRUE; +} - for (i = 0; i < read_options->len; i++) { - const char *s = g_ptr_array_index (read_options, i); +gboolean +_nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + new_candidate = _nm_ip_config_best_default_route_find_better (*best_default_route, + new_candidate); + return _nm_ip_config_best_default_route_set (best_default_route, new_candidate); +} - if (_nm_utils_dns_option_validate (s, NULL, NULL, FALSE, _nm_utils_dns_option_descs) && - _nm_utils_dns_option_find_idx (dns_options, s) < 0) { - g_ptr_array_add (dns_options, g_strdup (s)); - changed = TRUE; - } - } - g_ptr_array_unref (read_options); +const NMPObject * +nm_ip4_config_best_default_route_get (const NMIP4Config *self) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + return NM_IP4_CONFIG_GET_PRIVATE (self)->best_default_route; +} + +const NMPObject * +_nm_ip4_config_best_default_route_find (const NMIP4Config *self) +{ + NMDedupMultiIter ipconf_iter; + const NMPObject *new_best_default_route = NULL; + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, NULL) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, + ipconf_iter.current->obj); } + return new_best_default_route; +} + +in_addr_t +nmtst_ip4_config_get_gateway (NMIP4Config *config) +{ + const NMPObject *rt; - return changed; + g_assert (NM_IS_IP4_CONFIG (config)); + + rt = nm_ip4_config_best_default_route_get (config); + if (!rt) + return 0; + return NMP_OBJECT_CAST_IP4_ROUTE (rt)->gateway; } -static gboolean -addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +/*****************************************************************************/ + +static void +_notify_addresses (NMIP4Config *self) { - return a->address == b->address - && a->plen == b->plen - && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0; + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nm_clear_g_variant (&priv->address_data_variant); + nm_clear_g_variant (&priv->addresses_variant); + _notify (self, PROP_ADDRESS_DATA); + _notify (self, PROP_ADDRESSES); } -static gboolean -routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_gateway_and_metric) +static void +_notify_routes (NMIP4Config *self) { - return a->network == b->network && a->plen == b->plen && - (!consider_gateway_and_metric || (a->gateway == b->gateway && a->metric == b->metric)); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nm_assert (priv->best_default_route == _nm_ip4_config_best_default_route_find (self)); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + _notify (self, PROP_ROUTE_DATA); + _notify (self, PROP_ROUTES); } /*****************************************************************************/ @@ -203,11 +535,12 @@ _addresses_sort_cmp_get_prio (in_addr_t addr) return 1; } -static gint -_addresses_sort_cmp (gconstpointer a, gconstpointer b) +static int +_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) { gint p1, p2; - const NMPlatformIP4Address *a1 = a, *a2 = b; + const NMPlatformIP4Address *a1 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) a)); + const NMPlatformIP4Address *a2 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) b)); guint32 n1, n2; /* Sort by address type. For example link local will @@ -229,29 +562,19 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b) * subnet (and thus also the primary/secondary role) is * preserved. */ - n1 = a1->address & nm_utils_ip4_prefix_to_netmask (a1->plen); - n2 = a2->address & nm_utils_ip4_prefix_to_netmask (a2->plen); + n1 = a1->address & _nm_utils_ip4_prefix_to_netmask (a1->plen); + n2 = a2->address & _nm_utils_ip4_prefix_to_netmask (a2->plen); return memcmp (&n1, &n2, sizeof (guint32)); } /*****************************************************************************/ -static void -notify_addresses (NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - - nm_clear_g_variant (&priv->address_data_variant); - nm_clear_g_variant (&priv->addresses_variant); - _notify (self, PROP_ADDRESS_DATA); - _notify (self, PROP_ADDRESSES); -} - -static gint -sort_captured_addresses (gconstpointer a, gconstpointer b) +static int +sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer user_data) { - const NMPlatformIP4Address *addr_a = a, *addr_b = b; + const NMPlatformIP4Address *addr_a = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_a, NMDedupMultiEntry, lst_entries)->obj); + const NMPlatformIP4Address *addr_b = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_b, NMDedupMultiEntry, lst_entries)->obj); /* Primary addresses first */ return NM_FLAGS_HAS (addr_a->n_ifa_flags, IFA_F_SECONDARY) - @@ -259,175 +582,237 @@ sort_captured_addresses (gconstpointer a, gconstpointer b) } NMIP4Config * -nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) +nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) { - NMIP4Config *config; + NMIP4Config *self; NMIP4ConfigPrivate *priv; - guint i; - guint32 lowest_metric = G_MAXUINT32; - guint32 old_gateway = 0; - gboolean old_has_gateway = FALSE; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; + gboolean has_addresses = FALSE; + + nm_assert (ifindex > 0); /* Slaves have no IP configuration */ if (nm_platform_link_get_master (platform, ifindex) > 0) return NULL; - config = nm_ip4_config_new (ifindex); - priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - g_array_unref (priv->addresses); - g_array_unref (priv->routes); - - priv->addresses = nm_platform_ip4_address_get_all (platform, ifindex); - g_array_sort (priv->addresses, sort_captured_addresses); - - priv->routes = nm_platform_ip4_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - - /* Extract gateway from default route */ - old_gateway = priv->gateway; - old_has_gateway = priv->has_gateway; - for (i = 0; i < priv->routes->len; ) { - const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (route->metric < lowest_metric) { - priv->gateway = route->gateway; - lowest_metric = route->metric; - } - priv->has_gateway = TRUE; - /* Remove the default route from the list */ - g_array_remove_index_fast (priv->routes, i); - continue; + self = nm_ip4_config_new (multi_idx, ifindex); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (head_entry) { + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + if (!_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_addresses_, + ifindex, + plobj, + NULL, + FALSE, + TRUE, + NULL, + NULL)) + nm_assert_not_reached (); } - i++; + head_entry = nm_ip4_config_lookup_addresses (self); + nm_assert (head_entry); + nm_dedup_multi_head_entry_sort (head_entry, + sort_captured_addresses, + NULL); + has_addresses = TRUE; + _notify_addresses (self); } - /* we detect the route metric based on the default route. All non-default - * routes have their route metrics explicitly set. */ - priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1; + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex); - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - if (priv->has_gateway) { - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if ( (route->plen == 32) - && (route->network == priv->gateway) - && (route->gateway == 0)) { - g_array_remove_index (priv->routes, i); - i--; - } - } - } + /* Extract gateway from default route */ + nmp_cache_iter_for_each (&iter, head_entry, &plobj) + _add_route (self, plobj, NULL, NULL); /* If the interface has the default route, and has IPv4 addresses, capture * nameservers from /etc/resolv.conf. */ - if (priv->addresses->len && priv->has_gateway && capture_resolv_conf) { - if (nm_ip4_config_capture_resolv_conf (priv->nameservers, priv->dns_options, NULL)) - _notify (config, PROP_NAMESERVERS); + if ( has_addresses + && priv->best_default_route + && capture_resolv_conf) { + gs_free char *rc_contents = NULL; + + if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) { + if (nm_utils_resolve_conf_parse (AF_INET, + rc_contents, + priv->nameservers, + priv->dns_options)) + _notify (self, PROP_NAMESERVERS); + } } - /* actually, nobody should be connected to the signal, just to be sure, notify */ - _notify (config, PROP_ADDRESS_DATA); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ADDRESSES); - _notify (config, PROP_ROUTES); - if ( priv->gateway != old_gateway - || priv->has_gateway != old_has_gateway) - _notify (config, PROP_GATEWAY); - - return config; + return self; } -gboolean -nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - gs_unref_ptrarray GPtrArray *added_addresses = NULL; +void +nm_ip4_config_add_dependent_routes (NMIP4Config *self, + guint32 route_table, + guint32 route_metric, + GPtrArray **out_ip4_dev_route_blacklist) +{ + const NMIP4ConfigPrivate *priv; + GPtrArray *ip4_dev_route_blacklist = NULL; + const NMPlatformIP4Address *my_addr; + const NMPlatformIP4Route *my_route; + int ifindex; + NMDedupMultiIter iter; - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (config != NULL, FALSE); + g_return_if_fail (NM_IS_IP4_CONFIG (self)); - /* Addresses */ - nm_platform_ip4_address_sync (platform, ifindex, priv->addresses, - default_route_metric >= 0 ? &added_addresses : NULL); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - /* Routes */ - { - guint i; - guint count = nm_ip4_config_get_num_routes (config); - GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count); - gboolean success; - gs_unref_array GArray *device_route_purge_list = NULL; - - if ( default_route_metric >= 0 - && added_addresses) { - /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. - * As we don't do that for IPv4, add it here shortly before syncing - * the routes. For NMRouteManager these routes are very much important. */ - for (i = 0; i < added_addresses->len; i++) { - const NMPlatformIP4Address *addr = added_addresses->pdata[i]; - NMPlatformIP4Route route = { 0 }; - - if (addr->plen == 0) - continue; + ifindex = nm_ip4_config_get_ifindex (self); + g_return_if_fail (ifindex > 0); - nm_assert (addr->plen <= 32); + /* For IPv6 slaac, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4 (and manual IPv6 addresses), add them explicitly. */ - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &my_addr) { + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP4Route *route; + in_addr_t network; - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + if (my_addr->plen == 0) + continue; - if (_ipv4_is_zeronet (route.network)) { - /* Kernel doesn't add device-routes for destinations that - * start with 0.x.y.z. Skip them. */ - continue; - } + nm_assert (my_addr->plen <= 32); - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; + /* The destination network depends on the peer-address. */ + network = nm_utils_ip4_address_clear_host_address (my_addr->peer_address, my_addr->plen); - g_array_append_val (routes, route); + if (_ipv4_is_zeronet (network)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } - if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - if (!device_route_purge_list) - device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - g_array_append_val (device_route_purge_list, route); - } + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); + + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->network = network; + route->plen = my_addr->plen; + route->pref_src = my_addr->address; + route->table_coerced = nm_platform_route_table_coerce (route_table); + route->metric = route_metric; + route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK); + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + } else + _add_route (self, r, NULL, NULL); + + if ( out_ip4_dev_route_blacklist + && ( route_table != RT_TABLE_MAIN + || route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) { + nm_auto_nmpobj NMPObject *r_dev = NULL; + + r_dev = nmp_object_clone (r, FALSE); + route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev); + route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN); + route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r_dev, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we track such a route explicitly. Don't blacklist it. */ + } else { + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + g_steal_pointer (&r_dev)); } } + } - for (i = 0; i < count; i++) { - const NMPlatformIP4Route *route; +again: + nm_ip_config_iter_ip4_route_for_each (&iter, self, &my_route) { + NMPlatformIP4Route rt; - route = nm_ip4_config_get_route (config, i); - /* duplicates in @routes are no problem as route-manager handles them - * gracefully (by ignoring them). */ - g_array_append_vals (routes, route, 1); - } - - nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list); + if ( !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route) + || my_route->gateway == 0 + || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rt_source) + || nm_ip4_config_get_direct_route_for_host (self, + my_route->gateway, + nm_platform_route_table_uncoerce (my_route->table_coerced, TRUE))) + continue; - success = nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, default_route_metric < 0, routes_full_sync); - g_array_unref (routes); - if (!success) - return FALSE; + rt = *my_route; + rt.network = my_route->gateway; + rt.plen = 32; + rt.gateway = 0; + _add_route (self, NULL, &rt, NULL); + /* adding the route might have invalidated the iteration. Start again. */ + goto again; } - return TRUE; + NM_SET_OUT (out_ip4_dev_route_blacklist, ip4_dev_route_blacklist); +} + +gboolean +nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + NMIPRouteTableSyncMode route_table_sync) +{ + gs_unref_ptrarray GPtrArray *addresses = NULL; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + int ifindex; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + ifindex = nm_ip4_config_get_ifindex (self); + g_return_val_if_fail (ifindex > 0, FALSE); + + addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_addresses (self), + NULL, NULL); + + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_routes (self), + NULL, NULL); + + routes_prune = nm_platform_ip_route_get_prune_list (platform, + AF_INET, + ifindex, + route_table_sync); + + nm_platform_ip4_address_sync (platform, ifindex, addresses); + + if (!nm_platform_ip_route_sync (platform, + AF_INET, + ifindex, + routes, + routes_prune, + NULL)) + success = FALSE; + + return success; } static void -merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) +merge_route_attributes (NMIPRoute *s_route, + NMPlatformIP4Route *r, + guint32 route_table) { GVariant *variant; + guint32 u32; in_addr_t addr; #define GET_ATTR(name, field, variant_type, type) \ @@ -435,6 +820,12 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ r->field = g_variant_get_ ## type (variant); + variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TABLE); + u32 = variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32) + ? g_variant_get_uint32 (variant) + : 0; + r->table_coerced = nm_platform_route_table_coerce (u32 ?: (route_table ?: RT_TABLE_MAIN)); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, tos, BYTE, byte); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, window, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, cwnd, UINT32, uint32); @@ -457,20 +848,25 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) } void -nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) +nm_ip4_config_merge_setting (NMIP4Config *self, + NMSettingIPConfig *setting, + guint32 route_table, + guint32 route_metric) { NMIP4ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; int i, priority; + const char *gateway_str; + guint32 gateway_bin; if (!setting) return; g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); - priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - g_object_freeze_notify (G_OBJECT (config)); + g_object_freeze_notify (G_OBJECT (self)); naddresses = nm_setting_ip_config_get_num_addresses (setting); nroutes = nm_setting_ip_config_get_num_routes (setting); @@ -478,20 +874,20 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu nsearches = nm_setting_ip_config_get_num_dns_searches (setting); /* Gateway */ - if (nm_setting_ip_config_get_never_default (setting)) - nm_ip4_config_set_never_default (config, TRUE); - else if (nm_setting_ip_config_get_ignore_auto_routes (setting)) - nm_ip4_config_set_never_default (config, FALSE); - if (nm_setting_ip_config_get_gateway (setting)) { - guint32 gateway; + if ( !nm_setting_ip_config_get_never_default (setting) + && (gateway_str = nm_setting_ip_config_get_gateway (setting)) + && inet_pton (AF_INET, gateway_str, &gateway_bin) == 1 + && gateway_bin) { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .gateway = gateway_bin, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; - inet_pton (AF_INET, nm_setting_ip_config_get_gateway (setting), &gateway); - nm_ip4_config_set_gateway (config, gateway); + _add_route (self, NULL, &r, NULL); } - if (priv->route_metric == -1) - priv->route_metric = nm_setting_ip_config_get_route_metric (setting); - /* Addresses */ for (i = 0; i < naddresses; i++) { NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i); @@ -511,16 +907,21 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu if (label) g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label)); - nm_ip4_config_add_address (config, &address); + _add_address (self, NULL, &address); } /* Routes */ if (nm_setting_ip_config_get_ignore_auto_routes (setting)) - nm_ip4_config_reset_routes (config); + nm_ip4_config_reset_routes (self); for (i = 0; i < nroutes; i++) { NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP4Route route; + if (nm_ip_route_get_family (s_route) != AF_INET) { + nm_assert_not_reached (); + continue; + } + memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); @@ -531,73 +932,74 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu nm_ip_route_get_next_hop_binary (s_route, &route.gateway); if (nm_ip_route_get_metric (s_route) == -1) - route.metric = default_route_metric; + route.metric = route_metric; else route.metric = nm_ip_route_get_metric (s_route); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - merge_route_attributes (s_route, &route); - nm_ip4_config_add_route (config, &route); + route.network = nm_utils_ip4_address_clear_host_address (route.network, route.plen); + + merge_route_attributes (s_route, &route, route_table); + _add_route (self, NULL, &route, NULL); } /* DNS */ if (nm_setting_ip_config_get_ignore_auto_dns (setting)) { - nm_ip4_config_reset_nameservers (config); - nm_ip4_config_reset_domains (config); - nm_ip4_config_reset_searches (config); + nm_ip4_config_reset_nameservers (self); + nm_ip4_config_reset_domains (self); + nm_ip4_config_reset_searches (self); } for (i = 0; i < nnameservers; i++) { guint32 ip; if (inet_pton (AF_INET, nm_setting_ip_config_get_dns (setting, i), &ip) == 1) - nm_ip4_config_add_nameserver (config, ip); + nm_ip4_config_add_nameserver (self, ip); } for (i = 0; i < nsearches; i++) - nm_ip4_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i)); + nm_ip4_config_add_search (self, nm_setting_ip_config_get_dns_search (setting, i)); i = 0; while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) { - nm_ip4_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i)); + nm_ip4_config_add_dns_option (self, nm_setting_ip_config_get_dns_option (setting, i)); i++; } priority = nm_setting_ip_config_get_dns_priority (setting); if (priority) - nm_ip4_config_set_dns_priority (config, priority); + nm_ip4_config_set_dns_priority (self, priority); - g_object_thaw_notify (G_OBJECT (config)); + g_object_thaw_notify (G_OBJECT (self)); } NMSetting * -nm_ip4_config_create_setting (const NMIP4Config *config) +nm_ip4_config_create_setting (const NMIP4Config *self) { + const NMIP4ConfigPrivate *priv; NMSettingIPConfig *s_ip4; - guint32 gateway; - guint naddresses, nroutes, nnameservers, nsearches, noptions; + guint nnameservers, nsearches, noptions; const char *method = NULL; int i; - gint64 route_metric; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - if (!config) { + if (!self) { g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); return NM_SETTING (s_ip4); } - gateway = nm_ip4_config_get_gateway (config); - naddresses = nm_ip4_config_get_num_addresses (config); - nroutes = nm_ip4_config_get_num_routes (config); - nnameservers = nm_ip4_config_get_num_nameservers (config); - nsearches = nm_ip4_config_get_num_searches (config); - noptions = nm_ip4_config_get_num_dns_options (config); - route_metric = nm_ip4_config_get_route_metric (config); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nnameservers = nm_ip4_config_get_num_nameservers (self); + nsearches = nm_ip4_config_get_num_searches (self); + noptions = nm_ip4_config_get_num_dns_options (self); /* Addresses */ - for (i = 0; i < naddresses; i++) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { NMIPAddress *s_addr; /* Detect dynamic address */ @@ -619,10 +1021,12 @@ nm_ip4_config_create_setting (const NMIP4Config *config) } /* Gateway */ - if ( nm_ip4_config_has_gateway (config) + if ( priv->best_default_route && nm_setting_ip_config_get_num_addresses (s_ip4) > 0) { g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (gateway, NULL), + NM_SETTING_IP_CONFIG_GATEWAY, + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, + NULL), NULL); } @@ -632,16 +1036,13 @@ nm_ip4_config_create_setting (const NMIP4Config *config) g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, - NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric, NULL); /* Routes */ - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { NMIPRoute *s_route; - /* Ignore default route. */ - if (!route->plen) + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; /* Ignore routes provided by external sources */ @@ -658,25 +1059,25 @@ nm_ip4_config_create_setting (const NMIP4Config *config) /* DNS */ for (i = 0; i < nnameservers; i++) { - guint32 nameserver = nm_ip4_config_get_nameserver (config, i); + guint32 nameserver = nm_ip4_config_get_nameserver (self, i); nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, NULL)); } for (i = 0; i < nsearches; i++) { - const char *search = nm_ip4_config_get_search (config, i); + const char *search = nm_ip4_config_get_search (self, i); nm_setting_ip_config_add_dns_search (s_ip4, search); } for (i = 0; i < noptions; i++) { - const char *option = nm_ip4_config_get_dns_option (config, i); + const char *option = nm_ip4_config_get_dns_option (self, i); nm_setting_ip_config_add_dns_option (s_ip4, option); } g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DNS_PRIORITY, - nm_ip4_config_get_dns_priority (config), + nm_ip4_config_get_dns_priority (self), NULL); return NM_SETTING (s_ip4); @@ -685,11 +1086,16 @@ nm_ip4_config_create_setting (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags) +nm_ip4_config_merge (NMIP4Config *dst, + const NMIP4Config *src, + NMIPConfigMergeFlags merge_flags, + guint32 default_route_metric_penalty) { NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; guint32 i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address = NULL; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -700,8 +1106,8 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) - nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i)); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address) + _add_address (dst, NMP_OBJECT_UP_CAST (address), NULL); /* nameservers */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -709,20 +1115,25 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i)); } - /* default gateway */ - if (nm_ip4_config_has_gateway (src)) - nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src)); - /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) - nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); - } + const NMPlatformIP4Route *r_src; - if (dst_priv->route_metric == -1) - dst_priv->route_metric = src_priv->route_metric; - else if (src_priv->route_metric != -1) - dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r_src) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r_src)) { + if (NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES)) + continue; + if (default_route_metric_penalty) { + NMPlatformIP4Route r = *r_src; + + r.metric = nm_utils_ip_route_metric_penalize (AF_INET, r.metric, default_route_metric_penalty); + _add_route (dst, NULL, &r, NULL); + continue; + } + } + _add_route (dst, ipconf_iter.current->obj, NULL, NULL); + } + } /* domains */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -742,10 +1153,6 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl nm_ip4_config_add_dns_option (dst, nm_ip4_config_get_dns_option (src, i)); } - /* MSS */ - if (nm_ip4_config_get_mss (src)) - nm_ip4_config_set_mss (dst, nm_ip4_config_get_mss (src)); - /* MTU */ if ( src_priv->mtu_source > dst_priv->mtu_source || ( src_priv->mtu_source == dst_priv->mtu_source @@ -782,21 +1189,6 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl /*****************************************************************************/ static int -_addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->addresses->len; i++) { - const NMPlatformIP4Address *a = &g_array_index (priv->addresses, NMPlatformIP4Address, i); - - if (addresses_are_duplicate (addr, a)) - return (int) i; - } - return -1; -} - -static int _nameservers_get_index (const NMIP4Config *self, guint32 ns) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); @@ -812,22 +1204,6 @@ _nameservers_get_index (const NMIP4Config *self, guint32 ns) } static int -_routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP4Route *r = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if ( route->network == r->network - && route->plen == r->plen) - return (int) i; - } - return -1; -} - -static int _domains_get_index (const NMIP4Config *self, const char *domain) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); @@ -908,26 +1284,45 @@ _wins_get_index (const NMIP4Config *self, guint32 wins_server) * nm_ip4_config_subtract: * @dst: config from which to remove everything in @src * @src: config to remove from @dst + * @default_route_metric_penalty: pretend that on source we applied + * a route penalty on the default-route. It means, for default routes + * we don't remove routes that match exactly, but those with a lower + * metric (with the penalty removed). * * Removes everything in @src from @dst. */ void -nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) +nm_ip4_config_subtract (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty) { - guint32 i; + NMIP4ConfigPrivate *dst_priv; + guint i; gint idx; + const NMPlatformIP4Address *a; + const NMPlatformIP4Route *r; + NMDedupMultiIter ipconf_iter; + gboolean changed; + gboolean changed_default_route; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) { - idx = _addresses_get_index (dst, nm_ip4_config_get_address (src, i)); - if (idx >= 0) - nm_ip4_config_del_address (dst, idx); + changed = FALSE; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &a) { + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_addresses, + NMP_OBJECT_UP_CAST (a), + NULL)) + changed = TRUE; } + if (changed) + _notify_addresses (dst); /* nameservers */ for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) { @@ -936,22 +1331,46 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) nm_ip4_config_del_nameserver (dst, idx); } - /* default gateway */ - if ( (nm_ip4_config_has_gateway (src) == nm_ip4_config_has_gateway (dst)) - && (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst))) - nm_ip4_config_unset_gateway (dst); - - if (!nm_ip4_config_get_num_addresses (dst)) - nm_ip4_config_unset_gateway (dst); - - /* ignore route_metric */ - /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) { - idx = _routes_get_index (dst, nm_ip4_config_get_route (src, i)); - if (idx >= 0) - nm_ip4_config_del_route (dst, idx); + changed = FALSE; + changed_default_route = FALSE; + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { + const NMPObject *o_src = NMP_OBJECT_UP_CAST (r); + NMPObject o_lookup_copy; + const NMPObject *o_lookup; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP4Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When subtracting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_src); + rr = NMP_OBJECT_CAST_IP4_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_src; + + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_routes, + o_lookup, + (gconstpointer *) &obj_old)) { + if (dst_priv->best_default_route == obj_old) { + nm_clear_nmp_object (&dst_priv->best_default_route); + changed_default_route = TRUE; + } + changed = TRUE; + } + } + if (changed_default_route) { + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, + _nm_ip4_config_best_default_route_find (dst)); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) { @@ -974,10 +1393,6 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) nm_ip4_config_del_dns_option (dst, idx); } - /* MSS */ - if (nm_ip4_config_get_mss (src) == nm_ip4_config_get_mss (dst)) - nm_ip4_config_set_mss (dst, 0); - /* MTU */ if ( nm_ip4_config_get_mtu (src) == nm_ip4_config_get_mtu (dst) && nm_ip4_config_get_mtu_source (src) == nm_ip4_config_get_mtu_source (dst)) @@ -1008,43 +1423,83 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) } void -nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) +nm_ip4_config_intersect (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty) { - guint32 i; - gint idx; + NMIP4ConfigPrivate *dst_priv; + const NMIP4ConfigPrivate *src_priv; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *a; + const NMPlatformIP4Route *r; + const NMPObject *new_best_default_route; + gboolean changed; - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); + g_return_if_fail (src); + g_return_if_fail (dst); + + dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); + src_priv = NM_IP4_CONFIG_GET_PRIVATE (src); g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip4_config_get_num_addresses (dst); ) { - idx = _addresses_get_index (src, nm_ip4_config_get_address (dst, i)); - if (idx < 0) - nm_ip4_config_del_address (dst, i); - else - i++; + changed = FALSE; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, dst, &a) { + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip4_addresses, + NMP_OBJECT_UP_CAST (a))) + continue; + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; } + if (changed) + _notify_addresses (dst); - /* ignore route_metric */ /* ignore nameservers */ - /* default gateway */ - if ( !nm_ip4_config_get_num_addresses (dst) - || (nm_ip4_config_has_gateway (src) != nm_ip4_config_has_gateway (dst)) - || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) { - nm_ip4_config_unset_gateway (dst); - } - /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) { - idx = _routes_get_index (src, nm_ip4_config_get_route (dst, i)); - if (idx < 0) - nm_ip4_config_del_route (dst, i); - else - i++; + changed = FALSE; + new_best_default_route = NULL; + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) { + const NMPObject *o_dst = NMP_OBJECT_UP_CAST (r); + const NMPObject *o_lookup; + NMPObject o_lookup_copy; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP4Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When intersecting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_dst); + rr = NMP_OBJECT_CAST_IP4_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_dst; + + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip4_routes, + o_lookup)) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, o_dst); + continue; + } + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; + } + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) { + nm_assert (changed); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* ignore domains */ /* ignore dns searches */ @@ -1080,8 +1535,9 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev guint i, num; NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; - const NMPlatformIP4Address *dst_addr, *src_addr; - const NMPlatformIP4Route *dst_route, *src_route; + NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; + const NMDedupMultiHeadEntry *head_entry_src; + const NMPObject *new_best_default_route; g_return_val_if_fail (src != NULL, FALSE); g_return_val_if_fail (dst != NULL, FALSE); @@ -1102,72 +1558,106 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev has_minor_changes = TRUE; } - /* never_default */ - if (src_priv->never_default != dst_priv->never_default) { - dst_priv->never_default = src_priv->never_default; - has_minor_changes = TRUE; - } - - /* default gateway */ - if ( src_priv->gateway != dst_priv->gateway - || src_priv->has_gateway != dst_priv->has_gateway) { - if (src_priv->has_gateway) - nm_ip4_config_set_gateway (dst, src_priv->gateway); - else - nm_ip4_config_unset_gateway (dst); - has_relevant_changes = TRUE; - } - - if (src_priv->route_metric != dst_priv->route_metric) { - dst_priv->route_metric = src_priv->route_metric; - has_minor_changes = TRUE; - } - /* addresses */ - num = nm_ip4_config_get_num_addresses (src); - are_equal = num == nm_ip4_config_get_num_addresses (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip4_address_cmp (src_addr = nm_ip4_config_get_address (src, i), - dst_addr = nm_ip4_config_get_address (dst, i))) { - are_equal = FALSE; - if ( !addresses_are_duplicate (src_addr, dst_addr) - || src_addr->peer_address != dst_addr->peer_address) { - has_relevant_changes = TRUE; - break; - } + head_entry_src = nm_ip4_config_lookup_addresses (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip4_address_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP4Address *r_src = NULL; + const NMPlatformIP4Address *r_dst = NULL; + + has = nm_ip_config_iter_ip4_address_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip4_address_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip4_address_cmp (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( r_src->address != r_dst->address + || r_src->plen != r_dst->plen + || r_src->peer_address != r_dst->peer_address) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip4_config_reset_addresses (dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i)); has_minor_changes = TRUE; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_addresses); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_addresses_, + dst_priv->ifindex, + ipconf_iter_src.current->obj, + NULL, + FALSE, + TRUE, + NULL, + NULL); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_addresses, FALSE); + _notify_addresses (dst); } /* routes */ - num = nm_ip4_config_get_num_routes (src); - are_equal = num == nm_ip4_config_get_num_routes (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip4_route_cmp (src_route = nm_ip4_config_get_route (src, i), - dst_route = nm_ip4_config_get_route (dst, i))) { - are_equal = FALSE; - if (!routes_are_duplicate (src_route, dst_route, TRUE)) { - has_relevant_changes = TRUE; - break; - } + head_entry_src = nm_ip4_config_lookup_routes (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip4_route_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP4Route *r_src = NULL; + const NMPlatformIP4Route *r_dst = NULL; + + has = nm_ip_config_iter_ip4_route_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip4_route_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip4_route_cmp_full (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( r_src->plen != r_dst->plen + || !nm_utils_ip4_address_same_prefix (r_src->network, r_dst->network, r_src->plen) + || r_src->gateway != r_dst->gateway + || r_src->metric != r_dst->metric) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip4_config_reset_routes (dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); has_minor_changes = TRUE; + new_best_default_route = NULL; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + const NMPObject *o = ipconf_iter_src.current->obj; + const NMPObject *obj_new; + + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_routes_, + dst_priv->ifindex, + o, + NULL, + FALSE, + TRUE, + NULL, + &obj_new); + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, FALSE); + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + _notify (dst, PROP_GATEWAY); + _notify_routes (dst); } /* nameservers */ @@ -1251,12 +1741,6 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev has_minor_changes = TRUE; } - /* mss */ - if (src_priv->mss != dst_priv->mss) { - nm_ip4_config_set_mss (dst, src_priv->mss); - has_minor_changes = TRUE; - } - /* nis */ num = nm_ip4_config_get_num_nis_servers (src); are_equal = num == nm_ip4_config_get_num_nis_servers (dst); @@ -1327,386 +1811,344 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev } void -nm_ip4_config_dump (const NMIP4Config *config, const char *detail) +nm_ip4_config_dump (const NMIP4Config *self, const char *detail) { - guint32 i, tmp; + guint32 tmp; + guint i; const char *str; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - g_message ("--------- NMIP4Config %p (%s)", config, detail); + g_message ("--------- NMIP4Config %p (%s)", self, detail); - if (config == NULL) { + if (self == NULL) { g_message (" (null)"); return; } - str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config)); + str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); if (str) g_message (" path: %s", str); /* addresses */ - for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) - g_message (" a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i), NULL, 0)); - - /* default gateway */ - if (nm_ip4_config_has_gateway (config)) { - tmp = nm_ip4_config_get_gateway (config); - g_message (" gw: %s", nm_utils_inet4_ntop (tmp, NULL)); - } + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) + g_message (" a: %s", nm_platform_ip4_address_to_string (address, NULL, 0)); /* nameservers */ - for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) { - tmp = nm_ip4_config_get_nameserver (config, i); + for (i = 0; i < nm_ip4_config_get_num_nameservers (self); i++) { + tmp = nm_ip4_config_get_nameserver (self, i); g_message (" ns: %s", nm_utils_inet4_ntop (tmp, NULL)); } /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i), NULL, 0)); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) + g_message (" rt: %s", nm_platform_ip4_route_to_string (route, NULL, 0)); /* domains */ - for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) - g_message (" domain: %s", nm_ip4_config_get_domain (config, i)); + for (i = 0; i < nm_ip4_config_get_num_domains (self); i++) + g_message (" domain: %s", nm_ip4_config_get_domain (self, i)); /* dns searches */ - for (i = 0; i < nm_ip4_config_get_num_searches (config); i++) - g_message (" search: %s", nm_ip4_config_get_search (config, i)); + for (i = 0; i < nm_ip4_config_get_num_searches (self); i++) + g_message (" search: %s", nm_ip4_config_get_search (self, i)); /* dns options */ - for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++) - g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (config, i)); + for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++) + g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (self, i)); - g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (config)); + g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (self)); - g_message (" mss: %"G_GUINT32_FORMAT, nm_ip4_config_get_mss (config)); - g_message (" mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (config), (int) nm_ip4_config_get_mtu_source (config)); + g_message (" mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (self), (int) nm_ip4_config_get_mtu_source (self)); /* NIS */ - for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) { - tmp = nm_ip4_config_get_nis_server (config, i); + for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) { + tmp = nm_ip4_config_get_nis_server (self, i); g_message (" nis: %s", nm_utils_inet4_ntop (tmp, NULL)); } - g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config) ?: "(none)"); + g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (self) ?: "(none)"); /* WINS */ - for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) { - tmp = nm_ip4_config_get_wins (config, i); + for (i = 0; i < nm_ip4_config_get_num_wins (self); i++) { + tmp = nm_ip4_config_get_wins (self, i); g_message (" wins: %s", nm_utils_inet4_ntop (tmp, NULL)); } - g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config)); - g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (config)); -} - -gboolean -nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 network, guint8 plen) -{ - guint naddresses = nm_ip4_config_get_num_addresses (config); - guint i; - in_addr_t peer_network; - - for (i = 0; i < naddresses; i++) { - const NMPlatformIP4Address *item = nm_ip4_config_get_address (config, i); - - if (item->plen > plen) - continue; - - peer_network = nm_utils_ip4_address_clear_host_address (item->peer_address, item->plen); - if (_ipv4_is_zeronet (peer_network)) - continue; - - if (peer_network != nm_utils_ip4_address_clear_host_address (network, item->plen)) - continue; - - return TRUE; - } - - return FALSE; + g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (self)); } /*****************************************************************************/ void -nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default) +nm_ip4_config_reset_addresses (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - priv->never_default = never_default; + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip4_addresses) > 0) + _notify_addresses (self); } -gboolean -nm_ip4_config_get_never_default (const NMIP4Config *config) +static void +_add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - return priv->never_default; + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_addresses_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + NULL, + NULL)) + _notify_addresses (self); } +/** + * nm_ip4_config_add_address: + * @self: the #NMIP4Config + * @new: the new address to add to @self + * + * Adds the new address to @self. If an address with the same basic properties + * (address, prefix) already exists in @self, it is overwritten with the + * lifetime and preferred of @new. The source is also overwritten by the source + * from @new if that source is higher priority. + */ void -nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway) +nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + g_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen > 0 && new->plen <= 32); + g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); - if (priv->gateway != gateway || !priv->has_gateway) { - priv->gateway = gateway; - priv->has_gateway = TRUE; - _notify (config, PROP_GATEWAY); - } + _add_address (self, NULL, new); } void -nm_ip4_config_unset_gateway (NMIP4Config *config) +_nmtst_ip4_config_del_address (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMPlatformIP4Address *a; - if (priv->has_gateway) { - priv->gateway = 0; - priv->has_gateway = FALSE; - _notify (config, PROP_GATEWAY); - } + a = _nmtst_ip4_config_get_address (self, i); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (a))) + g_assert_not_reached (); } -/** - * nm_ip4_config_has_gateway: - * @config: the #NMIP4Config object - * - * NetworkManager's handling of default-routes is limited and usually a default-route - * cannot have gateway 0.0.0.0. For peer-to-peer routes, we still want to - * support that, so we need to differenciate between no-default-route and a - * on-link-default route. Hence nm_ip4_config_has_gateway(). - * - * Returns: whether the object has a gateway explicitly set. */ -gboolean -nm_ip4_config_has_gateway (const NMIP4Config *config) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - return priv->has_gateway; -} - -guint32 -nm_ip4_config_get_gateway (const NMIP4Config *config) +guint +nm_ip4_config_get_num_addresses (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->gateway; + head_entry = nm_ip4_config_lookup_addresses (self); + return head_entry ? head_entry->len : 0; } -gint64 -nm_ip4_config_get_route_metric (const NMIP4Config *config) +const NMPlatformIP4Address * +nm_ip4_config_get_first_address (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Address *a = NULL; - return priv->route_metric; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) + return a; + return NULL; } -/*****************************************************************************/ - -void -nm_ip4_config_reset_addresses (NMIP4Config *config) +const NMPlatformIP4Address * +_nmtst_ip4_config_get_address (const NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Address *a = NULL; + guint j; - if (priv->addresses->len != 0) { - g_array_set_size (priv->addresses, 0); - notify_addresses (config); + j = 0; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) { + if (i == j) + return a; + j++; } + g_return_val_if_reached (NULL); } -/** - * nm_ip4_config_add_address: - * @config: the #NMIP4Config - * @new: the new address to add to @config - * - * Adds the new address to @config. If an address with the same basic properties - * (address, prefix) already exists in @config, it is overwritten with the - * lifetime and preferred of @new. The source is also overwritten by the source - * from @new if that source is higher priority. - */ -void -nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new) +gboolean +nm_ip4_config_address_exists (const NMIP4Config *self, + const NMPlatformIP4Address *needle) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - NMPlatformIP4Address item_old; - int i; - - g_return_if_fail (new != NULL); - - for (i = 0; i < priv->addresses->len; i++ ) { - NMPlatformIP4Address *item = &g_array_index (priv->addresses, NMPlatformIP4Address, i); - - if (addresses_are_duplicate (item, new)) { - if (nm_platform_ip4_address_cmp (item, new) == 0) - return; - - /* remember the old values. */ - item_old = *item; - /* Copy over old item to get new lifetime, timestamp, preferred */ - *item = *new; - - /* But restore highest priority source */ - item->addr_source = MAX (item_old.addr_source, new->addr_source); - - /* for addresses that we read from the kernel, we keep the timestamps as defined - * by the previous source (item_old). The reason is, that the other source configured the lifetimes - * with "what should be" and the kernel values are "what turned out after configuring it". - * - * For other sources, the longer lifetime wins. */ - if ( (new->addr_source == NM_IP_CONFIG_SOURCE_KERNEL && new->addr_source != item_old.addr_source) - || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) { - item->timestamp = item_old.timestamp; - item->lifetime = item_old.lifetime; - item->preferred = item_old.preferred; - } - if (nm_platform_ip4_address_cmp (&item_old, item) == 0) - return; - goto NOTIFY; - } - } + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + NMPObject obj_stack; - g_array_append_val (priv->addresses, *new); -NOTIFY: - notify_addresses (config); + nmp_object_stackinit_id_ip4_address (&obj_stack, + priv->ifindex, + needle->address, + needle->plen, + needle->peer_address); + return !!nm_dedup_multi_index_lookup_obj (priv->multi_idx, + &priv->idx_ip4_addresses, + &obj_stack); } -void -nm_ip4_config_del_address (NMIP4Config *config, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - g_return_if_fail (i < priv->addresses->len); - - g_array_remove_index (priv->addresses, i); - - notify_addresses (config); -} +/*****************************************************************************/ -guint -nm_ip4_config_get_num_addresses (const NMIP4Config *config) +static const NMDedupMultiEntry * +_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv; - return priv->addresses->len; -} + nm_assert (NM_IS_IP4_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE); -const NMPlatformIP4Address * -nm_ip4_config_get_address (const NMIP4Config *config, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - return &g_array_index (priv->addresses, NMPlatformIP4Address, i); + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip4_routes_, + needle, + cmp_type); } -gboolean -nm_ip4_config_address_exists (const NMIP4Config *config, - const NMPlatformIP4Address *needle) +void +nm_ip4_config_reset_routes (NMIP4Config *self) { - return _addresses_get_index (config, needle) >= 0; -} + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); -/*****************************************************************************/ + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip4_routes) > 0) { + if (nm_clear_nmp_object (&priv->best_default_route)) + _notify (self, PROP_GATEWAY); + _notify_routes (self); + } +} -void -nm_ip4_config_reset_routes (NMIP4Config *config) +static void +_add_route (NMIP4Config *self, + const NMPObject *obj_new, + const NMPlatformIP4Route *new, + const NMPObject **out_obj_new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + const NMPObject *obj_new_2; + + nm_assert ((!new) != (!obj_new)); + nm_assert (!new || _route_valid (new)); + nm_assert (!obj_new || _route_valid (NMP_OBJECT_CAST_IP4_ROUTE (obj_new))); + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_routes_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + &obj_old, + &obj_new_2)) { + gboolean changed_default_route = FALSE; + + if ( priv->best_default_route == obj_old + && obj_old != obj_new_2) { + changed_default_route = TRUE; + nm_clear_nmp_object (&priv->best_default_route); + } + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); + if (_nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2)) + changed_default_route = TRUE; - if (priv->routes->len != 0) { - g_array_set_size (priv->routes, 0); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); - } + if (changed_default_route) + _notify (self, PROP_GATEWAY); + _notify_routes (self); + } else + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); } /** * nm_ip4_config_add_route: - * @config: the #NMIP4Config - * @new: the new route to add to @config + * @self: the #NMIP4Config + * @new: the new route to add to @self + * @out_obj_new: (allow-none): (out): the added route object. Must be unrefed + * by caller. * - * Adds the new route to @config. If a route with the same basic properties - * (network, prefix) already exists in @config, it is overwritten including the + * Adds the new route to @self. If a route with the same basic properties + * (network, prefix) already exists in @self, it is overwritten including the * gateway and metric of @new. The source is also overwritten by the source * from @new if that source is higher priority. */ void -nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new) +nm_ip4_config_add_route (NMIP4Config *self, + const NMPlatformIP4Route *new, + const NMPObject **out_obj_new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - NMIPConfigSource old_source; - int i; - - g_return_if_fail (new != NULL); - g_return_if_fail (new->plen > 0 && new->plen <= 32); - g_return_if_fail (priv->ifindex > 0); - - for (i = 0; i < priv->routes->len; i++ ) { - NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if (routes_are_duplicate (item, new, FALSE)) { - if (nm_platform_ip4_route_cmp (item, new) == 0) - return; - old_source = item->rt_source; - memcpy (item, new, sizeof (*item)); - /* Restore highest priority source */ - item->rt_source = MAX (old_source, new->rt_source); - item->ifindex = priv->ifindex; - goto NOTIFY; - } - } + g_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen <= 32); + g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); - g_array_append_val (priv->routes, *new); - g_array_index (priv->routes, NMPlatformIP4Route, priv->routes->len - 1).ifindex = priv->ifindex; -NOTIFY: - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + _add_route (self, NULL, new, out_obj_new); } void -nm_ip4_config_del_route (NMIP4Config *config, guint i) +_nmtst_ip4_config_del_route (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - g_return_if_fail (i < priv->routes->len); + const NMPlatformIP4Route *r; - g_array_remove_index (priv->routes, i); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + r = _nmtst_ip4_config_get_route (self, i); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (r))) + g_assert_not_reached (); } guint -nm_ip4_config_get_num_routes (const NMIP4Config *config) +nm_ip4_config_get_num_routes (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->routes->len; + head_entry = nm_ip4_config_lookup_routes (self); + nm_assert (!head_entry || head_entry->len == c_list_length (&head_entry->lst_entries_head)); + return head_entry ? head_entry->len : 0; } const NMPlatformIP4Route * -nm_ip4_config_get_route (const NMIP4Config *config, guint i) +_nmtst_ip4_config_get_route (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Route *r = NULL; + guint j; - return &g_array_index (priv->routes, NMPlatformIP4Route, i); + j = 0; + nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) { + if (i == j) + return r; + j++; + } + g_return_val_if_reached (NULL); } const NMPlatformIP4Route * -nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host) +nm_ip4_config_get_direct_route_for_host (const NMIP4Config *self, + in_addr_t host, + guint32 route_table) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *item; + NMDedupMultiIter ipconf_iter; g_return_val_if_fail (host, NULL); - for (i = 0; i < priv->routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); - + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &item) { if (item->gateway != 0) continue; if (best_route && best_route->plen > item->plen) continue; + if (nm_platform_route_table_uncoerce (item->table_coerced, TRUE) != route_table) + continue; + if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen)) continue; @@ -1715,27 +2157,26 @@ nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host best_route = item; } - return best_route; } /*****************************************************************************/ void -nm_ip4_config_reset_nameservers (NMIP4Config *config) +nm_ip4_config_reset_nameservers (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->nameservers->len != 0) { g_array_set_size (priv->nameservers, 0); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } } void -nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new) +nm_ip4_config_add_nameserver (NMIP4Config *self, guint32 new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != 0); @@ -1745,32 +2186,32 @@ nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new) return; g_array_append_val (priv->nameservers, new); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } void -nm_ip4_config_del_nameserver (NMIP4Config *config, guint i) +nm_ip4_config_del_nameserver (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->nameservers->len); g_array_remove_index (priv->nameservers, i); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } guint -nm_ip4_config_get_num_nameservers (const NMIP4Config *config) +nm_ip4_config_get_num_nameservers (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nameservers->len; } guint32 -nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i) +nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->nameservers, guint32, i); } @@ -1778,20 +2219,20 @@ nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_domains (NMIP4Config *config) +nm_ip4_config_reset_domains (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->domains->len != 0) { g_ptr_array_set_size (priv->domains, 0); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } } void -nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) +nm_ip4_config_add_domain (NMIP4Config *self, const char *domain) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (domain != NULL); @@ -1802,32 +2243,32 @@ nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) return; g_ptr_array_add (priv->domains, g_strdup (domain)); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } void -nm_ip4_config_del_domain (NMIP4Config *config, guint i) +nm_ip4_config_del_domain (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->domains->len); g_ptr_array_remove_index (priv->domains, i); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } guint -nm_ip4_config_get_num_domains (const NMIP4Config *config) +nm_ip4_config_get_num_domains (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->domains->len; } const char * -nm_ip4_config_get_domain (const NMIP4Config *config, guint i) +nm_ip4_config_get_domain (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->domains, i); } @@ -1835,20 +2276,20 @@ nm_ip4_config_get_domain (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_searches (NMIP4Config *config) +nm_ip4_config_reset_searches (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->searches->len != 0) { g_ptr_array_set_size (priv->searches, 0); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } } void -nm_ip4_config_add_search (NMIP4Config *config, const char *new) +nm_ip4_config_add_search (NMIP4Config *self, const char *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); char *search; size_t len; @@ -1874,32 +2315,32 @@ nm_ip4_config_add_search (NMIP4Config *config, const char *new) } g_ptr_array_add (priv->searches, search); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } void -nm_ip4_config_del_search (NMIP4Config *config, guint i) +nm_ip4_config_del_search (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->searches->len); g_ptr_array_remove_index (priv->searches, i); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } guint -nm_ip4_config_get_num_searches (const NMIP4Config *config) +nm_ip4_config_get_num_searches (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->searches->len; } const char * -nm_ip4_config_get_search (const NMIP4Config *config, guint i) +nm_ip4_config_get_search (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->searches, i); } @@ -1907,20 +2348,20 @@ nm_ip4_config_get_search (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_dns_options (NMIP4Config *config) +nm_ip4_config_reset_dns_options (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->dns_options->len != 0) { g_ptr_array_set_size (priv->dns_options, 0); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } } void -nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new) +nm_ip4_config_add_dns_option (NMIP4Config *self, const char *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != NULL); @@ -1931,32 +2372,32 @@ nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new) return; g_ptr_array_add (priv->dns_options, g_strdup (new)); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } void -nm_ip4_config_del_dns_option(NMIP4Config *config, guint i) +nm_ip4_config_del_dns_option(NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->dns_options->len); g_ptr_array_remove_index (priv->dns_options, i); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } guint -nm_ip4_config_get_num_dns_options (const NMIP4Config *config) +nm_ip4_config_get_num_dns_options (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->dns_options->len; } const char * -nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i) +nm_ip4_config_get_dns_option (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->dns_options, i); } @@ -1964,20 +2405,20 @@ nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_set_dns_priority (NMIP4Config *config, gint priority) +nm_ip4_config_set_dns_priority (NMIP4Config *self, gint priority) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priority != priv->dns_priority) { priv->dns_priority = priority; - _notify (config, PROP_DNS_PRIORITY); + _notify (self, PROP_DNS_PRIORITY); } } gint -nm_ip4_config_get_dns_priority (const NMIP4Config *config) +nm_ip4_config_get_dns_priority (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->dns_priority; } @@ -1985,35 +2426,17 @@ nm_ip4_config_get_dns_priority (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - priv->mss = mss; -} - -guint32 -nm_ip4_config_get_mss (const NMIP4Config *config) +nm_ip4_config_reset_nis_servers (NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - return priv->mss; -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_nis_servers (NMIP4Config *config) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_array_set_size (priv->nis, 0); } void -nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) +nm_ip4_config_add_nis_server (NMIP4Config *self, guint32 nis) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; for (i = 0; i < priv->nis->len; i++) @@ -2024,9 +2447,9 @@ nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) } void -nm_ip4_config_del_nis_server (NMIP4Config *config, guint i) +nm_ip4_config_del_nis_server (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->nis->len); @@ -2034,34 +2457,34 @@ nm_ip4_config_del_nis_server (NMIP4Config *config, guint i) } guint -nm_ip4_config_get_num_nis_servers (const NMIP4Config *config) +nm_ip4_config_get_num_nis_servers (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nis->len; } guint32 -nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i) +nm_ip4_config_get_nis_server (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->nis, guint32, i); } void -nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain) +nm_ip4_config_set_nis_domain (NMIP4Config *self, const char *domain) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_free (priv->nis_domain); priv->nis_domain = g_strdup (domain); } const char * -nm_ip4_config_get_nis_domain (const NMIP4Config *config) +nm_ip4_config_get_nis_domain (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nis_domain; } @@ -2069,20 +2492,20 @@ nm_ip4_config_get_nis_domain (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_reset_wins (NMIP4Config *config) +nm_ip4_config_reset_wins (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->wins->len != 0) { g_array_set_size (priv->wins, 0); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } } void -nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) +nm_ip4_config_add_wins (NMIP4Config *self, guint32 wins) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (wins != 0); @@ -2092,32 +2515,32 @@ nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) return; g_array_append_val (priv->wins, wins); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } void -nm_ip4_config_del_wins (NMIP4Config *config, guint i) +nm_ip4_config_del_wins (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->wins->len); g_array_remove_index (priv->wins, i); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } guint -nm_ip4_config_get_num_wins (const NMIP4Config *config) +nm_ip4_config_get_num_wins (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->wins->len; } guint32 -nm_ip4_config_get_wins (const NMIP4Config *config, guint i) +nm_ip4_config_get_wins (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->wins, guint32, i); } @@ -2125,9 +2548,9 @@ nm_ip4_config_get_wins (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source) +nm_ip4_config_set_mtu (NMIP4Config *self, guint32 mtu, NMIPConfigSource source) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (!mtu) source = NM_IP_CONFIG_SOURCE_UNKNOWN; @@ -2137,17 +2560,17 @@ nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source } guint32 -nm_ip4_config_get_mtu (const NMIP4Config *config) +nm_ip4_config_get_mtu (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->mtu; } NMIPConfigSource -nm_ip4_config_get_mtu_source (const NMIP4Config *config) +nm_ip4_config_get_mtu_source (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->mtu_source; } @@ -2155,23 +2578,102 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered) +nm_ip4_config_set_metered (NMIP4Config *self, gboolean metered) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); priv->metered = metered; } gboolean -nm_ip4_config_get_metered (const NMIP4Config *config) +nm_ip4_config_get_metered (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->metered; } /*****************************************************************************/ +const NMPObject * +nm_ip4_config_nmpobj_lookup (const NMIP4Config *self, const NMPObject *needle) +{ + const NMIP4ConfigPrivate *priv; + const NMDedupMultiIdxType *idx_type; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (NULL); + } + + return nm_dedup_multi_entry_get_obj (nm_dedup_multi_index_lookup_obj (priv->multi_idx, + idx_type, + needle)); +} + +gboolean +nm_ip4_config_nmpobj_remove (NMIP4Config *self, + const NMPObject *needle) +{ + NMIP4ConfigPrivate *priv; + NMDedupMultiIdxType *idx_type; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + guint n; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (FALSE); + } + + n = nm_dedup_multi_index_remove_obj (priv->multi_idx, + idx_type, + needle, + (gconstpointer *) &obj_old); + if (n != 1) { + nm_assert (n == 0); + return FALSE; + } + + nm_assert (NMP_OBJECT_GET_TYPE (obj_old) == NMP_OBJECT_GET_TYPE (needle)); + + switch (NMP_OBJECT_GET_TYPE (obj_old)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + _notify_addresses (self); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + if (priv->best_default_route == obj_old) { + if (_nm_ip_config_best_default_route_set (&priv->best_default_route, + _nm_ip4_config_best_default_route_find (self))) + _notify (self, PROP_GATEWAY); + } + _notify_routes (self); + break; + default: + nm_assert_not_reached (); + } + return TRUE; +} + +/*****************************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { @@ -2179,60 +2681,57 @@ hash_u32 (GChecksum *sum, guint32 n) } void -nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only) +nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only) { guint i; const char *s; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - g_return_if_fail (config); + g_return_if_fail (self); g_return_if_fail (sum); if (!dns_only) { - hash_u32 (sum, nm_ip4_config_has_gateway (config)); - hash_u32 (sum, nm_ip4_config_get_gateway (config)); - - for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { hash_u32 (sum, address->address); hash_u32 (sum, address->plen); - hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen)); + hash_u32 (sum, address->peer_address & _nm_utils_ip4_prefix_to_netmask (address->plen)); } - for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); - + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { hash_u32 (sum, route->network); hash_u32 (sum, route->plen); hash_u32 (sum, route->gateway); hash_u32 (sum, route->metric); } - for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) - hash_u32 (sum, nm_ip4_config_get_nis_server (config, i)); + for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) + hash_u32 (sum, nm_ip4_config_get_nis_server (self, i)); - s = nm_ip4_config_get_nis_domain (config); + s = nm_ip4_config_get_nis_domain (self); if (s) g_checksum_update (sum, (const guint8 *) s, strlen (s)); } - for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) - hash_u32 (sum, nm_ip4_config_get_nameserver (config, i)); + for (i = 0; i < nm_ip4_config_get_num_nameservers (self); i++) + hash_u32 (sum, nm_ip4_config_get_nameserver (self, i)); - for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) - hash_u32 (sum, nm_ip4_config_get_wins (config, i)); + for (i = 0; i < nm_ip4_config_get_num_wins (self); i++) + hash_u32 (sum, nm_ip4_config_get_wins (self, i)); - for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) { - s = nm_ip4_config_get_domain (config, i); + for (i = 0; i < nm_ip4_config_get_num_domains (self); i++) { + s = nm_ip4_config_get_domain (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } - for (i = 0; i < nm_ip4_config_get_num_searches (config); i++) { - s = nm_ip4_config_get_search (config, i); + for (i = 0; i < nm_ip4_config_get_num_searches (self); i++) { + s = nm_ip4_config_get_search (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } - for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++) { - s = nm_ip4_config_get_dns_option (config, i); + for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++) { + s = nm_ip4_config_get_dns_option (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } } @@ -2283,8 +2782,12 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMIP4Config *config = NM_IP4_CONFIG (object); - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4Config *self = NM_IP4_CONFIG (object); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; + GVariantBuilder builder_data, builder_legacy; switch (prop_id) { case PROP_IFINDEX: @@ -2292,25 +2795,32 @@ get_property (GObject *object, guint prop_id, break; case PROP_ADDRESS_DATA: case PROP_ADDRESSES: - { - GVariantBuilder array_builder, addr_builder; - gs_unref_array GArray *new = NULL; - guint naddr, i; + nm_assert (!!priv->address_data_variant == !!priv->addresses_variant); - g_return_if_fail (!!priv->address_data_variant == !!priv->addresses_variant); + if (priv->address_data_variant) + goto out_addresses_cached; - if (priv->address_data_variant) - goto return_cached; + g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau")); - naddr = nm_ip4_config_get_num_addresses (config); - new = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Address), naddr); - g_array_append_vals (new, priv->addresses->data, priv->addresses->len); - g_array_sort (new, _addresses_sort_cmp); + head_entry = nm_ip4_config_lookup_addresses (self); + if (head_entry) { + gs_free const NMPObject **addresses = NULL; + guint naddr, i; + + addresses = (const NMPObject **) nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr); + nm_assert (addresses && naddr); + + g_qsort_with_data (addresses, + naddr, + sizeof (addresses[0]), + _addresses_sort_cmp, + NULL); /* Build address data variant */ - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); for (i = 0; i < naddr; i++) { - const NMPlatformIP4Address *address = &g_array_index (new, NMPlatformIP4Address, i); + GVariantBuilder addr_builder; + const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses[i]); g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", @@ -2331,98 +2841,105 @@ get_property (GObject *object, guint prop_id, g_variant_new_string (address->label)); } - g_variant_builder_add (&array_builder, "a{sv}", &addr_builder); + g_variant_builder_add (&builder_data, "a{sv}", &addr_builder); + + { + const guint32 dbus_addr[3] = { + address->address, + address->plen, + ( i == 0 + && priv->best_default_route) + ? NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway + : (guint32) 0, + }; + + g_variant_builder_add (&builder_legacy, "@au", + g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, + dbus_addr, 3, sizeof (guint32))); + } } - priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); - - /* Build addresses variant */ - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau")); - for (i = 0; i < naddr; i++) { - const NMPlatformIP4Address *address = &g_array_index (new, NMPlatformIP4Address, i); - guint32 dbus_addr[3]; + } - dbus_addr[0] = address->address; - dbus_addr[1] = address->plen; - dbus_addr[2] = i == 0 ? priv->gateway : 0; + priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); - g_variant_builder_add (&array_builder, "@au", - g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, - dbus_addr, 3, sizeof (guint32))); - } - priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); - -return_cached: - g_value_set_variant (value, - prop_id == PROP_ADDRESS_DATA ? - priv->address_data_variant : - priv->addresses_variant); - } +out_addresses_cached: + g_value_set_variant (value, + prop_id == PROP_ADDRESS_DATA ? + priv->address_data_variant : + priv->addresses_variant); break; case PROP_ROUTE_DATA: - { - GVariantBuilder array_builder, route_builder; - guint nroutes = nm_ip4_config_get_num_routes (config); - guint i; + case PROP_ROUTES: + nm_assert (!!priv->route_data_variant == !!priv->routes_variant); - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); + if (priv->route_data_variant) + goto out_routes_cached; - g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&route_builder, "{sv}", - "dest", - g_variant_new_string (nm_utils_inet4_ntop (route->network, NULL))); - g_variant_builder_add (&route_builder, "{sv}", - "prefix", - g_variant_new_uint32 (route->plen)); - if (route->gateway) { - g_variant_builder_add (&route_builder, "{sv}", - "next-hop", - g_variant_new_string (nm_utils_inet4_ntop (route->gateway, NULL))); - } + g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau")); + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { + GVariantBuilder route_builder; + + nm_assert (_route_valid (route)); + + g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&route_builder, "{sv}", + "dest", + g_variant_new_string (nm_utils_inet4_ntop (route->network, NULL))); + g_variant_builder_add (&route_builder, "{sv}", + "prefix", + g_variant_new_uint32 (route->plen)); + if (route->gateway) { g_variant_builder_add (&route_builder, "{sv}", - "metric", - g_variant_new_uint32 (route->metric)); + "next-hop", + g_variant_new_string (nm_utils_inet4_ntop (route->gateway, NULL))); + } + g_variant_builder_add (&route_builder, "{sv}", + "metric", + g_variant_new_uint32 (route->metric)); - g_variant_builder_add (&array_builder, "a{sv}", &route_builder); + if (!nm_platform_route_table_is_main (route->table_coerced)) { + g_variant_builder_add (&route_builder, "{sv}", + "table", + g_variant_new_uint32 (nm_platform_route_table_uncoerce (route->table_coerced, TRUE))); } - g_value_take_variant (value, g_variant_builder_end (&array_builder)); - } - break; - case PROP_ROUTES: - { - GVariantBuilder array_builder; - guint nroutes = nm_ip4_config_get_num_routes (config); - guint i; - - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); - guint32 dbus_route[4]; - - /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the - * plen is positive. Skip the default routes not to break older clients. */ - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - continue; + g_variant_builder_add (&builder_data, "a{sv}", &route_builder); - dbus_route[0] = route->network; - dbus_route[1] = route->plen; - dbus_route[2] = route->gateway; - dbus_route[3] = route->metric; + /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the + * plen is positive. Skip the default routes not to break older clients. */ + if ( nm_platform_route_table_is_main (route->table_coerced) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { + const guint32 dbus_route[4] = { + route->network, + route->plen, + route->gateway, + route->metric, + }; - g_variant_builder_add (&array_builder, "@au", + g_variant_builder_add (&builder_legacy, "@au", g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, dbus_route, 4, sizeof (guint32))); } - - g_value_take_variant (value, g_variant_builder_end (&array_builder)); } + + priv->route_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->routes_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); + +out_routes_cached: + g_value_set_variant (value, + prop_id == PROP_ROUTE_DATA ? + priv->route_data_variant : + priv->routes_variant); break; case PROP_GATEWAY: - if (priv->has_gateway) - g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL)); - else + if (priv->best_default_route) { + g_value_set_string (value, + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, + NULL)); + } else g_value_set_string (value, NULL); break; case PROP_NAMESERVERS: @@ -2467,6 +2984,13 @@ set_property (GObject *object, NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); @@ -2480,26 +3004,29 @@ set_property (GObject *object, /*****************************************************************************/ static void -nm_ip4_config_init (NMIP4Config *config) +nm_ip4_config_init (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_addresses, + NMP_OBJECT_TYPE_IP4_ADDRESS); + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes, + NMP_OBJECT_TYPE_IP4_ROUTE); - priv->addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); - priv->routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); priv->nameservers = g_array_new (FALSE, FALSE, sizeof (guint32)); priv->domains = g_ptr_array_new_with_free_func (g_free); priv->searches = g_ptr_array_new_with_free_func (g_free); priv->dns_options = g_ptr_array_new_with_free_func (g_free); priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32)); priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32)); - priv->route_metric = -1; } NMIP4Config * -nm_ip4_config_new (int ifindex) +nm_ip4_config_new (NMDedupMultiIndex *multi_idx, int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, + NM_IP4_CONFIG_MULTI_IDX, multi_idx, NM_IP4_CONFIG_IFINDEX, ifindex, NULL); } @@ -2510,10 +3037,16 @@ finalize (GObject *object) NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_clear_nmp_object (&priv->best_default_route); + + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_addresses); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_routes); + nm_clear_g_variant (&priv->address_data_variant); nm_clear_g_variant (&priv->addresses_variant); - g_array_unref (priv->addresses); - g_array_unref (priv->routes); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); @@ -2523,6 +3056,8 @@ finalize (GObject *object) g_array_unref (priv->wins); G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); + + nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -2537,6 +3072,11 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class) object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_IP4_CONFIG_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index ceb52ac5..978471df 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -24,6 +24,97 @@ #include "nm-exported-object.h" #include "nm-setting-ip4-config.h" +#include "nm-utils/nm-dedup-multi.h" +#include "platform/nmp-object.h" + +/*****************************************************************************/ + +typedef struct { + NMDedupMultiIdxType parent; + NMPObjectType obj_type; +} NMIPConfigDedupMultiIdxType; + +void nm_ip_config_dedup_multi_idx_type_init (NMIPConfigDedupMultiIdxType *idx_type, NMPObjectType obj_type); + +/*****************************************************************************/ + +void nm_ip_config_iter_ip4_address_init (NMDedupMultiIter *iter, const NMIP4Config *self); +void nm_ip_config_iter_ip4_route_init (NMDedupMultiIter *iter, const NMIP4Config *self); + +static inline gboolean +nm_ip_config_iter_ip4_address_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP4Address **out_address) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (out_address) + *out_address = has_next ? NMP_OBJECT_CAST_IP4_ADDRESS (ipconf_iter->current->obj) : NULL; + return has_next; +} + +static inline gboolean +nm_ip_config_iter_ip4_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP4Route **out_route) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (out_route) + *out_route = has_next ? NMP_OBJECT_CAST_IP4_ROUTE (ipconf_iter->current->obj) : NULL; + return has_next; +} + +#define nm_ip_config_iter_ip4_address_for_each(iter, self, address) \ + for (nm_ip_config_iter_ip4_address_init ((iter), (self)); \ + nm_ip_config_iter_ip4_address_next ((iter), (address)); \ + ) + +#define nm_ip_config_iter_ip4_route_for_each(iter, self, route) \ + for (nm_ip_config_iter_ip4_route_init ((iter), (self)); \ + nm_ip_config_iter_ip4_route_next ((iter), (route)); \ + ) + +/*****************************************************************************/ + +static inline gboolean +nm_ip_config_best_default_route_is (const NMPObject *obj) +{ + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (obj); + + /* return whether @obj is considered a default-route. + * + * NMIP4Config/NMIP6Config tracks the (best) default-route explicitly, because + * at various places we act differently depending on whether there is a default-route + * configured. + * + * Note that this only considers the main routing table. */ + return r + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && nm_platform_route_table_is_main (r->table_coerced); +} + +const NMPObject *_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp); +gboolean _nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate); +gboolean _nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate); + +/*****************************************************************************/ + +gboolean _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, + NMIPConfigDedupMultiIdxType *idx_type, + int ifindex, + const NMPObject *obj_new, + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); + +const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); + +/*****************************************************************************/ + #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) #define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) #define NM_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) @@ -34,6 +125,7 @@ typedef struct _NMIP4ConfigClass NMIP4ConfigClass; /* internal */ +#define NM_IP4_CONFIG_MULTI_IDX "multi-idx" #define NM_IP4_CONFIG_IFINDEX "ifindex" /* public*/ @@ -54,106 +146,125 @@ typedef struct _NMIP4ConfigClass NMIP4ConfigClass; GType nm_ip4_config_get_type (void); -NMIP4Config * nm_ip4_config_new (int ifindex); +NMIP4Config * nm_ip4_config_new (NMDedupMultiIndex *multi_idx, + int ifindex); -int nm_ip4_config_get_ifindex (const NMIP4Config *config); +int nm_ip4_config_get_ifindex (const NMIP4Config *self); +NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self); -NMIP4Config *nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); -gboolean nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric); -void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); -NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config); +NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); +void nm_ip4_config_add_dependent_routes (NMIP4Config *self, + guint32 route_table, + guint32 route_metric, + GPtrArray **out_ip4_dev_route_blacklist); -void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags); -void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src); -void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src); -gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes); -gboolean nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 dest, guint8 plen); -void nm_ip4_config_dump (const NMIP4Config *config, const char *detail); - - -void nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default); -gboolean nm_ip4_config_get_never_default (const NMIP4Config *config); -void nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway); -void nm_ip4_config_unset_gateway (NMIP4Config *config); -gboolean nm_ip4_config_has_gateway (const NMIP4Config *config); -guint32 nm_ip4_config_get_gateway (const NMIP4Config *config); -gint64 nm_ip4_config_get_route_metric (const NMIP4Config *config); - -void nm_ip4_config_reset_addresses (NMIP4Config *config); -void nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *address); -void nm_ip4_config_del_address (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_addresses (const NMIP4Config *config); -const NMPlatformIP4Address *nm_ip4_config_get_address (const NMIP4Config *config, guint i); -gboolean nm_ip4_config_address_exists (const NMIP4Config *config, const NMPlatformIP4Address *address); - -void nm_ip4_config_reset_routes (NMIP4Config *config); -void nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *route); -void nm_ip4_config_del_route (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_routes (const NMIP4Config *config); -const NMPlatformIP4Route *nm_ip4_config_get_route (const NMIP4Config *config, guint i); - -const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host); - -void nm_ip4_config_reset_nameservers (NMIP4Config *config); -void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 nameserver); -void nm_ip4_config_del_nameserver (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_nameservers (const NMIP4Config *config); -guint32 nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i); - -void nm_ip4_config_reset_domains (NMIP4Config *config); -void nm_ip4_config_add_domain (NMIP4Config *config, const char *domain); -void nm_ip4_config_del_domain (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_domains (const NMIP4Config *config); -const char * nm_ip4_config_get_domain (const NMIP4Config *config, guint i); - -void nm_ip4_config_reset_searches (NMIP4Config *config); -void nm_ip4_config_add_search (NMIP4Config *config, const char *search); -void nm_ip4_config_del_search (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_searches (const NMIP4Config *config); -const char * nm_ip4_config_get_search (const NMIP4Config *config, guint i); - -void nm_ip4_config_reset_dns_options (NMIP4Config *config); -void nm_ip4_config_add_dns_option (NMIP4Config *config, const char *option); -void nm_ip4_config_del_dns_option (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_dns_options (const NMIP4Config *config); -const char * nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i); - -void nm_ip4_config_set_dns_priority (NMIP4Config *config, gint priority); -gint nm_ip4_config_get_dns_priority (const NMIP4Config *config); - -void nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss); -guint32 nm_ip4_config_get_mss (const NMIP4Config *config); - -void nm_ip4_config_reset_nis_servers (NMIP4Config *config); -void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis); -void nm_ip4_config_del_nis_server (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_nis_servers (const NMIP4Config *config); -guint32 nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i); -void nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain); -const char * nm_ip4_config_get_nis_domain (const NMIP4Config *config); - -void nm_ip4_config_reset_wins (NMIP4Config *config); -void nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins); -void nm_ip4_config_del_wins (NMIP4Config *config, guint i); -guint nm_ip4_config_get_num_wins (const NMIP4Config *config); -guint32 nm_ip4_config_get_wins (const NMIP4Config *config, guint i); - -void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source); -guint32 nm_ip4_config_get_mtu (const NMIP4Config *config); -NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *config); - -void nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered); -gboolean nm_ip4_config_get_metered (const NMIP4Config *config); - -void nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only); -gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b); +gboolean nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + NMIPRouteTableSyncMode route_table_sync); + +void nm_ip4_config_merge_setting (NMIP4Config *self, + NMSettingIPConfig *setting, + guint32 route_table, + guint32 route_metric); +NMSetting *nm_ip4_config_create_setting (const NMIP4Config *self); -/*****************************************************************************/ -/* Testing-only functions */ -gboolean nm_ip4_config_capture_resolv_conf (GArray *nameservers, GPtrArray *dns_options, - const char *rc_contents); +void nm_ip4_config_merge (NMIP4Config *dst, + const NMIP4Config *src, + NMIPConfigMergeFlags merge_flags, + guint32 default_route_metric_penalty); +void nm_ip4_config_subtract (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty); +void nm_ip4_config_intersect (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty); +gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes); +void nm_ip4_config_dump (const NMIP4Config *self, const char *detail); + +const NMPObject *nm_ip4_config_best_default_route_get (const NMIP4Config *self); +const NMPObject *_nm_ip4_config_best_default_route_find (const NMIP4Config *self); + +in_addr_t nmtst_ip4_config_get_gateway (NMIP4Config *config); + +const NMDedupMultiHeadEntry *nm_ip4_config_lookup_addresses (const NMIP4Config *self); +void nm_ip4_config_reset_addresses (NMIP4Config *self); +void nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *address); +void _nmtst_ip4_config_del_address (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_addresses (const NMIP4Config *self); +const NMPlatformIP4Address *nm_ip4_config_get_first_address (const NMIP4Config *self); +const NMPlatformIP4Address *_nmtst_ip4_config_get_address (const NMIP4Config *self, guint i); +gboolean nm_ip4_config_address_exists (const NMIP4Config *self, const NMPlatformIP4Address *address); + +const NMDedupMultiHeadEntry *nm_ip4_config_lookup_routes (const NMIP4Config *self); +void nm_ip4_config_reset_routes (NMIP4Config *self); +void nm_ip4_config_add_route (NMIP4Config *self, + const NMPlatformIP4Route *route, + const NMPObject **out_obj_new); +void _nmtst_ip4_config_del_route (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_routes (const NMIP4Config *self); +const NMPlatformIP4Route *_nmtst_ip4_config_get_route (const NMIP4Config *self, guint i); + +const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Config *self, + in_addr_t host, + guint32 route_table); + +void nm_ip4_config_reset_nameservers (NMIP4Config *self); +void nm_ip4_config_add_nameserver (NMIP4Config *self, guint32 nameserver); +void nm_ip4_config_del_nameserver (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_nameservers (const NMIP4Config *self); +guint32 nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i); + +void nm_ip4_config_reset_domains (NMIP4Config *self); +void nm_ip4_config_add_domain (NMIP4Config *self, const char *domain); +void nm_ip4_config_del_domain (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_domains (const NMIP4Config *self); +const char * nm_ip4_config_get_domain (const NMIP4Config *self, guint i); + +void nm_ip4_config_reset_searches (NMIP4Config *self); +void nm_ip4_config_add_search (NMIP4Config *self, const char *search); +void nm_ip4_config_del_search (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_searches (const NMIP4Config *self); +const char * nm_ip4_config_get_search (const NMIP4Config *self, guint i); + +void nm_ip4_config_reset_dns_options (NMIP4Config *self); +void nm_ip4_config_add_dns_option (NMIP4Config *self, const char *option); +void nm_ip4_config_del_dns_option (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_dns_options (const NMIP4Config *self); +const char * nm_ip4_config_get_dns_option (const NMIP4Config *self, guint i); + +void nm_ip4_config_set_dns_priority (NMIP4Config *self, gint priority); +gint nm_ip4_config_get_dns_priority (const NMIP4Config *self); + +void nm_ip4_config_reset_nis_servers (NMIP4Config *self); +void nm_ip4_config_add_nis_server (NMIP4Config *self, guint32 nis); +void nm_ip4_config_del_nis_server (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_nis_servers (const NMIP4Config *self); +guint32 nm_ip4_config_get_nis_server (const NMIP4Config *self, guint i); +void nm_ip4_config_set_nis_domain (NMIP4Config *self, const char *domain); +const char * nm_ip4_config_get_nis_domain (const NMIP4Config *self); + +void nm_ip4_config_reset_wins (NMIP4Config *self); +void nm_ip4_config_add_wins (NMIP4Config *self, guint32 wins); +void nm_ip4_config_del_wins (NMIP4Config *self, guint i); +guint nm_ip4_config_get_num_wins (const NMIP4Config *self); +guint32 nm_ip4_config_get_wins (const NMIP4Config *self, guint i); + +void nm_ip4_config_set_mtu (NMIP4Config *self, guint32 mtu, NMIPConfigSource source); +guint32 nm_ip4_config_get_mtu (const NMIP4Config *self); +NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *self); + +void nm_ip4_config_set_metered (NMIP4Config *self, gboolean metered); +gboolean nm_ip4_config_get_metered (const NMIP4Config *self); + +const NMPObject *nm_ip4_config_nmpobj_lookup (const NMIP4Config *self, + const NMPObject *needle); +gboolean nm_ip4_config_nmpobj_remove (NMIP4Config *self, + const NMPObject *needle); + +void nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only); +gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b); #endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index af88b21c..3f0bbfe7 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -25,34 +25,60 @@ #include <string.h> #include <arpa/inet.h> +#include <resolv.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "nm-utils.h" +#include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" +#include "nm-ip4-config.h" +#include "ndisc/nm-ndisc.h" #include "introspection/org.freedesktop.NetworkManager.IP6Config.h" /*****************************************************************************/ +static gboolean +_route_valid (const NMPlatformIP6Route *r) +{ + struct in6_addr n; + + return r + && r->plen <= 128 + && (memcmp (&r->network, + nm_utils_ip6_address_clear_host_address (&n, &r->network, r->plen), + sizeof (n)) == 0); +} + +/*****************************************************************************/ + typedef struct { - bool never_default:1; - guint32 mss; int ifindex; int dns_priority; NMSettingIP6ConfigPrivacy privacy; - gint64 route_metric; - struct in6_addr gateway; - GArray *addresses; - GArray *routes; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; GPtrArray *dns_options; GVariant *address_data_variant; GVariant *addresses_variant; + GVariant *route_data_variant; + GVariant *routes_variant; + NMDedupMultiIndex *multi_idx; + const NMPObject *best_default_route; + union { + NMIPConfigDedupMultiIdxType idx_ip6_addresses_; + NMDedupMultiIdxType idx_ip6_addresses; + }; + union { + NMIPConfigDedupMultiIdxType idx_ip6_routes_; + NMDedupMultiIdxType idx_ip6_routes; + }; } NMIP6ConfigPrivate; struct _NMIP6Config { @@ -69,6 +95,7 @@ G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT) #define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG) NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config, + PROP_MULTI_IDX, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, @@ -84,114 +111,122 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config, /*****************************************************************************/ +static void _add_address (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Address *new); +static void _add_route (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Route *new, const NMPObject **out_obj_new); +static const NMDedupMultiEntry *_lookup_route (const NMIP6Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); + +/*****************************************************************************/ + int -nm_ip6_config_get_ifindex (const NMIP6Config *config) +nm_ip6_config_get_ifindex (const NMIP6Config *self) { - return NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex; + return NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex; +} + +NMDedupMultiIndex * +nm_ip6_config_get_multi_idx (const NMIP6Config *self) +{ + return NM_IP6_CONFIG_GET_PRIVATE (self)->multi_idx; } void -nm_ip6_config_set_privacy (NMIP6Config *config, NMSettingIP6ConfigPrivacy privacy) +nm_ip6_config_set_privacy (NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); priv->privacy = privacy; } /*****************************************************************************/ -static void -notify_addresses (NMIP6Config *self) +const NMDedupMultiHeadEntry * +nm_ip6_config_lookup_addresses (const NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - nm_clear_g_variant (&priv->address_data_variant); - nm_clear_g_variant (&priv->addresses_variant); - _notify (self, PROP_ADDRESS_DATA); - _notify (self, PROP_ADDRESSES); + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip6_addresses, + NULL); } -/** - * nm_ip6_config_capture_resolv_conf(): - * @nameservers: array of struct in6_addr - * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf - * - * Reads all resolv.conf IPv6 nameservers and adds them to @nameservers. - * - * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged - */ -gboolean -nm_ip6_config_capture_resolv_conf (GArray *nameservers, - GPtrArray *dns_options, - const char *rc_contents) +void +nm_ip_config_iter_ip6_address_init (NMDedupMultiIter *ipconf_iter, const NMIP6Config *self) { - GPtrArray *read_ns, *read_options; - guint i, j; - gboolean changed = FALSE; - - g_return_val_if_fail (nameservers != NULL, FALSE); + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip6_config_lookup_addresses (self)); +} - read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents); - if (!read_ns) - return FALSE; +/*****************************************************************************/ - for (i = 0; i < read_ns->len; i++) { - const char *s = g_ptr_array_index (read_ns, i); - struct in6_addr ns = IN6ADDR_ANY_INIT; +const NMDedupMultiHeadEntry * +nm_ip6_config_lookup_routes (const NMIP6Config *self) +{ + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - if (!inet_pton (AF_INET6, s, (void *) &ns) || IN6_IS_ADDR_UNSPECIFIED (&ns)) - continue; + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip6_routes, + NULL); +} - /* Ignore duplicates */ - for (j = 0; j < nameservers->len; j++) { - struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, j); +void +nm_ip_config_iter_ip6_route_init (NMDedupMultiIter *ipconf_iter, const NMIP6Config *self) +{ + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip6_config_lookup_routes (self)); +} - if (IN6_ARE_ADDR_EQUAL (t, &ns)) - break; - } +/*****************************************************************************/ - if (j == nameservers->len) { - g_array_append_val (nameservers, ns); - changed = TRUE; - } - } - g_ptr_array_unref (read_ns); +const NMPObject * +nm_ip6_config_best_default_route_get (const NMIP6Config *self) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); - if (dns_options) { - read_options = nm_utils_read_resolv_conf_dns_options (rc_contents); - if (!read_options) - return changed; + return NM_IP6_CONFIG_GET_PRIVATE (self)->best_default_route; +} - for (i = 0; i < read_options->len; i++) { - const char *s = g_ptr_array_index (read_options, i); +const NMPObject * +_nm_ip6_config_best_default_route_find (const NMIP6Config *self) +{ + NMDedupMultiIter ipconf_iter; + const NMPObject *new_best_default_route = NULL; - if (_nm_utils_dns_option_validate (s, NULL, NULL, TRUE, _nm_utils_dns_option_descs) && - _nm_utils_dns_option_find_idx (dns_options, s) < 0) { - g_ptr_array_add (dns_options, g_strdup (s)); - changed = TRUE; - } - } - g_ptr_array_unref (read_options); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, NULL) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, + ipconf_iter.current->obj); } - - return changed; + return new_best_default_route; } -static gboolean -addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +/*****************************************************************************/ + +static void +_notify_addresses (NMIP6Config *self) { - return IN6_ARE_ADDR_EQUAL (&a->address, &b->address); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + nm_clear_g_variant (&priv->address_data_variant); + nm_clear_g_variant (&priv->addresses_variant); + _notify (self, PROP_ADDRESS_DATA); + _notify (self, PROP_ADDRESSES); } -static gboolean -routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_gateway_and_metric) +static void +_notify_routes (NMIP6Config *self) { - return IN6_ARE_ADDR_EQUAL (&a->network, &b->network) && a->plen == b->plen && - ( !consider_gateway_and_metric - || ( IN6_ARE_ADDR_EQUAL (&a->gateway, &b->gateway) - && nm_utils_ip6_route_metric_normalize (a->metric) == nm_utils_ip6_route_metric_normalize (b->metric))); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + nm_assert (priv->best_default_route == _nm_ip6_config_best_default_route_find (self)); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + _notify (self, PROP_ROUTE_DATA); + _notify (self, PROP_ROUTES); } +/*****************************************************************************/ + static gint _addresses_sort_cmp_get_prio (const struct in6_addr *addr) { @@ -210,13 +245,14 @@ _addresses_sort_cmp_get_prio (const struct in6_addr *addr) return 6; } -static gint -_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) +static int +_addresses_sort_cmp (const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, + gboolean prefer_temp) { gint p1, p2, c; gboolean perm1, perm2, tent1, tent2; gboolean ipv6_privacy1, ipv6_privacy2; - const NMPlatformIP6Address *a1 = a, *a2 = b; /* tentative addresses are always sorted back... */ /* sort tentative addresses after non-tentative. */ @@ -235,7 +271,6 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) ipv6_privacy1 = !!(a1->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); ipv6_privacy2 = !!(a2->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY)); if (ipv6_privacy1 || ipv6_privacy2) { - gboolean prefer_temp = ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR; gboolean public1 = TRUE, public2 = TRUE; if (ipv6_privacy1) { @@ -270,30 +305,60 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) return c != 0 ? c : memcmp (a1, a2, sizeof (*a1)); } +static int +_addresses_sort_cmp_prop (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return _addresses_sort_cmp (NMP_OBJECT_CAST_IP6_ADDRESS (*((const NMPObject **) a)), + NMP_OBJECT_CAST_IP6_ADDRESS (*((const NMPObject **) b)), + ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR); +} + +static int +sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer user_data) +{ + const NMPlatformIP6Address *addr_a = NMP_OBJECT_CAST_IP6_ADDRESS (c_list_entry (lst_a, NMDedupMultiEntry, lst_entries)->obj); + const NMPlatformIP6Address *addr_b = NMP_OBJECT_CAST_IP6_ADDRESS (c_list_entry (lst_b, NMDedupMultiEntry, lst_entries)->obj); + + return _addresses_sort_cmp (addr_a, addr_b, + ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR); +} + gboolean -nm_ip6_config_addresses_sort (NMIP6Config *self) +_nmtst_ip6_config_addresses_sort (NMIP6Config *self) { NMIP6ConfigPrivate *priv; - size_t data_len = 0; - char *data_pre = NULL; - gboolean changed; + const NMDedupMultiHeadEntry *head_entry; g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); - priv = NM_IP6_CONFIG_GET_PRIVATE (self); - if (priv->addresses->len > 1) { - data_len = priv->addresses->len * g_array_get_element_size (priv->addresses); - data_pre = g_new (char, data_len); - memcpy (data_pre, priv->addresses->data, data_len); + head_entry = nm_ip6_config_lookup_addresses (self); + if (head_entry && head_entry->len > 1) { + gboolean changed; + gs_free gconstpointer *addresses_old = NULL; + guint naddr, j; + NMDedupMultiIter iter; - g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, - GINT_TO_POINTER (priv->privacy)); + priv = NM_IP6_CONFIG_GET_PRIVATE (self); - changed = memcmp (data_pre, priv->addresses->data, data_len) != 0; - g_free (data_pre); + addresses_old = nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr); + nm_assert (addresses_old); + nm_assert (naddr > 0 && naddr == head_entry->len); + + nm_dedup_multi_head_entry_sort (head_entry, + sort_captured_addresses, + GINT_TO_POINTER (priv->privacy)); + + changed = FALSE; + j = 0; + nm_dedup_multi_iter_for_each (&iter, head_entry) { + nm_assert (j < naddr); + if (iter.current->obj != addresses_old[j++]) + changed = TRUE; + } + nm_assert (j == naddr); if (changed) { - notify_addresses (self); + _notify_addresses (self); return TRUE; } } @@ -301,129 +366,218 @@ nm_ip6_config_addresses_sort (NMIP6Config *self) } NMIP6Config * -nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) +nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) { - NMIP6Config *config; + NMIP6Config *self; NMIP6ConfigPrivate *priv; - guint i; - guint32 lowest_metric = G_MAXUINT32; - struct in6_addr old_gateway = IN6ADDR_ANY_INIT; - gboolean has_gateway = FALSE; - gboolean notify_nameservers = FALSE; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; + gboolean has_addresses = FALSE; + + nm_assert (ifindex > 0); /* Slaves have no IP configuration */ if (nm_platform_link_get_master (platform, ifindex) > 0) return NULL; - config = nm_ip6_config_new (ifindex); - priv = NM_IP6_CONFIG_GET_PRIVATE (config); + self = nm_ip6_config_new (multi_idx, ifindex); + priv = NM_IP6_CONFIG_GET_PRIVATE (self); - g_array_unref (priv->addresses); - g_array_unref (priv->routes); + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex); + if (head_entry) { + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + if (!_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_addresses_, + ifindex, + plobj, + NULL, + FALSE, + TRUE, + NULL, + NULL)) + nm_assert_not_reached (); + has_addresses = TRUE; + } + head_entry = nm_ip6_config_lookup_addresses (self); + nm_assert (head_entry); + nm_dedup_multi_head_entry_sort (head_entry, + sort_captured_addresses, + GINT_TO_POINTER (use_temporary)); + _notify_addresses (self); + } - priv->addresses = nm_platform_ip6_address_get_all (platform, ifindex); - priv->routes = nm_platform_ip6_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex); - /* Extract gateway from default route */ - old_gateway = priv->gateway; - for (i = 0; i < priv->routes->len; ) { - const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) + _add_route (self, plobj, NULL, NULL); - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (route->metric < lowest_metric) { - priv->gateway = route->gateway; - lowest_metric = route->metric; - } - has_gateway = TRUE; - /* Remove the default route from the list */ - g_array_remove_index_fast (priv->routes, i); - continue; + /* If the interface has the default route, and has IPv6 addresses, capture + * nameservers from /etc/resolv.conf. + */ + if ( has_addresses + && priv->best_default_route + && capture_resolv_conf) { + gs_free char *rc_contents = NULL; + + if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) { + if (nm_utils_resolve_conf_parse (AF_INET6, + rc_contents, + priv->nameservers, + priv->dns_options)) + _notify (self, PROP_NAMESERVERS); } - i++; } - /* we detect the route metric based on the default route. All non-default - * routes have their route metrics explicitly set. */ - priv->route_metric = has_gateway ? (gint64) lowest_metric : (gint64) -1; + return self; +} - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - if (has_gateway) { - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); - - if ( route->plen == 128 - && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway) - && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { - g_array_remove_index (priv->routes, i); - i--; +void +nm_ip6_config_add_dependent_routes (NMIP6Config *self, + guint32 route_table, + guint32 route_metric) +{ + const NMIP6ConfigPrivate *priv; + const NMPlatformIP6Address *my_addr; + const NMPlatformIP6Route *my_route; + int ifindex; + NMDedupMultiIter iter; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + ifindex = nm_ip6_config_get_ifindex (self); + g_return_if_fail (ifindex > 0); + + /* For IPv6 addresses received via SLAAC/autoconf, we explicitly add the + * device-routes (onlink) to NMIP6Config. + * + * For manually added IPv6 routes, add the device routes explicitly. */ + + nm_ip_config_iter_ip6_address_for_each (&iter, self, &my_addr) { + NMPlatformIP6Route *route; + gboolean has_peer; + int routes_n, routes_i; + + if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE)) + continue; + + has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->peer_address); + + /* If we have an IPv6 peer, we add two /128 routes + * (unless, both addresses are identical). */ + routes_n = ( has_peer + && !IN6_ARE_ADDR_EQUAL (&my_addr->address, &my_addr->peer_address)) + ? 2 : 1; + + for (routes_i = 0; routes_i < routes_n; routes_i++) { + nm_auto_nmpobj NMPObject *r = NULL; + + r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP6_ROUTE (r); + + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->table_coerced = nm_platform_route_table_coerce (route_table); + route->metric = route_metric; + + if (has_peer) { + if (routes_i == 0) + route->network = my_addr->address; + else + route->network = my_addr->peer_address; + route->plen = 128; + } else { + nm_utils_ip6_address_clear_host_address (&route->network, &my_addr->address, my_addr->plen); + route->plen = my_addr->plen; } + + nm_platform_ip_route_normalize (AF_INET6, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + } else + _add_route (self, r, NULL, NULL); } } - /* If the interface has the default route, and has IPv6 addresses, capture - * nameservers from /etc/resolv.conf. - */ - if (priv->addresses->len && has_gateway && capture_resolv_conf) - notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers, - priv->dns_options, - NULL); - - g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary)); +again: + nm_ip_config_iter_ip6_route_for_each (&iter, self, &my_route) { + NMPlatformIP6Route rt; - /* actually, nobody should be connected to the signal, just to be sure, notify */ - if (notify_nameservers) - _notify (config, PROP_NAMESERVERS); - _notify (config, PROP_ADDRESS_DATA); - _notify (config, PROP_ADDRESSES); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); - if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &old_gateway)) - _notify (config, PROP_GATEWAY); + if ( !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route) + || IN6_IS_ADDR_UNSPECIFIED (&my_route->gateway) + || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rt_source) + || nm_ip6_config_get_direct_route_for_host (self, + &my_route->gateway, + nm_platform_route_table_uncoerce (my_route->table_coerced, TRUE))) + continue; - return config; + rt = *my_route; + rt.network = my_route->gateway; + rt.plen = 128; + rt.gateway = in6addr_any; + _add_route (self, NULL, &rt, NULL); + /* adding the route might have invalidated the iteration. Start again. */ + goto again; + } } gboolean -nm_ip6_config_commit (const NMIP6Config *config, +nm_ip6_config_commit (const NMIP6Config *self, NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync) + NMIPRouteTableSyncMode route_table_sync, + GPtrArray **out_temporary_not_available) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - gboolean success; + gs_unref_ptrarray GPtrArray *addresses = NULL; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + int ifindex; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); + ifindex = nm_ip6_config_get_ifindex (self); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (config != NULL, FALSE); - /* Addresses */ - nm_platform_ip6_address_sync (platform, ifindex, priv->addresses, TRUE); + addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_addresses (self), + NULL, NULL); - /* Routes */ - { - guint i; - guint count = nm_ip6_config_get_num_routes (config); - GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), count); - const NMPlatformIP6Route *route; - - for (i = 0; i < count; i++) { - route = nm_ip6_config_get_route (config, i); - g_array_append_vals (routes, route, 1); - } + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_routes (self), + NULL, NULL); - success = nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync); - g_array_unref (routes); - } + routes_prune = nm_platform_ip_route_get_prune_list (platform, + AF_INET6, + ifindex, + route_table_sync); + + nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); + + if (!nm_platform_ip_route_sync (platform, + AF_INET6, + ifindex, + routes, + routes_prune, + out_temporary_not_available)) + success = FALSE; return success; } static void -merge_route_attributes (NMIPRoute *s_route, NMPlatformIP6Route *r) +merge_route_attributes (NMIPRoute *s_route, + NMPlatformIP6Route *r, + guint32 route_table) { GVariant *variant; + guint32 u32; struct in6_addr addr; #define GET_ATTR(name, field, variant_type, type) \ @@ -431,7 +585,12 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP6Route *r) if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ r->field = g_variant_get_ ## type (variant); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, tos, BYTE, byte); + variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TABLE); + u32 = variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32) + ? g_variant_get_uint32 (variant) + : 0; + r->table_coerced = nm_platform_route_table_coerce (u32 ?: (route_table ?: RT_TABLE_MAIN)); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, window, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, cwnd, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, initcwnd, UINT32, uint32); @@ -472,11 +631,15 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP6Route *r) } void -nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) +nm_ip6_config_merge_setting (NMIP6Config *self, + NMSettingIPConfig *setting, + guint32 route_table, + guint32 route_metric) { NMIP6ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; const char *gateway_str; + struct in6_addr gateway_bin; int i, priority; if (!setting) @@ -484,31 +647,30 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); - priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv = NM_IP6_CONFIG_GET_PRIVATE (self); naddresses = nm_setting_ip_config_get_num_addresses (setting); nroutes = nm_setting_ip_config_get_num_routes (setting); nnameservers = nm_setting_ip_config_get_num_dns (setting); nsearches = nm_setting_ip_config_get_num_dns_searches (setting); - g_object_freeze_notify (G_OBJECT (config)); + g_object_freeze_notify (G_OBJECT (self)); /* Gateway */ - if (nm_setting_ip_config_get_never_default (setting)) - nm_ip6_config_set_never_default (config, TRUE); - else if (nm_setting_ip_config_get_ignore_auto_routes (setting)) - nm_ip6_config_set_never_default (config, FALSE); - gateway_str = nm_setting_ip_config_get_gateway (setting); - if (gateway_str) { - struct in6_addr gateway; + if ( !nm_setting_ip_config_get_never_default (setting) + && (gateway_str = nm_setting_ip_config_get_gateway (setting)) + && inet_pton (AF_INET6, gateway_str, &gateway_bin) == 1 + && !IN6_IS_ADDR_UNSPECIFIED (&gateway_bin)) { + const NMPlatformIP6Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .gateway = gateway_bin, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; - inet_pton (AF_INET6, gateway_str, &gateway); - nm_ip6_config_set_gateway (config, &gateway); + _add_route (self, NULL, &r, NULL); } - if (priv->route_metric == -1) - priv->route_metric = nm_setting_ip_config_get_route_metric (setting); - /* Addresses */ for (i = 0; i < naddresses; i++) { NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i); @@ -522,16 +684,21 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; address.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_address (config, &address); + _add_address (self, NULL, &address); } /* Routes */ if (nm_setting_ip_config_get_ignore_auto_routes (setting)) - nm_ip6_config_reset_routes (config); + nm_ip6_config_reset_routes (self); for (i = 0; i < nroutes; i++) { NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP6Route route; + if (nm_ip_route_get_family (s_route) != AF_INET6) { + nm_assert_not_reached (); + continue; + } + memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); @@ -542,73 +709,74 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu nm_ip_route_get_next_hop_binary (s_route, &route.gateway); if (nm_ip_route_get_metric (s_route) == -1) - route.metric = default_route_metric; + route.metric = route_metric; else route.metric = nm_ip_route_get_metric (s_route); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - merge_route_attributes (s_route, &route); - nm_ip6_config_add_route (config, &route); + nm_utils_ip6_address_clear_host_address (&route.network, &route.network, route.plen); + + merge_route_attributes (s_route, &route, route_table); + _add_route (self, NULL, &route, NULL); } /* DNS */ if (nm_setting_ip_config_get_ignore_auto_dns (setting)) { - nm_ip6_config_reset_nameservers (config); - nm_ip6_config_reset_domains (config); - nm_ip6_config_reset_searches (config); + nm_ip6_config_reset_nameservers (self); + nm_ip6_config_reset_domains (self); + nm_ip6_config_reset_searches (self); } for (i = 0; i < nnameservers; i++) { struct in6_addr ip; if (inet_pton (AF_INET6, nm_setting_ip_config_get_dns (setting, i), &ip) == 1) - nm_ip6_config_add_nameserver (config, &ip); + nm_ip6_config_add_nameserver (self, &ip); } for (i = 0; i < nsearches; i++) - nm_ip6_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i)); + nm_ip6_config_add_search (self, nm_setting_ip_config_get_dns_search (setting, i)); i = 0; while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) { - nm_ip6_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i)); + nm_ip6_config_add_dns_option (self, nm_setting_ip_config_get_dns_option (setting, i)); i++; } priority = nm_setting_ip_config_get_dns_priority (setting); if (priority) - nm_ip6_config_set_dns_priority (config, priority); + nm_ip6_config_set_dns_priority (self, priority); - g_object_thaw_notify (G_OBJECT (config)); + g_object_thaw_notify (G_OBJECT (self)); } NMSetting * -nm_ip6_config_create_setting (const NMIP6Config *config) +nm_ip6_config_create_setting (const NMIP6Config *self) { + const NMIP6ConfigPrivate *priv; NMSettingIPConfig *s_ip6; - const struct in6_addr *gateway; - guint naddresses, nroutes, nnameservers, nsearches, noptions; + guint nnameservers, nsearches, noptions; const char *method = NULL; int i; - gint64 route_metric; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); - if (!config) { + if (!self) { g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); return NM_SETTING (s_ip6); } - gateway = nm_ip6_config_get_gateway (config); - naddresses = nm_ip6_config_get_num_addresses (config); - nroutes = nm_ip6_config_get_num_routes (config); - nnameservers = nm_ip6_config_get_num_nameservers (config); - nsearches = nm_ip6_config_get_num_searches (config); - noptions = nm_ip6_config_get_num_dns_options (config); - route_metric = nm_ip6_config_get_route_metric (config); + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + nnameservers = nm_ip6_config_get_num_nameservers (self); + nsearches = nm_ip6_config_get_num_searches (self); + noptions = nm_ip6_config_get_num_dns_options (self); /* Addresses */ - for (i = 0; i < naddresses; i++) { - const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) { NMIPAddress *s_addr; /* Ignore link-local address. */ @@ -634,10 +802,12 @@ nm_ip6_config_create_setting (const NMIP6Config *config) } /* Gateway */ - if ( gateway + if ( priv->best_default_route && nm_setting_ip_config_get_num_addresses (s_ip6) > 0) { g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet6_ntop (gateway, NULL), + NM_SETTING_IP_CONFIG_GATEWAY, + nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway, + NULL), NULL); } @@ -647,20 +817,17 @@ nm_ip6_config_create_setting (const NMIP6Config *config) g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, - NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric, NULL); /* Routes */ - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) { NMIPRoute *s_route; /* Ignore link-local route. */ if (IN6_IS_ADDR_LINKLOCAL (&route->network)) continue; - /* Ignore default route. */ - if (!route->plen) + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; /* Ignore routes provided by external sources */ @@ -677,24 +844,24 @@ nm_ip6_config_create_setting (const NMIP6Config *config) /* DNS */ for (i = 0; i < nnameservers; i++) { - const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (config, i); + const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (self, i); nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, NULL)); } for (i = 0; i < nsearches; i++) { - const char *search = nm_ip6_config_get_search (config, i); + const char *search = nm_ip6_config_get_search (self, i); nm_setting_ip_config_add_dns_search (s_ip6, search); } for (i = 0; i < noptions; i++) { - const char *option = nm_ip6_config_get_dns_option (config, i); + const char *option = nm_ip6_config_get_dns_option (self, i); nm_setting_ip_config_add_dns_option (s_ip6, option); } g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DNS_PRIORITY, - nm_ip6_config_get_dns_priority (config), + nm_ip6_config_get_dns_priority (self), NULL); return NM_SETTING (s_ip6); @@ -703,11 +870,16 @@ nm_ip6_config_create_setting (const NMIP6Config *config) /*****************************************************************************/ void -nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags) +nm_ip6_config_merge (NMIP6Config *dst, + const NMIP6Config *src, + NMIPConfigMergeFlags merge_flags, + guint32 default_route_metric_penalty) { NMIP6ConfigPrivate *dst_priv; const NMIP6ConfigPrivate *src_priv; guint32 i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address = NULL; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -718,8 +890,8 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) - nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i)); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &address) + _add_address (dst, NMP_OBJECT_UP_CAST (address), NULL); /* nameservers */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -727,20 +899,25 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i)); } - /* default gateway */ - if (nm_ip6_config_get_gateway (src)) - nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src)); - /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) - nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); - } + const NMPlatformIP6Route *r_src; + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r_src) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r_src)) { + if (NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES)) + continue; + if (default_route_metric_penalty) { + NMPlatformIP6Route r = *r_src; - if (dst_priv->route_metric == -1) - dst_priv->route_metric = src_priv->route_metric; - else if (src_priv->route_metric != -1) - dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric); + r.metric = nm_utils_ip_route_metric_penalize (AF_INET6, r.metric, default_route_metric_penalty); + _add_route (dst, NULL, &r, NULL); + continue; + } + } + _add_route (dst, ipconf_iter.current->obj, NULL, NULL); + } + } /* domains */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -760,9 +937,6 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i)); } - if (nm_ip6_config_get_mss (src)) - nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src)); - /* DNS priority */ if (nm_ip6_config_get_dns_priority (src)) nm_ip6_config_set_dns_priority (dst, nm_ip6_config_get_dns_priority (src)); @@ -770,45 +944,9 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl g_object_thaw_notify (G_OBJECT (dst)); } -gboolean -nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6_addr *network, guint8 plen) -{ - guint num = nm_ip6_config_get_num_addresses (config); - guint i; - - nm_assert (network); - nm_assert (plen <= 128); - - for (i = 0; i < num; i++) { - const NMPlatformIP6Address *item = nm_ip6_config_get_address (config, i); - - if ( item->plen <= plen - && !NM_FLAGS_HAS (item->n_ifa_flags, IFA_F_NOPREFIXROUTE) - && nm_utils_ip6_address_same_prefix (&item->address, network, item->plen)) - return TRUE; - } - - return FALSE; -} - /*****************************************************************************/ static int -_addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->addresses->len; i++) { - const NMPlatformIP6Address *a = &g_array_index (priv->addresses, NMPlatformIP6Address, i); - - if (addresses_are_duplicate (a, addr)) - return (int) i; - } - return -1; -} - -static int _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns) { const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); @@ -824,21 +962,6 @@ _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns) } static int -_routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP6Route *r = &g_array_index (priv->routes, NMPlatformIP6Route, i); - - if (routes_are_duplicate (route, r, FALSE)) - return (int) i; - } - return -1; -} - -static int _domains_get_index (const NMIP6Config *self, const char *domain) { const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); @@ -889,27 +1012,45 @@ _dns_options_get_index (const NMIP6Config *self, const char *option) * nm_ip6_config_subtract: * @dst: config from which to remove everything in @src * @src: config to remove from @dst - * + * @default_route_metric_penalty: pretend that on source we applied + * a route penalty on the default-route. It means, for default routes + * we don't remove routes that match exactly, but those with a lower + * metric (with the penalty removed). +* * Removes everything in @src from @dst. */ void -nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) +nm_ip6_config_subtract (NMIP6Config *dst, + const NMIP6Config *src, + guint32 default_route_metric_penalty) { + NMIP6ConfigPrivate *dst_priv; guint i; gint idx; - const struct in6_addr *dst_tmp, *src_tmp; + const NMPlatformIP6Address *a; + const NMPlatformIP6Route *r; + NMDedupMultiIter ipconf_iter; + gboolean changed; + gboolean changed_default_route; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) { - idx = _addresses_get_index (dst, nm_ip6_config_get_address (src, i)); - if (idx >= 0) - nm_ip6_config_del_address (dst, idx); + changed = FALSE; + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &a) { + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_addresses, + NMP_OBJECT_UP_CAST (a), + NULL)) + changed = TRUE; } + if (changed) + _notify_addresses (dst); /* nameservers */ for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) { @@ -918,23 +1059,46 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) nm_ip6_config_del_nameserver (dst, idx); } - /* default gateway */ - src_tmp = nm_ip6_config_get_gateway (src); - dst_tmp = nm_ip6_config_get_gateway (dst); - if (src_tmp && dst_tmp && IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp)) - nm_ip6_config_set_gateway (dst, NULL); - - if (!nm_ip6_config_get_num_addresses (dst)) - nm_ip6_config_set_gateway (dst, NULL); - - /* ignore route_metric */ - /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) { - idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i)); - if (idx >= 0) - nm_ip6_config_del_route (dst, idx); + changed = FALSE; + changed_default_route = FALSE; + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) { + const NMPObject *o_src = NMP_OBJECT_UP_CAST (r); + NMPObject o_lookup_copy; + const NMPObject *o_lookup; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP6Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When subtracting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_src); + rr = NMP_OBJECT_CAST_IP6_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET6, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_src; + + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_routes, + o_lookup, + (gconstpointer *) &obj_old)) { + if (dst_priv->best_default_route == obj_old) { + nm_clear_nmp_object (&dst_priv->best_default_route); + changed_default_route = TRUE; + } + changed = TRUE; + } + } + if (changed_default_route) { + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, + _nm_ip6_config_best_default_route_find (dst)); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* domains */ for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) { @@ -957,9 +1121,6 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) nm_ip6_config_del_dns_option (dst, idx); } - if (nm_ip6_config_get_mss (src) == nm_ip6_config_get_mss (dst)) - nm_ip6_config_set_mss (dst, 0); - /* DNS priority */ if (nm_ip6_config_get_dns_priority (src) == nm_ip6_config_get_dns_priority (dst)) nm_ip6_config_set_dns_priority (dst, 0); @@ -968,51 +1129,87 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) } void -nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) +nm_ip6_config_intersect (NMIP6Config *dst, + const NMIP6Config *src, + guint32 default_route_metric_penalty) { - guint i; - gint idx; - const struct in6_addr *dst_tmp, *src_tmp; + NMIP6ConfigPrivate *dst_priv; + const NMIP6ConfigPrivate *src_priv; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *a; + const NMPlatformIP6Route *r; + gboolean changed; + const NMPObject *new_best_default_route; - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); + g_return_if_fail (src); + g_return_if_fail (dst); + + dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); + src_priv = NM_IP6_CONFIG_GET_PRIVATE (src); g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ - for (i = 0; i < nm_ip6_config_get_num_addresses (dst); ) { - idx = _addresses_get_index (src, nm_ip6_config_get_address (dst, i)); - if (idx < 0) - nm_ip6_config_del_address (dst, i); - else - i++; + changed = FALSE; + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, dst, &a) { + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip6_addresses, + NMP_OBJECT_UP_CAST (a))) + continue; + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; } + if (changed) + _notify_addresses (dst); - /* ignore route_metric */ /* ignore nameservers */ - /* default gateway */ - dst_tmp = nm_ip6_config_get_gateway (dst); - if (dst_tmp) { - src_tmp = nm_ip6_config_get_gateway (src); - if ( !nm_ip6_config_get_num_addresses (dst) - || !src_tmp - || !IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp)) - nm_ip6_config_set_gateway (dst, NULL); - } - /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (dst); ) { - idx = _routes_get_index (src, nm_ip6_config_get_route (dst, i)); - if (idx < 0) - nm_ip6_config_del_route (dst, i); - else - i++; + changed = FALSE; + new_best_default_route = NULL; + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) { + const NMPObject *o_dst = NMP_OBJECT_UP_CAST (r); + const NMPObject *o_lookup; + NMPObject o_lookup_copy; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP6Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When intersecting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_dst); + rr = NMP_OBJECT_CAST_IP6_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET6, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_dst; + + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip6_routes, + o_lookup)) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, o_dst); + continue; + } + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; + } + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) { + nm_assert (changed); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* ignore domains */ /* ignore dns searches */ - /* ignome dns options */ + /* ignore dns options */ g_object_thaw_notify (G_OBJECT (dst)); } @@ -1041,8 +1238,9 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev guint i, num; NMIP6ConfigPrivate *dst_priv; const NMIP6ConfigPrivate *src_priv; - const NMPlatformIP6Address *dst_addr, *src_addr; - const NMPlatformIP6Route *dst_route, *src_route; + NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; + const NMDedupMultiHeadEntry *head_entry_src; + const NMPObject *new_best_default_route; g_return_val_if_fail (NM_IS_IP6_CONFIG (src), FALSE); g_return_val_if_fail (NM_IS_IP6_CONFIG (dst), FALSE); @@ -1055,6 +1253,8 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); src_priv = NM_IP6_CONFIG_GET_PRIVATE (src); + g_return_val_if_fail (src_priv->ifindex > 0, FALSE); + g_object_freeze_notify (G_OBJECT (dst)); /* ifindex */ @@ -1063,70 +1263,107 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev has_minor_changes = TRUE; } - /* never_default */ - if (src_priv->never_default != dst_priv->never_default) { - dst_priv->never_default = src_priv->never_default; - has_minor_changes = TRUE; - } - - /* default gateway */ - if (!IN6_ARE_ADDR_EQUAL (&src_priv->gateway, &dst_priv->gateway)) { - nm_ip6_config_set_gateway (dst, &src_priv->gateway); - has_relevant_changes = TRUE; - } - - if (src_priv->route_metric != dst_priv->route_metric) { - dst_priv->route_metric = src_priv->route_metric; - has_minor_changes = TRUE; - } - /* addresses */ - num = nm_ip6_config_get_num_addresses (src); - are_equal = num == nm_ip6_config_get_num_addresses (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip6_address_cmp (src_addr = nm_ip6_config_get_address (src, i), - dst_addr = nm_ip6_config_get_address (dst, i))) { - are_equal = FALSE; - if ( !addresses_are_duplicate (src_addr, dst_addr) - || src_addr->plen != dst_addr->plen - || !IN6_ARE_ADDR_EQUAL (nm_platform_ip6_address_get_peer (src_addr), - nm_platform_ip6_address_get_peer (dst_addr))) { - has_relevant_changes = TRUE; - break; - } + head_entry_src = nm_ip6_config_lookup_addresses (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip6_address_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP6Address *r_src = NULL; + const NMPlatformIP6Address *r_dst = NULL; + + has = nm_ip_config_iter_ip6_address_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip6_address_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip6_address_cmp (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( !IN6_ARE_ADDR_EQUAL (&r_src->address, &r_dst->address) + || r_src->plen != r_dst->plen + || !IN6_ARE_ADDR_EQUAL (nm_platform_ip6_address_get_peer (r_src), + nm_platform_ip6_address_get_peer (r_dst))) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip6_config_reset_addresses (dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i)); has_minor_changes = TRUE; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_addresses); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_addresses_, + dst_priv->ifindex, + ipconf_iter_src.current->obj, + NULL, + FALSE, + TRUE, + NULL, + NULL); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_addresses, FALSE); + _notify_addresses (dst); } /* routes */ - num = nm_ip6_config_get_num_routes (src); - are_equal = num == nm_ip6_config_get_num_routes (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip6_route_cmp (src_route = nm_ip6_config_get_route (src, i), - dst_route = nm_ip6_config_get_route (dst, i))) { - are_equal = FALSE; - if (!routes_are_duplicate (src_route, dst_route, TRUE)) { - has_relevant_changes = TRUE; - break; - } + head_entry_src = nm_ip6_config_lookup_routes (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip6_route_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP6Route *r_src = NULL; + const NMPlatformIP6Route *r_dst = NULL; + + has = nm_ip_config_iter_ip6_route_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip6_route_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip6_route_cmp_full (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( r_src->plen != r_dst->plen + || !nm_utils_ip6_address_same_prefix (&r_src->network, &r_dst->network, r_src->plen) + || r_src->metric != r_dst->metric + || !IN6_ARE_ADDR_EQUAL (&r_src->gateway, &r_dst->gateway)) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip6_config_reset_routes (dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); has_minor_changes = TRUE; + new_best_default_route = NULL; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + const NMPObject *o = ipconf_iter_src.current->obj; + const NMPObject *obj_new; + + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_routes_, + dst_priv->ifindex, + o, + NULL, + FALSE, + TRUE, + NULL, + &obj_new); + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, FALSE); + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + _notify (dst, PROP_GATEWAY); + _notify_routes (dst); } /* nameservers */ @@ -1205,12 +1442,6 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev has_relevant_changes = TRUE; } - /* mss */ - if (src_priv->mss != dst_priv->mss) { - nm_ip6_config_set_mss (dst, src_priv->mss); - has_minor_changes = TRUE; - } - /* DNS priority */ if (src_priv->dns_priority != dst_priv->dns_priority) { nm_ip6_config_set_dns_priority (dst, src_priv->dns_priority); @@ -1237,227 +1468,238 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev } void -nm_ip6_config_dump (const NMIP6Config *config, const char *detail) +nm_ip6_config_dump (const NMIP6Config *self, const char *detail) { const struct in6_addr *tmp; guint32 i; const char *str; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; - g_return_if_fail (config != NULL); + g_return_if_fail (self != NULL); - g_message ("--------- NMIP6Config %p (%s)", config, detail); + g_message ("--------- NMIP6Config %p (%s)", self, detail); - str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config)); + str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); if (str) g_message (" path: %s", str); /* addresses */ - for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++) - g_message (" a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0)); - - /* default gateway */ - tmp = nm_ip6_config_get_gateway (config); - if (tmp) - g_message (" gw: %s", nm_utils_inet6_ntop (tmp, NULL)); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) + g_message (" a: %s", nm_platform_ip6_address_to_string (address, NULL, 0)); /* nameservers */ - for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++) { - tmp = nm_ip6_config_get_nameserver (config, i); + for (i = 0; i < nm_ip6_config_get_num_nameservers (self); i++) { + tmp = nm_ip6_config_get_nameserver (self, i); g_message (" ns: %s", nm_utils_inet6_ntop (tmp, NULL)); } /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0)); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) + g_message (" rt: %s", nm_platform_ip6_route_to_string (route, NULL, 0)); /* domains */ - for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) - g_message (" domain: %s", nm_ip6_config_get_domain (config, i)); + for (i = 0; i < nm_ip6_config_get_num_domains (self); i++) + g_message (" domain: %s", nm_ip6_config_get_domain (self, i)); /* dns searches */ - for (i = 0; i < nm_ip6_config_get_num_searches (config); i++) - g_message (" search: %s", nm_ip6_config_get_search (config, i)); + for (i = 0; i < nm_ip6_config_get_num_searches (self); i++) + g_message (" search: %s", nm_ip6_config_get_search (self, i)); /* dns options */ - for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++) - g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (config, i)); + for (i = 0; i < nm_ip6_config_get_num_dns_options (self); i++) + g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (self, i)); - g_message (" dnspri: %d", nm_ip6_config_get_dns_priority (config)); - - g_message (" mss: %"G_GUINT32_FORMAT, nm_ip6_config_get_mss (config)); - g_message (" n-dflt: %d", nm_ip6_config_get_never_default (config)); + g_message (" dnspri: %d", nm_ip6_config_get_dns_priority (self)); } /*****************************************************************************/ void -nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default) +nm_ip6_config_reset_addresses_ndisc (NMIP6Config *self, + const NMNDiscAddress *addresses, + guint addresses_n, + guint8 plen, + guint32 ifa_flags) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - - priv->never_default = never_default; -} + NMIP6ConfigPrivate *priv; + guint i; + gboolean changed = FALSE; -gboolean -nm_ip6_config_get_never_default (const NMIP6Config *config) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + g_return_if_fail (NM_IS_IP6_CONFIG (self)); - return priv->never_default; -} + priv = NM_IP6_CONFIG_GET_PRIVATE (self); -void -nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *gateway) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + g_return_if_fail (priv->ifindex > 0); - if (gateway) { - if (IN6_ARE_ADDR_EQUAL (&priv->gateway, gateway)) - return; - priv->gateway = *gateway; - } else { - if (IN6_IS_ADDR_UNSPECIFIED (&priv->gateway)) - return; - memset (&priv->gateway, 0, sizeof (priv->gateway)); + nm_dedup_multi_index_dirty_set_idx (priv->multi_idx, &priv->idx_ip6_addresses); + + for (i = 0; i < addresses_n; i++) { + const NMNDiscAddress *ndisc_addr = &addresses[i]; + NMPObject obj; + NMPlatformIP6Address *a; + + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + a = NMP_OBJECT_CAST_IP6_ADDRESS (&obj); + a->ifindex = priv->ifindex; + a->address = ndisc_addr->address; + a->plen = plen; + a->timestamp = ndisc_addr->timestamp; + a->lifetime = ndisc_addr->lifetime; + a->preferred = MIN (ndisc_addr->lifetime, ndisc_addr->preferred); + a->addr_source = NM_IP_CONFIG_SOURCE_NDISC; + a->n_ifa_flags = ifa_flags; + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_addresses_, + priv->ifindex, + &obj, + NULL, + FALSE, + TRUE, + NULL, + NULL)) + changed = TRUE; } - _notify (config, PROP_GATEWAY); -} -const struct in6_addr * -nm_ip6_config_get_gateway (const NMIP6Config *config) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_addresses, FALSE) > 0) + changed = TRUE; - return IN6_IS_ADDR_UNSPECIFIED (&priv->gateway) ? NULL : &priv->gateway; + if (changed) + _notify_addresses (self); } -gint64 -nm_ip6_config_get_route_metric (const NMIP6Config *config) +void +nm_ip6_config_reset_addresses (NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - return priv->route_metric; + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip6_addresses) > 0) + _notify_addresses (self); } -/*****************************************************************************/ - -void -nm_ip6_config_reset_addresses (NMIP6Config *config) +static void +_add_address (NMIP6Config *self, + const NMPObject *obj_new, + const NMPlatformIP6Address *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - if (priv->addresses->len != 0) { - g_array_set_size (priv->addresses, 0); - notify_addresses (config); - } + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_addresses_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + NULL, + NULL)) + _notify_addresses (self); } /** * nm_ip6_config_add_address: - * @config: the #NMIP6Config - * @new: the new address to add to @config + * @self: the #NMIP6Config + * @new: the new address to add to @self * - * Adds the new address to @config. If an address with the same basic properties - * (address, prefix) already exists in @config, it is overwritten with the + * Adds the new address to @self. If an address with the same basic properties + * (address, prefix) already exists in @self, it is overwritten with the * lifetime and preferred of @new. The source is also overwritten by the source * from @new if that source is higher priority. */ void -nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new) +nm_ip6_config_add_address (NMIP6Config *self, const NMPlatformIP6Address *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - NMPlatformIP6Address item_old; - int i; - - g_return_if_fail (new != NULL); - - for (i = 0; i < priv->addresses->len; i++ ) { - NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i); - - if (addresses_are_duplicate (item, new)) { - if (nm_platform_ip6_address_cmp (item, new) == 0) - return; - - /* remember the old values. */ - item_old = *item; - /* Copy over old item to get new lifetime, timestamp, preferred */ - *item = *new; - - /* But restore highest priority source */ - item->addr_source = MAX (item_old.addr_source, new->addr_source); - - /* for addresses that we read from the kernel, we keep the timestamps as defined - * by the previous source (item_old). The reason is, that the other source configured the lifetimes - * with "what should be" and the kernel values are "what turned out after configuring it". - * - * For other sources, the longer lifetime wins. */ - if ( (new->addr_source == NM_IP_CONFIG_SOURCE_KERNEL && new->addr_source != item_old.addr_source) - || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) { - item->timestamp = item_old.timestamp; - item->lifetime = item_old.lifetime; - item->preferred = item_old.preferred; - } - if (nm_platform_ip6_address_cmp (&item_old, item) == 0) - return; - goto NOTIFY; - } - } + g_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen > 0 && new->plen <= 128); + g_return_if_fail (NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex > 0); - g_array_append_val (priv->addresses, *new); -NOTIFY: -notify_addresses (config); + _add_address (self, NULL, new); } void -nm_ip6_config_del_address (NMIP6Config *config, guint i) +_nmtst_ip6_config_del_address (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMPlatformIP6Address *a; - g_return_if_fail (i < priv->addresses->len); + a = _nmtst_ip6_config_get_address (self, i); + if (!nm_ip6_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (a))) + g_assert_not_reached (); +} - g_array_remove_index (priv->addresses, i); +guint +nm_ip6_config_get_num_addresses (const NMIP6Config *self) +{ + const NMDedupMultiHeadEntry *head_entry; - notify_addresses (config); + head_entry = nm_ip6_config_lookup_addresses (self); + return head_entry ? head_entry->len : 0; } -guint -nm_ip6_config_get_num_addresses (const NMIP6Config *config) +const NMPlatformIP6Address * +nm_ip6_config_get_first_address (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP6Address *a = NULL; - return priv->addresses->len; + nm_ip_config_iter_ip6_address_for_each (&iter, self, &a) + return a; + return NULL; } const NMPlatformIP6Address * -nm_ip6_config_get_address (const NMIP6Config *config, guint i) +_nmtst_ip6_config_get_address (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP6Address *a = NULL; + guint j; - return &g_array_index (priv->addresses, NMPlatformIP6Address, i); + j = 0; + nm_ip_config_iter_ip6_address_for_each (&iter, self, &a) { + if (i == j) + return a; + j++; + } + g_return_val_if_reached (NULL); } -gboolean -nm_ip6_config_address_exists (const NMIP6Config *config, - const NMPlatformIP6Address *needle) +const NMPlatformIP6Address * +nm_ip6_config_lookup_address (const NMIP6Config *self, + const struct in6_addr *addr) { - return _addresses_get_index (config, needle) >= 0; + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + NMPObject obj_stack; + const NMDedupMultiEntry *entry; + + nmp_object_stackinit_id_ip6_address (&obj_stack, + priv->ifindex, + addr); + entry = nm_dedup_multi_index_lookup_obj (priv->multi_idx, + &priv->idx_ip6_addresses, + &obj_stack); + return entry + ? NMP_OBJECT_CAST_IP6_ADDRESS (entry->obj) + : NULL; } const NMPlatformIP6Address * -nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal) +nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal) { const NMIP6ConfigPrivate *priv; - guint i; + const NMPlatformIP6Address *addr; + NMDedupMultiIter iter; - g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); - priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv = NM_IP6_CONFIG_GET_PRIVATE (self); linklocal = !!linklocal; - for (i = 0; i < priv->addresses->len; i++) { - const NMPlatformIP6Address *addr = &g_array_index (priv->addresses, NMPlatformIP6Address, i); - + nm_ip_config_iter_ip6_address_for_each (&iter, self, &addr) { if ( ((!!IN6_IS_ADDR_LINKLOCAL (&addr->address)) == linklocal) && !(addr->n_ifa_flags & IFA_F_TENTATIVE)) return addr; @@ -1482,23 +1724,16 @@ gboolean nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, const NMIP6Config *candidates) { + NMDedupMultiIter ipconf_iter; const NMPlatformIP6Address *addr, *addr_c; - guint i, j, num, num_c; - num = nm_ip6_config_get_num_addresses (self); - - for (i = 0; i < num; i++) { - addr = nm_ip6_config_get_address (self, i); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &addr) { if ( NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TENTATIVE) && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED) && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { - - num_c = nm_ip6_config_get_num_addresses (candidates); - - for (j = 0; j < num_c; j++) { - addr_c = nm_ip6_config_get_address (candidates, j); - if ( addresses_are_duplicate (addr, addr_c) - && addr->plen == addr_c->plen) + addr_c = nm_ip6_config_lookup_address (candidates, &addr->address); + if (addr_c) { + if (addr->plen == addr_c->plen) return TRUE; } } @@ -1509,107 +1744,264 @@ nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMIP6Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMIP6ConfigPrivate *priv; + + nm_assert (NM_IS_IP6_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip6_routes_, + needle, + cmp_type); +} + +void +nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, + const NMNDiscGateway *gateways, + guint gateways_n, + const NMNDiscRoute *routes, + guint routes_n, + guint32 route_table, + guint32 route_metric, + gboolean kernel_support_rta_pref) +{ + NMIP6ConfigPrivate *priv; + guint i; + gboolean changed = FALSE; + const NMPObject *new_best_default_route; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + g_return_if_fail (priv->ifindex > 0); + + nm_dedup_multi_index_dirty_set_idx (priv->multi_idx, &priv->idx_ip6_routes); + + new_best_default_route = NULL; + for (i = 0; i < routes_n; i++) { + const NMNDiscRoute *ndisc_route = &routes[i]; + NMPObject obj; + const NMPObject *obj_new; + NMPlatformIP6Route *r; + + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + r = NMP_OBJECT_CAST_IP6_ROUTE (&obj); + r->ifindex = priv->ifindex; + r->network = ndisc_route->network; + r->plen = ndisc_route->plen; + r->gateway = ndisc_route->gateway; + r->rt_source = NM_IP_CONFIG_SOURCE_NDISC; + r->table_coerced = nm_platform_route_table_coerce (route_table); + r->metric = route_metric; + r->rt_pref = ndisc_route->preference; + nm_assert ((NMIcmpv6RouterPref) r->rt_pref == ndisc_route->preference); + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_routes_, + priv->ifindex, + &obj, + NULL, + FALSE, + TRUE, + NULL, + &obj_new)) + changed = TRUE; + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); + } + + if (gateways_n) { + const NMPObject *obj_new; + NMPlatformIP6Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_NDISC, + .ifindex = priv->ifindex, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + const NMIcmpv6RouterPref first_pref = gateways[0].preference; + + for (i = 0; i < gateways_n; i++) { + r.gateway = gateways[i].address; + r.rt_pref = gateways[i].preference; + nm_assert ((NMIcmpv6RouterPref) r.rt_pref == gateways[i].preference); + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_routes_, + priv->ifindex, + NULL, + (const NMPlatformObject *) &r, + FALSE, + TRUE, + NULL, + &obj_new)) + changed = TRUE; + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); + + if ( first_pref != gateways[i].preference + && !kernel_support_rta_pref) { + /* We are unable to configure a router preference. Hence, we skip all gateways + * with a different preference from the first gateway. Note, that the gateways + * are sorted in order of highest to lowest preference. */ + break; + } + } + } + + if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_routes, FALSE) > 0) + changed = TRUE; + + if (_nm_ip_config_best_default_route_set (&priv->best_default_route, new_best_default_route)) { + changed = TRUE; + _notify (self, PROP_GATEWAY); + } + + if (changed) + _notify_routes (self); +} + void -nm_ip6_config_reset_routes (NMIP6Config *config) +nm_ip6_config_reset_routes (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - if (priv->routes->len != 0) { - g_array_set_size (priv->routes, 0); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip6_routes) > 0) { + if (nm_clear_nmp_object (&priv->best_default_route)) + _notify (self, PROP_GATEWAY); + _notify_routes (self); } } +static void +_add_route (NMIP6Config *self, + const NMPObject *obj_new, + const NMPlatformIP6Route *new, + const NMPObject **out_obj_new) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + const NMPObject *obj_new_2; + + nm_assert ((!new) != (!obj_new)); + nm_assert (!new || _route_valid (new)); + nm_assert (!obj_new || _route_valid (NMP_OBJECT_CAST_IP6_ROUTE (obj_new))); + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_routes_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + &obj_old, + &obj_new_2)) { + gboolean changed_default_route = FALSE; + + if ( priv->best_default_route == obj_old + && obj_old != obj_new_2) { + changed_default_route = TRUE; + nm_clear_nmp_object (&priv->best_default_route); + } + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); + if (_nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2)) + changed_default_route = TRUE; + + if (changed_default_route) + _notify (self, PROP_GATEWAY); + _notify_routes (self); + } else + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); +} + /** * nm_ip6_config_add_route: - * @config: the #NMIP6Config - * @new: the new route to add to @config + * @self: the #NMIP6Config + * @new: the new route to add to @self + * @out_obj_new: (allow-none): (out): the added route object. Must be unrefed + * by caller. * - * Adds the new route to @config. If a route with the same basic properties - * (network, prefix) already exists in @config, it is overwritten including the + * Adds the new route to @self. If a route with the same basic properties + * (network, prefix) already exists in @self, it is overwritten including the * gateway and metric of @new. The source is also overwritten by the source * from @new if that source is higher priority. */ void -nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new) +nm_ip6_config_add_route (NMIP6Config *self, + const NMPlatformIP6Route *new, + const NMPObject **out_obj_new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - NMIPConfigSource old_source; - int i; - - g_return_if_fail (new != NULL); - g_return_if_fail (new->plen > 0 && new->plen <= 128); - g_return_if_fail (priv->ifindex > 0); + g_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen <= 128); + g_return_if_fail (NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex > 0); - for (i = 0; i < priv->routes->len; i++ ) { - NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i); - - if (routes_are_duplicate (item, new, FALSE)) { - if (nm_platform_ip6_route_cmp (item, new) == 0) - return; - old_source = item->rt_source; - *item = *new; - /* Restore highest priority source */ - item->rt_source = MAX (old_source, new->rt_source); - item->ifindex = priv->ifindex; - goto NOTIFY; - } - } - - g_array_append_val (priv->routes, *new); - g_array_index (priv->routes, NMPlatformIP6Route, priv->routes->len - 1).ifindex = priv->ifindex; -NOTIFY: - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + _add_route (self, NULL, new, out_obj_new); } void -nm_ip6_config_del_route (NMIP6Config *config, guint i) +_nmtst_ip6_config_del_route (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - - g_return_if_fail (i < priv->routes->len); + const NMPlatformIP6Route *r; - g_array_remove_index (priv->routes, i); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + r = _nmtst_ip6_config_get_route (self, i); + if (!nm_ip6_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (r))) + g_assert_not_reached (); } guint -nm_ip6_config_get_num_routes (const NMIP6Config *config) +nm_ip6_config_get_num_routes (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->routes->len; + head_entry = nm_ip6_config_lookup_routes (self); + nm_assert (!head_entry || head_entry->len == c_list_length (&head_entry->lst_entries_head)); + return head_entry ? head_entry->len : 0; } const NMPlatformIP6Route * -nm_ip6_config_get_route (const NMIP6Config *config, guint i) +_nmtst_ip6_config_get_route (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP6Route *r = NULL; + guint j; - return &g_array_index (priv->routes, NMPlatformIP6Route, i); + j = 0; + nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) { + if (i == j) + return r; + j++; + } + g_return_val_if_reached (NULL); } const NMPlatformIP6Route * -nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host) +nm_ip6_config_get_direct_route_for_host (const NMIP6Config *self, + const struct in6_addr *host, + guint32 route_table) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP6Route *best_route = NULL; + const NMPlatformIP6Route *best_route = NULL; + const NMPlatformIP6Route *item; + NMDedupMultiIter ipconf_iter; g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL); - for (i = 0; i < priv->routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i); - + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &item) { if (!IN6_IS_ADDR_UNSPECIFIED (&item->gateway)) continue; if (best_route && best_route->plen > item->plen) continue; + if (nm_platform_route_table_uncoerce (item->table_coerced, TRUE) != route_table) + continue; + if (!nm_utils_ip6_address_same_prefix (host, &item->network, item->plen)) continue; @@ -1619,23 +2011,20 @@ nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct best_route = item; } - return best_route; } const NMPlatformIP6Address * -nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host) +nm_ip6_config_get_subnet_for_host (const NMIP6Config *self, const struct in6_addr *host) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP6Address *subnet = NULL; + NMDedupMultiIter iter; + const NMPlatformIP6Address *item; + const NMPlatformIP6Address *subnet = NULL; struct in6_addr subnet2, host2; g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL); - for (i = 0; i < priv->addresses->len; i++) { - NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i); - + nm_ip_config_iter_ip6_address_for_each (&iter, self, &item) { if (subnet && subnet->plen >= item->plen) continue; @@ -1653,20 +2042,20 @@ nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_a /*****************************************************************************/ void -nm_ip6_config_reset_nameservers (NMIP6Config *config) +nm_ip6_config_reset_nameservers (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (priv->nameservers->len != 0) { g_array_set_size (priv->nameservers, 0); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } } void -nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *new) +nm_ip6_config_add_nameserver (NMIP6Config *self, const struct in6_addr *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != NULL); @@ -1676,32 +2065,32 @@ nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *new) return; g_array_append_val (priv->nameservers, *new); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } void -nm_ip6_config_del_nameserver (NMIP6Config *config, guint i) +nm_ip6_config_del_nameserver (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->nameservers->len); g_array_remove_index (priv->nameservers, i); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } guint -nm_ip6_config_get_num_nameservers (const NMIP6Config *config) +nm_ip6_config_get_num_nameservers (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return priv->nameservers->len; } const struct in6_addr * -nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i) +nm_ip6_config_get_nameserver (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return &g_array_index (priv->nameservers, struct in6_addr, i); } @@ -1709,20 +2098,20 @@ nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i) /*****************************************************************************/ void -nm_ip6_config_reset_domains (NMIP6Config *config) +nm_ip6_config_reset_domains (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (priv->domains->len != 0) { g_ptr_array_set_size (priv->domains, 0); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } } void -nm_ip6_config_add_domain (NMIP6Config *config, const char *domain) +nm_ip6_config_add_domain (NMIP6Config *self, const char *domain) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (domain != NULL); @@ -1733,32 +2122,32 @@ nm_ip6_config_add_domain (NMIP6Config *config, const char *domain) return; g_ptr_array_add (priv->domains, g_strdup (domain)); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } void -nm_ip6_config_del_domain (NMIP6Config *config, guint i) +nm_ip6_config_del_domain (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->domains->len); g_ptr_array_remove_index (priv->domains, i); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } guint -nm_ip6_config_get_num_domains (const NMIP6Config *config) +nm_ip6_config_get_num_domains (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return priv->domains->len; } const char * -nm_ip6_config_get_domain (const NMIP6Config *config, guint i) +nm_ip6_config_get_domain (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->domains, i); } @@ -1766,20 +2155,20 @@ nm_ip6_config_get_domain (const NMIP6Config *config, guint i) /*****************************************************************************/ void -nm_ip6_config_reset_searches (NMIP6Config *config) +nm_ip6_config_reset_searches (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (priv->searches->len != 0) { g_ptr_array_set_size (priv->searches, 0); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } } void -nm_ip6_config_add_search (NMIP6Config *config, const char *new) +nm_ip6_config_add_search (NMIP6Config *self, const char *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); char *search; size_t len; @@ -1805,32 +2194,32 @@ nm_ip6_config_add_search (NMIP6Config *config, const char *new) } g_ptr_array_add (priv->searches, search); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } void -nm_ip6_config_del_search (NMIP6Config *config, guint i) +nm_ip6_config_del_search (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->searches->len); g_ptr_array_remove_index (priv->searches, i); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } guint -nm_ip6_config_get_num_searches (const NMIP6Config *config) +nm_ip6_config_get_num_searches (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return priv->searches->len; } const char * -nm_ip6_config_get_search (const NMIP6Config *config, guint i) +nm_ip6_config_get_search (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->searches, i); } @@ -1838,20 +2227,20 @@ nm_ip6_config_get_search (const NMIP6Config *config, guint i) /*****************************************************************************/ void -nm_ip6_config_reset_dns_options (NMIP6Config *config) +nm_ip6_config_reset_dns_options (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (priv->dns_options->len != 0) { g_ptr_array_set_size (priv->dns_options, 0); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } } void -nm_ip6_config_add_dns_option (NMIP6Config *config, const char *new) +nm_ip6_config_add_dns_option (NMIP6Config *self, const char *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != NULL); @@ -1862,32 +2251,32 @@ nm_ip6_config_add_dns_option (NMIP6Config *config, const char *new) return; g_ptr_array_add (priv->dns_options, g_strdup (new)); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } void -nm_ip6_config_del_dns_option (NMIP6Config *config, guint i) +nm_ip6_config_del_dns_option (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->dns_options->len); g_ptr_array_remove_index (priv->dns_options, i); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } guint -nm_ip6_config_get_num_dns_options (const NMIP6Config *config) +nm_ip6_config_get_num_dns_options (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return priv->dns_options->len; } const char * -nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i) +nm_ip6_config_get_dns_option (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->dns_options, i); } @@ -1895,40 +2284,101 @@ nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i) /*****************************************************************************/ void -nm_ip6_config_set_dns_priority (NMIP6Config *config, gint priority) +nm_ip6_config_set_dns_priority (NMIP6Config *self, gint priority) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (priority != priv->dns_priority) { priv->dns_priority = priority; - _notify (config, PROP_DNS_PRIORITY); + _notify (self, PROP_DNS_PRIORITY); } } gint -nm_ip6_config_get_dns_priority (const NMIP6Config *config) +nm_ip6_config_get_dns_priority (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); return priv->dns_priority; } /*****************************************************************************/ -void -nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss) +const NMPObject * +nm_ip6_config_nmpobj_lookup (const NMIP6Config *self, const NMPObject *needle) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMIP6ConfigPrivate *priv; + const NMDedupMultiIdxType *idx_type; - priv->mss = mss; + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + idx_type = &priv->idx_ip6_addresses; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + idx_type = &priv->idx_ip6_routes; + break; + default: + g_return_val_if_reached (NULL); + } + + return nm_dedup_multi_entry_get_obj (nm_dedup_multi_index_lookup_obj (priv->multi_idx, + idx_type, + needle)); } -guint32 -nm_ip6_config_get_mss (const NMIP6Config *config) +gboolean +nm_ip6_config_nmpobj_remove (NMIP6Config *self, + const NMPObject *needle) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv; + NMDedupMultiIdxType *idx_type; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + guint n; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); - return priv->mss; + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + idx_type = &priv->idx_ip6_addresses; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + idx_type = &priv->idx_ip6_routes; + break; + default: + g_return_val_if_reached (FALSE); + } + + n = nm_dedup_multi_index_remove_obj (priv->multi_idx, + idx_type, + needle, + (gconstpointer *) &obj_old); + if (n != 1) { + nm_assert (n == 0); + return FALSE; + } + + nm_assert (NMP_OBJECT_GET_TYPE (obj_old) == NMP_OBJECT_GET_TYPE (needle)); + + switch (NMP_OBJECT_GET_TYPE (obj_old)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + _notify_addresses (self); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + if (priv->best_default_route == obj_old) { + if (_nm_ip_config_best_default_route_set (&priv->best_default_route, + _nm_ip6_config_best_default_route_find (self))) + _notify (self, PROP_GATEWAY); + } + _notify_routes (self); + break; + default: + nm_assert_not_reached (); + } + return TRUE; } /*****************************************************************************/ @@ -1949,27 +2399,24 @@ hash_in6addr (GChecksum *sum, const struct in6_addr *a) } void -nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only) +nm_ip6_config_hash (const NMIP6Config *self, GChecksum *sum, gboolean dns_only) { guint32 i; const char *s; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; - g_return_if_fail (config); + g_return_if_fail (self); g_return_if_fail (sum); if (dns_only == FALSE) { - hash_in6addr (sum, nm_ip6_config_get_gateway (config)); - - for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++) { - const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i); - + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) { hash_in6addr (sum, &address->address); hash_u32 (sum, address->plen); } - for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) { hash_in6addr (sum, &route->network); hash_u32 (sum, route->plen); hash_in6addr (sum, &route->gateway); @@ -1977,21 +2424,21 @@ nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only } } - for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++) - hash_in6addr (sum, nm_ip6_config_get_nameserver (config, i)); + for (i = 0; i < nm_ip6_config_get_num_nameservers (self); i++) + hash_in6addr (sum, nm_ip6_config_get_nameserver (self, i)); - for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) { - s = nm_ip6_config_get_domain (config, i); + for (i = 0; i < nm_ip6_config_get_num_domains (self); i++) { + s = nm_ip6_config_get_domain (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } - for (i = 0; i < nm_ip6_config_get_num_searches (config); i++) { - s = nm_ip6_config_get_search (config, i); + for (i = 0; i < nm_ip6_config_get_num_searches (self); i++) { + s = nm_ip6_config_get_search (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } - for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++) { - s = nm_ip6_config_get_dns_option (config, i); + for (i = 0; i < nm_ip6_config_get_num_dns_options (self); i++) { + s = nm_ip6_config_get_dns_option (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } } @@ -2062,8 +2509,12 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMIP6Config *config = NM_IP6_CONFIG (object); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6Config *self = NM_IP6_CONFIG (object); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; + GVariantBuilder builder_data, builder_legacy; switch (prop_id) { case PROP_IFINDEX: @@ -2071,27 +2522,31 @@ get_property (GObject *object, guint prop_id, break; case PROP_ADDRESS_DATA: case PROP_ADDRESSES: - { - GVariantBuilder array_builder, addr_builder; - gs_unref_array GArray *new = NULL; - const struct in6_addr *gateway; - guint naddr, i; + nm_assert (!!priv->address_data_variant == !!priv->addresses_variant); - g_return_if_fail (!!priv->address_data_variant == !!priv->addresses_variant); + if (priv->address_data_variant) + goto out_addresses_cached; - if (priv->address_data_variant) - goto return_cached; + g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("a(ayuay)")); - naddr = nm_ip6_config_get_num_addresses (config); - gateway = nm_ip6_config_get_gateway (config); - new = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Address), naddr); - g_array_append_vals (new, priv->addresses->data, naddr); - g_array_sort_with_data (new, _addresses_sort_cmp, - GINT_TO_POINTER (priv->privacy)); + head_entry = nm_ip6_config_lookup_addresses (self); + if (head_entry) { + gs_free const NMPObject **addresses = NULL; + guint naddr, i; + + addresses = (const NMPObject **) nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr); + nm_assert (addresses && naddr); + + g_qsort_with_data (addresses, + naddr, + sizeof (addresses[0]), + _addresses_sort_cmp_prop, + GINT_TO_POINTER (priv->privacy)); - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); for (i = 0; i < naddr; i++) { - const NMPlatformIP6Address *address = &g_array_index (new, NMPlatformIP6Address, i); + GVariantBuilder addr_builder; + const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS (addresses[i]); g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", @@ -2107,80 +2562,75 @@ get_property (GObject *object, guint prop_id, g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, NULL))); } - g_variant_builder_add (&array_builder, "a{sv}", &addr_builder); - } - priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); - - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuay)")); - for (i = 0; i < naddr; i++) { - const NMPlatformIP6Address *address = &g_array_index (new, NMPlatformIP6Address, i); + g_variant_builder_add (&builder_data, "a{sv}", &addr_builder); - g_variant_builder_add (&array_builder, "(@ayu@ay)", + g_variant_builder_add (&builder_legacy, "(@ayu@ay)", g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &address->address, 16, 1), address->plen, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - (i == 0 && gateway ? gateway : &in6addr_any), + ( i == 0 + && priv->best_default_route) + ? &NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway + : &in6addr_any, 16, 1)); } - - priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); -return_cached: - g_value_set_variant (value, - prop_id == PROP_ADDRESS_DATA ? - priv->address_data_variant : - priv->addresses_variant); } + + priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); +out_addresses_cached: + g_value_set_variant (value, + prop_id == PROP_ADDRESS_DATA ? + priv->address_data_variant : + priv->addresses_variant); break; + case PROP_ROUTE_DATA: - { - GVariantBuilder array_builder, route_builder; - guint nroutes = nm_ip6_config_get_num_routes (config); - int i; + case PROP_ROUTES: + nm_assert (!!priv->route_data_variant == !!priv->routes_variant); - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); + if (priv->route_data_variant) + goto out_routes_cached; - g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&route_builder, "{sv}", - "dest", - g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL))); - g_variant_builder_add (&route_builder, "{sv}", - "prefix", - g_variant_new_uint32 (route->plen)); - if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { - g_variant_builder_add (&route_builder, "{sv}", - "next-hop", - g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL))); - } + g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("a(ayuayu)")); + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) { + GVariantBuilder route_builder; + nm_assert (_route_valid (route)); + + g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&route_builder, "{sv}", + "dest", + g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL))); + g_variant_builder_add (&route_builder, "{sv}", + "prefix", + g_variant_new_uint32 (route->plen)); + if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { g_variant_builder_add (&route_builder, "{sv}", - "metric", - g_variant_new_uint32 (route->metric)); + "next-hop", + g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL))); + } + + g_variant_builder_add (&route_builder, "{sv}", + "metric", + g_variant_new_uint32 (route->metric)); - g_variant_builder_add (&array_builder, "a{sv}", &route_builder); + if (!nm_platform_route_table_is_main (route->table_coerced)) { + g_variant_builder_add (&route_builder, "{sv}", + "table", + g_variant_new_uint32 (nm_platform_route_table_uncoerce (route->table_coerced, TRUE))); } - g_value_take_variant (value, g_variant_builder_end (&array_builder)); - } - break; - case PROP_ROUTES: - { - GVariantBuilder array_builder; - int nroutes = nm_ip6_config_get_num_routes (config); - int i; - - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuayu)")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - - /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the - * plen is positive. Skip the default routes not to break older clients. */ - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - continue; + g_variant_builder_add (&builder_data, "a{sv}", &route_builder); - g_variant_builder_add (&array_builder, "(@ayu@ayu)", + /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the + * plen is positive. Skip the default routes not to break older clients. */ + if ( nm_platform_route_table_is_main (route->table_coerced) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { + g_variant_builder_add (&builder_legacy, "(@ayu@ayu)", g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &route->network, 16, 1), (guint32) route->plen, @@ -2188,14 +2638,21 @@ return_cached: &route->gateway, 16, 1), (guint32) route->metric); } - - g_value_take_variant (value, g_variant_builder_end (&array_builder)); } + priv->route_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->routes_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); +out_routes_cached: + g_value_set_variant (value, + prop_id == PROP_ROUTE_DATA ? + priv->route_data_variant : + priv->routes_variant); break; case PROP_GATEWAY: - if (!IN6_IS_ADDR_UNSPECIFIED (&priv->gateway)) - g_value_set_string (value, nm_utils_inet6_ntop (&priv->gateway, NULL)); - else + if (priv->best_default_route) { + g_value_set_string (value, + nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway, + NULL)); + } else g_value_set_string (value, NULL); break; case PROP_NAMESERVERS: @@ -2225,10 +2682,17 @@ set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - NMIP6Config *config = NM_IP6_CONFIG (object); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6Config *self = NM_IP6_CONFIG (object); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); @@ -2242,24 +2706,27 @@ set_property (GObject *object, /*****************************************************************************/ static void -nm_ip6_config_init (NMIP6Config *config) +nm_ip6_config_init (NMIP6Config *self) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip6_addresses, + NMP_OBJECT_TYPE_IP6_ADDRESS); + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip6_routes, + NMP_OBJECT_TYPE_IP6_ROUTE); - priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address)); - priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route)); priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr)); priv->domains = g_ptr_array_new_with_free_func (g_free); priv->searches = g_ptr_array_new_with_free_func (g_free); priv->dns_options = g_ptr_array_new_with_free_func (g_free); - priv->route_metric = -1; } NMIP6Config * -nm_ip6_config_new (int ifindex) +nm_ip6_config_new (NMDedupMultiIndex *multi_idx, int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, + NM_IP6_CONFIG_MULTI_IDX, multi_idx, NM_IP6_CONFIG_IFINDEX, ifindex, NULL); } @@ -2271,7 +2738,8 @@ nm_ip6_config_new_cloned (const NMIP6Config *src) g_return_val_if_fail (NM_IS_IP6_CONFIG (src), NULL); - new = nm_ip6_config_new (nm_ip6_config_get_ifindex (src)); + new = nm_ip6_config_new (nm_ip6_config_get_multi_idx (src), + nm_ip6_config_get_ifindex (src)); nm_ip6_config_replace (new, src, NULL); return new; } @@ -2282,16 +2750,24 @@ finalize (GObject *object) NMIP6Config *self = NM_IP6_CONFIG (object); NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - g_array_unref (priv->addresses); - g_array_unref (priv->routes); + nm_clear_nmp_object (&priv->best_default_route); + + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip6_addresses); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip6_routes); + + nm_clear_g_variant (&priv->address_data_variant); + nm_clear_g_variant (&priv->addresses_variant); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); g_ptr_array_unref (priv->dns_options); - nm_clear_g_variant (&priv->address_data_variant); - nm_clear_g_variant (&priv->addresses_variant); G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object); + + nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -2306,6 +2782,11 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class) object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_IP6_CONFIG_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 557041c9..2fb8b8a4 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -26,6 +26,48 @@ #include "nm-exported-object.h" #include "nm-setting-ip6-config.h" +#include "nm-utils/nm-dedup-multi.h" +#include "platform/nmp-object.h" + +/*****************************************************************************/ + +void nm_ip_config_iter_ip6_address_init (NMDedupMultiIter *iter, const NMIP6Config *self); +void nm_ip_config_iter_ip6_route_init (NMDedupMultiIter *iter, const NMIP6Config *self); + +static inline gboolean +nm_ip_config_iter_ip6_address_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP6Address **out_address) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (out_address) + *out_address = has_next ? NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter->current->obj) : NULL; + return has_next; +} + +static inline gboolean +nm_ip_config_iter_ip6_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP6Route **out_route) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (out_route) + *out_route = has_next ? NMP_OBJECT_CAST_IP6_ROUTE (ipconf_iter->current->obj) : NULL; + return has_next; +} + +#define nm_ip_config_iter_ip6_address_for_each(iter, self, address) \ + for (nm_ip_config_iter_ip6_address_init ((iter), (self)); \ + nm_ip_config_iter_ip6_address_next ((iter), (address)); \ + ) + +#define nm_ip_config_iter_ip6_route_for_each(iter, self, route) \ + for (nm_ip_config_iter_ip6_route_init ((iter), (self)); \ + nm_ip_config_iter_ip6_route_next ((iter), (route)); \ + ) + +/*****************************************************************************/ + #define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type ()) #define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) #define NM_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) @@ -36,6 +78,7 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass; /* internal */ +#define NM_IP6_CONFIG_MULTI_IDX "multi-idx" #define NM_IP6_CONFIG_IFINDEX "ifindex" /* public */ @@ -55,96 +98,128 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass; GType nm_ip6_config_get_type (void); -NMIP6Config * nm_ip6_config_new (int ifindex); +NMIP6Config * nm_ip6_config_new (struct _NMDedupMultiIndex *multi_idx, int ifindex); NMIP6Config * nm_ip6_config_new_cloned (const NMIP6Config *src); -int nm_ip6_config_get_ifindex (const NMIP6Config *config); +int nm_ip6_config_get_ifindex (const NMIP6Config *self); +struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self); -NMIP6Config *nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); -gboolean nm_ip6_config_commit (const NMIP6Config *config, - NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync); -void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); -NMSetting *nm_ip6_config_create_setting (const NMIP6Config *config); +NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, + gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); +void nm_ip6_config_add_dependent_routes (NMIP6Config *self, + guint32 route_table, + guint32 route_metric); -void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags); -void nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src); -void nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src); +gboolean nm_ip6_config_commit (const NMIP6Config *self, + NMPlatform *platform, + NMIPRouteTableSyncMode route_table_sync, + GPtrArray **out_temporary_not_available); +void nm_ip6_config_merge_setting (NMIP6Config *self, + NMSettingIPConfig *setting, + guint32 route_table, + guint32 route_metric); +NMSetting *nm_ip6_config_create_setting (const NMIP6Config *self); + + +void nm_ip6_config_merge (NMIP6Config *dst, + const NMIP6Config *src, + NMIPConfigMergeFlags merge_flags, + guint32 default_route_metric_penalty); +void nm_ip6_config_subtract (NMIP6Config *dst, + const NMIP6Config *src, + guint32 default_route_metric_penalty); +void nm_ip6_config_intersect (NMIP6Config *dst, + const NMIP6Config *src, + guint32 default_route_metric_penalty); gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes); -int nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6_addr *dest, guint8 plen); -void nm_ip6_config_dump (const NMIP6Config *config, const char *detail); - - -void nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default); -gboolean nm_ip6_config_get_never_default (const NMIP6Config *config); -void nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *); -const struct in6_addr *nm_ip6_config_get_gateway (const NMIP6Config *config); -gint64 nm_ip6_config_get_route_metric (const NMIP6Config *config); - -void nm_ip6_config_reset_addresses (NMIP6Config *config); -void nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *address); -void nm_ip6_config_del_address (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_addresses (const NMIP6Config *config); -const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config, guint i); -const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal); -gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address); -gboolean nm_ip6_config_addresses_sort (NMIP6Config *config); +void nm_ip6_config_dump (const NMIP6Config *self, const char *detail); + +const NMPObject *nm_ip6_config_best_default_route_get (const NMIP6Config *self); +const NMPObject *_nm_ip6_config_best_default_route_find (const NMIP6Config *self); + +const NMDedupMultiHeadEntry *nm_ip6_config_lookup_addresses (const NMIP6Config *self); +void nm_ip6_config_reset_addresses (NMIP6Config *self); +void nm_ip6_config_add_address (NMIP6Config *self, const NMPlatformIP6Address *address); +void _nmtst_ip6_config_del_address (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_addresses (const NMIP6Config *self); +const NMPlatformIP6Address *nm_ip6_config_get_first_address (const NMIP6Config *self); +const NMPlatformIP6Address *_nmtst_ip6_config_get_address (const NMIP6Config *self, guint i); +const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal); +gboolean nm_ip6_config_address_exists (const NMIP6Config *self, const NMPlatformIP6Address *address); +const NMPlatformIP6Address *nm_ip6_config_lookup_address (const NMIP6Config *self, + const struct in6_addr *addr); +gboolean _nmtst_ip6_config_addresses_sort (NMIP6Config *self); gboolean nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, const NMIP6Config *candidates); -void nm_ip6_config_reset_routes (NMIP6Config *config); -void nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *route); -void nm_ip6_config_del_route (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_routes (const NMIP6Config *config); -const NMPlatformIP6Route *nm_ip6_config_get_route (const NMIP6Config *config, guint i); - -const NMPlatformIP6Route *nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host); -const NMPlatformIP6Address *nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host); - -void nm_ip6_config_reset_nameservers (NMIP6Config *config); -void nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *nameserver); -void nm_ip6_config_del_nameserver (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_nameservers (const NMIP6Config *config); -const struct in6_addr *nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i); - -void nm_ip6_config_reset_domains (NMIP6Config *config); -void nm_ip6_config_add_domain (NMIP6Config *config, const char *domain); -void nm_ip6_config_del_domain (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_domains (const NMIP6Config *config); -const char * nm_ip6_config_get_domain (const NMIP6Config *config, guint i); - -void nm_ip6_config_reset_searches (NMIP6Config *config); -void nm_ip6_config_add_search (NMIP6Config *config, const char *search); -void nm_ip6_config_del_search (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_searches (const NMIP6Config *config); -const char * nm_ip6_config_get_search (const NMIP6Config *config, guint i); - -void nm_ip6_config_reset_dns_options (NMIP6Config *config); -void nm_ip6_config_add_dns_option (NMIP6Config *config, const char *option); -void nm_ip6_config_del_dns_option (NMIP6Config *config, guint i); -guint nm_ip6_config_get_num_dns_options (const NMIP6Config *config); -const char * nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i); - -void nm_ip6_config_set_dns_priority (NMIP6Config *config, gint priority); -gint nm_ip6_config_get_dns_priority (const NMIP6Config *config); - -void nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss); -guint32 nm_ip6_config_get_mss (const NMIP6Config *config); - -void nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only); +const NMDedupMultiHeadEntry *nm_ip6_config_lookup_routes (const NMIP6Config *self); +void nm_ip6_config_reset_routes (NMIP6Config *self); +void nm_ip6_config_add_route (NMIP6Config *self, + const NMPlatformIP6Route *route, + const NMPObject **out_obj_new); +void _nmtst_ip6_config_del_route (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_routes (const NMIP6Config *self); +const NMPlatformIP6Route *_nmtst_ip6_config_get_route (const NMIP6Config *self, guint i); + +const NMPlatformIP6Route *nm_ip6_config_get_direct_route_for_host (const NMIP6Config *self, + const struct in6_addr *host, + guint32 route_table); +const NMPlatformIP6Address *nm_ip6_config_get_subnet_for_host (const NMIP6Config *self, const struct in6_addr *host); + +void nm_ip6_config_reset_nameservers (NMIP6Config *self); +void nm_ip6_config_add_nameserver (NMIP6Config *self, const struct in6_addr *nameserver); +void nm_ip6_config_del_nameserver (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_nameservers (const NMIP6Config *self); +const struct in6_addr *nm_ip6_config_get_nameserver (const NMIP6Config *self, guint i); + +void nm_ip6_config_reset_domains (NMIP6Config *self); +void nm_ip6_config_add_domain (NMIP6Config *self, const char *domain); +void nm_ip6_config_del_domain (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_domains (const NMIP6Config *self); +const char * nm_ip6_config_get_domain (const NMIP6Config *self, guint i); + +void nm_ip6_config_reset_searches (NMIP6Config *self); +void nm_ip6_config_add_search (NMIP6Config *self, const char *search); +void nm_ip6_config_del_search (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_searches (const NMIP6Config *self); +const char * nm_ip6_config_get_search (const NMIP6Config *self, guint i); + +void nm_ip6_config_reset_dns_options (NMIP6Config *self); +void nm_ip6_config_add_dns_option (NMIP6Config *self, const char *option); +void nm_ip6_config_del_dns_option (NMIP6Config *self, guint i); +guint nm_ip6_config_get_num_dns_options (const NMIP6Config *self); +const char * nm_ip6_config_get_dns_option (const NMIP6Config *self, guint i); + +void nm_ip6_config_set_dns_priority (NMIP6Config *self, gint priority); +gint nm_ip6_config_get_dns_priority (const NMIP6Config *self); + +const NMPObject *nm_ip6_config_nmpobj_lookup (const NMIP6Config *self, + const NMPObject *needle); +gboolean nm_ip6_config_nmpobj_remove (NMIP6Config *self, + const NMPObject *needle); + +void nm_ip6_config_hash (const NMIP6Config *self, GChecksum *sum, gboolean dns_only); gboolean nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b); -void nm_ip6_config_set_privacy (NMIP6Config *config, NMSettingIP6ConfigPrivacy privacy); - -/*****************************************************************************/ -/* Testing-only functions */ - -gboolean nm_ip6_config_capture_resolv_conf (GArray *nameservers, - GPtrArray *dns_options, - const char *rc_contents); +void nm_ip6_config_set_privacy (NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy); + +struct _NMNDiscAddress; +void nm_ip6_config_reset_addresses_ndisc (NMIP6Config *self, + const struct _NMNDiscAddress *addresses, + guint addresses_n, + guint8 plen, + guint32 ifa_flags); +struct _NMNDiscRoute; +struct _NMNDiscGateway; +void nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, + const struct _NMNDiscGateway *gateways, + guint gateways_n, + const struct _NMNDiscRoute *routes, + guint routes_n, + guint32 route_table, + guint32 route_metric, + gboolean kernel_support_rta_pref); #endif /* __NETWORKMANAGER_IP6_CONFIG_H__ */ diff --git a/src/nm-logging.h b/src/nm-logging.h index 91a41412..8fcbc8cc 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -142,11 +142,16 @@ typedef enum { /*< skip >*/ (prefix) ?: "", \ self _NM_UTILS_MACRO_REST(__VA_ARGS__)) +static inline gboolean +_nm_log_ptr_is_debug (NMLogLevel level) +{ + return level <= LOGL_DEBUG; +} + /* log a message for an object (with providing a generic @self pointer) */ #define nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ G_STMT_START { \ - NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") \ - if ((level) <= LOGL_DEBUG) { \ + if (_nm_log_ptr_is_debug (level)) { \ _nm_log_ptr ((level), \ (domain), \ (ifname), \ @@ -165,7 +170,6 @@ typedef enum { /*< skip >*/ __prefix ?: "", \ __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ - NM_PRAGMA_WARNING_REENABLE \ } G_STMT_END @@ -269,7 +273,7 @@ gboolean nm_logging_syslog_enabled (void); /*****************************************************************************/ /* Some implementation define a second set of logging macros, for a separate - * use. As with the _LOGD() macro familiy above, the exact implementation + * use. As with the _LOGD() macro family above, the exact implementation * depends on the file that uses them. * Still, it encourages a common pattern to have the common set of macros * like _LOG2D(), _LOG2I(), etc. and have _LOG2t() which by default diff --git a/src/nm-manager.c b/src/nm-manager.c index 7662c2e3..3b2b4861 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -35,6 +35,8 @@ #include "devices/nm-device.h" #include "devices/nm-device-generic.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" +#include "nm-hostname-manager.h" #include "nm-rfkill-manager.h" #include "dhcp/nm-dhcp-manager.h" #include "settings/nm-settings.h" @@ -92,6 +94,8 @@ static void settings_startup_complete_changed (NMSettings *settings, GParamSpec *pspec, NMManager *self); +static void retry_connections_for_parent_device (NMManager *self, NMDevice *device); + static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark) typedef struct { @@ -106,6 +110,8 @@ typedef struct { } RadioState; typedef struct { + NMPlatform *platform; + GArray *capabilities; GSList *active_connections; @@ -122,6 +128,8 @@ typedef struct { NMPolicy *policy; + NMHostnameManager *hostname_manager; + NMBusManager *dbus_mgr; struct { GDBusConnection *connection; @@ -129,10 +137,11 @@ typedef struct { } prop_filter; NMRfkillManager *rfkill_mgr; + CList link_cb_lst; + NMCheckpointManager *checkpoint_mgr; NMSettings *settings; - char *hostname; RadioState radio_states[RFKILL_TYPE_MAX]; NMVpnManager *vpn_manager; @@ -202,6 +211,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager, PROP_WIMAX_HARDWARE_ENABLED, PROP_ACTIVE_CONNECTIONS, PROP_CONNECTIVITY, + PROP_CONNECTIVITY_CHECK_AVAILABLE, + PROP_CONNECTIVITY_CHECK_ENABLED, PROP_PRIMARY_CONNECTION, PROP_PRIMARY_CONNECTION_TYPE, PROP_ACTIVATING_CONNECTION, @@ -211,7 +222,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager, PROP_ALL_DEVICES, /* Not exported */ - PROP_HOSTNAME, PROP_SLEEPING, ); @@ -329,7 +339,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active) if (nm_settings_has_connection (priv->settings, connection)) { _LOGD (LOGD_DEVICE, "assumed connection disconnected. Deleting generated connection '%s' (%s)", nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection)); - nm_settings_connection_delete (connection, NULL, NULL); + nm_settings_connection_delete (connection, NULL); } g_object_unref (connection); } @@ -827,7 +837,7 @@ find_best_device_state (NMManager *manager) case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: if ( nm_active_connection_get_default (ac) || nm_active_connection_get_default6 (ac)) { - if (priv->connectivity_state) + if (priv->connectivity_state == NM_CONNECTIVITY_FULL) return NM_STATE_CONNECTED_GLOBAL; best_state = NM_STATE_CONNECTED_SITE; @@ -1149,6 +1159,10 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM for (iter = priv->devices; iter; iter = iter->next) { NMDevice *candidate = iter->data; + /* Unmanaged devices are not compatible with any connection */ + if (!nm_device_get_managed (candidate, FALSE)) + continue; + if (nm_device_get_settings_connection (candidate) == parent_connection) return candidate; @@ -1256,6 +1270,40 @@ nm_manager_iface_for_uuid (NMManager *self, const char *uuid) return nm_connection_get_interface_name (NM_CONNECTION (connection)); } +NMDevice * +nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + NMDevice *d; + + g_return_val_if_fail (ifname, NULL); + g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL); + + for (iter = priv->devices; iter; iter = iter->next) { + d = iter->data; + + if ( nm_device_get_device_type (d) == device_type + && nm_streq0 (nm_device_get_iface (d), ifname)) + return d; + } + + return NULL; +} + +gboolean +nm_manager_remove_device (NMManager *self, const char *ifname, NMDeviceType device_type) +{ + NMDevice *d; + + d = nm_manager_get_device (self, ifname, device_type); + if (!d) + return FALSE; + + remove_device (self, d, FALSE, FALSE); + return TRUE; +} + /** * system_create_virtual_device: * @self: the #NMManager @@ -1277,6 +1325,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) gs_free char *iface = NULL; NMDevice *device = NULL, *parent = NULL; GError *error = NULL; + NMLogLevel log_level; g_return_val_if_fail (NM_IS_MANAGER (self), NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); @@ -1357,12 +1406,20 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) /* Create any backing resources the device needs */ if (!nm_device_create_and_realize (device, connection, parent, &error)) { - _LOG3W (LOGD_DEVICE, connection, "couldn't create the device: %s", - error->message); + log_level = g_error_matches (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_MISSING_DEPENDENCIES) + ? LOGL_DEBUG + : LOGL_ERR; + _NMLOG3 (log_level, LOGD_DEVICE, connection, + "couldn't create the device: %s", + error->message); g_error_free (error); remove_device (self, device, FALSE, TRUE); return NULL; } + + retry_connections_for_parent_device (self, device); break; } @@ -1448,35 +1505,17 @@ system_unmanaged_devices_changed_cb (NMSettings *settings, } static void -system_hostname_changed_cb (NMSettings *settings, - GParamSpec *pspec, - gpointer user_data) +hostname_changed_cb (NMHostnameManager *hostname_manager, + GParamSpec *pspec, + NMManager *self) { - NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - char *hostname; - - hostname = nm_settings_get_hostname (priv->settings); - - /* nm_settings_get_hostname() does not return an empty hostname. */ - nm_assert (!hostname || *hostname); - - if (!hostname && !priv->hostname) - return; - if (hostname && priv->hostname && !strcmp (hostname, priv->hostname)) { - g_free (hostname); - return; - } + const char *hostname; - /* realloc, to free possibly trailing data after NUL. */ - if (hostname) - hostname = g_realloc (hostname, strlen (hostname) + 1); + hostname = nm_hostname_manager_get_hostname (priv->hostname_manager); - g_free (priv->hostname); - priv->hostname = hostname; - _notify (self, PROP_HOSTNAME); - - nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), priv->hostname); + nm_dispatcher_call_hostname (NULL, NULL, NULL); + nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), hostname); } /*****************************************************************************/ @@ -1761,14 +1800,14 @@ get_existing_connection (NMManager *self, nm_device_capture_initial_config (device); if (ifindex) { - int master_ifindex = nm_platform_link_get_master (NM_PLATFORM_GET, ifindex); + int master_ifindex = nm_platform_link_get_master (priv->platform, ifindex); if (master_ifindex) { master = nm_manager_get_device_by_ifindex (self, master_ifindex); if (!master) { _LOG2D (LOGD_DEVICE, device, "assume: don't assume because " "cannot generate connection for slave before its master (%s/%d)", - nm_platform_link_get_name (NM_PLATFORM_GET, master_ifindex), master_ifindex); + nm_platform_link_get_name (priv->platform, master_ifindex), master_ifindex); return NULL; } if (!nm_device_get_act_request (master)) { @@ -1822,8 +1861,8 @@ get_existing_connection (NMManager *self, matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, connection, nm_device_has_carrier (device), - nm_device_get_ip4_route_metric (device), - nm_device_get_ip6_route_metric (device), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_metric (device, AF_INET6), NULL, NULL)); } else matched = NULL; @@ -1851,8 +1890,8 @@ get_existing_connection (NMManager *self, matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection ((NMConnection *const*) connections, connection, nm_device_has_carrier (device), - nm_device_get_ip4_route_metric (device), - nm_device_get_ip6_route_metric (device), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_metric (device, AF_INET6), NULL, NULL)); } } @@ -1967,7 +2006,7 @@ recheck_assume_connection (NMManager *self, if (generated) { _LOG2D (LOGD_DEVICE, device, "assume: deleting generated connection after assuming failed"); - nm_settings_connection_delete (connection, NULL, NULL); + nm_settings_connection_delete (connection, NULL); } else { if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME) nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); @@ -2008,6 +2047,7 @@ device_ip_iface_changed (NMDevice *device, NMManager *self) { const char *ip_iface = nm_device_get_ip_iface (device); + NMDeviceType device_type = nm_device_get_device_type (device); GSList *iter; /* Remove NMDevice objects that are actually child devices of others, @@ -2020,6 +2060,7 @@ device_ip_iface_changed (NMDevice *device, if ( candidate != device && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 + && nm_device_get_device_type (candidate) == device_type && nm_device_is_real (candidate)) { remove_device (self, candidate, FALSE, FALSE); break; @@ -2228,11 +2269,6 @@ add_device (NMManager *self, NMDevice *device, GError **error) _parent_notify_changed (self, device, FALSE); - /* Virtual connections may refer to the new device as - * parent device, retry to activate them. - */ - retry_connections_for_parent_device (self, device); - return TRUE; } @@ -2258,6 +2294,7 @@ factory_device_added_cb (NMDeviceFactory *factory, &error)) { add_device (self, device, NULL); _device_realize_finish (self, device, NULL); + retry_connections_for_parent_device (self, device); } else { _LOG2W (LOGD_DEVICE, device, "failed to realize device: %s", error->message); g_error_free (error); @@ -2320,6 +2357,9 @@ platform_link_added (NMManager *self, gboolean compatible = TRUE; gs_free_error GError *error = NULL; + if (nm_device_get_link_type (candidate) != plink->type) + continue; + if (strcmp (nm_device_get_iface (candidate), plink->name)) continue; @@ -2327,6 +2367,7 @@ platform_link_added (NMManager *self, /* Ignore the link added event since there's already a realized * device with the link's name. */ + nm_device_update_from_platform_link (candidate, plink); return; } else if (nm_device_realize_start (candidate, plink, @@ -2411,6 +2452,7 @@ platform_link_added (NMManager *self, &error)) { add_device (self, device, NULL); _device_realize_finish (self, device, plink); + retry_connections_for_parent_device (self, device); } else { _LOGW (LOGD_DEVICE, "%s: failed to realize device: %s", plink->name, error->message); @@ -2420,32 +2462,34 @@ platform_link_added (NMManager *self, } typedef struct { + CList lst; NMManager *self; int ifindex; + guint idle_id; } PlatformLinkCbData; static gboolean _platform_link_cb_idle (PlatformLinkCbData *data) { + int ifindex = data->ifindex; NMManager *self = data->self; - const NMPlatformLink *l; - - if (!self) - goto out; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + const NMPlatformLink *plink; - g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self); + c_list_unlink (&data->lst); + g_slice_free (PlatformLinkCbData, data); - l = nm_platform_link_get (NM_PLATFORM_GET, data->ifindex); - if (l) { - NMPlatformLink pllink; + plink = nm_platform_link_get (priv->platform, ifindex); + if (plink) { + const NMPObject *plink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); - pllink = *l; /* make a copy of the link instance */ - platform_link_added (self, data->ifindex, &pllink, FALSE, NULL); + platform_link_added (self, ifindex, plink, FALSE, NULL); + nmp_object_unref (plink_keep_alive); } else { NMDevice *device; GError *error = NULL; - device = nm_manager_get_device_by_ifindex (self, data->ifindex); + device = nm_manager_get_device_by_ifindex (self, ifindex); if (device) { if (nm_device_is_software (device)) { nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_REMOVED); @@ -2454,6 +2498,8 @@ _platform_link_cb_idle (PlatformLinkCbData *data) _LOG2W (LOGD_DEVICE, device, "failed to unrealize: %s", error->message); g_clear_error (&error); remove_device (self, device, FALSE, TRUE); + } else { + nm_device_update_from_platform_link (device, NULL); } } else { /* Hardware and external devices always get removed when their kernel link is gone */ @@ -2462,8 +2508,6 @@ _platform_link_cb_idle (PlatformLinkCbData *data) } } -out: - g_slice_free (PlatformLinkCbData, data); return G_SOURCE_REMOVE; } @@ -2475,17 +2519,22 @@ platform_link_cb (NMPlatform *platform, int change_type_i, gpointer user_data) { + NMManager *self; + NMManagerPrivate *priv; const NMPlatformSignalChangeType change_type = change_type_i; PlatformLinkCbData *data; switch (change_type) { case NM_PLATFORM_SIGNAL_ADDED: case NM_PLATFORM_SIGNAL_REMOVED: + self = NM_MANAGER (user_data); + priv = NM_MANAGER_GET_PRIVATE (self); + data = g_slice_new (PlatformLinkCbData); - data->self = NM_MANAGER (user_data); + data->self = self; data->ifindex = ifindex; - g_object_add_weak_pointer (G_OBJECT (data->self), (gpointer *) &data->self); - g_idle_add ((GSourceFunc) _platform_link_cb_idle, data); + c_list_link_tail (&priv->link_cb_lst, &data->lst); + data->idle_id = g_idle_add ((GSourceFunc) _platform_link_cb_idle, data); break; default: break; @@ -2495,32 +2544,32 @@ platform_link_cb (NMPlatform *platform, static void platform_query_devices (NMManager *self) { - GArray *links_array; - NMPlatformLink *links; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gs_unref_ptrarray GPtrArray *links = NULL; int i; gboolean guess_assume; - const char *order; + gs_free char *order = NULL; guess_assume = nm_config_get_first_start (nm_config_get ()); - order = nm_config_data_get_value_cached (NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, - NM_CONFIG_GET_VALUE_STRIP); - links_array = nm_platform_link_get_all (NM_PLATFORM_GET, !nm_streq0 (order, "index")); - links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) { + order = nm_config_data_get_value (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, + NM_CONFIG_GET_VALUE_STRIP); + links = nm_platform_link_get_all (priv->platform, !nm_streq0 (order, "index")); + if (!links) + return; + for (i = 0; i < links->len; i++) { + const NMPlatformLink *link = NMP_OBJECT_CAST_LINK (links->pdata[i]); gs_free NMConfigDeviceStateData *dev_state = NULL; - dev_state = nm_config_device_state_load (links[i].ifindex); + dev_state = nm_config_device_state_load (link->ifindex); platform_link_added (self, - links[i].ifindex, - &links[i], + link->ifindex, + link, guess_assume && (!dev_state || !dev_state->connection_uuid), dev_state); } - - g_array_unref (links_array); } static void @@ -3090,14 +3139,15 @@ autoconnect_slaves (NMManager *self, if (should_connect_slaves (NM_CONNECTION (master_connection), master_device)) { gs_free SlaveConnectionInfo *slaves = NULL; guint i, n_slaves = 0; - const char *value; slaves = find_slaves (self, master_connection, master_device, &n_slaves); if (n_slaves > 1) { - value = nm_config_data_get_value_cached (NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, - NM_CONFIG_GET_VALUE_STRIP); + gs_free char *value = NULL; + + value = nm_config_data_get_value (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, + NM_CONFIG_GET_VALUE_STRIP); g_qsort_with_data (slaves, n_slaves, sizeof (slaves[0]), compare_slaves, GINT_TO_POINTER (!nm_streq0 (value, "index"))); @@ -3197,7 +3247,10 @@ unmanaged_to_disconnected (NMDevice *device) nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_USER_EXPLICIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED); - g_return_if_fail (nm_device_get_managed (device, FALSE)); + if (!nm_device_get_managed (device, FALSE)) { + /* the device is still marked as unmanaged. Nothing to do. */ + return; + } if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) { nm_device_state_changed (device, @@ -3806,10 +3859,11 @@ validate_activation_request (NMManager *self, device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL); if (!device && !vpn) { - gboolean is_software = nm_connection_is_virtual (connection); + gs_free char *iface = NULL; - /* VPN and software-device connections don't need a device yet */ - if (!is_software) { + /* VPN and software-device connections don't need a device yet, + * but non-virtual connections do ... */ + if (!nm_connection_is_virtual (connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, @@ -3817,17 +3871,12 @@ validate_activation_request (NMManager *self, goto error; } - if (is_software) { - char *iface; - - /* Look for an existing device with the connection's interface name */ - iface = nm_manager_get_connection_iface (self, connection, NULL, error); - if (!iface) - goto error; + /* Look for an existing device with the connection's interface name */ + iface = nm_manager_get_connection_iface (self, connection, NULL, error); + if (!iface) + goto error; - device = find_device_by_iface (self, iface, connection, NULL); - g_free (iface); - } + device = find_device_by_iface (self, iface, connection, NULL); } if ((!vpn || device_path) && !device) { @@ -3922,8 +3971,8 @@ impl_manager_activate_connection (NMManager *self, connection = nm_settings_get_connection_by_path (priv->settings, connection_path); if (!connection) { error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection could not be found."); goto error; } } else { @@ -4011,8 +4060,9 @@ activation_add_done (NMSettings *settings, if (_internal_activate_generic (self, active, &local)) { nm_settings_connection_commit_changes (new_connection, + NULL, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED, - NULL, NULL); + NULL); g_dbus_method_invocation_return_value ( context, g_variant_new ("(oo)", @@ -4032,7 +4082,7 @@ activation_add_done (NMSettings *settings, g_assert (error); _internal_activation_failed (self, active, error->message); if (new_connection) - nm_settings_connection_delete (new_connection, NULL, NULL); + nm_settings_connection_delete (new_connection, NULL); g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, @@ -4155,7 +4205,7 @@ impl_manager_add_and_activate_connection (NMManager *self, goto error; } - nm_utils_complete_generic (NM_PLATFORM_GET, + nm_utils_complete_generic (priv->platform, connection, NM_SETTING_VPN_SETTING_NAME, all_connections, @@ -4372,9 +4422,9 @@ done: } static gboolean -device_is_wake_on_lan (NMDevice *device) +device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) { - return nm_platform_link_get_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device)); + return nm_platform_link_get_wake_on_lan (platform, nm_device_get_ip_ifindex (device)); } static gboolean @@ -4492,7 +4542,8 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) if (nm_device_is_software (device)) continue; /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ - if (suspending && device_is_wake_on_lan (device)) { + if ( suspending + && device_is_wake_on_lan (priv->platform, device)) { _LOGD (LOGD_SUSPEND, "sleep: device %s has wake-on-lan, skipping", nm_device_get_ip_iface (device)); continue; @@ -4523,7 +4574,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) /* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this * but for now it's the only way to make sure we re-check their connectivity. */ - if (device_is_wake_on_lan (device)) + if (device_is_wake_on_lan (priv->platform, device)) nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); /* Check if the device is unmanaged but the state transition is still pending. @@ -4871,6 +4922,7 @@ get_permissions_done_cb (NMAuthChain *chain, get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_RELOAD); get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK); g_dbus_method_invocation_return_value (context, g_variant_new ("(a{ss})", &results)); @@ -4912,6 +4964,7 @@ impl_manager_get_permissions (NMManager *self, nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_RELOAD, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, FALSE); } static void @@ -5095,7 +5148,7 @@ nm_manager_write_device_state (NMManager *self) continue; } - if (!nm_platform_link_get (NM_PLATFORM_GET, ifindex)) + if (!nm_platform_link_get (priv->platform, ifindex)) continue; managed = nm_device_get_managed (device, FALSE); @@ -5174,15 +5227,15 @@ nm_manager_start (NMManager *self, GError **error) priv->net_enabled ? "enabled" : "disabled"); system_unmanaged_devices_changed_cb (priv->settings, NULL, self); - system_hostname_changed_cb (priv->settings, NULL, self); + hostname_changed_cb (priv->hostname_manager, NULL, self); /* Start device factories */ nm_device_factory_manager_load_factories (_register_device_factory, self); nm_device_factory_manager_for_each_factory (start_factory, NULL); - nm_platform_process_events (NM_PLATFORM_GET); + nm_platform_process_events (priv->platform); - g_signal_connect (NM_PLATFORM_GET, + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (platform_link_cb), self); @@ -5567,6 +5620,10 @@ prop_filter (GDBusConnection *connection, permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS; audit_op = NM_AUDIT_OP_NET_CONTROL; expected_type = G_VARIANT_TYPE ("a{sv}"); + } else if (!strcmp (propname, "ConnectivityCheckEnabled")) { + glib_propname = NM_MANAGER_CONNECTIVITY_CHECK_ENABLED; + permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK; + audit_op = NM_AUDIT_OP_NET_CONTROL; } else return message; interface_type = NMDBUS_TYPE_MANAGER_SKELETON; @@ -6069,12 +6126,15 @@ constructed (GObject *object) G_CALLBACK (settings_startup_complete_changed), self); g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS, G_CALLBACK (system_unmanaged_devices_changed_cb), self); - g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME, - G_CALLBACK (system_hostname_changed_cb), self); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (connection_added_cb), self); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (connection_updated_cb), self); + + priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); + g_signal_connect (priv->hostname_manager, "notify::" NM_HOSTNAME_MANAGER_HOSTNAME, + G_CALLBACK (hostname_changed_cb), self); + /* * Do not delete existing virtual devices to keep connectivity up. * Virtual devices are reused when NetworkManager is restarted. @@ -6126,6 +6186,10 @@ nm_manager_init (NMManager *self) guint i; GFile *file; + c_list_init (&priv->link_cb_lst); + + priv->platform = g_object_ref (NM_PLATFORM_GET); + priv->capabilities = g_array_new (FALSE, FALSE, sizeof (guint32)); /* Initialize rfkill structures and states */ @@ -6210,6 +6274,7 @@ get_property (GObject *object, guint prop_id, NMConfigData *config_data; const NMGlobalDnsConfig *dns_config; const char *type; + NMConnectivity *connectivity; switch (prop_id) { case PROP_VERSION: @@ -6254,6 +6319,14 @@ get_property (GObject *object, guint prop_id, case PROP_CONNECTIVITY: g_value_set_uint (value, priv->connectivity_state); break; + case PROP_CONNECTIVITY_CHECK_AVAILABLE: + config_data = nm_config_get_data (priv->config); + g_value_set_boolean (value, nm_config_data_get_connectivity_uri (config_data) != NULL); + break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + connectivity = nm_connectivity_get (); + g_value_set_boolean (value, nm_connectivity_check_enabled (connectivity)); + break; case PROP_PRIMARY_CONNECTION: nm_utils_g_value_set_object_path (value, priv->primary_connection); break; @@ -6271,9 +6344,6 @@ get_property (GObject *object, guint prop_id, case PROP_ACTIVATING_CONNECTION: nm_utils_g_value_set_object_path (value, priv->activating_connection); break; - case PROP_HOSTNAME: - g_value_set_string (value, priv->hostname); - break; case PROP_SLEEPING: g_value_set_boolean (value, priv->sleeping); break; @@ -6320,6 +6390,10 @@ set_property (GObject *object, guint prop_id, case PROP_WIMAX_ENABLED: /* WIMAX is depreacted. This does nothing. */ break; + case PROP_CONNECTIVITY_CHECK_ENABLED: + nm_config_set_connectivity_check_enabled (priv->config, + g_value_get_boolean (value)); + break; case PROP_GLOBAL_DNS_CONFIGURATION: dns_config = nm_global_dns_config_from_dbus (value, &error); if (!error) @@ -6347,8 +6421,20 @@ _deinit_device_factory (NMDeviceFactory *factory, gpointer user_data) static void dispose (GObject *object) { - NMManager *manager = NM_MANAGER (object); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMManager *self = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + CList *iter, *iter_safe; + + g_signal_handlers_disconnect_by_func (priv->platform, + G_CALLBACK (platform_link_cb), + self); + c_list_for_each_safe (iter, iter_safe, &priv->link_cb_lst) { + PlatformLinkCbData *data = c_list_entry (iter, PlatformLinkCbData, lst); + + g_source_remove (data->idle_id); + c_list_unlink (iter); + g_slice_free (PlatformLinkCbData, data); + } g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref); priv->auth_chains = NULL; @@ -6363,7 +6449,7 @@ dispose (GObject *object) if (priv->auth_mgr) { g_signal_handlers_disconnect_by_func (priv->auth_mgr, G_CALLBACK (auth_mgr_changed), - manager); + self); g_clear_object (&priv->auth_mgr); } @@ -6372,52 +6458,54 @@ dispose (GObject *object) nm_clear_g_source (&priv->ac_cleanup_id); while (priv->active_connections) - active_connection_remove (manager, NM_ACTIVE_CONNECTION (priv->active_connections->data)); + active_connection_remove (self, NM_ACTIVE_CONNECTION (priv->active_connections->data)); g_clear_pointer (&priv->active_connections, g_slist_free); g_clear_object (&priv->primary_connection); g_clear_object (&priv->activating_connection); if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, manager); + g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, self); g_clear_object (&priv->config); } - g_free (priv->hostname); - if (priv->policy) { - g_signal_handlers_disconnect_by_func (priv->policy, policy_default_device_changed, manager); - g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_device_changed, manager); + g_signal_handlers_disconnect_by_func (priv->policy, policy_default_device_changed, self); + g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_device_changed, self); g_clear_object (&priv->policy); } if (priv->settings) { - g_signal_handlers_disconnect_by_func (priv->settings, settings_startup_complete_changed, manager); - g_signal_handlers_disconnect_by_func (priv->settings, system_unmanaged_devices_changed_cb, manager); - g_signal_handlers_disconnect_by_func (priv->settings, system_hostname_changed_cb, manager); - g_signal_handlers_disconnect_by_func (priv->settings, connection_added_cb, manager); - g_signal_handlers_disconnect_by_func (priv->settings, connection_updated_cb, manager); + g_signal_handlers_disconnect_by_func (priv->settings, settings_startup_complete_changed, self); + g_signal_handlers_disconnect_by_func (priv->settings, system_unmanaged_devices_changed_cb, self); + g_signal_handlers_disconnect_by_func (priv->settings, connection_added_cb, self); + g_signal_handlers_disconnect_by_func (priv->settings, connection_updated_cb, self); g_clear_object (&priv->settings); } + if (priv->hostname_manager) { + g_signal_handlers_disconnect_by_func (priv->hostname_manager, hostname_changed_cb, self); + g_clear_object (&priv->hostname_manager); + } + g_clear_object (&priv->vpn_manager); /* Unregister property filter */ if (priv->dbus_mgr) { - g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, manager); + g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self); g_clear_object (&priv->dbus_mgr); } - _set_prop_filter (manager, NULL); + _set_prop_filter (self, NULL); - sleep_devices_clear (manager); + sleep_devices_clear (self); g_clear_pointer (&priv->sleep_devices, g_hash_table_unref); if (priv->sleep_monitor) { - g_signal_handlers_disconnect_by_func (priv->sleep_monitor, sleeping_cb, manager); + g_signal_handlers_disconnect_by_func (priv->sleep_monitor, sleeping_cb, self); g_clear_object (&priv->sleep_monitor); } if (priv->fw_monitor) { - g_signal_handlers_disconnect_by_func (priv->fw_monitor, firmware_dir_changed, manager); + g_signal_handlers_disconnect_by_func (priv->fw_monitor, firmware_dir_changed, self); nm_clear_g_source (&priv->fw_changed_id); @@ -6426,11 +6514,11 @@ dispose (GObject *object) } if (priv->rfkill_mgr) { - g_signal_handlers_disconnect_by_func (priv->rfkill_mgr, rfkill_manager_rfkill_changed_cb, manager); + g_signal_handlers_disconnect_by_func (priv->rfkill_mgr, rfkill_manager_rfkill_changed_cb, self); g_clear_object (&priv->rfkill_mgr); } - nm_device_factory_manager_for_each_factory (_deinit_device_factory, manager); + nm_device_factory_manager_for_each_factory (_deinit_device_factory, self); nm_clear_g_source (&priv->timestamp_update_id); @@ -6445,6 +6533,8 @@ finalize (GObject *object) g_array_free (priv->capabilities, TRUE); G_OBJECT_CLASS (nm_manager_parent_class)->finalize (object); + + g_object_unref (priv->platform); } static void @@ -6542,6 +6632,18 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONNECTIVITY_CHECK_AVAILABLE] = + g_param_spec_boolean (NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE, "", "", + TRUE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_CONNECTIVITY_CHECK_ENABLED] = + g_param_spec_boolean (NM_MANAGER_CONNECTIVITY_CHECK_ENABLED, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PRIMARY_CONNECTION] = g_param_spec_string (NM_MANAGER_PRIMARY_CONNECTION, "", "", NULL, @@ -6560,13 +6662,6 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - /* Hostname is not exported over D-Bus */ - obj_properties[PROP_HOSTNAME] = - g_param_spec_string (NM_MANAGER_HOSTNAME, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - /* Sleeping is not exported over D-Bus */ obj_properties[PROP_SLEEPING] = g_param_spec_boolean (NM_MANAGER_SLEEPING, "", "", diff --git a/src/nm-manager.h b/src/nm-manager.h index 676fa995..622edb5b 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -45,6 +45,8 @@ #define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" #define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections" #define NM_MANAGER_CONNECTIVITY "connectivity" +#define NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE "connectivity-check-available" +#define NM_MANAGER_CONNECTIVITY_CHECK_ENABLED "connectivity-check-enabled" #define NM_MANAGER_PRIMARY_CONNECTION "primary-connection" #define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" @@ -54,7 +56,6 @@ #define NM_MANAGER_ALL_DEVICES "all-devices" /* Not exported */ -#define NM_MANAGER_HOSTNAME "hostname" #define NM_MANAGER_SLEEPING "sleeping" /* signals */ @@ -125,4 +126,11 @@ gboolean nm_manager_deactivate_connection (NMManager *manager, void nm_manager_set_capability (NMManager *self, NMCapability cap); +NMDevice * nm_manager_get_device (NMManager *self, + const char *ifname, + NMDeviceType device_type); +gboolean nm_manager_remove_device (NMManager *self, + const char *ifname, + NMDeviceType device_type); + #endif /* __NETWORKMANAGER_MANAGER_H__ */ diff --git a/src/nm-multi-index.c b/src/nm-multi-index.c deleted file mode 100644 index 6ae4c21f..00000000 --- a/src/nm-multi-index.c +++ /dev/null @@ -1,473 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-multi-index.h" - -#include <string.h> - -struct NMMultiIndex { - NMMultiIndexFuncEqual equal_fcn; - NMMultiIndexFuncClone clone_fcn; - GHashTable *hash; -}; - -typedef struct { - /* when storing the first item for a multi-index id, we don't yet create - * the hashtable @index. Instead we store it inplace to @value0. Note that - * &values_data->value0 is a NULL terminated array with one item that is - * suitable to be returned directly from nm_multi_index_lookup(). */ - union { - gpointer value0; - gpointer *values; - }; - GHashTable *index; -} ValuesData; - -/*****************************************************************************/ - -static void -_values_data_destroy (ValuesData *values_data) -{ - if (values_data->index) { - g_free (values_data->values); - g_hash_table_unref (values_data->index); - } - g_slice_free (ValuesData, values_data); -} - -static gboolean -_values_data_contains (ValuesData *values_data, gconstpointer value) -{ - return values_data->index - ? g_hash_table_contains (values_data->index, value) - : value == values_data->value0; -} - -static void -_values_data_get_data (ValuesData *values_data, - void *const**out_data, - guint *out_len) -{ - guint i, len; - gpointer *values; - GHashTableIter iter; - - nm_assert (values_data); - - if (!values_data->index) { - NM_SET_OUT (out_data, &values_data->value0); - NM_SET_OUT (out_len, 1); - return; - } - - nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0); - - if (!values_data->values) { - len = g_hash_table_size (values_data->index); - values = g_new (gpointer, len + 1); - - g_hash_table_iter_init (&iter, values_data->index); - for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++) - nm_assert (i < len); - nm_assert (i == len); - values[i] = NULL; - - values_data->values = values; - NM_SET_OUT (out_len, len); - } else if (out_len) - NM_SET_OUT (out_len, g_hash_table_size (values_data->index)); - - NM_SET_OUT (out_data, values_data->values); -} - -/*****************************************************************************/ - -/** - * nm_multi_index_lookup(): - * @index: - * @id: - * @out_len: (allow-none): output the number of values - * that are returned. - * - * Returns: (transfer none): %NULL if there are no values - * or a %NULL terminated array of pointers. - */ -void *const* -nm_multi_index_lookup (const NMMultiIndex *index, - const NMMultiIndexId *id, - guint *out_len) -{ - ValuesData *values_data; - void *const*values; - - g_return_val_if_fail (index, NULL); - g_return_val_if_fail (id, NULL); - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) { - if (out_len) - *out_len = 0; - return NULL; - } - _values_data_get_data (values_data, &values, out_len); - return values; -} - -gboolean -nm_multi_index_contains (const NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (id, FALSE); - g_return_val_if_fail (value, FALSE); - - values_data = g_hash_table_lookup (index->hash, id); - return values_data && _values_data_contains (values_data, value); -} - -const NMMultiIndexId * -nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, - gconstpointer value) -{ - GHashTableIter iter; - const NMMultiIndexId *id; - ValuesData *values_data; - - g_return_val_if_fail (index, NULL); - g_return_val_if_fail (value, NULL); - - /* reverse-lookup needs to iterate over all hash tables. It should - * still be fairly quick, if the number of hash tables is small. - * There is no O(1) reverse lookup implemented, because this access - * pattern is not what NMMultiIndex is here for. - * You are supposed to use NMMultiIndex by always knowing which @id - * a @value has. - */ - - g_hash_table_iter_init (&iter, index->hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { - if (_values_data_contains (values_data, value)) - return id; - } - return NULL; -} - -void -nm_multi_index_foreach (const NMMultiIndex *index, - gconstpointer value, - NMMultiIndexFuncForeach foreach_func, - gpointer user_data) -{ - GHashTableIter iter; - const NMMultiIndexId *id; - ValuesData *values_data; - guint len; - void *const*values; - - g_return_if_fail (index); - g_return_if_fail (foreach_func); - - g_hash_table_iter_init (&iter, index->hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { - if (value && !_values_data_contains (values_data, value)) - continue; - - _values_data_get_data (values_data, &values, &len); - if (!foreach_func (id, values, len, user_data)) - return; - } -} - -void -nm_multi_index_iter_init (NMMultiIndexIter *iter, - const NMMultiIndex *index, - gconstpointer value) -{ - g_return_if_fail (index); - g_return_if_fail (iter); - - g_hash_table_iter_init (&iter->_iter, index->hash); - iter->_index = index; - iter->_value = value; -} - -gboolean -nm_multi_index_iter_next (NMMultiIndexIter *iter, - const NMMultiIndexId **out_id, - void *const**out_values, - guint *out_len) -{ - const NMMultiIndexId *id; - ValuesData *values_data; - - g_return_val_if_fail (iter, FALSE); - - while (g_hash_table_iter_next (&iter->_iter, (gpointer *) &id, (gpointer *) &values_data)) { - if ( !iter->_value - || _values_data_contains (values_data, iter->_value)) { - if (out_values || out_len) - _values_data_get_data (values_data, out_values, out_len); - if (out_id) - *out_id = id; - return TRUE; - } - } - return FALSE; -} - -/*****************************************************************************/ - -void -nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, - const NMMultiIndex *index, - const NMMultiIndexId *id) -{ - ValuesData *values_data; - - g_return_if_fail (index); - g_return_if_fail (iter); - g_return_if_fail (id); - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) - iter->_state = 2; - else if (values_data->index) { - iter->_state = 1; - g_hash_table_iter_init (&iter->_iter, values_data->index); - } else { - iter->_state = 0; - iter->_value = values_data->value0; - } -} - -gboolean -nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, - void **out_value) -{ - g_return_val_if_fail (iter, FALSE); - - switch (iter->_state) { - case 0: - iter->_state = 2; - NM_SET_OUT (out_value, iter->_value); - return TRUE; - case 1: - return g_hash_table_iter_next (&iter->_iter, out_value, NULL); - case 2: - iter->_state = 3; - return FALSE; - default: - g_return_val_if_reached (FALSE); - } -} - -/*****************************************************************************/ - -static gboolean -_do_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) { - NMMultiIndexId *id_new; - - /* Contrary to GHashTable, we don't take ownership of the @id that was - * provided to nm_multi_index_add(). Instead we clone it via @clone_fcn - * when needed. - * - * The reason is, that we expect in most cases that there exists - * already a @id so that we don't need ownership of it (or clone it). - * By doing this, the caller can pass a stack allocated @id or - * reuse the @id for other insertions. - */ - id_new = index->clone_fcn (id); - if (!id_new) - g_return_val_if_reached (FALSE); - - values_data = g_slice_new0 (ValuesData); - values_data->value0 = (gpointer) value; - - g_hash_table_insert (index->hash, id_new, values_data); - } else { - if (!values_data->index) { - if (values_data->value0 == value) - return FALSE; - values_data->index = g_hash_table_new (NULL, NULL); - g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value); - g_hash_table_replace (values_data->index, values_data->value0, values_data->value0); - values_data->values = NULL; - } else { - if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value)) - return FALSE; - g_clear_pointer (&values_data->values, g_free); - } - } - return TRUE; -} - -static gboolean -_do_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) - return FALSE; - - if (values_data->index) { - if (!g_hash_table_remove (values_data->index, value)) - return FALSE; - if (g_hash_table_size (values_data->index) == 0) - g_hash_table_remove (index->hash, id); - else - g_clear_pointer (&values_data->values, g_free); - } else { - if (values_data->value0 != value) - return FALSE; - g_hash_table_remove (index->hash, id); - } - - return TRUE; -} - -gboolean -nm_multi_index_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (id, FALSE); - g_return_val_if_fail (value, FALSE); - - return _do_add (index, id, value); -} - -gboolean -nm_multi_index_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (value, FALSE); - - if (!id) - g_return_val_if_reached (FALSE); - return _do_remove (index, id, value); -} - -/** - * nm_multi_index_move: - * @index: - * @id_old: (allow-none): remove @value at @id_old - * @id_new: (allow-none): add @value under @id_new - * @value: the value to add - * - * Similar to a remove(), followed by an add(). The difference - * is, that we allow %NULL for both @id_old and @id_new. - * And the return value indicates whether @value was successfully - * removed *and* added. - * - * Returns: %TRUE, if the value was removed from @id_old and added - * as %id_new. %FALSE could mean, that @value was not added to @id_old - * before, or that that @value was already part of @id_new. */ -gboolean -nm_multi_index_move (NMMultiIndex *index, - const NMMultiIndexId *id_old, - const NMMultiIndexId *id_new, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (value, FALSE); - - if (!id_old && !id_new) { - /* nothing to do, @value was and is not in @index. */ - return TRUE; - } if (!id_old) { - /* add @value to @index with @id_new */ - return _do_add (index, id_new, value); - } else if (!id_new) { - /* remove @value from @index with @id_old */ - return _do_remove (index, id_old, value); - } else if (index->equal_fcn (id_old, id_new)) { - if (_do_add (index, id_new, value)) { - /* we would expect, that @value is already in @index, - * Return %FALSE, if it wasn't. */ - return FALSE; - } - return TRUE; - } else { - gboolean did_remove; - - did_remove = _do_remove (index, id_old, value); - return _do_add (index, id_new, value) && did_remove; - } -} - -/*****************************************************************************/ - -guint -nm_multi_index_get_num_groups (const NMMultiIndex *index) -{ - g_return_val_if_fail (index, 0); - return g_hash_table_size (index->hash); -} - -NMMultiIndex * -nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, - NMMultiIndexFuncEqual equal_fcn, - NMMultiIndexFuncClone clone_fcn, - NMMultiIndexFuncDestroy destroy_fcn) -{ - NMMultiIndex *index; - - g_return_val_if_fail (hash_fcn, NULL); - g_return_val_if_fail (equal_fcn, NULL); - g_return_val_if_fail (clone_fcn, NULL); - g_return_val_if_fail (destroy_fcn, NULL); - - index = g_new (NMMultiIndex, 1); - index->equal_fcn = equal_fcn; - index->clone_fcn = clone_fcn; - - index->hash = g_hash_table_new_full ((GHashFunc) hash_fcn, - (GEqualFunc) equal_fcn, - (GDestroyNotify) destroy_fcn, - (GDestroyNotify) _values_data_destroy); - return index; -} - -void -nm_multi_index_free (NMMultiIndex *index) -{ - g_return_if_fail (index); - g_hash_table_unref (index->hash); - g_free (index); -} - diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h deleted file mode 100644 index fb102574..00000000 --- a/src/nm-multi-index.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_MULTI_INDEX__ -#define __NM_MULTI_INDEX__ - -typedef struct { - char _dummy; -} NMMultiIndexId; - -typedef struct NMMultiIndex NMMultiIndex; - -typedef struct { - GHashTableIter _iter; - const NMMultiIndex *_index; - gconstpointer _value; -} NMMultiIndexIter; - -typedef struct { - union { - GHashTableIter _iter; - gpointer _value; - }; - guint _state; -} NMMultiIndexIdIter; - -typedef gboolean (*NMMultiIndexFuncEqual) (const NMMultiIndexId *id_a, const NMMultiIndexId *id_b); -typedef guint (*NMMultiIndexFuncHash) (const NMMultiIndexId *id); -typedef NMMultiIndexId *(*NMMultiIndexFuncClone) (const NMMultiIndexId *id); -typedef void (*NMMultiIndexFuncDestroy) (NMMultiIndexId *id); - -typedef gboolean (*NMMultiIndexFuncForeach) (const NMMultiIndexId *id, void *const* values, guint len, gpointer user_data); - - -NMMultiIndex *nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, - NMMultiIndexFuncEqual equal_fcn, - NMMultiIndexFuncClone clone_fcn, - NMMultiIndexFuncDestroy destroy_fcn); - -void nm_multi_index_free (NMMultiIndex *index); - -gboolean nm_multi_index_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -gboolean nm_multi_index_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -gboolean nm_multi_index_move (NMMultiIndex *index, - const NMMultiIndexId *id_old, - const NMMultiIndexId *id_new, - gconstpointer value); - -guint nm_multi_index_get_num_groups (const NMMultiIndex *index); - -void *const*nm_multi_index_lookup (const NMMultiIndex *index, - const NMMultiIndexId *id, - guint *out_len); - -gboolean nm_multi_index_contains (const NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -const NMMultiIndexId *nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, - gconstpointer value); - -void nm_multi_index_foreach (const NMMultiIndex *index, - gconstpointer value, - NMMultiIndexFuncForeach foreach_func, - gpointer user_data); - -void nm_multi_index_iter_init (NMMultiIndexIter *iter, - const NMMultiIndex *index, - gconstpointer value); -gboolean nm_multi_index_iter_next (NMMultiIndexIter *iter, - const NMMultiIndexId **out_id, - void *const**out_values, - guint *out_len); - -void nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, - const NMMultiIndex *index, - const NMMultiIndexId *id); -gboolean nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, - void **out_value); - -#endif /* __NM_MULTI_INDEX__ */ - diff --git a/src/nm-netns.c b/src/nm-netns.c index a81aa696..96ab2b35 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -22,10 +22,10 @@ #include "nm-netns.h" +#include "nm-utils/nm-dedup-multi.h" + #include "platform/nm-platform.h" #include "platform/nmp-netns.h" -#include "nm-route-manager.h" -#include "nm-default-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -38,8 +38,6 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; - NMRouteManager *route_manager; - NMDefaultRouteManager *default_route_manager; bool log_with_ptr; } NMNetnsPrivate; @@ -74,16 +72,10 @@ nm_netns_get_platform (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->platform; } -NMDefaultRouteManager * -nm_netns_get_default_route_manager (NMNetns *self) -{ - return NM_NETNS_GET_PRIVATE (self)->default_route_manager; -} - -NMRouteManager * -nm_netns_get_route_manager (NMNetns *self) +NMDedupMultiIndex * +nm_netns_get_multi_idx (NMNetns *self) { - return NM_NETNS_GET_PRIVATE (self)->route_manager; + return nm_platform_get_multi_idx (NM_NETNS_GET_PRIVATE (self)->platform); } /*****************************************************************************/ @@ -129,8 +121,6 @@ constructed (GObject *object) log_with_ptr = nm_platform_get_log_with_ptr (priv->platform); priv->platform_netns = nm_platform_netns_get (priv->platform); - priv->route_manager = nm_route_manager_new (log_with_ptr, priv->platform); - priv->default_route_manager = nm_default_route_manager_new (log_with_ptr, priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); } @@ -149,8 +139,6 @@ dispose (GObject *object) NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); - g_clear_object (&priv->route_manager); - g_clear_object (&priv->default_route_manager); g_clear_object (&priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->dispose (object); diff --git a/src/nm-netns.h b/src/nm-netns.h index fd5daf47..ae343cce 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -39,8 +39,8 @@ NMNetns *nm_netns_new (NMPlatform *platform); NMPlatform *nm_netns_get_platform (NMNetns *self); NMPNetns *nm_netns_get_platform_netns (NMNetns *self); -NMRouteManager *nm_netns_get_route_manager (NMNetns *self); -NMDefaultRouteManager *nm_netns_get_default_route_manager (NMNetns *self); + +struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); #define NM_NETNS_GET (nm_netns_get ()) diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index 87e0a364..08e10e40 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -27,8 +27,7 @@ #include "nm-proxy-config.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" - -static void pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data); +#include "nm-utils/c-list.h" #define PACRUNNER_DBUS_SERVICE "org.pacrunner" #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager" @@ -37,11 +36,15 @@ static void pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointe /*****************************************************************************/ struct _NMPacrunnerCallId { - NMPacrunnerManager *manager; + CList lst; + + /* this might be a dangling pointer after the async operation + * is cancelled. */ + NMPacrunnerManager *manager_maybe_dangling; + GVariant *args; char *path; guint refcount; - bool removed; }; typedef struct _NMPacrunnerCallId Config; @@ -49,8 +52,8 @@ typedef struct _NMPacrunnerCallId Config; typedef struct { char *iface; GDBusProxy *pacrunner; - GCancellable *pacrunner_cancellable; - GList *configs; + GCancellable *cancellable; + CList configs; } NMPacrunnerManagerPrivate; struct _NMPacrunnerManager { @@ -80,49 +83,59 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP G_STMT_START { \ nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ "%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - "pacrunner: call[", \ + _NMLOG2_PREFIX_NAME": call[", \ (config) \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END /*****************************************************************************/ +static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data); + +/*****************************************************************************/ + static Config * config_new (NMPacrunnerManager *manager, GVariant *args) { Config *config; config = g_slice_new0 (Config); - config->manager = manager; + config->manager_maybe_dangling = manager; config->args = g_variant_ref_sink (args); config->refcount = 1; + c_list_link_tail (&NM_PACRUNNER_MANAGER_GET_PRIVATE (manager)->configs, + &config->lst); return config; } -static void +static Config * config_ref (Config *config) { - g_assert (config); - g_assert (config->refcount > 0); + nm_assert (config); + nm_assert (config->refcount > 0); config->refcount++; + return config; } static void config_unref (Config *config) { - g_assert (config); - g_assert (config->refcount > 0); + nm_assert (config); + nm_assert (config->refcount > 0); if (config->refcount == 1) { g_variant_unref (config->args); g_free (config->path); + c_list_unlink (&config->lst); g_slice_free (Config, config); } else config->refcount--; } +/*****************************************************************************/ + static void add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config) { @@ -155,8 +168,11 @@ add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config static void get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) { + NMDedupMultiIter ipconf_iter; char *cidr; - int i; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *routes; + guint i; /* Extract searches */ for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++) @@ -167,18 +183,17 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_domain (ip4, i))); /* Add addresses and routes in CIDR form */ - for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4, i); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) { cidr = g_strdup_printf ("%s/%u", nm_utils_inet4_ntop (address->address, NULL), address->plen); g_ptr_array_add (domains, cidr); } - for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) { - const NMPlatformIP4Route *routes = nm_ip4_config_get_route (ip4, i); - + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; cidr = g_strdup_printf ("%s/%u", nm_utils_inet4_ntop (routes->network, NULL), routes->plen); @@ -189,8 +204,11 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) static void get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) { + NMDedupMultiIter ipconf_iter; char *cidr; - int i; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *routes; + guint i; /* Extract searches */ for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++) @@ -201,18 +219,16 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_domain (ip6, i))); /* Add addresses and routes in CIDR form */ - for (i = 0; i < nm_ip6_config_get_num_addresses (ip6); i++) { - const NMPlatformIP6Address *address = nm_ip6_config_get_address (ip6, i); - + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) { cidr = g_strdup_printf ("%s/%u", nm_utils_inet6_ntop (&address->address, NULL), address->plen); g_ptr_array_add (domains, cidr); } - for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) { - const NMPlatformIP6Route *routes = nm_ip6_config_get_route (ip6, i); - + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; cidr = g_strdup_printf ("%s/%u", nm_utils_inet6_ntop (&routes->network, NULL), routes->plen); @@ -220,8 +236,18 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) } } +/*****************************************************************************/ + +static GCancellable * +_ensure_cancellable (NMPacrunnerManagerPrivate *priv) +{ + if (G_UNLIKELY (!priv->cancellable)) + priv->cancellable = g_cancellable_new (); + return priv->cancellable; +} + static void -pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) +pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data) { Config *config = user_data; NMPacrunnerManager *self; @@ -230,15 +256,13 @@ pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) gs_unref_variant GVariant *variant = NULL; const char *path = NULL; - g_return_if_fail (!config->path); + nm_assert (!config->path); - variant = g_dbus_proxy_call_finish (proxy, res, &error); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - config_unref (config); - return; - } + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + goto out; - self = NM_PACRUNNER_MANAGER (config->manager); + self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling); priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); if (!variant) @@ -246,21 +270,23 @@ pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) else { g_variant_get (variant, "(&o)", &path); - config->path = g_strdup (path); - _LOG2D (config, "sent"); - - if (config->removed) { - config_ref (config); + if (c_list_is_empty (&config->lst)) { + _LOG2D (config, "sent (%s), but destory it right away", path); g_dbus_proxy_call (priv->pacrunner, "DestroyProxyConfiguration", - g_variant_new ("(o)", config->path), + g_variant_new ("(o)", path), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, - priv->pacrunner_cancellable, - (GAsyncReadyCallback) pacrunner_remove_done, - config); + _ensure_cancellable (priv), + pacrunner_remove_done, + config_ref (config)); + } else { + _LOG2D (config, "sent (%s)", path); + config->path = g_strdup (path); } } + +out: config_unref (config); } @@ -272,17 +298,15 @@ pacrunner_send_config (NMPacrunnerManager *self, Config *config) if (priv->pacrunner) { _LOG2T (config, "sending..."); - config_ref (config); - g_clear_pointer (&config->path, g_free); - + nm_assert (!config->path); g_dbus_proxy_call (priv->pacrunner, "CreateProxyConfiguration", config->args, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, - priv->pacrunner_cancellable, - (GAsyncReadyCallback) pacrunner_send_done, - config); + _ensure_cancellable (priv), + pacrunner_send_done, + config_ref (config)); } } @@ -291,15 +315,18 @@ name_owner_changed (NMPacrunnerManager *self) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); gs_free char *owner = NULL; - GList *iter = NULL; + CList *iter; owner = g_dbus_proxy_get_name_owner (priv->pacrunner); if (owner) { _LOGD ("name owner appeared (%s)", owner); - for (iter = g_list_first (priv->configs); iter; iter = g_list_next (iter)) - pacrunner_send_config (self, iter->data); + c_list_for_each (iter, &priv->configs) + pacrunner_send_config (self, c_list_entry (iter, Config, lst)); } else { _LOGD ("name owner disappeared"); + nm_clear_g_cancellable (&priv->cancellable); + c_list_for_each (iter, &priv->configs) + nm_clear_g_free (&c_list_entry (iter, Config, lst)->path); } } @@ -316,21 +343,19 @@ pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMPacrunnerManager *self = user_data; NMPacrunnerManagerPrivate *priv; - GError *error = NULL; + gs_free_error GError *error = NULL; GDBusProxy *proxy; proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (!proxy) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - _LOGW ("failed to connect to pacrunner via DBus: %s", error->message); - g_error_free (error); + _LOGE ("failed to create D-Bus proxy for pacrunner: %s", error->message); return; } priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); priv->pacrunner = proxy; - g_signal_connect (priv->pacrunner, "notify::g-name-owner", G_CALLBACK (name_owner_changed_cb), self); name_owner_changed (self); @@ -420,7 +445,6 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self, } config = config_new (self, g_variant_new ("(a{sv})", &proxy_data)); - priv->configs = g_list_append (priv->configs, config); { gs_free char *args_str = NULL; @@ -439,26 +463,24 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self, } static void -pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) +pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data) { Config *config = user_data; NMPacrunnerManager *self; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; - ret = g_dbus_proxy_call_finish (proxy, res, &error); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - config_unref (config); - return; - } - - self = NM_PACRUNNER_MANAGER (config->manager); + ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + goto out; + self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling); if (!ret) _LOG2D (config, "remove failed: %s", error->message); else _LOG2D (config, "removed"); +out: config_unref (config); } @@ -472,7 +494,6 @@ nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_i { NMPacrunnerManagerPrivate *priv; Config *config; - GList *list; g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self)); g_return_if_fail (call_id); @@ -482,31 +503,29 @@ nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_i _LOG2T (config, "removing..."); - list = g_list_find (priv->configs, config); - if (!list) - g_return_if_reached (); + nm_assert (c_list_contains (&priv->configs, &config->lst)); if (priv->pacrunner) { if (!config->path) { - /* send() failed or is still pending. Mark the item as - * removed, so that we ask pacrunner to drop it when the - * send() completes. + /* send() failed or is still pending. The item is unlinked from + * priv->configs, so pacrunner_send_done() knows to call + * DestroyProxyConfiguration right away. */ - config->removed = TRUE; - config_unref (config); } else { g_dbus_proxy_call (priv->pacrunner, "DestroyProxyConfiguration", g_variant_new ("(o)", config->path), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, - priv->pacrunner_cancellable, - (GAsyncReadyCallback) pacrunner_remove_done, - config); + _ensure_cancellable (priv), + pacrunner_remove_done, + config_ref (config)); + nm_clear_g_free (&config->path); } - } else - config_unref (config); - priv->configs = g_list_delete_link (priv->configs, list); + } + + c_list_unlink_init (&config->lst); + config_unref (config); } gboolean @@ -532,16 +551,15 @@ nm_pacrunner_manager_init (NMPacrunnerManager *self) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - priv->pacrunner_cancellable = g_cancellable_new (); - + c_list_init (&priv->configs); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, PACRUNNER_DBUS_SERVICE, PACRUNNER_DBUS_PATH, PACRUNNER_DBUS_INTERFACE, - priv->pacrunner_cancellable, - (GAsyncReadyCallback) pacrunner_proxy_cb, + _ensure_cancellable (priv), + pacrunner_proxy_cb, self); } @@ -549,14 +567,22 @@ static void dispose (GObject *object) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object); + CList *iter, *safe; + + c_list_for_each_safe (iter, safe, &priv->configs) { + c_list_unlink_init (iter); + config_unref (c_list_entry (iter, Config, lst)); + } + + /* we cancel all pending operations. Note that pacrunner automatically + * removes all configuration once NetworkManager disconnects from + * the bus -- which happens soon after we destroy the pacrunner manager. + */ + nm_clear_g_cancellable (&priv->cancellable); g_clear_pointer (&priv->iface, g_free); - nm_clear_g_cancellable (&priv->pacrunner_cancellable); g_clear_object (&priv->pacrunner); - g_list_free_full (priv->configs, (GDestroyNotify) config_unref); - priv->configs = NULL; - G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object); } diff --git a/src/nm-policy.c b/src/nm-policy.c index 7c74a6b4..3cfb1f7c 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -31,7 +31,6 @@ #include "NetworkManagerUtils.h" #include "nm-act-request.h" #include "devices/nm-device.h" -#include "nm-default-route-manager.h" #include "nm-setting-ip4-config.h" #include "nm-setting-connection.h" #include "platform/nm-platform.h" @@ -49,6 +48,7 @@ #include "nm-dhcp6-config.h" #include "nm-config.h" #include "nm-netns.h" +#include "nm-hostname-manager.h" /*****************************************************************************/ @@ -68,17 +68,23 @@ typedef struct { GSList *pending_activation_checks; GHashTable *devices; + GHashTable *pending_active_connections; GSList *pending_secondaries; NMSettings *settings; + NMHostnameManager *hostname_manager; + NMDevice *default_device4, *activating_device4; NMDevice *default_device6, *activating_device6; - GResolver *resolver; - GInetAddress *lookup_addr; - GCancellable *lookup_cancellable; + struct { + GInetAddress *addr; + GResolver *resolver; + GCancellable *cancellable; + } lookup; + NMDnsManager *dns_manager; gulong config_changed_id; @@ -136,6 +142,7 @@ _PRIV_TO_SELF (NMPolicyPrivate *priv) /*****************************************************************************/ static void schedule_activate_all (NMPolicy *self); +static void schedule_activate_check (NMPolicy *self, NMDevice *device); /*****************************************************************************/ @@ -369,25 +376,81 @@ device_ip6_subnet_needed (NMDevice *device, /*****************************************************************************/ static NMDevice * -get_best_ip4_device (NMPolicy *self, gboolean fully_activated) +get_best_ip_device (NMPolicy *self, + int addr_family, + gboolean fully_activated) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + const GSList *iter; + NMDevice *best_device; + NMDevice *prev_device; + guint32 best_metric = G_MAXUINT32; + gboolean best_is_fully_activated = FALSE; + + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + /* we prefer the current device in case of identical metric. + * Hence, try that one first.*/ + best_device = NULL; + prev_device = addr_family == AF_INET + ? (fully_activated ? priv->default_device4 : priv->activating_device4) + : (fully_activated ? priv->default_device6 : priv->activating_device6); + + for (iter = nm_manager_get_devices (priv->manager); iter; iter = iter->next) { + NMDevice *device = NM_DEVICE (iter->data); + NMDeviceState state; + const NMPObject *r; + NMConnection *connection; + guint32 metric; + gboolean is_fully_activated; - return nm_default_route_manager_ip4_get_best_device (nm_netns_get_default_route_manager (priv->netns), - nm_manager_get_devices (priv->manager), - fully_activated, - priv->default_device4); -} + state = nm_device_get_state (device); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) + continue; -static NMDevice * -get_best_ip6_device (NMPolicy *self, gboolean fully_activated) -{ - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + if (nm_device_sys_iface_state_is_external (device)) + continue; + + r = nm_device_get_best_default_route (device, addr_family); + if (r) { + /* XXX: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN, + * which means it was injected by a VPN, not added by device. + * + * In this case, is it really the best device? Why do we even need the best + * device?? */ + metric = nm_utils_ip_route_metric_normalize (addr_family, + NMP_OBJECT_CAST_IP_ROUTE (r)->metric); + is_fully_activated = TRUE; + } else if ( !fully_activated + && (connection = nm_device_get_applied_connection (device)) + && nm_utils_connection_has_default_route (connection, addr_family, NULL)) { + metric = nm_utils_ip_route_metric_normalize (addr_family, + nm_device_get_route_metric (device, addr_family)); + is_fully_activated = FALSE; + } else + continue; + + if ( !best_device + || (!best_is_fully_activated && is_fully_activated) + || ( metric < best_metric + || (metric == best_metric && device == prev_device))) { + best_device = device; + best_metric = metric; + best_is_fully_activated = is_fully_activated; + } + } - return nm_default_route_manager_ip6_get_best_device (nm_netns_get_default_route_manager (priv->netns), - nm_manager_get_devices (priv->manager), - fully_activated, - priv->default_device6); + if ( !fully_activated + && best_device + && best_is_fully_activated) { + /* There's only a best activating device if the best device + * among all activating and already-activated devices is a + * still-activating one. */ + return NULL; + } + + return best_device; } static gboolean @@ -443,50 +506,46 @@ settings_set_hostname_cb (const char *hostname, #define HOST_NAME_BUFSIZE (HOST_NAME_MAX + 2) static char * -_get_hostname (NMPolicy *self, char **hostname) +_get_hostname (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - char *buf; - - g_assert (hostname && *hostname == NULL); + char *hostname = NULL; /* If there is an in-progress hostname change, return * the last hostname set as would be set soon... */ if (priv->changing_hostname) { _LOGT (LOGD_DNS, "get-hostname: \"%s\" (last on set)", priv->last_hostname); - *hostname = g_strdup (priv->last_hostname); - return *hostname; + return g_strdup (priv->last_hostname); } /* try to get the hostname via dbus... */ - if (nm_settings_get_transient_hostname (priv->settings, hostname)) { - _LOGT (LOGD_DNS, "get-hostname: \"%s\" (from dbus)", *hostname); - return *hostname; + if (nm_hostname_manager_get_transient_hostname (priv->hostname_manager, &hostname)) { + _LOGT (LOGD_DNS, "get-hostname: \"%s\" (from dbus)", hostname); + return hostname; } /* ...or retrieve it by yourself */ - buf = g_malloc (HOST_NAME_BUFSIZE); - if (gethostname (buf, HOST_NAME_BUFSIZE -1) != 0) { + hostname = g_malloc (HOST_NAME_BUFSIZE); + if (gethostname (hostname, HOST_NAME_BUFSIZE -1) != 0) { int errsv = errno; _LOGT (LOGD_DNS, "get-hostname: couldn't get the system hostname: (%d) %s", errsv, g_strerror (errsv)); - g_free (buf); + g_free (hostname); return NULL; } /* the name may be truncated... */ - buf[HOST_NAME_BUFSIZE - 1] = '\0'; - if (strlen (buf) >= HOST_NAME_BUFSIZE -1) { - _LOGT (LOGD_DNS, "get-hostname: system hostname too long: \"%s\"", buf); - g_free (buf); + hostname[HOST_NAME_BUFSIZE - 1] = '\0'; + if (strlen (hostname) >= HOST_NAME_BUFSIZE -1) { + _LOGT (LOGD_DNS, "get-hostname: system hostname too long: \"%s\"", hostname); + g_free (hostname); return NULL; } - _LOGT (LOGD_DNS, "get-hostname: \"%s\"", buf); - *hostname = buf; - return *hostname; + _LOGT (LOGD_DNS, "get-hostname: \"%s\"", hostname); + return hostname; } static void @@ -508,7 +567,7 @@ _set_hostname (NMPolicy *self, * restart the reverse lookup thread later. */ if (new_hostname) - g_clear_object (&priv->lookup_addr); + g_clear_object (&priv->lookup.addr); /* Update the DNS only if the hostname is actually * going to change. @@ -534,7 +593,7 @@ _set_hostname (NMPolicy *self, name = new_hostname; /* Don't set the hostname if it isn't actually changing */ - if ( _get_hostname (self, &old_hostname) + if ( (old_hostname = _get_hostname (self)) && (nm_streq (name, old_hostname))) { _LOGT (LOGD_DNS, "set-hostname: hostname already set to '%s' (%s)", name, msg); return; @@ -549,10 +608,10 @@ _set_hostname (NMPolicy *self, /* Ask NMSettings to update the transient hostname using its * systemd-hostnamed proxy */ - nm_settings_set_transient_hostname (priv->settings, - name, - settings_set_hostname_cb, - g_object_ref (self)); + nm_hostname_manager_set_transient_hostname (priv->hostname_manager, + name, + settings_set_hostname_cb, + g_object_ref (self)); } static void @@ -572,7 +631,7 @@ lookup_callback (GObject *source, self = user_data; priv = NM_POLICY_GET_PRIVATE (self); - g_clear_object (&priv->lookup_cancellable); + g_clear_object (&priv->lookup.cancellable); if (hostname) _set_hostname (self, hostname, "from address lookup"); @@ -581,15 +640,30 @@ lookup_callback (GObject *source, } static void -update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const char *msg) +lookup_by_address (NMPolicy *self) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + + nm_clear_g_cancellable (&priv->lookup.cancellable); + priv->lookup.cancellable = g_cancellable_new (); + g_resolver_lookup_by_address_async (priv->lookup.resolver, + priv->lookup.addr, + priv->lookup.cancellable, + lookup_callback, self); +} + +static void +update_system_hostname (NMPolicy *self, const char *msg) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - char *configured_hostname = NULL; + const char *configured_hostname; gs_free char *temp_hostname = NULL; const char *dhcp_hostname, *p; NMIP4Config *ip4_config; NMIP6Config *ip6_config; gboolean external_hostname = FALSE; + const NMPlatformIP4Address *addr4; + const NMPlatformIP6Address *addr6; g_return_if_fail (self != NULL); @@ -600,12 +674,12 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const _LOGT (LOGD_DNS, "set-hostname: updating hostname (%s)", msg); - nm_clear_g_cancellable (&priv->lookup_cancellable); + nm_clear_g_cancellable (&priv->lookup.cancellable); /* Check if the hostname was set externally to NM, so that in that case * we can avoid to fallback to the one we got when we started. * Consider "not specific" hostnames as equal. */ - if ( _get_hostname (self, &temp_hostname) + if ( (temp_hostname = _get_hostname (self)) && !nm_streq0 (temp_hostname, priv->last_hostname) && ( nm_utils_is_specific_hostname (temp_hostname) || nm_utils_is_specific_hostname (priv->last_hostname))) { @@ -614,14 +688,14 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const temp_hostname); priv->dhcp_hostname = FALSE; + if (!nm_utils_is_specific_hostname (temp_hostname)) + nm_clear_g_free (&temp_hostname); if (!nm_streq0 (temp_hostname, priv->orig_hostname)) { /* Update original (fallback) hostname */ g_free (priv->orig_hostname); - if (nm_utils_is_specific_hostname (temp_hostname)) { - priv->orig_hostname = temp_hostname; - temp_hostname = NULL; - } else - priv->orig_hostname = NULL; + priv->orig_hostname = g_steal_pointer (&temp_hostname); + _LOGT (LOGD_DNS, "hostname-original: update to %s%s%s", + NM_PRINT_FMT_QUOTE_STRING (priv->orig_hostname)); } } @@ -635,56 +709,46 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const */ /* Try a persistent hostname first */ - g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL); + configured_hostname = nm_hostname_manager_get_hostname (priv->hostname_manager); if (configured_hostname && nm_utils_is_specific_hostname (configured_hostname)) { _set_hostname (self, configured_hostname, "from system configuration"); priv->dhcp_hostname = FALSE; - g_free (configured_hostname); return; } - g_free (configured_hostname); - /* Try automatically determined hostname from the best device's IP config */ - if (!best4) - best4 = get_best_ip4_device (self, TRUE); - if (!best6) - best6 = get_best_ip6_device (self, TRUE); - - if (best4) { + if (priv->default_device4) { NMDhcp4Config *dhcp4_config; /* Grab a hostname out of the device's DHCP4 config */ - dhcp4_config = nm_device_get_dhcp4_config (best4); + dhcp4_config = nm_device_get_dhcp4_config (priv->default_device4); if (dhcp4_config) { - p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name"); - if (dhcp_hostname && strlen (dhcp_hostname)) { - /* Sanity check; strip leading spaces */ - while (*p) { - if (!g_ascii_isspace (*p++)) { - _set_hostname (self, p-1, "from DHCPv4"); - priv->dhcp_hostname = TRUE; - return; - } + dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name"); + if (dhcp_hostname && dhcp_hostname[0]) { + p = nm_str_skip_leading_spaces (dhcp_hostname); + if (p[0]) { + _set_hostname (self, p, "from DHCPv4"); + priv->dhcp_hostname = TRUE; + return; } _LOGW (LOGD_DNS, "set-hostname: DHCPv4-provided hostname '%s' looks invalid; ignoring it", dhcp_hostname); } } - } else if (best6) { + } + + if (priv->default_device6) { NMDhcp6Config *dhcp6_config; /* Grab a hostname out of the device's DHCP6 config */ - dhcp6_config = nm_device_get_dhcp6_config (best6); + dhcp6_config = nm_device_get_dhcp6_config (priv->default_device6); if (dhcp6_config) { - p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name"); - if (dhcp_hostname && strlen (dhcp_hostname)) { - /* Sanity check; strip leading spaces */ - while (*p) { - if (!g_ascii_isspace (*p++)) { - _set_hostname (self, p-1, "from DHCPv6"); - priv->dhcp_hostname = TRUE; - return; - } + dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name"); + if (dhcp_hostname && dhcp_hostname[0]) { + p = nm_str_skip_leading_spaces (dhcp_hostname); + if (p[0]) { + _set_hostname (self, p, "from DHCPv6"); + priv->dhcp_hostname = TRUE; + return; } _LOGW (LOGD_DNS, "set-hostname: DHCPv6-provided hostname '%s' looks invalid; ignoring it", dhcp_hostname); @@ -711,7 +775,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const priv->dhcp_hostname = FALSE; - if (!best4 && !best6) { + if (!priv->default_device4 && !priv->default_device6) { /* No best device; fall back to the last hostname set externally * to NM or if there wasn't one, 'localhost.localdomain' */ @@ -730,22 +794,18 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const /* No configured hostname, no automatically determined hostname, and no * bootup hostname. Start reverse DNS of the current IPv4 or IPv6 address. */ - ip4_config = best4 ? nm_device_get_ip4_config (best4) : NULL; - ip6_config = best6 ? nm_device_get_ip6_config (best6) : NULL; - - if (ip4_config && nm_ip4_config_get_num_addresses (ip4_config) > 0) { - const NMPlatformIP4Address *addr4; + ip4_config = priv->default_device4 ? nm_device_get_ip4_config (priv->default_device4) : NULL; + ip6_config = priv->default_device6 ? nm_device_get_ip6_config (priv->default_device6) : NULL; - addr4 = nm_ip4_config_get_address (ip4_config, 0); - g_clear_object (&priv->lookup_addr); - priv->lookup_addr = g_inet_address_new_from_bytes ((guint8 *) &addr4->address, + if ( ip4_config + && (addr4 = nm_ip4_config_get_first_address (ip4_config))) { + g_clear_object (&priv->lookup.addr); + priv->lookup.addr = g_inet_address_new_from_bytes ((guint8 *) &addr4->address, G_SOCKET_FAMILY_IPV4); - } else if (ip6_config && nm_ip6_config_get_num_addresses (ip6_config) > 0) { - const NMPlatformIP6Address *addr6; - - addr6 = nm_ip6_config_get_address (ip6_config, 0); - g_clear_object (&priv->lookup_addr); - priv->lookup_addr = g_inet_address_new_from_bytes ((guint8 *) &addr6->address, + } else if ( ip6_config + && (addr6 = nm_ip6_config_get_first_address (ip6_config))) { + g_clear_object (&priv->lookup.addr); + priv->lookup.addr = g_inet_address_new_from_bytes ((guint8 *) &addr6->address, G_SOCKET_FAMILY_IPV6); } else { /* No valid IP config; fall back to localhost.localdomain */ @@ -753,11 +813,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const return; } - priv->lookup_cancellable = g_cancellable_new (); - g_resolver_lookup_by_address_async (priv->resolver, - priv->lookup_addr, - priv->lookup_cancellable, - lookup_callback, self); + lookup_by_address (self); } static void @@ -783,20 +839,83 @@ update_default_ac (NMPolicy *self, set_active_func (best, TRUE); } -static NMIP4Config * -get_best_ip4_config (NMPolicy *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) +static gpointer +get_best_ip_config (NMPolicy *self, + int addr_family, + const char **out_ip_iface, + NMActiveConnection **out_ac, + NMDevice **out_device, + NMVpnConnection **out_vpn) { - return nm_default_route_manager_ip4_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns), - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + NMDevice *device; + gpointer conf; + const GSList *iter; + + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + for (iter = nm_manager_get_active_connections (priv->manager); iter; iter = iter->next) { + NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data); + NMVpnConnection *candidate; + NMVpnConnectionState vpn_state; + + if (!NM_IS_VPN_CONNECTION (active)) + continue; + + candidate = NM_VPN_CONNECTION (active); + + vpn_state = nm_vpn_connection_get_vpn_state (candidate); + if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) + continue; + + if (addr_family == AF_INET) + conf = nm_vpn_connection_get_ip4_config (candidate); + else + conf = nm_vpn_connection_get_ip6_config (candidate); + if (!conf) + continue; + + if (addr_family == AF_INET) { + if (!nm_ip4_config_best_default_route_get (conf)) + continue; + } else { + if (!nm_ip6_config_best_default_route_get (conf)) + continue; + } + + /* FIXME: in case of multiple VPN candidates, choose the one with the + * best metric. */ + NM_SET_OUT (out_device, NULL); + NM_SET_OUT (out_vpn, candidate); + NM_SET_OUT (out_ac, active); + NM_SET_OUT (out_ip_iface, nm_vpn_connection_get_ip_iface (candidate, TRUE)); + return conf; + } + + device = get_best_ip_device (self, addr_family, TRUE); + if (device) { + NMActRequest *req; + + if (addr_family == AF_INET) + conf = nm_device_get_ip4_config (device); + else + conf = nm_device_get_ip6_config (device); + req = nm_device_get_act_request (device); + + if (conf && req) { + NM_SET_OUT (out_device, device); + NM_SET_OUT (out_vpn, NULL); + NM_SET_OUT (out_ac, NM_ACTIVE_CONNECTION (req)); + NM_SET_OUT (out_ip_iface, nm_device_get_ip_iface (device)); + return conf; + } + } + + NM_SET_OUT (out_device, NULL); + NM_SET_OUT (out_vpn, NULL); + NM_SET_OUT (out_ac, NULL); + NM_SET_OUT (out_ip_iface, NULL); + return NULL; } static void @@ -807,7 +926,7 @@ update_ip4_dns (NMPolicy *self, NMDnsManager *dns_mgr) NMVpnConnection *vpn = NULL; NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; - ip4_config = get_best_ip4_config (self, TRUE, &ip_iface, NULL, NULL, &vpn); + ip4_config = get_best_ip_config (self, AF_INET, &ip_iface, NULL, NULL, &vpn); if (ip4_config) { if (vpn) dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; @@ -823,8 +942,7 @@ static void update_ip4_routing (NMPolicy *self, gboolean force_update) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - NMDevice *best = NULL, *default_device; - NMConnection *connection = NULL; + NMDevice *best = NULL; NMVpnConnection *vpn = NULL; NMActiveConnection *best_ac = NULL; const char *ip_iface = NULL; @@ -832,19 +950,18 @@ update_ip4_routing (NMPolicy *self, gboolean force_update) /* Note that we might have an IPv4 VPN tunneled over an IPv6-only device, * so we can get (vpn != NULL && best == NULL). */ - if (!get_best_ip4_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) { - gboolean changed; - - changed = (priv->default_device4 != NULL); - priv->default_device4 = NULL; - if (changed) + if (!get_best_ip_config (self, AF_INET, &ip_iface, &best_ac, &best, &vpn)) { + if (nm_clear_g_object (&priv->default_device4)) { + _LOGt (LOGD_DNS, "set-default-device-4: %p", NULL); _notify (self, PROP_DEFAULT_IP4_DEVICE); - + } return; } g_assert ((best || vpn) && best_ac); - if (!force_update && best && (best == priv->default_device4)) + if ( !force_update + && best + && best == priv->default_device4) return; if (best) { @@ -862,38 +979,20 @@ update_ip4_routing (NMPolicy *self, gboolean force_update) } if (vpn) - default_device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); - else - default_device = best; + best = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); update_default_ac (self, best_ac, nm_active_connection_set_default); - if (default_device == priv->default_device4) + if (!nm_g_object_ref_set (&priv->default_device4, best)) return; + _LOGt (LOGD_DNS, "set-default-device-4: %p", priv->default_device4); - priv->default_device4 = default_device; - connection = nm_active_connection_get_applied_connection (best_ac); _LOGI (LOGD_CORE, "set '%s' (%s) as default for IPv4 routing and DNS", - nm_connection_get_id (connection), ip_iface); + nm_connection_get_id (nm_active_connection_get_applied_connection (best_ac)), + ip_iface); _notify (self, PROP_DEFAULT_IP4_DEVICE); } -static NMIP6Config * -get_best_ip6_config (NMPolicy *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return nm_default_route_manager_ip6_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns), - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - static void update_ip6_dns_delegation (NMPolicy *self) { @@ -917,7 +1016,7 @@ update_ip6_dns (NMPolicy *self, NMDnsManager *dns_mgr) NMVpnConnection *vpn = NULL; NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; - ip6_config = get_best_ip6_config (self, TRUE, &ip_iface, NULL, NULL, &vpn); + ip6_config = get_best_ip_config (self, AF_INET6, &ip_iface, NULL, NULL, &vpn); if (ip6_config) { if (vpn) dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; @@ -951,8 +1050,7 @@ static void update_ip6_routing (NMPolicy *self, gboolean force_update) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - NMDevice *best = NULL, *default_device6; - NMConnection *connection = NULL; + NMDevice *best = NULL; NMVpnConnection *vpn = NULL; NMActiveConnection *best_ac = NULL; const char *ip_iface = NULL; @@ -960,19 +1058,18 @@ update_ip6_routing (NMPolicy *self, gboolean force_update) /* Note that we might have an IPv6 VPN tunneled over an IPv4-only device, * so we can get (vpn != NULL && best == NULL). */ - if (!get_best_ip6_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) { - gboolean changed; - - changed = (priv->default_device6 != NULL); - priv->default_device6 = NULL; - if (changed) + if (!get_best_ip_config (self, AF_INET6, &ip_iface, &best_ac, &best, &vpn)) { + if (nm_clear_g_object (&priv->default_device6)) { + _LOGt (LOGD_DNS, "set-default-device-6: %p", NULL); _notify (self, PROP_DEFAULT_IP6_DEVICE); - + } return; } g_assert ((best || vpn) && best_ac); - if (!force_update && best && (best == priv->default_device6)) + if ( !force_update + && best + && best == priv->default_device6) return; if (best) { @@ -990,21 +1087,19 @@ update_ip6_routing (NMPolicy *self, gboolean force_update) } if (vpn) - default_device6 = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); - else - default_device6 = best; + best = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); update_default_ac (self, best_ac, nm_active_connection_set_default6); - if (default_device6 == priv->default_device6) + if (!nm_g_object_ref_set (&priv->default_device6, best)) return; - priv->default_device6 = default_device6; + _LOGt (LOGD_DNS, "set-default-device-6: %p", priv->default_device6); update_ip6_prefix_delegation (self); - connection = nm_active_connection_get_applied_connection (best_ac); _LOGI (LOGD_CORE, "set '%s' (%s) as default for IPv6 routing and DNS", - nm_connection_get_id (connection), ip_iface); + nm_connection_get_id (nm_active_connection_get_applied_connection (best_ac)), + ip_iface); _notify (self, PROP_DEFAULT_IP6_DEVICE); } @@ -1022,7 +1117,7 @@ update_routing_and_dns (NMPolicy *self, gboolean force_update) update_ip6_routing (self, force_update); /* Update the system hostname */ - update_system_hostname (self, priv->default_device4, priv->default_device6, "routing and dns"); + update_system_hostname (self, "routing and dns"); nm_dns_manager_end_updates (priv->dns_manager, __func__); } @@ -1031,24 +1126,23 @@ static void check_activating_devices (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - GObject *object = G_OBJECT (self); NMDevice *best4, *best6 = NULL; - best4 = get_best_ip4_device (self, FALSE); - best6 = get_best_ip6_device (self, FALSE); + best4 = get_best_ip_device (self, AF_INET, FALSE); + best6 = get_best_ip_device (self, AF_INET6, FALSE); - g_object_freeze_notify (object); + g_object_freeze_notify (G_OBJECT (self)); - if (best4 != priv->activating_device4) { - priv->activating_device4 = best4; + if (nm_g_object_ref_set (&priv->activating_device4, best4)) { + _LOGt (LOGD_DNS, "set-activating-device-4: %p", priv->activating_device4); _notify (self, PROP_ACTIVATING_IP4_DEVICE); } - if (best6 != priv->activating_device6) { - priv->activating_device6 = best6; + if (nm_g_object_ref_set (&priv->activating_device6, best6)) { + _LOGt (LOGD_DNS, "set-activating-device-6: %p", priv->activating_device6); _notify (self, PROP_ACTIVATING_IP6_DEVICE); } - g_object_thaw_notify (object); + g_object_thaw_notify (G_OBJECT (self)); } typedef struct { @@ -1073,6 +1167,45 @@ activate_data_free (ActivateData *data) } static void +pending_ac_gone (gpointer data, GObject *where_the_object_was) +{ + NMPolicy *self = NM_POLICY (data); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + + /* Active connections should reach the DEACTIVATED state + * before disappearing. */ + nm_assert_not_reached(); + + if (g_hash_table_remove (priv->pending_active_connections, where_the_object_was)) + g_object_unref (self); +} + +static void +pending_ac_state_changed (NMActiveConnection *ac, guint state, guint reason, NMPolicy *self) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + NMSettingsConnection *con; + + if (state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + /* The AC is being deactivated before the device had a chance + * to move to PREPARE. Schedule a new auto-activation on the + * device, but block the current connection to avoid an activation + * loop. + */ + con = nm_active_connection_get_settings_connection (ac); + nm_settings_connection_autoconnect_blocked_reason_set (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED); + schedule_activate_check (self, nm_active_connection_get_device (ac)); + + /* Cleanup */ + g_signal_handlers_disconnect_by_func (ac, pending_ac_state_changed, self); + if (!g_hash_table_remove (priv->pending_active_connections, ac)) + nm_assert_not_reached (); + g_object_weak_unref (G_OBJECT (ac), pending_ac_gone, self); + g_object_unref (self); + } +} + +static void auto_activate_device (NMPolicy *self, NMDevice *device) { @@ -1102,9 +1235,23 @@ auto_activate_device (NMPolicy *self, best_connection = NULL; for (i = 0; i < len; i++) { NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (connections[i]); + NMSettingConnection *s_con; + const char *permission; - if (!nm_settings_connection_can_autoconnect (candidate)) + if ( !nm_settings_connection_is_visible (candidate) + || nm_settings_connection_autoconnect_retries_get (candidate) == 0 + || nm_settings_connection_autoconnect_blocked_reason_get (candidate) != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE) continue; + + s_con = nm_connection_get_setting_connection (NM_CONNECTION (candidate)); + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (candidate)); + if ( permission + && !nm_settings_connection_check_permission (candidate, permission)) + continue; + if (nm_device_can_auto_connect (device, (NMConnection *) candidate, &specific_object)) { best_connection = candidate; break; @@ -1114,24 +1261,41 @@ auto_activate_device (NMPolicy *self, if (best_connection) { GError *error = NULL; NMAuthSubject *subject; + NMActiveConnection *ac; _LOGI (LOGD_DEVICE, "auto-activating connection '%s'", nm_settings_connection_get_id (best_connection)); subject = nm_auth_subject_new_internal (); - if (!nm_manager_activate_connection (priv->manager, + ac = nm_manager_activate_connection (priv->manager, best_connection, NULL, specific_object, device, subject, NM_ACTIVATION_TYPE_MANAGED, - &error)) { + &error); + if (!ac) { _LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: (%d) %s", nm_settings_connection_get_id (best_connection), error->code, error->message); g_error_free (error); + nm_settings_connection_autoconnect_blocked_reason_set (best_connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED); + schedule_activate_check (self, device); + return; } + + /* Subscribe to AC state-changed signal to detect when the + * activation fails in early stages without changing device + * state. + */ + if (nm_g_hash_table_add (priv->pending_active_connections, ac)) { + g_signal_connect (ac, NM_ACTIVE_CONNECTION_STATE_CHANGED, + G_CALLBACK (pending_ac_state_changed), g_object_ref (self)); + g_object_weak_ref (G_OBJECT (ac), (GWeakNotify) pending_ac_gone, self); + } + g_object_unref (subject); } } @@ -1248,12 +1412,12 @@ process_secondaries (NMPolicy *self, } static void -hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) +hostname_changed (NMHostnameManager *hostname_manager, GParamSpec *pspec, gpointer user_data) { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); - update_system_hostname (self, NULL, NULL, "hostname changed"); + update_system_hostname (self, "hostname changed"); } static void @@ -1274,8 +1438,8 @@ reset_autoconnect_all (NMPolicy *self, NMDevice *device) NMSettingsConnection *connection = connections[i]; if (!device || nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) { - nm_settings_connection_reset_autoconnect_retries (connection); - nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED); + nm_settings_connection_autoconnect_retries_reset (connection); + nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE); } } } @@ -1293,9 +1457,9 @@ reset_autoconnect_for_failed_secrets (NMPolicy *self) for (i = 0; connections[i]; i++) { NMSettingsConnection *connection = connections[i]; - if (nm_settings_connection_get_autoconnect_blocked_reason (connection) == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS) { - nm_settings_connection_reset_autoconnect_retries (connection); - nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED); + if (nm_settings_connection_autoconnect_blocked_reason_get (connection) == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS) { + nm_settings_connection_autoconnect_retries_reset (connection); + nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE); } } } @@ -1322,8 +1486,8 @@ block_autoconnect_for_device (NMPolicy *self, NMDevice *device) NMSettingsConnection *connection = connections[i]; if (nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) { - nm_settings_connection_set_autoconnect_blocked_reason (connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED); + nm_settings_connection_autoconnect_blocked_reason_set (connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST); } } } @@ -1406,12 +1570,12 @@ reset_connections_retries (gpointer user_data) for (i = 0; connections[i]; i++) { NMSettingsConnection *connection = connections[i]; - con_stamp = nm_settings_connection_get_autoconnect_retry_time (connection); + con_stamp = nm_settings_connection_autoconnect_blocked_until_get (connection); if (con_stamp == 0) continue; if (con_stamp <= now) { - nm_settings_connection_reset_autoconnect_retries (connection); + nm_settings_connection_autoconnect_retries_reset (connection); changed = TRUE; } else if (min_stamp == 0 || min_stamp > con_stamp) min_stamp = con_stamp; @@ -1436,6 +1600,7 @@ activate_slave_connections (NMPolicy *self, NMDevice *device) guint i; NMActRequest *req; gboolean internal_activation = FALSE; + gs_free NMSettingsConnection **connections = NULL; master_device = nm_device_get_iface (device); g_assert (master_device); @@ -1459,28 +1624,34 @@ activate_slave_connections (NMPolicy *self, NMDevice *device) internal_activation = subject && nm_auth_subject_is_internal (subject); } - if (!internal_activation) { - gs_free NMSettingsConnection **connections = NULL; + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMConnection *slave; + NMSettingConnection *s_slave_con; + const char *slave_master; - connections = nm_settings_get_connections_sorted (priv->settings, NULL); + slave = NM_CONNECTION (connections[i]); - for (i = 0; connections[i]; i++) { - NMConnection *slave; - NMSettingConnection *s_slave_con; - const char *slave_master; + s_slave_con = nm_connection_get_setting_connection (slave); + g_assert (s_slave_con); + slave_master = nm_setting_connection_get_master (s_slave_con); + if (!slave_master) + continue; - slave = NM_CONNECTION (connections[i]); + if ( nm_streq0 (slave_master, master_device) + || nm_streq0 (slave_master, master_uuid_applied) + || nm_streq0 (slave_master, master_uuid_settings)) { + NMSettingsConnection *settings = NM_SETTINGS_CONNECTION (slave); + NMSettingsAutoconnectBlockedReason reason; - s_slave_con = nm_connection_get_setting_connection (slave); - g_assert (s_slave_con); - slave_master = nm_setting_connection_get_master (s_slave_con); - if (!slave_master) - continue; + if (!internal_activation) + nm_settings_connection_autoconnect_retries_reset (settings); - if ( !g_strcmp0 (slave_master, master_device) - || !g_strcmp0 (slave_master, master_uuid_applied) - || !g_strcmp0 (slave_master, master_uuid_settings)) - nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave)); + reason = nm_settings_connection_autoconnect_blocked_reason_get (settings); + if (reason == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED) { + reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE; + nm_settings_connection_autoconnect_blocked_reason_set (settings, reason); + } } } @@ -1568,6 +1739,7 @@ device_state_changed (NMDevice *device, { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); + NMActiveConnection *ac; NMSettingsConnection *connection = nm_device_get_settings_connection (device); @@ -1576,6 +1748,28 @@ device_state_changed (NMDevice *device, NMIP6Config *ip6_config; NMSettingConnection *s_con = NULL; + switch (nm_device_state_reason_check (reason)) { + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: + /* Block autoconnect of the just-failed connection for situations + * where a retry attempt would just fail again. + */ + if (connection) { + nm_settings_connection_autoconnect_blocked_reason_set (connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED); + } + break; + default: + break; + } + switch (new_state) { case NM_DEVICE_STATE_FAILED: /* Mark the connection invalid if it failed during activation so that @@ -1584,26 +1778,31 @@ device_state_changed (NMDevice *device, if ( connection && old_state >= NM_DEVICE_STATE_PREPARE && old_state <= NM_DEVICE_STATE_ACTIVATED) { - int tries = nm_settings_connection_get_autoconnect_retries (connection); + int tries; + tries = nm_settings_connection_autoconnect_retries_get (connection); if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_NO_SECRETS) { _LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets", nm_settings_connection_get_id (connection)); - nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS); + nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS); } else if (tries != 0) { - _LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left", - nm_settings_connection_get_id (connection), tries); - if (tries > 0) - nm_settings_connection_set_autoconnect_retries (connection, tries - 1); + if (tries > 0) { + _LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left", + nm_settings_connection_get_id (connection), tries); + nm_settings_connection_autoconnect_retries_set (connection, --tries); + } else { + _LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; infinite tries left", + nm_settings_connection_get_id (connection)); + } } - if (nm_settings_connection_get_autoconnect_retries (connection) == 0) { + if (nm_settings_connection_autoconnect_retries_get (connection) == 0) { _LOGI (LOGD_DEVICE, "disabling autoconnect for connection '%s'.", nm_settings_connection_get_id (connection)); /* Schedule a handler to reset retries count */ if (!priv->reset_retries_id) { - gint32 retry_time = nm_settings_connection_get_autoconnect_retry_time (connection); + gint32 retry_time = nm_settings_connection_autoconnect_blocked_until_get (connection); g_warn_if_fail (retry_time != 0); priv->reset_retries_id = g_timeout_add_seconds (MAX (0, retry_time - nm_utils_get_monotonic_timestamp_s ()), reset_connections_retries, self); @@ -1615,7 +1814,7 @@ device_state_changed (NMDevice *device, case NM_DEVICE_STATE_ACTIVATED: if (connection) { /* Reset auto retries back to default since connection was successful */ - nm_settings_connection_reset_autoconnect_retries (connection); + nm_settings_connection_autoconnect_retries_reset (connection); /* And clear secrets so they will always be requested from the * settings service when the next connection is made. @@ -1654,8 +1853,8 @@ device_state_changed (NMDevice *device, /* The connection was deactivated, so block just this connection */ _LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s' by user request", nm_settings_connection_get_id (connection)); - nm_settings_connection_set_autoconnect_blocked_reason (connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED); + nm_settings_connection_autoconnect_blocked_reason_set (connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST); } } } @@ -1680,11 +1879,20 @@ device_state_changed (NMDevice *device, /* Reset auto-connect retries of all slaves and schedule them for * activation. */ activate_slave_connections (self, device); + + /* Now that the device state is progressing, we don't care + * anymore for the AC state. */ + ac = (NMActiveConnection *) nm_device_get_act_request (device); + if (ac && g_hash_table_remove (priv->pending_active_connections, ac)) { + g_signal_handlers_disconnect_by_func (ac, pending_ac_state_changed, self); + g_object_weak_unref (G_OBJECT (ac), pending_ac_gone, self); + g_object_unref (self); + } break; case NM_DEVICE_STATE_IP_CONFIG: /* We must have secrets if we got here. */ if (connection) - nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED); + nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE); break; case NM_DEVICE_STATE_SECONDARIES: if (connection) @@ -1735,7 +1943,7 @@ device_ip4_config_changed (NMDevice *device, } update_ip4_dns (self, priv->dns_manager); update_ip4_routing (self, TRUE); - update_system_hostname (self, priv->default_device4, priv->default_device6, "ip4 conf"); + update_system_hostname (self, "ip4 conf"); } else { /* Old configs get removed immediately */ if (old_config) @@ -1771,7 +1979,7 @@ device_ip6_config_changed (NMDevice *device, } update_ip6_dns (self, priv->dns_manager); update_ip6_routing (self, TRUE); - update_system_hostname (self, priv->default_device4, priv->default_device6, "ip6 conf"); + update_system_hostname (self, "ip6 conf"); } else { /* Old configs get removed immediately */ if (old_config) @@ -2088,30 +2296,26 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data) * (race in updating DNS and doing the reverse lookup). */ - nm_clear_g_cancellable (&priv->lookup_cancellable); + nm_clear_g_cancellable (&priv->lookup.cancellable); /* Re-start the hostname lookup thread if we don't have hostname yet. */ - if (priv->lookup_addr) { + if (priv->lookup.addr) { char *str = NULL; gs_free char *hostname = NULL; /* Check if the hostname was externally set */ - if ( _get_hostname (self, &hostname) + if ( (hostname = _get_hostname (self)) && nm_utils_is_specific_hostname (hostname) && !nm_streq0 (hostname, priv->last_hostname)) { - g_clear_object (&priv->lookup_addr); + g_clear_object (&priv->lookup.addr); return; } _LOGD (LOGD_DNS, "restarting reverse-lookup thread for address %s", - (str = g_inet_address_to_string (priv->lookup_addr))); + (str = g_inet_address_to_string (priv->lookup.addr))); g_free (str); - priv->lookup_cancellable = g_cancellable_new (); - g_resolver_lookup_by_address_async (priv->resolver, - priv->lookup_addr, - priv->lookup_cancellable, - lookup_callback, self); + lookup_by_address (self); } } @@ -2141,7 +2345,7 @@ connection_updated (NMSettings *settings, nm_device_reapply_settings_immediately (device); /* Reset auto retries back to default since connection was updated */ - nm_settings_connection_reset_autoconnect_retries (connection); + nm_settings_connection_autoconnect_retries_reset (connection); } schedule_activate_all (self); @@ -2240,6 +2444,15 @@ nm_policy_get_activating_ip6_device (NMPolicy *self) /*****************************************************************************/ +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_hostname_mode_to_string, NMPolicyHostnameMode, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), + NM_UTILS_LOOKUP_STR_ITEM (NM_POLICY_HOSTNAME_MODE_NONE, "none"), + NM_UTILS_LOOKUP_STR_ITEM (NM_POLICY_HOSTNAME_MODE_DHCP, "dhcp"), + NM_UTILS_LOOKUP_STR_ITEM (NM_POLICY_HOSTNAME_MODE_FULL, "full"), +); + +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -2300,6 +2513,8 @@ nm_policy_init (NMPolicy *self) priv->netns = g_object_ref (nm_netns_get ()); + priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); + hostname_mode = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE, @@ -2311,8 +2526,8 @@ nm_policy_init (NMPolicy *self) else /* default - full mode */ priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL; - _LOGI (LOGD_DNS, "hostname management mode: %s", hostname_mode ? hostname_mode : "default"); priv->devices = g_hash_table_new (NULL, NULL); + priv->pending_active_connections = g_hash_table_new (NULL, NULL); priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation)); g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation); } @@ -2325,7 +2540,7 @@ constructed (GObject *object) char *hostname = NULL; /* Grab hostname on startup and use that if nothing provides one */ - if (_get_hostname (self, &hostname)) { + if ((hostname = _get_hostname (self))) { /* init last_hostname */ priv->last_hostname = hostname; @@ -2333,6 +2548,8 @@ constructed (GObject *object) if (nm_utils_is_specific_hostname (hostname)) priv->orig_hostname = g_strdup (hostname); } + _LOGT (LOGD_DNS, "hostname-original: set to %s%s%s", + NM_PRINT_FMT_QUOTE_STRING (priv->orig_hostname)); priv->firewall_manager = g_object_ref (nm_firewall_manager_get ()); g_signal_connect (priv->firewall_manager, NM_FIREWALL_MANAGER_STATE_CHANGED, @@ -2343,9 +2560,10 @@ constructed (GObject *object) priv->config_changed_id = g_signal_connect (priv->dns_manager, NM_DNS_MANAGER_CONFIG_CHANGED, G_CALLBACK (dns_config_changed), self); - priv->resolver = g_resolver_get_default (); + priv->lookup.resolver = g_resolver_get_default (); + + g_signal_connect (priv->hostname_manager, "notify::" NM_HOSTNAME_MANAGER_HOSTNAME, (GCallback) hostname_changed, priv); - g_signal_connect (priv->manager, "notify::" NM_MANAGER_HOSTNAME, (GCallback) hostname_changed, priv); g_signal_connect (priv->manager, "notify::" NM_MANAGER_SLEEPING, (GCallback) sleeping_changed, priv); g_signal_connect (priv->manager, "notify::" NM_MANAGER_NETWORKING_ENABLED, (GCallback) sleeping_changed, priv); g_signal_connect (priv->manager, NM_MANAGER_INTERNAL_DEVICE_ADDED, (GCallback) device_added, priv); @@ -2360,6 +2578,8 @@ constructed (GObject *object) g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, (GCallback) secret_agent_registered, priv); G_OBJECT_CLASS (nm_policy_parent_class)->constructed (object); + + _LOGD (LOGD_DNS, "hostname-mode: %s", _hostname_mode_to_string (priv->hostname_mode)); } NMPolicy * @@ -2383,10 +2603,15 @@ dispose (GObject *object) GHashTableIter h_iter; NMDevice *device; - nm_clear_g_cancellable (&priv->lookup_cancellable); + nm_clear_g_cancellable (&priv->lookup.cancellable); + g_clear_object (&priv->lookup.addr); + g_clear_object (&priv->lookup.resolver); - g_clear_object (&priv->lookup_addr); - g_clear_object (&priv->resolver); + nm_clear_g_object (&priv->default_device4); + nm_clear_g_object (&priv->default_device6); + nm_clear_g_object (&priv->activating_device4); + nm_clear_g_object (&priv->activating_device6); + g_clear_pointer (&priv->pending_active_connections, g_hash_table_unref); while (priv->pending_activation_checks) activate_data_free (priv->pending_activation_checks->data); @@ -2424,6 +2649,11 @@ dispose (GObject *object) g_clear_pointer (&priv->cur_hostname, g_free); g_clear_pointer (&priv->last_hostname, g_free); + if (priv->hostname_manager) { + g_signal_handlers_disconnect_by_data (priv->hostname_manager, priv); + g_clear_object (&priv->hostname_manager); + } + if (priv->settings) { g_signal_handlers_disconnect_by_data (priv->settings, priv); g_clear_object (&priv->settings); diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c deleted file mode 100644 index f293f130..00000000 --- a/src/nm-route-manager.c +++ /dev/null @@ -1,1321 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-route-manager.h" - -#include <string.h> - -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "platform/nmp-object.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -/* if within half a second after adding an IP address a matching device-route shows - * up, we delete it. */ -#define IP4_DEVICE_ROUTES_WAIT_TIME_NS (NM_UTILS_NS_PER_SECOND / 2) - -#define IP4_DEVICE_ROUTES_GC_INTERVAL_SEC (IP4_DEVICE_ROUTES_WAIT_TIME_NS * 2) - -/*****************************************************************************/ - -typedef struct { - guint len; - NMPlatformIPXRoute *entries[1]; -} RouteIndex; - -typedef struct { - GArray *entries; - RouteIndex *index; - - /* list of effective metrics. The indexes of the array correspond to @index, not @entries. */ - GArray *effective_metrics; - - /* this array contains the effective metrics but using the reversed index that corresponds - * to @entries, instead of @index. */ - GArray *effective_metrics_reverse; -} RouteEntries; - -typedef struct { - NMRouteManager *self; - gint64 scheduled_at_ns; - guint idle_id; - NMPObject *obj; -} IP4DeviceRoutePurgeEntry; - -/*****************************************************************************/ - -enum { - IP4_ROUTES_CHANGED, - LAST_SIGNAL, -}; -static guint signals[LAST_SIGNAL] = { 0 }; - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_LOG_WITH_PTR, - PROP_PLATFORM, -); - -typedef struct { - NMPlatform *platform; - - RouteEntries ip4_routes; - RouteEntries ip6_routes; - struct { - GHashTable *entries; - guint gc_id; - } ip4_device_routes; - - bool log_with_ptr; -} NMRouteManagerPrivate; - -struct _NMRouteManager { - GObject parent; - NMRouteManagerPrivate _priv; -}; - -struct _NMRouteManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT); - -#define NM_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMRouteManager, NM_IS_ROUTE_MANAGER) - -/*****************************************************************************/ - -typedef struct { - const NMPlatformVTableRoute *vt; - - /* a compare function for two routes that considers only the destination fields network/plen. - * It is a looser comparisong then @route_id_cmp(), that means that if @route_dest_cmp() - * returns non-zero, also @route_id_cmp() returns the same value. It also means, that - * sorting by @route_id_cmp() implicitly sorts by @route_dest_cmp() as well. */ - int (*route_dest_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); - - /* a compare function for two routes that considers only the fields network/plen,metric. */ - int (*route_id_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); -} VTableIP; - -static const VTableIP vtable_v4, vtable_v6; - -#define VTABLE_ROUTE_INDEX(vtable, garray, idx) ((NMPlatformIPXRoute *) &((garray)->data[(idx) * (vtable)->vt->sizeof_route])) - -#define VTABLE_IS_DEVICE_ROUTE(vtable, route) ((vtable)->vt->is_ip4 \ - ? ((route)->r4.gateway == 0) \ - : IN6_IS_ADDR_UNSPECIFIED (&(route)->r6.gateway) ) - -#define CMP_AND_RETURN_INT(a, b) \ - G_STMT_START { \ - typeof(a) _a = (a), _b = (b); \ - \ - if (_a < _b) \ - return -1; \ - if (_a > _b) \ - return 1; \ - } G_STMT_END - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "route-mgr" -#undef _NMLOG_ENABLED -#define _NMLOG_ENABLED(level, addr_family) \ - ({ \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - nm_logging_enabled (__level, __domain); \ - }) -#define _NMLOG(level, addr_family, ...) \ - G_STMT_START { \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ - char __prefix[30] = _NMLOG_PREFIX_NAME; \ - \ - if (NM_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr) \ - g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _NMLOG_PREFIX_NAME, __ch, (self)); \ - else \ - __prefix[NM_STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \ - _nm_log ((level), (__domain), 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static gboolean _ip4_device_routes_cancel (NMRouteManager *self); - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS && !defined (G_DISABLE_ASSERT) -static inline void -ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) -{ - guint i, j; - int c; - const NMPlatformIPXRoute *r1, *r2; - gs_unref_hashtable GHashTable *ptrs = g_hash_table_new (NULL, NULL); - const NMPlatformIPXRoute *r_first = NULL, *r_last = NULL; - - g_assert (index); - - if (entries) - g_assert_cmpint (entries->len, ==, index->len); - else - g_assert (index->len == 0); - - if (index->len > 0) { - r_first = VTABLE_ROUTE_INDEX (vtable, entries, 0); - r_last = VTABLE_ROUTE_INDEX (vtable, entries, index->len - 1); - } - - /* assert that the @index is valid for the @entries. */ - - g_assert (!index->entries[index->len]); - for (i = 0; i < index->len; i++) { - r1 = index->entries[i]; - - g_assert (r1); - g_assert (r1 >= r_first); - g_assert (r1 <= r_last); - g_assert_cmpint ((((char *) r1) - ((char *) entries->data)) % vtable->vt->sizeof_route, ==, 0); - - g_assert (!g_hash_table_contains (ptrs, (gpointer) r1)); - g_hash_table_add (ptrs, (gpointer) r1); - - for (j = i; j > 0; ) { - r2 = index->entries[--j]; - - c = vtable->route_id_cmp (r1, r2); - g_assert (c >= 0); - if (c != 0) - break; - if (unique_ifindexes) - g_assert_cmpint (r1->rx.ifindex, !=, r2->rx.ifindex); - } - } -} -#else -#define ASSERT_route_index_valid(vtable, entries, index, unique_ifindexes) G_STMT_START { (void) 0; } G_STMT_END -#endif - -/*****************************************************************************/ - -static int -_v4_route_dest_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), - nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); - return 0; -} - -static int -_v6_route_dest_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); - nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen ); - return memcmp (&n1, &n2, sizeof (n1)); -} - -static int -_v4_route_id_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), - nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); - CMP_AND_RETURN_INT (r1->metric, r2->metric); - return 0; -} - -static int -_v6_route_id_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - int c; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); - nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); - c = memcmp (&n1, &n2, sizeof (n1)); - if (c != 0) - return c; - - CMP_AND_RETURN_INT (nm_utils_ip6_route_metric_normalize (r1->metric), - nm_utils_ip6_route_metric_normalize (r2->metric)); - return 0; -} - -/*****************************************************************************/ - -static int -_route_index_create_sort (const NMPlatformIPXRoute **p1, const NMPlatformIPXRoute ** p2, const VTableIP *vtable) -{ - return vtable->route_id_cmp (*p1, *p2); -} - -static RouteIndex * -_route_index_create (const VTableIP *vtable, const GArray *routes) -{ - RouteIndex *index; - guint i; - guint len = routes ? routes->len : 0; - - index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - - index->len = len; - for (i = 0; i < len; i++) - index->entries[i] = VTABLE_ROUTE_INDEX (vtable, routes, i); - index->entries[i] = NULL; - - /* this is a stable sort, which is very important at this point. */ - g_qsort_with_data (index->entries, - len, - sizeof (NMPlatformIPXRoute *), - (GCompareDataFunc) _route_index_create_sort, - (gpointer) vtable); - return index; -} - -static int -_vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) -{ - return vtable->route_id_cmp (r1, r2); -} - -static gssize -_route_index_find (const VTableIP *vtable, const RouteIndex *index, const NMPlatformIPXRoute *needle) -{ - gssize idx, idx2; - - idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) index->entries, index->len, needle, (GCompareDataFunc) _vx_route_id_cmp_full, (gpointer) vtable); - if (idx < 0) - return idx; - - /* we only know that the route at index @idx has matching destination. Also find the one with the right - * ifindex by searching the neighbours */ - - idx2 = idx; - do { - if (index->entries[idx2]->rx.ifindex == needle->rx.ifindex) - return idx2; - } while ( idx2 > 0 - && vtable->route_id_cmp (index->entries[--idx2], needle) != 0); - - for (idx++; idx < index->len; idx++ ){ - if (vtable->route_id_cmp (index->entries[idx], needle) != 0) - break; - if (index->entries[idx]->rx.ifindex == needle->rx.ifindex) - return idx; - } - - return ~idx; -} - -static guint -_route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint idx_idx, const GArray *routes) -{ - const NMPlatformIPXRoute *r, *r0; - gssize offset; - - /* reverse the @idx_idx that points into @index, to the corresponding index into the unsorted @routes array. */ - - r = index->entries[idx_idx]; - r0 = VTABLE_ROUTE_INDEX (vtable, routes, 0); - - if (vtable->vt->is_ip4) - offset = &r->r4 - &r0->r4; - else - offset = &r->r6 - &r0->r6; - g_assert (offset >= 0 && offset < index->len); - g_assert (VTABLE_ROUTE_INDEX (vtable, routes, offset) == r); - return offset; -} - -/*****************************************************************************/ - -static gboolean -_route_equals_ignoring_ifindex (const VTableIP *vtable, - const NMPlatformIPXRoute *plat_rt, - const NMPlatformIPXRoute *rt, - gint64 rt_metric) -{ - NMPlatformIPXRoute rt_backup; - - memcpy (&rt_backup, rt, vtable->vt->sizeof_route); - rt_backup.rx.ifindex = plat_rt->rx.ifindex; - if (rt_metric >= 0) - rt_backup.rx.metric = (guint32) rt_metric; - rt_backup.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt_backup.rx.rt_source); - - return vtable->vt->route_cmp (plat_rt, &rt_backup, FALSE) == 0; -} - -static NMPlatformIPXRoute * -_get_next_ipx_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx, int ifindex) -{ - guint i; - - if (start_at_zero) - i = 0; - else - i = *cur_idx + 1; - /* Find the next route with matching @ifindex. */ - for (; i < index->len; i++) { - if (index->entries[i]->rx.ifindex == ifindex) { - *cur_idx = i; - return index->entries[i]; - } - } - *cur_idx = index->len; - return NULL; -} - -static const NMPlatformIPXRoute * -_get_next_known_route (const VTableIP *vtable, const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) -{ - guint i = 0; - const NMPlatformIPXRoute *cur = NULL; - - if (!start_at_zero) { - i = *cur_idx; - cur = index->entries[i]; - i++; - } - /* For @known_routes we expect that all routes have the same @ifindex. This is not enforced however, - * the ifindex value of these routes is ignored. */ - for (; i < index->len; i++) { - const NMPlatformIPXRoute *r = index->entries[i]; - - /* skip over default routes. */ - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) - continue; - - /* @known_routes should not, but could contain duplicate routes. Skip over them. */ - if (cur && vtable->route_id_cmp (cur, r) == 0) - continue; - - *cur_idx = i; - return r; - } - *cur_idx = index->len; - return NULL; -} - -static const NMPlatformIPXRoute * -_get_next_plat_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) -{ - if (start_at_zero) - *cur_idx = 0; - else - ++*cur_idx; - - /* get next route from the platform index. */ - if (*cur_idx < index->len) - return index->entries[*cur_idx]; - *cur_idx = index->len; - return NULL; -} - -static int -_sort_indexes_cmp (guint *a, guint *b) -{ - CMP_AND_RETURN_INT (*a, *b); - g_return_val_if_reached (0); -} - -/*****************************************************************************/ - -static gboolean -_vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - GArray *plat_routes; - RouteEntries *ipx_routes; - RouteIndex *plat_routes_idx, *known_routes_idx; - gboolean success = TRUE; - guint i, i_type; - GArray *to_delete_indexes = NULL; - GPtrArray *to_add_routes = NULL; - guint i_known_routes, i_plat_routes, i_ipx_routes; - const NMPlatformIPXRoute *cur_known_route, *cur_plat_route; - NMPlatformIPXRoute *cur_ipx_route; - gint64 *p_effective_metric = NULL; - gboolean ipx_routes_changed = FALSE; - gint64 *effective_metrics = NULL; - - nm_platform_process_events (priv->platform); - - ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; - plat_routes = vtable->vt->route_get_all (priv->platform, ifindex, - ignore_kernel_routes - ? NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT - : NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - plat_routes_idx = _route_index_create (vtable, plat_routes); - known_routes_idx = _route_index_create (vtable, known_routes); - - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - ASSERT_route_index_valid (vtable, plat_routes, plat_routes_idx, TRUE); - ASSERT_route_index_valid (vtable, known_routes, known_routes_idx, FALSE); - - _LOGD (vtable->vt->addr_family, "%3d: sync %u IPv%c routes", ifindex, known_routes_idx->len, vtable->vt->is_ip4 ? '4' : '6'); - if (_LOGt_ENABLED (vtable->vt->addr_family)) { - for (i = 0; i < known_routes_idx->len; i++) { - _LOGt (vtable->vt->addr_family, "%3d: sync new route #%u: %s", - ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i), NULL, 0)); - } - for (i = 0; i < ipx_routes->index->len; i++) - _LOGt (vtable->vt->addr_family, "%3d: STATE: has #%u - %s (%lld)", - ifindex, i, - vtable->vt->route_to_string (ipx_routes->index->entries[i], NULL, 0), - (long long) g_array_index (ipx_routes->effective_metrics, gint64, i)); - } - - /*************************************************************************** - * Check which routes are in @known_routes, and update @ipx_routes. - * - * This first part only updates @ipx_routes to find out what routes must - * be added/deleted. - **************************************************************************/ - - /* iterate over @ipx_routes and @known_routes */ - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - cur_known_route = _get_next_known_route (vtable, known_routes_idx, TRUE, &i_known_routes); - while (cur_ipx_route || cur_known_route) { - int route_id_cmp_result = -1; - - while ( cur_ipx_route - && ( !cur_known_route - || ((route_id_cmp_result = vtable->route_id_cmp (cur_ipx_route, cur_known_route)) < 0))) { - /* we have @cur_ipx_route, which is less then @cur_known_route. Hence, - * the route does no longer exist in @known_routes */ - if (!to_delete_indexes) - to_delete_indexes = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (to_delete_indexes, i_ipx_routes); - - /* find the next @cur_ipx_route with matching ifindex. */ - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - } - if ( cur_ipx_route - && cur_known_route - && route_id_cmp_result == 0) { - if (!_route_equals_ignoring_ifindex (vtable, cur_ipx_route, cur_known_route, -1)) { - /* The routes match. Update the entry in place. As this is an exact match of primary - * fields, this only updates possibly modified fields such as @gateway or @mss. - * Modifiying @cur_ipx_route this way does not invalidate @ipx_routes->index. */ - memcpy (cur_ipx_route, cur_known_route, vtable->vt->sizeof_route); - cur_ipx_route->rx.ifindex = ifindex; - cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, cur_ipx_route->rx.network_ptr, - cur_ipx_route->rx.network_ptr, cur_ipx_route->rx.plen); - ipx_routes_changed = TRUE; - _LOGt (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); - } - } else if (cur_known_route) { - g_assert (!cur_ipx_route || route_id_cmp_result > 0); - /* @cur_known_route is new. We cannot immediately add @cur_known_route to @ipx_routes, because - * it would invalidate @ipx_routes->index. Instead remember to add it later. */ - if (!to_add_routes) - to_add_routes = g_ptr_array_new (); - g_ptr_array_add (to_add_routes, (gpointer) cur_known_route); - } - - if (cur_ipx_route && (!cur_known_route || route_id_cmp_result == 0)) - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - if (cur_known_route) - cur_known_route = _get_next_known_route (vtable, known_routes_idx, FALSE, &i_known_routes); - } - - if (!full_sync && to_delete_indexes) { - /*************************************************************************** - * Delete routes in platform, that we are about to remove from @ipx_routes - * - * When doing a non-full_sync, we delete routes from platform that were previously - * known by route-manager, and are now deleted. - ***************************************************************************/ - - /* iterate over @to_delete_indexes and @plat_routes. - * @to_delete_indexes contains the indexes (relative to ipx_routes->index) of items - * we are about to delete. */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - for (i = 0; i < to_delete_indexes->len; i++) { - int route_dest_cmp_result = 0; - i_ipx_routes = g_array_index (to_delete_indexes, guint, i); - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - nm_assert (cur_ipx_route->rx.ifindex == ifindex); - - if (*p_effective_metric == -1) - continue; - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - - if (!cur_plat_route) { - /* no more platform routes. Break the loop. */ - break; - } - - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric == *p_effective_metric) { - /* we are about to delete cur_ipx_route and we have a matching route - * in platform. Delete it. */ - _LOGt (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, - vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - vtable->vt->route_delete (priv->platform, ifindex, cur_plat_route); - } - } - } - - /* Update @ipx_routes with the just learned changes. */ - if (to_delete_indexes || to_add_routes) { - if (to_delete_indexes) { - for (i = 0; i < to_delete_indexes->len; i++) { - guint idx = g_array_index (to_delete_indexes, guint, i); - - _LOGt (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, - vtable->vt->route_to_string (ipx_routes->index->entries[idx], NULL, 0)); - g_array_index (to_delete_indexes, guint, i) = _route_index_reverse_idx (vtable, ipx_routes->index, idx, ipx_routes->entries); - } - g_array_sort (to_delete_indexes, (GCompareFunc) _sort_indexes_cmp); - nm_utils_array_remove_at_indexes (ipx_routes->entries, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); - nm_utils_array_remove_at_indexes (ipx_routes->effective_metrics_reverse, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); - g_array_unref (to_delete_indexes); - } - if (to_add_routes) { - guint j = ipx_routes->effective_metrics_reverse->len; - - g_array_set_size (ipx_routes->effective_metrics_reverse, j + to_add_routes->len); - - for (i = 0; i < to_add_routes->len; i++) { - NMPlatformIPXRoute *ipx_route; - - g_array_append_vals (ipx_routes->entries, g_ptr_array_index (to_add_routes, i), 1); - - ipx_route = VTABLE_ROUTE_INDEX (vtable, ipx_routes->entries, ipx_routes->entries->len - 1); - ipx_route->rx.ifindex = ifindex; - ipx_route->rx.metric = vtable->vt->metric_normalize (ipx_route->rx.metric); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, ipx_route->rx.network_ptr, - ipx_route->rx.network_ptr, ipx_route->rx.plen); - - g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1; - - _LOGt (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, - vtable->vt->route_to_string (ipx_route, NULL, 0)); - } - g_ptr_array_unref (to_add_routes); - } - g_free (ipx_routes->index); - ipx_routes->index = _route_index_create (vtable, ipx_routes->entries); - ipx_routes_changed = TRUE; - ASSERT_route_index_valid (vtable, ipx_routes->entries, ipx_routes->index, TRUE); - } - - if (ipx_routes_changed) { - /*************************************************************************** - * Rebuild the list of effective metrics. In case of conflicting routes, - * we configure device routes with a bumped metric. We do this, because non-direct - * routes might require this direct route to reach the gateway (e.g. the default - * route). - * - * We determine the effective metrics only based on our internal list @ipx_routes - * and don't consider @plat_routes. That means, we might bump the metric of a route - * and thereby cause a conflict with an existing route on an unmanaged device (which - * causes the route on the unmanaged device to be replaced). - * Still, that is not much different then from messing with unmanaged routes when - * the effective and the intended metrics equal. The rules is: NM will leave routes - * on unmanaged devices alone, unless they conflict with what NM wants to configure. - ***************************************************************************/ - - g_array_set_size (ipx_routes->effective_metrics, ipx_routes->entries->len); - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - /* Completely regenerate the list of effective metrics by walking through - * ipx_routes->index and determining the effective metric. */ - - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->index->len; i_ipx_routes++) { - gint64 *p_effective_metric_before; - gboolean is_shadowed; - guint i_ipx_routes_before; - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - is_shadowed = i_ipx_routes > 0 - && vtable->route_dest_cmp (cur_ipx_route, ipx_routes->index->entries[i_ipx_routes - 1]) == 0; - - if (!is_shadowed) { - /* the route is not shadowed, the effective metric is just as specified. */ - *p_effective_metric = cur_ipx_route->rx.metric; - goto next; - } - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* The route is not a device route. We want to add redundant device routes, because - * we might need the direct routes to the gateway. For non-direct routes, there is not much - * reason to do the metric increment. */ - *p_effective_metric = -1; - goto next; - } - - /* The current route might be shadowed by several other routes. Find the one with the highest metric, - * i.e. the one with an effecive metric set and in the index before the current index. */ - i_ipx_routes_before = i_ipx_routes; - while (TRUE) { - nm_assert (i_ipx_routes_before > 0); - - i_ipx_routes_before--; - - p_effective_metric_before = &effective_metrics[i_ipx_routes_before]; - - if (*p_effective_metric_before == -1) { - /* this route is also shadowed, continue search. */ - continue; - } - - if (*p_effective_metric_before < cur_ipx_route->rx.metric) { - /* the previous route has a lower metric. There is no conflict, - * just use the original metric. */ - *p_effective_metric = cur_ipx_route->rx.metric; - } else if (*p_effective_metric_before == G_MAXUINT32) { - /* we cannot bump the metric. Don't configure this route. */ - *p_effective_metric = -1; - } else { - /* bump the metric by one. */ - *p_effective_metric = *p_effective_metric_before + 1; - } - break; - } -next: - _LOGt (vtable->vt->addr_family, "%3d: new metric #%u - %s (%lld)", - ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0), - (long long) *p_effective_metric); - } - } - - if (full_sync) { - /*************************************************************************** - * Delete all routes in platform, that no longer exist in @ipx_routes - * - * Different from the delete action above, we delete every unknown route on - * the interface. - ***************************************************************************/ - - /* iterate over @plat_routes and @ipx_routes */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - while (cur_plat_route) { - int route_dest_cmp_result = 0; - - g_assert (cur_plat_route->rx.ifindex == ifindex); - - _LOGt (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - - /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ - while ( cur_ipx_route - && ((route_dest_cmp_result = vtable->route_dest_cmp (cur_ipx_route, cur_plat_route)) <= 0)) { - if ( route_dest_cmp_result == 0 - && *p_effective_metric != -1 - && *p_effective_metric >= cur_plat_route->rx.metric) { - break; - } - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - } - - /* if @cur_ipx_route is not equal to @plat_route, the route must be deleted. */ - if ( !cur_ipx_route - || route_dest_cmp_result != 0 - || *p_effective_metric != cur_plat_route->rx.metric) - vtable->vt->route_delete (priv->platform, ifindex, cur_plat_route); - - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - } - - /*************************************************************************** - * Restore shadowed routes. These routes are on an other @ifindex then what - * we are syncing now. But the current changes make it necessary to add those - * routes. - * - * Only add some routes that might be necessary. We don't delete any routes - * on other ifindexes here. I.e. we don't do a full sync, but only ~add~ routes - * that were shadowed previously, but should be now present with a different - * metric. - **************************************************************************/ - - if (ipx_routes_changed) { - GArray *gateway_routes = NULL; - - /* @effective_metrics_reverse contains the list of assigned metrics from the last - * sync. Walk through it and see what changes there are (and possibly restore a - * shadowed route). - * Thereby also update @effective_metrics_reverse to be up-to-date again. */ - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->entries->len; i_ipx_routes++) { - guint i_ipx_routes_reverse; - gint64 *p_effective_metric_reversed; - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - i_ipx_routes_reverse = _route_index_reverse_idx (vtable, ipx_routes->index, i_ipx_routes, ipx_routes->entries); - p_effective_metric_reversed = &g_array_index (ipx_routes->effective_metrics_reverse, gint64, i_ipx_routes_reverse); - - if (*p_effective_metric_reversed == *p_effective_metric) { - /* The entry is up to date. No change, continue with the next one. */ - continue; - } - *p_effective_metric_reversed = *p_effective_metric; - - if (*p_effective_metric == -1) { - /* the entry is shadowed. Nothing to do. */ - continue; - } - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - if (cur_ipx_route->rx.ifindex == ifindex) { - /* @cur_ipx_route is on the current @ifindex. No need to special handling them - * because we are about to do a full sync of the ifindex. */ - continue; - } - - /* the effective metric from previous sync changed. While @cur_ipx_route is not on the - * ifindex we are about to sync, we still must add this route. Possibly it was shadowed - * before, and now we want to restore it. - * - * Note that we don't do a full sync on the other ifindex. Especially, we don't delete - * or add any further routes then this. That means there might be some stale routes - * (with a higher metric!). They will only be removed on the next sync of that other - * ifindex. */ - - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* the route to restore has a gateway. We can only restore the route - * when we also have a direct route to the gateway. There can be cases - * where the direct route is shadowed too, and we cannot restore the gateway - * route. - * - * Restore first the direct-routes, and gateway-routes afterwards. - * This can avoid some cases where we would fail to add the - * gateway route. */ - if (!gateway_routes) - gateway_routes = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (gateway_routes, i_ipx_routes); - } else - vtable->vt->route_add (priv->platform, 0, cur_ipx_route, *p_effective_metric); - } - - if (gateway_routes) { - for (i = 0; i < gateway_routes->len; i++) { - i_ipx_routes = g_array_index (gateway_routes, guint, i); - vtable->vt->route_add (priv->platform, 0, - ipx_routes->index->entries[i_ipx_routes], - effective_metrics[i_ipx_routes]); - } - g_array_unref (gateway_routes); - } - } - - /*************************************************************************** - * Sync @ipx_routes for @ifindex to platform - **************************************************************************/ - - for (i_type = 0; i_type < 2; i_type++) { - /* iterate (twice) over @ipx_routes and @plat_routes */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - /* Iterate here over @ipx_routes instead of @known_routes. That is done because - * we need to know whether a route is shadowed by another route, and that - * requires to look at @ipx_routes. */ - for (; cur_ipx_route; cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex)) { - int route_dest_cmp_result = -1; - - if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) - || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route))) { - /* Make two runs over the list of @ipx_routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - if (*p_effective_metric == -1) { - /* @cur_ipx_route is shadewed by another route. */ - continue; - } - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - - /* only add the route if we don't have an identical route in @plat_routes, - * i.e. if @cur_plat_route is different from @cur_ipx_route. */ - if ( !cur_plat_route - || route_dest_cmp_result != 0 - || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) { - - if (!vtable->vt->route_add (priv->platform, ifindex, cur_ipx_route, *p_effective_metric)) { - if (cur_ipx_route->rx.rt_source < NM_IP_CONFIG_SOURCE_USER) { - _LOGD (vtable->vt->addr_family, - "ignore error adding IPv%c route to kernel: %s", - vtable->vt->is_ip4 ? '4' : '6', - vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); - } else { - /* Remember that there was a failure, but for now continue trying - * to sync the remaining routes. */ - success = FALSE; - } - } - } - } - } - - if (vtable->vt->is_ip4 && ipx_routes_changed) - g_signal_emit (self, signals[IP4_ROUTES_CHANGED], 0); - - g_free (known_routes_idx); - g_free (plat_routes_idx); - g_array_unref (plat_routes); - - return success; -} - -/** - * nm_route_manager_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v4, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -/** - * nm_route_manager_ip6_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v6, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -gboolean -nm_route_manager_route_flush (NMRouteManager *self, int ifindex) -{ - bool success = TRUE; - - success &= (bool) nm_route_manager_ip4_route_sync (self, ifindex, NULL, FALSE, TRUE); - success &= (bool) nm_route_manager_ip6_route_sync (self, ifindex, NULL, FALSE, TRUE); - return success; -} - -/** - * nm_route_manager_ip4_routes_shadowed: - * @ifindex: Interface index - * - * Returns: %TRUE if some other link has a route to the same destination - * with a lower metric. - */ -gboolean -nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - RouteIndex *index = priv->ip4_routes.index; - const NMPlatformIP4Route *route; - guint i; - - for (i = 1; i < index->len; i++) { - route = (const NMPlatformIP4Route *) index->entries[i]; - - if (route->ifindex != ifindex) - continue; - if (_v4_route_dest_cmp (route, (const NMPlatformIP4Route *) index->entries[i - 1]) == 0) - return TRUE; - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -_ip4_device_routes_entry_expired (const IP4DeviceRoutePurgeEntry *entry, gint64 now) -{ - return entry->scheduled_at_ns + IP4_DEVICE_ROUTES_WAIT_TIME_NS < now; -} - -static IP4DeviceRoutePurgeEntry * -_ip4_device_routes_purge_entry_create (NMRouteManager *self, const NMPlatformIP4Route *route, gint64 now_ns) -{ - IP4DeviceRoutePurgeEntry *entry; - - entry = g_slice_new (IP4DeviceRoutePurgeEntry); - - entry->self = self; - entry->scheduled_at_ns = now_ns; - entry->idle_id = 0; - entry->obj = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route); - return entry; -} - -static void -_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry) -{ - nmp_object_unref (entry->obj); - nm_clear_g_source (&entry->idle_id); - g_slice_free (IP4DeviceRoutePurgeEntry, entry); -} - -static gboolean -_ip4_device_routes_idle_cb (IP4DeviceRoutePurgeEntry *entry) -{ - NMRouteManager *self; - NMRouteManagerPrivate *priv; - - nm_clear_g_source (&entry->idle_id); - - self = entry->self; - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - if (_route_index_find (&vtable_v4, priv->ip4_routes.index, &entry->obj->ipx_route) >= 0) { - /* we have an identical route in our list. Don't delete it. */ - return G_SOURCE_REMOVE; - } - - _LOGt (vtable_v4.vt->addr_family, "device-route: delete %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - nm_platform_ip4_route_delete (priv->platform, - entry->obj->ip4_route.ifindex, - entry->obj->ip4_route.network, - entry->obj->ip4_route.plen, - entry->obj->ip4_route.metric); - - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return G_SOURCE_REMOVE; -} - -static void -_ip4_device_routes_ip4_route_changed (NMPlatform *platform, - int obj_type_i, - int ifindex, - const NMPlatformIP4Route *route, - int change_type_i, - NMRouteManager *self) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - NMRouteManagerPrivate *priv; - NMPObject obj_needle; - IP4DeviceRoutePurgeEntry *entry; - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - return; - - if ( route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - || route->metric != 0) { - /* we don't have an automatically created device route at hand. Bail out early. */ - return; - } - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - entry = g_hash_table_lookup (priv->ip4_device_routes.entries, - nmp_object_stackinit (&obj_needle, NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route)); - if (!entry) - return; - - if (_ip4_device_routes_entry_expired (entry, nm_utils_get_monotonic_timestamp_ns ())) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-ch %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return; - } - - if (entry->idle_id == 0) { - _LOGt (vtable_v4.vt->addr_family, "device-route: schedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - entry->idle_id = g_idle_add ((GSourceFunc) _ip4_device_routes_idle_cb, entry); - } -} - -static gboolean -_ip4_device_routes_cancel (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->ip4_device_routes.gc_id) { - if (g_hash_table_size (priv->ip4_device_routes.entries) > 0) - return G_SOURCE_CONTINUE; - _LOGt (vtable_v4.vt->addr_family, "device-route: cancel"); - if (priv->platform) - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - nm_clear_g_source (&priv->ip4_device_routes.gc_id); - } - return G_SOURCE_REMOVE; -} - -static gboolean -_ip4_device_routes_gc (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv; - GHashTableIter iter; - IP4DeviceRoutePurgeEntry *entry; - gint64 now = nm_utils_get_monotonic_timestamp_ns (); - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_iter_init (&iter, priv->ip4_device_routes.entries); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) { - if (_ip4_device_routes_entry_expired (entry, now)) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-gc %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_iter_remove (&iter); - } - } - - return _ip4_device_routes_cancel (self); -} - -/** - * nm_route_manager_ip4_route_register_device_route_purge_list: - * - * When adding an IPv4 address, kernel will automatically add a device route with - * metric zero. We don't want that route and want to delete it. However, the route - * by kernel immediately, but some time after. That means during nm_route_manager_ip4_route_sync() - * such a route doesn't exist yet. We must remember that we expect such a route to appear later - * and to remove it. */ -void -nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list) -{ - NMRouteManagerPrivate *priv; - guint i; - gint64 now_ns; - - if (!device_route_purge_list || device_route_purge_list->len == 0) - return; - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - now_ns = nm_utils_get_monotonic_timestamp_ns (); - for (i = 0; i < device_route_purge_list->len; i++) { - IP4DeviceRoutePurgeEntry *entry; - - entry = _ip4_device_routes_purge_entry_create (self, &g_array_index (device_route_purge_list, NMPlatformIP4Route, i), now_ns); - _LOGt (vtable_v4.vt->addr_family, "device-route: watch (%s) %s", - g_hash_table_contains (priv->ip4_device_routes.entries, entry->obj) - ? "update" : "new", - nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_replace (priv->ip4_device_routes.entries, - nmp_object_ref (entry->obj), - entry); - } - if (priv->ip4_device_routes.gc_id == 0) { - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - priv->ip4_device_routes.gc_id = g_timeout_add (IP4_DEVICE_ROUTES_GC_INTERVAL_SEC, (GSourceFunc) _ip4_device_routes_gc, self); - } -} - -/*****************************************************************************/ - -static const VTableIP vtable_v4 = { - .vt = &nm_platform_vtable_route_v4, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, -}; - -static const VTableIP vtable_v6 = { - .vt = &nm_platform_vtable_route_v6, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_id_cmp, -}; - -/*****************************************************************************/ - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_LOG_WITH_PTR: - /* construct-only */ - priv->log_with_ptr = g_value_get_boolean (value); - break; - case PROP_PLATFORM: - /* construct-only */ - priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET; - if (!priv->platform) - g_return_if_reached (); - g_object_ref (priv->platform); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_route_manager_init (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->ip4_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - priv->ip4_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip4_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries); - priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries); - priv->ip4_device_routes.entries = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - (GDestroyNotify) _ip4_device_routes_purge_entry_free); -} - -NMRouteManager * -nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform) -{ - return g_object_new (NM_TYPE_ROUTE_MANAGER, - NM_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr, - NM_ROUTE_MANAGER_PLATFORM, platform, - NULL); -} - -static void -dispose (GObject *object) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->ip4_device_routes.entries); - _ip4_device_routes_cancel (self); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE ((NMRouteManager *) object); - - g_array_free (priv->ip4_routes.entries, TRUE); - g_array_free (priv->ip6_routes.entries, TRUE); - g_array_free (priv->ip4_routes.effective_metrics, TRUE); - g_array_free (priv->ip6_routes.effective_metrics, TRUE); - g_array_free (priv->ip4_routes.effective_metrics_reverse, TRUE); - g_array_free (priv->ip6_routes.effective_metrics_reverse, TRUE); - g_free (priv->ip4_routes.index); - g_free (priv->ip6_routes.index); - - g_hash_table_unref (priv->ip4_device_routes.entries); - - g_clear_object (&priv->platform); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); -} - -static void -nm_route_manager_class_init (NMRouteManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - obj_properties[PROP_LOG_WITH_PTR] = - g_param_spec_boolean (NM_ROUTE_MANAGER_LOG_WITH_PTR, "", "", - TRUE, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PLATFORM] = - g_param_spec_object (NM_ROUTE_MANAGER_PLATFORM, "", "", - NM_TYPE_PLATFORM, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[IP4_ROUTES_CHANGED] = - g_signal_new (NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); -} diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h deleted file mode 100644 index bdf79a09..00000000 --- a/src/nm-route-manager.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_ROUTE_MANAGER_H__ -#define __NM_ROUTE_MANAGER_H__ - -#define NM_TYPE_ROUTE_MANAGER (nm_route_manager_get_type ()) -#define NM_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManager)) -#define NM_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) -#define NM_IS_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ROUTE_MANAGER)) -#define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER)) -#define NM_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) - -#define NM_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr" -#define NM_ROUTE_MANAGER_PLATFORM "platform" - -#define NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED "ip4-routes-changed" - -typedef struct _NMRouteManagerClass NMRouteManagerClass; - -GType nm_route_manager_get_type (void); - -gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex); - -gboolean nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex); -void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list); - -NMRouteManager *nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform); - -#endif /* __NM_ROUTE_MANAGER_H__ */ diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c index 151deec8..20781bd4 100644 --- a/src/nm-session-monitor.c +++ b/src/nm-session-monitor.c @@ -260,9 +260,9 @@ ck_init (NMSessionMonitor *monitor) if ((monitor->ck.monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error))) { monitor->ck.cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); g_signal_connect (monitor->ck.monitor, - "changed", - G_CALLBACK (ck_changed), - monitor); + "changed", + G_CALLBACK (ck_changed), + monitor); } else { _LOGE ("error monitoring " CKDB_PATH ": %s", error->message); g_clear_error (&error); diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 4e8e2f98..58beadcd 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -195,7 +195,7 @@ nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gate static inline int _nmtst_platform_ip4_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip4_route_cmp ((const NMPlatformIP4Route *) a, (const NMPlatformIP4Route *) b); + return nm_platform_ip4_route_cmp_full ((const NMPlatformIP4Route *) a, (const NMPlatformIP4Route *) b); } static inline void @@ -215,7 +215,7 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP } for (i = 0; i < len; i++) { - if (nm_platform_ip4_route_cmp (&a[i], &b[i]) != 0) { + if (nm_platform_ip4_route_cmp_full (&a[i], &b[i]) != 0) { char buf[sizeof (_nm_utils_to_string_buffer)]; g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (unsigned long) i, @@ -226,10 +226,29 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP } } +#ifdef __NMP_OBJECT_H__ + +static inline void +nmtst_platform_ip4_routes_equal_aptr (const NMPObject *const*a, const NMPlatformIP4Route *b, gsize len, gboolean ignore_order) +{ + gsize i; + gs_free NMPlatformIP4Route *c_a = NULL; + + g_assert (len > 0); + g_assert (a); + + c_a = g_new (NMPlatformIP4Route, len); + for (i = 0; i < len; i++) + c_a[i] = *NMP_OBJECT_CAST_IP4_ROUTE (a[i]); + nmtst_platform_ip4_routes_equal (c_a, b, len, ignore_order); +} + +#endif + static inline int _nmtst_platform_ip6_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip6_route_cmp ((const NMPlatformIP6Route *) a, (const NMPlatformIP6Route *) b); + return nm_platform_ip6_route_cmp_full ((const NMPlatformIP6Route *) a, (const NMPlatformIP6Route *) b); } static inline void @@ -249,7 +268,7 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP } for (i = 0; i < len; i++) { - if (nm_platform_ip6_route_cmp (&a[i], &b[i]) != 0) { + if (nm_platform_ip6_route_cmp_full (&a[i], &b[i]) != 0) { char buf[sizeof (_nm_utils_to_string_buffer)]; g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (unsigned long) i, @@ -260,18 +279,48 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP } } +#ifdef __NMP_OBJECT_H__ + +static inline void +nmtst_platform_ip6_routes_equal_aptr (const NMPObject *const*a, const NMPlatformIP6Route *b, gsize len, gboolean ignore_order) +{ + gsize i; + gs_free NMPlatformIP6Route *c_a = NULL; + + g_assert (len > 0); + g_assert (a); + + c_a = g_new (NMPlatformIP6Route, len); + for (i = 0; i < len; i++) + c_a[i] = *NMP_OBJECT_CAST_IP6_ROUTE (a[i]); + nmtst_platform_ip6_routes_equal (c_a, b, len, ignore_order); +} + +#endif + #endif #ifdef __NETWORKMANAGER_IP4_CONFIG_H__ +#include "nm-utils/nm-dedup-multi.h" + +static inline NMIP4Config * +nmtst_ip4_config_new (int ifindex) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + + return nm_ip4_config_new (multi_idx, ifindex); +} + static inline NMIP4Config * nmtst_ip4_config_clone (NMIP4Config *config) { - NMIP4Config *copy = nm_ip4_config_new (-1); + NMIP4Config *copy; - g_assert (copy); g_assert (config); + copy = nm_ip4_config_new (nm_ip4_config_get_multi_idx (config), -1); + g_assert (copy); nm_ip4_config_replace (copy, config, NULL); return copy; } @@ -281,13 +330,24 @@ nmtst_ip4_config_clone (NMIP4Config *config) #ifdef __NETWORKMANAGER_IP6_CONFIG_H__ +#include "nm-utils/nm-dedup-multi.h" + +static inline NMIP6Config * +nmtst_ip6_config_new (int ifindex) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + + return nm_ip6_config_new (multi_idx, ifindex); +} + static inline NMIP6Config * nmtst_ip6_config_clone (NMIP6Config *config) { - NMIP6Config *copy = nm_ip6_config_new (-1); + NMIP6Config *copy; - g_assert (copy); g_assert (config); + copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (config), -1); + g_assert (copy); nm_ip6_config_replace (copy, config, NULL); return copy; } diff --git a/src/nm-types.h b/src/nm-types.h index 44b4fecb..e01c48b0 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -38,7 +38,6 @@ typedef struct _NMConfigData NMConfigData; typedef struct _NMArpingManager NMArpingManager; typedef struct _NMConnectionProvider NMConnectionProvider; typedef struct _NMConnectivity NMConnectivity; -typedef struct _NMDefaultRouteManager NMDefaultRouteManager; typedef struct _NMDevice NMDevice; typedef struct _NMDhcp4Config NMDhcp4Config; typedef struct _NMDhcp6Config NMDhcp6Config; @@ -50,12 +49,13 @@ typedef struct _NMNetns NMNetns; typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; -typedef struct _NMRouteManager NMRouteManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData; +struct _NMDedupMultiIndex; + /*****************************************************************************/ typedef enum { @@ -108,6 +108,7 @@ NM_IS_IP_CONFIG_SOURCE_RTPROT (NMIPConfigSource source) /* platform */ typedef struct _NMPlatform NMPlatform; +typedef struct _NMPlatformObject NMPlatformObject; typedef struct _NMPlatformIP4Address NMPlatformIP4Address; typedef struct _NMPlatformIP4Route NMPlatformIP4Route; typedef struct _NMPlatformIP6Address NMPlatformIP6Address; @@ -139,7 +140,8 @@ typedef enum { NM_LINK_TYPE_WIMAX, /* Software types */ - NM_LINK_TYPE_DUMMY = 0x10000, + NM_LINK_TYPE_BNEP = 0x10000, /* Bluetooth Ethernet emulation */ + NM_LINK_TYPE_DUMMY, NM_LINK_TYPE_GRE, NM_LINK_TYPE_GRETAP, NM_LINK_TYPE_IFB, @@ -150,13 +152,13 @@ typedef enum { NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP, NM_LINK_TYPE_OPENVSWITCH, + NM_LINK_TYPE_PPP, NM_LINK_TYPE_SIT, NM_LINK_TYPE_TAP, NM_LINK_TYPE_TUN, NM_LINK_TYPE_VETH, NM_LINK_TYPE_VLAN, NM_LINK_TYPE_VXLAN, - NM_LINK_TYPE_BNEP, /* Bluetooth Ethernet emulation */ /* Software types with slaves */ NM_LINK_TYPE_BRIDGE = 0x10000 | 0x20000, @@ -192,9 +194,27 @@ typedef enum { typedef enum { NM_IP_CONFIG_MERGE_DEFAULT = 0, NM_IP_CONFIG_MERGE_NO_ROUTES = (1LL << 0), - NM_IP_CONFIG_MERGE_NO_DNS = (1LL << 1), + NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES = (1LL << 1), + NM_IP_CONFIG_MERGE_NO_DNS = (1LL << 2), } NMIPConfigMergeFlags; + +/** + * NMIPRouteTableSyncMode: + * @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_ALL: NM will sync all tables, including the + * local table (255). + */ +typedef enum { + NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN = 1, + NM_IP_ROUTE_TABLE_SYNC_MODE_FULL = 2, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL = 3, +} NMIPRouteTableSyncMode; + + /* settings */ typedef struct _NMAgentManager NMAgentManager; typedef struct _NMSecretAgent NMSecretAgent; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 38706f37..c199c5ed 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -32,6 +32,7 @@ #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nm-test-utils-core.h" @@ -39,20 +40,14 @@ /*****************************************************************************/ typedef struct { - NMPlatformLink link; - + const NMPObject *obj; char *udi; - NMPObject *lnk; struct in6_addr ip6_lladdr; } NMFakePlatformLink; typedef struct { GHashTable *options; GArray *links; - GArray *ip4_addresses; - GArray *ip6_addresses; - GArray *ip4_routes; - GArray *ip6_routes; } NMFakePlatformPrivate; struct _NMFakePlatform { @@ -96,7 +91,22 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /*****************************************************************************/ -static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); +static void link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old); + +static gboolean ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr); + +static gboolean ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + const NMPObject *obj); static gboolean ip6_address_add (NMPlatform *platform, int ifindex, @@ -110,14 +120,6 @@ static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in /*****************************************************************************/ -static gboolean -_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, guint8 plen) -{ - return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0; -} - -/*****************************************************************************/ - #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START { \ const char *const _pathid = (pathid); \ @@ -158,288 +160,338 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat return g_strdup (g_hash_table_lookup (priv->options, path)); } -static const char * -type_to_type_name (NMLinkType type) -{ - switch (type) { - case NM_LINK_TYPE_UNKNOWN: - return "unknown"; - case NM_LINK_TYPE_LOOPBACK: - return "loopback"; - case NM_LINK_TYPE_ETHERNET: - return "ethernet"; - case NM_LINK_TYPE_DUMMY: - return "dummy"; - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bond"; - case NM_LINK_TYPE_TEAM: - return "team"; - case NM_LINK_TYPE_VLAN: - return "vlan"; - case NM_LINK_TYPE_NONE: - default: - return NULL; - } -} - -static void -link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) -{ - gs_free char *ip6_lladdr = NULL; - - g_assert (!name || strlen (name) < sizeof(device->link.name)); - - memset (device, 0, sizeof (*device)); - - ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - - device->link.ifindex = name ? ifindex : 0; - device->link.type = type; - device->link.kind = type_to_type_name (type); - device->link.driver = type_to_type_name (type); - device->udi = g_strdup_printf ("fake:%d", ifindex); - device->link.initialized = TRUE; - device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); - if (name) - strcpy (device->link.name, name); - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - break; - default: - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - break; - } -} - static NMFakePlatformLink * link_get (NMPlatform *platform, int ifindex) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device; + int idx; + + if (ifindex <= 0) + g_return_val_if_reached (NULL); - if (ifindex >= priv->links->len) + idx = ifindex - 1; + if (idx >= priv->links->len) goto not_found; - device = &g_array_index (priv->links, NMFakePlatformLink, ifindex); - if (!device->link.ifindex) + + device = &g_array_index (priv->links, NMFakePlatformLink, idx); + if (!device->obj) goto not_found; + g_assert (ifindex == NMP_OBJECT_CAST_LINK (device->obj)->ifindex); + g_assert (device->obj == nm_platform_link_get_obj (platform, ifindex, FALSE)); + return device; not_found: _LOGD ("link not found: %d", ifindex); return NULL; } -static GArray * -link_get_all (NMPlatform *platform) +static void +link_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), priv->links->len); - int i; + gboolean connected; - for (i = 0; i < priv->links->len; i++) - if (g_array_index (priv->links, NMFakePlatformLink, i).link.ifindex) - g_array_append_val (links, g_array_index (priv->links, NMFakePlatformLink, i).link); + /* we must clear the driver, because platform cache want's to set it */ + g_assert (obj_tmp->link.driver == g_intern_string (obj_tmp->link.driver)); + obj_tmp->link.driver = NULL; - return links; -} - -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); + if (NM_IN_SET (obj_tmp->link.type, NM_LINK_TYPE_BRIDGE, + NM_LINK_TYPE_BOND)) { + connected = FALSE; + if (NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP)) { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject *slave_candidate = NULL; + + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (nm_platform_get_cache (platform), + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &slave_candidate) { + if (nmp_cache_link_connected_for_slave (obj_tmp->link.ifindex, slave_candidate)) { + connected = TRUE; + break; + } + } + } + } else + connected = NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP); - return device ? &device->link : NULL; + obj_tmp->link.n_ifi_flags = NM_FLAGS_ASSIGN (obj_tmp->link.n_ifi_flags, IFF_LOWER_UP, connected); + obj_tmp->link.connected = connected; } -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, const char *ifname) +static NMFakePlatformLink * +link_add_pre (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; + NMFakePlatformLink *device; + int ifindex; + NMPObject *o; + NMPlatformLink *link; + gs_free char *ip6_lladdr = NULL; - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + g_assert (!name || strlen (name) < IFNAMSIZ); - if (!strcmp (device->link.name, ifname)) - return &device->link; - } - return NULL; -} + g_array_set_size (priv->links, priv->links->len + 1); + device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + ifindex = priv->links->len; -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; + memset (device, 0, sizeof (*device)); - if ( length == 0 - || length > NM_UTILS_HWADDR_LEN_MAX - || !address) - g_return_val_if_reached (NULL); + o = nmp_object_new_link (ifindex); + link = NMP_OBJECT_CAST_LINK (o); - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - if ( device->link.addr.len == length - && memcmp (device->link.addr.data, address, length) == 0) { - return &device->link; - } + link->ifindex = name ? ifindex : 0; + link->type = type; + link->kind = g_intern_string (nm_link_type_to_string (type)); + link->initialized = TRUE; + if (name) + strcpy (link->name, name); + switch (link->type) { + case NM_LINK_TYPE_DUMMY: + link->n_ifi_flags = NM_FLAGS_SET (link->n_ifi_flags, IFF_NOARP); + break; + default: + link->n_ifi_flags = NM_FLAGS_UNSET (link->n_ifi_flags, IFF_NOARP); + break; } - return NULL; -} -static const NMPObject * -link_get_lnk (NMPlatform *platform, - int ifindex, - NMLinkType link_type, - const NMPlatformLink **out_link) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); + o->_link.netlink.is_in_netlink = TRUE; - if (!device) - return NULL; - - NM_SET_OUT (out_link, &device->link); - - if (!device->lnk) - return NULL; - - if (link_type == NM_LINK_TYPE_NONE) - return device->lnk; + if (address) { + g_assert (address_len > 0 && address_len <= sizeof (link->addr.data)); + memcpy (link->addr.data, address, address_len); + link->addr.len = address_len; + } else + g_assert (address_len == 0); - if ( link_type != device->link.type - || link_type != NMP_OBJECT_GET_CLASS (device->lnk)->lnk_link_type) - return NULL; + device->obj = o; + device->udi = g_strdup_printf ("fake:%d", ifindex); + device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); - return device->lnk; + return device; } static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMFakePlatformLink device; - NMFakePlatformLink *new_device; + NMFakePlatformLink *device; + NMFakePlatformLink *device_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_new_veth = NULL; + NMPCacheOpsType cache_op; + NMPCacheOpsType cache_op_veth = NMP_CACHE_OPS_UNCHANGED; + + device = link_add_pre (platform, name, type, address, address_len); + + if (veth_peer) { + g_assert (type == NM_LINK_TYPE_VETH); + device_veth = link_add_pre (platform, veth_peer, type, NULL, 0); + } else + g_assert (type != NM_LINK_TYPE_VETH); + + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + FALSE, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + if (veth_peer) { + link_add_prepare (platform, device_veth, (NMPObject *) device_veth->obj); + cache_op_veth = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device_veth->obj, + FALSE, + &obj_old_veth, &obj_new_veth); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + } - link_init (&device, priv->links->len, type, name); + if (out_link) + *out_link = NMP_OBJECT_CAST_LINK (device->obj); - if (address) { - g_return_val_if_fail (address_len > 0 && address_len <= sizeof (device.link.addr.data), FALSE); - memcpy (device.link.addr.data, address, address_len); - device.link.addr.len = address_len; - } + link_changed (platform, device, cache_op, NULL); + if (veth_peer) + link_changed (platform, device_veth, cache_op_veth, NULL); + + return TRUE; +} + +static NMFakePlatformLink * +link_add_one (NMPlatform *platform, + const char *name, + NMLinkType link_type, + void (*prepare_fcn) (NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data), + gconstpointer user_data, + const NMPlatformLink **out_link) +{ + NMFakePlatformLink *device; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCacheOpsType cache_op; + int ifindex; - g_array_append_val (priv->links, device); - new_device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + device = link_add_pre (platform, name, NM_LINK_TYPE_VLAN, NULL, 0); - if (device.link.ifindex) { - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, (int) NM_PLATFORM_SIGNAL_ADDED); + ifindex = NMP_OBJECT_CAST_LINK (device->obj)->ifindex; - link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); - } + if (prepare_fcn) + prepare_fcn (platform, device, user_data); - if (out_link) - *out_link = &new_device->link; - return TRUE; + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + FALSE, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + + link_changed (platform, device, cache_op, obj_old); + + device = link_get (platform, ifindex); + if (!device) + g_assert_not_reached (); + + NM_SET_OUT (out_link, NMP_OBJECT_CAST_LINK (device->obj)); + return device; } static gboolean link_delete (NMPlatform *platform, int ifindex) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device = link_get (platform, ifindex); - NMPlatformLink deleted_device; - int i; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_old2 = NULL; + NMPCacheOpsType cache_op; - if (!device || !device->link.ifindex) + if (!device) return FALSE; - memcpy (&deleted_device, &device->link, sizeof (deleted_device)); - memset (&device->link, 0, sizeof (device->link)); - g_clear_pointer (&device->lnk, nmp_object_unref); + obj_old = g_steal_pointer (&device->obj); g_clear_pointer (&device->udi, g_free); + cache_op = nmp_cache_remove (nm_platform_get_cache (platform), + obj_old, + FALSE, + FALSE, + &obj_old2); + g_assert (cache_op == NMP_CACHE_OPS_REMOVED); + g_assert (obj_old2); + g_assert (obj_old == obj_old2); + /* Remove addresses and routes which belong to the deleted interface */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + ipx_address_delete (platform, AF_INET, ifindex, NULL, NULL, NULL); + ipx_address_delete (platform, AF_INET6, ifindex, NULL, NULL, NULL); + ipx_route_delete (platform, AF_INET, ifindex, NULL); + ipx_route_delete (platform, AF_INET6, ifindex, NULL); + + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old2, + NULL); + return TRUE; +} - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); +static void +link_set_obj (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) +{ + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj NMPObject *obj_tmp_tmp = NULL; + NMPCacheOpsType cache_op; - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); + if (!obj_tmp) { + obj_tmp_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp = obj_tmp_tmp; } - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); - } + g_assert (NMP_OBJECT_GET_TYPE (obj_tmp) == NMP_OBJECT_TYPE_LINK); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, (int) NM_PLATFORM_SIGNAL_REMOVED); + link_add_prepare (platform, device, obj_tmp); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + obj_tmp, + FALSE, + &obj_old, &obj_new); + g_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UNCHANGED, + NMP_CACHE_OPS_UPDATED)); + g_assert (obj_old == device->obj); + g_assert (obj_new); - return TRUE; -} + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - return type_to_type_name (nm_platform_link_get_type (platform, ifindex)); + link_changed (platform, device, cache_op, obj_old); } static void -link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal) +link_set_flags (NMPlatform *platform, + NMFakePlatformLink *device, + guint n_ifi_flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (raise_signal) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, (int) NM_PLATFORM_SIGNAL_CHANGED); + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); - if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { - if (device->link.connected) - ip6_address_add (platform, device->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); - else - ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); - } + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.n_ifi_flags = n_ifi_flags; + link_set_obj (platform, device, obj_tmp); +} - if (device->link.master) { - gboolean connected = FALSE; +static void +link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old) +{ + g_assert (device->obj); - NMFakePlatformLink *master = link_get (platform, device->link.master); + g_assert (!nmp_cache_link_connected_needs_toggle (nm_platform_get_cache (platform), + device->obj, NULL, NULL)); - g_return_if_fail (master && master != device); + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old, + device->obj); - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i); + if (!IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { + if (device->obj->link.connected) + ip6_address_add (platform, device->obj->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); + else + ip6_address_delete (platform, device->obj->link.ifindex, device->ip6_lladdr, 64); + } - if (slave && slave->link.master == master->link.ifindex && slave->link.connected) - connected = TRUE; - } + if (device->obj->link.master) { + NMFakePlatformLink *master; - if (master->link.connected != connected) { - master->link.connected = connected; - link_changed (platform, master, TRUE); - } + master = link_get (platform, device->obj->link.master); + link_set_obj (platform, master, NULL); } } @@ -447,7 +499,6 @@ static gboolean link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMFakePlatformLink *device = link_get (platform, ifindex); - gboolean up, connected; if (out_no_firmware) *out_no_firmware = FALSE; @@ -457,29 +508,9 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) return FALSE; } - up = TRUE; - connected = TRUE; - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - case NM_LINK_TYPE_VLAN: - break; - case NM_LINK_TYPE_BRIDGE: - case NM_LINK_TYPE_BOND: - case NM_LINK_TYPE_TEAM: - connected = FALSE; - break; - default: - connected = FALSE; - g_error ("Unexpected device type: %d", device->link.type); - } - - if ( NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) != !!up - || device->link.connected != connected) { - device->link.n_ifi_flags = NM_FLAGS_ASSIGN (device->link.n_ifi_flags, IFF_UP, up); - device->link.connected = connected; - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_ASSIGN (device->obj->link.n_ifi_flags, IFF_UP, TRUE)); return TRUE; } @@ -493,13 +524,9 @@ link_set_down (NMPlatform *platform, int ifindex) return FALSE; } - if (NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) || device->link.connected) { - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = FALSE; - - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_UP)); return TRUE; } @@ -513,10 +540,9 @@ link_set_arp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -530,10 +556,9 @@ link_set_noarp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -541,36 +566,40 @@ static NMPlatformError link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if ( !device - || len == 0 + if ( len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - if ( device->link.addr.len != len - || ( len > 0 - && memcmp (device->link.addr.data, addr, len) != 0)) { - memcpy (device->link.addr.data, addr, len); - device->link.addr.len = len; - link_changed (platform, link_get (platform, ifindex), TRUE); - } + if (!device) + return NM_PLATFORM_ERROR_EXISTS; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.addr.len = len; + memset (obj_tmp->link.addr.data, 0, sizeof (obj_tmp->link.addr.data)); + memcpy (obj_tmp->link.addr.data, addr, len); + + link_set_obj (platform, device, obj_tmp); return NM_PLATFORM_ERROR_SUCCESS; } -static gboolean +static NMPlatformError link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (device) { - device->link.mtu = mtu; - link_changed (platform, device, TRUE); - } else + if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); + return NM_PLATFORM_ERROR_EXISTS; + } - return !!device; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.mtu = mtu; + link_set_obj (platform, device, obj_tmp); + return NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -614,7 +643,7 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_DUMMY: return FALSE; default: @@ -630,7 +659,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -646,7 +675,7 @@ link_supports_sriov (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -663,15 +692,14 @@ link_enslave (NMPlatform *platform, int master, int slave) g_return_val_if_fail (device, FALSE); g_return_val_if_fail (master_device, FALSE); - if (device->link.master != master) { - device->link.master = master; - - if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = TRUE; - } + if (device->obj->link.master != master) { + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - link_changed (platform, device, TRUE); + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.master = master; + if (NM_IN_SET (master_device->obj->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) + obj_tmp->link.n_ifi_flags = NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_UP); + link_set_obj (platform, device, obj_tmp); } return TRUE; @@ -682,40 +710,56 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) { NMFakePlatformLink *master = link_get (platform, master_idx); NMFakePlatformLink *slave = link_get (platform, slave_idx); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); - if (slave->link.master != master->link.ifindex) + if (slave->obj->link.master != master->obj->link.ifindex) return FALSE; - slave->link.master = 0; - - link_changed (platform, slave, TRUE); - link_changed (platform, master, TRUE); - + obj_tmp = nmp_object_clone (slave->obj, FALSE); + obj_tmp->link.master = 0; + link_set_obj (platform, slave, obj_tmp); return TRUE; } -static gboolean -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) +struct vlan_add_data { + guint32 vlan_flags; + int parent; + int vlan_id; +}; + +static void +_vlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) { - NMFakePlatformLink *device; + const struct vlan_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, out_link)) - return FALSE; + obj_tmp = (NMPObject *) device->obj; - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + lnk->lnk_vlan.id = d->vlan_id; + lnk->lnk_vlan.flags = d->vlan_flags; - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; +} - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_vlan.id = vlan_id; - device->link.parent = parent; +static gboolean +vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) +{ + const struct vlan_add_data d = { + .parent = parent, + .vlan_id = vlan_id, + .vlan_flags = vlan_flags, + }; - if (out_link) - *out_link = &device->link; + link_add_one (platform, name, NM_LINK_TYPE_VLAN, + _vlan_add_prepare, &d, out_link); return TRUE; } @@ -734,53 +778,76 @@ link_vlan_change (NMPlatform *platform, return FALSE; } +static void +_vxlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const NMPlatformLnkVxlan *props = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; + + obj_tmp = (NMPObject *) device->obj; + + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + lnk->lnk_vxlan = *props; + + obj_tmp->link.parent = props->parent_ifindex; + obj_tmp->_link.netlink.lnk = lnk; +} + static gboolean link_vxlan_add (NMPlatform *platform, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link) { - NMFakePlatformLink *device; + link_add_one (platform, name, NM_LINK_TYPE_VXLAN, + _vxlan_add_prepare, props, out_link); + return TRUE; +} - if (!link_add (platform, name, NM_LINK_TYPE_VXLAN, NULL, 0, out_link)) - return FALSE; +struct infiniband_add_data { + int parent; + int p_key; +}; - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); +static void +_infiniband_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const struct infiniband_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + obj_tmp = (NMPObject *) device->obj; - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); - device->lnk->lnk_vxlan = *props; - device->link.parent = props->parent_ifindex; + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + lnk->lnk_infiniband.p_key = d->p_key; + lnk->lnk_infiniband.mode = "datagram"; - if (out_link) - *out_link = &device->link; - return TRUE; + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; } static gboolean infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { - NMFakePlatformLink *device, *parent_device; + NMFakePlatformLink *parent_device; char name[IFNAMSIZ]; + const struct infiniband_add_data d = { + .parent = parent, + .p_key = p_key, + }; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); - - if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0, out_link)) - return FALSE; - - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_infiniband.p_key = p_key; - device->lnk->lnk_infiniband.mode = "datagram"; - device->link.parent = parent; + link_add_one (platform, name, NM_LINK_TYPE_INFINIBAND, + _infiniband_add_prepare, &d, out_link); return TRUE; } @@ -793,7 +860,7 @@ infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); return link_delete (platform, nm_platform_link_get_ifindex (platform, name)); } @@ -804,7 +871,7 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti g_return_val_if_fail (device, FALSE); - if (device->link.type != NM_LINK_TYPE_WIFI) + if (device->obj->link.type != NM_LINK_TYPE_WIFI) return FALSE; if (caps) { @@ -894,59 +961,25 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) /*****************************************************************************/ -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP4Address *address; - int count = 0, i; - - /* Count addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - count++; - } - - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count); - - /* Fill addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } - - return addresses; -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) +static gboolean +ipx_address_add (NMPlatform *platform, int addr_family, const NMPlatformObject *address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP6Address *address; - int count = 0, i; - - /* Count addresses */ - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - count++; - } + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCache *cache = nm_platform_get_cache (platform); - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count); + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - /* Fill addresses */ - count = 0; - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + address); - return addresses; + cache_op = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + return TRUE; } static gboolean @@ -960,9 +993,7 @@ ip4_address_add (NMPlatform *platform, guint32 flags, const char *label) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP4Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -977,28 +1008,7 @@ ip4_address_add (NMPlatform *platform, if (label) g_strlcpy (address.label, label, sizeof (address.label)); - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - gboolean changed; - - if ( item->ifindex != address.ifindex - || item->address != address.address - || item->plen != address.plen - || !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen)) - continue; - - changed = !nm_platform_ip4_address_cmp (item, &address); - - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; - } - - g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; + return ipx_address_add (platform, AF_INET, (const NMPlatformObject *) &address); } static gboolean @@ -1011,9 +1021,7 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint32 flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP6Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -1026,380 +1034,321 @@ ip6_address_add (NMPlatform *platform, address.preferred = preferred; address.n_ifa_flags = flags; - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - gboolean changed; - - if ( item->ifindex != address.ifindex - || !IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) - continue; + return ipx_address_add (platform, AF_INET6, (const NMPlatformObject *) &address); +} - changed = !nm_platform_ip6_address_cmp (item, &address); +static gboolean +ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr) +{ + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; + guint i; + guint32 peer_addr_i; + + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + 0), + &o) { + const NMPObject *obj_old = NULL; + + if (addr_family == AF_INET) { + const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (o); + + if ( address->ifindex != ifindex + || (addr && address->address != *((guint32 *) addr)) + || (plen && address->plen != *plen) + || ( peer_addr + && (((peer_addr_i ^ address->peer_address) & _nm_utils_ip4_prefix_to_netmask (address->plen)) != 0))) + continue; + } else { + const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS (o); + + g_assert (!peer_addr); + if ( address->ifindex != ifindex + || (addr && !IN6_ARE_ADDR_EQUAL (&address->address, addr)) + || (plen && address->plen != *plen)) + continue; + } - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + FALSE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } - g_array_append_val (priv->ip6_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); + } return TRUE; } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) { - NMPlatformIP4Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; + return ipx_address_delete (platform, AF_INET, ifindex, &addr, &plen, &peer_address); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { - NMPlatformIP6Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; -} - -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && _ip4_address_equal_peer_net (address->peer_address, peer_address, plen)) - return address; - } - - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) - return address; - } - - return NULL; + return ipx_address_delete (platform, AF_INET6, ifindex, &addr, &plen, NULL); } /*****************************************************************************/ -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) +static gboolean +ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + const NMPObject *obj) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP4Route *route; + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; guint i; - - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - /* Fill routes */ - for (i = 0; i < priv->ip4_routes->len; i++) { - route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); - } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); - } - } + NMPObjectType obj_type; + + if (addr_family == AF_UNSPEC) { + g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + g_assert (ifindex == -1); + ifindex = obj->object.ifindex; + obj_type = NMP_OBJECT_GET_TYPE (obj); + } else { + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + g_assert (!obj); + g_assert (ifindex > 0); + obj_type = addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE; } - return routes; -} - -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP6Route *route; - guint i; - - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - /* Fill routes */ - for (i = 0; i < priv->ip6_routes->len; i++) { - route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + obj_type, + ifindex), + &o) { + const NMPObject *obj_old = NULL; + + if (obj) { + if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { + const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (o); + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (obj); + + if ( route->network != r->network + || route->plen != r->plen + || route->metric != r->metric) + continue; } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); + const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (o); + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (obj); + + if ( !IN6_ARE_ADDR_EQUAL (&route->network, &r->network) + || route->plen != r->plen + || route->metric != r->metric) + continue; } } - } - - return routes; -} -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - NMPlatformIP4Route deleted_route; - - if ( route->ifindex != ifindex - || route->network != network - || route->plen != plen - || route->metric != metric) - continue; - - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip4_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + FALSE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); + } return TRUE; } static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +ip_route_delete (NMPlatform *platform, const NMPObject *obj) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - NMPlatformIP6Route deleted_route; - - if ( route->ifindex != ifindex - || !IN6_ARE_ADDR_EQUAL (&route->network, &network) - || route->plen != plen - || route->metric != metric) - continue; - - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip6_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); - } + g_assert (NM_IS_FAKE_PLATFORM (platform)); + g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - return TRUE; + return ipx_route_delete (platform, AF_UNSPEC, -1, obj); } -static gboolean -ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP4Route rt = *route; - guint i; - - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - rt.network = nm_utils_ip4_address_clear_host_address (rt.network, rt.plen); - rt.scope_inv = nm_platform_route_scope_inv (rt.gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); +static NMPlatformError +ip_route_add (NMPlatform *platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) +{ + NMDedupMultiIter iter; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + const NMPObject *o = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean has_gateway = FALSE; + NMPlatformIPRoute *r = NULL; + NMPlatformIP4Route *r4 = NULL; + NMPlatformIP6Route *r6 = NULL; + gboolean has_same_weak_id; + gboolean only_dirty; + guint16 nlmsgflags; + + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + flags = NM_FLAGS_UNSET (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); + + /* currently, only replace is implemented. */ + g_assert (flags == NMP_NLM_FLAG_REPLACE); + + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + (const NMPlatformObject *) route); + r = NMP_OBJECT_CAST_IP_ROUTE (obj); + nm_platform_ip_route_normalize (addr_family, r); + + switch (addr_family) { + case AF_INET: + r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + if (r4->gateway) + has_gateway = TRUE; + break; + case AF_INET6: + r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + if (!IN6_IS_ADDR_UNSPECIFIED (&r6->gateway)) + has_gateway = TRUE; + break; + default: + nm_assert_not_reached (); + } - if (rt.gateway) { - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, - NMPlatformIP4Route, i); - guint32 gate = ntohl (item->network) >> (32 - item->plen); - guint32 host = ntohl (rt.gateway) >> (32 - item->plen); + if (has_gateway) { + gboolean has_route_to_gw = FALSE; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_GET_TYPE (obj), + 0), + &o) { + if (addr_family == AF_INET) { + const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o); + guint32 n = nm_utils_ip4_address_clear_host_address (item->network, item->plen); + guint32 g = nm_utils_ip4_address_clear_host_address (r4->gateway, item->plen); + + if ( r->ifindex == item->ifindex + && n == g) { + has_route_to_gw = TRUE; + break; + } + } else { + const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE (o); - if (rt.ifindex == item->ifindex && gate == host) - break; + if ( r->ifindex == item->ifindex + && nm_utils_ip6_address_same_prefix (&r6->gateway, &item->network, item->plen)) { + has_route_to_gw = TRUE; + break; + } + } } - if (i == priv->ip4_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet4_ntop (rt.network, NULL), rt.plen, rt.metric); - return FALSE; + if (!has_route_to_gw) { + if (addr_family == AF_INET) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", + r->ifindex, nm_utils_inet4_ntop (r4->network, NULL), r->plen, r->metric); + } else { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + r->ifindex, nm_utils_inet6_ntop (&r6->network, NULL), r->plen, r->metric); + } + return NM_PLATFORM_ERROR_UNSPECIFIED; } } - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (item->network != rt.network) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; + has_same_weak_id = FALSE; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_all (platform, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj), + &o) { + if (addr_family == AF_INET) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (o), r4, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (o), r6, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + has_same_weak_id = TRUE; } - g_array_append_val (priv->ip4_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; -} - -static gboolean -ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP6Route rt = *route; - guint i; - - rt.metric = nm_utils_ip6_route_metric_normalize (rt.metric); - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - nm_utils_ip6_address_clear_host_address (&rt.network, &rt.network, rt.plen); - - if (!IN6_IS_ADDR_UNSPECIFIED (&rt.gateway)) { - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, - NMPlatformIP6Route, i); - guint8 gate_bits = rt.gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - - if ( rt.ifindex == item->ifindex - && memcmp (&rt.gateway, &item->network, item->plen / 8) == 0 - && gate_bits == host_bits) - break; - } - if (i == priv->ip6_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet6_ntop (&rt.network, NULL), rt.plen, rt.metric); - return FALSE; + nlmsgflags = 0; + if (has_same_weak_id) { + switch (flags) { + case NMP_NLM_FLAG_REPLACE: + nlmsgflags = NLM_F_REPLACE; + break; + default: + g_assert_not_reached (); + break; } } - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (!IN6_ARE_ADDR_EQUAL (&item->network, &rt.network)) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; + /* we manipulate the cache the same was as NMLinuxPlatform does it. */ + cache_op = nmp_cache_update_netlink_route (cache, + obj, + FALSE, + nlmsgflags, + &obj_old, + &obj_new, + &obj_replace, + NULL); + only_dirty = FALSE; + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } - g_array_append_val (priv->ip6_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; -} - -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (route->ifindex == ifindex - && route->network == network - && route->plen == plen - && route->metric == metric) - return route; - } - - return NULL; -} - -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (route->ifindex == ifindex - && IN6_ARE_ADDR_EQUAL (&route->network, &network) - && route->plen == plen - && route->metric == metric) - return route; + if (obj_replace) { + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } } - return NULL; + return NM_PLATFORM_ERROR_SUCCESS; } /*****************************************************************************/ @@ -1409,12 +1358,8 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform); - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); priv->links = g_array_new (TRUE, TRUE, sizeof (NMFakePlatformLink)); - priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address)); - priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address)); - priv->ip4_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } void @@ -1428,16 +1373,13 @@ nm_fake_platform_setup (void) nm_platform_setup (platform); - /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0, NULL); - /* add loopback interface */ - link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0, NULL); + link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, NULL, 0, NULL); /* add some ethernets */ - link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); - link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); - link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); + link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); + link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, NULL, 0, NULL); } static void @@ -1451,13 +1393,9 @@ finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); - g_clear_pointer (&device->lnk, nmp_object_unref); + g_clear_pointer (&device->obj, nmp_object_unref); } g_array_unref (priv->links); - g_array_unref (priv->ip4_addresses); - g_array_unref (priv->ip6_addresses); - g_array_unref (priv->ip4_routes); - g_array_unref (priv->ip6_routes); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } @@ -1473,15 +1411,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_get_udi = link_get_udi; @@ -1525,21 +1456,11 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; - platform_class->ip4_route_add = ip4_route_add; - platform_class->ip6_route_add = ip6_route_add; - platform_class->ip4_route_delete = ip4_route_delete; - platform_class->ip6_route_delete = ip6_route_delete; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_delete = ip_route_delete; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6b84c185..c4c93ed3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -48,6 +48,7 @@ #include "nmp-object.h" #include "nmp-netns.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" #include "nm-utils/unaligned.h" @@ -103,6 +104,14 @@ #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif + +static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */); + +G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); +#define RTA_PREF 20 +#undef RTA_MAX +#define RTA_MAX (MAX ((__RTA_MAX - 1), RTA_PREF)) + #ifndef MACVLAN_FLAG_NOPROMISC #define MACVLAN_FLAG_NOPROMISC 1 #endif @@ -197,6 +206,21 @@ typedef enum { INFINIBAND_ACTION_DELETE_CHILD, } InfinibandAction; +typedef enum { + CHANGE_LINK_TYPE_UNSPEC, + CHANGE_LINK_TYPE_SET_MTU, + CHANGE_LINK_TYPE_SET_ADDRESS, +} ChangeLinkType; + +typedef struct { + union { + struct { + gconstpointer address; + gsize length; + } set_address; + }; +} ChangeLinkData; + enum { DELAYED_ACTION_IDX_REFRESH_ALL_LINKS, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES, @@ -255,13 +279,25 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType act static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type); -static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); -static void cache_prune_candidates_prune (NMPlatform *platform); +static void cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); +static void cache_prune_all (NMPlatform *platform); static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static void ASSERT_NETNS_CURRENT (NMPlatform *platform); /*****************************************************************************/ +static NMPlatformError +wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) +{ + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return NM_PLATFORM_ERROR_SUCCESS; + if (seq_result < 0) + return (NMPlatformError) seq_result; + return NM_PLATFORM_ERROR_NETLINK; +} + static const char * wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) { @@ -287,35 +323,117 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g return buf0; } -/****************************************************************** +/***************************************************************************** * Support IFLA_INET6_ADDR_GEN_MODE - ******************************************************************/ + *****************************************************************************/ static int _support_user_ipv6ll = 0; #define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +static void +_support_user_ipv6ll_detect (struct nlattr **tb) +{ + gboolean supported; + + nm_assert (_support_user_ipv6ll_still_undecided ()); + + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + _support_user_ipv6ll = supported ? 1 : -1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", + supported ? "detected" : "not detected"); +} + static gboolean _support_user_ipv6ll_get (void) { if (_support_user_ipv6ll_still_undecided ()) { - _support_user_ipv6ll = -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume no support"); - return FALSE; + _support_user_ipv6ll = 1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); } - return _support_user_ipv6ll > 0; + return _support_user_ipv6ll >= 0; +} + +/***************************************************************************** + * extended IFA_FLAGS support + *****************************************************************************/ +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + gboolean support; + + nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); + nm_assert (msg); + + msg_hdr = nlmsg_hdr (msg); + + nm_assert (msg_hdr && msg_hdr->nlmsg_type == RTM_NEWADDR); + + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if (NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6)) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + support = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); + _support_kernel_extended_ifa_flags = support ? 1 : -1; + _LOG2D ("kernel-support: extended-ifa-flags: %s", support ? "detected" : "not detected"); } +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); + _support_kernel_extended_ifa_flags = 1; + } + return _support_kernel_extended_ifa_flags >= 0; +} + +/***************************************************************************** + * Support RTA_PREF + *****************************************************************************/ + +static int _support_rta_pref = 0; +#define _support_rta_pref_still_undecided() (G_UNLIKELY (_support_rta_pref == 0)) + static void -_support_user_ipv6ll_detect (struct nlattr **tb) +_support_rta_pref_detect (struct nlattr **tb) { - if (_support_user_ipv6ll_still_undecided ()) { - gboolean supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + gboolean supported; + + nm_assert (_support_rta_pref_still_undecided ()); - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); + /* RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ + supported = !!tb[RTA_PREF]; + _support_rta_pref = supported ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + supported ? "detected" : "not detected"); +} + +static gboolean +_support_rta_pref_get (void) +{ + if (_support_rta_pref_still_undecided ()) { + /* if we couldn't detect support, we fallback on compile-time check, whether + * RTA_PREF is present in the kernel headers. */ + _support_rta_pref = RTA_PREF_SUPPORTED_AT_COMPILETIME ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + RTA_PREF_SUPPORTED_AT_COMPILETIME ? "assume support" : "assume no support"); } + return _support_rta_pref >= 0; } /****************************************************************** @@ -377,6 +495,7 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_WWAN_NET, "wwan", NULL, "wwan" }, { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, @@ -388,13 +507,13 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, + { NM_LINK_TYPE_PPP, "ppp", NULL, "ppp" }, { NM_LINK_TYPE_SIT, "sit", "sit", NULL }, { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, - { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_BRIDGE, "bridge", "bridge", "bridge" }, { NM_LINK_TYPE_BOND, "bond", "bond", "bond" }, @@ -628,7 +747,7 @@ _linktype_get_type (NMPlatform *platform, { guint i; - ASSERT_NETNS_CURRENT (platform); + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (platform); nm_assert (ifname); if (completed_from_cache) { @@ -693,6 +812,8 @@ _linktype_get_type (NMPlatform *platform, return NM_LINK_TYPE_SIT; else if (arptype == ARPHRD_TUNNEL6) return NM_LINK_TYPE_IP6TNL; + else if (arptype == ARPHRD_PPP) + return NM_LINK_TYPE_PPP; { NMPUtilsEthtoolDriverInfo driver_info; @@ -762,6 +883,10 @@ _linktype_get_type (NMPlatform *platform, * aside from the DEVTYPE. */ if (!g_strcmp0 (devtype, "gadget")) return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (!g_strcmp0 (devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; } } @@ -772,32 +897,149 @@ _linktype_get_type (NMPlatform *platform, * libnl unility functions and wrappers ******************************************************************/ -#define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +/* copied from iproute2's addattr_l(). */ +static gboolean +_nl_addattr_l (struct nlmsghdr *n, + int maxlen, + int type, + const void *data, + int alen) +{ + int len = RTA_LENGTH (alen); + struct rtattr *rta; + + if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) + return FALSE; + + rta = NLMSG_TAIL (n); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); + return TRUE; +} + static void _nm_auto_nl_msg_cleanup (void *ptr) { nlmsg_free (*((struct nl_msg **) ptr)); } +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) static const char * -_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) { - const char *str_type = NULL; + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; - switch (type) { - case RTM_NEWLINK: str_type = "NEWLINK"; break; - case RTM_DELLINK: str_type = "DELLINK"; break; - case RTM_NEWADDR: str_type = "NEWADDR"; break; - case RTM_DELADDR: str_type = "DELADDR"; break; - case RTM_NEWROUTE: str_type = "NEWROUTE"; break; - case RTM_DELROUTE: str_type = "DELROUTE"; break; + nm_utils_to_string_buffer_init (&buf, &len); + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: s = "NEWLINK"; break; + case RTM_DELLINK: s = "DELLINK"; break; + case RTM_NEWADDR: s = "NEWADDR"; break; + case RTM_DELADDR: s = "DELADDR"; break; + case RTM_NEWROUTE: s = "NEWROUTE"; break; + case RTM_DELROUTE: s = "DELROUTE"; break; + default: s = NULL; break; } - if (str_type) - g_strlcpy (buf, str_type, len); + + if (s) + nm_utils_strbuf_append (&buf, &len, "RTM_%s", s); else - g_snprintf (buf, len, "(%d)", type); - return buf; + nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START { \ + if (NM_FLAGS_ALL (flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F (NLM_F_REQUEST, "request"); + _F (NLM_F_MULTI, "multi"); + _F (NLM_F_ACK, "ack"); + _F (NLM_F_ECHO, "echo"); + _F (NLM_F_DUMP_INTR, "dump_intr"); + _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + _F (NLM_F_REPLACE, "replace"); + _F (NLM_F_EXCL, "excl"); + _F (NLM_F_CREATE, "create"); + _F (NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + _F (NLM_F_DUMP, "dump"); + _F (NLM_F_ROOT, "root"); + _F (NLM_F_MATCH, "match"); + _F (NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +static int +_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); +} +#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) + +static int +_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy); } +#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__) + +static int +_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); +} +#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) /****************************************************************** * NMPObject/netlink functions @@ -826,7 +1068,7 @@ _parse_af_inet6 (NMPlatform *platform, guint8 *out_addr_gen_mode_inv, gboolean *out_addr_gen_mode_valid) { - static struct nla_policy policy[IFLA_INET6_MAX+1] = { + static const struct nla_policy policy[IFLA_INET6_MAX+1] = { [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, [IFLA_INET6_CACHEINFO] = { .minlen = nm_offsetofend (struct ifla_cacheinfo, retrans_time) }, [IFLA_INET6_CONF] = { .minlen = 4 }, @@ -862,7 +1104,8 @@ _parse_af_inet6 (NMPlatform *platform, /* Hack to detect support addrgenmode of the kernel. We only parse * netlink messages that we receive from kernel, hence this check * is valid. */ - _support_user_ipv6ll_detect (tb); + if (_support_user_ipv6ll_still_undecided ()) + _support_user_ipv6ll_detect (tb); if (tb[IFLA_INET6_ADDR_GEN_MODE]) { i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); @@ -892,7 +1135,7 @@ errout: static NMPObject * _parse_lnk_gre (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_GRE_MAX + 1] = { + static const struct nla_policy policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_LINK] = { .type = NLA_U32 }, [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, @@ -952,7 +1195,7 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, @@ -998,7 +1241,7 @@ _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_UNSPEC, .minlen = sizeof (struct in6_addr)}, @@ -1051,7 +1294,7 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ipip (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1089,7 +1332,7 @@ _parse_lnk_ipip (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, }; @@ -1132,7 +1375,7 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macsec (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[__IFLA_MACSEC_MAX] = { + static const struct nla_policy policy[__IFLA_MACSEC_MAX] = { [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, @@ -1182,7 +1425,7 @@ _parse_lnk_macsec (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_sit (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1284,7 +1527,7 @@ _vlan_qos_mapping_from_nla (struct nlattr *nlattr, static NMPObject * _parse_lnk_vlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VLAN_MAX+1] = { + static const struct nla_policy policy[IFLA_VLAN_MAX+1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, [IFLA_VLAN_FLAGS] = { .minlen = nm_offsetofend (struct ifla_vlan_flags, flags) }, [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, @@ -1370,7 +1613,7 @@ struct nm_ifla_vxlan_port_range { static NMPObject * _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_ID] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, @@ -1460,7 +1703,7 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) static NMPObject * _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFLA_MAX+1] = { + static const struct nla_policy policy[IFLA_MAX+1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MTU] = { .type = NLA_U32 }, @@ -1488,7 +1731,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr [IFLA_NET_NS_PID] = { .type = NLA_U32 }, [IFLA_NET_NS_FD] = { .type = NLA_U32 }, }; - static struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { + static const struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, [IFLA_INFO_DATA] = { .type = NLA_NESTED }, [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, @@ -1514,6 +1757,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr return NULL; ifi = nlmsg_data(nlh); + if (ifi->ifi_family != AF_UNSPEC) + return NULL; + obj = nmp_object_new_link (ifi->ifi_index); if (id_only) @@ -1680,7 +1926,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * Also, sometimes the info-data is missing for updates. In this case * we want to keep the previously received lnk_data. */ nmp_object_unref (lnk_data); - lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk); } if (address_complete_from_cache) obj->link.addr = link_cached->link.addr; @@ -1711,7 +1957,7 @@ errout: static NMPObject * _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFA_MAX+1] = { + static const struct nla_policy policy[IFA_MAX+1] = { [IFA_LABEL] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFA_CACHEINFO] = { .minlen = nm_offsetofend (struct ifa_cacheinfo, tstamp) }, @@ -1733,7 +1979,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) goto errout; is_v4 = ifa->ifa_family == AF_INET; - err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, policy); + err = nlmsg_parse (nlh, sizeof(*ifa), tb, IFA_MAX, policy); if (err < 0) goto errout; @@ -1826,10 +2072,12 @@ errout: static NMPObject * _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[RTA_MAX+1] = { + static const struct nla_policy policy[RTA_MAX+1] = { + [RTA_TABLE] = { .type = NLA_U32 }, [RTA_IIF] = { .type = NLA_U32 }, [RTA_OIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_PREF] = { .type = NLA_U8 }, [RTA_FLOW] = { .type = NLA_U32 }, [RTA_CACHEINFO] = { .minlen = nm_offsetofend (struct rta_cacheinfo, rta_tsage) }, [RTA_METRICS] = { .type = NLA_NESTED }, @@ -1849,7 +2097,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) } nh; guint32 mss; guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0; - guint32 table; if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) return NULL; @@ -1869,12 +2116,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (err < 0) goto errout; - table = tb[RTA_TABLE] - ? nla_get_u32 (tb[RTA_TABLE]) - : (guint32) rtm->rtm_table; - if (table != RT_TABLE_MAIN) - goto errout; - /*****************************************************************/ is_v4 = rtm->rtm_family == AF_INET; @@ -1928,7 +2169,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) || tb[RTA_GATEWAY] || tb[RTA_FLOW]) { int ifindex = 0; - NMIPAddr gateway = NMIPAddrInit; + NMIPAddr gateway = { }; if (tb[RTA_OIF]) ifindex = nla_get_u32 (tb[RTA_OIF]); @@ -1956,7 +2197,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) mss = 0; if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; - static struct nla_policy rtax_policy[RTAX_MAX + 1] = { + static const struct nla_policy rtax_policy[RTAX_MAX + 1] = { [RTAX_LOCK] = { .type = NLA_U32 }, [RTAX_ADVMSS] = { .type = NLA_U32 }, [RTAX_WINDOW] = { .type = NLA_U32 }, @@ -1990,6 +2231,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip_route.table_coerced = nm_platform_route_table_coerce ( tb[RTA_TABLE] + ? nla_get_u32 (tb[RTA_TABLE]) + : (guint32) rtm->rtm_table); + obj->ip_route.ifindex = nh.ifindex; if (_check_addr_or_errout (tb, RTA_DST, addr_len)) @@ -2015,9 +2260,13 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); } - if (!is_v4 && tb[RTA_SRC]) { - _check_addr_or_errout (tb, RTA_SRC, addr_len); - memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + if (is_v4) + obj->ip4_route.tos = rtm->rtm_tos; + else { + if (tb[RTA_SRC]) { + _check_addr_or_errout (tb, RTA_SRC, addr_len); + memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + } obj->ip6_route.src_plen = rtm->rtm_src_len; } @@ -2027,12 +2276,20 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip_route.initcwnd = initcwnd; obj->ip_route.initrwnd = initrwnd; obj->ip_route.mtu = mtu; - obj->ip_route.tos = rtm->rtm_tos; - obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); - obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); + obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); + obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); obj->ip_route.lock_initcwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITCWND); obj->ip_route.lock_initrwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITRWND); - obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); + obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); + + if (!is_v4) { + /* Detect support for RTA_PREF by inspecting the netlink message. */ + if (_support_rta_pref_still_undecided ()) + _support_rta_pref_detect (tb); + + if (tb[RTA_PREF]) + obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]); + } if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) { /* we must not straight way reject cloned routes, because we might have cached @@ -2135,12 +2392,14 @@ nla_put_failure: static gboolean _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, - NMLinkType link_type) + NMLinkType link_type, + const char *veth_peer) { struct nlattr *info; const char *kind; nm_assert (msg); + nm_assert (!!veth_peer == (link_type == NM_LINK_TYPE_VETH)); kind = nm_link_type_to_rtnl_type_string (link_type); if (!kind) @@ -2151,11 +2410,26 @@ _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); + if (veth_peer) { + struct ifinfomsg ifi = { }; + struct nlattr *data, *info_peer; + + if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) + goto nla_put_failure; + if (!(info_peer = nla_nest_start (msg, 1 /*VETH_INFO_PEER*/))) + goto nla_put_failure; + if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + NLA_PUT_STRING (msg, IFLA_IFNAME, veth_peer); + nla_nest_end (msg, info_peer); + nla_nest_end (msg, data); + } + nla_nest_end (msg, info); return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -2363,7 +2637,7 @@ _nl_msg_new_address (int nlmsg_type, && *((in_addr_t *) address) != 0) { in_addr_t broadcast; - broadcast = *((in_addr_t *) address) | ~nm_utils_ip4_prefix_to_netmask (plen); + broadcast = *((in_addr_t *) address) | ~_nm_utils_ip4_prefix_to_netmask (plen); NLA_PUT (msg, IFA_BROADCAST, addr_len, &broadcast); } @@ -2394,87 +2668,109 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static guint32 +ip_route_get_lock_flag (const NMPlatformIPRoute *route) +{ + return (((guint32) route->lock_window) << RTAX_WINDOW) + | (((guint32) route->lock_cwnd) << RTAX_CWND) + | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) + | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) + | (((guint32) route->lock_mtu) << RTAX_MTU); +} + /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ static struct nl_msg * _nl_msg_new_route (int nlmsg_type, - int nlmsg_flags, - int family, - int ifindex, - NMIPConfigSource source, - unsigned char scope, - gconstpointer network, - guint8 plen, - gconstpointer gateway, - guint32 metric, - guint32 mss, - gconstpointer pref_src, - gconstpointer src, - guint8 src_plen, - guint8 tos, - guint32 window, - guint32 cwnd, - guint32 initcwnd, - guint32 initrwnd, - guint32 mtu, - guint32 lock) + guint16 nlmsgflags, + const NMPObject *obj) { struct nl_msg *msg; + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + gboolean is_v4 = klass->addr_family == AF_INET; + const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj)); + const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE); struct rtmsg rtmsg = { - .rtm_family = family, - .rtm_tos = tos, - .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ - .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source), - .rtm_scope = scope, + .rtm_family = klass->addr_family, + .rtm_tos = is_v4 + ? obj->ip4_route.tos + : 0, + .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, + .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (obj->ip_route.rt_source), + .rtm_scope = is_v4 + ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) + : RT_SCOPE_NOWHERE, .rtm_type = RTN_UNICAST, .rtm_flags = 0, - .rtm_dst_len = plen, - .rtm_src_len = src ? src_plen : 0, + .rtm_dst_len = obj->ip_route.plen, + .rtm_src_len = is_v4 + ? 0 + : NMP_OBJECT_CAST_IP6_ROUTE (obj)->src_plen, }; gsize addr_len; - nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - nm_assert (network); - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); if (!msg) g_return_val_if_reached (NULL); if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); - NLA_PUT (msg, RTA_DST, addr_len, network); + NLA_PUT (msg, RTA_DST, addr_len, + is_v4 + ? (gconstpointer) &obj->ip4_route.network + : (gconstpointer) &obj->ip6_route.network); - if (src) - NLA_PUT (msg, RTA_SRC, addr_len, src); + if (!is_v4) { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->src)) + NLA_PUT (msg, RTA_SRC, addr_len, &obj->ip6_route.src); + } - NLA_PUT_U32 (msg, RTA_PRIORITY, metric); + NLA_PUT_U32 (msg, RTA_PRIORITY, obj->ip_route.metric); - if (pref_src) - NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); + if (table > 0xFF) + NLA_PUT_U32 (msg, RTA_TABLE, table); + + if (is_v4) { + if (NMP_OBJECT_CAST_IP4_ROUTE (obj)->pref_src) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->pref_src)) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); + } - if (mss || window || cwnd || initcwnd || initrwnd || mtu || lock) { + if ( obj->ip_route.mss + || obj->ip_route.window + || obj->ip_route.cwnd + || obj->ip_route.initcwnd + || obj->ip_route.initrwnd + || obj->ip_route.mtu + || lock) { struct nlattr *metrics; metrics = nla_nest_start (msg, RTA_METRICS); if (!metrics) goto nla_put_failure; - if (mss) - NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); - if (window) - NLA_PUT_U32 (msg, RTAX_WINDOW, window); - if (cwnd) - NLA_PUT_U32 (msg, RTAX_CWND, cwnd); - if (initcwnd) - NLA_PUT_U32 (msg, RTAX_INITCWND, initcwnd); - if (initrwnd) - NLA_PUT_U32 (msg, RTAX_INITRWND, initrwnd); - if (mtu) - NLA_PUT_U32 (msg, RTAX_MTU, mtu); + if (obj->ip_route.mss) + NLA_PUT_U32 (msg, RTAX_ADVMSS, obj->ip_route.mss); + if (obj->ip_route.window) + NLA_PUT_U32 (msg, RTAX_WINDOW, obj->ip_route.window); + if (obj->ip_route.cwnd) + NLA_PUT_U32 (msg, RTAX_CWND, obj->ip_route.cwnd); + if (obj->ip_route.initcwnd) + NLA_PUT_U32 (msg, RTAX_INITCWND, obj->ip_route.initcwnd); + if (obj->ip_route.initrwnd) + NLA_PUT_U32 (msg, RTAX_INITRWND, obj->ip_route.initrwnd); + if (obj->ip_route.mtu) + NLA_PUT_U32 (msg, RTAX_MTU, obj->ip_route.mtu); if (lock) NLA_PUT_U32 (msg, RTAX_LOCK, lock); @@ -2482,10 +2778,17 @@ _nl_msg_new_route (int nlmsg_type, } /* We currently don't have need for multi-hop routes... */ - if ( gateway - && memcmp (gateway, &nm_ip_addr_zero, addr_len) != 0) - NLA_PUT (msg, RTA_GATEWAY, addr_len, gateway); - NLA_PUT_U32 (msg, RTA_OIF, ifindex); + if (is_v4) { + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&obj->ip6_route.gateway)) + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); + } + NLA_PUT_U32 (msg, RTA_OIF, obj->ip_route.ifindex); + + if ( !is_v4 + && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) + NLA_PUT_U8 (msg, RTA_PREF, obj->ip6_route.rt_pref); return msg; @@ -2494,56 +2797,27 @@ nla_put_failure: g_return_val_if_reached (NULL); } -/*****************************************************************************/ - -static int _support_kernel_extended_ifa_flags = -1; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; - - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; - - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); - _LOG2D ("kernel-support: extended-ifa-flags: %s", _support_kernel_extended_ifa_flags ? "detected" : "not detected"); -} - -static gboolean -_support_kernel_extended_ifa_flags_get (void) -{ - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); - _support_kernel_extended_ifa_flags = 1; - } - return _support_kernel_extended_ifa_flags; -} - /****************************************************************** * NMPlatform types and functions ******************************************************************/ +typedef enum { + DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, + DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, +} DelayedActionWaitForNlResponseType; + typedef struct { guint32 seq_number; WaitForNlResponseResult seq_result; + DelayedActionWaitForNlResponseType response_type; gint64 timeout_abs_ns; WaitForNlResponseResult *out_seq_result; - gint *out_refresh_all_in_progess; + union { + gint *out_refresh_all_in_progess; + NMPObject **out_route_get; + gpointer out_data; + } response; } DelayedActionWaitForNlResponseData; typedef struct { @@ -2553,11 +2827,12 @@ typedef struct { guint32 nlh_seq_last_handled; #endif guint32 nlh_seq_last_seen; - NMPCache *cache; GIOChannel *event_channel; guint event_id; - gboolean sysctl_get_warned; + bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + + bool sysctl_get_warned; GHashTable *sysctl_get_prev_values; NMUdevClient *udev_client; @@ -2578,8 +2853,6 @@ typedef struct { gint is_handling; } delayed_action; - GHashTable *prune_candidates; - GHashTable *wifi_data; } NMLinuxPlatformPrivate; @@ -2599,8 +2872,15 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) NMPlatform * nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) { + gboolean use_udev = FALSE; + + if ( nmp_netns_is_initial () + && access ("/sys", W_OK) == 0) + use_udev = TRUE; + return g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, + NM_PLATFORM_USE_UDEV, use_udev, NM_PLATFORM_NETNS_SUPPORT, netns_support, NULL); } @@ -2611,13 +2891,6 @@ nm_linux_platform_setup (void) nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); } -static void -ASSERT_NETNS_CURRENT (NMPlatform *platform) -{ - nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); -} - /*****************************************************************************/ #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ @@ -2676,7 +2949,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; gssize nwrote; - gsize len; + gssize len; char *actual; gs_free char *actual_free = NULL; int errsv; @@ -2731,6 +3004,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat * about to write. */ len = strlen (value) + 1; + nm_assert (len > 0); if (len > 512) actual = actual_free = g_malloc (len + 1); else @@ -2752,16 +3026,27 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat break; } } - if (nwrote == -1 && errsv != EEXIST) { - _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s", - path, value, errsv, strerror (errsv)); + if (nwrote == -1) { + NMLogLevel level = LOGL_ERR; + + if (errsv == EEXIST) { + level = LOGL_DEBUG; + } else if ( errsv == EINVAL + && nm_utils_sysctl_ip_conf_is_path (AF_INET6, path, NULL, "mtu")) { + /* setting the MTU can fail under regular conditions. Suppress + * logging a warning. */ + level = LOGL_DEBUG; + } + + _NMLOG (level, "sysctl: failed to set '%s' to '%s': (%d) %s", + path, value, errsv, strerror (errsv)); } else if (nwrote < len - 1) { _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts", path, value); } if (nwrote < len - 1) { - if (close (fd) != 0) { + if (nm_close (fd) != 0) { if (errsv != 0) errno = errsv; } else if (errsv != 0) @@ -2770,7 +3055,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat errno = EIO; return FALSE; } - if (close (fd) != 0) { + if (nm_close (fd) != 0) { /* errno is already properly set. */ return FALSE; } @@ -2804,7 +3089,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char * if (!priv->sysctl_get_prev_values) { _nm_logging_clear_platform_logging_cache = _nm_logging_clear_platform_logging_cache_impl; sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform); - priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->sysctl_get_prev_values = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); } else prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid); @@ -2872,20 +3157,30 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat /*****************************************************************************/ -static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +static NMPlatformKernelSupportFlags +check_kernel_support (NMPlatform *platform, + NMPlatformKernelSupportFlags request_flags) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + NMPlatformKernelSupportFlags response = 0; - return _support_kernel_extended_ifa_flags_get (); -} + nm_assert (NM_IS_LINUX_PLATFORM (platform)); -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + if (_support_kernel_extended_ifa_flags_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS; + } - return _support_user_ipv6ll_get (); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { + if (_support_user_ipv6ll_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL; + } + + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)) { + if (_support_rta_pref_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF; + } + + return response; } static void @@ -2896,86 +3191,6 @@ process_events (NMPlatform *platform) /*****************************************************************************/ -#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ - ({ \ - NMPCacheId _cache_id; \ - \ - ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ - nmp_cache_id_init_object_type (&_cache_id, (obj_type), (visible_only)), \ - NULL)); \ - }) - -/*****************************************************************************/ - -static void -do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible) -{ - gboolean is_visible; - NMPObject obj_clone; - const NMPClass *klass; - - nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); - - nm_assert (obj || cache_op == NMP_CACHE_OPS_UNCHANGED); - nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - - ASSERT_NETNS_CURRENT (platform); - - switch (cache_op) { - case NMP_CACHE_OPS_ADDED: - if (!nmp_object_is_visible (obj)) - return; - break; - case NMP_CACHE_OPS_UPDATED: - is_visible = nmp_object_is_visible (obj); - if (!was_visible && is_visible) - cache_op = NMP_CACHE_OPS_ADDED; - else if (was_visible && !is_visible) { - /* This is a bit ugly. The object was visible and changed in a way that it became invisible. - * We raise a removed signal, but contrary to a real 'remove', @obj is already changed to be - * different from what it was when the user saw it the last time. - * - * The more correct solution would be to have cache_pre_hook() create a clone of the original - * value before it was changed to become invisible. - * - * But, don't bother. Probably nobody depends on the original values and only cares about the - * id properties (which are still correct). - */ - cache_op = NMP_CACHE_OPS_REMOVED; - } else if (!is_visible) - return; - break; - case NMP_CACHE_OPS_REMOVED: - if (!was_visible) - return; - break; - default: - g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); - return; - } - - klass = NMP_OBJECT_GET_CLASS (obj); - - _LOGt ("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - /* don't expose @obj directly, but clone the public fields. A signal handler might - * call back into NMPlatform which could invalidate (or modify) @obj. */ - memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); - g_signal_emit (platform, - _nm_platform_signal_id_get (klass->signal_type_id), - 0, - (int) klass->obj_type, - obj_clone.object.ifindex, - &obj_clone.object, - (int) cache_op); -} - -/*****************************************************************************/ - _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS), @@ -3043,11 +3258,12 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns (); char b[255]; - nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT"%s%s)", + nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT", response-type %d%s%s)", data->seq_number, timeout < 0 ? "-" : "", (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND, (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, + (int) data->response_type, data->seq_result ? ", " : "", data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); } else @@ -3109,9 +3325,22 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; if (data->out_seq_result) *data->out_seq_result = seq_result; - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; + switch (data->response_type) { + case DELAYED_ACTION_RESPONSE_TYPE_VOID: + break; + case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: + if (data->response.out_refresh_all_in_progess) { + nm_assert (*data->response.out_refresh_all_in_progess > 0); + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + } + break; + case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: + if (data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + data->response.out_route_get = NULL; + } + break; } g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); @@ -3146,13 +3375,15 @@ delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + cache_op = nmp_cache_update_link_master_connected (nm_platform_get_cache (platform), master_ifindex, &obj_old, &obj_new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } static void @@ -3273,7 +3504,7 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) any = TRUE; priv->delayed_action.is_handling--; - cache_prune_candidates_prune (platform); + cache_prune_all (platform); return any; } @@ -3319,13 +3550,15 @@ static void delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { DelayedActionWaitForNlResponseData data = { .seq_number = seq_number, .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), .out_seq_result = out_seq_result, - .out_refresh_all_in_progess = out_refresh_all_in_progess, + .response_type = response_type, + .response.out_data = response_out_data, }; delayed_action_schedule (platform, @@ -3336,146 +3569,111 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, - nmp_cache_id_init_object_type (&cache_id, obj_type, FALSE), - priv->prune_candidates); - _LOGt ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, - priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); -} - -static void -cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) -{ - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (!priv->prune_candidates) - priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - if (_LOGt_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); -} - -static void -cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) +cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) { - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - if (priv->prune_candidates) { - if (_LOGt_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_remove (priv->prune_candidates, obj); + NMDedupMultiIter iter; + const NMPObject *obj; + NMPCacheOpsType cache_op; + NMPLookup lookup; + NMPCache *cache = nm_platform_get_cache (platform); + + nmp_lookup_init_obj_type (&lookup, + obj_type); + nm_dedup_multi_iter_init (&iter, + nmp_cache_lookup (cache, + &lookup)); + while (nm_dedup_multi_iter_next (&iter)) { + if (iter.current->dirty) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + obj = iter.current->obj; + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_old, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); + } } } static void -cache_prune_candidates_prune (NMPlatform *platform) +cache_prune_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GHashTable *prune_candidates; - GHashTableIter iter; - const NMPObject *obj; - gboolean was_visible; - NMPCacheOpsType cache_op; - - if (!priv->prune_candidates) - return; - - prune_candidates = priv->prune_candidates; - priv->prune_candidates = NULL; + DelayedActionType iflags, action_type; - g_hash_table_iter_init (&iter, prune_candidates); - while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { - nm_auto_nmpobj NMPObject *obj_cache = NULL; + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL; + FOR_EACH_DELAYED_ACTION (iflags, action_type) { + bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)]; - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + if (*p) { + *p = FALSE; + cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); + } } - - g_hash_table_unref (prune_candidates); } static void -cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) { - NMPlatform *platform = user_data; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPClass *klass; char str_buf[sizeof (_nm_utils_to_string_buffer)]; char str_buf2[sizeof (_nm_utils_to_string_buffer)]; + NMPCache *cache = nm_platform_get_cache (platform); - nm_assert (old || new); - nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); - nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); - nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); - nm_assert (!old || !new || NMP_OBJECT_GET_CLASS (old) == NMP_OBJECT_GET_CLASS (new)); + ASSERT_nmp_cache_ops (cache, cache_op, obj_old, obj_new); + nm_assert (cache_op != NMP_CACHE_OPS_UNCHANGED); - klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); - - nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new); _LOGt ("update-cache-%s: %s: %s%s%s", klass->obj_type_name, - (ops_type == NMP_CACHE_OPS_UPDATED + (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" - : (ops_type == NMP_CACHE_OPS_REMOVED + : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" - : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), - (ops_type != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) - : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), - (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (ops_type == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (cache_op != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (cache_op == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) : "")); switch (klass->obj_type) { case NMP_OBJECT_TYPE_LINK: { /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if ( old - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); - if ( new - && (!old || old->link.master != new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + if ( obj_old + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_old->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_old->link.master)); + if ( obj_new + && (!obj_old || obj_old->link.master != obj_new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_new->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.master)); } { /* check whether we are about to change a master link that needs toggling connected state. */ - if ( new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + if ( obj_new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex)); } { int ifindex = 0; /* if we remove a link (from netlink), we must refresh the addresses and routes */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && !new->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && !obj_new->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink != obj_old->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { delayed_action_schedule (platform, @@ -3494,40 +3692,40 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP * Currently, kernel misses to sent us a notification in this case * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && !new->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && !obj_new->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { - const NMPlatformLink *const *links; - - links = cache_lookup_all_objects (NMPlatformLink, platform, NMP_OBJECT_TYPE_LINK, FALSE); - if (links) { - for (; *links; links++) { - const NMPlatformLink *l = (*links); - - if (l->parent == ifindex) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); - } + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *l; + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, &lookup), + &l) { + if (l->parent == ifindex) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); } } } { /* if a link goes down, we must refresh routes */ - if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink - && ( ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_UP)) - || ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP)))) { + if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink + && ( ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_UP)) + || ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I * think kernel does send RTM_DELROUTE events for IPv6 routes, so * we might not need to refresh IPv6 routes. */ @@ -3537,17 +3735,17 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP NULL); } } - if ( NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (new && new->_link.netlink.is_in_netlink) - && (!old || !old->_link.netlink.is_in_netlink)) + if ( NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) + && (obj_new && obj_new->_link.netlink.is_in_netlink) + && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - if (!new->_link.netlink.lnk) { + if (!obj_new->_link.netlink.lnk) { /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that * kernel didn't send this notification, thus when we first learn about a link * that lacks an lnk_data we re-request it again. * * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - switch (new->link.type) { + switch (obj_new->link.type) { case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_IP6TNL: case NM_LINK_TYPE_INFINIBAND: @@ -3558,23 +3756,23 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP case NM_LINK_TYPE_VXLAN: delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); break; default: break; } } - if ( new->link.type == NM_LINK_TYPE_VETH - && new->link.parent == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_VETH + && obj_new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). * Request it again. */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } - if ( new->link.type == NM_LINK_TYPE_ETHERNET - && new->link.addr.len == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.addr.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -3584,7 +3782,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } } { @@ -3592,14 +3790,14 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP int ifindex1 = 0, ifindex2 = 0; gboolean changed_master, changed_connected; - changed_master = (new && new->_link.netlink.is_in_netlink && new->link.master > 0 ? new->link.master : 0) - != (old && old->_link.netlink.is_in_netlink && old->link.master > 0 ? old->link.master : 0); - changed_connected = (new && new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP) : 2) - != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) : 2); + changed_master = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 ? obj_new->link.master : 0) + != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 ? obj_old->link.master : 0); + changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP) : 2) + != (obj_old && obj_old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) : 2); if (changed_master || changed_connected) { - ifindex1 = (old && old->_link.netlink.is_in_netlink && old->link.master > 0) ? old->link.master : 0; - ifindex2 = (new && new->_link.netlink.is_in_netlink && new->link.master > 0) ? new->link.master : 0; + ifindex1 = (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) ? obj_old->link.master : 0; + ifindex2 = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) ? obj_new->link.master : 0; if (ifindex1 > 0) delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); @@ -3607,29 +3805,13 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); } } - { - if ( ( (ops_type == NMP_CACHE_OPS_REMOVED) - || ( (ops_type == NMP_CACHE_OPS_UPDATED) - && new - && !new->_link.netlink.is_in_netlink)) - && old - && old->_link.netlink.is_in_netlink - && old->link.master) { - /* sometimes we receive a wrong RTM_DELLINK message when unslaving - * a device. Refetch the link again to check whether the device - * is really gone. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1285719#c2 */ - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (old->link.ifindex)); - } - } break; case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP6_ADDRESS: { /* Address deletion is sometimes accompanied by route deletion. We need to * check all routes belonging to the same interface. */ - if (ops_type == NMP_CACHE_OPS_REMOVED) { + if (cache_op == NMP_CACHE_OPS_REMOVED) { delayed_action_schedule (platform, (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES @@ -3643,69 +3825,113 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } } -static void -cache_post (NMPlatform *platform, - struct nlmsghdr *msghdr, - NMPCacheOpsType cache_op, - NMPObject *obj, - NMPObject *obj_cache) +/*****************************************************************************/ + +static guint32 +_nlh_seq_next_get (NMLinuxPlatformPrivate *priv) +{ + /* generate a new sequence number, but skip zero. */ + return priv->nlh_seq_next++ ?: priv->nlh_seq_next++; +} + +/** + * _nl_send_nlmsghdr: + * @platform: + * @nlhdr: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + */ +static int +_nl_send_nlmsghdr (NMPlatform *platform, + struct nlmsghdr *nlhdr, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; + int nle; - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache)); - - if (msghdr->nlmsg_type == RTM_NEWROUTE) { - DelayedActionType action_type; - - action_type = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; - if ( !delayed_action_refresh_all_in_progress (platform, action_type) - && nmp_cache_find_other_route_for_same_destination (priv->cache, obj)) { - /* via `iproute route change` the user can update an existing route which effectively - * means that a new object (with a different ID) comes into existance, replacing the - * old on. In other words, as the ID of the object changes, we really see a new - * object with the old one deleted. - * However, kernel decides not to send a RTM_DELROUTE event for that. - * - * To hack around that, check if the update leaves us with multiple routes for the - * same network/plen,metric part. In that case, we cannot do better then requesting - * all routes anew, which sucks. - * - * One mitigation to avoid a dump is only to request a new dump, if we are not in - * the middle of an ongoing dump (delayed_action_refresh_all_in_progress). */ - delayed_action_schedule (platform, action_type, NULL); + nm_assert (nlhdr); + + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; + + { + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + struct iovec iov = { + .iov_base = nlhdr, + .iov_len = nlhdr->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int try_count; + + if (!nlhdr->nlmsg_pid) + nlhdr->nlmsg_pid = nl_socket_get_local_port (priv->nlh); + nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + try_count = 0; +again: + nle = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0); + if (nle < 0) { + nle = errno; + if (nle == EINTR && try_count++ < 100) + goto again; + _LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", g_strerror (nle), nle); + return -nle; } } -} -/*****************************************************************************/ + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; +} +/** + * _nl_send_nlmsg: + * @platform: + * @nlmsg: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). + */ static int -_nl_send_auto_with_seq (NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) +_nl_send_nlmsg (NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nlmsghdr *nlhdr; guint32 seq; int nle; - /* complete the message with a sequence number (ensuring it's not zero). */ - seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++; - - nlmsg_hdr (nlmsg)->nlmsg_seq = seq; + nlhdr = nlmsg_hdr (nlmsg); + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; nle = nl_send_auto (priv->nlh, nlmsg); + if (nle < 0) { + _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nl_geterror (nle), nle); + return nle; + } - if (nle >= 0) { - nle = 0; - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_refresh_all_in_progess); - } else - _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle); - - return nle; + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; } static void @@ -3713,17 +3939,23 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int nle; if (name && !name[0]) name = NULL; g_return_if_fail (ifindex > 0 || name); - _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); + _LOGD ("do-request-link: %d %s", ifindex, name ?: ""); if (ifindex > 0) { - cache_prune_candidates_record_one (platform, - (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + const NMDedupMultiEntry *entry; + + entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); + if (entry) { + priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } } event_handler_read_netlink (platform, FALSE); @@ -3734,8 +3966,15 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha name, 0, 0); - if (nlmsg) - _nl_send_auto_with_seq (platform, nlmsg, NULL, NULL); + if (nlmsg) { + nle = _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", + ifindex, name ?: "", + nl_geterror (nle), -nle); + return; + } + } } static void @@ -3755,7 +3994,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; FOR_EACH_DELAYED_ACTION (iflags, action_type) { - cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); + priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; + nmp_cache_dirty_set_all (nm_platform_get_cache (platform), + delayed_action_refresh_to_object_type (iflags)); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { @@ -3795,7 +4036,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_auto_with_seq (platform, nlmsg, NULL, out_refresh_all_in_progess) < 0) { + if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { nm_assert (*out_refresh_all_in_progess > 0); *out_refresh_all_in_progess -= 1; } @@ -3825,13 +4066,12 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - if (data->seq_number == priv->nlh_seq_last_seen) { - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; - data->out_refresh_all_in_progess = NULL; - break; - } + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progess + && data->seq_number == priv->nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + break; } } } @@ -3879,18 +4119,19 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu static void event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; - nm_auto_nmpobj NMPObject *obj_cache = NULL; NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; - char buf_nlmsg_type[16]; + char buf_nlmsghdr[400]; gboolean id_only = FALSE; - gboolean was_visible; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean is_dump; msghdr = nlmsg_hdr (msg); - if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) + if ( _support_kernel_extended_ifa_flags_still_undecided () + && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); if (!handle_events) @@ -3902,156 +4143,144 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event id_only = TRUE; } - obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { - _LOGT ("event-notification: %s, seq %u: ignore", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq); + _LOGT ("event-notification: %s: ignore", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); return; } - _LOGT ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, - id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - switch (msghdr->nlmsg_type) { - - case RTM_NEWLINK: case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWROUTE: - case RTM_GETLINK: - cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - - cache_post (platform, msghdr, cache_op, obj, obj_cache); - - do_emit_signal (platform, obj_cache, cache_op, was_visible); - break; - - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + is_dump = delayed_action_refresh_all_in_progress (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); break; - default: - break; + is_dump = FALSE; } - cache_prune_candidates_drop (platform, obj_cache); -} - -/*****************************************************************************/ - -static const NMPObject * -cache_lookup_link (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if (!nmp_object_is_visible (obj_cache)) - return NULL; - - return obj_cache; -} - -const NMPlatformObject *const* -nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL); - g_return_val_if_fail (cache_id, NULL); + _LOGT ("event-notification: %s%s: %s", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), + is_dump ? ", in-dump" : "", + nmp_object_to_string (obj, + id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, + NULL, 0)); - return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - cache_id, out_len); -} + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + + switch (msghdr->nlmsg_type) { + + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_GETLINK: + cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; -static GArray * -link_get_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; + case RTM_NEWROUTE: { + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + gboolean resync_required = FALSE; + gboolean only_dirty = FALSE; + + if (obj->ip_route.rt_cloned) { + /* a cloned route might be a response for RTM_GETROUTE. Check, whether it is. */ + nm_assert (!nmp_object_is_alive (obj)); + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + guint i; + + nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET + && data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + if (data->seq_number == nlmsg_hdr (msg)->nlmsg_seq) { + *data->response.out_route_get = nmp_object_clone (obj, FALSE); + data->response.out_route_get = NULL; + break; + } + } + } + } + } - return nmp_cache_lookup_multi_to_array (priv->cache, - NMP_OBJECT_TYPE_LINK, - nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, TRUE)); -} + cache_op = nmp_cache_update_netlink_route (cache, + obj, + is_dump, + msghdr->nlmsg_flags, + &obj_old, + &obj_new, + &obj_replace, + &resync_required); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + /* we found an object that is to be replaced by the RTM_NEWROUTE message. + * While we invoke the signal, the platform cache might change and invalidate + * the findings. Mitigate that (for the most part), by marking the entry as + * dirty and only delete @obj_replace if it is still dirty afterwards. + * + * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, + * the signal handlers do not cause to call the platform again, so the cache + * is not really changing. -- if they would, it would anyway be dangerous to overflow + * the stack and it's not ensured that the processing of netlink messages is + * reentrant (maybe it is). + */ + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; + } + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj; + if (obj_replace) { + /* the RTM_NEWROUTE message indicates that another route was replaced. + * Remove it now. */ + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_replace, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } + } - obj = cache_lookup_link (platform, ifindex); - return obj ? &obj->link : NULL; -} + if (resync_required) { + /* we'd like to avoid such resyncs as they are expensive and we should only rely on the + * netlink events. This needs investigation. */ + _LOGT ("schedule resync of routes after RTM_NEWROUTE"); + delayed_action_schedule (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + NULL); + } + break; + } -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, - const char *ifname) -{ - const NMPObject *obj = NULL; + case RTM_DELLINK: + case RTM_DELADDR: + case RTM_DELROUTE: + cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; - if (ifname && *ifname) { - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + default: + break; + } } - return obj ? &obj->link : NULL; -} - -struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; -}; - -static gboolean -_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) -{ - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); -} - -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - const NMPObject *obj; - struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, - }; - - if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) - return NULL; - if (!address) - return NULL; - - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, NULL, TRUE, NM_LINK_TYPE_NONE, - (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); - return obj ? &obj->link : NULL; -} - -/*****************************************************************************/ - -static const NMPObject * -link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - NM_SET_OUT (out_link, &obj->link); - - if (!obj->_link.netlink.lnk) - return NULL; - if ( link_type != NM_LINK_TYPE_NONE - && ( link_type != obj->link.type - || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) - return NULL; - - return obj->_link.netlink.lnk; } /*****************************************************************************/ @@ -4063,34 +4292,21 @@ do_add_link_with_lookup (NMPlatform *platform, struct nl_msg *nlmsg, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj = NULL; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; + NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { - /* hm, a link with such a name already exists. Try reloading first. */ - do_request_link (platform, 0, name); - - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); - if (obj) { - _LOGE ("do-add-link[%s/%s]: link already exists: %s", - name, - nm_link_type_to_string (link_type), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - return FALSE; - } - } - - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, nm_link_type_to_string (link_type), nl_geterror (nle), -nle); + NM_SET_OUT (out_link, NULL); return FALSE; } @@ -4100,35 +4316,29 @@ do_add_link_with_lookup (NMPlatform *platform, _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-link[%s/%s]: %s", name, nm_link_type_to_string (link_type), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); - - if (!obj) { - /* either kernel signaled failure, or it signaled success and the link object - * is not (yet) in the cache. Try to reload it... */ - do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + if (out_link) { + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); + *out_link = NMP_OBJECT_CAST_LINK (obj); } - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; + return seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } -static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +static NMPlatformError +do_add_addrroute (NMPlatform *platform, + const NMPObject *obj_id, + struct nl_msg *nlmsg, + gboolean suppress_netlink_failure) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - const NMPObject *obj; nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, @@ -4136,72 +4346,69 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - return FALSE; + return NM_PLATFORM_ERROR_NETLINK; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + _NMLOG (( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + || ( suppress_netlink_failure + && seq_result < 0)) ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * FIXME: if the object already existed previously, we might not notice a - * missing update. It's not clear how to fix that reliably without refechting - * all the time. */ - obj = nmp_cache_lookup_obj (priv->cache, obj_id); - if (!obj) { - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - obj = nmp_cache_lookup_obj (priv->cache, obj_id); + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); } - /* Adding is only successful, if kernel reported success *and* we have the - * expected object in cache afterwards. */ - return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + return wait_for_nl_response_to_plerr (seq_result); } static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - gboolean success = TRUE; + gboolean success; const char *log_detail = ""; event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - goto out; + return FALSE; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); + success = TRUE; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { /* ok */ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) @@ -4216,42 +4423,60 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * else success = FALSE; - _NMLOG (success ? LOGL_DEBUG : LOGL_ERR, + _NMLOG (success ? LOGL_DEBUG : LOGL_WARN, "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); -out: - if (!nmp_cache_lookup_obj (priv->cache, obj_id)) - return TRUE; + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is still there after we receive the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + } - /* such an object still exists in the cache. To be sure, refetch it (and - * hope it's gone) */ - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - return !nmp_cache_lookup_obj (priv->cache, obj_id); + return success; } -static WaitForNlResponseResult -do_change_link_request (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) +static NMPlatformError +do_change_link (NMPlatform *platform, + ChangeLinkType change_link_type, + int ifindex, + struct nl_msg *nlmsg, + const ChangeLinkData *data) { nm_auto_pop_netns NMPNetns *netns = NULL; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + char s_buf[256]; + NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; + NMLogLevel log_level = LOGL_DEBUG; + const char *log_result = "failure"; + const char *log_detail = ""; + gs_free char *log_detail_free = NULL; + const NMPObject *obj_cache; - if (!nm_platform_netns_push (platform, &netns)) - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + if (!nm_platform_netns_push (platform, &netns)) { + log_level = LOGL_ERR; + log_detail = ", failure to change network namespace"; + goto out; + } retry: - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { - _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", - ifindex, - nl_geterror (nle), -nle); - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + log_level = LOGL_ERR; + log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", + nl_geterror (nle), -nle); + log_detail = log_detail_free; + goto out; } /* always refetch the link after changing it. There seems to be issues @@ -4267,18 +4492,6 @@ retry: nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; goto retry; } - return seq_result; -} - -static NMPlatformError -do_change_link_result (NMPlatform *platform, - int ifindex, - WaitForNlResponseResult seq_result) -{ - char s_buf[256]; - NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; - NMLogLevel log_level = LOGL_DEBUG; - const char *log_result = "failure", *log_detail = ""; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { log_result = "success"; @@ -4287,38 +4500,43 @@ do_change_link_result (NMPlatform *platform, } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { log_detail = ", firmware not found"; result = NM_PLATFORM_ERROR_NO_FIRMWARE; + } else if ( NM_IN_SET (-((int) seq_result), ERANGE) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + log_detail = ", setting MTU to requested size is not possible"; + result = NM_PLATFORM_ERROR_CANT_SET_MTU; + } else if ( NM_IN_SET (-((int) seq_result), ENFILE) + && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS + && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) + && obj_cache->link.addr.len == data->set_address.length + && memcmp (obj_cache->link.addr.data, data->set_address.address, data->set_address.length) == 0) { + /* workaround ENFILE which may be wrongly returned (bgo #770456). + * If the MAC address is as expected, assume success? */ + log_result = "success"; + log_detail = " (assume success changing address)"; + result = NM_PLATFORM_ERROR_SUCCESS; } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { log_level = LOGL_DEBUG; result = NM_PLATFORM_ERROR_NOT_FOUND; } else { - log_level = LOGL_ERR; + log_level = LOGL_WARN; result = NM_PLATFORM_ERROR_UNSPECIFIED; } + +out: _NMLOG (log_level, "do-change-link[%d]: %s changing link: %s%s", ifindex, log_result, wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); - return result; } -static NMPlatformError -do_change_link (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) -{ - WaitForNlResponseResult seq_result; - - seq_result = do_change_link_request (platform, ifindex, nlmsg); - return do_change_link_result (platform, ifindex, seq_result); -} - static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) @@ -4337,9 +4555,6 @@ link_add (NMPlatform *platform, (void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } - _LOGD ("link: add link '%s' of type '%s' (%d)", - name, nm_link_type_to_string (type), (int) type); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -4352,7 +4567,7 @@ link_add (NMPlatform *platform, if (address && address_len) NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - if (!_nl_msg_new_link_set_linkinfo (nlmsg, type)) + if (!_nl_msg_new_link_set_linkinfo (nlmsg, type, veth_peer)) return FALSE; return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); @@ -4364,11 +4579,10 @@ static gboolean link_delete (NMPlatform *platform, int ifindex) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); NMPObject obj_id; const NMPObject *obj; - obj = nmp_cache_lookup_link (priv->cache, ifindex); + obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; @@ -4383,56 +4597,11 @@ link_delete (NMPlatform *platform, int ifindex) return do_delete_object (platform, &obj_id, nlmsg); } -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { - /* We could detect the @link_type. In this case the function returns - * our internel module names, which differs from rtnl_link_get_type(): - * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") - * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). - * Note that this functions is only used by NMDeviceGeneric to - * set type_description. */ - return nm_link_type_to_string (obj->link.type); - } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - return obj->link.kind ?: "unknown"; -} - -static gboolean -link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *link; - struct udev_device *udevice = NULL; - const char *uproperty; - - link = nmp_cache_lookup_link (priv->cache, ifindex); - if (!link) - return FALSE; - - udevice = link->_link.udev.device; - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); - if (!uproperty) - return FALSE; - - *unmanaged = nm_udev_utils_property_as_boolean (uproperty); - return TRUE; -} - static gboolean link_refresh (NMPlatform *platform, int ifindex) { do_request_link (platform, ifindex, NULL); - return !!cache_lookup_link (platform, ifindex); + return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } static gboolean @@ -4454,7 +4623,7 @@ link_set_netns (NMPlatform *platform, return FALSE; NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); @@ -4484,7 +4653,7 @@ link_change_flags (NMPlatform *platform, flags_set); if (!nlmsg) return NM_PLATFORM_ERROR_UNSPECIFIED; - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4519,7 +4688,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) static const char * link_get_udi (NMPlatform *platform, int ifindex) { - const NMPObject *obj = cache_lookup_link (platform, ifindex); + const NMPObject *obj = nm_platform_link_get_obj (platform, ifindex, TRUE); if ( !obj || !obj->_link.netlink.is_in_netlink @@ -4528,35 +4697,21 @@ link_get_udi (NMPlatform *platform, int ifindex) return udev_device_get_syspath (obj->_link.udev.device); } -static struct udev_device * -link_get_udev_device (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - /* we don't use cache_lookup_link() because this would return NULL - * if the link is not visible in libnl. For link_get_udev_device() - * we want to return whatever we have, even if the link itself - * appears invisible via other platform functions. */ - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - return obj_cache ? obj_cache->_link.udev.device : NULL; -} - static NMPlatformError link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + if (!_support_user_ipv6ll_get ()) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); return NM_PLATFORM_ERROR_OPNOTSUPP; } - _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -4567,7 +4722,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4582,7 +4737,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -4606,7 +4761,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) nm_auto_pop_netns NMPNetns *netns = NULL; const NMPObject *obj; - obj = cache_lookup_link (platform, ifindex); + obj = nm_platform_link_get_obj (platform, ifindex, TRUE); /* Only ARPHRD_ETHER links can possibly support VLANs. */ if (!obj || obj->link.arptype != ARPHRD_ETHER) @@ -4647,8 +4802,12 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; gs_free char *mac = NULL; - WaitForNlResponseResult seq_result; - char s_buf[256]; + const ChangeLinkData d = { + .set_address = { + .address = address, + .length = length, + }, + }; if (!address || !length) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); @@ -4668,32 +4827,32 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); - seq_result = do_change_link_request (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); +nla_put_failure: + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); +} - if (NM_IN_SET (-((int) seq_result), ENFILE)) { - const NMPObject *obj_cache; +static NMPlatformError +link_set_name (NMPlatform *platform, int ifindex, const char *name) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - /* workaround ENFILE which may be wrongly returned (bgo #770456). - * If the MAC address is as expected, assume success? */ + _LOGD ("link: change %d: name: %s", ifindex, name); - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if ( obj_cache - && obj_cache->link.addr.len == length - && memcmp (obj_cache->link.addr.data, address, length) == 0) { - _NMLOG (LOGL_DEBUG, - "do-change-link[%d]: %s changing link: %s%s", - ifindex, - "success", - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), - " (assume success changing address)"); - return NM_PLATFORM_ERROR_SUCCESS; - } - } + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); - return do_change_link_result (platform, ifindex, seq_result); + NLA_PUT (nlmsg, IFLA_IFNAME, strlen (name) + 1, name); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (FALSE); } static gboolean @@ -4710,7 +4869,7 @@ link_get_permanent_address (NMPlatform *platform, return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); } -static gboolean +static NMPlatformError link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -4728,7 +4887,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5359,7 +5518,6 @@ link_vlan_change (NMPlatform *platform, const NMVlanQosMapping *egress_map, gsize n_egress_map) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj_cache; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const NMPObjectLnkVlan *lnk; @@ -5371,7 +5529,7 @@ link_vlan_change (NMPlatform *platform, char s_ingress[256]; char s_egress[256]; - obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( !obj_cache || !obj_cache->_link.netlink.is_in_netlink) { _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); @@ -5437,7 +5595,7 @@ link_vlan_change (NMPlatform *platform, new_n_egress_map)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static int @@ -5449,9 +5607,6 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, struct ifreq ifr = { }; int fd; - _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, - tap ? "tap" : "tun", name, owner, group); - fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) return FALSE; @@ -5467,30 +5622,30 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; if (ioctl (fd, TUNSETIFF, &ifr)) { - close (fd); + nm_close (fd); return FALSE; } if (owner >= 0 && owner < G_MAXINT32) { if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { - close (fd); + nm_close (fd); return FALSE; } } if (group >= 0 && group < G_MAXINT32) { if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { - close (fd); + nm_close (fd); return FALSE; } } if (ioctl (fd, TUNSETPERSIST, 1)) { - close (fd); + nm_close (fd); return FALSE; } do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, NULL, NULL); @@ -5520,7 +5675,7 @@ link_enslave (NMPlatform *platform, int master, int slave) NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5540,7 +5695,6 @@ _infiniband_partition_action (NMPlatform *platform, int p_key, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_close int dirfd = -1; char ifname_parent[IFNAMSIZ]; const NMPObject *obj; @@ -5576,7 +5730,7 @@ _infiniband_partition_action (NMPlatform *platform, if (action == INFINIBAND_ACTION_DELETE_CHILD) return TRUE; - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); if (out_link) *out_link = obj ? &obj->link : NULL; @@ -5714,16 +5868,15 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru static gboolean link_can_assume (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformObject *const *objs; - guint i, len; - const NMPObject *link; + NMPLookup lookup; + const NMPObject *link, *o; + NMDedupMultiIter iter; + NMPCache *cache = nm_platform_get_cache (platform); if (ifindex <= 0) return FALSE; - link = cache_lookup_link (platform, ifindex); + link = nm_platform_link_get_obj (platform, ifindex, TRUE); if (!link) return FALSE; @@ -5733,21 +5886,21 @@ link_can_assume (NMPlatform *platform, int ifindex) if (link->link.master > 0) return TRUE; - if (nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex), - NULL)) + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (nmp_cache_lookup (cache, &lookup)) return TRUE; - objs = nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), - &len); - if (objs) { - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *a = (NMPlatformIP6Address *) objs[i]; - - if (!IN6_IS_ADDR_LINKLOCAL (&a->address)) - return TRUE; - } + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex); + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (cache, &lookup), + &o) { + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ADDRESS); + if (!IN6_IS_ADDR_LINKLOCAL (&o->ip6_address.address)) + return TRUE; } return FALSE; } @@ -5822,33 +5975,6 @@ link_get_driver_info (NMPlatform *platform, /*****************************************************************************/ -static GArray * -ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - - return nmp_cache_lookup_multi_to_array (priv->cache, - obj_type, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, - obj_type, - ifindex)); -} - -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); -} - static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -5877,7 +6003,7 @@ ip4_address_add (NMPlatform *platform, label); nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return do_add_addrroute (platform, &obj_id, nlmsg); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5906,8 +6032,8 @@ ip6_address_add (NMPlatform *platform, preferred, NULL); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - return do_add_addrroute (platform, &obj_id, nlmsg); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5956,303 +6082,139 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui if (!nlmsg) g_return_val_if_reached (FALSE); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); return do_delete_object (platform, &obj_id, nlmsg); } -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_address; - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_address; - return NULL; -} - /*****************************************************************************/ -static GArray * -ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) +static NMPlatformError +ip_route_add (NMPlatform *platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformIPRoute *const* routes; - GArray *array; - const NMPClass *klass; - gboolean with_rtprot_kernel; - guint i, len; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - klass = nmp_class_from_type (obj_type); - - nmp_cache_id_init_routes_visible (&cache_id, - obj_type, - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), - ifindex); - - routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - - with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj; - if ( with_rtprot_kernel - || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - g_array_append_vals (array, routes[i], 1); + switch (addr_family) { + case AF_INET: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + break; + case AF_INET6: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + break; + default: + nm_assert_not_reached (); } - return array; -} -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ROUTE, flags); -} + nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); -} - -static guint32 -ip_route_get_lock_flag (NMPlatformIPRoute *route) -{ - return (((guint32) route->lock_window) << RTAX_WINDOW) - | (((guint32) route->lock_cwnd) << RTAX_CWND) - | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) - | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); -} - -static gboolean -ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - in_addr_t network; - - network = nm_utils_ip4_address_clear_host_address (route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET, - route->ifindex, - route->rt_source, - route->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - route->pref_src ? &route->pref_src : NULL, - NULL, - 0, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip4_route (&obj_id, route->ifindex, network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + return do_add_addrroute (platform, + &obj, + nlmsg, + NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean -ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) +ip_route_delete (NMPlatform *platform, + const NMPObject *obj) { - NMPObject obj_id; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct in6_addr network; - - nm_utils_ip6_address_clear_host_address (&network, &route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET6, - route->ifindex, - route->rt_source, - IN6_IS_ADDR_UNSPECIFIED (&route->gateway) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - !IN6_IS_ADDR_UNSPECIFIED (&route->pref_src) ? &route->pref_src : NULL, - !IN6_IS_ADDR_UNSPECIFIED (&route->src) ? &route->src : NULL, - route->src_plen, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip6_route (&obj_id, route->ifindex, &network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); -} -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - network = nm_utils_ip4_address_clear_host_address (network, plen); + if (!NMP_OBJECT_IS_STACKINIT (obj)) + obj_keep_alive = nmp_object_ref (obj); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); + if (!nlmsg) + g_return_val_if_reached (FALSE); + return do_delete_object (platform, obj, nlmsg); +} - if (metric == 0) { - /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. - * If no route with metric 0 exists, it might delete another route to the same destination. - * For nm_platform_ip4_route_delete() we don't want this semantic. - * - * Instead, make sure that we have the most recent state and process all - * delayed actions (including re-reading data from netlink). */ - delayed_action_handle_all (platform, TRUE); - - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { - /* hmm... we are about to delete an IP4 route with metric 0. We must only - * send the delete request if such a route really exists. Above we refreshed - * the platform cache, still no such route exists. - * - * Be extra careful and reload the routes. We must be sure that such a - * route doesn't exists, because when we add an IPv4 address, we immediately - * afterwards try to delete the kernel-added device route with metric 0. - * It might be, that we didn't yet get the notification about that route. - * - * FIXME: once our ip4_address_add() is sure that upon return we have - * the latest state from in the platform cache, we might save this - * additional expensive cache-resync. */ - do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE); +/*****************************************************************************/ - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) - return TRUE; - } - } +static NMPlatformError +ip_route_get (NMPlatform *platform, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route) +{ + const gboolean is_v4 = (addr_family == AF_INET); + const int addr_len = is_v4 ? 4 : 16; + int try_count = 0; + WaitForNlResponseResult seq_result; + int nle; + nm_auto_nlmsg NMPObject *route = NULL; - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + nm_assert (NM_IS_LINUX_PLATFORM (platform)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (address); + + do { + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = addr_family, + .r.rtm_tos = 0, + .r.rtm_dst_len = is_v4 ? 32 : 128, + .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, + }; - return do_delete_object (platform, &obj_id, nlmsg); -} + g_clear_pointer (&route, nmp_object_unref); -static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_DST, address, addr_len)) + nm_assert_not_reached (); - metric = nm_utils_ip6_route_metric_normalize (metric); - - nm_utils_ip6_address_clear_host_address (&network, &network, plen); - - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET6, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + if (oif_ifindex > 0) { + gint32 ii = oif_ifindex; - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_OIF, &ii, sizeof (ii))) + nm_assert_not_reached (); + } - return do_delete_object (platform, &obj_id, nlmsg); -} + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); + if (nle < 0) { + _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", + g_strerror (-nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + delayed_action_handle_all (platform, FALSE); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_route; - return NULL; -} + /* Retry, if we failed due to a cache resync. That can happen when the netlink + * socket fills up and we lost the response. */ + } while ( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC + && ++try_count < 10); -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + if (seq_result < 0) { + /* negative seq_result is an errno from kernel. Map it to negative + * NMPlatformError (which are also errno). */ + return (NMPlatformError) seq_result; + } - metric = nm_utils_ip6_route_metric_normalize (metric); + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + if (route) { + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return NM_PLATFORM_ERROR_SUCCESS; + } + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + } - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_route; - return NULL; + return NM_PLATFORM_ERROR_UNSPECIFIED; } /*****************************************************************************/ @@ -6351,6 +6313,7 @@ continue_reading: gboolean abort_parsing = FALSE; gboolean process_valid_msg = FALSE; guint32 seq_number; + char buf_nlmsghdr[400]; msg = nlmsg_convert (hdr); if (!msg) { @@ -6370,8 +6333,8 @@ continue_reading: goto stop; } - _LOGt ("netlink: recvmsg: new message type %d, seq %u", - hdr->nlmsg_type, hdr->nlmsg_seq); + _LOGt ("netlink: recvmsg: new message %s", + _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); if (creds) nlmsg_set_creds (msg, creds); @@ -6512,7 +6475,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) nle = event_handler_recvmsgs (platform, TRUE); - if (nle < 0) + if (nle < 0) { switch (nle) { case -NLE_AGAIN: goto after_read; @@ -6543,6 +6506,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) default: _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; + } } any = TRUE; } @@ -6615,19 +6579,19 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, struct udev_device *udevice) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform); + cache_op = nmp_cache_update_link_udev (nm_platform_get_cache (platform), ifindex, udevice, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { nm_auto_pop_netns NMPNetns *netns = NULL; + cache_on_change (platform, cache_op, obj_old, obj_new); if (!nm_platform_netns_push (platform, &netns)) return; - do_emit_signal (platform, obj_cache, cache_op, was_visible); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } } @@ -6683,7 +6647,7 @@ udev_device_removed (NMPlatform *platform, if (ifindex <= 0) { const NMPObject *obj; - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice); if (obj) ifindex = obj->link.ifindex; @@ -6735,22 +6699,12 @@ static void nm_linux_platform_init (NMLinuxPlatform *self) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); - gboolean use_udev; - - use_udev = nmp_netns_is_initial () - && access ("/sys", W_OK) == 0; priv->nlh_seq_next = 1; - priv->cache = nmp_cache_new (use_udev); priv->delayed_action.list_master_connected = g_ptr_array_new (); priv->delayed_action.list_refresh_link = g_ptr_array_new (); priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - - if (use_udev) { - priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, - handle_udev_event, self); - } } static void @@ -6764,6 +6718,11 @@ constructed (GObject *_object) nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ()); + if (nm_platform_get_use_udev (platform)) { + priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, + handle_udev_event, platform); + } + _LOGD ("create (%s netns, %s, %s udev)", !platform->_netns ? "ignore" : "use", !platform->_netns && nmp_netns_is_initial () @@ -6773,7 +6732,7 @@ constructed (GObject *_object) : nm_sprintf_bufa (100, "in netns[%p]%s", nmp_netns_get_current (), nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")), - nmp_cache_use_udev_get (priv->cache) ? "use" : "no"); + nm_platform_get_use_udev (platform) ? "use" : "no"); priv->nlh = nl_socket_alloc (); g_assert (priv->nlh); @@ -6874,8 +6833,6 @@ dispose (GObject *object) g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -6884,8 +6841,6 @@ finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); - nmp_cache_free (priv->cache); - g_ptr_array_unref (priv->delayed_action.list_master_connected); g_ptr_array_unref (priv->delayed_action.list_refresh_link); g_array_unref (priv->delayed_action.list_wait_for_nl_response); @@ -6919,16 +6874,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - platform_class->link_get_unmanaged = link_get_unmanaged; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_refresh = link_refresh; @@ -6940,7 +6887,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_noarp = link_set_noarp; platform_class->link_get_udi = link_get_udi; - platform_class->link_get_udev_device = link_get_udev_device; platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; @@ -6948,6 +6894,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_address = link_set_address; platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; + platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; platform_class->link_get_physical_port_id = link_get_physical_port_id; @@ -6995,26 +6942,16 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; - platform_class->ip4_route_add = ip4_route_add; - platform_class->ip6_route_add = ip6_route_add; - platform_class->ip4_route_delete = ip4_route_delete; - platform_class->ip6_route_delete = ip6_route_delete; - - platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; - platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_delete = ip_route_delete; + platform_class->ip_route_get = ip_route_get; + + platform_class->check_kernel_support = check_kernel_support; platform_class->process_events = process_events; } diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6b66ea69..bff6c00c 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -39,10 +39,4 @@ NMPlatform *nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support void nm_linux_platform_setup (void); -struct _NMPCacheId; - -const NMPlatformObject *const *nm_linux_platform_lookup (NMPlatform *platform, - const struct _NMPCacheId *cache_id, - guint *out_len); - #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h new file mode 100644 index 00000000..b6c94baa --- /dev/null +++ b/src/platform/nm-platform-private.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_PRIVATE_H__ +#define __NM_PLATFORM_PRIVATE_H__ + +#include "nm-platform.h" +#include "nmp-object.h" + +NMPCache *nm_platform_get_cache (NMPlatform *self); + +#define NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform) \ + G_STMT_START { \ + NMPlatform *_platform = (platform); \ + \ + nm_assert (NM_IS_PLATFORM (_platform)); \ + nm_assert (NM_IN_SET (nm_platform_netns_get (_platform), NULL, nmp_netns_get_current ())); \ + } G_STMT_END + +void nm_platform_cache_update_emit_signal (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); + +#endif /* __NM_PLATFORM_PRIVATE_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index a244ff39..ffc4b395 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -32,12 +32,16 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> +#include <libudev.h> #include "nm-utils.h" #include "nm-core-internal.h" +#include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-udev-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nmp-netns.h" @@ -79,12 +83,23 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 }; enum { PROP_0, PROP_NETNS_SUPPORT, + PROP_USE_UDEV, PROP_LOG_WITH_PTR, LAST_PROP, }; typedef struct _NMPlatformPrivate { + bool use_udev:1; bool log_with_ptr:1; + + NMPlatformKernelSupportFlags support_checked; + NMPlatformKernelSupportFlags support_present; + + guint ip4_dev_route_blacklist_check_id; + guint ip4_dev_route_blacklist_gc_timeout_id; + GHashTable *ip4_dev_route_blacklist_hash; + NMDedupMultiIndex *multi_idx; + NMPCache *cache; } NMPlatformPrivate; G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) @@ -93,6 +108,16 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) /*****************************************************************************/ +static void _ip4_dev_route_blacklist_schedule (NMPlatform *self); + +/*****************************************************************************/ + +gboolean +nm_platform_get_use_udev (NMPlatform *self) +{ + return NM_PLATFORM_GET_PRIVATE (self)->use_udev; +} + gboolean nm_platform_get_log_with_ptr (NMPlatform *self) { @@ -193,17 +218,18 @@ nm_platform_get () /*****************************************************************************/ -/** - * _nm_platform_error_to_string: - * @error_code: the error code to stringify. - * - * Returns: A string representation of the error. - * For negative numbers, this function interprets - * the code as -errno. - * For invalid (positive) numbers it returns NULL. - */ -NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, - NM_UTILS_LOOKUP_DEFAULT ( val < 0 ? g_strerror (- ((int) val)) : NULL ), +NMDedupMultiIndex * +nm_platform_get_multi_idx (NMPlatform *self) +{ + g_return_val_if_fail (NM_IS_PLATFORM (self), NULL); + + return NM_PLATFORM_GET_PRIVATE (self)->multi_idx; +} + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nm_platform_error_to_string, NMPlatformError, + NM_UTILS_LOOKUP_DEFAULT (NULL), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_SUCCESS, "success"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_BUG, "bug"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_UNSPECIFIED, "unspecified"), @@ -213,35 +239,101 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE, "not-slave"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NETLINK, "netlink"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_CANT_SET_MTU, "cant-set-mtu"), NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT), ); -/*****************************************************************************/ - -gboolean -nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) +/** + * nm_platform_error_to_string: + * @error_code: the error code to stringify. + * @buf: (allow-none): buffer + * @buf_len: size of buffer + * + * Returns: A string representation of the error. + * For negative numbers, this function interprets + * the code as -errno. + * For invalid (positive) numbers it returns NULL. + */ +const char * +nm_platform_error_to_string (NMPlatformError error_code, char *buf, gsize buf_len) { - _CHECK_SELF (self, klass, FALSE); + const char *s; - if (!klass->check_support_kernel_extended_ifa_flags) - return FALSE; + if (error_code < 0) { + int errsv = -((int) error_code); - return klass->check_support_kernel_extended_ifa_flags (self); + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "%s (%d)", g_strerror (errsv), errsv); + } else { + s = _nm_platform_error_to_string (error_code); + if (s) { + if (!buf) + return s; + g_strlcpy (buf, s, buf_len); + } else { + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "(%d)", (int) error_code); + } + } + + return buf; } -gboolean -nm_platform_check_support_user_ipv6ll (NMPlatform *self) +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, + NM_UTILS_LOOKUP_DEFAULT (NULL), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_FMASK), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE), +); + +#define _nmp_nlm_flag_to_string(flags) \ + ({ \ + NMPNlmFlags _flags = (flags); \ + \ + _nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \ + }) + +/*****************************************************************************/ + +NMPlatformKernelSupportFlags +nm_platform_check_kernel_support (NMPlatform *self, + NMPlatformKernelSupportFlags request_flags) { - static int supported = -1; + NMPlatformPrivate *priv; - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, TRUE); - if (!klass->check_support_user_ipv6ll) - return FALSE; + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* we cache the response from subclasses and only request it once. + * This probably gives better performance, but more importantly, + * we are guaranteed that the answer for a certain request_flag + * is always the same. */ + if (G_UNLIKELY (!NM_FLAGS_ALL (priv->support_checked, request_flags))) { + NMPlatformKernelSupportFlags checked, response; - if (supported < 0) - supported = klass->check_support_user_ipv6ll (self) ? 1 : 0; - return !!supported; + checked = request_flags & ~priv->support_checked; + nm_assert (checked); + + if (klass->check_kernel_support) + response = klass->check_kernel_support (self, checked); + else { + /* fake platform. Pretend no support for anything. */ + response = 0; + } + + priv->support_checked |= checked; + priv->support_present = (priv->support_present & ~checked) | (response & checked); + } + + return priv->support_present & request_flags; } /** @@ -322,6 +414,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, { const char *path; gint64 cur; + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; _CHECK_SELF (self, klass, FALSE); @@ -333,7 +426,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, if (value < 10) return FALSE; - path = nm_utils_ip6_property_path (iface, "hop_limit"); + path = nm_utils_sysctl_ip_conf_path (AF_INET6, buf, iface, "hop_limit"); cur = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 1, G_MAXINT32, -1); /* only allow increasing the hop-limit to avoid DOS by an attacker @@ -440,8 +533,8 @@ _link_get_all_presort (gconstpointer p_a, gconstpointer p_b, gpointer sort_by_name) { - const NMPlatformLink *a = p_a; - const NMPlatformLink *b = p_b; + const NMPlatformLink *a = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_a)); + const NMPlatformLink *b = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_b)); /* Loopback always first */ if (a->ifindex == 1) @@ -463,43 +556,56 @@ _link_get_all_presort (gconstpointer p_a, /** * nm_platform_link_get_all: - * self: platform instance + * @self: platform instance + * @sort_by_name: whether to sort by name or ifindex. * * Retrieve a snapshot of configuration for all links at once. The result is - * owned by the caller and should be freed with g_array_unref(). + * owned by the caller and should be freed with g_ptr_array_unref(). */ -GArray * +GPtrArray * nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) { - GArray *links, *result; - guint i, j, nresult; - GHashTable *unseen; - NMPlatformLink *item; + gs_unref_ptrarray GPtrArray *links = NULL; + GPtrArray *result; + guint i, nresult; + gs_unref_hashtable GHashTable *unseen = NULL; + const NMPlatformLink *item; + NMPLookup lookup; _CHECK_SELF (self, klass, NULL); - links = klass->link_get_all (self); + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + links = nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, &lookup), + NULL, NULL); + if (!links) + return NULL; + + for (i = 0; i < links->len; ) { + if (!nmp_object_is_visible (links->pdata[i])) + g_ptr_array_remove_index_fast (links, i); + else + i++; + } - if (!links || links->len == 0) - return links; + if (links->len == 0) + return NULL; /* first sort the links by their ifindex or name. Below we will sort * further by moving children/slaves to the end. */ - g_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); + g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); unseen = g_hash_table_new (g_direct_hash, g_direct_equal); for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); - + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); nm_assert (item->ifindex > 0); if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL)) nm_assert_not_reached (); } -#ifndef G_DISABLE_ASSERT +#if NM_MORE_ASSERTS /* Ensure that link_get_all returns a consistent and valid result. */ for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); if (!item->ifindex) continue; @@ -519,54 +625,75 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) #endif /* Re-order the links list such that children/slaves come after all ancestors */ - nresult = g_hash_table_size (unseen); - result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult); - g_array_set_size (result, nresult); + nm_assert (g_hash_table_size (unseen) == links->len); + nresult = links->len; + result = g_ptr_array_new_full (nresult, (GDestroyNotify) nmp_object_unref); - j = 0; - do { + while (TRUE) { gboolean found_something = FALSE; guint first_idx = G_MAXUINT; for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); - if (!item->ifindex) + if (!item) continue; - if (first_idx == G_MAXUINT) - first_idx = i; - g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master))) - continue; + goto skip; if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) - continue; + goto skip; g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[i]); + links->pdata[i] = NULL; found_something = TRUE; + continue; +skip: + if (first_idx == G_MAXUINT) + first_idx = i; } - if (!found_something) { + if (found_something) { + if (first_idx == G_MAXUINT) + break; + } else { + nm_assert (first_idx != G_MAXUINT); /* There is a loop, pop the first (remaining) element from the list. * This can happen for veth pairs where each peer is parent of the other end. */ - item = &g_array_index (links, NMPlatformLink, first_idx); - + item = NMP_OBJECT_CAST_LINK (links->pdata[first_idx]); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[first_idx]); + links->pdata[first_idx] = NULL; } - } while (j < nresult); - - g_hash_table_destroy (unseen); - g_array_free (links, TRUE); + nm_assert (result->len < nresult); + } + nm_assert (result->len == nresult); return result; } +/*****************************************************************************/ + +const NMPObject * +nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only) +{ + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if ( !obj_cache + || ( visible_only + && !nmp_object_is_visible (obj_cache))) + return NULL; + return obj_cache; +} + +/*****************************************************************************/ + /** * nm_platform_link_get: * @self: platform instance @@ -577,16 +704,20 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) * Returns: %NULL, if such a link exists or the internal * platform link object. Do not modify the returned value. * Also, be aware that any subsequent platform call might - * invalidated/modify the returned instance. + * invalidate/modify the returned instance. **/ const NMPlatformLink * nm_platform_link_get (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifindex > 0) - return klass->link_get (self, ifindex); - return NULL; + if (ifindex <= 0) + return NULL; + + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + return NMP_OBJECT_CAST_LINK (obj); } /** @@ -599,11 +730,27 @@ nm_platform_link_get (NMPlatform *self, int ifindex) const NMPlatformLink * nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifname && *ifname) - return klass->link_get_by_ifname (self, ifname); - return NULL; + if (!ifname || !*ifname) + return NULL; + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + return NMP_OBJECT_CAST_LINK (obj); +} + +struct _nm_platform_link_get_by_address_data { + gconstpointer address; + guint8 length; +}; + +static gboolean +_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) +{ + return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); } /** @@ -620,15 +767,26 @@ nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length) { + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (length == 0 || address, NULL); - if (length > 0) { - if (length > NM_UTILS_HWADDR_LEN_MAX) - g_return_val_if_reached (NULL); - return klass->link_get_by_address (self, address, length); - } - return NULL; + if (length == 0) + return NULL; + + if (length > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (NULL); + if (!address) + g_return_val_if_reached (NULL); + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, NULL, TRUE, NM_LINK_TYPE_NONE, + (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); + return NMP_OBJECT_CAST_LINK (obj); } static NMPlatformError @@ -662,6 +820,7 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, c * @self: platform instance * @name: Interface name * @type: Interface type + * @veth_peer: For veths, the peer name * @address: (allow-none): set the mac address of the link * @address_len: the length of the @address * @out_link: on success, the link object @@ -680,27 +839,51 @@ static NMPlatformError nm_platform_link_add (NMPlatform *self, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) { NMPlatformError plerr; + char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ((address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (address_len <= NM_UTILS_HWADDR_LEN_MAX, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ((!!veth_peer) == (type == NM_LINK_TYPE_VETH), NM_PLATFORM_ERROR_BUG); plerr = _link_add_check_existing (self, name, type, out_link); if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding %s '%s'", nm_link_type_to_string (type), name); - if (!klass->link_add (self, name, type, address, address_len, out_link)) + _LOGD ("link: adding link '%s' of type '%s' (%d)" + "%s%s" /* address */ + "%s%s" /* veth peer */ + "", + name, + nm_link_type_to_string (type), + (int) type, + address ? ", address: " : "", + address ? nm_utils_hwaddr_ntoa_buf (address, address_len, FALSE, addr_buf, sizeof (addr_buf)) : "", + veth_peer ? ", veth-peer: " : "", + veth_peer ?: ""); + + if (!klass->link_add (self, name, type, veth_peer, address, address_len, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; } +NMPlatformError +nm_platform_link_veth_add (NMPlatform *self, + const char *name, + const char *peer, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add (self, name, NM_LINK_TYPE_VETH, peer, NULL, 0, out_link); +} + /** * nm_platform_link_dummy_add: * @self: platform instance @@ -714,7 +897,7 @@ nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, NULL, 0, out_link); } /** @@ -846,9 +1029,26 @@ nm_platform_link_get_type (NMPlatform *self, int ifindex) const char * nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - return klass->link_get_type_name (self, ifindex); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + + if (!obj) + return NULL; + + if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { + /* We could detect the @link_type. In this case the function returns + * our internel module names, which differs from rtnl_link_get_type(): + * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") + * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). + * Note that this functions is only used by NMDeviceGeneric to + * set type_description. */ + return nm_link_type_to_string (obj->link.type); + } + /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ + return obj->link.kind ?: "unknown"; } /** @@ -863,11 +1063,26 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) { + const NMPObject *link; + struct udev_device *udevice = NULL; + const char *uproperty; + _CHECK_SELF (self, klass, FALSE); - if (klass->link_get_unmanaged) - return klass->link_get_unmanaged (self, ifindex, unmanaged); - return FALSE; + link = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if (!link) + return FALSE; + + udevice = link->_link.udev.device; + if (!udevice) + return FALSE; + + uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); + if (!uproperty) + return FALSE; + + *unmanaged = nm_udev_utils_property_as_boolean (uproperty); + return TRUE; } /** @@ -1013,13 +1228,14 @@ nm_platform_link_get_udi (NMPlatform *self, int ifindex) struct udev_device * nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) { + const NMPObject *obj_cache; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, NULL); - if (klass->link_get_udev_device) - return klass->link_get_udev_device (self, ifindex); - return NULL; + obj_cache = nm_platform_link_get_obj (self, ifindex, FALSE); + return obj_cache ? obj_cache->_link.udev.device : NULL; } /** @@ -1284,7 +1500,7 @@ nm_platform_link_set_noarp (NMPlatform *self, int ifindex) * * Set interface MTU. */ -gboolean +NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { _CHECK_SELF (self, klass, FALSE); @@ -1315,6 +1531,30 @@ nm_platform_link_get_mtu (NMPlatform *self, int ifindex) } /** + * nm_platform_link_set_name: + * @self: platform instance + * @ifindex: Interface index + * @name: The new interface name + * + * Set interface name. + */ +gboolean +nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (name, FALSE); + + _LOGD ("link: setting '%s' (%d) name %s", nm_platform_link_get_name (self, ifindex), ifindex, name); + + if (strlen (name) + 1 > IFNAMSIZ) + return FALSE; + + return klass->link_set_name (self, ifindex, name); +} + +/** * nm_platform_link_get_physical_port_id: * @self: platform instance * @ifindex: Interface index @@ -1465,7 +1705,7 @@ nm_platform_link_release (NMPlatform *self, int master, int slave) * @self: platform instance * @slave: Interface index of the slave. * - * Returns: Interfase index of the slave's master. + * Returns: Interface index of the slave's master. */ int nm_platform_link_get_master (NMPlatform *self, int slave) @@ -1519,13 +1759,28 @@ nm_platform_link_can_assume (NMPlatform *self, int ifindex) const NMPObject * nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) { + const NMPObject *obj; + _CHECK_SELF (self, klass, FALSE); NM_SET_OUT (out_link, NULL); g_return_val_if_fail (ifindex > 0, NULL); - return klass->link_get_lnk (self, ifindex, link_type, out_link); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + if (!obj) + return NULL; + + NM_SET_OUT (out_link, &obj->link); + + if (!obj->_link.netlink.lnk) + return NULL; + if ( link_type != NM_LINK_TYPE_NONE + && ( link_type != obj->link.type + || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) + return NULL; + + return obj->_link.netlink.lnk; } static gconstpointer @@ -1616,7 +1871,7 @@ nm_platform_link_bridge_add (NMPlatform *self, size_t address_len, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, NULL, address, address_len, out_link); } /** @@ -1632,7 +1887,7 @@ nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, NULL, 0, out_link); } /** @@ -1648,7 +1903,7 @@ nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) { - return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); + return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, NULL, 0, out_link); } /** @@ -2617,6 +2872,82 @@ nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean * /*****************************************************************************/ +const NMDedupMultiHeadEntry * +nm_platform_lookup_all (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nmp_cache_lookup_all (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + +const NMDedupMultiEntry * +nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + +const NMDedupMultiHeadEntry * +nm_platform_lookup (NMPlatform *self, + const NMPLookup *lookup) +{ + return nmp_cache_lookup (nm_platform_get_cache (self), + lookup); +} + +gboolean +nm_platform_lookup_predicate_routes_main (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return nm_platform_route_table_is_main (obj->ip_route.table_coerced); +} + +gboolean +nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return nm_platform_route_table_is_main (obj->ip_route.table_coerced) + && obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; +} + +/** + * nm_platform_lookup_clone: + * @self: + * @lookup: + * @predicate: if given, only objects for which @predicate returns %TRUE are included + * in the result. + * @user_data: user data for @predicate + * + * Returns the result of lookup in a GPtrArray. The result array contains + * references objects from the cache, it's destroy function will unref them. + * + * The user must unref the GPtrArray, which will also unref the NMPObject + * elements. + * + * The elements in the array *must* not be modified. + * + * Returns: the result of the lookup. + */ +GPtrArray * +nm_platform_lookup_clone (NMPlatform *self, + const NMPLookup *lookup, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + return nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, lookup), + (NMDedupMultiFcnSelectPredicate) predicate, + user_data); +} + void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) { @@ -2636,26 +2967,6 @@ nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) return &addr->peer_address; } -GArray * -nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - - return klass->ip4_address_get_all (self, ifindex); -} - -GArray * -nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - - return klass->ip6_address_get_all (self, ifindex); -} - gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, @@ -2768,54 +3079,41 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a const NMPlatformIP4Address * nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, guint32 peer_address) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (plen <= 32, NULL); - return klass->ip4_address_get (self, ifindex, address, plen, peer_address); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, address, plen, peer_address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP4_ADDRESS (obj); } const NMPlatformIP6Address * -nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) +nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address) { - _CHECK_SELF (self, klass, NULL); + NMPObject obj_id; + const NMPObject *obj; - g_return_val_if_fail (plen <= 128, NULL); - - return klass->ip6_address_get (self, ifindex, address, plen); -} - -static const NMPlatformIP4Address * -array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint32 now) -{ - guint len = addresses ? addresses->len : 0; - guint i; - - for (i = 0; i < len; i++) { - const NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i); - - if ( candidate->address == address->address - && candidate->plen == address->plen - && ((candidate->peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) { - guint32 lifetime, preferred; - - if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, - now, &lifetime, &preferred)) - return candidate; - } - } + _CHECK_SELF (self, klass, NULL); - return NULL; + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP6_ADDRESS (obj); } static gboolean -array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint32 now) +array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now) { guint len = addresses ? addresses->len : 0; guint i; for (i = 0; i < len; i++) { - NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i); + NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]); if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { guint32 lifetime, preferred; @@ -2830,69 +3128,100 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address } static gboolean -_ptr_inside_ip4_addr_array (const GArray *array, gconstpointer needle) +ip4_addr_subnets_is_plain_address (const GPtrArray *addresses, gconstpointer needle) { - return needle >= (gconstpointer) &g_array_index (array, const NMPlatformIP4Address, 0) - && needle < (gconstpointer) &g_array_index (array, const NMPlatformIP4Address, array->len); + return needle >= (gconstpointer) &addresses->pdata[0] + && needle < (gconstpointer) &addresses->pdata[addresses->len]; +} + +static const NMPObject ** +ip4_addr_subnets_addr_list_get (const GPtrArray *addr_list, guint idx) +{ + nm_assert (addr_list); + nm_assert (addr_list->len > 1); + nm_assert (idx < addr_list->len); + nm_assert (addr_list->pdata[idx]); + nm_assert ( !(*((gpointer *) addr_list->pdata[idx])) + || NMP_OBJECT_CAST_IP4_ADDRESS (*((gpointer *) addr_list->pdata[idx]))); + nm_assert (idx == 0 || ip4_addr_subnets_addr_list_get (addr_list, idx - 1)); + return addr_list->pdata[idx]; } static void -ip4_addr_subnets_destroy_index (GHashTable *ht, const GArray *addresses) +ip4_addr_subnets_destroy_index (GHashTable *subnets, const GPtrArray *addresses) { GHashTableIter iter; gpointer p; - g_hash_table_iter_init (&iter, ht); + if (!subnets) + return; + g_hash_table_iter_init (&iter, subnets); while (g_hash_table_iter_next (&iter, NULL, &p)) { - if (!_ptr_inside_ip4_addr_array (addresses, p)) { + if (!ip4_addr_subnets_is_plain_address (addresses, p)) g_ptr_array_free ((GPtrArray *) p, TRUE); - } } - g_hash_table_unref (ht); + g_hash_table_unref (subnets); } static GHashTable * -ip4_addr_subnets_build_index (const GArray *addresses, gboolean consider_flags) +ip4_addr_subnets_build_index (const GPtrArray *addresses, + gboolean consider_flags, + gboolean full_index) { - const NMPlatformIP4Address *address; - gpointer p; GHashTable *subnets; - GPtrArray *ptr; - guint32 net; guint i; - gint position; - if (!addresses) - return NULL; + nm_assert (addresses && addresses->len); - subnets = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - NULL); + subnets = g_hash_table_new (NULL, NULL); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, const NMPlatformIP4Address, i); - net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); - if (!g_hash_table_lookup_extended (subnets, GUINT_TO_POINTER (net), NULL, &p)) { - g_hash_table_insert (subnets, GUINT_TO_POINTER (net), (gpointer) address); + const NMPlatformIP4Address *address; + gpointer p_address; + GPtrArray *addr_list; + guint32 net; + int position; + gpointer p; + + if (!addresses->pdata[i]) continue; - } - if (_ptr_inside_ip4_addr_array (addresses, p)) { - ptr = g_ptr_array_new (); - g_hash_table_insert (subnets, GUINT_TO_POINTER (net), ptr); - g_ptr_array_add (ptr, p); - } else - ptr = p; - if (!consider_flags || NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) - position = -1; /* append */ - else - position = 0; /* prepend */ + p_address = &addresses->pdata[i]; + address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses->pdata[i]); - g_ptr_array_insert (ptr, position, (gpointer) address); + net = address->address & _nm_utils_ip4_prefix_to_netmask (address->plen); + if (!g_hash_table_lookup_extended (subnets, GUINT_TO_POINTER (net), NULL, &p)) { + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address); + continue; + } + nm_assert (p); + + if (full_index) { + if (ip4_addr_subnets_is_plain_address (addresses, p)) { + addr_list = g_ptr_array_new (); + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), addr_list); + g_ptr_array_add (addr_list, p); + } else + addr_list = p; + + if ( !consider_flags + || NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) + position = -1; /* append */ + else + position = 0; /* prepend */ + g_ptr_array_insert (addr_list, position, p_address); + } else { + /* we only care about the primary. No need to track the secondaries + * as a GPtrArray. */ + nm_assert (ip4_addr_subnets_is_plain_address (addresses, p)); + if ( consider_flags + && !NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) { + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address); + } + } } return subnets; @@ -2911,23 +3240,33 @@ ip4_addr_subnets_build_index (const GArray *addresses, gboolean consider_flags) * Returns: %TRUE if the address is secondary, %FALSE otherwise */ static gboolean -ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable *subnets, const GArray *addresses, GPtrArray **out_addr_list) -{ - GPtrArray *addr_list; - gpointer p; +ip4_addr_subnets_is_secondary (const NMPObject *address, + GHashTable *subnets, + const GPtrArray *addresses, + const GPtrArray **out_addr_list) +{ + const NMPlatformIP4Address *a; + const GPtrArray *addr_list; + gconstpointer p; guint32 net; + const NMPObject **o; + + a = NMP_OBJECT_CAST_IP4_ADDRESS (address); - net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); + net = a->address & _nm_utils_ip4_prefix_to_netmask (a->plen); p = g_hash_table_lookup (subnets, GUINT_TO_POINTER (net)); nm_assert (p); - if (!_ptr_inside_ip4_addr_array (addresses, p)) { + if (!ip4_addr_subnets_is_plain_address (addresses, p)) { addr_list = p; + nm_assert (addr_list->len > 1); NM_SET_OUT (out_addr_list, addr_list); - if (addr_list->pdata[0] != address) + o = ip4_addr_subnets_addr_list_get (addr_list, 0); + nm_assert (o && *o); + if (*o != address) return TRUE; } else { - nm_assert ((gconstpointer) address == p); NM_SET_OUT (out_addr_list, NULL); + return address != *((gconstpointer *) p); } return FALSE; } @@ -2936,11 +3275,14 @@ ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable * * nm_platform_ip4_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of addresses - * @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray - * with the addresses added. The pointers point into @known_addresses. - * It possibly does not contain all addresses from @known_address because - * some addresses might be expired. + * @known_addresses: List of addresses. The list will be modified and only + * addresses that were successfully added will be kept in the list. + * That means, expired addresses and addresses that could not be added + * will be dropped. + * Hence, the input argument @known_addresses is also an output argument + * telling which addresses were succesfully added. + * Addresses are removed by unrefing the instance via nmp_object_unref() + * and leaving a NULL tombstone. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -2949,102 +3291,168 @@ ip4_addr_subnets_is_secondary (const NMPlatformIP4Address *address, GHashTable * * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses) +nm_platform_ip4_address_sync (NMPlatform *self, + int ifindex, + GPtrArray *known_addresses) { - GArray *addresses; - NMPlatformIP4Address *address; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; const NMPlatformIP4Address *known_address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - GHashTable *plat_subnets; - GHashTable *known_subnets; - GPtrArray *ptr; - int i, j; + GHashTable *plat_subnets = NULL; + GHashTable *known_subnets = NULL; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + guint i, j, len; + NMPLookup lookup; + guint32 lifetime, preferred; + guint32 ifa_flags; _CHECK_SELF (self, klass, FALSE); - addresses = nm_platform_ip4_address_get_all (self, ifindex); - plat_subnets = ip4_addr_subnets_build_index (addresses, TRUE); - known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE); + if (known_addresses) { + /* remove all addresses that are already expired. */ + for (i = 0; i < known_addresses->len; i++) { + const NMPObject *o; + + o = known_addresses->pdata[i]; + nm_assert (o); + + known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]); + + if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, &lifetime, &preferred)) + goto delete_and_next; + + if (G_UNLIKELY (!known_addresses_idx)) { + known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + if (!nm_g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) { + /* duplicate? Keep only the first instance. */ + goto delete_and_next; + } + + continue; +delete_and_next: + nmp_object_unref (o); + known_addresses->pdata[i] = NULL; + } + + if ( !known_addresses_idx + || g_hash_table_size (known_addresses_idx) == 0) + known_addresses = NULL; + } + + plat_addresses = nm_platform_lookup_clone (self, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex), + NULL, NULL); + if (plat_addresses) + plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE); /* Delete unknown addresses */ - for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, NMPlatformIP4Address, i); + len = plat_addresses ? plat_addresses->len : 0; + for (i = 0; i < len; i++) { + const NMPObject *plat_obj; + const NMPlatformIP4Address *plat_address; + const GPtrArray *addr_list; - if (!address->ifindex) { + plat_obj = plat_addresses->pdata[i]; + if (!plat_obj) { /* Already deleted */ continue; } - known_address = array_contains_ip4_address (known_addresses, address, now); - if (known_address) { - gboolean secondary; + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS (plat_obj); + + if (known_addresses) { + const NMPObject *o; + + o = g_hash_table_lookup (known_addresses_idx, plat_obj); + if (o) { + gboolean secondary; + + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE); - secondary = ip4_addr_subnets_is_secondary (known_address, known_subnets, known_addresses, NULL); - /* Ignore the matching address if it has a different primary/slave - * role. */ - if (secondary != NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) - known_address = NULL; + secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; + } + } } - if (!known_address) { - nm_platform_ip4_address_delete (self, ifindex, - address->address, - address->plen, - address->peer_address); - if ( !ip4_addr_subnets_is_secondary (address, plat_subnets, addresses, &ptr) - && ptr) { - /* If we just deleted a primary addresses and there were - * secondary ones the kernel can do two things, depending on - * version and sysctl setting: delete also secondary addresses - * or promote a secondary to primary. Ensure that secondary - * addresses are deleted, so that we can start with a clean - * slate and add addresses in the right order. */ - for (j = 1; j < ptr->len; j++) { - address = ptr->pdata[j]; + nm_platform_ip4_address_delete (self, ifindex, + plat_address->address, + plat_address->plen, + plat_address->peer_address); + + if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list) + && addr_list) { + /* If we just deleted a primary addresses and there were + * secondary ones the kernel can do two things, depending on + * version and sysctl setting: delete also secondary addresses + * or promote a secondary to primary. Ensure that secondary + * addresses are deleted, so that we can start with a clean + * slate and add addresses in the right order. */ + for (j = 1; j < addr_list->len; j++) { + const NMPObject **o; + + o = ip4_addr_subnets_addr_list_get (addr_list, j); + nm_assert (o); + + if (*o) { + const NMPlatformIP4Address *a; + + a = NMP_OBJECT_CAST_IP4_ADDRESS (*o); nm_platform_ip4_address_delete (self, ifindex, - address->address, - address->plen, - address->peer_address); - address->ifindex = 0; + a->address, + a->plen, + a->peer_address); + nmp_object_unref (*o); + *o = NULL; } } } } - ip4_addr_subnets_destroy_index (plat_subnets, addresses); - g_array_free (addresses, TRUE); - - if (out_added_addresses) - *out_added_addresses = NULL; + ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses); + ip4_addr_subnets_destroy_index (known_subnets, known_addresses); if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { - guint32 lifetime, preferred; + const NMPObject *o; - known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); + o = known_addresses->pdata[i]; + if (!o) + continue; + + known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o); if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, now, &lifetime, &preferred)) - continue; + goto delete_and_next2; if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, lifetime, preferred, - 0, known_address->label)) { - ip4_addr_subnets_destroy_index (known_subnets, known_addresses); - return FALSE; - } - - if (out_added_addresses) { - if (!*out_added_addresses) - *out_added_addresses = g_ptr_array_new (); - g_ptr_array_add (*out_added_addresses, (gpointer) known_address); - } + ifa_flags, + known_address->label)) + goto delete_and_next2; + + continue; +delete_and_next2: + nmp_object_unref (o); + known_addresses->pdata[i] = NULL; } - ip4_addr_subnets_destroy_index (known_subnets, known_addresses); - return TRUE; } @@ -3052,7 +3460,8 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known * nm_platform_ip6_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of addresses + * @known_addresses: List of IPv6 addresses, as NMPObject. The list + * is not modified. * @keep_link_local: Don't remove link-local address * * A convenience function to synchronize addresses for a specific interface @@ -3062,33 +3471,47 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local) +nm_platform_ip6_address_sync (NMPlatform *self, + int ifindex, + const GPtrArray *known_addresses, + gboolean keep_link_local) { - GArray *addresses; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; NMPlatformIP6Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - int i; + guint i; + NMPLookup lookup; + guint32 ifa_flags; /* Delete unknown addresses */ - addresses = nm_platform_ip6_address_get_all (self, ifindex); - for (i = 0; i < addresses->len; i++) { - address = &g_array_index (addresses, NMPlatformIP6Address, i); - - /* Leave link local address management to the kernel */ - if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) - continue; + plat_addresses = nm_platform_lookup_clone (self, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex), + NULL, NULL); + if (plat_addresses) { + for (i = 0; i < plat_addresses->len; i++) { + address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + + /* Leave link local address management to the kernel */ + if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) + continue; - if (!array_contains_ip6_address (known_addresses, address, now)) - nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + if (!array_contains_ip6_address (known_addresses, address, now)) + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + } } - g_array_free (addresses, TRUE); if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_kernel_support (self, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { - const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i); + const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]); guint32 lifetime, preferred; if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_TEMPORARY)) { @@ -3102,7 +3525,8 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, - lifetime, preferred, known_address->n_ifa_flags)) + lifetime, preferred, + ifa_flags | known_address->n_ifa_flags)) return FALSE; } @@ -3110,125 +3534,797 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known } gboolean -nm_platform_address_flush (NMPlatform *self, int ifindex) +nm_platform_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex) { + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL) - && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) + success &= nm_platform_ip4_address_sync (self, ifindex, NULL); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) + success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + return success; } /*****************************************************************************/ -GArray * -nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) +static gboolean +_err_inval_due_to_ipv6_tentative_pref_src (NMPlatform *self, const NMPObject *obj) { - _CHECK_SELF (self, klass, NULL); + const NMPlatformIP6Route *r; + const NMPlatformIP6Address *a; - g_return_val_if_fail (ifindex >= 0, NULL); + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_IS_VALID (obj)); - return klass->ip4_route_get_all (self, ifindex, flags); + /* trying to add an IPv6 route with pref-src fails, if the address is + * still tentative (rh#1452684). We need to hack around that. + * + * Detect it, by guessing whether that's the case. */ + + if (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_IP6_ROUTE) + return FALSE; + + r = NMP_OBJECT_CAST_IP6_ROUTE (obj); + + /* we only allow this workaround for routes added manually by the user. */ + if (r->rt_source != NM_IP_CONFIG_SOURCE_USER) + return FALSE; + + if (IN6_IS_ADDR_UNSPECIFIED (&r->pref_src)) + return FALSE; + + a = nm_platform_ip6_address_get (self, r->ifindex, r->pref_src); + if (!a) + return FALSE; + if ( !NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_TENTATIVE) + || NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_DADFAILED)) + return FALSE; + + return TRUE; } -GArray * -nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) -{ - _CHECK_SELF (self, klass, NULL); +GPtrArray * +nm_platform_ip_route_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex, + NMIPRouteTableSyncMode route_table_sync) +{ + NMPLookup lookup; + GPtrArray *routes_prune; + const NMDedupMultiHeadEntry *head_entry; + CList *iter; + + nm_assert (NM_IS_PLATFORM (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)); + + nmp_lookup_init_addrroute (&lookup, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex); + head_entry = nm_platform_lookup (self, &lookup); + if (!head_entry) + return NULL; - g_return_val_if_fail (ifindex >= 0, NULL); + routes_prune = g_ptr_array_new_full (head_entry->len, + (GDestroyNotify) nm_dedup_multi_obj_unref); + + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMPObject *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; + + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_FULL) { + if (nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE) == RT_TABLE_LOCAL) + continue; + } else if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN) { + if (!nm_platform_route_table_is_main (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced)) + continue; + } else + nm_assert (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + + g_ptr_array_add (routes_prune, (gpointer) nmp_object_ref (obj)); + } - return klass->ip6_route_get_all (self, ifindex, flags); + if (routes_prune->len == 0) { + g_ptr_array_unref (routes_prune); + return NULL; + } + return routes_prune; } /** - * nm_platform_ip4_route_add: - * @self: - * @route: - * - * For kernel, a gateway can be either explicitly set or left - * at zero (0.0.0.0). In addition, there is the scope of the IPv4 - * route. - * When adding a route with - * $ ip route add default dev $IFNAME - * the resulting route will have gateway 0.0.0.0 and scope "link". - * Contrary to - * $ ip route add default via 0.0.0.0 dev $IFNAME - * which adds the route with scope "global". - * - * NetworkManager's Platform can currently only add on-link-routes with scope - * "link" (and gateway 0.0.0.0) or gateway-routes with scope "global" (and - * gateway not 0.0.0.0). + * nm_platform_ip_route_sync: + * @self: the #NMPlatform instance. + * @addr_family: AF_INET or AF_INET6. + * @ifindex: the @ifindex for which the routes are to be added. + * @routes: (allow-none): a list of routes to configure. Must contain + * NMPObject instances of routes, according to @addr_family. + * @routes_prune: (allow-none): the list of routes to delete. + * If platform has such a route configured, it will be deleted + * at the end of the operation. Note that if @routes contains + * the same route, then it will not be deleted. @routes overrules + * @routes_prune list. + * @out_temporary_not_available: (allow-none): (out): routes that could + * currently not be synced. The caller shall keep them and try later again. * - * It does not support adding globally scoped routes via 0.0.0.0. - * - * Returns: %TRUE in case of success. + * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route) -{ - _CHECK_SELF (self, klass, FALSE); +nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + GPtrArray *routes_prune, + GPtrArray **out_temporary_not_available) +{ + const NMPlatformVTableRoute *vt; + gs_unref_hashtable GHashTable *routes_idx = NULL; + const NMPObject *conf_o; + const NMDedupMultiEntry *plat_entry; + guint i; + int i_type; + gboolean success = TRUE; + char sbuf1[sizeof (_nm_utils_to_string_buffer)]; + char sbuf2[sizeof (_nm_utils_to_string_buffer)]; + char sbuf_err[60]; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (ifindex > 0); + + vt = addr_family == AF_INET + ? &nm_platform_vtable_route_v4 + : &nm_platform_vtable_route_v6; + + for (i_type = 0; routes && i_type < 2; i_type++) { + for (i = 0; i < routes->len; i++) { + NMPlatformError plerr; + + conf_o = routes->pdata[i]; + +#define VTABLE_IS_DEVICE_ROUTE(vt, o) (vt->is_ip4 \ + ? (NMP_OBJECT_CAST_IP4_ROUTE (o)->gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (o)->gateway) ) + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vt, conf_o)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vt, conf_o))) { + /* we add routes in two runs over @i_type. + * + * First device routes, then gateway routes. */ + continue; + } + + if (!routes_idx) { + routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + _LOGD ("route-sync: skip adding duplicate route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + continue; + } + + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (plat_entry) { + const NMPObject *plat_o; + + plat_o = plat_entry->obj; - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 32, FALSE); + if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_o), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) + continue; - _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (route, NULL, 0)); + /* we need to replace the existing route with a (slightly) differnt + * one. Delete it first. */ + if (!nm_platform_ip_route_delete (self, plat_o)) { + /* ignore error. */ + } + } - return klass->ip4_route_add (self, route); + plerr = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + conf_o); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + if (-((int) plerr) == EEXIST) { + /* Don't fail for EEXIST. It's not clear that the existing route + * is identical to the one that we were about to add. However, + * above we should have deleted conflicting (non-identical) routes. */ + if (_LOGD_ENABLED ()) { + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (!plat_entry) { + _LOGD ("route-sync: adding route %s failed with EEXIST, however we cannot find such a route", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + } else if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_entry->obj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) != 0) { + _LOGD ("route-sync: adding route %s failed due to existing (different!) route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + } + } + } else if ( -((int) plerr) == EINVAL + && out_temporary_not_available + && _err_inval_due_to_ipv6_tentative_pref_src (self, conf_o)) { + _LOGD ("route-sync: ignore failure to add IPv6 route with tentative IPv6 pref-src: %s: %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + if (!*out_temporary_not_available) + *out_temporary_not_available = g_ptr_array_new_full (0, (GDestroyNotify) nmp_object_unref); + g_ptr_array_add (*out_temporary_not_available, (gpointer) nmp_object_ref (conf_o)); + } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + } else { + const char *reason = ""; + + if ( -((int) plerr) == ENETUNREACH + && ( vt->is_ip4 + ? !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway + : !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway))) + reason = "; is the gateway directly reachable?"; + + _LOGW ("route-sync: failure to add IPv%c route: %s: %s%s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), + reason); + success = FALSE; + } + } + } + } + + if (routes_prune) { + for (i = 0; i < routes_prune->len; i++) { + const NMPObject *prune_o; + + prune_o = routes_prune->pdata[i]; + + nm_assert ( (addr_family == AF_INET && NMP_OBJECT_GET_TYPE (prune_o) == NMP_OBJECT_TYPE_IP4_ROUTE) + || (addr_family == AF_INET6 && NMP_OBJECT_GET_TYPE (prune_o) == NMP_OBJECT_TYPE_IP6_ROUTE)); + + if ( routes_idx + && g_hash_table_lookup (routes_idx, prune_o)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + prune_o)) + continue; + + if (!nm_platform_ip_route_delete (self, prune_o)) { + /* ignore error... */ + } + } + } + + return success; } gboolean -nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route) +nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex) { + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 128, FALSE); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); - _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (route, NULL, 0)); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + gs_unref_ptrarray GPtrArray *routes_prune = NULL; - return klass->ip6_route_add (self, route); + routes_prune = nm_platform_ip_route_get_prune_list (self, + AF_INET, + ifindex, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + success &= nm_platform_ip_route_sync (self, AF_INET, ifindex, NULL, routes_prune, NULL); + } + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + + routes_prune = nm_platform_ip_route_get_prune_list (self, + AF_INET6, + ifindex, + NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); + success &= nm_platform_ip_route_sync (self, AF_INET6, ifindex, NULL, routes_prune, NULL); + } + return success; } -gboolean -nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +/*****************************************************************************/ + +static guint8 +_ip_route_scope_inv_get_normalized (const NMPlatformIP4Route *route) +{ + /* in kernel, you cannot set scope to RT_SCOPE_NOWHERE (255). + * That means, in NM, we treat RT_SCOPE_NOWHERE as unset, and detect + * it based on the presence of the gateway. In other words, when adding + * a route with scope RT_SCOPE_NOWHERE (in NetworkManager) to kernel, + * the resulting scope will be either "link" or "universe" (depending + * on the gateway). + * + * Note that internally, we track @scope_inv is the inverse of scope, + * so that the default equals zero (~(RT_SCOPE_NOWHERE)). + **/ + if (route->scope_inv == 0) { + return nm_platform_route_scope_inv (!route->gateway + ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + } + return route->scope_inv; +} + +static guint8 +_route_pref_normalize (guint8 pref) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + /* for kernel (and ICMPv6) pref can only have one of 3 values. Normalize. */ + return NM_IN_SET (pref, NM_ICMPV6_ROUTER_PREF_LOW, + NM_ICMPV6_ROUTER_PREF_HIGH) + ? pref + : NM_ICMPV6_ROUTER_PREF_MEDIUM; +} + +/** + * nm_platform_ip_route_normalize: + * @addr_family: AF_INET or AF_INET6 + * @route: an NMPlatformIP4Route or NMPlatformIP6Route instance, depending on @addr_family. + * + * Adding a route to kernel via nm_platform_ip_route_add() will normalize/coerce some + * properties of the route. This function modifies (normalizes) the route like it + * would be done by adding the route in kernel. + * + * Note that this function is related to NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY + * in that if two routes compare semantically equal, after normalizing they also shall + * compare equal with NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL. + */ +void +nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route) +{ + NMPlatformIP4Route *r4; + NMPlatformIP6Route *r6; + + switch (addr_family) { + case AF_INET: + r4 = (NMPlatformIP4Route *) route; + r4->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r4->table_coerced, TRUE)); + r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen); + r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source); + r4->scope_inv = _ip_route_scope_inv_get_normalized (r4); + break; + case AF_INET6: + r6 = (NMPlatformIP6Route *) route; + r6->table_coerced = nm_platform_route_table_coerce (nm_platform_route_table_uncoerce (r6->table_coerced, TRUE)); + nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen); + r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source), + r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric); + nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen); + break; + default: + nm_assert_not_reached (); + break; + } +} + +static NMPlatformError +_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + int addr_family, + gconstpointer route) +{ + char sbuf[sizeof (_nm_utils_to_string_buffer)]; _CHECK_SELF (self, klass, FALSE); - _LOGD ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", - nm_utils_inet4_ntop (network, NULL), plen, metric, ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_route_delete (self, ifindex, network, plen, metric); + nm_assert (route); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + _LOGD ("route: %-10s IPv%c route: %s", + _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), + nm_utils_addr_family_to_char (addr_family), + addr_family == AF_INET + ? nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)) + : nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); + + return klass->ip_route_add (self, flags, addr_family, route); +} + +NMPlatformError +nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route) +{ + int addr_family; + + switch (NMP_OBJECT_GET_TYPE (route)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + addr_family = AF_INET; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + addr_family = AF_INET6; + break; + default: + g_return_val_if_reached (FALSE); + } + + return _ip_route_add (self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE (route)); +} + +NMPlatformError +nm_platform_ip4_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route) +{ + return _ip_route_add (self, flags, AF_INET, route); +} + +NMPlatformError +nm_platform_ip6_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP6Route *route) +{ + return _ip_route_add (self, flags, AF_INET6, route); } gboolean -nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +nm_platform_ip_route_delete (NMPlatform *self, + const NMPObject *obj) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + _CHECK_SELF (self, klass, FALSE); + + if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)) + g_return_val_if_reached (FALSE); + + _LOGD ("route: delete IPv%c route %s", + NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE ? '4' : '6', + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + return klass->ip_route_delete (self, obj); +} + +/*****************************************************************************/ + +NMPlatformError +nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address /* in_addr_t or struct in6_addr */, + int oif_ifindex, + NMPObject **out_route) +{ + nm_auto_nmpobj NMPObject *route = NULL; + NMPlatformError result; + char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf_err[200]; + char buf_oif[64]; _CHECK_SELF (self, klass, FALSE); - _LOGD ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", - nm_utils_inet6_ntop (&network, NULL), plen, metric, ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_route_delete (self, ifindex, network, plen, metric); + g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, + AF_INET6), NM_PLATFORM_ERROR_BUG); + + _LOGT ("route: get IPv%c route for: %s%s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + oif_ifindex > 0 ? nm_sprintf_buf (buf_oif, " oif %d", oif_ifindex) : ""); + + if (!klass->ip_route_get) + result = NM_PLATFORM_ERROR_OPNOTSUPP; + else { + result = klass->ip_route_get (self, + addr_family, + address, + oif_ifindex, + &route); + } + + if (result != NM_PLATFORM_ERROR_SUCCESS) { + nm_assert (!route); + _LOGW ("route: get IPv%c route for: %s failed with %s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + nm_platform_error_to_string (result, buf_err, sizeof (buf_err))); + } else { + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (route), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (route)); + nm_assert (route->parent._ref_count == 1); + _LOGD ("route: get IPv%c route for: %s succeeded: %s", + nm_utils_addr_family_to_char (addr_family), + inet_ntop (addr_family, address, buf, sizeof (buf)), + nmp_object_to_string (route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + NM_SET_OUT (out_route, g_steal_pointer (&route)); + } + return result; } -const NMPlatformIP4Route * -nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +/*****************************************************************************/ + +#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500) +#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000)) + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_get (gint64 timeout_ms) { - _CHECK_SELF (self, klass, FALSE); + return timeout_ms >> 1; +} - return klass->ip4_route_get (self ,ifindex, network, plen, metric); +static gint64 +_ip4_dev_route_blacklist_timeout_ms_marked (gint64 timeout_ms) +{ + return !!(timeout_ms & ((gint64) 1)); } -const NMPlatformIP6Route * -nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +static gboolean +_ip4_dev_route_blacklist_check_cb (gpointer user_data) { - _CHECK_SELF (self, klass, FALSE); + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + priv->ip4_dev_route_blacklist_check_id = 0; + +again: + if (!priv->ip4_dev_route_blacklist_hash) + goto out; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (!_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) + continue; + + /* unmark because we checked it. */ + *p_timeout_ms = *p_timeout_ms & ~((gint64) 1); + + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + p_obj)) + continue; + + _LOGT ("ip4-dev-route: delete %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nm_platform_ip_route_delete (self, p_obj); + goto again; + } + +out: + return G_SOURCE_REMOVE; +} + +static void +_ip4_dev_route_blacklist_check_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if (!priv->ip4_dev_route_blacklist_check_id) { + priv->ip4_dev_route_blacklist_check_id = g_idle_add_full (G_PRIORITY_HIGH, + _ip4_dev_route_blacklist_check_cb, + self, + NULL); + } +} + +static void +_ip4_dev_route_blacklist_notify_route (NMPlatform *self, + const NMPObject *obj) +{ + NMPlatformPrivate *priv; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + if (!g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + obj, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) + return; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + /* already expired. Wait for gc. */ + return; + } + + if (_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) { + nm_assert (priv->ip4_dev_route_blacklist_check_id); + return; + } + + /* We cannot delete it right away because we are in the process of receiving netlink messages. + * It may be possible to do so, but complicated and error prone. + * + * Instead, we mark the entry and schedule an idle action (with high priority). */ + *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1); + _ip4_dev_route_blacklist_check_schedule (self); +} + +static gboolean +_ip4_dev_route_blacklist_gc_timeout_handle (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + _LOGT ("ip4-dev-route: cleanup %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + _ip4_dev_route_blacklist_schedule (self); + return G_SOURCE_CONTINUE; +} + +static void +_ip4_dev_route_blacklist_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if ( !priv->ip4_dev_route_blacklist_hash + || g_hash_table_size (priv->ip4_dev_route_blacklist_hash) == 0) { + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + } else { + if (!priv->ip4_dev_route_blacklist_gc_timeout_id) { + /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash. + * It can run infrequently, and it doesn't hurt if expired entries linger around a bit + * longer then necessary. */ + priv->ip4_dev_route_blacklist_gc_timeout_id = g_timeout_add_seconds (IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S, + _ip4_dev_route_blacklist_gc_timeout_handle, + self); + } + } +} + +/** + * nm_platform_ip4_dev_route_blacklist_set: + * @self: + * @ifindex: + * @ip4_dev_route_blacklist: + * + * When adding an IP address, kernel automatically adds a device route. + * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For proper + * IPv6 support, we require kernel support for IFA_F_NOPREFIXROUTE and always + * add the device route manually. + * + * For IPv4, this flag is rather new and we don't rely on it yet. We want to use + * it (but currently still don't). So, for IPv4, kernel possibly adds a device + * route, however it has a wrong metric of zero. We add our own device route (with + * proper metric), but need to delete the route that kernel adds. + * + * The problem is, that kernel does not immidiately add the route, when adding + * the address. It only shows up some time later. So, we register here a list + * of blacklisted routes, and when they show up within a time out, we assume it's + * the kernel generated one, and we delete it. + * + * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4 + * routes as well. + */ +void +nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist) +{ + NMPlatformPrivate *priv; + GHashTableIter iter; + const NMPObject *p_obj; + guint i; + gint64 timeout_ms; + gint64 timeout_ms_val; + gint64 *p_timeout_ms; + gboolean needs_check = FALSE; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (ifindex > 0); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* first, expire all for current ifindex... */ + if (priv->ip4_dev_route_blacklist_hash) { + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (NMP_OBJECT_CAST_IP4_ROUTE (p_obj)->ifindex == ifindex) { + /* we could g_hash_table_iter_remove(&iter) the current entry. + * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle() + * handle it. + * + * The assumption is, that ip4_dev_route_blacklist contains the very same entry + * again, with a new timeout. So, we can un-expire it below. */ + *p_timeout_ms = 0; + } + } + } + + if ( ip4_dev_route_blacklist + && ip4_dev_route_blacklist->len > 0) { + + if (!priv->ip4_dev_route_blacklist_hash) { + priv->ip4_dev_route_blacklist_hash = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn_gint64); + } + + timeout_ms = nm_utils_get_monotonic_timestamp_ms () + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS; + timeout_ms_val = (timeout_ms << 1) | ((gint64) 1); + for (i = 0; i < ip4_dev_route_blacklist->len; i++) { + const NMPObject *o; + + needs_check = TRUE; + o = ip4_dev_route_blacklist->pdata[i]; + if (g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + o, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) { + if (nmp_object_equal (p_obj, o)) { + /* un-expire and reuse the entry. */ + _LOGT ("ip4-dev-route: register %s (update)", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + *p_timeout_ms = timeout_ms_val; + continue; + } + } + + _LOGT ("ip4-dev-route: register %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + p_timeout_ms = g_slice_new (gint64); + *p_timeout_ms = timeout_ms_val; + g_hash_table_replace (priv->ip4_dev_route_blacklist_hash, + (gpointer) nmp_object_ref (o), + p_timeout_ms); + } + } + + _ip4_dev_route_blacklist_schedule (self); - return klass->ip6_route_get (self, ifindex, network, plen, metric); + if (needs_check) + _ip4_dev_route_blacklist_check_schedule (self); } /*****************************************************************************/ @@ -3898,6 +4994,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_table[30]; char str_scope[30], s_source[50]; char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; @@ -3909,20 +5006,9 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - if (route->tos) - nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); - if (route->window) - nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); - if (route->cwnd) - nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); - if (route->initcwnd) - nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); - if (route->initrwnd) - nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); - if (route->mtu) - nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" @@ -3939,6 +5025,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi "%s" /* initrwnd */ "%s" /* mtu */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "", s_network, route->plen, s_gateway, @@ -3951,12 +5038,12 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", route->pref_src ? " pref-src " : "", route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "", - route->tos ? str_tos : "", - route->window ? str_window : "", - route->cwnd ? str_cwnd : "", - route->initcwnd ? str_initcwnd : "", - route->initrwnd ? str_initrwnd : "", - route->mtu ? str_mtu : ""); + route->tos ? nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos) : "", + route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "", + route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "", + route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "", + route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) : ""); return buf; } @@ -3976,16 +5063,18 @@ const char * nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN]; - char s_src[INET6_ADDRSTRLEN]; + char s_src_all[INET6_ADDRSTRLEN + 40], s_src[INET6_ADDRSTRLEN]; + char str_table[30]; + char str_pref[40]; + char str_pref2[30]; char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50]; - char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; + char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) return buf; inet_ntop (AF_INET6, &route->network, s_network, sizeof (s_network)); inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof (s_gateway)); - inet_ntop (AF_INET6, &route->src, s_src, sizeof (s_src)); if (IN6_IS_ADDR_UNSPECIFIED (&route->pref_src)) s_pref_src[0] = 0; @@ -3994,36 +5083,25 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - if (route->tos) - nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); - if (route->window) - nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); - if (route->cwnd) - nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); - if (route->initcwnd) - nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); - if (route->initrwnd) - nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); - if (route->mtu) - nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); - g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" " metric %"G_GUINT32_FORMAT " mss %"G_GUINT32_FORMAT " rt-src %s" /* protocol */ - " src %s/%u" /* source */ + "%s" /* source */ "%s" /* cloned */ "%s%s" /* pref-src */ - "%s" /* tos */ "%s" /* window */ "%s" /* cwnd */ "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ + "%s" /* pref */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_uncoerce (route->table_coerced, FALSE)) : "", s_network, route->plen, s_gateway, @@ -4031,358 +5109,724 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi route->metric, route->mss, nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)), - s_src, route->src_plen, + route->src_plen || !IN6_IS_ADDR_UNSPECIFIED (&route->src) + ? nm_sprintf_buf (s_src_all, " src %s/%u", nm_utils_inet6_ntop (&route->src, s_src), (unsigned) route->src_plen) + : "", route->rt_cloned ? " cloned" : "", s_pref_src[0] ? " pref-src " : "", s_pref_src[0] ? s_pref_src : "", - route->tos ? str_tos : "", - route->window ? str_window : "", - route->cwnd ? str_cwnd : "", - route->initcwnd ? str_initcwnd : "", - route->initrwnd ? str_initrwnd : "", - route->mtu ? str_mtu : ""); + route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "", + route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "", + route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "", + route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, 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))) : ""); return buf; } -#define _CMP_SELF(a, b) \ - G_STMT_START { \ - if ((a) == (b)) \ - return 0; \ - if (!(a)) \ - return -1; \ - if (!(b)) \ - return 1; \ - } G_STMT_END - -#define _CMP_DIRECT(a, b) \ - G_STMT_START { \ - if ((a) != (b)) \ - return ((a) < (b)) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_DIRECT_MEMCMP(a, b, size) \ - G_STMT_START { \ - int c = memcmp ((a), (b), (size)); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD(a, b, field) \ - G_STMT_START { \ - if (((a)->field) != ((b)->field)) \ - return (((a)->field) < ((b)->field)) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_BOOL(a, b, field) \ - G_STMT_START { \ - if ((!((a)->field)) != (!((b)->field))) \ - return ((!((a)->field)) < (!((b)->field))) ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_STR(a, b, field) \ - G_STMT_START { \ - int c = strcmp ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_STR_INTERNED(a, b, field) \ - G_STMT_START { \ - if (((a)->field) != ((b)->field)) { \ - /* just to be sure, also do a strcmp() if the pointers don't match */ \ - int c = g_strcmp0 ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } \ - } G_STMT_END - -#define _CMP_FIELD_STR0(a, b, field) \ - G_STMT_START { \ - int c = g_strcmp0 ((a)->field, (b)->field); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ - G_STMT_START { \ - int c = memcmp (&((a)->field), &((b)->field), \ - MIN (len, sizeof ((a)->field))); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END - -#define _CMP_FIELD_MEMCMP(a, b, field) \ - G_STMT_START { \ - int c = memcmp (&((a)->field), &((b)->field), \ - sizeof ((a)->field)); \ - if (c != 0) \ - return c < 0 ? -1 : 1; \ - } G_STMT_END +void +nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->master, + obj->parent, + obj->n_ifi_flags, + obj->mtu, + obj->type, + obj->arptype, + obj->inet6_addr_gen_mode_inv, + obj->inet6_token, + obj->rx_packets, + obj->rx_bytes, + obj->tx_packets, + obj->tx_bytes, + NM_HASH_COMBINE_BOOLS (guint8, + obj->connected, + obj->initialized)); + nm_hash_update_strarr (h, obj->name); + nm_hash_update_str0 (h, obj->kind); + nm_hash_update_str0 (h, obj->driver); + /* nm_hash_update_mem() also hashes the length obj->addr.len */ + nm_hash_update_mem (h, obj->addr.data, obj->addr.len); +} int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD (a, b, type); - _CMP_FIELD_STR (a, b, name); - _CMP_FIELD (a, b, master); - _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, n_ifi_flags); - _CMP_FIELD (a, b, connected); - _CMP_FIELD (a, b, mtu); - _CMP_FIELD_BOOL (a, b, initialized); - _CMP_FIELD (a, b, arptype); - _CMP_FIELD (a, b, addr.len); - _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); - _CMP_FIELD_STR_INTERNED (a, b, kind); - _CMP_FIELD_STR_INTERNED (a, b, driver); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD (a, b, type); + NM_CMP_FIELD_STR (a, b, name); + NM_CMP_FIELD (a, b, master); + NM_CMP_FIELD (a, b, parent); + NM_CMP_FIELD (a, b, n_ifi_flags); + NM_CMP_FIELD_UNSAFE (a, b, connected); + NM_CMP_FIELD (a, b, mtu); + NM_CMP_FIELD_BOOL (a, b, initialized); + NM_CMP_FIELD (a, b, arptype); + NM_CMP_FIELD (a, b, addr.len); + NM_CMP_FIELD (a, b, inet6_addr_gen_mode_inv); + NM_CMP_FIELD_STR_INTERNED (a, b, kind); + NM_CMP_FIELD_STR_INTERNED (a, b, driver); if (a->addr.len) - _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); - _CMP_FIELD_MEMCMP (a, b, inet6_token); - _CMP_FIELD (a, b, rx_packets); - _CMP_FIELD (a, b, rx_bytes); - _CMP_FIELD (a, b, tx_packets); - _CMP_FIELD (a, b, tx_bytes); + NM_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + NM_CMP_FIELD_MEMCMP (a, b, inet6_token); + NM_CMP_FIELD (a, b, rx_packets); + NM_CMP_FIELD (a, b, rx_bytes); + NM_CMP_FIELD (a, b, tx_packets); + NM_CMP_FIELD (a, b, tx_bytes); return 0; } +void +nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->input_flags, + obj->output_flags, + obj->input_key, + obj->output_key, + obj->ttl, + obj->tos, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, input_flags); - _CMP_FIELD (a, b, output_flags); - _CMP_FIELD (a, b, input_key); - _CMP_FIELD (a, b, output_key); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, input_flags); + NM_CMP_FIELD (a, b, output_flags); + NM_CMP_FIELD (a, b, input_key); + NM_CMP_FIELD (a, b, output_key); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); return 0; } +void +nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h) +{ + nm_hash_update_val (h, obj->p_key); + nm_hash_update_str0 (h, obj->mode); +} + int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, p_key); - _CMP_FIELD_STR_INTERNED (a, b, mode); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, p_key); + NM_CMP_FIELD_STR_INTERNED (a, b, mode); return 0; } +void +nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ttl, + obj->tclass, + obj->encap_limit, + obj->proto, + obj->flow_label); +} + int nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD_MEMCMP (a, b, local); - _CMP_FIELD_MEMCMP (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tclass); - _CMP_FIELD (a, b, encap_limit); - _CMP_FIELD (a, b, flow_label); - _CMP_FIELD (a, b, proto); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD_MEMCMP (a, b, local); + NM_CMP_FIELD_MEMCMP (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tclass); + NM_CMP_FIELD (a, b, encap_limit); + NM_CMP_FIELD (a, b, flow_label); + NM_CMP_FIELD (a, b, proto); return 0; } +void +nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ttl, + obj->tos, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); return 0; } +void +nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->parent_ifindex, + obj->sci, + obj->cipher_suite, + obj->window, + obj->icv_length, + obj->encoding_sa, + obj->validation, + NM_HASH_COMBINE_BOOLS (guint8, + obj->encrypt, + obj->protect, + obj->include_sci, + obj->es, + obj->scb, + obj->replay_protect)); +} + int nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, sci); - _CMP_FIELD (a, b, icv_length); - _CMP_FIELD (a, b, cipher_suite); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, encoding_sa); - _CMP_FIELD (a, b, validation); - _CMP_FIELD (a, b, encrypt); - _CMP_FIELD (a, b, protect); - _CMP_FIELD (a, b, include_sci); - _CMP_FIELD (a, b, es); - _CMP_FIELD (a, b, scb); - _CMP_FIELD (a, b, replay_protect); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, sci); + NM_CMP_FIELD (a, b, icv_length); + NM_CMP_FIELD (a, b, cipher_suite); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, encoding_sa); + NM_CMP_FIELD (a, b, validation); + NM_CMP_FIELD_UNSAFE (a, b, encrypt); + NM_CMP_FIELD_UNSAFE (a, b, protect); + NM_CMP_FIELD_UNSAFE (a, b, include_sci); + NM_CMP_FIELD_UNSAFE (a, b, es); + NM_CMP_FIELD_UNSAFE (a, b, scb); + NM_CMP_FIELD_UNSAFE (a, b, replay_protect); return 0; } +void +nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h ) +{ + nm_hash_update_vals (h, + obj->mode, + NM_HASH_COMBINE_BOOLS (guint8, + obj->no_promisc, + obj->tap)); +} + int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, mode); - _CMP_FIELD_BOOL (a, b, no_promisc); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, mode); + NM_CMP_FIELD_UNSAFE (a, b, no_promisc); + NM_CMP_FIELD_UNSAFE (a, b, tap); return 0; } +void +nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->flags, + obj->ttl, + obj->tos, + obj->proto, + (bool) obj->path_mtu_discovery); +} + int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, local); - _CMP_FIELD (a, b, remote); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD (a, b, tos); - _CMP_FIELD_BOOL (a, b, path_mtu_discovery); - _CMP_FIELD (a, b, flags); - _CMP_FIELD (a, b, proto); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD (a, b, remote); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery); + NM_CMP_FIELD (a, b, flags); + NM_CMP_FIELD (a, b, proto); return 0; } +void +nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->id, + obj->flags); +} + int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, id); - _CMP_FIELD (a, b, flags); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, id); + NM_CMP_FIELD (a, b, flags); return 0; } +void +nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->group6, + obj->local6, + obj->group, + obj->local, + obj->parent_ifindex, + obj->id, + obj->ageing, + obj->limit, + obj->dst_port, + obj->src_port_min, + obj->src_port_max, + obj->tos, + obj->ttl, + NM_HASH_COMBINE_BOOLS (guint8, + obj->learning, + obj->proxy, + obj->rsc, + obj->l2miss, + obj->l3miss)); +} + int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, parent_ifindex); - _CMP_FIELD (a, b, id); - _CMP_FIELD (a, b, group); - _CMP_FIELD (a, b, local); - _CMP_FIELD_MEMCMP (a, b, group6); - _CMP_FIELD_MEMCMP (a, b, local6); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, ttl); - _CMP_FIELD_BOOL (a, b, learning); - _CMP_FIELD (a, b, ageing); - _CMP_FIELD (a, b, limit); - _CMP_FIELD (a, b, dst_port); - _CMP_FIELD (a, b, src_port_min); - _CMP_FIELD (a, b, src_port_max); - _CMP_FIELD_BOOL (a, b, proxy); - _CMP_FIELD_BOOL (a, b, rsc); - _CMP_FIELD_BOOL (a, b, l2miss); - _CMP_FIELD_BOOL (a, b, l3miss); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, parent_ifindex); + NM_CMP_FIELD (a, b, id); + NM_CMP_FIELD (a, b, group); + NM_CMP_FIELD (a, b, local); + NM_CMP_FIELD_MEMCMP (a, b, group6); + NM_CMP_FIELD_MEMCMP (a, b, local6); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD (a, b, ttl); + NM_CMP_FIELD_BOOL (a, b, learning); + NM_CMP_FIELD (a, b, ageing); + NM_CMP_FIELD (a, b, limit); + NM_CMP_FIELD (a, b, dst_port); + NM_CMP_FIELD (a, b, src_port_min); + NM_CMP_FIELD (a, b, src_port_max); + NM_CMP_FIELD_BOOL (a, b, proxy); + NM_CMP_FIELD_BOOL (a, b, rsc); + NM_CMP_FIELD_BOOL (a, b, l2miss); + NM_CMP_FIELD_BOOL (a, b, l3miss); return 0; } +void +nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address); + nm_hash_update_strarr (h, obj->label); +} + int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD (a, b, address); - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, peer_address); - _CMP_FIELD (a, b, addr_source); - _CMP_FIELD (a, b, timestamp); - _CMP_FIELD (a, b, lifetime); - _CMP_FIELD (a, b, preferred); - _CMP_FIELD (a, b, n_ifa_flags); - _CMP_FIELD_STR (a, b, label); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD (a, b, address); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, peer_address); + NM_CMP_FIELD (a, b, addr_source); + NM_CMP_FIELD (a, b, timestamp); + NM_CMP_FIELD (a, b, lifetime); + NM_CMP_FIELD (a, b, preferred); + NM_CMP_FIELD (a, b, n_ifa_flags); + NM_CMP_FIELD_STR (a, b, label); return 0; } +void +nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address); +} + int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) { const struct in6_addr *p_a, *p_b; - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - _CMP_FIELD_MEMCMP (a, b, address); - _CMP_FIELD (a, b, plen); + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD_MEMCMP (a, b, address); + NM_CMP_FIELD (a, b, plen); p_a = nm_platform_ip6_address_get_peer (a); p_b = nm_platform_ip6_address_get_peer (b); - _CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a)); - _CMP_FIELD (a, b, addr_source); - _CMP_FIELD (a, b, timestamp); - _CMP_FIELD (a, b, lifetime); - _CMP_FIELD (a, b, preferred); - _CMP_FIELD (a, b, n_ifa_flags); + NM_CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a)); + NM_CMP_FIELD (a, b, addr_source); + NM_CMP_FIELD (a, b, timestamp); + NM_CMP_FIELD (a, b, lifetime); + NM_CMP_FIELD (a, b, preferred); + NM_CMP_FIELD (a, b, n_ifa_flags); return 0; } +void +nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h) +{ + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->tos); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->tos, + /* on top of WEAK_ID: */ + obj->ifindex, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + _ip_route_scope_inv_get_normalized (obj), + obj->gateway, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + obj->ifindex, + nm_utils_ip4_address_clear_host_address (obj->network, obj->plen), + obj->plen, + obj->metric, + obj->gateway, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + _ip_route_scope_inv_get_normalized (obj), + obj->tos, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + nm_hash_update_vals (h, + obj->table_coerced, + obj->ifindex, + obj->network, + obj->plen, + obj->metric, + obj->gateway, + obj->rt_source, + obj->scope_inv, + obj->tos, + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu)); + break; + } +} + int -nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part) -{ - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - if (consider_host_part) - _CMP_FIELD (a, b, network); - else { - _CMP_DIRECT (nm_utils_ip4_address_clear_host_address (a->network, a->plen), - nm_utils_ip4_address_clear_host_address (b->network, b->plen)); +nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type) +{ + NM_CMP_SELF (a, b); + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD (a, b, tos); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); + NM_CMP_FIELD (a, b, gateway); + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD (a, b, pref_src); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + } + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + } else + NM_CMP_FIELD (a, b, table_coerced); + NM_CMP_FIELD (a, b, ifindex); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); + else + NM_CMP_FIELD (a, b, network); + NM_CMP_FIELD (a, b, plen); + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD (a, b, gateway); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); + } else { + NM_CMP_FIELD (a, b, rt_source); + NM_CMP_FIELD (a, b, scope_inv); + } + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD (a, b, pref_src); + NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); + NM_CMP_FIELD (a, b, tos); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + break; } - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, metric); - _CMP_FIELD (a, b, gateway); - _CMP_FIELD (a, b, rt_source); - _CMP_FIELD (a, b, mss); - _CMP_FIELD (a, b, scope_inv); - _CMP_FIELD (a, b, pref_src); - _CMP_FIELD (a, b, rt_cloned); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, lock_window); - _CMP_FIELD (a, b, lock_cwnd); - _CMP_FIELD (a, b, lock_initcwnd); - _CMP_FIELD (a, b, lock_initrwnd); - _CMP_FIELD (a, b, lock_mtu); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, cwnd); - _CMP_FIELD (a, b, initcwnd); - _CMP_FIELD (a, b, initrwnd); - _CMP_FIELD (a, b, mtu); return 0; } -int -nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part) -{ - _CMP_SELF (a, b); - _CMP_FIELD (a, b, ifindex); - if (consider_host_part) - _CMP_FIELD_MEMCMP (a, b, network); - else { - struct in6_addr n1, n2; +void +nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h) +{ + struct in6_addr a1, a2; + + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen, + /* on top of WEAK_ID: */ + obj->ifindex, + obj->gateway); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + nm_hash_update_vals (h, + nm_platform_route_table_uncoerce (obj->table_coerced, TRUE), + obj->ifindex, + *nm_utils_ip6_address_clear_host_address (&a1, &obj->network, obj->plen), + obj->plen, + nm_utils_ip6_route_metric_normalize (obj->metric), + obj->gateway, + obj->pref_src, + *nm_utils_ip6_address_clear_host_address (&a2, &obj->src, obj->src_plen), + obj->src_plen, + nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source), + obj->mss, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu), + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + _route_pref_normalize (obj->rt_pref)); + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + nm_hash_update_vals (h, + obj->table_coerced, + obj->ifindex, + obj->network, + obj->plen, + obj->metric, + obj->gateway, + obj->pref_src, + obj->src, + obj->src_plen, + obj->rt_source, + obj->mss, + NM_HASH_COMBINE_BOOLS (guint8, + obj->rt_cloned, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu), + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + obj->rt_pref); + break; + } +} - nm_utils_ip6_address_clear_host_address (&n1, &a->network, a->plen); - nm_utils_ip6_address_clear_host_address (&n2, &b->network, b->plen); - _CMP_DIRECT_MEMCMP (&n1, &n2, sizeof (struct in6_addr)); +int +nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type) +{ + NM_CMP_SELF (a, b); + switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); + NM_CMP_FIELD (a, b, plen); + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); + NM_CMP_FIELD (a, b, src_plen); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD_IN6ADDR (a, b, gateway); + } + break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nm_platform_route_table_uncoerce (a->table_coerced, TRUE), + nm_platform_route_table_uncoerce (b->table_coerced, TRUE)); + } else + NM_CMP_FIELD (a, b, table_coerced); + NM_CMP_FIELD (a, b, ifindex); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); + else + NM_CMP_FIELD_IN6ADDR (a, b, network); + NM_CMP_FIELD (a, b, plen); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); + else + NM_CMP_FIELD (a, b, metric); + NM_CMP_FIELD_IN6ADDR (a, b, gateway); + NM_CMP_FIELD_IN6ADDR (a, b, pref_src); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); + NM_CMP_FIELD (a, b, src_plen); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + } else { + NM_CMP_FIELD_IN6ADDR (a, b, src); + NM_CMP_FIELD (a, b, src_plen); + NM_CMP_FIELD (a, b, rt_source); + } + NM_CMP_FIELD (a, b, mss); + NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); + NM_CMP_FIELD_UNSAFE (a, b, lock_window); + NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd); + NM_CMP_FIELD_UNSAFE (a, b, lock_mtu); + NM_CMP_FIELD (a, b, window); + NM_CMP_FIELD (a, b, cwnd); + NM_CMP_FIELD (a, b, initcwnd); + NM_CMP_FIELD (a, b, initrwnd); + NM_CMP_FIELD (a, b, mtu); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT (_route_pref_normalize (a->rt_pref), _route_pref_normalize (b->rt_pref)); + else + NM_CMP_FIELD (a, b, rt_pref); + break; } - _CMP_FIELD (a, b, plen); - _CMP_FIELD (a, b, metric); - _CMP_FIELD_MEMCMP (a, b, gateway); - _CMP_FIELD_MEMCMP (a, b, pref_src); - _CMP_FIELD_MEMCMP (a, b, src); - _CMP_FIELD (a, b, src_plen); - _CMP_FIELD (a, b, rt_source); - _CMP_FIELD (a, b, mss); - _CMP_FIELD (a, b, rt_cloned); - _CMP_FIELD (a, b, tos); - _CMP_FIELD (a, b, lock_window); - _CMP_FIELD (a, b, lock_cwnd); - _CMP_FIELD (a, b, lock_initcwnd); - _CMP_FIELD (a, b, lock_initrwnd); - _CMP_FIELD (a, b, lock_mtu); - _CMP_FIELD (a, b, window); - _CMP_FIELD (a, b, cwnd); - _CMP_FIELD (a, b, initcwnd); - _CMP_FIELD (a, b, initrwnd); - _CMP_FIELD (a, b, mtu); return 0; } @@ -4404,7 +5848,7 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor { gint64 ta = 0, tb = 0; - _CMP_SELF (a, b); + NM_CMP_SELF (a, b); if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0) ta = G_MAXINT64; @@ -4485,120 +5929,139 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform /*****************************************************************************/ -NMPNetns * -nm_platform_netns_get (NMPlatform *self) +void +nm_platform_cache_update_emit_signal (NMPlatform *self, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) { - _CHECK_SELF (self, klass, NULL); + gboolean visible_new; + gboolean visible_old; + const NMPObject *o; + const NMPClass *klass; - return self->_netns; -} + nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); -gboolean -nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns) -{ - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new); - if ( platform->_netns - && !nmp_netns_push (platform->_netns)) { - NM_SET_OUT (netns, NULL); - return FALSE; + nm_assert (NM_IN_SET (nm_platform_netns_get (self), + NULL, + nmp_netns_get_current ())); + + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self); + + switch (cache_op) { + case NMP_CACHE_OPS_ADDED: + if (!nmp_object_is_visible (obj_new)) + return; + o = obj_new; + break; + case NMP_CACHE_OPS_UPDATED: + visible_old = nmp_object_is_visible (obj_old); + visible_new = nmp_object_is_visible (obj_new); + if (!visible_old && visible_new) { + o = obj_new; + cache_op = NMP_CACHE_OPS_ADDED; + } else if (visible_old && !visible_new) { + o = obj_old; + cache_op = NMP_CACHE_OPS_REMOVED; + } else if (!visible_new) { + /* it was invisible and stayed invisible. Nothing to do. */ + return; + } else + o = obj_new; + break; + case NMP_CACHE_OPS_REMOVED: + if (!nmp_object_is_visible (obj_old)) + return; + o = obj_old; + break; + default: + nm_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); + return; } - NM_SET_OUT (netns, platform->_netns); - return TRUE; + klass = NMP_OBJECT_GET_CLASS (o); + + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id + && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) + _ip4_dev_route_blacklist_notify_route (self, o); + + _LOGt ("emit signal %s %s: %s", + klass->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + nmp_object_ref (o); + g_signal_emit (self, + _nm_platform_signal_id_get (klass->signal_type_id), + 0, + (int) klass->obj_type, + o->object.ifindex, + &o->object, + (int) cache_op); + nmp_object_unref (o); } /*****************************************************************************/ -static gboolean -_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) +NMPCache * +nm_platform_get_cache (NMPlatform *self) { - NMPlatformIP4Route rt = route->r4; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip4_route_add (self, &rt); + return NM_PLATFORM_GET_PRIVATE (self)->cache; } -static gboolean -_vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) +NMPNetns * +nm_platform_netns_get (NMPlatform *self) { - NMPlatformIP6Route rt = route->r6; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; + _CHECK_SELF (self, klass, NULL); - return nm_platform_ip6_route_add (self, &rt); + return self->_netns; } -static gboolean -_vtr_v4_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) +gboolean +nm_platform_netns_push (NMPlatform *self, NMPNetns **netns) { - return nm_platform_ip4_route_delete (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->r4.network, - route->rx.plen, - route->rx.metric); -} + g_return_val_if_fail (NM_IS_PLATFORM (self), FALSE); -static gboolean -_vtr_v6_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) -{ - return nm_platform_ip6_route_delete (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->r6.network, - route->rx.plen, - route->rx.metric); + if ( self->_netns + && !nmp_netns_push (self->_netns)) { + NM_SET_OUT (netns, NULL); + return FALSE; + } + + NM_SET_OUT (netns, self->_netns); + return TRUE; } +/*****************************************************************************/ + static guint32 _vtr_v4_metric_normalize (guint32 metric) { return metric; } -static gboolean -_vtr_v4_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) -{ - return nm_platform_ip4_route_delete (self, ifindex, 0, 0, metric); -} - -static gboolean -_vtr_v6_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) -{ - return nm_platform_ip6_route_delete (self, ifindex, in6addr_any, 0, metric); -} - /*****************************************************************************/ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .addr_family = AF_INET, .sizeof_route = sizeof (NMPlatformIP4Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip4_route_cmp_full, + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .route_get_all = nm_platform_ip4_route_get_all, - .route_add = _vtr_v4_route_add, - .route_delete = _vtr_v4_route_delete, - .route_delete_default = _vtr_v4_route_delete_default, .metric_normalize = _vtr_v4_metric_normalize, }; const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .addr_family = AF_INET6, .sizeof_route = sizeof (NMPlatformIP6Route), - .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip6_route_cmp_full, + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .route_get_all = nm_platform_ip6_route_get_all, - .route_add = _vtr_v6_route_add, - .route_delete = _vtr_v6_route_delete, - .route_delete_default = _vtr_v6_route_delete_default, .metric_normalize = nm_utils_ip6_route_metric_normalize, }; @@ -4622,6 +6085,10 @@ set_property (GObject *object, guint prop_id, self->_netns = g_object_ref (netns); } break; + case PROP_USE_UDEV: + /* construct-only */ + priv->use_udev = g_value_get_boolean (value); + break; case PROP_LOG_WITH_PTR: /* construct-only */ priv->log_with_ptr = g_value_get_boolean (value); @@ -4638,12 +6105,40 @@ nm_platform_init (NMPlatform *self) self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_PLATFORM, NMPlatformPrivate); } +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMPlatform *self; + NMPlatformPrivate *priv; + + object = G_OBJECT_CLASS (nm_platform_parent_class)->constructor (type, + n_construct_params, + construct_params); + self = NM_PLATFORM (object); + priv = NM_PLATFORM_GET_PRIVATE (self); + + priv->multi_idx = nm_dedup_multi_index_new (); + + priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self), + priv->use_udev); + return object; +} + static void finalize (GObject *object) { NMPlatform *self = NM_PLATFORM (object); + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_check_id); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); g_clear_object (&self->_netns); + nm_dedup_multi_index_unref (priv->multi_idx); + nmp_cache_free (priv->cache); } static void @@ -4653,6 +6148,7 @@ nm_platform_class_init (NMPlatformClass *platform_class) g_type_class_add_private (object_class, sizeof (NMPlatformPrivate)); + object_class->constructor = constructor; object_class->set_property = set_property; object_class->finalize = finalize; @@ -4667,6 +6163,14 @@ nm_platform_class_init (NMPlatformClass *platform_class) G_PARAM_STATIC_STRINGS)); g_object_class_install_property + (object_class, PROP_USE_UDEV, + g_param_spec_boolean (NM_PLATFORM_USE_UDEV, "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_LOG_WITH_PTR, g_param_spec_boolean (NM_PLATFORM_LOG_WITH_PTR, "", "", TRUE, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 1b8fa133..d155c109 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -45,12 +45,16 @@ /*****************************************************************************/ #define NM_PLATFORM_NETNS_SUPPORT "netns-support" +#define NM_PLATFORM_USE_UDEV "use-udev" #define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" /*****************************************************************************/ struct udev_device; +typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, + gpointer user_data); + /* workaround for older libnl version, that does not define these flags. */ #ifndef IFA_F_MANAGETEMPADDR #define IFA_F_MANAGETEMPADDR 0x100 @@ -59,6 +63,8 @@ struct udev_device; #define IFA_F_NOPREFIXROUTE 0x200 #endif +#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ + /* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers * that don't define it. */ #define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ @@ -72,6 +78,74 @@ struct udev_device; /* Redefine this in host's endianness */ #define NM_GRE_KEY 0x2000 +typedef enum { + /* use our own platform enum for the nlmsg-flags. Otherwise, we'd have + * to include <linux/netlink.h> */ + NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */ + NMP_NLM_FLAG_F_EXCL = 0x200, /* NLM_F_EXCL, Do not touch, if it exists */ + NMP_NLM_FLAG_F_CREATE = 0x400, /* NLM_F_CREATE, Create, if it does not exist */ + NMP_NLM_FLAG_F_APPEND = 0x800, /* NLM_F_APPEND, Add to end of list */ + + NMP_NLM_FLAG_FMASK = 0xFFFF, /* a mask for all NMP_NLM_FLAG_F_* flags */ + + /* instructs NM to suppress logging an error message for any failures + * received from kernel. + * + * It will still log with debug-level, and it will still log + * other failures aside the kernel response. */ + NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE = 0x10000, + + /* the following aliases correspond to iproute2's `ip route CMD` for + * RTM_NEWROUTE, with CMD being one of add, change, replace, prepend, + * append and test. */ + NMP_NLM_FLAG_ADD = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_EXCL, + NMP_NLM_FLAG_CHANGE = NMP_NLM_FLAG_F_REPLACE, + NMP_NLM_FLAG_REPLACE = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_REPLACE, + NMP_NLM_FLAG_PREPEND = NMP_NLM_FLAG_F_CREATE, + NMP_NLM_FLAG_APPEND = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_APPEND, + NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL, +} NMPNlmFlags; + +typedef enum { + /* compare fields which kernel considers as similar routes. + * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + * and means that `ip route add` would fail to add two routes + * that have the same NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID. + * On the other hand, `ip route append` would allow that, as + * long as NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID differs. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, + + /* compare two routes as kernel would allow to add them with + * `ip route append`. In other words, kernel does not allow you to + * add two routes (at the same time) which compare equal according + * to NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID. + * + * For the ID we can only recognize route fields that we actually implement. + * However, kernel supports more routing options, some of them also part of + * the ID. NetworkManager is oblivious to these options and will wrongly think + * that two routes are idential, while they are not. That can lead to an + * inconsistent platform cache. Not much what we can do about that, except + * implementing all options that kernel supports *sigh*. See rh#1337860. + */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + + /* compare all fields as they make sense for kernel. For example, + * a route destination 192.168.1.5/24 is not accepted by kernel and + * we treat it identical to 192.168.1.0/24. Semantically these + * routes are identical, but NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL will + * report them as different. + * + * The result shall be identical to call first nm_platform_ip_route_normalize() + * on both routes and then doing a full comparison. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY, + + /* compare all fields. This should have the same effect as memcmp(), + * except allowing for undefined data in holes between field alignment. + */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, + +} NMPlatformIPRouteCmpType; + typedef enum { /*< skip >*/ /* dummy value, to enforce that the enum type is signed and has a size @@ -91,6 +165,8 @@ typedef enum { /*< skip >*/ NM_PLATFORM_ERROR_NOT_SLAVE, NM_PLATFORM_ERROR_NO_FIRMWARE, NM_PLATFORM_ERROR_OPNOTSUPP, + NM_PLATFORM_ERROR_NETLINK, + NM_PLATFORM_ERROR_CANT_SET_MTU, } NMPlatformError; #define NM_PLATFORM_LINK_OTHER_NETNS (-1) @@ -172,21 +248,9 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -typedef enum { /*< skip >*/ - NM_PLATFORM_GET_ROUTE_FLAGS_NONE = 0, - - /* Whether to include default-routes/non-default-routes. Omitting - * both WITH_DEFAULT and WITH_NON_DEFAULT, is equal to specifying - * both of them. */ - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT = (1LL << 0), - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT = (1LL << 1), - - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL = (1LL << 2), -} NMPlatformGetRouteFlags; - -typedef struct { +struct _NMPlatformObject { __NMPlatformObject_COMMON; -} NMPlatformObject; +}; #define __NMPlatformIPAddress_COMMON \ @@ -298,7 +362,15 @@ typedef union { /* The NMIPConfigSource. For routes that we receive from cache this corresponds * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values). * When adding a route, the source will be coerced to the protocol using - * nmp_utils_ip_config_source_coerce_to_rtprot(). */ \ + * nmp_utils_ip_config_source_coerce_to_rtprot(). + * + * rtm_protocol is part of the primary key of an IPv4 route (meaning, you can add + * two IPv4 routes that only differ in their rtm_protocol. For IPv6, that is not + * the case. + * + * When deleting an IPv4/IPv6 route, the rtm_protocol field must match (even + * if it is not part of the primary key for IPv6) -- unless rtm_protocol is set + * to zero, in which case the first matching route (with proto ignored) is deleted. */ \ NMIPConfigSource rt_source; \ \ guint8 plen; \ @@ -308,21 +380,58 @@ typedef union { * of platform users. This flag is internal to track those hidden * routes. Such a route is not alive, according to nmp_object_is_alive(). */ \ bool rt_cloned:1; \ + \ + \ + /* RTA_METRICS: + * + * For IPv4 routes, these properties are part of their + * ID (meaning: you can add otherwise idential IPv4 routes that + * only differ by the metric property). + * On the other hand, for IPv6 you cannot add two IPv6 routes that only differ + * by an RTA_METRICS property. + * + * When deleting a route, kernel seems to ignore the RTA_METRICS propeties. + * That is a problem/bug for IPv4 because you cannot explicitly select which + * route to delete. Kernel just picks the first. See rh#1475642. */ \ + \ + /* RTA_METRICS.RTAX_LOCK (iproute2: "lock" arguments) */ \ bool lock_window:1; \ bool lock_cwnd:1; \ bool lock_initcwnd:1; \ bool lock_initrwnd:1; \ bool lock_mtu:1; \ \ - guint32 metric; \ + /* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */ \ guint32 mss; \ - guint32 tos; \ + \ + /* RTA_METRICS.RTAX_WINDOW (iproute2: window) */ \ guint32 window; \ + \ + /* RTA_METRICS.RTAX_CWND (iproute2: cwnd) */ \ guint32 cwnd; \ + \ + /* RTA_METRICS.RTAX_INITCWND (iproute2: initcwnd) */ \ guint32 initcwnd; \ + \ + /* RTA_METRICS.RTAX_INITRWND (iproute2: initrwnd) */ \ guint32 initrwnd; \ + \ + /* RTA_METRICS.RTAX_MTU (iproute2: mtu) */ \ guint32 mtu; \ - ; + \ + \ + /* RTA_PRIORITY (iproute2: metric) */ \ + guint32 metric; \ + \ + /* rtm_table, RTA_TABLE. + * + * This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and + * zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main + * table. Use nm_platform_route_table_coerce()/nm_platform_route_table_uncoerce(). */ \ + guint32 table_coerced; \ + \ + /*end*/ + typedef struct { __NMPlatformIPRoute_COMMON; @@ -332,30 +441,89 @@ typedef struct { }; } NMPlatformIPRoute; +#if _NM_CC_SUPPORT_GENERIC +#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ + (_Generic ((route), \ + const NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const void *: ((const NMPlatformIPRoute *) (route))->plen, \ + void *: ((const NMPlatformIPRoute *) (route))->plen) == 0) +#else #define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ ( ((const NMPlatformIPRoute *) (route))->plen <= 0 ) +#endif struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; in_addr_t network; - in_addr_t gateway; - /* The bitwise inverse of the route scope. It is inverted so that the - * default value (RT_SCOPE_NOWHERE) is nul. */ - guint8 scope_inv; + /* RTA_GATEWAY. The gateway is part of the primary key for a route */ + in_addr_t gateway; - /* RTA_PREFSRC/rtnl_route_get_pref_src(). A value of zero means that - * no pref-src is set. */ + /* RTA_PREFSRC (called "src" by iproute2). + * + * pref_src is part of the ID of an IPv4 route. When deleting a route, + * pref_src must match, unless set to 0.0.0.0 to match any. */ in_addr_t pref_src; + + /* rtm_tos (iproute2: tos) + * + * For IPv4, tos is part of the weak-id (like metric). + * + * For IPv6, tos is ignored by kernel. */ + guint8 tos; + + /* The bitwise inverse of the route scope rtm_scope. It is inverted so that the + * default value (RT_SCOPE_NOWHERE) is zero. Use nm_platform_route_scope_inv() + * to convert back and forth between the inverese representation and the + * real value. + * + * rtm_scope is part of the primary key for IPv4 routes. When deleting a route, + * the scope must match, unless it is left at RT_SCOPE_NOWHERE, in which case the first + * matching route is deleted. + * + * For IPv6 routes, the scope is ignored and kernel always assumes global scope. + * Hence, this field is only in NMPlatformIP4Route. */ + guint8 scope_inv; }; struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; struct in6_addr network; + + /* RTA_GATEWAY. The gateway is part of the primary key for a route */ struct in6_addr gateway; + + /* RTA_PREFSRC (called "src" by iproute2). + * + * pref_src is not part of the ID for an IPv6 route. You cannot add two + * routes that only differ by pref_src. + * + * When deleting a route, pref_src is ignored by kernel. */ struct in6_addr pref_src; + + /* RTA_SRC and rtm_src_len (called "from" by iproute2). + * + * Kernel clears the host part of src/src_plen. + * + * src/src_plen is part of the ID of a route just like network/plen. That is, + * Not only `ip route append`, but also `ip route add` allows to add routes that only + * differ in their src/src_plen. + */ struct in6_addr src; guint8 src_plen; + + /* RTA_PREF router preference. + * + * The type is guint8 to keep the struct size small. But the values are compatible with + * the NMIcmpv6RouterPref enum. */ + guint8 rt_pref; }; typedef union { @@ -372,14 +540,11 @@ typedef union { typedef struct { gboolean is_ip4; + NMPObjectType obj_type; int addr_family; gsize sizeof_route; - int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part); + int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); - GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); - gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); - gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); - gboolean (*route_delete_default) (NMPlatform *self, int ifindex, guint32 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; @@ -426,7 +591,7 @@ typedef struct { typedef struct { int parent_ifindex; - guint64 sci; /* host byte order */ + guint64 sci; /* host byte order */ guint64 cipher_suite; guint32 window; guint8 icv_length; @@ -452,11 +617,11 @@ typedef struct { in_addr_t local; in_addr_t remote; int parent_ifindex; + guint16 flags; guint8 ttl; guint8 tos; guint8 proto; bool path_mtu_discovery:1; - guint16 flags; } NMPlatformLnkSit; typedef struct { @@ -501,6 +666,12 @@ typedef enum { NM_PLATFORM_LINK_DUPLEX_FULL, } NMPlatformLinkDuplexType; +typedef enum { + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS = (1LL << 0), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL = (1LL << 1), + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF = (1LL << 2), +} NMPlatformKernelSupportFlags; + /*****************************************************************************/ struct _NMPlatformPrivate; @@ -517,22 +688,14 @@ typedef struct { gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); - const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); - const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); - const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); - - const NMPObject *(*link_get_lnk) (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); - - GArray *(*link_get_all) (NMPlatform *); gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); - const char *(*link_get_type_name) (NMPlatform *, int ifindex); - gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); gboolean (*link_refresh) (NMPlatform *, int ifindex); @@ -556,7 +719,8 @@ typedef struct { guint8 *buf, size_t *length); NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); + NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); + gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); @@ -641,8 +805,6 @@ typedef struct { gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); - GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex); - GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); gboolean (*ip4_address_add) (NMPlatform *, int ifindex, in_addr_t address, @@ -662,20 +824,21 @@ typedef struct { guint32 flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); - const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route); - gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route); - gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); - gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); - const NMPlatformIP6Route *(*ip6_route_get) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - - gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); - gboolean (*check_support_user_ipv6ll) (NMPlatform *); + + NMPlatformError (*ip_route_add) (NMPlatform *, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route); + gboolean (*ip_route_delete) (NMPlatform *, const NMPObject *obj); + + NMPlatformError (*ip_route_get) (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); + + NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self, + NMPlatformKernelSupportFlags request_flags); } NMPlatformClass; /* NMPlatform signals @@ -709,6 +872,72 @@ NMPlatform *nm_platform_get (void); /*****************************************************************************/ /** + * nm_platform_route_table_coerce: + * @table: the route table, in its original value as received + * from rtm_table/RTA_TABLE. + * + * Returns: returns the coerced table id, that can be stored in + * NMPlatformIPRoute.table_coerced. + */ +static inline guint32 +nm_platform_route_table_coerce (guint32 table) +{ + /* For kernel, the default table is RT_TABLE_MAIN (254). + * We want that in NMPlatformIPRoute.table_coerced a numeric + * zero is the default. Hence, @table_coerced swaps the + * value 0 and 254. Use nm_platform_route_table_coerce() + * and nm_platform_route_table_uncoerce() to convert between + * the two domains. */ + switch (table) { + case 0 /* RT_TABLE_UNSPEC */: + return 254; + case 254 /* RT_TABLE_MAIN */: + return 0; + default: + return table; + } +} + +/** + * nm_platform_route_table_uncoerce: + * @table: the route table, in its coerced value + * @normalize: whether to normalize RT_TABLE_UNSPEC to + * RT_TABLE_MAIN. For kernel, routes with a table id + * RT_TABLE_UNSPEC do not exist and are treated like + * RT_TABLE_MAIN. + * + * Returns: reverts the coerced table ID in NMPlatformIPRoute.table_coerced + * to the original value as kernel understands it. + */ +static inline guint32 +nm_platform_route_table_uncoerce (guint32 table_coerced, gboolean normalize) +{ + /* this undoes nm_platform_route_table_coerce(). */ + switch (table_coerced) { + case 0 /* RT_TABLE_UNSPEC */: + return 254; + case 254 /* RT_TABLE_MAIN */: + return normalize ? 254 : 0; + default: + return table_coerced; + } +} + +static inline gboolean +nm_platform_route_table_is_main (guint32 table) +{ + /* same as + * nm_platform_route_table_uncoerce (table, TRUE) == RT_TABLE_MAIN + * and + * nm_platform_route_table_uncoerce (nm_platform_route_table_coerce (table), TRUE) == RT_TABLE_MAIN + * + * That is, the function operates the same on @table and its coerced + * form. + */ + return table == 0 || table == 254; +} + +/** * nm_platform_route_scope_inv: * @scope: the route scope, either its original value, or its inverse. * @@ -725,6 +954,7 @@ _nm_platform_uint8_inv (guint8 scope) return (guint8) ~scope; } +gboolean nm_platform_get_use_udev (NMPlatform *self); gboolean nm_platform_get_log_with_ptr (NMPlatform *self); NMPNetns *nm_platform_netns_get (NMPlatform *self); @@ -732,8 +962,11 @@ gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); -const char *_nm_platform_error_to_string (NMPlatformError error); -#define nm_platform_error_to_string(error) NM_UTILS_LOOKUP_STR (_nm_platform_error_to_string, error) +const char *nm_platform_error_to_string (NMPlatformError error, + char *buf, + gsize buf_len); +#define nm_platform_error_to_string_a(error) \ + (nm_platform_error_to_string ((error), g_alloca (30), 30)) #define NMP_SYSCTL_PATHID_ABSOLUTE(path) \ ((const char *) NULL), -1, (path) @@ -759,19 +992,39 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); +const NMPObject *nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only); const NMPlatformLink *nm_platform_link_get (NMPlatform *self, int ifindex); const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname); const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length); -GArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); +GPtrArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_veth_add (NMPlatform *self, const char *name, const char *peer, const NMPlatformLink **out_link); + gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd); +struct _NMDedupMultiHeadEntry; +struct _NMPLookup; +const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, + const struct _NMPLookup *lookup); + +gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, + gpointer user_data); +gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data); + +GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, + const struct _NMPLookup *lookup, + NMPObjectPredicateFunc predicate, + gpointer user_data); + /* convienience methods to lookup the link and access fields of NMPlatformLink. */ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); @@ -808,7 +1061,8 @@ gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtils gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); -gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); +NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); +gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); @@ -938,9 +1192,8 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self, const NMPlatformLnkSit *props, const NMPlatformLink **out_link); -const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -GArray *nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex); -GArray *nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address); + gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, in_addr_t address, @@ -960,18 +1213,44 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self, guint32 flags); gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses); -gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local); -gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); - -const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); -const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); -GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route); -gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route); -gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); -gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse); +gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local); +gboolean nm_platform_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex); + +void nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route); + +NMPlatformError nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route); +NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); + +gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route); + +GPtrArray *nm_platform_ip_route_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex, + NMIPRouteTableSyncMode route_table_sync); + +gboolean nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + GPtrArray *routes_prune, + GPtrArray **out_temporary_not_available); + +gboolean nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex); + +NMPlatformError nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); @@ -1006,23 +1285,39 @@ int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVla int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); -int nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part); -int nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part); + +int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); +int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); static inline int -nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) +nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) { - return nm_platform_ip4_route_cmp_full (a, b, TRUE); + return nm_platform_ip4_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline int -nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) +nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) { - return nm_platform_ip6_route_cmp_full (a, b, TRUE); + return nm_platform_ip6_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } -gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); -gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); +void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h); +void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h); +void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h); +void nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h); +void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h); +void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h); +void nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState *h); +void nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h); +void nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h); +void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h); +void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h); +void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h); + +NMPlatformKernelSupportFlags nm_platform_check_kernel_support (NMPlatform *self, + NMPlatformKernelSupportFlags request_flags); const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); @@ -1035,4 +1330,10 @@ gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMS gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist); + +struct _NMDedupMultiIndex *nm_platform_get_multi_idx (NMPlatform *self); + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index 4acd4761..34215828 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -299,7 +299,7 @@ _netns_new (GError **error) g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "Failed opening mntns: %s", g_strerror (errsv)); - close (fd_net); + nm_close (fd_net); return NULL; } @@ -620,7 +620,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd) filename, g_strerror (errsv)); return FALSE; } - close (fd); + nm_close (fd); if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { errsv = errno; @@ -702,15 +702,11 @@ dispose (GObject *object) NMPNetns *self = NMP_NETNS (object); NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE (self); - if (priv->fd_net > 0) { - close (priv->fd_net); - priv->fd_net = 0; - } + nm_close (priv->fd_net); + priv->fd_net = -1; - if (priv->fd_mnt > 0) { - close (priv->fd_mnt); - priv->fd_mnt = 0; - } + nm_close (priv->fd_mnt); + priv->fd_mnt = -1; G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object); } diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h index 56c1e7e8..55a4b95f 100644 --- a/src/platform/nmp-netns.h +++ b/src/platform/nmp-netns.h @@ -60,8 +60,7 @@ _nm_auto_pop_netns (NMPNetns **p) errno = errsv; } } - -#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns))) +#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) gboolean nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd); gboolean nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index ecec8f0f..a8600705 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -51,6 +51,11 @@ /*****************************************************************************/ +typedef struct { + NMDedupMultiIdxType parent; + NMPCacheIdType cache_id_type; +} DedupMultiIdxType; + struct _NMPCache { /* the cache contains only one hash table for all object types, and similarly * it contains only one NMMultiIndex. @@ -66,23 +71,238 @@ struct _NMPCache { * This effectively merges the udev-device cache into the NMPCache. */ - GHashTable *idx_main; - NMMultiIndex *idx_multi; + NMDedupMultiIndex *multi_idx; + + /* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero) + * is skipped, so the index is shifted by one: idx_type[cache_id_type - 1]. + * + * Don't bother, use _idx_type_get() instead! */ + DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX]; gboolean use_udev; }; /*****************************************************************************/ -static inline guint -_id_hash_ip6_addr (const struct in6_addr *addr) +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class; + +static void +_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) { - guint hash = (guint) 0x897da53981a13ULL; - int i; + const NMPObject *o = (NMPObject *) obj; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o) != NMP_OBJECT_TYPE_UNKNOWN); + + nmp_object_id_hash_update (o, h); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const NMPObject *o_a = (NMPObject *) obj_a; + const NMPObject *o_b = (NMPObject *) obj_b; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (NMP_OBJECT_GET_TYPE (o_b) != NMP_OBJECT_TYPE_UNKNOWN); - for (i = 0; i < sizeof (*addr); i++) - hash = (hash * 33) + ((const guint8 *) addr)[i]; - return hash; + return nmp_object_id_equal (o_a, o_b); +} + +static guint +_idx_obj_part (const DedupMultiIdxType *idx_type, + const NMPObject *obj_a, + const NMPObject *obj_b, + NMHashState *h) +{ + NMPObjectType obj_type; + + /* the hash/equals functions are strongly related. So, keep them + * side-by-side and do it all in _idx_obj_part(). */ + + nm_assert (idx_type); + nm_assert (idx_type->parent.klass == &_dedup_multi_idx_type_class); + nm_assert (obj_a); + nm_assert (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_b || (NMP_OBJECT_GET_TYPE (obj_b) != NMP_OBJECT_TYPE_UNKNOWN)); + nm_assert (!h || !obj_b); + + switch (idx_type->cache_id_type) { + + case NMP_CACHE_ID_TYPE_OBJECT_TYPE: + if (obj_b) + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b); + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + NMP_OBJECT_GET_TYPE (obj_a)); + } + return 1; + + case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: + if (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_LINK) { + /* first check, whether obj_a is suitable for this idx_type. + * If not, return 0 (which is correct for partitionable(), hash() and equal() + * functions. */ + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + /* we are in equal() mode. Compare obj_b with obj_a. */ + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_LINK + && nm_streq (obj_a->link.name, obj_b->link.name); + } + if (h) { + nm_hash_update_val (h, idx_type->cache_id_type); + nm_hash_update_strarr (h, obj_a->link.name); + } + /* just return 1, to indicate that obj_a is partitionable by this idx_type. */ + return 1; + + case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_a->ip_route) + || !nmp_object_is_visible (obj_a)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_b->ip_route) + && nmp_object_is_visible (obj_b); + } + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + NMP_OBJECT_GET_TYPE (obj_a)); + } + return 1; + + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !nmp_object_is_visible (obj_a)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + nm_assert (obj_a->object.ifindex > 0); + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && obj_a->object.ifindex == obj_b->object.ifindex + && nmp_object_is_visible (obj_b); + } + if (h) { + nm_hash_update_vals (h, + idx_type->cache_id_type, + obj_a->object.ifindex); + } + return 1; + + case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + if ( !NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || obj_a->object.ifindex <= 0) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return obj_type == NMP_OBJECT_GET_TYPE (obj_b) + && obj_b->object.ifindex > 0 + && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + ? (nm_platform_ip4_route_cmp (&obj_a->ip4_route, &obj_b->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0) + : (nm_platform_ip6_route_cmp (&obj_a->ip6_route, &obj_b->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0)); + } + if (h) { + nm_hash_update_val (h, idx_type->cache_id_type); + if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + nm_platform_ip4_route_hash_update (&obj_a->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, h); + else + nm_platform_ip6_route_hash_update (&obj_a->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, h); + } + return 1; + + case NMP_CACHE_ID_TYPE_NONE: + case __NMP_CACHE_ID_TYPE_MAX: + break; + } + nm_assert_not_reached (); + return 0; +} + +static gboolean +_idx_obj_partitionable (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj, + NULL, + NULL) != 0; +} + +static void +_idx_obj_partition_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) +{ + _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj, + NULL, + h); +} + +static gboolean +_idx_obj_partition_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + (NMPObject *) obj_a, + (NMPObject *) obj_b, + NULL); +} + +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = { + .idx_obj_id_hash_update = _idx_obj_id_hash_update, + .idx_obj_id_equal = _idx_obj_id_equal, + .idx_obj_partitionable = _idx_obj_partitionable, + .idx_obj_partition_hash_update = _idx_obj_partition_hash_update, + .idx_obj_partition_equal = _idx_obj_partition_equal, +}; + +static void +_dedup_multi_idx_type_init (DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type) +{ + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &_dedup_multi_idx_type_class); + idx_type->cache_id_type = cache_id_type; +} + +/*****************************************************************************/ + +static void +_vlan_xgress_qos_mappings_hash_update (guint n_map, + const NMVlanQosMapping *map, + NMHashState *h) +{ + /* ensure no padding. */ + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == 2 * sizeof (guint32)); + + nm_hash_update_val (h, n_map); + if (n_map) + nm_hash_update (h, map, n_map * sizeof (*map)); } static int @@ -150,12 +370,18 @@ _link_get_driver (struct udev_device *udevice, const char *kind, int ifindex) } void -_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) +_nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev) { const char *driver = NULL; gboolean initialized = FALSE; + NMPObject *obj; - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; /* The link contains internal fields that are combined by * properties from netlink and udev. Update those properties */ @@ -179,17 +405,34 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) } } + if ( nm_streq0 (obj->link.driver, driver) + && obj->link.initialized == initialized) + return; + + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); + obj->link.driver = driver; obj->link.initialized = initialized; } static void -_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache) +_nmp_object_fixup_link_master_connected (NMPObject **obj_new, NMPObject *obj_orig, const NMPCache *cache) { - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + NMPObject *obj; + + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; - if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) + if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) { + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); obj->link.connected = !obj->link.connected; + } } /*****************************************************************************/ @@ -204,33 +447,6 @@ nmp_class_from_type (NMPObjectType obj_type) /*****************************************************************************/ -NMPObject * -nmp_object_ref (NMPObject *obj) -{ - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); - g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL); - obj->_ref_count++; - - return obj; -} - -void -nmp_object_unref (NMPObject *obj) -{ - if (obj) { - g_return_if_fail (obj->_ref_count > 0); - g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT); - if (--obj->_ref_count <= 0) { - const NMPClass *klass = obj->_class; - - nm_assert (!obj->is_cached); - if (klass->cmd_obj_dispose) - klass->cmd_obj_dispose (obj); - g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), obj); - } - } -} - static void _vt_cmd_obj_dispose_link (NMPObject *obj) { @@ -259,7 +475,7 @@ _nmp_object_new_from_class (const NMPClass *klass) obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object)); obj->_class = klass; - obj->_ref_count = 1; + obj->parent._ref_count = 1; return obj; } @@ -287,14 +503,29 @@ nmp_object_new_link (int ifindex) /*****************************************************************************/ -static const NMPObject * +static void _nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) { + nm_assert (obj); + nm_assert (klass); + + memset (obj, 0, sizeof (NMPObject)); + obj->_class = klass; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; +} + +static NMPObject * +_nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type) +{ + const NMPClass *klass; + + nm_assert (obj); + klass = nmp_class_from_type (obj_type); nm_assert (klass); memset (obj, 0, sizeof (NMPObject)); obj->_class = klass; - obj->_ref_count = NMP_REF_COUNT_STACKINIT; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; } @@ -318,31 +549,24 @@ nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) nm_assert (obj); klass = NMP_OBJECT_GET_CLASS (src); - if (!klass->cmd_obj_stackinit_id) - nmp_object_stackinit (obj, klass->obj_type, NULL); - else - klass->cmd_obj_stackinit_id (obj, src); + _nmp_object_stackinit_from_class (obj, klass); + if (klass->cmd_plobj_id_copy) + klass->cmd_plobj_id_copy (&obj->object, &src->object); return obj; } const NMPObject * nmp_object_stackinit_id_link (NMPObject *obj, int ifindex) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_LINK, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_LINK); obj->link.ifindex = ifindex; return obj; } -static void -_vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_link (obj, src->link.ifindex); -} - const NMPObject * nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP4_ADDRESS); obj->ip4_address.ifindex = ifindex; obj->ip4_address.address = address; obj->ip4_address.plen = plen; @@ -350,64 +574,16 @@ nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 addres return obj; } -static void -_vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen, src->ip4_address.peer_address); -} - const NMPObject * -nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen) +nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP6_ADDRESS); obj->ip4_address.ifindex = ifindex; if (address) obj->ip6_address.address = *address; - obj->ip6_address.plen = plen; return obj; } -static void -_vt_cmd_obj_stackinit_id_ip6_address (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip6_address (obj, src->ip_address.ifindex, &src->ip6_address.address, src->ip_address.plen); -} - -const NMPObject * -nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric) -{ - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - obj->ip4_route.ifindex = ifindex; - obj->ip4_route.network = network; - obj->ip4_route.plen = plen; - obj->ip4_route.metric = metric; - return obj; -} - -static void -_vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip4_route (obj, src->ip_route.ifindex, src->ip4_route.network, src->ip_route.plen, src->ip_route.metric); -} - -const NMPObject * -nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric) -{ - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - obj->ip6_route.ifindex = ifindex; - if (network) - obj->ip6_route.network = *network; - obj->ip6_route.plen = plen; - obj->ip6_route.metric = metric; - return obj; -} - -static void -_vt_cmd_obj_stackinit_id_ip6_route (NMPObject *obj, const NMPObject *src) -{ - nmp_object_stackinit_id_ip6_route (obj, src->ip_route.ifindex, &src->ip6_route.network, src->ip_route.plen, src->ip_route.metric); -} - /*****************************************************************************/ const char * @@ -435,9 +611,8 @@ nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2))); @@ -462,9 +637,8 @@ _vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_strin return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', @@ -503,9 +677,8 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_s case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); @@ -562,10 +735,53 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz _vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen, obj->peer_address != obj->address ? "," : "", - obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : ""); + obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen), buf2) : ""); _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); -_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); -_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); + +void +nmp_object_hash_update (const NMPObject *obj, NMHashState *h) +{ + const NMPClass *klass; + + g_return_if_fail (NMP_OBJECT_IS_VALID (obj)); + + klass = NMP_OBJECT_GET_CLASS (obj); + + nm_hash_update_val (h, klass->obj_type); + if (klass->cmd_obj_hash_update) + klass->cmd_obj_hash_update (obj, h); + else if (klass->cmd_plobj_hash_update) + klass->cmd_plobj_hash_update (&obj->object, h); + else + nm_hash_update_val (h, obj); +} + +static void +_vt_cmd_obj_hash_update_link (const NMPObject *obj, NMHashState *h) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + + nm_platform_link_hash_update (&obj->link, h); + nm_hash_update_vals (h, + obj->_link.netlink.is_in_netlink, + obj->_link.udev.device); + if (obj->_link.netlink.lnk) + nmp_object_hash_update (obj->_link.netlink.lnk, h); +} + +static void +_vt_cmd_obj_hash_update_lnk_vlan (const NMPObject *obj, NMHashState *h) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LNK_VLAN); + + nm_platform_lnk_vlan_hash_update (&obj->lnk_vlan, h); + _vlan_xgress_qos_mappings_hash_update (obj->_lnk_vlan.n_ingress_qos_map, + obj->_lnk_vlan.ingress_qos_map, + h); + _vlan_xgress_qos_mappings_hash_update (obj->_lnk_vlan.n_egress_qos_map, + obj->_lnk_vlan.egress_qos_map, + h); +} int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) @@ -585,8 +801,10 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) klass1 = NMP_OBJECT_GET_CLASS (obj1); klass2 = NMP_OBJECT_GET_CLASS (obj2); - if (klass1 != klass2) + if (klass1 != klass2) { + nm_assert (klass1->obj_type != klass2->obj_type); return klass1->obj_type < klass2->obj_type ? -1 : 1; + } if (klass1->cmd_obj_cmp) return klass1->cmd_obj_cmp (obj1, obj2); @@ -731,16 +949,12 @@ _vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, { dst->address = src->address; }); _vt_cmd_plobj_id_copy (ip4_route, NMPlatformIP4Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; + *dst = *src; + nm_assert (nm_platform_ip4_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; + *dst = *src; + nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); /* Uses internally nmp_object_copy(), hence it also violates the const @@ -761,127 +975,157 @@ nmp_object_clone (const NMPObject *obj, gboolean id_only) return dst; } -gboolean -nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +int +nmp_object_id_cmp (const NMPObject *obj1, const NMPObject *obj2) { - const NMPClass *klass; + const NMPClass *klass, *klass2; - if (obj1 == obj2) - return TRUE; - if (!obj1 || !obj2) - return FALSE; + NM_CMP_SELF (obj1, obj2); g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); klass = NMP_OBJECT_GET_CLASS (obj1); - return klass == NMP_OBJECT_GET_CLASS (obj2) - && klass->cmd_plobj_id_equal - && klass->cmd_plobj_id_equal (&obj1->object, &obj2->object); + nm_assert (!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); + + klass2 = NMP_OBJECT_GET_CLASS (obj2); + nm_assert (klass); + if (klass != klass2) { + nm_assert (klass2); + NM_CMP_DIRECT (klass->obj_type, klass2->obj_type); + /* resort to pointer comparison */ + if (klass < klass2) + return -1; + return 1; + } + + if (!klass->cmd_plobj_id_cmp) { + /* the klass doesn't implement ID cmp(). That means, different objects + * never compare equal, but the cmp() according to their pointer value. */ + return (obj1 < obj2) ? -1 : 1; + } + + return klass->cmd_plobj_id_cmp (&obj1->object, &obj2->object); } -#define _vt_cmd_plobj_id_equal(type, plat_type, cmd) \ -static gboolean \ -_vt_cmd_plobj_id_equal_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \ +#define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \ +static int \ +_vt_cmd_plobj_id_cmp_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \ { \ const plat_type *const obj1 = (const plat_type *) _obj1; \ const plat_type *const obj2 = (const plat_type *) _obj2; \ - return (cmd); \ -} -_vt_cmd_plobj_id_equal (link, NMPlatformLink, - obj1->ifindex == obj2->ifindex); -_vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->address == obj2->address - /* for IPv4 addresses, you can add the same local address with differing peer-adddress - * (IFA_ADDRESS), provided that their net-part differs. */ - && ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0); -_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, - obj1->ifindex == obj2->ifindex - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - && IN6_ARE_ADDR_EQUAL (&obj1->address, &obj2->address)); -_vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->metric == obj2->metric - && nm_utils_ip4_address_clear_host_address (obj1->network, obj1->plen) == nm_utils_ip4_address_clear_host_address (obj2->network, obj2->plen)); -_vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, - obj1->ifindex == obj2->ifindex - && obj1->plen == obj2->plen - && obj1->metric == obj2->metric - && ({ - struct in6_addr n1, n2; - - IN6_ARE_ADDR_EQUAL(nm_utils_ip6_address_clear_host_address (&n1, &obj1->network, obj1->plen), - nm_utils_ip6_address_clear_host_address (&n2, &obj2->network, obj2->plen)); - })); + \ + NM_CMP_SELF (obj1, obj2); \ + { cmd; } \ + return 0; \ +} +_vt_cmd_plobj_id_cmp (link, NMPlatformLink, + NM_CMP_FIELD (obj1, obj2, ifindex); +) +_vt_cmd_plobj_id_cmp (ip4_address, NMPlatformIP4Address, + NM_CMP_FIELD (obj1, obj2, ifindex); + NM_CMP_FIELD (obj1, obj2, plen); + NM_CMP_FIELD (obj1, obj2, address); + /* for IPv4 addresses, you can add the same local address with differing peer-adddress + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (obj1->peer_address, obj2->peer_address, obj1->plen); +) +_vt_cmd_plobj_id_cmp (ip6_address, NMPlatformIP6Address, + NM_CMP_FIELD (obj1, obj2, ifindex); + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + NM_CMP_FIELD_IN6ADDR (obj1, obj2, address); +) -guint -nmp_object_id_hash (const NMPObject *obj) +static int +_vt_cmd_plobj_id_cmp_ip4_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - const NMPClass *klass; + return nm_platform_ip4_route_cmp ((NMPlatformIP4Route *) obj1, (NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); +} - if (!obj) - return 0; +static int +_vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); +} + +void +nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h) +{ + const NMPClass *klass; - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); + g_return_if_fail (NMP_OBJECT_IS_VALID (obj)); klass = NMP_OBJECT_GET_CLASS (obj); - if (klass->cmd_plobj_id_hash) - return klass->cmd_plobj_id_hash (&obj->object); + nm_assert (!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); + + if (!klass->cmd_plobj_id_hash_update) { + /* The klass doesn't implement ID compare. It means, to use pointer + * equality. */ + nm_hash_update_val (h, obj); + return; + } - /* unhashable objects implement pointer equality. */ - return g_direct_hash (obj); + nm_hash_update_val (h, klass->obj_type); + klass->cmd_plobj_id_hash_update (&obj->object, h); } -#define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ -static guint \ -_vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ +guint +nmp_object_id_hash (const NMPObject *obj) +{ + NMHashState h; + + if (!obj) + return 0; + + nm_hash_init (&h, 914932607u); + nmp_object_id_hash_update (obj, &h); + return nm_hash_complete (&h); +} + +#define _vt_cmd_plobj_id_hash_update(type, plat_type, cmd) \ +static void \ +_vt_cmd_plobj_id_hash_update_##type (const NMPlatformObject *_obj, NMHashState *h) \ { \ const plat_type *const obj = (const plat_type *) _obj; \ - guint hash; \ { cmd; } \ - return hash; \ } -_vt_cmd_plobj_id_hash (link, NMPlatformLink, { - hash = (guint) 3982791431u; - hash = hash + ((guint) obj->ifindex); +_vt_cmd_plobj_id_hash_update (link, NMPlatformLink, { + nm_hash_update_val (h, obj->ifindex); }) -_vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { - hash = (guint) 3591309853u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->address); - - /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); +_vt_cmd_plobj_id_hash_update (ip4_address, NMPlatformIP4Address, { + nm_hash_update_vals (h, + obj->ifindex, + obj->plen, + obj->address, + /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ + nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen)); }) -_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { - hash = (guint) 2907861637u; - hash = hash + ((guint) obj->ifindex); - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - hash = hash * 33 + _id_hash_ip6_addr (&obj->address); +_vt_cmd_plobj_id_hash_update (ip6_address, NMPlatformIP6Address, { + nm_hash_update_vals (h, + obj->ifindex, + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + obj->address); }) -_vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { - hash = (guint) 2569857221u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ((guint) nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); +_vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, { + nm_platform_ip4_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) -_vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { - hash = (guint) 3999787007u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ({ - struct in6_addr n1; - _id_hash_ip6_addr (nm_utils_ip6_address_clear_host_address (&n1, &obj->network, obj->plen)); - }); +_vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, { + nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) +static inline void +_vt_cmd_plobj_hash_update_ip4_route (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_ip4_route_hash_update ((const NMPlatformIP4Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); +} + +static inline void +_vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_ip6_route_hash_update ((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); +} + gboolean nmp_object_is_alive (const NMPObject *obj) { @@ -958,365 +1202,92 @@ _vt_cmd_obj_is_visible_link (const NMPObject *obj) /*****************************************************************************/ -_NM_UTILS_LOOKUP_DEFINE (static, _nmp_cache_id_size_by_type, NMPCacheIdType, guint, - NM_UTILS_LOOKUP_DEFAULT (({ nm_assert_not_reached (); (guint) 0; })), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, nm_offsetofend (NMPCacheId, link_by_ifname)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, nm_offsetofend (NMPCacheId, routes_by_destination_ip4)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, nm_offsetofend (NMPCacheId, routes_by_destination_ip6)), - NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_CACHE_ID_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE (__NMP_CACHE_ID_TYPE_MAX), -); - -gboolean -nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) -{ - if (a->_id_type != b->_id_type) - return FALSE; - return memcmp (a, b, _nmp_cache_id_size_by_type (a->_id_type)) == 0; -} - -guint -nmp_cache_id_hash (const NMPCacheId *id) -{ - guint hash = 5381; - guint i, n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - for (i = 0; i < n; i++) - hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */ - return hash; -} - -NMPCacheId * -nmp_cache_id_clone (const NMPCacheId *id) -{ - NMPCacheId *id2; - guint n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - id2 = g_slice_alloc (n); - memcpy (id2, id, n); - return id2; -} +static const guint8 _supported_cache_ids_link[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, + 0, +}; -void -nmp_cache_id_destroy (NMPCacheId *id) -{ - guint n; +static const guint8 _supported_cache_ids_ipx_address[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + 0, +}; - n = _nmp_cache_id_size_by_type (id->_id_type); - g_slice_free1 (n, id); -} +static const guint8 _supported_cache_ids_ipx_route[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + 0, +}; /*****************************************************************************/ static void -_nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type) -{ - /* there is no need to set the entire @id to zero when - * initializing the ID. - * - * First, depending on the @id_type only part of the - * @id is actually used (_nmp_cache_id_size_by_type). - * - * Second, the nmp_cache_id_init_*() *MUST* anyway make sure - * that all relevant fields are set. Since it happens that - * all structs have the packed attribute, there are no holes - * due to alignment, and it becomes simple for nmp_cache_id_init_*() - * to ensure that all fields are set. */ - -#if NM_MORE_ASSERTS - nm_assert (id); - { - guint i; - - /* initialized with some bogus canary to hopefully detect when we miss - * to initialize a field of the cache-id. */ - for (i = 0; i < sizeof (*id); i++) { - ((char *) id)[i] = GPOINTER_TO_UINT (id) ^ i; - } - } -#endif - - id->_id_type = id_type; -} - -NMPCacheId * -nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only) +_vt_dedup_obj_destroy (NMDedupMultiObj *obj) { - _nmp_cache_id_init (id, visible_only - ? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY - : NMP_CACHE_ID_TYPE_OBJECT_TYPE); - id->object_type.obj_type = obj_type; - return id; -} - -NMPCacheId * -nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, - NMPObjectType obj_type, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, - NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX); - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_routes_visible (NMPCacheId *id, - NMPObjectType obj_type, - gboolean with_default, - gboolean with_non_default, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (with_default) { - if (with_non_default) { - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex); - } - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT); - } else if (with_non_default) - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT); - else - g_return_val_if_reached (NULL); - - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_link_by_ifname (NMPCacheId *id, - const char *ifname) -{ - gsize l; - - if ( !ifname - || (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short)) - g_return_val_if_reached (id); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME); + NMPObject *o = (NMPObject *) obj; + const NMPClass *klass; - memset (id->link_by_ifname.ifname_short, 0, sizeof (id->link_by_ifname.ifname_short)); - /* the trailing NUL is dropped!! */ - memcpy (id->link_by_ifname.ifname_short, ifname, l); + nm_assert (o->parent._ref_count == 0); + nm_assert (!o->parent._multi_idx); - return id; + klass = o->_class; + if (klass->cmd_obj_dispose) + klass->cmd_obj_dispose (o); + g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), o); } -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, - guint32 network, - guint8 plen, - guint32 metric) +static const NMDedupMultiObj * +_vt_dedup_obj_clone (const NMDedupMultiObj *obj) { - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip4._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip4._misaligned_network, &network, sizeof (guint32)); - return id; + return (const NMDedupMultiObj *) nmp_object_clone ((const NMPObject *) obj, FALSE); } -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, - const struct in6_addr *network, - guint8 plen, - guint32 metric) -{ - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip6._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip6._misaligned_network, network ?: &nm_ip_addr_zero.addr6, sizeof (struct in6_addr)); - return id; -} +#define DEDUP_MULTI_OBJ_CLASS_INIT() \ + { \ + .obj_clone = _vt_dedup_obj_clone, \ + .obj_destroy = _vt_dedup_obj_destroy, \ + .obj_full_hash_update = (void (*)(const NMDedupMultiObj *obj, NMHashState *h)) nmp_object_hash_update, \ + .obj_full_equal = (gboolean (*)(const NMDedupMultiObj *obj_a, const NMDedupMultiObj *obj_b)) nmp_object_equal, \ + } /*****************************************************************************/ -static gboolean -_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +static NMDedupMultiIdxType * +_idx_type_get (const NMPCache *cache, NMPCacheIdType cache_id_type) { - const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + nm_assert (cache); + nm_assert (cache_id_type > NMP_CACHE_ID_TYPE_NONE); + nm_assert (cache_id_type <= NMP_CACHE_ID_TYPE_MAX); + nm_assert ((int) cache_id_type - 1 >= 0); + nm_assert ((int) cache_id_type - 1 < G_N_ELEMENTS (cache->idx_types)); - switch (id_type) { - case NMP_CACHE_ID_TYPE_OBJECT_TYPE: - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE); - return TRUE; - case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY: - if (nmp_object_is_visible (obj)) - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE); - else - *out_id = NULL; - return TRUE; - default: - return klass->cmd_obj_init_cache_id - && klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); - } + return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1]; } -static const guint8 _supported_cache_ids_link[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - 0, -}; - -static gboolean -_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +gboolean +nmp_cache_use_udev_get (const NMPCache *cache) { - switch (id_type) { - case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: - if (obj->link.name[0]) { - *out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - -static const guint8 _supported_cache_ids_ipx_address[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - 0, -}; - -static gboolean -_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - -static const guint8 _supported_cache_ids_ip4_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, - 0, -}; - -static const guint8 _supported_cache_ids_ip6_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, - 0, -}; + g_return_val_if_fail (cache, TRUE); -static gboolean -_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip4 (id, obj->ip4_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip6 (id, &obj->ip6_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; + return cache->use_udev; } /*****************************************************************************/ gboolean -nmp_cache_use_udev_get (const NMPCache *cache) +nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave) { - g_return_val_if_fail (cache, TRUE); + nm_assert (NMP_OBJECT_GET_TYPE (slave) == NMP_OBJECT_TYPE_LINK); - return cache->use_udev; + return ifindex_master > 0 + && slave->link.master == ifindex_master + && slave->link.connected + && nmp_object_is_visible (slave); } -/*****************************************************************************/ - /** * nmp_cache_link_connected_needs_toggle: * @cache: the platform cache @@ -1341,9 +1312,7 @@ nmp_cache_use_udev_get (const NMPCache *cache) gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave) { - const NMPlatformLink *const *links; gboolean is_lower_up = FALSE; - guint len, i; if ( !master || NMP_OBJECT_GET_TYPE (master) != NMP_OBJECT_TYPE_LINK @@ -1361,27 +1330,23 @@ nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *m potential_slave = NULL; if ( potential_slave - && nmp_object_is_visible (potential_slave) - && potential_slave->link.ifindex > 0 - && potential_slave->link.master == master->link.ifindex - && potential_slave->link.connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, potential_slave)) is_lower_up = TRUE; - } else { - NMPCacheId cache_id; - - links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, FALSE), &len); - for (i = 0; i < len; i++) { - const NMPlatformLink *link = links[i]; + else { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *link = NULL; + + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &link) { const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link); - nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == NMP_OBJECT_TYPE_LINK); - if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex) && ignore_slave != obj - && link->ifindex > 0 - && link->master == master->link.ifindex - && nmp_object_is_visible (obj) - && link->connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, obj)) { is_lower_up = TRUE; break; } @@ -1424,95 +1389,293 @@ nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int mas /*****************************************************************************/ -const NMPlatformObject *const * -nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len) +static const NMDedupMultiEntry * +_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj) { - return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi, - (const NMMultiIndexId *) cache_id, - out_len); + const NMDedupMultiEntry *entry; + + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + entry = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, cache_id_type), + obj); + nm_assert (!entry + || ( NMP_OBJECT_IS_VALID (entry->obj) + && NMP_OBJECT_GET_CLASS (entry->obj) == NMP_OBJECT_GET_CLASS (obj))); + return entry; } -GArray * -nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id) +static const NMDedupMultiEntry * +_lookup_entry (const NMPCache *cache, const NMPObject *obj) { - const NMPClass *klass = nmp_class_from_type (obj_type); - guint len, i; - const NMPlatformObject *const *objects; - GArray *array; - - g_return_val_if_fail (klass, NULL); + return _lookup_entry_with_idx_type (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); +} - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); +const NMDedupMultiEntry * +nmp_cache_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + g_return_val_if_fail (cache, NULL); + g_return_val_if_fail (obj, NULL); + g_return_val_if_fail (cache_id_type > NMP_CACHE_ID_TYPE_NONE && cache_id_type <= NMP_CACHE_ID_TYPE_MAX, NULL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[i])) == klass); - g_array_append_vals (array, objects[i], 1); - } - return array; + return _lookup_entry_with_idx_type (cache, cache_id_type, obj); } -const NMPObject * -nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) +const NMDedupMultiEntry * +nmp_cache_lookup_entry (const NMPCache *cache, const NMPObject *obj) { + g_return_val_if_fail (cache, NULL); g_return_val_if_fail (obj, NULL); - return g_hash_table_lookup (cache->idx_main, obj); + return _lookup_entry (cache, obj); } -const NMPObject * -nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +const NMDedupMultiEntry * +nmp_cache_lookup_entry_link (const NMPCache *cache, int ifindex) { NMPObject obj_needle; - return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); + g_return_val_if_fail (cache, NULL); + g_return_val_if_fail (ifindex > 0, NULL); + + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return _lookup_entry (cache, &obj_needle); } -/** - * nmp_cache_find_other_route_for_same_destination: - * @cache: - * @route: - * - * Look into the cache whether there is a route to the same destination, - * in terms of network/plen,metric. - * - * Returns: (transfer none): the first found route object from the cache - * that has the same (network/plen,metric) values as @route, but has different - * ID. Or %NULL, if no such route exists. - */ const NMPObject * -nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route) +nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) { - NMPCacheId cache_id; - const NMPlatformObject *const *list; + return nm_dedup_multi_entry_get_obj (nmp_cache_lookup_entry (cache, obj)); +} +const NMPObject * +nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +{ + return nm_dedup_multi_entry_get_obj (nmp_cache_lookup_entry_link (cache, ifindex)); +} + +/*****************************************************************************/ + +const NMDedupMultiHeadEntry * +nmp_cache_lookup_all (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *select_obj) +{ nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (select_obj)); - switch (NMP_OBJECT_GET_TYPE (route)) { + return nm_dedup_multi_index_lookup_head (cache->multi_idx, + _idx_type_get (cache, cache_id_type), + select_obj); +} + +static const NMPLookup * +_L (const NMPLookup *lookup) +{ +#if NM_MORE_ASSERTS + DedupMultiIdxType idx_type; + + nm_assert (lookup); + _dedup_multi_idx_type_init (&idx_type, lookup->cache_id_type); + nm_assert (idx_type.parent.klass->idx_obj_partitionable ((NMDedupMultiIdxType *) &idx_type, (NMDedupMultiObj *) &lookup->selector_obj)); +#endif + return lookup; +} + +const NMPLookup * +nmp_lookup_init_obj_type (NMPLookup *lookup, + NMPObjectType obj_type) +{ + NMPObject *o; + + nm_assert (lookup); + + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: - nmp_cache_id_init_routes_by_destination_ip4 (&cache_id, route->ip4_route.network, route->ip_route.plen, route->ip_route.metric); - break; case NMP_OBJECT_TYPE_IP6_ROUTE: - nmp_cache_id_init_routes_by_destination_ip6 (&cache_id, &route->ip6_route.network, route->ip_route.plen, route->ip_route.metric); - break; + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; + return _L (lookup); default: + nm_assert_not_reached (); + return NULL; + } +} + +const NMPLookup * +nmp_lookup_init_link_by_ifname (NMPLookup *lookup, + const char *ifname) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (ifname); + nm_assert (strlen (ifname) < IFNAMSIZ); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_LINK); + if (g_strlcpy (o->link.name, ifname, sizeof (o->link.name)) >= sizeof (o->link.name)) g_return_val_if_reached (NULL); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_addrroute (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + if (ifindex <= 0) { + return nmp_lookup_init_obj_type (lookup, + obj_type); } - list = nmp_cache_lookup_multi (cache, &cache_id, NULL); - if (list) { - for (; *list; list++) { - const NMPObject *candidate = NMP_OBJECT_UP_CAST (*list); + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = ifindex; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_default (NMPLookup *lookup, + NMPObjectType obj_type) +{ + NMPObject *o; - nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (candidate)); + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - if (!nmp_object_id_equal (route, candidate)) - return candidate; - } + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = 1; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj) +{ + const NMPlatformIP4Route *r4; + const NMPlatformIP6Route *r6; + + nm_assert (lookup); + + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + return nmp_lookup_init_ip4_route_by_weak_id (lookup, + r4->network, + r4->plen, + r4->metric, + r4->tos); + case NMP_OBJECT_TYPE_IP6_ROUTE: + r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + return nmp_lookup_init_ip6_route_by_weak_id (lookup, + &r6->network, + r6->plen, + r6->metric, + &r6->src, + r6->src_plen); + default: + nm_assert_not_reached (); + return NULL; } - return NULL; } +const NMPLookup * +nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) +{ + NMPObject *o; + + nm_assert (lookup); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip4_route.network = network; + o->ip4_route.tos = tos; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPObject *o; + + nm_assert (lookup); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip6_route.network = *network; + if (src) + o->ip6_route.src = *src; + o->ip6_route.src_plen = src_plen; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; + return _L (lookup); +} + +/*****************************************************************************/ + +GArray * +nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, + NMPObjectType obj_type, + gboolean visible_only) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + NMDedupMultiIter iter; + const NMPObject *o; + GArray *array; + + g_return_val_if_fail (klass, NULL); + + array = g_array_sized_new (FALSE, FALSE, + klass->sizeof_public, + head_entry ? head_entry->len : 0); + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { + nm_assert (NMP_OBJECT_GET_CLASS (o) == klass); + if ( visible_only + && !nmp_object_is_visible (o)) + continue; + g_array_append_vals (array, &o->object, 1); + } + return array; +} + +/*****************************************************************************/ + const NMPObject * nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, @@ -1524,9 +1687,10 @@ nmp_cache_lookup_link_full (const NMPCache *cache, { NMPObject obj_needle; const NMPObject *obj; - const NMPlatformObject *const *list; - guint i, len; - NMPCacheId cache_id, *p_cache_id; + NMDedupMultiIter iter; + const NMDedupMultiHeadEntry *head_entry; + const NMPlatformLink *link = NULL; + NMPLookup lookup; if (ifindex > 0) { obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); @@ -1541,25 +1705,21 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } else if (!ifname && !match_fn) return NULL; else { - if ( ifname - && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) { - p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname); - ifname = NULL; - } else { - p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only); - visible_only = FALSE; - } + if (ifname) { + if (strlen (ifname) >= IFNAMSIZ) + return NULL; + nmp_lookup_init_link_by_ifname (&lookup, ifname); + } else + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); - list = nmp_cache_lookup_multi (cache, p_cache_id, &len); - for (i = 0; i < len; i++) { - obj = NMP_OBJECT_UP_CAST (list[i]); + head_entry = nmp_cache_lookup (cache, &lookup); + nmp_cache_iter_for_each_link (&iter, head_entry, &link) { + obj = NMP_OBJECT_UP_CAST (link); if (visible_only && !nmp_object_is_visible (obj)) continue; if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) continue; - if (ifname && strcmp (ifname, obj->link.name)) - continue; if (match_fn && !match_fn (obj, user_data)) continue; @@ -1569,455 +1729,710 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } } -GHashTable * -nmp_cache_lookup_all_to_hash (const NMPCache *cache, - NMPCacheId *cache_id, - GHashTable *hash) -{ - NMMultiIndexIdIter iter; - gpointer plobj; - - nm_multi_index_id_iter_init (&iter, cache->idx_multi, (const NMMultiIndexId *) cache_id); - - if (nm_multi_index_id_iter_next (&iter, &plobj)) { - if (!hash) - hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - do { - g_hash_table_add (hash, nmp_object_ref (NMP_OBJECT_UP_CAST (plobj))); - } while (nm_multi_index_id_iter_next (&iter, &plobj)); - } - - return hash; -} - /*****************************************************************************/ static void -_nmp_cache_update_cache (NMPCache *cache, NMPObject *obj, gboolean remove) -{ - const guint8 *id_type; - - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id; - - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; - - /* We don't put @obj itself into the multi index, but &obj->object. As of now, all - * users expect a pointer to NMPlatformObject, not NMPObject. - * You can use NMP_OBJECT_UP_CAST() to retrieve the original @obj pointer. - * - * If need be, we could determine based on @id_type which pointer we want to store. */ - - if (remove) { - if (!nm_multi_index_remove (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); +_idxcache_update_other_cache_ids (NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj_old, + const NMPObject *obj_new, + gboolean is_dump) +{ + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_order; + NMDedupMultiIdxType *idx_type; + + nm_assert (obj_new || obj_old); + nm_assert (!obj_new || NMP_OBJECT_GET_TYPE (obj_new) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || NMP_OBJECT_GET_TYPE (obj_old) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_new) == NMP_OBJECT_GET_CLASS (obj_old)); + nm_assert (!obj_old || !obj_new || !nmp_object_equal (obj_new, obj_old)); + nm_assert (!obj_new || obj_new == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_new)); + nm_assert (!obj_old || obj_old == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_old)); + + idx_type = _idx_type_get (cache, cache_id_type); + + if (obj_old) { + entry_old = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_old); + if (!obj_new) { + if (entry_old) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); + return; + } + } else + entry_old = NULL; + + if (obj_new) { + if ( obj_old + && nm_dedup_multi_idx_type_id_equal (idx_type, obj_old, obj_new) + && nm_dedup_multi_idx_type_partition_equal (idx_type, obj_old, obj_new)) { + /* optimize. We just looked up the @obj_old entry and @obj_new compares equal + * according to idx_obj_id_equal(). entry_new is the same as entry_old. */ + entry_new = entry_old; } else { - if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); + entry_new = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_new); } - } -} -static void -_nmp_cache_update_add (NMPCache *cache, NMPObject *obj) -{ - nm_assert (!obj->is_cached); - nmp_object_ref (obj); - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); - if (!nm_g_hash_table_add (cache->idx_main, obj)) - g_assert_not_reached (); - obj->is_cached = TRUE; - _nmp_cache_update_cache (cache, obj, FALSE); -} + if (entry_new) + entry_order = entry_new; + else if ( entry_old + && nm_dedup_multi_idx_type_partition_equal (idx_type, entry_old->obj, obj_new)) + entry_order = entry_old; + else + entry_order = NULL; + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type, + obj_new, + is_dump + ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE + : NM_DEDUP_MULTI_IDX_MODE_APPEND, + is_dump + ? NULL + : entry_order, + entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING, + entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL), + &entry_new, + NULL); -static void -_nmp_cache_update_remove (NMPCache *cache, NMPObject *obj) -{ - nm_assert (obj->is_cached); - _nmp_cache_update_cache (cache, obj, TRUE); - obj->is_cached = FALSE; - if (!g_hash_table_remove (cache->idx_main, obj)) - g_assert_not_reached (); +#if NM_MORE_ASSERTS + if (entry_new) { + nm_assert (idx_type->klass->idx_obj_partitionable); + nm_assert (idx_type->klass->idx_obj_partition_equal); + nm_assert (idx_type->klass->idx_obj_partitionable (idx_type, entry_new->obj)); + nm_assert (idx_type->klass->idx_obj_partition_equal (idx_type, (gpointer) obj_new, entry_new->obj)); + } +#endif + } else + entry_new = NULL; - /* @obj is possibly a dangling pointer at this point. No problem, multi-index doesn't dereference. */ - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); + if ( entry_old + && entry_old != entry_new) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); } static void -_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new) +_idxcache_update (NMPCache *cache, + const NMDedupMultiEntry *entry_old, + NMPObject *obj_new, + gboolean is_dump, + const NMDedupMultiEntry **out_entry_new) { - const guint8 *id_type; + const NMPClass *klass; + const guint8 *i_idx_type; + NMDedupMultiIdxType *idx_type_o = _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE); + const NMDedupMultiEntry *entry_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + /* we update an object in the cache. + * + * Note that @entry_old MUST be what is currently tracked in multi_idx, and it must + * have the same ID as @obj_new. */ - nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new)); - nm_assert (obj->is_cached); - nm_assert (!new->is_cached); + nm_assert (cache); + nm_assert (entry_old || obj_new); + nm_assert (!obj_new || nmp_object_is_alive (obj_new)); + nm_assert (!entry_old || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, entry_old->obj)); + nm_assert (!obj_new || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, obj_new)); + nm_assert (!entry_old || entry_old->head->idx_type == idx_type_o); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_partition_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_id_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || ( obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass + && !obj_new->parent.klass->obj_full_equal ((NMDedupMultiObj *) obj_new, entry_old->obj))); + + /* keep a reference to the pre-existing entry */ + if (entry_old) + obj_old = nmp_object_ref (entry_old->obj); + + /* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE. + * We already know the pre-existing @entry old, so all that + * nm_dedup_multi_index_add_full() effectively does, is update the + * obj reference. + * + * We also get the new boxed object, which we need below. */ + if (obj_new) { + nm_auto_nmpobj NMPObject *obj_old2 = NULL; + + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type_o, + obj_new, + is_dump + ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE + : NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + (const NMDedupMultiObj **) &obj_old2); + nm_assert (entry_new); + nm_assert (obj_old == obj_old2); + nm_assert (!entry_old || entry_old == entry_new); + } else + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage_obj, cache_id_storage_new; - const NMPCacheId *cache_id_obj, *cache_id_new; + /* now update all other indexes. We know the previously boxed entry, and the + * newly boxed one. */ + klass = NMP_OBJECT_GET_CLASS (entry_new ? entry_new->obj : obj_old); + for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { + NMPCacheIdType id_type = *i_idx_type; - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage_obj, &cache_id_obj)) + if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) continue; - if (!_nmp_object_init_cache_id (new, *id_type, &cache_id_storage_new, &cache_id_new)) - g_assert_not_reached (); - if (!nm_multi_index_move (cache->idx_multi, (NMMultiIndexId *) cache_id_obj, (NMMultiIndexId *) cache_id_new, &obj->object)) - g_assert_not_reached (); + _idxcache_update_other_cache_ids (cache, id_type, + obj_old, + entry_new ? entry_new->obj : NULL, + is_dump); } - nmp_object_copy (obj, new, FALSE); + + NM_SET_OUT (out_entry_new, entry_new); } NMPCacheOpsType -nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + gboolean only_dirty, + const NMPObject **out_obj_old) { - NMPObject *old; + const NMDedupMultiEntry *entry_old; + const NMPObject *obj_old; - nm_assert (NMP_OBJECT_IS_VALID (obj)); + entry_old = _lookup_entry (cache, obj_needle); - old = g_hash_table_lookup (cache->idx_main, obj); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - if (equals_by_ptr && old != obj) { + obj_old = entry_old->obj; + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + + if ( equals_by_ptr + && obj_old != obj_needle) { /* We found an identical object, but we only delete it if it's the same pointer as - * @obj. */ + * @obj_needle. */ return NMP_CACHE_OPS_UNCHANGED; } - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if ( only_dirty + && !entry_old->dirty) { + /* the entry is not dirty. Skip. */ + return NMP_CACHE_OPS_UNCHANGED; + } + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); return NMP_CACHE_OPS_REMOVED; } NMPCacheOpsType -nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_remove_netlink (NMPCache *cache, + const NMPObject *obj_needle, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + + entry_old = _lookup_entry (cache, obj_needle); + + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } + + obj_old = entry_old->obj; + if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be * removed from netlink. Link objects are alive without being in netlink when they * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink * flag. */ - old = (NMPObject *) nmp_cache_lookup_link (cache, obj_needle->link.ifindex); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; - return NMP_CACHE_OPS_UNCHANGED; - } - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; } - if (!old->_link.udev.device) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!obj_old->_link.udev.device) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); - obj->_link.netlink.is_in_netlink = FALSE; + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->_link.netlink.is_in_netlink = FALSE; - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + _nmp_object_fixup_link_master_connected (&obj_new, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; - } else - return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data); + } + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, NULL); + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + return NMP_CACHE_OPS_REMOVED; } /** * nmp_cache_update_netlink: * @cache: the platform cache - * @obj: a #NMPObject instance as received from netlink and created via + * @obj_hand_over: a #NMPObject instance as received from netlink and created via * nmp_object_from_nl(). Especially for link, it must not have the udev * replated fields set. * This instance will be modified and might be put into the cache. When * calling nmp_cache_update_netlink() you hand @obj over to the cache. * Except, that the cache will increment the ref count as appropriate. You * must still unref the obj to release your part of the ownership. - * @out_obj: (allow-none): (out): return the object instance that is inside - * the cache. If you specify non %NULL, you must always unref the returned - * instance. If the return value indicates that the object was removed, - * the object is no longer in the cache. Even if the return value indicates - * that the object was unchanged, it will still return @out_obj -- if - * such an object is in the cache. - * @out_was_visible: (allow-none): (out): whether the object was visible before - * the update operation. - * @pre_hook: (allow-none): a callback *before* the object gets updated. You cannot - * influence the outcome and must not do anything beyong inspecting the changes. - * @user_data: + * @is_dump: whether this update comes during a dump of object of the same kind. + * kernel dumps objects in a certain order, which matters especially for routes. + * Before a dump we mark all objects as dirty, and remove all untouched objects + * afterwards. Hence, during a dump, every update should move the object to the + * end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, + * instead of NM_DEDUP_MULTI_IDX_MODE_APPEND. + * @out_obj_old: (allow-none): (out): return the object with same ID as @obj_hand_over, + * that was in the cache before update. If an object is returned, the caller must + * unref it afterwards. + * @out_obj_new: (allow-none): (out): return the object from the cache after update. + * The caller must unref this object. * * Returns: how the cache changed. + * + * Even if there was no change in the cace (NMP_CACHE_OPS_UNCHANGED), @out_obj_old + * and @out_obj_new will be set accordingly. **/ NMPCacheOpsType -nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_netlink (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!NMP_OBJECT_IS_STACKINIT (obj)); - nm_assert (!obj->is_cached); + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMPObject *obj_old; + gboolean is_alive; + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); /* A link object from netlink must have the udev related fields unset. * We could implement to handle that, but there is no need to support such * a use-case */ - nm_assert (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_LINK || - ( !obj->_link.udev.device - && !obj->link.driver)); + nm_assert (NMP_OBJECT_GET_TYPE (obj_hand_over) != NMP_OBJECT_TYPE_LINK || + ( !obj_hand_over->_link.udev.device + && !obj_hand_over->link.driver)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); - old = g_hash_table_lookup (cache->idx_main, obj); + entry_old = _lookup_entry (cache, obj_hand_over); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { - if (!old) { - if (!nmp_object_is_alive (obj)) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, NULL); - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + if (!nmp_object_is_alive (obj_hand_over)) { + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (obj); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + } - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; - } else if (old == obj) { - /* updating a cached object inplace is not supported because the object contributes to hash-key - * for NMMultiIndex. Modifying an object that is inside NMMultiIndex means that these - * keys change. - * The problem is, that for a given object NMMultiIndex does not support (efficient) - * reverse lookup to get all the NMPCacheIds to which it belongs. If that would be implemented, - * it would be possible to implement inplace-update. - * - * There is an un-optimized reverse lookup via nm_multi_index_iter_init(), but we don't want - * that because we might have a large number of indexes to search. - * - * We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that - * also adds some cost to support an (uncommon?) usage pattern. - * - * Instead we just don't support it, instead we expect the user to - * create a new instance from netlink. - * - * TL;DR: a cached object must never be modified. - */ - g_assert_not_reached (); - } else { - gboolean is_alive = FALSE; - - nm_assert (old->is_cached); - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - if (!obj->_link.netlink.is_in_netlink) { - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); - return NMP_CACHE_OPS_UNCHANGED; - } - if (old->_link.udev.device) { - /* @obj is not in netlink. - * - * This is similar to nmp_cache_remove_netlink(), but there we preserve the - * preexisting netlink properties. The use case of that is when kernel_get_object() - * cannot load an object (based on the id of a needle). - * - * Here we keep the data provided from @obj. The usecase is when receiving - * a valid @obj instance from netlink with RTM_DELROUTE. - */ - is_alive = TRUE; - } - } else - is_alive = TRUE; + } - if (is_alive) { - _nmp_object_fixup_link_master_connected (obj, cache); + obj_old = entry_old->obj; - /* Merge the netlink parts with what we have from udev. */ - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = old->_link.udev.device ? udev_device_ref (old->_link.udev.device) : NULL; - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + if (!obj_hand_over->_link.netlink.is_in_netlink) { + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); + return NMP_CACHE_OPS_UNCHANGED; } + if (obj_old->_link.udev.device) { + /* @obj_hand_over is not in netlink. + * + * This is similar to nmp_cache_remove_netlink(), but there we preserve the + * preexisting netlink properties. The use case of that is when kernel_get_object() + * cannot load an object (based on the id of a needle). + * + * Here we keep the data provided from @obj_hand_over. The usecase is when receiving + * a valid @obj_hand_over instance from netlink with RTM_DELROUTE. + */ + is_alive = TRUE; + } else + is_alive = FALSE; } else - is_alive = nmp_object_is_alive (obj); + is_alive = TRUE; - if (!is_alive) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); - return NMP_CACHE_OPS_REMOVED; + if (is_alive) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + + /* Merge the netlink parts with what we have from udev. */ + udev_device_unref (obj_hand_over->_link.udev.device); + obj_hand_over->_link.udev.device = obj_old->_link.udev.device ? udev_device_ref (obj_old->_link.udev.device) : NULL; + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + + if (obj_hand_over->_link.netlink.lnk) { + nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk; + + /* let's dedup/intern the lnk object. */ + obj_hand_over->_link.netlink.lnk = nm_dedup_multi_index_obj_intern (cache->multi_idx, lnk_old); + } } + } else + is_alive = nmp_object_is_alive (obj_hand_over); - if (nmp_object_equal (old, obj)) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); - return NMP_CACHE_OPS_UPDATED; + if (!is_alive) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_REMOVED; } + + if (nmp_object_equal (obj_old, obj_hand_over)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); + return NMP_CACHE_OPS_UNCHANGED; + } + + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); + return NMP_CACHE_OPS_UPDATED; } NMPCacheOpsType -nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) -{ - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; +nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required) +{ + NMDedupMultiIter iter; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_cur; + const NMDedupMultiEntry *entry_replace; + const NMDedupMultiHeadEntry *head_entry; + gboolean is_alive; + NMPCacheOpsType ops_type = NMP_CACHE_OPS_UNCHANGED; + gboolean resync_required; - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); + /* A link object from netlink must have the udev related fields unset. + * We could implement to handle that, but there is no need to support such + * a use-case */ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_hand_over), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + entry_old = _lookup_entry (cache, obj_hand_over); + entry_new = NULL; - if (!old) { - if (!udevice) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_old))); + + if (!entry_old) { - obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); - obj->link.ifindex = ifindex; - obj->_link.udev.device = udev_device_ref (udevice); + if (!nmp_object_is_alive (obj_hand_over)) + goto update_done; + + _idxcache_update (cache, + NULL, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_ADDED; + goto update_done; + } - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + is_alive = nmp_object_is_alive (obj_hand_over); + + if (!is_alive) { + /* the update would make @entry_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + ops_type = NMP_CACHE_OPS_REMOVED; + goto update_done; + } + + if (nmp_object_equal (entry_old->obj, obj_hand_over)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto update_done; + } - nm_assert (nmp_object_is_alive (obj)); + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_UPDATED; - if (out_obj) - *out_obj = nmp_object_ref (obj); +update_done: + NM_SET_OUT (out_obj_new, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_new))); - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + /* a RTM_GETROUTE event may signal that another object was replaced. + * Find out whether that is the case and return it as @obj_replaced. + * + * Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID + * index. For most parts, we don't care about the order of objects (including routes). + * But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to + * properly find @obj_replaced. */ + resync_required = FALSE; + entry_replace = NULL; + if (is_dump) { + goto out; + } + + if (!entry_new) { + if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE) + && nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj_hand_over)) { + /* hm. @obj_hand_over was not added, meaning it was not alive. + * However, we track some other objects with the same weak-id. + * It's unclear what that means. To be sure, resync. */ + resync_required = TRUE; + } + goto out; + } + + entry_cur = _lookup_entry_with_idx_type (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_new->obj); + if (!entry_cur) { + nm_assert_not_reached (); + goto out; + } + nm_assert (entry_cur->obj == entry_new->obj); + + head_entry = entry_cur->head; + nm_assert (head_entry == nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_cur->obj)); + + if (head_entry->len == 1) { + /* there is only one object, and we expect it to be @obj_new. */ + nm_assert (nm_dedup_multi_head_entry_get_idx (head_entry, 0) == entry_cur); + goto out; + } + + switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) { + case NLM_F_REPLACE: + /* ip route change */ + + /* get the first element (but skip @obj_new). */ + nm_dedup_multi_iter_init (&iter, head_entry); + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + if (iter.current == entry_cur) { + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + } + entry_replace = iter.current; + + nm_assert ( entry_replace + && entry_cur != entry_replace); + + nm_dedup_multi_entry_reorder (entry_cur, entry_replace, FALSE); + break; + case NLM_F_CREATE | NLM_F_APPEND: + /* ip route append */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, TRUE); + break; + case NLM_F_CREATE: + /* ip route prepend */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, FALSE); + break; + default: + /* this is an unexecpted case, probably a bug that we need to handle better. */ + resync_required = TRUE; + break; + } + +out: + NM_SET_OUT (out_obj_replace, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_replace))); + NM_SET_OUT (out_resync_required, resync_required); + return ops_type; +} + + +NMPCacheOpsType +nmp_cache_update_link_udev (NMPCache *cache, + int ifindex, + struct udev_device *udevice, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) +{ + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); + + if (!entry_old) { + if (!udevice) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } + + obj_new = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); + obj_new->link.ifindex = ifindex; + obj_new->_link.udev.device = udev_device_ref (udevice); + + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); + + _idxcache_update (cache, + NULL, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; } else { - nm_assert (old->is_cached); - - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + obj_old = entry_old->obj; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (old->_link.udev.device == udevice) + if (obj_old->_link.udev.device == udevice) { + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; + } - if (!udevice && !old->_link.netlink.is_in_netlink) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!udevice && !obj_old->_link.netlink.is_in_netlink) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); - - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; + obj_new = nmp_object_clone (obj_old, FALSE); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + udev_device_unref (obj_new->_link.udev.device); + obj_new->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; - nm_assert (nmp_object_is_alive (obj)); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; } } NMPCacheOpsType -nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_link_master_connected (NMPCache *cache, + int ifindex, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + obj_old = entry_old->obj; + if (!nmp_cache_link_connected_needs_toggle (cache, obj_old, NULL, NULL)) { + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; } - nm_assert (old->is_cached); + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->link.connected = !obj_old->link.connected; - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + _idxcache_update (cache, + entry_old, + obj_new, + FALSE, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); + return NMP_CACHE_OPS_UPDATED; +} - if (!nmp_cache_link_connected_needs_toggle (cache, old, NULL, NULL)) - return NMP_CACHE_OPS_UNCHANGED; +/*****************************************************************************/ - obj = nmp_object_clone (old, FALSE); - obj->link.connected = !old->link.connected; +void +nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type) +{ + NMPObject obj_needle; - nm_assert (nmp_object_is_alive (obj)); + nm_assert (cache); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); - return NMP_CACHE_OPS_UPDATED; + nm_dedup_multi_index_dirty_set_head (cache->multi_idx, + _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE), + _nmp_object_stackinit_from_type (&obj_needle, obj_type)); } /*****************************************************************************/ NMPCache * -nmp_cache_new (gboolean use_udev) -{ - NMPCache *cache = g_new (NMPCache, 1); - - cache->idx_main = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - NULL); - cache->idx_multi = nm_multi_index_new ((NMMultiIndexFuncHash) nmp_cache_id_hash, - (NMMultiIndexFuncEqual) nmp_cache_id_equal, - (NMMultiIndexFuncClone) nmp_cache_id_clone, - (NMMultiIndexFuncDestroy) nmp_cache_id_destroy); +nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev) +{ + NMPCache *cache = g_slice_new0 (NMPCache); + guint i; + + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + _dedup_multi_idx_type_init ((DedupMultiIdxType *) _idx_type_get (cache, i), i); + + cache->multi_idx = nm_dedup_multi_index_ref (multi_idx); + cache->use_udev = !!use_udev; return cache; } @@ -2025,23 +2440,14 @@ nmp_cache_new (gboolean use_udev) void nmp_cache_free (NMPCache *cache) { - GHashTableIter iter; - NMPObject *obj; + guint i; - /* No need to cumbersomely remove the objects properly. They are not hooked up - * in a complicated way, we can just unref them together with cache->idx_main. - * - * But we must clear the @is_cached flag. */ - g_hash_table_iter_init (&iter, cache->idx_main); - while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { - nm_assert (obj->is_cached); - obj->is_cached = FALSE; - } + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + nm_dedup_multi_index_remove_idx (cache->multi_idx, _idx_type_get (cache, i)); - nm_multi_index_free (cache->idx_multi); - g_hash_table_unref (cache->idx_main); + nm_dedup_multi_index_unref (cache->multi_idx); - g_free (cache); + g_slice_free (NMPCache, cache); } /*****************************************************************************/ @@ -2049,63 +2455,13 @@ nmp_cache_free (NMPCache *cache) void ASSERT_nmp_cache_is_consistent (const NMPCache *cache) { -#if NM_MORE_ASSERTS - NMMultiIndexIter iter_multi; - GHashTableIter iter_hash; - guint i, len; - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id, *cache_id2; - const NMPlatformObject *const *objects; - const NMPObject *obj; - - g_assert (cache); - - g_hash_table_iter_init (&iter_hash, cache->idx_main); - while (g_hash_table_iter_next (&iter_hash, (gpointer *) &obj, NULL)) { - const guint8 *id_type; - - g_assert (NMP_OBJECT_IS_VALID (obj)); - g_assert (nmp_object_is_alive (obj)); - - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; - g_assert (nm_multi_index_contains (cache->idx_multi, &cache_id->base, &obj->object)); - } - } - - nm_multi_index_iter_init (&iter_multi, cache->idx_multi, NULL); - while (nm_multi_index_iter_next (&iter_multi, - (const NMMultiIndexId **) &cache_id, - (void *const**) &objects, - &len)) { - g_assert (len > 0 && objects && objects[len] == NULL); - - for (i = 0; i < len; i++) { - g_assert (objects[i]); - obj = NMP_OBJECT_UP_CAST (objects[i]); - g_assert (NMP_OBJECT_IS_VALID (obj)); - - /* for now, enforce that all objects for a certain index are of the same type. */ - g_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[0]))); - - if (!_nmp_object_init_cache_id (obj, cache_id->_id_type, &cache_id_storage, &cache_id2)) - g_assert_not_reached (); - g_assert (cache_id2); - g_assert (nmp_cache_id_equal (cache_id, cache_id2)); - g_assert_cmpint (nmp_cache_id_hash (cache_id), ==, nmp_cache_id_hash (cache_id2)); - - g_assert (obj == g_hash_table_lookup (cache->idx_main, obj)); - } - } -#endif } + /*****************************************************************************/ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LINK, .sizeof_data = sizeof (NMPObjectLink), .sizeof_public = sizeof (NMPlatformLink), @@ -2115,22 +2471,23 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, .cmd_obj_cmp = _vt_cmd_obj_cmp_link, .cmd_obj_copy = _vt_cmd_obj_copy_link, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, .cmd_obj_dispose = _vt_cmd_obj_dispose_link, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, .cmd_obj_to_string = _vt_cmd_obj_to_string_link, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_link, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_link_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_link_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, .sizeof_data = sizeof (NMPObjectIP4Address), .sizeof_public = sizeof (NMPlatformIP4Address), @@ -2140,17 +2497,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_address_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_ip4_address_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, }, [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, .sizeof_data = sizeof (NMPObjectIP6Address), .sizeof_public = sizeof (NMPlatformIP6Address), @@ -2160,17 +2517,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_address_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_ip6_address_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .sizeof_data = sizeof (NMPObjectIP4Route), .sizeof_public = sizeof (NMPlatformIP4Route), @@ -2179,18 +2536,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip4_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp_full, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .sizeof_data = sizeof (NMPObjectIP6Route), .sizeof_public = sizeof (NMPlatformIP6Route), @@ -2199,109 +2556,129 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip6_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, - .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route, - .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, }, [NMP_OBJECT_TYPE_LNK_GRE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_GRE, .sizeof_data = sizeof (NMPObjectLnkGre), .sizeof_public = sizeof (NMPlatformLnkGre), .obj_type_name = "gre", .lnk_link_type = NM_LINK_TYPE_GRE, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_gre_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, }, [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, .sizeof_data = sizeof (NMPObjectLnkInfiniband), .sizeof_public = sizeof (NMPlatformLnkInfiniband), .obj_type_name = "infiniband", .lnk_link_type = NM_LINK_TYPE_INFINIBAND, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_infiniband_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp, }, [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL, .sizeof_data = sizeof (NMPObjectLnkIp6Tnl), .sizeof_public = sizeof (NMPlatformLnkIp6Tnl), .obj_type_name = "ip6tnl", .lnk_link_type = NM_LINK_TYPE_IP6TNL, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp, }, [NMP_OBJECT_TYPE_LNK_IPIP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, .sizeof_data = sizeof (NMPObjectLnkIpIp), .sizeof_public = sizeof (NMPlatformLnkIpIp), .obj_type_name = "ipip", .lnk_link_type = NM_LINK_TYPE_IPIP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ipip_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ipip_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ipip_cmp, }, [NMP_OBJECT_TYPE_LNK_MACSEC - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACSEC, .sizeof_data = sizeof (NMPObjectLnkMacsec), .sizeof_public = sizeof (NMPlatformLnkMacsec), .obj_type_name = "macsec", .lnk_link_type = NM_LINK_TYPE_MACSEC, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macsec_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macsec_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macsec_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, .sizeof_data = sizeof (NMPObjectLnkMacvlan), .sizeof_public = sizeof (NMPlatformLnkMacvlan), .obj_type_name = "macvlan", .lnk_link_type = NM_LINK_TYPE_MACVLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macvlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP, .sizeof_data = sizeof (NMPObjectLnkMacvtap), .sizeof_public = sizeof (NMPlatformLnkMacvtap), .obj_type_name = "macvtap", .lnk_link_type = NM_LINK_TYPE_MACVTAP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_macvlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_SIT - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_SIT, .sizeof_data = sizeof (NMPObjectLnkSit), .sizeof_public = sizeof (NMPlatformLnkSit), .obj_type_name = "sit", .lnk_link_type = NM_LINK_TYPE_SIT, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_sit_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_sit_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp, }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, .sizeof_data = sizeof (NMPObjectLnkVlan), .sizeof_public = sizeof (NMPlatformLnkVlan), .obj_type_name = "vlan", .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, }, [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, .sizeof_data = sizeof (NMPObjectLnkVxlan), .sizeof_public = sizeof (NMPlatformLnkVxlan), .obj_type_name = "vxlan", .lnk_link_type = NM_LINK_TYPE_VXLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vxlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp, }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index b69680f6..41fd08cb 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -21,8 +21,9 @@ #ifndef __NMP_OBJECT_H__ #define __NMP_OBJECT_H__ +#include "nm-utils/nm-obj.h" +#include "nm-utils/nm-dedup-multi.h" #include "nm-platform.h" -#include "nm-multi-index.h" struct udev_device; @@ -47,36 +48,42 @@ typedef enum { /*< skip >*/ * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT. * * Of one index type, there can be multiple indexes or not. - * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX there + * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX there * are multiple instances (for different route/addresses, v4/v6, per-ifindex). * * But one object, can only be indexed by one particular index of a * type. For example, a certain address instance is only indexed by - * the index NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX with + * the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible. * */ typedef enum { /*< skip >*/ NMP_CACHE_ID_TYPE_NONE, - /* all the objects of a certain type */ + /* all the objects of a certain type. + * + * This index is special. It is the only one that contains *all* object. + * Other indexes may consider some object as non "partitionable", hence + * they don't track all objects. + * + * Hence, this index type is used when looking at all objects (still + * partitioned by type). + * + * Also, note that links may be considered invisible. This index type + * expose all links, even invisible ones. For addresses/routes, this + * distiction doesn't exist, as all addresses/routes that are alive + * are visible as well. */ NMP_CACHE_ID_TYPE_OBJECT_TYPE, /* index for the link objects by ifname. */ NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - /* all the visible objects of a certain type */ - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, + /* indeces for the visible default-routes, ignoring ifindex. + * This index only contains two partitions: all visible default-routes, + * separate for IPv4 and IPv6. */ + NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, - /* indeces for the visible routes, ignoring ifindex. */ - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - - /* all the visible addresses/routes (by object-type) for an ifindex. */ - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - - /* three indeces for the visible routes, per ifindex. */ - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, + /* all the addresses/routes (by object-type) for an ifindex. */ + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, /* Consider all the destination fields of a route, that is, the ID without the ifindex * and gateway (meaning: network/plen,metric). @@ -86,65 +93,21 @@ typedef enum { /*< skip >*/ * sends one RTM_NEWADDR notification without notifying about the deletion. We detect * that by having this index to contain overlapping routes which require special * cache-resync. */ - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, } NMPCacheIdType; -typedef struct _NMPCacheId NMPCacheId; - -struct _NMPCacheId { - union { - NMMultiIndexId base; - guint8 _id_type; /* NMPCacheIdType as guint8 */ - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_OBJECT_TYPE */ - /* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */ - guint8 _id_type; - guint8 obj_type; /* NMPObjectType as guint8 */ - } object_type; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */ - /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */ - guint8 _id_type; - guint8 obj_type; /* NMPObjectType as guint8 */ - int _misaligned_ifindex; - } object_type_by_ifindex; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */ - guint8 _id_type; - char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */ - } link_by_ifname; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4 */ - guint8 _id_type; - guint8 plen; - guint32 _misaligned_metric; - guint32 _misaligned_network; - } routes_by_destination_ip4; - struct _nm_packed { - /* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6 */ - guint8 _id_type; - guint8 plen; - guint32 _misaligned_metric; - struct in6_addr _misaligned_network; - } routes_by_destination_ip6; - }; -}; - typedef struct { + NMDedupMultiObjClass parent; + const char *obj_type_name; + int sizeof_data; + int sizeof_public; NMPObjectType obj_type; int addr_family; int rtm_gettype; - int sizeof_data; - int sizeof_public; NMPlatformSignalIdType signal_type_id; - const char *obj_type_name; const char *signal_type; const guint8 *supported_cache_ids; @@ -152,13 +115,9 @@ typedef struct { /* Only for NMPObjectLnk* types. */ NMLinkType lnk_link_type; - /* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index, - * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */ - gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id); - + void (*cmd_obj_hash_update) (const NMPObject *obj, NMHashState *h); int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2); void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src); - void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src); void (*cmd_obj_dispose) (NMPObject *obj); gboolean (*cmd_obj_is_alive) (const NMPObject *obj); gboolean (*cmd_obj_is_visible) (const NMPObject *obj); @@ -166,10 +125,11 @@ typedef struct { /* functions that operate on NMPlatformObject */ void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src); - gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); - guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj); + int (*cmd_plobj_id_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); + void (*cmd_plobj_id_hash_update) (const NMPlatformObject *obj, NMHashState *h); const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size); const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj, char *buf, gsize len); + void (*cmd_plobj_hash_update) (const NMPlatformObject *obj, NMHashState *h); int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; @@ -182,7 +142,7 @@ typedef struct { bool is_in_netlink; /* Additional data that depends on the link-type (IFLA_INFO_DATA) */ - NMPObject *lnk; + const NMPObject *lnk; } netlink; struct { @@ -266,9 +226,10 @@ typedef struct { } NMPObjectIP6Route; struct _NMPObject { - const NMPClass *_class; - int _ref_count; - bool is_cached; + union { + NMDedupMultiObj parent; + const NMPClass *_class; + }; union { NMPlatformObject object; @@ -326,8 +287,6 @@ NMP_CLASS_IS_VALID (const NMPClass *klass) && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0; } -#define NMP_REF_COUNT_STACKINIT (G_MAXINT) - static inline NMPObject * NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { @@ -336,7 +295,7 @@ NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) obj = plobj ? (NMPObject *) ( &(((char *) plobj)[-((int) G_STRUCT_OFFSET (NMPObject, object))]) ) : NULL; - nm_assert (!obj || (obj->_ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); + nm_assert (!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); return obj; } #define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj))) @@ -345,7 +304,7 @@ static inline gboolean NMP_OBJECT_IS_VALID (const NMPObject *obj) { nm_assert (!obj || ( obj - && obj->_ref_count > 0 + && obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); /* There isn't really much to check. Either @obj is NULL, or we must @@ -358,7 +317,7 @@ NMP_OBJECT_IS_STACKINIT (const NMPObject *obj) { nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); - return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT; + return obj && obj->parent._ref_count == NM_OBJ_REF_COUNT_STACKINIT; } static inline const NMPClass * @@ -377,65 +336,264 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; } - +#define NMP_OBJECT_CAST_LINK(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ + }) + +#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ + }) + +#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ + _obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ + }) const NMPClass *nmp_class_from_type (NMPObjectType obj_type); -NMPObject *nmp_object_ref (NMPObject *object); -void nmp_object_unref (NMPObject *object); +static inline const NMPObject * +nmp_object_ref (const NMPObject *obj) +{ + if (!obj) { + /* for convenience, allow NULL. */ + return NULL; + } + + /* ref and unref accept const pointers. NMPObject is supposed to be shared + * and kept immutable. Disallowing to take/retrun a reference to a const + * NMPObject is cumbersome, because callers are precisely expected to + * keep a ref on the otherwise immutable object. */ + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + g_return_val_if_fail (obj->parent._ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL); + + return (const NMPObject *) nm_dedup_multi_obj_ref ((const NMDedupMultiObj *) obj); +} + +static inline const NMPObject * +nmp_object_unref (const NMPObject *obj) +{ + nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + return NULL; +} + +#define nm_clear_nmp_object(ptr) \ + ({ \ + typeof (ptr) _ptr = (ptr); \ + typeof (*_ptr) _pptr; \ + gboolean _changed = FALSE; \ + \ + if ( _ptr \ + && (_pptr = *_ptr)) { \ + *_ptr = NULL; \ + nmp_object_unref (_pptr); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); + +static inline NMPObject * +nmp_object_stackinit_obj (NMPObject *obj, const NMPObject *src) +{ + return obj == src + ? obj + : (NMPObject *) nmp_object_stackinit (obj, NMP_OBJECT_GET_TYPE (src), &src->object); +} + const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src); const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex); const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address); -const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen); -const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric); -const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric); +const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address); const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); +void nmp_object_hash_update (const NMPObject *obj, NMHashState *h); int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2); gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2); void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only); NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only); -gboolean nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2); + +int nmp_object_id_cmp (const NMPObject *obj1, const NMPObject *obj2); +void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h); guint nmp_object_id_hash (const NMPObject *obj); + +static inline gboolean +nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + return nmp_object_id_cmp (obj1, obj2) == 0; +} + gboolean nmp_object_is_alive (const NMPObject *obj); gboolean nmp_object_is_visible (const NMPObject *obj); -void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev); +void _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev); -#define nm_auto_nmpobj __attribute__((cleanup(_nm_auto_nmpobj_cleanup))) static inline void -_nm_auto_nmpobj_cleanup (NMPObject **pobj) +_nm_auto_nmpobj_cleanup (gpointer p) { - nmp_object_unref (*pobj); + nmp_object_unref (*((const NMPObject **) p)); } +#define nm_auto_nmpobj nm_auto(_nm_auto_nmpobj_cleanup) typedef struct _NMPCache NMPCache; typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data); -gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b); -guint nmp_cache_id_hash (const NMPCacheId *id); -NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id); -void nmp_cache_id_destroy (NMPCacheId *id); +const NMDedupMultiEntry *nmp_cache_lookup_entry (const NMPCache *cache, + const NMPObject *obj); +const NMDedupMultiEntry *nmp_cache_lookup_entry_with_idx_type (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj); +const NMDedupMultiEntry *nmp_cache_lookup_entry_link (const NMPCache *cache, + int ifindex); +const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, + const NMPObject *obj); +const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, + int ifindex); + +typedef struct _NMPLookup NMPLookup; + +struct _NMPLookup { + NMPCacheIdType cache_id_type; + NMPObject selector_obj; +}; + +const NMDedupMultiHeadEntry *nmp_cache_lookup_all (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *select_obj); + +static inline const NMDedupMultiHeadEntry * +nmp_cache_lookup (const NMPCache *cache, + const NMPLookup *lookup) +{ + return nmp_cache_lookup_all (cache, lookup->cache_id_type, &lookup->selector_obj); +} + +const NMPLookup *nmp_lookup_init_obj_type (NMPLookup *lookup, + NMPObjectType obj_type); +const NMPLookup *nmp_lookup_init_link_by_ifname (NMPLookup *lookup, + const char *ifname); +const NMPLookup *nmp_lookup_init_addrroute (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex); +const NMPLookup *nmp_lookup_init_route_default (NMPLookup *lookup, + NMPObjectType obj_type); +const NMPLookup *nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj); +const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos); +const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); + +GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, + NMPObjectType obj_type, + gboolean visible_only); + +static inline gboolean +nmp_cache_iter_next (NMDedupMultiIter *iter, const NMPObject **out_obj) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (iter); + nm_assert (!has_next || NMP_OBJECT_IS_VALID (iter->current->obj)); + if (out_obj) + *out_obj = has_next ? iter->current->obj : NULL; + return has_next; +} + +static inline gboolean +nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj) +{ + gboolean has_next; -NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only); -NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex); -NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex); -NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname); -NMPCacheId *nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, guint32 network, guint8 plen, guint32 metric); -NMPCacheId *nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, const struct in6_addr *network, guint8 plen, guint32 metric); + has_next = nm_dedup_multi_iter_next (iter); + nm_assert (!has_next || NMP_OBJECT_GET_TYPE (iter->current->obj) == NMP_OBJECT_TYPE_LINK); + if (out_obj) + *out_obj = has_next ? &(((const NMPObject *) iter->current->obj)->link) : NULL; + return has_next; +} -const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len); -GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id); -const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj); -const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex); +#define nmp_cache_iter_for_each(iter, head, obj) \ + for (nm_dedup_multi_iter_init ((iter), \ + (head)); \ + nmp_cache_iter_next ((iter), (obj)); \ + ) -const NMPObject *nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route); +#define nmp_cache_iter_for_each_link(iter, head, obj) \ + for (nm_dedup_multi_iter_init ((iter), \ + (head)); \ + nmp_cache_iter_next_link ((iter), (obj)); \ + ) const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, @@ -444,10 +602,8 @@ const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, NMLinkType link_type, NMPObjectMatchFn match_fn, gpointer user_data); -GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache, - NMPCacheId *cache_id, - GHashTable *hash); +gboolean nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave); gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave); const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave); @@ -455,13 +611,182 @@ gboolean nmp_cache_use_udev_get (const NMPCache *cache); void ASSERT_nmp_cache_is_consistent (const NMPCache *cache); -NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); -NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); - -NMPCache *nmp_cache_new (gboolean use_udev); +NMPCacheOpsType nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + gboolean only_dirty, + const NMPObject **out_obj_old); +NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, + const NMPObject *obj_needle, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required); +NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, + int ifindex, + struct udev_device *udevice, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, + int ifindex, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); + +void nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type); + +NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev); void nmp_cache_free (NMPCache *cache); +static inline void +ASSERT_nmp_cache_ops (const NMPCache *cache, + NMPCacheOpsType ops_type, + const NMPObject *obj_old, + const NMPObject *obj_new) +{ +#if NM_MORE_ASSERTS + nm_assert (cache); + nm_assert (obj_old || obj_new); + nm_assert (!obj_old || ( NMP_OBJECT_IS_VALID (obj_old) + && !NMP_OBJECT_IS_STACKINIT (obj_old) + && nmp_object_is_alive (obj_old))); + nm_assert (!obj_new || ( NMP_OBJECT_IS_VALID (obj_new) + && !NMP_OBJECT_IS_STACKINIT (obj_new) + && nmp_object_is_alive (obj_new))); + + switch (ops_type) { + case NMP_CACHE_OPS_UNCHANGED: + nm_assert (obj_old == obj_new); + break; + case NMP_CACHE_OPS_ADDED: + nm_assert (!obj_old && obj_new); + break; + case NMP_CACHE_OPS_UPDATED: + nm_assert (obj_old && obj_new && obj_old != obj_new); + break; + case NMP_CACHE_OPS_REMOVED: + nm_assert (obj_old && !obj_new); + break; + default: + nm_assert_not_reached (); + } + + nm_assert (obj_new == NULL || obj_old == NULL || nmp_object_id_equal (obj_new, obj_old)); + nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_old) == NMP_OBJECT_GET_CLASS (obj_new)); + + nm_assert (obj_new == nmp_cache_lookup_obj (cache, obj_new ?: obj_old)); +#endif +} + +const NMDedupMultiHeadEntry *nm_platform_lookup_all (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj); + +const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj); + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_obj_type (NMPlatform *platform, + NMPObjectType obj_type) +{ + NMPLookup lookup; + + nmp_lookup_init_obj_type (&lookup, obj_type); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_link_by_ifname (NMPlatform *platform, + const char *ifname) +{ + NMPLookup lookup; + + nmp_lookup_init_link_by_ifname (&lookup, ifname); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_addrroute (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex) +{ + NMPLookup lookup; + + nmp_lookup_init_addrroute (&lookup, obj_type, ifindex); + return nm_platform_lookup (platform, &lookup); +} + +static inline GPtrArray * +nm_platform_lookup_addrroute_clone (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + NMPLookup lookup; + + nmp_lookup_init_addrroute (&lookup, obj_type, ifindex); + return nm_platform_lookup_clone (platform, &lookup, predicate, user_data); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_route_default (NMPlatform *platform, + NMPObjectType obj_type) +{ + NMPLookup lookup; + + nmp_lookup_init_route_default (&lookup, obj_type); + return nm_platform_lookup (platform, &lookup); +} + +static inline GPtrArray * +nm_platform_lookup_route_default_clone (NMPlatform *platform, + NMPObjectType obj_type, + NMPObjectPredicateFunc predicate, + gpointer user_data) +{ + NMPLookup lookup; + + nmp_lookup_init_route_default (&lookup, obj_type); + return nm_platform_lookup_clone (platform, &lookup, predicate, user_data); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_ip4_route_by_weak_id (NMPlatform *platform, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) +{ + NMPLookup lookup; + + nmp_lookup_init_ip4_route_by_weak_id (&lookup, network, plen, metric, tos); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPLookup lookup; + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, network, plen, metric, src, src_plen); + return nm_platform_lookup (platform, &lookup); +} + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index d0c58aeb..e1220052 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -78,6 +78,8 @@ main (int argc, char **argv) nm_linux_platform_setup (); + nm_platform_check_kernel_support (NM_PLATFORM_GET, ~((NMPlatformKernelSupportFlags) 0)); + if (global_opt.persist) g_main_loop_run (loop); diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 4c139ef0..93851ff7 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -22,7 +22,6 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" #define IP4_ADDRESS "192.0.2.1" #define IP4_ADDRESS_PEER "192.0.2.2" #define IP4_ADDRESS_PEER2 "192.0.3.1" @@ -30,8 +29,8 @@ #define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" #define IP6_PLEN 64 -static int DEVICE_IFINDEX = -1; -static int EX = -1; +#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX +#define EX NMTSTP_ENV1_EX /*****************************************************************************/ @@ -103,7 +102,7 @@ test_ip4_address_general (void) accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP4Address, 0); @@ -143,9 +142,9 @@ test_ip6_address_general (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); accept_signal (address_added); /* Add address again (aka update) */ @@ -153,7 +152,7 @@ test_ip6_address_general (void) accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + addresses = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP6Address, 0); @@ -164,7 +163,7 @@ test_ip6_address_general (void) /* Remove address */ nmtstp_ip6_address_del (NULL, EX, ifindex, addr, IP6_PLEN); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); accept_signal (address_removed); /* Remove address again */ @@ -229,20 +228,20 @@ test_ip6_address_general_2 (void) /* Add/delete notification */ nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); accept_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_del (NULL, EX, ifindex, addr, IP6_PLEN); accept_signal (address_removed); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); /* Add/delete conflict */ nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); accept_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); ensure_no_signal (address_added); - g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr)); free_signal (address_added); free_signal (address_removed); @@ -330,7 +329,7 @@ test_ip4_address_peer_zero (void) nmtstp_ip4_address_add (NULL, EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, 0, label); - addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addrs = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addrs); g_assert_cmpint (addrs->len, ==, i + 1); g_array_unref (addrs); @@ -345,7 +344,7 @@ test_ip4_address_peer_zero (void) nmtstp_ip4_address_del (NULL, EX, ifindex, addr, plen, r_peers[i]); - addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addrs = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addrs); g_assert_cmpint (addrs->len, ==, G_N_ELEMENTS (peers) - i - 1); g_array_unref (addrs); @@ -366,62 +365,16 @@ _nmtstp_init_tests (int *argc, char ***argv) * SETUP TESTS *****************************************************************************/ -typedef struct { - const char *testpath; - GTestFunc test_func; -} TestSetup; - -static void -_g_test_run (gconstpointer user_data) -{ - const TestSetup *s = user_data; - int ifindex; - - _LOGT ("TEST: start %s", s->testpath); - - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); - - ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint (ifindex, >, 0); - g_assert_cmpint (DEVICE_IFINDEX, ==, -1); - - DEVICE_IFINDEX = ifindex; - EX = nmtstp_run_command_check_external_global (); - - s->test_func (); - - g_assert_cmpint (DEVICE_IFINDEX, ==, ifindex); - DEVICE_IFINDEX = -1; - - g_assert_cmpint (ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - _LOGT ("TEST: finished %s", s->testpath); -} - -static void -_g_test_add_func (const char *testpath, - GTestFunc test_func) -{ - TestSetup *s; - - s = g_new0 (TestSetup, 1); - s->testpath = testpath; - s->test_func = test_func; - - g_test_add_data_func_full (testpath, s, _g_test_run, g_free); -} - void _nmtstp_setup_tests (void) { - _g_test_add_func ("/address/ipv4/general", test_ip4_address_general); - _g_test_add_func ("/address/ipv6/general", test_ip6_address_general); +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE) + add_test_func ("/address/ipv4/general", test_ip4_address_general); + add_test_func ("/address/ipv6/general", test_ip6_address_general); - _g_test_add_func ("/address/ipv4/general-2", test_ip4_address_general_2); - _g_test_add_func ("/address/ipv6/general-2", test_ip6_address_general_2); + add_test_func ("/address/ipv4/general-2", test_ip4_address_general_2); + add_test_func ("/address/ipv6/general-2", test_ip6_address_general_2); - _g_test_add_func ("/address/ipv4/peer", test_ip4_address_peer); - _g_test_add_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); + add_test_func ("/address/ipv4/peer", test_ip4_address_peer); + add_test_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); } diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 71a92cbf..937cd12c 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -22,8 +22,6 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" - static void test_cleanup_internal (void) { @@ -31,8 +29,8 @@ test_cleanup_internal (void) int ifindex; GArray *addresses4; GArray *addresses6; - GArray *routes4; - GArray *routes6; + GPtrArray *routes4; + GPtrArray *routes6; in_addr_t addr4; in_addr_t network4; int plen4 = 24; @@ -72,10 +70,10 @@ test_cleanup_internal (void) nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, in6addr_any, metric, mss); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, in6addr_any, metric, mss); - addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); - addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + addresses4 = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 1); g_assert_cmpint (addresses6->len, ==, 2); /* also has a IPv6 LL address. */ @@ -84,26 +82,24 @@ test_cleanup_internal (void) g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); + g_ptr_array_unref (routes4); + g_ptr_array_unref (routes6); /* Delete interface with all addresses and routes */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); - addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + addresses4 = nmtstp_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 0); g_assert_cmpint (addresses6->len, ==, 0); - g_assert_cmpint (routes4->len, ==, 0); - g_assert_cmpint (routes6->len, ==, 0); + g_assert (!routes4); + g_assert (!routes6); g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); } NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index a9d0694d..18076712 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -30,6 +30,9 @@ #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" #define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count +int NMTSTP_ENV1_IFINDEX = -1; +int NMTSTP_ENV1_EX = -1; + /*****************************************************************************/ void @@ -67,6 +70,90 @@ _init_platform (NMPlatform **platform, gboolean external_command) /*****************************************************************************/ +static GArray * +_ipx_address_get_all (NMPlatform *self, int ifindex, NMPObjectType obj_type) +{ + NMPLookup lookup; + + g_assert (NM_IS_PLATFORM (self)); + g_assert (ifindex > 0); + g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); + nmp_lookup_init_addrroute (&lookup, + obj_type, + ifindex); + return nmp_cache_lookup_to_array (nm_platform_lookup (self, &lookup), + obj_type, + FALSE /*addresses are always visible. */); +} + +GArray * +nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex) +{ + return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); +} + +GArray * +nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex) +{ + return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); +} + +/*****************************************************************************/ + +gboolean +nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) +{ + NMDedupMultiIter iter; + + nm_platform_process_events (platform); + + nm_dedup_multi_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (iter.current->obj); + + if ( r->ifindex != ifindex + || r->network != network + || r->plen != plen + || r->metric != metric) { + continue; + } + + return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r)); + } + + return TRUE; +} + +gboolean +nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) +{ + NMDedupMultiIter iter; + + nm_platform_process_events (platform); + + nm_dedup_multi_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex)) { + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (iter.current->obj); + + if ( r->ifindex != ifindex + || !IN6_ARE_ADDR_EQUAL (&r->network, &network) + || r->plen != plen + || r->metric != metric) { + continue; + } + + return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r)); + } + + return TRUE; +} + +/*****************************************************************************/ + SignalData * add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname) { @@ -153,9 +240,9 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; - GArray *links; - NMPlatformLink *cached; - int i; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *cached; g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_LINK); g_assert (received); @@ -185,19 +272,21 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /* Check the data */ g_assert (received->ifindex > 0); - links = nm_platform_link_get_all (NM_PLATFORM_GET, TRUE); - for (i = 0; i < links->len; i++) { - cached = &g_array_index (links, NMPlatformLink, i); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nm_platform_lookup (platform, &lookup), + &cached) { + if (!nmp_object_is_visible (NMP_OBJECT_UP_CAST (cached))) + continue; if (cached->ifindex == received->ifindex) { g_assert_cmpint (nm_platform_link_cmp (cached, received), ==, 0); g_assert (!memcmp (cached, received, sizeof (*cached))); if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED) g_error ("Deleted link still found in the local cache."); - g_array_unref (links); return; } } - g_array_unref (links); if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED) g_error ("Added/changed link not found in the local cache."); @@ -205,113 +294,219 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /*****************************************************************************/ -gboolean -nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric) +static const NMPlatformIP4Route * +_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos, + guint *out_c_exists) { - gs_free char *arg_network = NULL; - const char *argv[] = { - NULL, - "route", - "list", - "dev", - ifname, - "exact", - NULL, - NULL, - }; - int exit_status; - gs_free char *std_out = NULL, *std_err = NULL; - char *out; - gboolean success; - gs_free_error GError *error = NULL; - gs_free char *metric_pattern = NULL; + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o = NULL; + guint c; + const NMPlatformIP4Route *r = NULL; - g_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL)); - g_assert (!strstr (ifname, " metric ")); - g_assert (plen >= 0 && plen <= 32); + _init_platform (&platform, FALSE); - if (!nmtstp_is_root_test ()) { - /* If we don't test against linux-platform, we don't actually configure any - * routes in the system. */ - return -1; + nmp_lookup_init_ip4_route_by_weak_id (&lookup, + network, + plen, + metric, + tos); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP4_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP4_ROUTE (o); + c++; } - argv[0] = nm_utils_file_search_in_paths ("ip", NULL, - (const char *[]) { "/sbin", "/usr/sbin", NULL }, - G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL); - argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen); + NM_SET_OUT (out_c_exists, c); + return r; +} - if (!argv[0]) { - /* Hm. There is no 'ip' binary. Return *unknown* */ - return -1; +const NMPlatformIP4Route * +_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + int ifindex; + guint c; + const NMPlatformIP4Route *r = NULL; + + _init_platform (&platform, FALSE); + + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); } - success = g_spawn_sync (NULL, - (char **) argv, - (char *[]) { NULL }, - 0, - NULL, - NULL, - &std_out, - &std_err, - &exit_status, - &error); - g_assert_no_error (error); - g_assert (success); - g_assert_cmpstr (std_err, ==, ""); - g_assert (std_out); - - metric_pattern = g_strdup_printf (" metric %u", metric); - out = std_out; - while (out) { - char *eol = strchr (out, '\n'); - gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out); - const char *p; - - out = eol ? &eol[1] : NULL; - if (!line[0]) - continue; + r = _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + &c); - if (metric == 0) { - if (!strstr (line, " metric ")) - return TRUE; - } - p = strstr (line, metric_pattern); - if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0')) - return TRUE; + if (c != c_exists && c_exists != -1) { + g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but platform has it %u times", + file, line, func, + nm_utils_inet4_ntop (network, NULL), plen, + metric, + tos, + c_exists, + c); } - return FALSE; + + return r; } -void -_nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric) +const NMPlatformIP4Route * +nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + return _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + NULL); +} + +/*****************************************************************************/ + +static const NMPlatformIP6Route * +_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen, + guint *out_c_exists) +{ + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o = NULL; + guint c; + const NMPlatformIP6Route *r = NULL; + + _init_platform (&platform, FALSE); + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, + network, + plen, + metric, + src, + src_plen); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP6_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP6_ROUTE (o); + c++; + } + + NM_SET_OUT (out_c_exists, c); + return r; +} + +const NMPlatformIP6Route * +_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) { int ifindex; - gboolean exists_checked; + guint c; + const NMPlatformIP6Route *r = NULL; _init_platform (&platform, FALSE); - /* Check for existance of the route by spawning iproute2. Do this because platform - * code might be entirely borked, but we expect ip-route to give a correct result. - * If the ip command cannot be found, we accept this as success. */ - exists_checked = nmtstp_ip4_route_exists (ifname, network, plen, metric); - if (exists_checked != -1 && !exists_checked != !exists) { - g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s", - file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "to exist" : "not to exist", - exists ? "doesn't" : "does"); + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); } - ifindex = nm_platform_link_get_ifindex (platform, ifname); - g_assert (ifindex > 0); - if (!nm_platform_ip4_route_get (platform, ifindex, network, plen, metric) != !exists) { - g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", + r = _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + &c); + + if (c != c_exists && c_exists != -1) { + char s_src[NM_UTILS_INET_ADDRSTRLEN]; + char s_network[NM_UTILS_INET_ADDRSTRLEN]; + + g_error ("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but platform has it %u times", file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "exists" : "does not exist", - exists ? "it doesn't" : "it does"); + nm_utils_inet6_ntop (network, s_network), + plen, + metric, + nm_utils_inet6_ntop (src, s_src), + src_plen, + c_exists, + c); } + + return r; +} + +const NMPlatformIP6Route * +nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + return _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + NULL); } /*****************************************************************************/ @@ -369,7 +564,7 @@ _wait_for_signal_timeout (gpointer user_data) } guint -nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms) +nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_ms) { WaitForSignalData data = { 0 }; gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; @@ -384,8 +579,18 @@ nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms) id_ip4_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); id_ip6_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); - if (timeout_ms != 0) - data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data); + /* if timeout_ms is negative, it means the wait-time already expired. + * Maybe, we should do nothing and return right away, without even + * processing events from platform. However, that inconsistency (of not + * processing events from mainloop) is inconvenient. + * + * It's better that on the return of nmtstp_wait_for_signal(), we always + * have no events pending. So, a negative timeout is treated the same as + * a zero timeout: we check whether there are any events pending in platform, + * and quite the mainloop immediately afterwards. But we always check. */ + + data.id = g_timeout_add (CLAMP (timeout_ms, 0, G_MAXUINT32), + _wait_for_signal_timeout, &data); g_main_loop_run (data.loop); @@ -414,14 +619,14 @@ nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms) if (until_ms < now) return 0; - signal_counts = nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)); + signal_counts = nmtstp_wait_for_signal (platform, until_ms - now); if (signal_counts) return signal_counts; } } const NMPlatformLink * -nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms) { return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); } @@ -445,7 +650,7 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType if (until_ms < now) return NULL; - nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)); + nmtstp_wait_for_signal (platform, until_ms - now); } } @@ -717,7 +922,7 @@ _ip_address_add (NMPlatform *platform, g_assert (label == NULL); g_assert (flags == 0); - a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + a = nm_platform_ip6_address_get (platform, ifindex, address->addr6); if ( a && !memcmp (nm_platform_ip6_address_get_peer (a), (IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) || IN6_ARE_ADDR_EQUAL (&address->addr6, &peer_address->addr6)) @@ -804,7 +1009,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip4_route_add (platform, &route)); + g_assert_cmpint (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } void nmtstp_ip6_route_add (NMPlatform *platform, @@ -828,7 +1033,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip6_route_add (platform, &route)); + g_assert_cmpint (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } /*****************************************************************************/ @@ -861,7 +1066,7 @@ _ip_address_del (NMPlatform *platform, if (is_v4) had_address = !!nm_platform_ip4_address_get (platform, ifindex, address->addr4, plen, peer_address->addr4); else - had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6); if (is_v4) { success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s", @@ -913,7 +1118,7 @@ _ip_address_del (NMPlatform *platform, } else { const NMPlatformIP6Address *a; - a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen); + a = nm_platform_ip6_address_get (platform, ifindex, address->addr6); if (!a) break; } @@ -974,6 +1179,36 @@ nmtstp_ip6_address_del (NMPlatform *platform, } G_STMT_END const NMPlatformLink * +nmtstp_link_veth_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const char *peer) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + + g_assert (nm_utils_is_valid_iface_name (name, NULL)); + + external_command = nmtstp_run_command_check_external (external_command); + + _init_platform (&platform, external_command); + + if (external_command) { + success = !nmtstp_run_command ("ip link add dev %s type veth peer name %s", + name, peer); + if (success) { + pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_VETH, 100); + nmtstp_assert_wait_for_link (platform, peer, NM_LINK_TYPE_VETH, 10); + } + } else + success = nm_platform_link_veth_add (platform, name, peer, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + g_assert (success); + _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_VETH); + return pllink; +} + +const NMPlatformLink * nmtstp_link_dummy_add (NMPlatform *platform, gboolean external_command, const char *name) diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index a52a5db5..4010aa2f 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -5,6 +5,7 @@ #include <arpa/inet.h> #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "platform/nm-fake-platform.h" #include "platform/nm-linux-platform.h" @@ -93,9 +94,9 @@ int nmtstp_run_command (const char *format, ...) _nm_printf (1, 2); /*****************************************************************************/ -guint nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms); +guint nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_ms); guint nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms); -const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms); const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms); #define nmtstp_assert_wait_for_signal(platform, timeout_ms) \ @@ -120,10 +121,45 @@ gboolean nmtstp_run_command_check_external (int external_command); /*****************************************************************************/ -gboolean nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); - -void _nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); -#define nmtstp_assert_ip4_route_exists(platform, exists, ifname, network, plen, metric) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, exists, ifname, network, plen, metric) +const NMPlatformIP4Route *_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos); +#define nmtstp_assert_ip4_route_exists(platform, c_exists, ifname, network, plen, metric, tos) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, tos) + +const NMPlatformIP4Route *nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos); + +const NMPlatformIP6Route *_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); +#define nmtstp_assert_ip6_route_exists(platform, c_exists, ifname, network, plen, metric, src, src_plen) _nmtstp_assert_ip6_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, src, src_plen) + +const NMPlatformIP6Route *nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); /*****************************************************************************/ @@ -187,8 +223,36 @@ void nmtstp_ip6_route_add (NMPlatform *platform, guint32 metric, guint32 mss); +static inline GPtrArray * +nmtstp_ip4_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_addrroute_clone (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL); +} + +static inline GPtrArray * +nmtstp_ip6_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_addrroute_clone (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL); +} + /*****************************************************************************/ +GArray *nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex); +GArray *nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex); + +gboolean nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric); +gboolean nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); + const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type); const NMPlatformLink *nmtstp_link_get (NMPlatform *platform, int ifindex, const char *name); @@ -197,6 +261,10 @@ void nmtstp_link_set_updown (NMPlatform *platform, int ifindex, gboolean up); +const NMPlatformLink *nmtstp_link_veth_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const char *peer); const NMPlatformLink *nmtstp_link_dummy_add (NMPlatform *platform, gboolean external_command, const char *name); @@ -231,6 +299,100 @@ void nmtstp_link_del (NMPlatform *platform, int ifindex, const char *name); +/*****************************************************************************/ + +extern int NMTSTP_ENV1_IFINDEX; +extern int NMTSTP_ENV1_EX; + +static inline void +_nmtstp_env1_wrapper_setup (const NmtstTestData *test_data) +{ + int *p_ifindex; + gpointer p_ifup; + + nmtst_test_data_unpack (test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup); + + g_assert (p_ifindex && *p_ifindex == -1); + + _LOGT ("TEST[%s]: setup", test_data->testpath); + + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); + + *p_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + g_assert_cmpint (*p_ifindex, >, 0); + g_assert_cmpint (NMTSTP_ENV1_IFINDEX, ==, -1); + + if (GPOINTER_TO_INT (p_ifup)) + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, *p_ifindex, NULL)); + + nm_platform_process_events (NM_PLATFORM_GET); + + NMTSTP_ENV1_IFINDEX = *p_ifindex; + NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global (); +} + +static inline void +_nmtstp_env1_wrapper_run (gconstpointer user_data) +{ + const NmtstTestData *test_data = user_data; + GTestDataFunc test_func_data; + GTestFunc test_func; + gconstpointer d; + + nmtst_test_data_unpack (test_data, NULL, &test_func, &test_func_data, &d, NULL); + + _LOGT ("TEST[%s]: run", test_data->testpath); + if (test_func) + test_func (); + else + test_func_data (d); +} + +static inline void +_nmtstp_env1_wrapper_teardown (const NmtstTestData *test_data) +{ + int *p_ifindex; + + nmtst_test_data_unpack (test_data, &p_ifindex, NULL, NULL, NULL, NULL); + + g_assert_cmpint (NMTSTP_ENV1_IFINDEX, ==, *p_ifindex); + NMTSTP_ENV1_IFINDEX = -1; + + _LOGT ("TEST[%s]: teardown", test_data->testpath); + + g_assert_cmpint (*p_ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, *p_ifindex)); + + nm_platform_process_events (NM_PLATFORM_GET); + + _LOGT ("TEST[%s]: finished", test_data->testpath); + + *p_ifindex = -1; +} + +/* add test function, that set's up a particular environment, consisting + * of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */ +#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \ + nmtst_add_test_func_full (testpath, \ + _nmtstp_env1_wrapper_run, \ + _nmtstp_env1_wrapper_setup, \ + _nmtstp_env1_wrapper_teardown, \ + ({ static int _ifindex = -1; &_ifindex; }), \ + ({ GTestFunc _test_func = (test_func); _test_func; }), \ + ({ GTestDataFunc _test_func = (test_data_func); _test_func; }), \ + (arg), \ + ({ gboolean _ifup = (ifup); GINT_TO_POINTER (_ifup);})) + +#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup) + +#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup) + +/*****************************************************************************/ + typedef void (*NMTstpSetupFunc) (void); extern NMTstpSetupFunc const _nmtstp_setup_platform_func; diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index e772662c..342aa0d6 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -44,7 +44,7 @@ static void test_link_get_all (void) { gs_unref_object NMPlatform *platform = NULL; - gs_unref_array GArray *links = NULL; + gs_unref_ptrarray GPtrArray *links = NULL; platform = nm_linux_platform_new (TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index ed435567..9c72371c 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -76,7 +76,7 @@ test_bogus(void) g_assert (!addrlen); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); - g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU) != NM_PLATFORM_ERROR_SUCCESS); g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); @@ -264,7 +264,8 @@ test_slave (int master, int type, SignalData *master_changed) } g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - if (nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { + if ( nmtstp_is_root_test () + && nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { if (nm_platform_link_get_type (NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if @@ -285,7 +286,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); /* Enslave again * @@ -294,7 +295,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); accept_signals (link_changed, 0, 2); - ensure_no_signal (master_changed); + accept_signals (master_changed, 0, 2); /* Set slave option */ switch (type) { @@ -327,7 +328,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); accept_signal (link_removed); } - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); ensure_no_signal (master_changed); @@ -511,7 +512,8 @@ test_bridge_addr (void) plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); - if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { + if (nm_platform_check_kernel_support (NM_PLATFORM_GET, + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); @@ -601,7 +603,7 @@ test_internal (void) accept_signal (link_changed); /* Set MTU */ - g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU)); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU) == NM_PLATFORM_ERROR_SUCCESS); g_assert_cmpint (nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal (link_changed); @@ -790,7 +792,7 @@ test_software_detect (gconstpointer user_data) * namespaced, the creation can fail if a macvtap in another namespace * has the same index. Try to detect this situation and skip already * used indexes. - * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17af2bce88d31e65ed73d638bb752d2e13c66ced + * The fix (17af2bce) is included kernel 4.7, dated 24 July, 2016. */ for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) { snprintf (buf, sizeof (buf), "/sys/class/macvtap/tap%d", i); @@ -1713,9 +1715,8 @@ test_nl_bugs_veth (void) NMTstpNamespaceHandle *ns_handle = NULL; /* create veth pair. */ - nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); - ifindex_veth0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; - ifindex_veth1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex; + ifindex_veth0 = nmtstp_link_veth_add (NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; + ifindex_veth1 = nmtstp_link_get_typed (NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex; /* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */ g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i)); @@ -1728,8 +1729,8 @@ test_nl_bugs_veth (void) pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); g_assert (pllink_veth0); if (pllink_veth0->parent == 0) { - /* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder - * of the test. */ + /* Kernels prior to 4.1 dated 21 June, 2015 don't support exposing the veth peer + * as IFA_LINK. skip the remainder of the test. */ goto out; } g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); @@ -2023,8 +2024,8 @@ test_netns_general (gpointer fixture, gconstpointer test_data) _sysctl_assert_eq (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6", NULL); _sysctl_assert_eq (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6", NULL); - /* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and - * skip asserts that are known to fail. */ + /* Kernels prior to 3.19 dated 8 February, 2015 don't support ethtool -i for dummy devices. + * Work around that and skip asserts that are known to fail. */ ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0; if (ethtool_support) { g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 42dfc572..a02388d2 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -33,6 +33,60 @@ struct { /*****************************************************************************/ +static void +test_obj_base (void) +{ + static const union { + GObject g; + NMPObject k; + } x = { }; + static const union { + GTypeClass k; + NMPClass c; + } l = { }; + static const GObject *g = &x.g; + static const GTypeClass *k = &l.k; + static const NMPObject *o = &x.k; + static const NMPClass *c = &l.c; + + NMObjBaseInst *obj; + gs_unref_object GCancellable *obj_cancellable = g_cancellable_new (); + nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10); + +#define STATIC_ASSERT(cond) \ + G_STMT_START { \ + G_STATIC_ASSERT (cond); \ + G_STATIC_ASSERT_EXPR (cond); \ + g_assert (cond); \ + } G_STMT_END + + STATIC_ASSERT (&g->g_type_instance == (void *) &o->_class); + STATIC_ASSERT (&g->g_type_instance.g_class == (void *) &o->_class); + + STATIC_ASSERT (sizeof (o->parent.parent) == sizeof (GTypeInstance)); + + STATIC_ASSERT (&c->parent == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type_class == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == &k->g_type); + + STATIC_ASSERT (sizeof (c->parent.parent) == sizeof (GTypeClass)); + + STATIC_ASSERT (&o->parent == (void *) o); + STATIC_ASSERT (&o->parent.klass == (void *) &o->_class); + + obj = (NMObjBaseInst *) obj_cancellable; + g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + + obj = (NMObjBaseInst *) obj_link; + g_assert (NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (!G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + +} + +/*****************************************************************************/ + static gboolean _nmp_object_id_equal (const NMPObject *a, const NMPObject *b) { @@ -56,150 +110,144 @@ _nmp_object_equal (const NMPObject *a, const NMPObject *b) /*****************************************************************************/ static void -_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains) +_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMDedupMultiHeadEntry *head_entry, const NMPObject *obj, gboolean visible_only, gboolean contains) { - const NMPlatformObject *const *objects; - guint i, len; + NMDedupMultiIter iter; gboolean found; + guint i, len; + const NMPObject *o; - g_assert (cache_id); g_assert (NMP_OBJECT_IS_VALID (obj)); g_assert (nmp_cache_lookup_obj (cache, obj) == obj); + g_assert (!head_entry || (head_entry->len > 0 && c_list_length (&head_entry->lst_entries_head) == head_entry->len)); - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - - g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len])); + len = head_entry ? head_entry->len : 0; found = FALSE; - for (i = 0; i < len; i++) { - NMPObject *o; - - g_assert (objects[i]); - o = NMP_OBJECT_UP_CAST (objects[i]); + i = 0; + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { g_assert (NMP_OBJECT_IS_VALID (o)); - if (obj == o) { - g_assert (!found); - found = TRUE; + if ( !visible_only + || nmp_object_is_visible (o)) { + g_assert (!found); + found = TRUE; + } } + i++; } + g_assert (len == i); g_assert (!!contains == found); } -/*****************************************************************************/ +static void +_assert_cache_multi_lookup_contains_link (const NMPCache *cache, + gboolean visible_only, + const NMPObject *obj, + gboolean contains) +{ + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; -typedef struct { - NMPCache *cache; - NMPCacheOpsType expected_ops_type; - const NMPObject *obj_clone; - NMPObject *new_clone; - gboolean was_visible; - gboolean called; -} _NMPCacheUpdateData; + g_assert (cache); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + head_entry = nmp_cache_lookup (cache, &lookup); + _assert_cache_multi_lookup_contains (cache, head_entry, obj, visible_only, contains); +} + +/*****************************************************************************/ static void -_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +ops_post_check (NMPCache *cache, + NMPCacheOpsType ops_type, + const NMPObject *obj_old, + const NMPObject *obj_new, + const NMPObject *obj_new_expected, + NMPCacheOpsType expected_ops_type) { - _NMPCacheUpdateData *data = user_data; - - g_assert (data); - g_assert (!data->called); - g_assert (data->cache == cache); + g_assert (cache); - g_assert_cmpint (data->expected_ops_type, ==, ops_type); + g_assert_cmpint (expected_ops_type, ==, ops_type); switch (ops_type) { case NMP_CACHE_OPS_ADDED: - g_assert (!old); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); + g_assert (!obj_old); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); break; case NMP_CACHE_OPS_UPDATED: - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); - g_assert (nmp_object_id_equal (old, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); - g_assert (!nmp_object_equal (data->obj_clone, old)); - g_assert (!nmp_object_equal (old, new)); + g_assert (obj_old != obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + g_assert (nmp_object_id_equal (obj_old, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); + g_assert (!nmp_object_equal (obj_new_expected, obj_old)); + g_assert (!nmp_object_equal (obj_old, obj_new)); break; case NMP_CACHE_OPS_REMOVED: - g_assert (!new); - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); + g_assert (!obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + if (obj_new_expected) + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + break; + case NMP_CACHE_OPS_UNCHANGED: + g_assert (obj_old == obj_new); + if (obj_old) { + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_equal (obj_old, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + } else + g_assert (!obj_new_expected); break; default: g_assert_not_reached (); } - - data->was_visible = old ? nmp_object_is_visible (old) : FALSE; - data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL; - data->called = TRUE; } static void -_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type) +_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **out_obj_old, const NMPObject **out_obj_new, NMPCacheOpsType expected_ops_type) { NMPCacheOpsType ops_type; - NMPObject *obj2; - gboolean was_visible; - nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); - nm_auto_nmpobj NMPObject *new_clone = NULL; + const NMPObject *obj_prev; const NMPObject *obj_old; - _NMPCacheUpdateData data = { - .cache = cache, - .expected_ops_type = expected_ops_type, - .obj_clone = obj_clone, - }; - - obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex); - if (obj_old && obj_old->_link.udev.device) - obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device); - _nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache)); + const NMPObject *obj_new; + nm_auto_nmpobj NMPObject *obj_new_expected = NULL; g_assert (cache); g_assert (NMP_OBJECT_IS_VALID (obj)); - ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data); - - new_clone = data.new_clone; - - g_assert_cmpint (ops_type, ==, expected_ops_type); + obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex); + obj_new_expected = nmp_object_clone (obj, FALSE); + if (obj_prev && obj_prev->_link.udev.device) + obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device); + _nmp_object_fixup_link_udev_fields (&obj_new_expected, NULL, nmp_cache_use_udev_get (cache)); - if (ops_type != NMP_CACHE_OPS_UNCHANGED) { - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (data.called); - g_assert_cmpint (data.was_visible, ==, was_visible); + ops_type = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); + ops_post_check (cache, ops_type, obj_old, obj_new, + nmp_object_is_alive (obj_new_expected) ? obj_new_expected : NULL, + expected_ops_type); - if (ops_type == NMP_CACHE_OPS_REMOVED) - g_assert (!data.new_clone); - else { - g_assert (data.new_clone); - g_assert (nmp_object_equal (obj2, data.new_clone)); - } - } else { - g_assert (!data.called); - g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2)); - } - - g_assert (!obj2 || nmp_object_id_equal (obj, obj2)); - if (ops_type != NMP_CACHE_OPS_REMOVED && obj2) - g_assert (nmp_object_equal (obj, obj2)); - - if (out_obj) - *out_obj = obj2; + if (out_obj_new) + *out_obj_new = obj_new; else - nmp_object_unref (obj2); - if (out_was_visible) - *out_was_visible = was_visible; + nmp_object_unref (obj_new); + if (out_obj_old) + *out_obj_old = obj_old; + else + nmp_object_unref (obj_old); } static const NMPlatformLink pl_link_2 = { @@ -218,168 +266,189 @@ static void test_cache_link (void) { NMPCache *cache; - NMPObject *obj1, *obj2; + NMPObject *objm1; + const NMPObject *obj_old, *obj_new; NMPObject objs1; - gboolean was_visible; - NMPCacheId cache_id_storage; struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0); struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0); NMPCacheOpsType ops_type; + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; + + multi_idx = nm_dedup_multi_index_new (); - cache = nmp_cache_new (nmtst_get_rand_int () % 2); + cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2); /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (NMP_OBJECT_UP_CAST (&objm1->object) == objm1); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (!obj2); - g_assert (!was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); + nmp_object_unref (objm1); /* Only when setting @is_in_netlink the link is added. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (objm1 == obj_new); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); /* updating the same link with identical value, has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new); + g_assert (obj_new != objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); + nmp_object_unref (obj_new); /* remove the link from netlink */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); if (udev_device_2) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new); ASSERT_nmp_cache_is_consistent (cache); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (obj_new); } /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + if (udev_device_2) { + g_assert (obj_old); + g_assert (!nmp_object_is_visible (obj_old)); + } else + g_assert (!obj_old); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove again from netlink. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = FALSE; - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = FALSE; + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); + if (udev_device_2) + g_assert (obj_new == objm1); + else + g_assert (!obj_new); + g_assert (obj_old); + g_assert (nmp_object_is_alive (obj_old)); if (udev_device_2) { - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); } else { - g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL); + g_assert (nmp_cache_lookup_obj (cache, objm1) == NULL); g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL); - g_assert (nmp_object_is_visible (obj2)); + g_assert (nmp_object_is_visible (obj_new)); } - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* now another link only with aspect UDEV. */ if (udev_device_3) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (!was_visible); - g_assert (!nmp_object_is_visible (obj2)); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); - g_assert_cmpint (obj2->link.initialized, ==, FALSE); - nmp_object_unref (obj2); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (!obj_old); + g_assert (!nmp_object_is_visible (obj_new)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj_new->link.initialized, ==, FALSE); + nmp_object_unref (obj_new); /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new == objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (!obj_old || !nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache)); - nmp_object_unref (obj2); + g_assert (obj_old && nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, !nmp_cache_use_udev_get (cache)); + nmp_object_unref (obj_new); + nmp_object_unref (obj_old); } nmp_cache_free (cache); @@ -429,6 +498,7 @@ main (int argc, char **argv) udev_enumerate_unref (enumerator); } + g_test_add_func ("/nmp-object/obj-base", test_obj_base); g_test_add_func ("/nmp-object/cache_link", test_cache_link); result = g_test_run (); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 9960d867..2c00fada 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -27,13 +27,50 @@ #include "test-common.h" -#define DEVICE_NAME "nm-test-device" +#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX +#define EX NMTSTP_ENV1_EX + +static void +_wait_for_ipv6_addr_non_tentative (NMPlatform *platform, + gint64 timeout_ms, + int ifindex, + guint addr_n, + const struct in6_addr *addrs) +{ + guint i; + + /* Wait that the addresses become non-tentative. Dummy interfaces are NOARP + * and thus don't do DAD, but the kernel sets the address as tentative for a + * small amount of time, which prevents the immediate addition of the route + * with RTA_PREFSRC */ + + NMTST_WAIT_ASSERT (400, { + gboolean should_wait = FALSE; + const NMPlatformIP6Address *plt_addr; + + for (i = 0; i < addr_n; i++) { + plt_addr = nm_platform_ip6_address_get (platform, ifindex, addrs[i]); + if ( !plt_addr + || NM_FLAGS_HAS (plt_addr->n_ifa_flags, IFA_F_TENTATIVE)) { + should_wait = TRUE; + break; + } + } + if (!should_wait) + return; + nmtstp_assert_wait_for_signal (platform, + (nmtst_wait_end_us - g_get_monotonic_time ()) / 1000); + }); +} + static void ip4_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformIP4Route *received, int change_type_i, SignalData *data) { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; + NMPObject o_id; + nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert (received); @@ -41,6 +78,11 @@ ip4_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP g_assert (data && data->name); g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED); + /* run code for initializing the ID only */ + nmp_object_stackinit_id (&o_id, NMP_OBJECT_UP_CAST (received)); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), TRUE); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), FALSE); + if (data->ifindex && data->ifindex != received->ifindex) return; if (data->change_type != change_type) @@ -58,6 +100,8 @@ ip6_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; + NMPObject o_id; + nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert (received); @@ -65,6 +109,11 @@ ip6_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMP g_assert (data && data->name); g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED); + /* run code for initializing the ID only */ + nmp_object_stackinit_id (&o_id, NMP_OBJECT_UP_CAST (received)); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), TRUE); + nmp_object_copy (o_id_p, NMP_OBJECT_UP_CAST (received), FALSE); + if (data->ifindex && data->ifindex != received->ifindex) return; if (data->change_type != change_type) @@ -90,50 +139,50 @@ test_ip4_route_metric0 (void) int mss = 1000; /* No routes initially */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); /* add the first route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Deleting route with metric 0 does nothing */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* add the second route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 again (we expect nothing to happen) */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete the other route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); free_signal (route_added); free_signal (route_changed); @@ -147,7 +196,7 @@ test_ip4_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[3]; in_addr_t network; guint8 plen = 24; @@ -164,9 +213,9 @@ test_ip4_route (void) accept_signal (route_added); /* Add route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_added); /* Add route again */ @@ -174,9 +223,9 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, 0, 0, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, 0, 0, metric, 0); accept_signal (route_added); /* Add default route again */ @@ -184,7 +233,7 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -211,23 +260,23 @@ test_ip4_route (void) rts[2].mss = mss; rts[2].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); /* Remove default route */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric)); accept_signal (route_removed); /* Remove route to gateway */ - g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric)); + g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric)); accept_signal (route_removed); free_signal (route_added); @@ -242,7 +291,7 @@ test_ip6_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP6Route rts[3]; struct in6_addr network; guint8 plen = 64; @@ -259,28 +308,16 @@ test_ip6_route (void) NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0)); accept_signals (route_added, 0, 1); - /* Wait that the address becomes non-tentative. Dummy interfaces are NOARP - * and thus don't do DAD, but the kernel sets the address as tentative for a - * small amount of time, which prevents the immediate addition of the route - * with RTA_PREFSRC */ - NMTST_WAIT_ASSERT (200, { - const NMPlatformIP6Address *plt_addr; - - nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); - nm_platform_process_events (NM_PLATFORM_GET); - plt_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, pref_src, 128); - if (plt_addr && !NM_FLAGS_HAS (plt_addr->n_ifa_flags, IFA_F_TENTATIVE)) - break; - }); + _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 200, ifindex, 1, &pref_src); /* Add route to gateway */ nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, in6addr_any, metric, mss); accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_added); /* Add route again */ @@ -288,9 +325,9 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); accept_signal (route_added); /* Add default route again */ @@ -298,7 +335,7 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -325,23 +362,23 @@ test_ip6_route (void) rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); /* Remove default route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); accept_signal (route_removed); /* Remove route to gateway */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric)); + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric)); accept_signal (route_removed); free_signal (route_added); @@ -352,6 +389,45 @@ test_ip6_route (void) /*****************************************************************************/ static void +test_ip_route_get (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + in_addr_t a; + NMPlatformError result; + nm_auto_nmpobj NMPObject *route = NULL; + const NMPlatformIP4Route *r; + + nmtstp_run_command_check ("ip route add 1.2.3.0/24 dev %s", DEVICE_NAME); + + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); + if (nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.0"), 24, 0, 0)) + break; + }); + + a = nmtst_inet4_from_string ("1.2.3.1"); + result = nm_platform_ip_route_get (NM_PLATFORM_GET, + AF_INET, + &a, + nmtst_get_rand_int () % 2 ? 0 : ifindex, + &route); + + g_assert (result == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMP_OBJECT_GET_TYPE (route) == NMP_OBJECT_TYPE_IP4_ROUTE); + g_assert (!NMP_OBJECT_IS_STACKINIT (route)); + g_assert (route->parent._ref_count == 1); + r = NMP_OBJECT_CAST_IP4_ROUTE (route); + g_assert (r->rt_cloned); + g_assert (r->ifindex == ifindex); + g_assert (r->network == a); + g_assert (r->plen == 32); + + nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); + + nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); +} + +static void test_ip4_zero_gateway (void) { int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -361,15 +437,14 @@ test_ip4_zero_gateway (void) NMTST_WAIT_ASSERT (100, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); - if ( nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0) - && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0)) + if ( nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0, 0) + && nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0, 0)) break; }); nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); - nm_platform_process_events (NM_PLATFORM_GET); } static void @@ -378,7 +453,7 @@ test_ip4_route_options (void) int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); NMPlatformIP4Route route = { }; in_addr_t network; - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[1]; inet_pton (AF_INET, "172.16.1.0", &network); @@ -396,12 +471,10 @@ test_ip4_route_options (void) route.mtu = 1350; route.lock_cwnd = TRUE; - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route) == NM_PLATFORM_ERROR_SUCCESS); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); @@ -416,69 +489,268 @@ test_ip4_route_options (void) rts[0].initrwnd = 50; rts[0].mtu = 1350; rts[0].lock_cwnd = TRUE; - g_assert_cmpint (routes->len, ==, 1); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); /* Remove route */ - /* FIXME. Due to a bug, we cannot delete routes with non-zero TOS. See bgo#785004. */ - //g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20)); + g_assert (nm_platform_ip_route_delete (NM_PLATFORM_GET, routes->pdata[0])); - g_array_unref (routes); + g_ptr_array_unref (routes); } static void -test_ip6_route_options (void) +test_ip6_route_options (gconstpointer test_data) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); - NMPlatformIP6Route route = { }; - struct in6_addr network; - GArray *routes; - NMPlatformIP6Route rts[3]; - - inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network); - - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - route.network = network; - route.plen = 64; - route.gateway = in6addr_any; - route.metric = 1024; - route.window = 20000; - route.cwnd = 8; - route.initcwnd = 22; - route.initrwnd = 33; - route.mtu = 1300; - route.lock_mtu = TRUE; - - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &route)); - - /* Test route listing */ - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - memset (rts, 0, sizeof (rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - rts[0].network = network; - rts[0].plen = 64; - rts[0].ifindex = ifindex; - rts[0].gateway = in6addr_any; - rts[0].metric = 1024; - rts[0].window = 20000; - rts[0].cwnd = 8; - rts[0].initcwnd = 22; - rts[0].initrwnd = 33; - rts[0].mtu = 1300; - rts[0].lock_mtu = TRUE; - - g_assert_cmpint (routes->len, ==, 1); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + GPtrArray *routes; +#define RTS_MAX 3 + NMPlatformIP6Route rts_add[RTS_MAX] = { }; + NMPlatformIP6Route rts_cmp[RTS_MAX] = { }; + NMPlatformIP6Address addr[1] = { }; + struct in6_addr addr_in6[G_N_ELEMENTS (addr)] = { }; + guint rts_n = 0; + guint addr_n = 0; + guint i; + + switch (TEST_IDX) { + case 1: + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:a:b:0:0:0:0"), + .plen = 64, + .gateway = in6addr_any, + .metric = 1024, + .window = 20000, + .cwnd = 8, + .initcwnd = 22, + .initrwnd = 33, + .mtu = 1300, + .lock_mtu = TRUE, + }); + break; + case 2: + addr[addr_n++] = ((NMPlatformIP6Address) { + .ifindex = IFINDEX, + .address = *nmtst_inet6_from_string ("2000::2"), + .plen = 128, + .peer_address = in6addr_any, + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .n_ifa_flags = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("1010::1"), + .plen = 128, + .gateway = in6addr_any, + .metric = 256, + .pref_src = *nmtst_inet6_from_string ("2000::2"), + }); + break; + case 3: + addr[addr_n++] = ((NMPlatformIP6Address) { + .ifindex = IFINDEX, + .address = *nmtst_inet6_from_string ("2001:db8:8086::5"), + .plen = 128, + .peer_address = in6addr_any, + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .n_ifa_flags = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 110, + .metric = 10021, + .mss = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), + .metric = 21, + .mss = 0, + }); + break; + default: + g_assert_not_reached (); + } + + for (i = 0; i < addr_n; i++) { + g_assert (addr[i].ifindex == IFINDEX); + addr_in6[i] = addr[i].address; + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, + IFINDEX, + addr[i].address, + addr[i].plen, + addr[i].peer_address, + addr[i].lifetime, + addr[i].preferred, + addr[i].n_ifa_flags)); + } + + _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6); + + for (i = 0; i < rts_n; i++) + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS); + + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX); + switch (TEST_IDX) { + case 1: + case 2: + case 3: + for (i = 0; i < rts_n; i++) { + rts_cmp[i] = rts_add[i]; + rts_cmp[i].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); + } + break; + default: + g_assert_not_reached (); + } + g_assert_cmpint (routes->len, ==, rts_n); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts_cmp, routes->len, TRUE); + g_ptr_array_unref (routes); + + for (i = 0; i < rts_n; i++) { + g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, IFINDEX, + rts_add[i].network, rts_add[i].plen, + rts_add[i].metric)); + } + + for (i = 0; i < addr_n; i++) { + nmtstp_ip6_address_del (NM_PLATFORM_GET, + EX, + IFINDEX, + rts_add[i].network, + rts_add[i].plen); + } +} - /* Remove route */ - g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, 64, 1024)); +/*****************************************************************************/ - g_array_unref (routes); +static void +test_ip (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + guint i, j, k; + const NMPlatformLink *l; + char ifname[IFNAMSIZ]; + char ifname2[IFNAMSIZ]; + char s1[NM_UTILS_INET_ADDRSTRLEN]; + NMPlatform *platform = NM_PLATFORM_GET; + const int EX_ = -1; + struct { + int ifindex; + } iface_data[10] = { 0 }; + int order_idx[G_N_ELEMENTS (iface_data)] = { 0 }; + guint order_len; + guint try; + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) { + nm_sprintf_buf (ifname, "v%02u", i); + nm_sprintf_buf (ifname2, "w%02u", i); + + g_assert (!nm_platform_link_get_by_ifname (platform, ifname)); + g_assert (!nm_platform_link_get_by_ifname (platform, ifname2)); + l = nmtstp_link_veth_add (platform, EX_, ifname, ifname2); + iface_data[i].ifindex = l->ifindex; + + nmtstp_link_set_updown (platform, EX_, iface_data[i].ifindex, TRUE); + nmtstp_link_set_updown (platform, EX_, nmtstp_link_get (platform, -1, ifname2)->ifindex, TRUE); + + nm_sprintf_buf (s1, "192.168.7.%d", 100 + i); + nmtstp_ip4_address_add (platform, + EX_, + iface_data[i].ifindex, + nmtst_inet4_from_string (s1), + 24, + nmtst_inet4_from_string (s1), + 3600, + 3600, + 0, + NULL); + } + + order_len = 0; + for (try = 0; try < 5 * G_N_ELEMENTS (order_idx); try++) { + NMPObject o; + NMPlatformIP4Route *r; + guint idx; + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; + + nmp_object_stackinit (&o, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + r = NMP_OBJECT_CAST_IP4_ROUTE (&o); + r->network = nmtst_inet4_from_string ("192.168.9.0"); + r->plen = 24; + r->metric = 109; + + if ( order_len == 0 + || ( order_len < G_N_ELEMENTS (order_idx) + && nmtst_get_rand_int () % 2)) { +again_find_idx: + idx = nmtst_get_rand_int () % G_N_ELEMENTS (iface_data); + for (i = 0; i < order_len; i++) { + if (order_idx[i] == idx) + goto again_find_idx; + } + order_idx[order_len++] = idx; + + r->ifindex = iface_data[idx].ifindex; + g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r) == NM_PLATFORM_ERROR_SUCCESS); + } else { + i = nmtst_get_rand_int () % order_len; + idx = order_idx[i]; + for (i++; i < order_len; i++) + order_idx[i - 1] = order_idx[i]; + order_len--; + + r->ifindex = iface_data[idx].ifindex; + g_assert (nm_platform_ip_route_delete (platform, &o)); + } + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_IP4_ROUTE)); + for (j = 0; j < G_N_ELEMENTS (iface_data); j++) { + gboolean has; + NMDedupMultiIter iter; + const NMPObject *o_cached; + + has = FALSE; + for (k = 0; k < order_len; k++) { + if (order_idx[k] == j) { + g_assert (!has); + has = TRUE; + } + } + + nmp_cache_iter_for_each (&iter, head_entry, &o_cached) { + const NMPlatformIP4Route *r_cached = NMP_OBJECT_CAST_IP4_ROUTE (o_cached); + + if ( r_cached->ifindex != iface_data[j].ifindex + || r_cached->metric != 109) + continue; + + g_assert (has); + has = FALSE; + } + g_assert (!has); + } + } + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) + g_assert (nm_platform_link_delete (platform, iface_data[i].ifindex)); + + (void) TEST_IDX; + (void) IFINDEX; } /*****************************************************************************/ @@ -494,22 +766,19 @@ _nmtstp_init_tests (int *argc, char ***argv) void _nmtstp_setup_tests (void) { - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); - - g_test_add_func ("/route/ip4", test_ip4_route); - g_test_add_func ("/route/ip6", test_ip6_route); - g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); - g_test_add_func ("/route/ip4_options", test_ip4_route_options); - g_test_add_func ("/route/ip6_options", test_ip6_route_options); - - if (nmtstp_is_root_test ()) - g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE) +#define add_test_func_data(testpath, test_func, arg) nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE) + add_test_func ("/route/ip4", test_ip4_route); + add_test_func ("/route/ip6", test_ip6_route); + add_test_func ("/route/ip4_metric0", test_ip4_route_metric0); + add_test_func ("/route/ip4_options", test_ip4_route_options); + add_test_func_data ("/route/ip6_options/1", test_ip6_route_options, GINT_TO_POINTER (1)); + add_test_func_data ("/route/ip6_options/2", test_ip6_route_options, GINT_TO_POINTER (2)); + add_test_func_data ("/route/ip6_options/3", test_ip6_route_options, GINT_TO_POINTER (3)); + + if (nmtstp_is_root_test ()) { + add_test_func_data ("/route/ip/1", test_ip, GINT_TO_POINTER (1)); + add_test_func ("/route/ip_route_get", test_ip_route_get); + add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); + } } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index 06eb7cb9..a5f25b02 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -46,6 +46,24 @@ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END +/*****************************************************************************/ + +static int +_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); +} +#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) + +static int +_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); +} +#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) + /***************************************************************************** * Copied from libnl3/genl: *****************************************************************************/ @@ -131,7 +149,7 @@ genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) static int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], - int maxtype, struct nla_policy *policy) + int maxtype, const struct nla_policy *policy) { struct genlmsghdr *ghdr; @@ -150,7 +168,7 @@ genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], static int probe_response (struct nl_msg *msg, void *arg) { - static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, .maxlen = GENL_NAMSIZ }, @@ -389,7 +407,7 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) struct nlattr *tb[NL80211_ATTR_MAX + 1]; if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), - genlmsg_attrlen (gnlh, 0), NULL) < 0) + genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; if (!tb[NL80211_ATTR_IFTYPE]) @@ -529,7 +547,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct nlattr *bss[NL80211_BSS_MAX + 1]; - static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + static const struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { [NL80211_BSS_TSF] = { .type = NLA_U64 }, [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, [NL80211_BSS_BSSID] = { }, @@ -543,15 +561,15 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) guint32 status; if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), - genlmsg_attrlen (gnlh, 0), NULL) < 0) + genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; if (tb[NL80211_ATTR_BSS] == NULL) return NL_SKIP; if (nla_parse_nested (bss, NL80211_BSS_MAX, - tb[NL80211_ATTR_BSS], - bss_policy)) + tb[NL80211_ATTR_BSS], + bss_policy)) return NL_SKIP; if (bss[NL80211_BSS_STATUS] == NULL) @@ -665,7 +683,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; - static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + static const struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, @@ -678,7 +696,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, }; - static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + static const struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, @@ -871,7 +889,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) int rem_freq; int rem_band; int freq_idx; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + static const struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, #ifdef NL80211_FREQUENCY_ATTR_NO_IR diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index 1bc29ae8..c4d3c999 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -97,8 +97,7 @@ wifi_wext_deinit (WifiData *parent) { WifiDataWext *wext = (WifiDataWext *) parent; - if (wext->fd >= 0) - close (wext->fd); + nm_close (wext->fd); } static gboolean @@ -757,7 +756,7 @@ wifi_wext_is_wifi (const char *iface) nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface); if (ioctl (fd, SIOCGIWNAME, &iwr) == 0) is_wifi = TRUE; - close (fd); + nm_close (fd); } return is_wifi; } diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c index d67c0a99..ad3307a9 100644 --- a/src/ppp/nm-ppp-manager-call.c +++ b/src/ppp/nm-ppp-manager-call.c @@ -98,6 +98,22 @@ nm_ppp_manager_create (const char *iface, GError **error) return ret; } +void +nm_ppp_manager_set_route_parameters (NMPPPManager *self, + guint32 ip4_route_table, + guint32 ip4_route_metric, + guint32 ip6_route_table, + guint32 ip6_route_metric) +{ + g_return_if_fail (ppp_ops); + + ppp_ops->set_route_parameters (self, + ip4_route_table, + ip4_route_metric, + ip6_route_table, + ip6_route_metric); +} + gboolean nm_ppp_manager_start (NMPPPManager *self, NMActRequest *req, diff --git a/src/ppp/nm-ppp-manager-call.h b/src/ppp/nm-ppp-manager-call.h index f21005f0..2258ae08 100644 --- a/src/ppp/nm-ppp-manager-call.h +++ b/src/ppp/nm-ppp-manager-call.h @@ -25,6 +25,13 @@ NMPPPManager * nm_ppp_manager_create (const char *iface, GError **error); + +void nm_ppp_manager_set_route_parameters (NMPPPManager *ppp_manager, + guint32 ip4_route_table, + guint32 ip4_route_metric, + guint32 ip6_route_table, + guint32 ip6_route_metric); + gboolean nm_ppp_manager_start (NMPPPManager *self, NMActRequest *req, const char *ppp_name, diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index 6343df8b..3ef3f3dc 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -42,6 +42,7 @@ #endif #include <linux/if.h> #include <linux/if_ppp.h> +#include <linux/rtnetlink.h> #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" @@ -105,6 +106,11 @@ typedef struct { char *ip_iface; int monitor_fd; guint monitor_id; + + guint32 ip4_route_table; + guint32 ip4_route_metric; + guint32 ip6_route_table; + guint32 ip6_route_metric; } NMPPPManagerPrivate; struct _NMPPPManager { @@ -132,6 +138,37 @@ static void _ppp_kill (NMPPPManager *manager); /*****************************************************************************/ +static void +_ppp_manager_set_route_paramters (NMPPPManager *self, + guint32 ip4_route_table, + guint32 ip4_route_metric, + guint32 ip6_route_table, + guint32 ip6_route_metric) +{ + NMPPPManagerPrivate *priv; + + g_return_if_fail (NM_IS_PPP_MANAGER (self)); + + priv = NM_PPP_MANAGER_GET_PRIVATE (self); + if ( priv->ip4_route_table != ip4_route_table + || priv->ip4_route_metric != ip4_route_metric + || priv->ip6_route_table != ip6_route_table + || priv->ip6_route_metric != ip6_route_metric) { + priv->ip4_route_table = ip4_route_table; + priv->ip4_route_metric = ip4_route_metric; + priv->ip6_route_table = ip6_route_table; + priv->ip6_route_metric = ip6_route_metric; + + _LOGT ("route-parameters: table-v4: %u, metric-v4: %u, table-v6: %u, metric-v6: %u", + priv->ip4_route_table, + priv->ip4_route_metric, + priv->ip6_route_table, + priv->ip6_route_metric); + } +} + +/*****************************************************************************/ + static gboolean monitor_cb (gpointer user_data) { @@ -400,16 +437,27 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, GVariant *config_dict) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - NMIP4Config *config; + gs_unref_object NMIP4Config *config = NULL; NMPlatformIP4Address address; - guint32 u32; + guint32 u32, mtu; GVariantIter *iter; + int ifindex; _LOGI ("(IPv4 Config Get) reply received."); nm_clear_g_source (&priv->ppp_timeout_handler); - config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); + if (!set_ip_config_common (manager, config_dict, NM_PPP_IP4_CONFIG_INTERFACE, &mtu)) + goto out; + + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); + if (ifindex <= 0) + goto out; + + config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex); + + if (mtu) + nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_PPP); memset (&address, 0, sizeof (address)); address.plen = 32; @@ -418,7 +466,15 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, address.address = u32; if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_GATEWAY, "u", &u32)) { - nm_ip4_config_set_gateway (config, u32); + const NMPlatformIP4Route r = { + .ifindex = ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_PPP, + .gateway = u32, + .table_coerced = nm_platform_route_table_coerce (priv->ip4_route_table), + .metric = priv->ip4_route_metric, + }; + + nm_ip4_config_add_route (config, &r, NULL); address.peer_address = u32; } else address.peer_address = address.address; @@ -446,17 +502,10 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, g_variant_iter_free (iter); } - if (!set_ip_config_common (manager, config_dict, NM_PPP_IP4_CONFIG_INTERFACE, &u32)) - goto out; - - if (u32) - nm_ip4_config_set_mtu (config, u32, NM_IP_CONFIG_SOURCE_PPP); - /* Push the IP4 config up to the device */ g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config); out: - g_object_unref (config); g_dbus_method_invocation_return_value (context, NULL); } @@ -495,23 +544,39 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager, GVariant *config_dict) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - NMIP6Config *config; + gs_unref_object NMIP6Config *config = NULL; NMPlatformIP6Address addr; struct in6_addr a; NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; gboolean has_peer = FALSE; + int ifindex; _LOGI ("(IPv6 Config Get) reply received."); nm_clear_g_source (&priv->ppp_timeout_handler); - config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); + if (!set_ip_config_common (manager, config_dict, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) + goto out; + + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); + if (ifindex <= 0) + goto out; + + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex); memset (&addr, 0, sizeof (addr)); addr.plen = 64; if (iid_value_to_ll6_addr (config_dict, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) { - nm_ip6_config_set_gateway (config, &a); + const NMPlatformIP6Route r = { + .ifindex = ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_PPP, + .gateway = a, + .table_coerced = nm_platform_route_table_coerce (priv->ip6_route_table), + .metric = priv->ip6_route_metric, + }; + + nm_ip6_config_add_route (config, &r, NULL); addr.peer_address = a; has_peer = TRUE; } @@ -521,14 +586,12 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager, addr.peer_address = addr.address; nm_ip6_config_add_address (config, &addr); - if (set_ip_config_common (manager, config_dict, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) { - /* Push the IPv6 config and interface identifier up to the device */ - g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config); - } + /* Push the IPv6 config and interface identifier up to the device */ + g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config); } else _LOGE ("invalid IPv6 address received!"); - g_object_unref (config); +out: g_dbus_method_invocation_return_value (context, NULL); } @@ -676,6 +739,7 @@ create_pppd_cmd_line (NMPPPManager *self, const char *pppd_binary = NULL; NMCmdLine *cmd; gboolean ppp_debug; + static int unit; g_return_val_if_fail (setting != NULL, NULL); @@ -840,6 +904,15 @@ create_pppd_cmd_line (NMPPPManager *self, nm_cmd_line_add_string (cmd, "plugin"); nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN); + if (pppoe && nm_setting_pppoe_get_parent (pppoe)) { + /* The PPP interface is going to be renamed, so pass a + * different unit each time so that activations don't + * race with each others. */ + nm_cmd_line_add_string (cmd, "unit"); + nm_cmd_line_add_int (cmd, unit); + unit = unit < G_MAXINT ? unit + 1 : 0; + } + return cmd; } @@ -1015,7 +1088,7 @@ _ppp_cleanup (NMPPPManager *manager) if (priv->monitor_fd >= 0) { /* Get the stats one last time */ monitor_cb (manager); - close (priv->monitor_fd); + nm_close (priv->monitor_fd); priv->monitor_fd = -1; } @@ -1155,7 +1228,7 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PARENT_IFACE: - g_free (priv->parent_iface); + /* construct-only */ priv->parent_iface = g_value_dup_string (value); break; default: @@ -1169,7 +1242,13 @@ set_property (GObject *object, guint prop_id, static void nm_ppp_manager_init (NMPPPManager *manager) { - NM_PPP_MANAGER_GET_PRIVATE (manager)->monitor_fd = -1; + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + priv->monitor_fd = -1; + priv->ip4_route_table = RT_TABLE_MAIN; + priv->ip4_route_metric = 460; + priv->ip6_route_table = RT_TABLE_MAIN; + priv->ip6_route_metric = 460; } static NMPPPManager * @@ -1279,9 +1358,10 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) } NMPPPOps ppp_ops = { - .create = _ppp_manager_new, - .start = _ppp_manager_start, - .stop_async = _ppp_manager_stop_async, - .stop_finish = _ppp_manager_stop_finish, - .stop_sync = _ppp_manager_stop_sync, + .create = _ppp_manager_new, + .set_route_parameters = _ppp_manager_set_route_paramters, + .start = _ppp_manager_start, + .stop_async = _ppp_manager_stop_async, + .stop_finish = _ppp_manager_stop_finish, + .stop_sync = _ppp_manager_stop_sync, }; diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h index b1a7bb60..35fb1b60 100644 --- a/src/ppp/nm-ppp-manager.h +++ b/src/ppp/nm-ppp-manager.h @@ -22,7 +22,7 @@ #ifndef __NM_PPP_MANAGER_H__ #define __NM_PPP_MANAGER_H__ -#define NM_PPP_MANAGER_PARENT_IFACE "parent-iface" +#define NM_PPP_MANAGER_PARENT_IFACE "parent-iface" #define NM_PPP_MANAGER_SIGNAL_STATE_CHANGED "state-changed" #define NM_PPP_MANAGER_SIGNAL_IP4_CONFIG "ip4-config" diff --git a/src/ppp/nm-ppp-plugin-api.h b/src/ppp/nm-ppp-plugin-api.h index 0a38fe05..bb53690c 100644 --- a/src/ppp/nm-ppp-plugin-api.h +++ b/src/ppp/nm-ppp-plugin-api.h @@ -24,6 +24,12 @@ typedef const struct { NMPPPManager *(*create) (const char *iface); + void (*set_route_parameters) (NMPPPManager *manager, + guint32 route_table_v4, + guint32 route_metric_v4, + guint32 route_table_v6, + guint32 route_metric_v6); + gboolean (*start) (NMPPPManager *manager, NMActRequest *req, const char *ppp_name, diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 3d6b1cfb..bcd17843 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -36,6 +36,7 @@ #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-utils/c-list.h" #include "introspection/org.freedesktop.NetworkManager.AgentManager.h" @@ -50,6 +51,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { NMAuthManager *auth_mgr; + NMSessionMonitor *session_monitor; /* Auth chains for checking agent permissions */ GSList *chains; @@ -59,7 +61,7 @@ typedef struct { */ GHashTable *agents; - GHashTable *requests; + CList requests; } NMAgentManagerPrivate; struct _NMAgentManager { @@ -123,7 +125,7 @@ typedef struct _NMAgentManagerCallId Request; static void request_add_agent (Request *req, NMSecretAgent *agent); -static void request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs); +static void request_remove_agent (Request *req, NMSecretAgent *agent); static void request_next_agent (Request *req); @@ -155,14 +157,62 @@ _request_type_to_string (RequestType request_type, gboolean verbose) /*****************************************************************************/ +struct _NMAgentManagerCallId { + CList lst_request; + + NMAgentManager *self; + + RequestType request_type; + + char *detail; + + NMAuthSubject *subject; + + /* Current agent being asked for secrets */ + NMSecretAgent *current; + NMSecretAgentCallId current_call_id; + + /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ + GSList *pending; + + guint idle_id; + + union { + struct { + char *path; + NMConnection *connection; + + NMAuthChain *chain; + + /* Whether the agent currently being asked for secrets + * has the system.modify privilege. + */ + gboolean current_has_modify; + + union { + struct { + NMSecretAgentGetSecretsFlags flags; + char *setting_name; + char **hints; + + GVariant *existing_secrets; + + NMAgentSecretsResultFunc callback; + gpointer callback_data; + } get; + }; + } con; + }; +}; + +/*****************************************************************************/ + static gboolean remove_agent (NMAgentManager *self, const char *owner) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMSecretAgent *agent; - GHashTableIter iter; - gpointer data; - GSList *pending_reqs = NULL; + CList *iter, *safe; g_return_val_if_fail (owner != NULL, FALSE); @@ -174,16 +224,8 @@ remove_agent (NMAgentManager *self, const char *owner) _LOGD (agent, "agent unregistered or disappeared"); /* Remove this agent from any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, &data, NULL)) - request_remove_agent ((Request *) data, agent, &pending_reqs); - - /* We cannot call request_next_agent() from within hash iterating loop, - * because it may remove the request from the hash table, which invalidates - * the iterator. So, only remove the agent from requests. And store the requests - * that should be sent to other agent to a temporary list to proceed afterwards. - */ - g_slist_free_full (pending_reqs, (GDestroyNotify) request_next_agent); + c_list_for_each_safe (iter, safe, &priv->requests) + request_remove_agent (c_list_entry (iter, Request, lst_request), agent); /* And dispose of the agent */ g_hash_table_remove (priv->agents, owner); @@ -270,8 +312,7 @@ agent_register_permissions_done (NMAuthChain *chain, const char *sender; GError *local = NULL; NMAuthCallResult result; - GHashTableIter iter; - Request *req; + CList *iter; g_assert (context); @@ -304,9 +345,8 @@ agent_register_permissions_done (NMAuthChain *chain, g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); /* Add this agent to any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, (gpointer) &req, NULL)) - request_add_agent (req, agent); + c_list_for_each (iter, &priv->requests) + request_add_agent (c_list_entry (iter, Request, lst_request), agent); } nm_auth_chain_unref (chain); @@ -450,52 +490,6 @@ done: /*****************************************************************************/ -struct _NMAgentManagerCallId { - NMAgentManager *self; - - RequestType request_type; - - char *detail; - - NMAuthSubject *subject; - - /* Current agent being asked for secrets */ - NMSecretAgent *current; - NMSecretAgentCallId current_call_id; - - /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ - GSList *pending; - - guint idle_id; - - union { - struct { - char *path; - NMConnection *connection; - - NMAuthChain *chain; - - /* Whether the agent currently being asked for secrets - * has the system.modify privilege. - */ - gboolean current_has_modify; - - union { - struct { - NMSecretAgentGetSecretsFlags flags; - char *setting_name; - char **hints; - - GVariant *existing_secrets; - - NMAgentSecretsResultFunc callback; - gpointer callback_data; - } get; - }; - } con; - }; -}; - static Request * request_new (NMAgentManager *self, RequestType request_type, @@ -509,6 +503,7 @@ request_new (NMAgentManager *self, req->request_type = request_type; req->detail = g_strdup (detail); req->subject = g_object_ref (subject); + c_list_link_tail (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request); return req; } @@ -597,7 +592,7 @@ req_complete_cancel (Request *req, gboolean is_disposing) gs_free_error GError *error = NULL; nm_assert (req && req->self); - nm_assert (!g_hash_table_contains (req->self->_priv.requests, req)); + nm_assert (!c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (req->self)->requests, &req->lst_request)); nm_utils_error_set_cancelled (&error, is_disposing, "NMAgentManager"); req_complete_release (req, NULL, NULL, NULL, error); @@ -611,10 +606,11 @@ req_complete (Request *req, GError *error) { NMAgentManager *self = req->self; - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->requests, req)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request)); + + c_list_unlink_init (&req->lst_request); + req_complete_release (req, secrets, agent_dbus_owner, agent_username, error); } @@ -630,6 +626,7 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) NMSecretAgent *a = (NMSecretAgent *)aa; NMSecretAgent *b = (NMSecretAgent *)bb; Request *req = user_data; + NMSessionMonitor *sm; gboolean a_active, b_active; gulong a_pid, b_pid, requester; @@ -648,8 +645,9 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) } /* Prefer agents in active sessions */ - a_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (a), TRUE); - b_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (b), TRUE); + sm = NM_AGENT_MANAGER_GET_PRIVATE (req->self)->session_monitor; + a_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (a), TRUE); + b_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (b), TRUE); if (a_active && !b_active) return -1; else if (a_active == b_active) @@ -734,7 +732,7 @@ request_next_agent (Request *req) nm_secret_agent_cancel_secrets (req->current, req->current_call_id); g_clear_object (&req->current); } - g_warn_if_fail (!req->current_call_id); + nm_assert (!req->current_call_id); if (req->pending) { /* Send the request to the next agent */ @@ -769,7 +767,7 @@ request_next_agent (Request *req) } static void -request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs) +request_remove_agent (Request *req, NMSecretAgent *agent) { NMAgentManager *self; @@ -798,7 +796,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs) g_assert_not_reached (); } - *pending_reqs = g_slist_prepend (*pending_reqs, req); + request_next_agent (req); } else if (g_slist_find (req->pending, agent)) { req->pending = g_slist_remove (req->pending, agent); @@ -1219,7 +1217,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self, NMAgentSecretsResultFunc callback, gpointer callback_data) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); Request *req; g_return_val_if_fail (self != NULL, NULL); @@ -1253,9 +1250,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self, req->con.get.callback = callback; req->con.get.callback_data = callback_data; - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); - /* Kick off the request */ if (!(req->con.get.flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)) request_add_agents (self, req); @@ -1271,9 +1265,9 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self, g_return_if_fail (request_id); g_return_if_fail (request_id->request_type == REQUEST_TYPE_CON_GET); - if (!g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, - request_id)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &request_id->lst_request)); + + c_list_unlink_init (&request_id->lst_request); req_complete_cancel (request_id, FALSE); } @@ -1341,7 +1335,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self, NMConnection *connection, NMAuthSubject *subject) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); Request *req; g_return_if_fail (self); @@ -1359,8 +1352,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self, subject); req->con.path = g_strdup (path); req->con.connection = g_object_ref (connection); - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); /* Kick off the request */ request_add_agents (self, req); @@ -1426,7 +1417,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self, const char *path, NMConnection *connection) { - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMAuthSubject *subject; Request *req; @@ -1447,8 +1437,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self, req->con.path = g_strdup (path); req->con.connection = g_object_ref (connection); g_object_unref (subject); - if (!nm_g_hash_table_add (priv->requests, req)) - g_assert_not_reached (); /* Kick off the request */ request_add_agents (self, req); @@ -1570,8 +1558,8 @@ nm_agent_manager_init (NMAgentManager *self) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal); + c_list_init (&priv->requests); + priv->agents = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void @@ -1582,6 +1570,7 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_agent_manager_parent_class)->constructed (object); priv->auth_mgr = g_object_ref (nm_auth_manager_get ()); + priv->session_monitor = g_object_ref (nm_session_monitor_get ()); nm_exported_object_export (NM_EXPORTED_OBJECT (object)); @@ -1589,28 +1578,19 @@ constructed (GObject *object) NM_AUTH_MANAGER_SIGNAL_CHANGED, G_CALLBACK (authority_changed_cb), object); - - NM_UTILS_KEEP_ALIVE (object, nm_session_monitor_get (), "NMAgentManager-depends-on-NMSessionMonitor"); } static void dispose (GObject *object) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE ((NMAgentManager *) object); - - if (priv->requests) { - GHashTableIter iter; - Request *req; + CList *iter; cancel_more: - g_hash_table_iter_init (&iter, priv->requests); - if (g_hash_table_iter_next (&iter, (gpointer *) &req, NULL)) { - g_hash_table_iter_remove (&iter); - req_complete_cancel (req, TRUE); - goto cancel_more; - } - g_hash_table_unref (priv->requests); - priv->requests = NULL; + c_list_for_each (iter, &priv->requests) { + c_list_unlink_init (iter); + req_complete_cancel (c_list_entry (iter, Request, lst_request), TRUE); + goto cancel_more; } g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref); @@ -1630,6 +1610,8 @@ cancel_more: nm_exported_object_unexport (NM_EXPORTED_OBJECT (object)); + g_clear_object (&priv->session_monitor); + G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); } diff --git a/src/settings/nm-inotify-helper.c b/src/settings/nm-inotify-helper.c index a0432a25..4c65b02d 100644 --- a/src/settings/nm-inotify-helper.c +++ b/src/settings/nm-inotify-helper.c @@ -188,8 +188,7 @@ finalize (GObject *object) { NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE ((NMInotifyHelper *) object); - if (priv->ifd >= 0) - close (priv->ifd); + nm_close (priv->ifd); g_hash_table_destroy (priv->wd_refs); diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index b9ca34d0..5fe4dd17 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -30,6 +30,7 @@ #include "nm-auth-subject.h" #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" +#include "nm-utils/c-list.h" #include "introspection/org.freedesktop.NetworkManager.SecretAgent.h" @@ -58,7 +59,7 @@ typedef struct { gboolean connection_is_private; gulong on_disconnected_id; - GHashTable *requests; + CList requests; } NMSecretAgentPrivate; struct _NMSecretAgent { @@ -99,6 +100,7 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) /*****************************************************************************/ struct _NMSecretAgentCallId { + CList lst; NMSecretAgent *agent; GCancellable *cancellable; char *path; @@ -129,6 +131,8 @@ request_new (NMSecretAgent *self, r->callback = callback; r->callback_data = callback_data; r->cancellable = g_cancellable_new (); + c_list_link_tail (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests, + &r->lst); _LOGt ("request "LOG_REQ_FMT": created", LOG_REQ_ARG (r)); return r; } @@ -140,6 +144,7 @@ request_free (Request *r) NMSecretAgent *self = r->agent; _LOGt ("request "LOG_REQ_FMT": destroyed", LOG_REQ_ARG (r)); + c_list_unlink (&r->lst); g_free (r->path); g_free (r->setting_name); if (r->cancellable) @@ -150,17 +155,15 @@ request_free (Request *r) static gboolean request_check_return (Request *r) { - NMSecretAgentPrivate *priv; - if (!r->cancellable) return FALSE; g_return_val_if_fail (NM_IS_SECRET_AGENT (r->agent), FALSE); - priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent); + nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (r->agent)->requests, + &r->lst)); - if (!g_hash_table_remove (priv->requests, r)) - g_return_val_if_reached (FALSE); + c_list_unlink_init (&r->lst); return TRUE; } @@ -373,7 +376,6 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data); r->is_get_secrets = TRUE; - g_hash_table_add (priv->requests, r); /* Increase the timeout only for this call */ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), 120000); @@ -467,15 +469,15 @@ do_cancel_secrets (NMSecretAgent *self, Request *r, gboolean disposing) void nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId call_id) { - NMSecretAgentPrivate *priv; Request *r = call_id; g_return_if_fail (NM_IS_SECRET_AGENT (self)); g_return_if_fail (r); - priv = NM_SECRET_AGENT_GET_PRIVATE (self); - if (!g_hash_table_remove (priv->requests, r)) - g_return_if_reached (); + nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests, + &r->lst)); + + c_list_unlink_init (&r->lst); do_cancel_secrets (self, r, FALSE); } @@ -523,7 +525,6 @@ nm_secret_agent_save_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data); - g_hash_table_add (priv->requests, r); nmdbus_secret_agent_call_save_secrets (priv->proxy, dict, path, @@ -576,7 +577,6 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data); - g_hash_table_add (priv->requests, r); nmdbus_secret_agent_call_delete_secrets (priv->proxy, dict, path, @@ -634,7 +634,7 @@ _on_disconnected_name_owner_changed (GDBusConnection *connection, { NMSecretAgent *self = NM_SECRET_AGENT (user_data); NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - const char *old_owner, *new_owner; + const char *old_owner = NULL, *new_owner = NULL; g_variant_get (parameters, "(&s&s&s)", @@ -747,7 +747,7 @@ nm_secret_agent_init (NMSecretAgent *self) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal); + c_list_init (&priv->requests); } static void @@ -755,13 +755,13 @@ dispose (GObject *object) { NMSecretAgent *self = NM_SECRET_AGENT (object); NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self); - GHashTableIter iter; - Request *r; + CList *iter; - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, (gpointer *) &r, NULL)) { - g_hash_table_iter_remove (&iter); - do_cancel_secrets (self, r, TRUE); +again: + c_list_for_each (iter, &priv->requests) { + c_list_unlink_init (iter); + do_cancel_secrets (self, c_list_entry (iter, Request, lst), TRUE); + goto again; } _on_disconnected_cleanup (priv); @@ -783,7 +783,6 @@ finalize (GObject *object) g_free (priv->dbus_owner); g_slist_free_full (priv->permissions, g_free); - g_hash_table_destroy (priv->requests); G_OBJECT_CLASS (nm_secret_agent_parent_class)->finalize (object); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 45a1b664..ed69115c 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -109,7 +109,7 @@ typedef struct _NMSettingsConnectionPrivate { GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ int autoconnect_retries; - gint32 autoconnect_retry_time; + gint32 autoconnect_blocked_until; char *filename; } NMSettingsConnectionPrivate; @@ -510,18 +510,12 @@ connection_changed_cb (NMSettingsConnection *self, gpointer unused) _emit_updated (self, FALSE); } -/* Update the settings of this connection to match that of 'new_connection', - * taking care to make a private copy of secrets. - */ gboolean -nm_settings_connection_replace_settings (NMSettingsConnection *self, - NMConnection *new_connection, - gboolean update_unsaved, - const char *log_diff_name, - GError **error) +nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self, + NMConnection *new_connection, + GError **error) { NMSettingsConnectionPrivate *priv; - gboolean success = FALSE; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); @@ -540,6 +534,30 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, return FALSE; } + return TRUE; +} + +gboolean +nm_settings_connection_replace_settings_full (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean prepare_new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error) +{ + NMSettingsConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if ( prepare_new_connection + && !nm_settings_connection_replace_settings_prepare (self, + new_connection, + error)) + return FALSE; + /* Do nothing if there's nothing to update */ if (nm_connection_compare (NM_CONNECTION (self), new_connection, @@ -567,7 +585,6 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, * nm_connection_clear_secrets() and clears them. */ update_system_secrets_cache (self); - success = TRUE; /* Add agent and always-ask secrets back; they won't necessarily be * in the replacement connection data if it was eg reread from disk. @@ -594,114 +611,101 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, _emit_updated (self, TRUE); - return success; -} - -static void -ignore_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ + return TRUE; } -/* Replaces the settings in this connection with those in 'new_connection'. If - * any changes are made, commits them to permanent storage and to any other - * subsystems watching this connection. Before returning, 'callback' is run - * with the given 'user_data' along with any errors encountered. +/* Update the settings of this connection to match that of 'new_connection', + * taking care to make a private copy of secrets. */ -static void -replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_replace_settings (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error) { - GError *error = NULL; - NMSettingsConnectionCommitReason commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION; - - if (g_strcmp0 (nm_connection_get_id (NM_CONNECTION (self)), - nm_connection_get_id (new_connection)) != 0) - commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED; - - if (nm_settings_connection_replace_settings (self, new_connection, TRUE, "replace-and-commit-disk", &error)) - nm_settings_connection_commit_changes (self, commit_reason, callback, user_data); - else { - g_assert (error); - if (callback) - callback (self, error, user_data); - g_clear_error (&error); - } + return nm_settings_connection_replace_settings_full (self, + new_connection, + TRUE, + update_unsaved, + log_diff_name, + error); } -void -nm_settings_connection_replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_commit_changes (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionCommitReason commit_reason, + GError **error) { - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - g_return_if_fail (NM_IS_CONNECTION (new_connection)); + NMSettingsConnectionClass *klass; + gs_free_error GError *local = NULL; + gs_unref_object NMConnection *reread_connection = NULL; + gs_free char *logmsg_change = NULL; - NM_SETTINGS_CONNECTION_GET_CLASS (self)->replace_and_commit (self, new_connection, callback, user_data); -} + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); -static void -commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - /* Subclasses only call this function if the save was successful, so at - * this point the connection is synced to disk and no longer unsaved. - */ - set_unsaved (self, FALSE); + klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); + if (!klass->commit_changes) { + _LOGW ("write: setting plugin %s does not support to write connection", + G_OBJECT_TYPE_NAME (self)); + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "writing settings not supported"); + return FALSE; + } - g_object_ref (self); - callback (self, NULL, user_data); - g_object_unref (self); -} + if ( new_connection + && !nm_settings_connection_replace_settings_prepare (self, + new_connection, + &local)) { + _LOGW ("write: failed to prepare connection for writing: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } -void -nm_settings_connection_commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + if (!klass->commit_changes (self, + new_connection, + commit_reason, + &reread_connection, + &logmsg_change, + &local)) { + _LOGW ("write: failure to write setting: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } - if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes) { - NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes (self, - commit_reason, - callback ? callback : ignore_cb, - user_data); - } else { - GError *error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__); - if (callback) - callback (self, error, user_data); - g_error_free (error); + if (reread_connection || new_connection) { + if (!nm_settings_connection_replace_settings_full (self, + reread_connection ?: new_connection, + !reread_connection, + FALSE, + new_connection + ? "update-during-write" + : "replace-and-commit-disk", + &local)) { + /* this can't really happen, because at this point replace-settings + * is no longer supposed to fail. It's a bug. */ + _LOGE ("write: replacing setting failed: %s", + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + g_return_val_if_reached (FALSE); + } } -} -void -nm_settings_connection_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) -{ - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + set_unsaved (self, FALSE); - if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete) { - NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete (self, - callback ? callback : ignore_cb, - user_data); - } else { - GError *error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__); - if (callback) - callback (self, error, user_data); - g_error_free (error); - } + if (reread_connection) + _LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change); + else if (new_connection) + _LOGI ("write: successfully updated (%s)", logmsg_change); + else + _LOGI ("write: successfully commited (%s)", logmsg_change); + + return TRUE; } static void @@ -740,15 +744,32 @@ remove_entry_from_db (NMSettingsConnection *self, const char* db_name) g_key_file_free (key_file); } -static void -do_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +gboolean +nm_settings_connection_delete (NMSettingsConnection *self, + GError **error) { + gs_unref_object NMSettingsConnection *self_keep_alive = NULL; + NMSettingsConnectionClass *klass; NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); NMConnection *for_agents; - g_object_ref (self); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + + klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); + + self_keep_alive = g_object_ref (self); + + if (!klass->delete) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "delete not supported"); + return FALSE; + } + if (!klass->delete (self, + error)) + return FALSE; + set_visible (self, FALSE); /* Tell agents to remove secrets for this connection */ @@ -766,12 +787,10 @@ do_delete (NMSettingsConnection *self, remove_entry_from_db (self, "seen-bssids"); nm_settings_connection_signal_remove (self, FALSE); - - callback (self, NULL, user_data); - - g_object_unref (self); + return TRUE; } + /*****************************************************************************/ @@ -887,15 +906,6 @@ secret_is_system_owned (NMSettingSecretFlags flags, } static void -new_secrets_commit_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - if (error) - _LOGW ("Error saving new secrets to backing storage: %s", error->message); -} - -static void get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */ GetSecretsInfo *info, /* only needed for logging */ NMConnection *connection, @@ -974,6 +984,33 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */ } } +gboolean +nm_settings_connection_new_secrets (NMSettingsConnection *self, + NMConnection *applied_connection, + const char *setting_name, + GVariant *secrets, + GError **error) +{ + if (!nm_settings_connection_has_unmodified_applied_connection (self, applied_connection, + NM_SETTING_COMPARE_FLAG_NONE)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The connection was modified since activation"); + return FALSE; + } + + if (!nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, error)) + return FALSE; + + update_system_secrets_cache (self); + update_agent_secrets_cache (self, NULL); + + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); + return TRUE; +} + static void get_secrets_done_cb (NMAgentManager *manager, NMAgentManagerCallId call_id_a, @@ -1084,7 +1121,10 @@ get_secrets_done_cb (NMAgentManager *manager, setting_name, info); - nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, new_secrets_commit_cb, NULL); + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); } else { _LOGD ("(%s:%p) new agent secrets processed", setting_name, @@ -1545,11 +1585,6 @@ typedef struct { char *audit_args; } UpdateInfo; -typedef struct { - GDBusMethodInvocation *context; - NMAuthSubject *subject; -} CallbackInfo; - static void has_some_secrets_cb (NMSetting *setting, const char *key, @@ -1626,33 +1661,6 @@ update_complete (NMSettingsConnection *self, } static void -con_update_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - UpdateInfo *info = user_data; - NMConnection *for_agent; - - if (!error) { - /* Dupe the connection so we can clear out non-agent-owned secrets, - * as agent-owned secrets are the only ones we send back be saved. - * Only send secrets to agents of the same UID that called update too. - */ - for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nm_agent_manager_save_secrets (info->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), - for_agent, - info->subject); - g_object_unref (for_agent); - } - - update_complete (self, info, error); -} - -static void update_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, @@ -1660,63 +1668,93 @@ update_auth_cb (NMSettingsConnection *self, gpointer data) { UpdateInfo *info = data; - GError *local = NULL; + NMSettingsConnectionCommitReason commit_reason; + gs_free_error GError *local = NULL; if (error) { update_complete (self, info, error); return; } - if (!info->new_settings) { - /* We're just calling Save(). Just commit the existing connection. */ - if (info->save_to_disk) { - nm_settings_connection_commit_changes (self, - NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION, - con_update_cb, - info); + if (info->new_settings) { + if (!any_secrets_present (info->new_settings)) { + /* If the new connection has no secrets, we do not want to remove all + * secrets, rather we keep all the existing ones. Do that by merging + * them in to the new connection. + */ + cached_secrets_to_connection (self, info->new_settings); + } else { + /* Cache the new secrets from the agent, as stuff like inotify-triggered + * changes to connection's backing config files will blow them away if + * they're in the main connection. + */ + update_agent_secrets_cache (self, info->new_settings); } - return; - } - if (!any_secrets_present (info->new_settings)) { - /* If the new connection has no secrets, we do not want to remove all - * secrets, rather we keep all the existing ones. Do that by merging - * them in to the new connection. - */ - cached_secrets_to_connection (self, info->new_settings); - } else { - /* Cache the new secrets from the agent, as stuff like inotify-triggered - * changes to connection's backing config files will blow them away if - * they're in the main connection. - */ - update_agent_secrets_cache (self, info->new_settings); + if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) { + gs_unref_hashtable GHashTable *diff = NULL; + gboolean same; + + same = nm_connection_diff (NM_CONNECTION (self), info->new_settings, + NM_SETTING_COMPARE_FLAG_EXACT | + NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, + &diff); + if (!same && diff) + info->audit_args = nm_utils_format_con_diff_for_audit (diff); + } } - if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) { - gs_unref_hashtable GHashTable *diff = NULL; - gboolean same; + if (!info->save_to_disk) { + if (info->new_settings) { + nm_settings_connection_replace_settings (self, + info->new_settings, + TRUE, + "replace-unsaved", + &local); + } + goto out; + } - same = nm_connection_diff (NM_CONNECTION (self), info->new_settings, - NM_SETTING_COMPARE_FLAG_EXACT | - NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, - &diff); - if (!same && diff) - info->audit_args = nm_utils_format_con_diff_for_audit (diff); + if (info->new_settings) { + if (!nm_settings_connection_replace_settings_prepare (self, + info->new_settings, + &local)) + goto out; } - if (info->save_to_disk) { - nm_settings_connection_replace_and_commit (self, - info->new_settings, - con_update_cb, - info); - } else { - if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, "replace-and-commit-memory", &local)) - g_assert (local); - con_update_cb (self, local, info); - g_clear_error (&local); + commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION; + if ( info->new_settings + && !nm_streq0 (nm_connection_get_id (NM_CONNECTION (self)), + nm_connection_get_id (info->new_settings))) + commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED; + + nm_settings_connection_commit_changes (self, + info->new_settings, + commit_reason, + &local); + +out: + if (!local) { + gs_unref_object NMConnection *for_agent = NULL; + + /* Dupe the connection so we can clear out non-agent-owned secrets, + * as agent-owned secrets are the only ones we send back be saved. + * Only send secrets to agents of the same UID that called update too. + */ + for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self)); + nm_connection_clear_secrets_with_flags (for_agent, + secrets_filter_cb, + GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nm_agent_manager_save_secrets (info->agent_mgr, + nm_connection_get_path (NM_CONNECTION (self)), + for_agent, + info->subject); } + + update_complete (self, info, local); } + static const char * get_update_modify_permission (NMConnection *old, NMConnection *new) { @@ -1840,30 +1878,16 @@ impl_settings_connection_save (NMSettingsConnection *self, } static void -con_delete_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - CallbackInfo *info = user_data; - - if (error) - g_dbus_method_invocation_return_gerror (info->context, error); - else - g_dbus_method_invocation_return_value (info->context, NULL); - - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, - !error, NULL, info->subject, error ? error->message : NULL); - g_free (info); -} - -static void delete_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, GError *error, gpointer data) { - CallbackInfo *info; + gs_unref_object NMSettingsConnection *self_keep_alive = NULL; + gs_free_error GError *local = NULL; + + self_keep_alive = g_object_ref (self); if (error) { nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject, @@ -1872,11 +1896,15 @@ delete_auth_cb (NMSettingsConnection *self, return; } - info = g_malloc0 (sizeof (*info)); - info->context = context; - info->subject = subject; + nm_settings_connection_delete (self, &local); - nm_settings_connection_delete (self, con_delete_cb, info); + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, + !local, NULL, subject, local ? local->message : NULL); + + if (local) + g_dbus_method_invocation_return_gerror (context, local); + else + g_dbus_method_invocation_return_value (context, NULL); } static const char * @@ -1996,23 +2024,6 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self, } static void -clear_secrets_cb (NMSettingsConnection *self, - GError *error, - gpointer user_data) -{ - CallbackInfo *info = user_data; - - if (error) - g_dbus_method_invocation_return_gerror (info->context, error); - else - g_dbus_method_invocation_return_value (info->context, NULL); - - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, - !error, NULL, info->subject, error ? error->message : NULL); - g_free (info); -} - -static void dbus_clear_secrets_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, @@ -2020,31 +2031,39 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, gpointer user_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - CallbackInfo *info; + gs_free_error GError *local = NULL; if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, FALSE, NULL, subject, error->message); - } else { - /* Clear secrets in connection and caches */ - nm_connection_clear_secrets (NM_CONNECTION (self)); - if (priv->system_secrets) - nm_connection_clear_secrets (priv->system_secrets); - if (priv->agent_secrets) - nm_connection_clear_secrets (priv->agent_secrets); + return; + } + + /* Clear secrets in connection and caches */ + nm_connection_clear_secrets (NM_CONNECTION (self)); + if (priv->system_secrets) + nm_connection_clear_secrets (priv->system_secrets); + if (priv->agent_secrets) + nm_connection_clear_secrets (priv->agent_secrets); - /* Tell agents to remove secrets for this connection */ - nm_agent_manager_delete_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), - NM_CONNECTION (self)); + /* Tell agents to remove secrets for this connection */ + nm_agent_manager_delete_secrets (priv->agent_mgr, + nm_connection_get_path (NM_CONNECTION (self)), + NM_CONNECTION (self)); - info = g_malloc0 (sizeof (*info)); - info->context = context; - info->subject = subject; + nm_settings_connection_commit_changes (self, + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + &local); - nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, clear_secrets_cb, info); - } + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, + !local, NULL, subject, local ? local->message : NULL); + + if (local) + g_dbus_method_invocation_return_gerror (context, local); + else + g_dbus_method_invocation_return_value (context, NULL); } static void @@ -2512,8 +2531,10 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) } } +/*****************************************************************************/ + /** - * nm_settings_connection_get_autoconnect_retries: + * nm_settings_connection_autoconnect_retries_get: * @self: the settings connection * * Returns the number of autoconnect retries left. If the value is @@ -2521,14 +2542,13 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) * with the global default. */ int -nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) +nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - if (priv->autoconnect_retries == AUTOCONNECT_RETRIES_UNSET) { + if (G_UNLIKELY (priv->autoconnect_retries == AUTOCONNECT_RETRIES_UNSET)) { NMSettingConnection *s_con; int retries = -1; - const char *value; s_con = nm_connection_get_setting_connection ((NMConnection *) self); if (s_con) @@ -2536,20 +2556,18 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) /* -1 means 'default' */ if (retries == -1) { - value = nm_config_data_get_value_cached (NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - "autoconnect-retries-default", - NM_CONFIG_GET_VALUE_STRIP); - - retries = _nm_utils_ascii_str_to_int64 (value, - 10, 0, G_MAXINT32, - AUTOCONNECT_RETRIES_DEFAULT); + retries = nm_config_data_get_value_int64 (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_GROUP_MAIN, + "autoconnect-retries-default", + 10, 0, G_MAXINT32, + AUTOCONNECT_RETRIES_DEFAULT); } /* 0 means 'forever', which is translated to a retry count of -1 */ if (retries == 0) retries = AUTOCONNECT_RETRIES_FOREVER; + _LOGT ("autoconnect-retries: init %d", retries); priv->autoconnect_retries = retries; } @@ -2557,74 +2575,57 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self) } void -nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self, +nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + NMSettingsConnectionPrivate *priv; + + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + nm_assert (retries == AUTOCONNECT_RETRIES_UNSET || retries >= 0); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); if (priv->autoconnect_retries != retries) { _LOGT ("autoconnect-retries: set %d", retries); priv->autoconnect_retries = retries; } if (retries) - priv->autoconnect_retry_time = 0; + priv->autoconnect_blocked_until = 0; else - priv->autoconnect_retry_time = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER; + priv->autoconnect_blocked_until = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER; } void -nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self) +nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self) { - nm_settings_connection_set_autoconnect_retries (self, AUTOCONNECT_RETRIES_UNSET); + nm_settings_connection_autoconnect_retries_set (self, AUTOCONNECT_RETRIES_UNSET); } gint32 -nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self) +nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self) { - return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time; + return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_until; } NMSettingsAutoconnectBlockedReason -nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self) +nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self) { return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason; } void -nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self, +nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self, NMSettingsAutoconnectBlockedReason reason) { g_return_if_fail (NM_IN_SET (reason, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS)); NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason; } -gboolean -nm_settings_connection_can_autoconnect (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - const char *permission; - - if ( !priv->visible - || priv->autoconnect_retries == 0 - || priv->autoconnect_blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED) - return FALSE; - - s_con = nm_connection_get_setting_connection (NM_CONNECTION (self)); - if (!nm_setting_connection_get_autoconnect (s_con)) - return FALSE; - - permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (self)); - if (permission) { - if (nm_settings_connection_check_permission (self, permission) == FALSE) - return FALSE; - } - - return TRUE; -} +/*****************************************************************************/ /** * nm_settings_connection_get_nm_generated: @@ -2747,7 +2748,7 @@ nm_settings_connection_init (NMSettingsConnection *self) priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); - priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->seen_bssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET; @@ -2873,9 +2874,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) object_class->get_property = get_property; object_class->set_property = set_property; - class->replace_and_commit = replace_and_commit; - class->commit_changes = commit_changes; - class->delete = do_delete; class->supports_secrets = supports_secrets; obj_properties[PROP_VISIBLE] = diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index b449e2bd..faacd949 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -83,9 +83,10 @@ typedef enum { /*< skip >*/ } NMSettingsConnectionCommitReason; typedef enum { - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED = 0, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED = 1, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS = 2, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE = 0, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST = 1, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED = 2, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS = 3, } NMSettingsAutoconnectBlockedReason; struct _NMSettingsConnectionCallId; @@ -93,14 +94,6 @@ typedef struct _NMSettingsConnectionCallId *NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass; -typedef void (*NMSettingsConnectionCommitFunc) (NMSettingsConnection *self, - GError *error, - gpointer user_data); - -typedef void (*NMSettingsConnectionDeleteFunc) (NMSettingsConnection *self, - GError *error, - gpointer user_data); - struct _NMSettingsConnectionPrivate; struct _NMSettingsConnection { @@ -111,20 +104,15 @@ struct _NMSettingsConnection { struct _NMSettingsConnectionClass { NMExportedObjectClass parent; - /* virtual methods */ - void (*replace_and_commit) (NMSettingsConnection *self, + gboolean (*commit_changes) (NMSettingsConnection *self, NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); - - void (*commit_changes) (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); + NMSettingsConnectionCommitReason commit_reason, + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error); - void (*delete) (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data); + gboolean (*delete) (NMSettingsConnection *self, + GError **error); gboolean (*supports_secrets) (NMSettingsConnection *self, const char *setting_name); @@ -136,10 +124,14 @@ gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsCon NMConnection *applied_connection, NMSettingCompareFlags compare_flage); -void nm_settings_connection_commit_changes (NMSettingsConnection *self, - NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); +gboolean nm_settings_connection_commit_changes (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionCommitReason commit_reason, + GError **error); + +gboolean nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self, + NMConnection *new_connection, + GError **error); gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self, NMConnection *new_connection, @@ -147,14 +139,15 @@ gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self, const char *log_diff_name, GError **error); -void nm_settings_connection_replace_and_commit (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data); +gboolean nm_settings_connection_replace_settings_full (NMSettingsConnection *self, + NMConnection *new_connection, + gboolean prepare_new_connection, + gboolean update_unsaved, + const char *log_diff_name, + GError **error); -void nm_settings_connection_delete (NMSettingsConnection *self, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data); +gboolean nm_settings_connection_delete (NMSettingsConnection *self, + GError **error); typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self, NMSettingsConnectionCallId call_id, @@ -163,6 +156,12 @@ typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self, GError *error, gpointer user_data); +gboolean nm_settings_connection_new_secrets (NMSettingsConnection *self, + NMConnection *applied_connection, + const char *setting_name, + GVariant *secrets, + GError **error); + NMSettingsConnectionCallId nm_settings_connection_get_secrets (NMSettingsConnection *self, NMConnection *applied_connection, NMAuthSubject *subject, @@ -214,19 +213,17 @@ void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self); -int nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self); -void nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self, +int nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries); -void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self); -gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self); +gint32 nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self); -NMSettingsAutoconnectBlockedReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self); -void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self, +NMSettingsAutoconnectBlockedReason nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self); +void nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self, NMSettingsAutoconnectBlockedReason reason); -gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *self); - gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self); gboolean nm_settings_connection_get_volatile (NMSettingsConnection *self); diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c index 7de7e597..234e345f 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -169,13 +169,17 @@ nm_settings_plugin_add_connection (NMSettingsPlugin *config, gboolean save_to_disk, GError **error) { + NMSettingsPluginInterface *config_interface; + g_return_val_if_fail (config != NULL, NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection) - return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection (config, connection, save_to_disk, error); + config_interface = NM_SETTINGS_PLUGIN_GET_INTERFACE (config); + if (!config_interface->add_connection) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Plugin does not support adding connections"); + return NULL; + } - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "Plugin does not support adding connections"); - return NULL; + return config_interface->add_connection (config, connection, save_to_disk, error); } diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index afd1b084..e2b467a2 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -76,6 +76,7 @@ #include "NetworkManagerUtils.h" #include "nm-dispatcher.h" #include "nm-inotify-helper.h" +#include "nm-hostname-manager.h" #include "introspection/org.freedesktop.NetworkManager.Settings.h" @@ -90,20 +91,9 @@ EXPORT(nm_inotify_helper_remove_watch) EXPORT(nm_settings_connection_get_type) EXPORT(nm_settings_connection_replace_settings) -EXPORT(nm_settings_connection_replace_and_commit) /*****************************************************************************/ -#define HOSTNAMED_SERVICE_NAME "org.freedesktop.hostname1" -#define HOSTNAMED_SERVICE_PATH "/org/freedesktop/hostname1" -#define HOSTNAMED_SERVICE_INTERFACE "org.freedesktop.hostname1" - -#define HOSTNAME_FILE_DEFAULT "/etc/hostname" -#define HOSTNAME_FILE_UCASE_HOSTNAME "/etc/HOSTNAME" -#define HOSTNAME_FILE_GENTOO "/etc/conf.d/hostname" -#define IFCFG_DIR SYSCONFDIR "/sysconfig/network" -#define CONF_DHCP IFCFG_DIR "/dhcp" - static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark) #if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1 @@ -162,14 +152,8 @@ typedef struct { gboolean started; gboolean startup_complete; - struct { - char *value; - GFileMonitor *monitor; - GFileMonitor *dhcp_monitor; - gulong monitor_id; - gulong dhcp_monitor_id; - GDBusProxy *hostnamed_proxy; - } hostname; + NMHostnameManager *hostname_manager; + } NMSettingsPrivate; struct _NMSettings { @@ -568,131 +552,6 @@ get_plugin (NMSettings *self, guint32 capability) return NULL; } -#if defined(HOSTNAME_PERSIST_GENTOO) -static gchar * -read_hostname_gentoo (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - const char *tmp; - guint i; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - all_lines = g_strsplit (contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip (all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - if (g_str_has_prefix (all_lines[i], "hostname=")) { - tmp = &all_lines[i][NM_STRLEN ("hostname=")]; - return g_shell_unquote (tmp, NULL); - } - } - return NULL; -} -#endif - -#if defined(HOSTNAME_PERSIST_SLACKWARE) -static gchar * -read_hostname_slackware (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - char *tmp; - guint i, j = 0; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - all_lines = g_strsplit (contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip (all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - tmp = &all_lines[i][0]; - /* We only want up to the first '.' -- the rest of the */ - /* fqdn is defined in /etc/hosts */ - while (tmp[j] != '\0') { - if (tmp[j] == '.') { - tmp[j] = '\0'; - break; - } - j++; - } - return g_shell_unquote (tmp, NULL); - } - return NULL; -} -#endif - -#if defined(HOSTNAME_PERSIST_SUSE) -static gboolean -hostname_is_dynamic (void) -{ - GIOChannel *channel; - char *str = NULL; - gboolean dynamic = FALSE; - - channel = g_io_channel_new_file (CONF_DHCP, "r", NULL); - if (!channel) - return dynamic; - - while (g_io_channel_read_line (channel, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) { - if (str) { - g_strstrip (str); - if (g_str_has_prefix (str, "DHCLIENT_SET_HOSTNAME=")) - dynamic = strcmp (&str[NM_STRLEN ("DHCLIENT_SET_HOSTNAME=")], "\"yes\"") == 0; - g_free (str); - } - } - - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - - return dynamic; -} -#endif - -/* Returns an allocated string which the caller owns and must eventually free */ -char * -nm_settings_get_hostname (NMSettings *self) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - char *hostname = NULL; - - if (!priv->started) - return NULL; - - if (priv->hostname.hostnamed_proxy) { - hostname = g_strdup (priv->hostname.value); - goto out; - } - -#if defined(HOSTNAME_PERSIST_SUSE) - if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ()) - return NULL; -#endif - -#if defined(HOSTNAME_PERSIST_GENTOO) - hostname = read_hostname_gentoo (HOSTNAME_FILE); -#elif defined(HOSTNAME_PERSIST_SLACKWARE) - hostname = read_hostname_slackware (HOSTNAME_FILE); -#else - if (g_file_get_contents (HOSTNAME_FILE, &hostname, NULL, NULL)) - g_strchomp (hostname); -#endif - -out: - if (hostname && !hostname[0]) { - g_free (hostname); - hostname = NULL; - } - - return hostname; -} - static gboolean find_spec (GSList *spec_list, const char *spec) { @@ -1626,160 +1485,7 @@ impl_settings_reload_connections (NMSettings *self, g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE)); } -typedef struct { - char *hostname; - NMSettingsSetHostnameCb cb; - gpointer user_data; -} SetHostnameInfo; - -static void -set_transient_hostname_done (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GDBusProxy *proxy = G_DBUS_PROXY (object); - gs_free SetHostnameInfo *info = user_data; - gs_unref_variant GVariant *result = NULL; - gs_free_error GError *error = NULL; - - result = g_dbus_proxy_call_finish (proxy, res, &error); - - if (error) { - _LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s", - info->hostname, error->message); - } - - info->cb (info->hostname, !error, info->user_data); - g_free (info->hostname); -} - -void -nm_settings_set_transient_hostname (NMSettings *self, - const char *hostname, - NMSettingsSetHostnameCb cb, - gpointer user_data) -{ - NMSettingsPrivate *priv; - SetHostnameInfo *info; - - g_return_if_fail (NM_IS_SETTINGS (self)); - priv = NM_SETTINGS_GET_PRIVATE (self); - - if (!priv->hostname.hostnamed_proxy) { - cb (hostname, FALSE, user_data); - return; - } - - info = g_new0 (SetHostnameInfo, 1); - info->hostname = g_strdup (hostname); - info->cb = cb; - info->user_data = user_data; - - g_dbus_proxy_call (priv->hostname.hostnamed_proxy, - "SetHostname", - g_variant_new ("(sb)", hostname, FALSE), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - set_transient_hostname_done, - info); -} - -gboolean -nm_settings_get_transient_hostname (NMSettings *self, char **hostname) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GVariant *v_hostname; - - if (!priv->hostname.hostnamed_proxy) - return FALSE; - - v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy, - "Hostname"); - if (!v_hostname) { - _LOGT ("transient hostname retrieval failed"); - return FALSE; - } - - *hostname = g_variant_dup_string (v_hostname, NULL); - g_variant_unref (v_hostname); - - return TRUE; -} - -static gboolean -write_hostname (NMSettingsPrivate *priv, const char *hostname) -{ - char *hostname_eol; - gboolean ret; - gs_free_error GError *error = NULL; - const char *file = HOSTNAME_FILE; - gs_free char *link_path = NULL; - gs_unref_variant GVariant *var = NULL; - struct stat file_stat; -#if HAVE_SELINUX - security_context_t se_ctx_prev = NULL, se_ctx = NULL; - mode_t st_mode = 0; -#endif - - if (priv->hostname.hostnamed_proxy) { - var = g_dbus_proxy_call_sync (priv->hostname.hostnamed_proxy, - "SetStaticHostname", - g_variant_new ("(sb)", hostname, FALSE), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); - if (error) - _LOGW ("could not set hostname: %s", error->message); - - return !error; - } - - /* If the hostname file is a symbolic link, follow it to find where the - * real file is located, otherwise g_file_set_contents will attempt to - * replace the link with a plain file. - */ - if ( lstat (file, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode) - && (link_path = nm_utils_read_link_absolute (file, NULL))) - file = link_path; - -#if HAVE_SELINUX - /* Get default context for hostname file and set it for fscreate */ - if (stat (file, &file_stat) == 0) - st_mode = file_stat.st_mode; - matchpathcon (file, st_mode, &se_ctx); - matchpathcon_fini (); - getfscreatecon (&se_ctx_prev); - setfscreatecon (se_ctx); -#endif - -#if defined (HOSTNAME_PERSIST_GENTOO) - hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n" - "hostname=\"%s\"\n", hostname); -#else - hostname_eol = g_strdup_printf ("%s\n", hostname); -#endif - - ret = g_file_set_contents (file, hostname_eol, -1, &error); - -#if HAVE_SELINUX - /* Restore previous context and cleanup */ - setfscreatecon (se_ctx_prev); - freecon (se_ctx); - freecon (se_ctx_prev); -#endif - - g_free (hostname_eol); - - if (!ret) { - _LOGW ("could not save hostname to %s: %s", file, error->message); - return FALSE; - } - - return TRUE; -} +/*****************************************************************************/ static void pk_hostname_cb (NMAuthChain *chain, @@ -1812,7 +1518,7 @@ pk_hostname_cb (NMAuthChain *chain, } else { hostname = nm_auth_chain_get_data (chain, "hostname"); - if (!write_hostname (priv, hostname)) { + if (!nm_hostname_manager_write_hostname (priv->hostname_manager, hostname)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Saving the hostname failed."); @@ -1827,33 +1533,6 @@ pk_hostname_cb (NMAuthChain *chain, nm_auth_chain_unref (chain); } -static gboolean -validate_hostname (const char *hostname) -{ - const char *p; - gboolean dot = TRUE; - - if (!hostname || !hostname[0]) - return FALSE; - - for (p = hostname; *p; p++) { - if (*p == '.') { - if (dot) - return FALSE; - dot = TRUE; - } else { - if (!g_ascii_isalnum (*p) && (*p != '-') && (*p != '_')) - return FALSE; - dot = FALSE; - } - } - - if (dot) - return FALSE; - - return (p - hostname <= HOST_NAME_MAX); -} - static void impl_settings_save_hostname (NMSettings *self, GDBusMethodInvocation *context, @@ -1864,7 +1543,7 @@ impl_settings_save_hostname (NMSettings *self, GError *error = NULL; /* Minimal validation of the hostname */ - if (!validate_hostname (hostname)) { + if (!nm_hostname_manager_validate_hostname (hostname)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_HOSTNAME, "The hostname was too long or contained invalid characters."); @@ -1888,37 +1567,7 @@ done: g_dbus_method_invocation_take_error (context, error); } -static void -hostname_maybe_changed (NMSettings *settings) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (settings); - char *new_hostname; - - new_hostname = nm_settings_get_hostname (settings); - - if ( (new_hostname && !priv->hostname.value) - || (!new_hostname && priv->hostname.value) - || (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) { - - _LOGI ("hostname changed from %s%s%s to %s%s%s", - NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"), - NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)")); - g_free (priv->hostname.value); - priv->hostname.value = new_hostname; - _notify (settings, PROP_HOSTNAME); - } else - g_free (new_hostname); -} - -static void -hostname_file_changed_cb (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - hostname_maybe_changed (user_data); -} +/*****************************************************************************/ static gboolean have_connection_for_device (NMSettings *self, NMDevice *device) @@ -2124,7 +1773,7 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin * remains up and can be assumed if NM starts again. */ if (quitting == FALSE) - nm_settings_connection_delete (connection, NULL, NULL); + nm_settings_connection_delete (connection, NULL); } } @@ -2141,95 +1790,19 @@ nm_settings_get_startup_complete (NMSettings *self) /*****************************************************************************/ static void -hostnamed_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - char **invalidated_properties, - gpointer user_data) +_hostname_changed_cb (NMHostnameManager *hostname_manager, + GParamSpec *pspec, + gpointer user_data) { - NMSettings *self = user_data; - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GVariant *v_hostname; - const char *hostname; - - v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy, - "StaticHostname"); - if (!v_hostname) - return; - - hostname = g_variant_get_string (v_hostname, NULL); - - if (g_strcmp0 (priv->hostname.value, hostname) != 0) { - _LOGI ("hostname changed from %s%s%s to %s%s%s", - NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"), - NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)")); - g_free (priv->hostname.value); - priv->hostname.value = g_strdup (hostname); - _notify (self, PROP_HOSTNAME); - nm_dispatcher_call_hostname (NULL, NULL, NULL); - } - - g_variant_unref (v_hostname); + _notify (user_data, PROP_HOSTNAME); } -static void -setup_hostname_file_monitors (NMSettings *self) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GFileMonitor *monitor; - const char *path = HOSTNAME_FILE; - char *link_path = NULL; - struct stat file_stat; - GFile *file; - - priv->hostname.value = nm_settings_get_hostname (self); - - /* resolve the path to the hostname file if it is a symbolic link */ - if ( lstat(path, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode) - && (link_path = nm_utils_read_link_absolute (path, NULL))) { - path = link_path; - if ( lstat(link_path, &file_stat) == 0 - && S_ISLNK (file_stat.st_mode)) { - _LOGW ("only one level of symbolic link indirection is allowed when monitoring " - HOSTNAME_FILE); - } - } - - /* monitor changes to hostname file */ - file = g_file_new_for_path (path); - monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - g_free(link_path); - if (monitor) { - priv->hostname.monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (hostname_file_changed_cb), - self); - priv->hostname.monitor = monitor; - } - -#if defined (HOSTNAME_PERSIST_SUSE) - /* monitor changes to dhcp file to know whether the hostname is valid */ - file = g_file_new_for_path (CONF_DHCP); - monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - if (monitor) { - priv->hostname.dhcp_monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (hostname_file_changed_cb), - self); - priv->hostname.dhcp_monitor = monitor; - } -#endif - - hostname_maybe_changed (self); -} +/*****************************************************************************/ gboolean nm_settings_start (NMSettings *self, GError **error) { NMSettingsPrivate *priv; - GDBusProxy *proxy; - GVariant *variant; - GError *local_error = NULL; gs_strfreev char **plugins = NULL; priv = NM_SETTINGS_GET_PRIVATE (self); @@ -2245,33 +1818,14 @@ nm_settings_start (NMSettings *self, GError **error) load_connections (self); check_startup_complete (self); - proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, - HOSTNAMED_SERVICE_NAME, HOSTNAMED_SERVICE_PATH, - HOSTNAMED_SERVICE_INTERFACE, NULL, &local_error); - if (proxy) { - variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname"); - if (variant) { - _LOGI ("hostname: using hostnamed"); - priv->hostname.hostnamed_proxy = proxy; - g_signal_connect (proxy, "g-properties-changed", - G_CALLBACK (hostnamed_properties_changed), self); - hostnamed_properties_changed (proxy, NULL, NULL, self); - g_variant_unref (variant); - } else { - _LOGI ("hostname: couldn't get property from hostnamed"); - g_object_unref (proxy); - } - } else { - _LOGI ("hostname: hostnamed not used as proxy creation failed with: %s", - local_error->message); - g_clear_error (&local_error); - } - - if (!priv->hostname.hostnamed_proxy) - setup_hostname_file_monitors (self); + priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); + g_signal_connect (priv->hostname_manager, + "notify::"NM_HOSTNAME_MANAGER_HOSTNAME, + G_CALLBACK (_hostname_changed_cb), + self); + if (nm_hostname_manager_get_hostname (priv->hostname_manager)) + _notify (self, PROP_HOSTNAME); - priv->started = TRUE; - _notify (self, PROP_HOSTNAME); return TRUE; } @@ -2298,11 +1852,10 @@ get_property (GObject *object, guint prop_id, g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); break; case PROP_HOSTNAME: - g_value_take_string (value, nm_settings_get_hostname (self)); - - /* Don't ever pass NULL through D-Bus */ - if (!g_value_get_string (value)) - g_value_set_static_string (value, ""); + g_value_set_string (value, + priv->hostname_manager + ? nm_hostname_manager_get_hostname (priv->hostname_manager) + : NULL); break; case PROP_CAN_MODIFY: g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)); @@ -2331,7 +1884,7 @@ nm_settings_init (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); /* Hold a reference to the agent manager so it stays alive; the only * other holders are NMSettingsConnection objects which are often @@ -2362,32 +1915,13 @@ dispose (GObject *object) g_object_unref (priv->agent_mgr); - if (priv->hostname.hostnamed_proxy) { - g_signal_handlers_disconnect_by_func (priv->hostname.hostnamed_proxy, - G_CALLBACK (hostnamed_properties_changed), + if (priv->hostname_manager) { + g_signal_handlers_disconnect_by_func (priv->hostname_manager, + G_CALLBACK (_hostname_changed_cb), self); - g_clear_object (&priv->hostname.hostnamed_proxy); - } - - if (priv->hostname.monitor) { - if (priv->hostname.monitor_id) - g_signal_handler_disconnect (priv->hostname.monitor, priv->hostname.monitor_id); - - g_file_monitor_cancel (priv->hostname.monitor); - g_clear_object (&priv->hostname.monitor); + g_clear_object (&priv->hostname_manager); } - if (priv->hostname.dhcp_monitor) { - if (priv->hostname.dhcp_monitor_id) - g_signal_handler_disconnect (priv->hostname.dhcp_monitor, - priv->hostname.dhcp_monitor_id); - - g_file_monitor_cancel (priv->hostname.dhcp_monitor); - g_clear_object (&priv->hostname.dhcp_monitor); - } - - g_clear_pointer (&priv->hostname.value, g_free); - G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object); } diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 7110a12b..eede76b0 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -119,20 +119,10 @@ gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *con const GSList *nm_settings_get_unmanaged_specs (NMSettings *self); -char *nm_settings_get_hostname (NMSettings *self); - void nm_settings_device_added (NMSettings *self, NMDevice *device); void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting); gboolean nm_settings_get_startup_complete (NMSettings *self); -void nm_settings_set_transient_hostname (NMSettings *self, - const char *hostname, - NMSettingsSetHostnameCb cb, - gpointer user_data); - -gboolean nm_settings_get_transient_hostname (NMSettings *self, - char **hostname); - #endif /* __NM_SETTINGS_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c index c9069dc7..9b1f5ccd 100644 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ b/src/settings/plugins/ibft/nms-ibft-plugin.c @@ -152,7 +152,7 @@ nms_ibft_plugin_init (NMSIbftPlugin *self) { NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf index 8fefaf18..cc6ccb5c 100644 --- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf +++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf @@ -2,16 +2,11 @@ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> - <policy user="root"> - <allow own="com.redhat.ifcfgrh1"/> - <allow send_destination="com.redhat.ifcfgrh1"/> - </policy> - <policy at_console="true"> - <allow send_destination="com.redhat.ifcfgrh1"/> - </policy> <policy context="default"> - <deny own="com.redhat.ifcfgrh1"/> <allow send_destination="com.redhat.ifcfgrh1"/> </policy> + <policy user="root"> + <allow own="com.redhat.ifcfgrh1"/> + </policy> </busconfig> diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c index b54f9549..4c1d02ae 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c @@ -306,75 +306,52 @@ nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self) return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec; } -static void -replace_and_commit (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) -{ - const char *filename; - GError *error = NULL; - - filename = nm_settings_connection_get_filename (connection); - if (filename && utils_has_complex_routes (filename)) { - if (callback) { - error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot modify a connection that has an associated 'rule-' or 'rule6-' file"); - callback (connection, error, user_data); - g_clear_error (&error); - } - return; - } - - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->replace_and_commit (connection, new_connection, callback, user_data); -} - -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - GError *error = NULL; - gboolean success = FALSE; - char *ifcfg_path = NULL; const char *filename; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = TRUE; + const char *operation_message; + gs_free char *ifcfg_path = NULL; - filename = nm_settings_connection_get_filename (connection); - if (filename) { - success = writer_update_connection (NM_CONNECTION (connection), - IFCFG_DIR, - filename, - NULL, - NULL, - &error); - } else { - success = writer_new_connection (NM_CONNECTION (connection), - IFCFG_DIR, - &ifcfg_path, - NULL, - NULL, - &error); - if (success) { - nm_settings_connection_set_filename (connection, ifcfg_path); - g_free (ifcfg_path); - } - } + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); - if (success) { - /* Chain up to parent to handle success */ - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data); - } else { - /* Otherwise immediate error */ - callback (connection, error, user_data); - g_error_free (error); - } + filename = nm_settings_connection_get_filename (connection); + if (!nms_ifcfg_rh_writer_write_connection (new_connection ?: NM_CONNECTION (connection), + IFCFG_DIR, + filename, + &ifcfg_path, + &reread, + &reread_same, + error)) + return FALSE; + + nm_assert ((!filename && ifcfg_path) || (filename && !ifcfg_path)); + if (ifcfg_path) { + nm_settings_connection_set_filename (connection, ifcfg_path); + operation_message = "persist"; + } else + operation_message = "update"; + + if (reread && !reread_same) + *out_reread_connection = g_steal_pointer (&reread); + + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("ifcfg-rh: %s %s", + operation_message, filename)); + return TRUE; } -static void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) connection); const char *filename; @@ -390,7 +367,7 @@ do_delete (NMSettingsConnection *connection, g_unlink (priv->route6file); } - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->delete (connection, callback, user_data); + return TRUE; } /*****************************************************************************/ @@ -529,8 +506,7 @@ nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class) object_class->get_property = get_property; object_class->dispose = dispose; - settings_class->delete = do_delete; - settings_class->replace_and_commit = replace_and_commit; + settings_class->delete = delete; settings_class->commit_changes = commit_changes; obj_properties[PROP_UNMANAGED_SPEC] = diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index a3092e71..da0920ef 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -461,7 +461,7 @@ _paths_from_connections (GHashTable *connections) { GHashTableIter iter; NMIfcfgConnection *connection; - GHashTable *paths = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); g_hash_table_iter_init (&iter, connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { @@ -679,18 +679,16 @@ add_connection (NMSettingsPlugin *config, { SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config); gs_free char *path = NULL; - - /* Ensure we reject attempts to add the connection long before we're - * asked to write it to disk. - */ - if (!writer_can_write_connection (connection, error)) - return NULL; + gs_unref_object NMConnection *reread = NULL; if (save_to_disk) { - if (!writer_new_connection (connection, IFCFG_DIR, &path, NULL, NULL, error)) + if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, &path, &reread, NULL, error)) + return NULL; + } else { + if (!nms_ifcfg_rh_writer_can_write_connection (connection, error)) return NULL; } - return NM_SETTINGS_CONNECTION (update_connection (self, connection, path, NULL, FALSE, NULL, error)); + return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); } static void @@ -991,7 +989,7 @@ settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin) { SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 164f6844..4754bea5 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -89,6 +89,64 @@ get_uint (const char *str, guint32 *value) return TRUE; } +static void +check_if_bond_slave (shvarFile *ifcfg, + NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char *v; + const char *master; + + v = svGetValueStr (ifcfg, "MASTER_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "MASTER", &value); + + if (v) { + master = nm_setting_connection_get_master (s_con); + if (master) { + PARSE_WARNING ("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"", + master, v); + return; + } + + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, v, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + } + + /* We should be checking for SLAVE=yes as well, but NM used to not set that, + * so for backward-compatibility, we don't check. + */ +} + +static void +check_if_team_slave (shvarFile *ifcfg, + NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char *v; + const char *master; + + v = svGetValueStr (ifcfg, "TEAM_MASTER_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "TEAM_MASTER", &value); + if (!v) + return; + + master = nm_setting_connection_get_master (s_con); + if (master) { + PARSE_WARNING ("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"", + master, v); + return; + } + + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, v, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + NULL); +} + static char * make_connection_name (shvarFile *ifcfg, const char *ifcfg_name, @@ -128,8 +186,14 @@ make_connection_setting (const char *file, NMSettingConnection *s_con; NMSettingConnectionLldp lldp; const char *ifcfg_name = NULL; - char *new_id, *uuid = NULL, *zone = NULL, *value; + char *new_id; + const char *uuid; + gs_free char *uuid_free = NULL; + gs_free char *value = NULL; + const char *v; gs_free char *stable_id = NULL; + const char *const *iter; + int vint64; ifcfg_name = utils_get_ifcfg_name (file, TRUE); if (!ifcfg_name) @@ -142,38 +206,38 @@ make_connection_setting (const char *file, g_free (new_id); /* Try for a UUID key before falling back to hashing the file name */ - uuid = svGetValueStr_cp (ifcfg, "UUID"); - if (!uuid) - uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + uuid = svGetValueStr (ifcfg, "UUID", &uuid_free); + if (!uuid) { + uuid_free = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + uuid = uuid_free; + } g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, type, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_STABLE_ID, svGetValue (ifcfg, "STABLE_ID", &stable_id), NULL); - g_free (uuid); - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (value) { + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (v) { GError *error = NULL; - if (nm_utils_is_valid_iface_name (value, &error)) { + if (nm_utils_is_valid_iface_name (v, &error)) { g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, value, + NM_SETTING_CONNECTION_INTERFACE_NAME, v, NULL); } else { - PARSE_WARNING ("invalid DEVICE name '%s': %s", value, error->message); + PARSE_WARNING ("invalid DEVICE name '%s': %s", v, error->message); g_error_free (error); } - g_free (value); } - value = svGetValueStr_cp (ifcfg, "LLDP"); - if (!g_strcmp0 (value, "rx")) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "LLDP", &value); + if (nm_streq0 (v, "rx")) lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX; else - lldp = svParseBoolean (value, NM_SETTING_CONNECTION_LLDP_DEFAULT); - g_free (value); + lldp = svParseBoolean (v, NM_SETTING_CONNECTION_LLDP_DEFAULT); /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */ g_object_set (s_con, @@ -192,68 +256,69 @@ make_connection_setting (const char *file, NM_SETTING_CONNECTION_LLDP, lldp, NULL); - value = svGetValueStr_cp (ifcfg, "USERS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "USERS", &value); + if (v) { + gs_free const char **items = NULL; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) - PARSE_WARNING ("invalid USERS item '%s'", *iter); - } + if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) + PARSE_WARNING ("invalid USERS item '%s'", *iter); } - g_free (value); - g_strfreev (items); } - zone = svGetValueStr_cp (ifcfg, "ZONE"); - g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, zone, NULL); - g_free (zone); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "ZONE", &value); + g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, v, NULL); - value = svGetValueStr_cp (ifcfg, "SECONDARY_UUIDS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SECONDARY_UUIDS", &value); + if (v) { + gs_free const char **items = NULL; - items = g_strsplit_set (value, " \t", -1); + items = nm_utils_strsplit_set (v, " \t"); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - if (!nm_setting_connection_add_secondary (s_con, *iter)) - PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); - } + if (!nm_setting_connection_add_secondary (s_con, *iter)) + PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); } - g_free (value); - g_strfreev (items); } - value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "BRIDGE"); - if (value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "BRIDGE_UUID", &value); + if (!v) + v = svGetValueStr (ifcfg, "BRIDGE", &value); + if (v) { const char *old_value; if ((old_value = nm_setting_connection_get_master (s_con))) { PARSE_WARNING ("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"", - old_value, value); + old_value, v); } else { - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); + g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, v, NULL); g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); } - g_free (value); } - value = svGetValueStr_cp (ifcfg, "GATEWAY_PING_TIMEOUT"); - if (value) { + check_if_bond_slave (ifcfg, s_con); + check_if_team_slave (ifcfg, s_con); + + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", &value); + if (v) { gint64 tmp; - tmp = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32 - 1, -1); - if (tmp >= 0) + tmp = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXINT32 - 1, -1); + if (tmp >= 0) { + if (tmp > 600) { + tmp = 600; + PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time"); + } g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, (guint) tmp, NULL); - else + } else PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time"); - g_free (value); } switch (svGetValueBoolean (ifcfg, "CONNECTION_METERED", -1)) { @@ -265,6 +330,9 @@ make_connection_setting (const char *file, break; } + vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); + g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (gint) vint64, NULL); + return NM_SETTING (s_con); } @@ -302,30 +370,6 @@ read_ip4_address (shvarFile *ifcfg, return TRUE; } -static void -_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which) -{ - gsize l; - - l = g_strlcpy (buf, tag_name, buf_len); - nm_assert (l < buf_len); - if (which != -1) { - buf_len -= l; - l = g_snprintf (&buf[l], buf_len, "%d", which); - nm_assert (l < buf_len); - } -} -#define numbered_tag(buf, tag_name, which) \ - ({ \ - _nm_unused char *const _buf = (buf); \ - \ - /* some static assert trying to ensure that the buffer is statically allocated. - * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ - G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \ - _numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \ - buf; \ - }) - static gboolean is_any_ip4_address_defined (shvarFile *ifcfg, int *idx) { @@ -368,6 +412,7 @@ read_full_ip4_address (shvarFile *ifcfg, char prefix_tag[256]; guint32 ipaddr; gs_free char *value = NULL; + const char *v; int prefix = 0; gboolean has_key; guint32 a; @@ -402,12 +447,12 @@ read_full_ip4_address (shvarFile *ifcfg, /* Prefix */ numbered_tag (prefix_tag, "PREFIX", which); - value = svGetValueStr_cp (ifcfg, prefix_tag); - if (value) { - prefix = _nm_utils_ascii_str_to_int64 (value, 10, 0, 32, -1); + v = svGetValueStr (ifcfg, prefix_tag, &value); + if (v) { + prefix = _nm_utils_ascii_str_to_int64 (v, 10, 0, 32, -1); if (prefix < 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 prefix '%s'", value); + "Invalid IP4 prefix '%s'", v); return FALSE; } } else { @@ -423,7 +468,7 @@ read_full_ip4_address (shvarFile *ifcfg, prefix = nm_ip_address_get_prefix (base_addr); else { /* Try to autodetermine the prefix for the address' class */ - prefix = nm_utils_ip4_get_default_prefix (ipaddr); + prefix = _nm_utils_ip4_get_default_prefix (ipaddr); PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix); } } @@ -436,131 +481,448 @@ read_full_ip4_address (shvarFile *ifcfg, return FALSE; } -/* - * Use looser syntax to comprise all the possibilities. - * The validity must be checked after the match. - */ -#define IPV4_ADDR_REGEX "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}" -#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+" - -/* - * NOTE: The regexes below don't describe all variants allowed by 'ip route add', - * namely destination IP without 'to' keyword is recognized just at line start. - */ +/*****************************************************************************/ static gboolean -parse_route_options (NMIPRoute *route, int family, const char *line, GError **error) +parse_route_line_is_comment (const char *line) { - GRegex *regex = NULL; - GMatchInfo *match_info = NULL; - gboolean success = FALSE; - static const char *metrics[] = { NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND, - NM_IP_ROUTE_ATTRIBUTE_INITCWND, NM_IP_ROUTE_ATTRIBUTE_INITRWND, - NM_IP_ROUTE_ATTRIBUTE_MTU, NULL }; - char buffer[1024]; - int i; + /* we obtained the line from a legacy route file. Here we skip + * empty lines and comments. + * + * initscripts compares: "$line" =~ '^[[:space:]]*(\#.*)?$' + */ + while (NM_IN_SET (line[0], ' ', '\t')) + line++; + if (NM_IN_SET (line[0], '\0', '#')) + return TRUE; + return FALSE; +} + +/*****************************************************************************/ + +typedef struct { + const char *key; + + /* the element is not available in this case. */ + bool disabled:1; + + /* whether the element is to be ignored. Ignord is different from + * "disabled", because we still parse the option, but don't use it. */ + bool ignore:1; + + bool int_base_16:1; + + /* the type, one of PARSE_LINE_TYPE_* */ + char type; - g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + /* whether the command line option was found, and @v is + * initialized. */ + bool has:1; - for (i = 0; metrics[i]; i++) { - nm_sprintf_buf (buffer, "(?:\\s|^)%s\\s+(lock\\s+)?(\\d+)(?:$|\\s)", metrics[i]); - regex = g_regex_new (buffer, 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *lock = g_match_info_fetch (match_info, 1); - gs_free char *str = g_match_info_fetch (match_info, 2); - gint64 num = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1); + union { + guint8 uint8; + guint32 uint32; + struct { + guint32 uint32; + bool lock:1; + } uint32_with_lock; + struct { + NMIPAddr addr; + guint8 plen; + bool has_plen:1; + } addr; + } v; - if (num == -1) { - g_match_info_free (match_info); +} ParseLineInfo; + +enum { + /* route attributes */ + PARSE_LINE_ATTR_ROUTE_TABLE, + PARSE_LINE_ATTR_ROUTE_SRC, + PARSE_LINE_ATTR_ROUTE_FROM, + PARSE_LINE_ATTR_ROUTE_TOS, + PARSE_LINE_ATTR_ROUTE_WINDOW, + PARSE_LINE_ATTR_ROUTE_CWND, + PARSE_LINE_ATTR_ROUTE_INITCWND, + PARSE_LINE_ATTR_ROUTE_INITRWND, + PARSE_LINE_ATTR_ROUTE_MTU, + + /* iproute2 arguments that only matter when parsing the file. */ + PARSE_LINE_ATTR_ROUTE_TO, + PARSE_LINE_ATTR_ROUTE_VIA, + PARSE_LINE_ATTR_ROUTE_METRIC, + + /* iproute2 paramters that are well known and that we silently ignore. */ + PARSE_LINE_ATTR_ROUTE_DEV, +}; + +#define PARSE_LINE_TYPE_UINT8 '8' +#define PARSE_LINE_TYPE_UINT32 'u' +#define PARSE_LINE_TYPE_UINT32_WITH_LOCK 'l' +#define PARSE_LINE_TYPE_ADDR 'a' +#define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p' +#define PARSE_LINE_TYPE_IFNAME 'i' + +/** + * parse_route_line: + * @line: the line to parse. This is either a line from the route-* or route6-* file, + * or the numbered OPTIONS setting. + * @addr_family: the address family. + * @options_route: (in-out): when line is from the OPTIONS setting, this is a pre-created + * route object that is completed with the settings from options. Otherwise, + * it shall point to %NULL and a new route is created and returned. + * @out_route: (out): (transfer-full): (allow-none): the parsed %NMIPRoute instance. + * In case a @options_route is passed in, it returns the input route that was modified + * in-place. But the caller must unref the returned route in either case. + * @error: the failure description. + * + * Parsing the route options line has two modes: one for the numbered OPTIONS + * setting, and one for initscript's handle_ip_file(), which takes the lines + * and passes them to `ip route add`. The modes are similar, but certain properties + * are not allowed for OPTIONS. + * The mode is differenciated by having an @options_route argument. + * + * Returns: returns a negative errno on failure. On success, it returns 0 + * and @out_route. + */ +static int +parse_route_line (const char *line, + int addr_family, + NMIPRoute *options_route, + NMIPRoute **out_route, + GError **error) +{ + nm_auto_ip_route_unref NMIPRoute *route = NULL; + gs_free const char **words_free = NULL; + const char *const*words; + const char *s; + gsize i_words; + guint i; + char buf1[256]; + char buf2[256]; + ParseLineInfo infos[] = { + [PARSE_LINE_ATTR_ROUTE_TABLE] = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE, + .type = PARSE_LINE_TYPE_UINT32, }, + [PARSE_LINE_ATTR_ROUTE_SRC] = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC, + .type = PARSE_LINE_TYPE_ADDR, }, + [PARSE_LINE_ATTR_ROUTE_FROM] = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM, + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled = (addr_family != AF_INET6), }, + [PARSE_LINE_ATTR_ROUTE_TOS] = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS, + .type = PARSE_LINE_TYPE_UINT8, + .int_base_16 = TRUE, + .ignore = (addr_family != AF_INET), }, + [PARSE_LINE_ATTR_ROUTE_WINDOW] = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_CWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + [PARSE_LINE_ATTR_ROUTE_MTU] = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, }, + + [PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to", + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled = (options_route != NULL), }, + [PARSE_LINE_ATTR_ROUTE_VIA] = { .key = "via", + .type = PARSE_LINE_TYPE_ADDR, + .disabled = (options_route != NULL), }, + [PARSE_LINE_ATTR_ROUTE_METRIC] = { .key = "metric", + .type = PARSE_LINE_TYPE_UINT32, + .disabled = (options_route != NULL), }, + + [PARSE_LINE_ATTR_ROUTE_DEV] = { .key = "dev", + .type = PARSE_LINE_TYPE_IFNAME, + .ignore = TRUE, + .disabled = (options_route != NULL), }, + }; + + nm_assert (line); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (!options_route || nm_ip_route_get_family (options_route) == addr_family); + + /* initscripts read the legacy route file line-by-line and + * use it as `ip route add $line`, thus doing split+glob. + * Splitting on IFS (which we consider '<space><tab><newline>') + * and globbing (which we obviously don't do). + * + * I think it's a mess, because it doesn't support escaping or + * quoting. In fact, it can only encode benign values. + * + * We also use the same form for the numbered OPTIONS + * variable. I think it's bad not to support any form of + * escaping. But do that for now. + * + * Maybe later we want to support some form of quotation here. + * Which of course, would be incompatible with initscripts. + */ + words_free = nm_utils_strsplit_set (line, " \t\n"); + + words = words_free ?: NM_PTRARRAY_EMPTY (const char *); + + for (i_words = 0; words[i_words]; ) { + const gsize i_words0 = i_words; + const char *const w = words[i_words0]; + ParseLineInfo *info; + gboolean unqualified_addr = FALSE; + + for (i = 0; i < G_N_ELEMENTS (infos); i++) { + info = &infos[i]; + + if (info->disabled) + continue; + + if (!nm_streq (w, info->key)) + continue; + + if (info->has) { + /* iproute2 for most arguments allows specifying them multiple times. + * Let's not do that. */ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route %s '%s'", metrics[i], str); - goto out; + "Duplicate option \"%s\"", w); + return -EINVAL; } - nm_ip_route_set_attribute (route, metrics[i], - g_variant_new_uint32 (num)); - if (lock && lock[0]) { - nm_sprintf_buf (buffer, "lock-%s", metrics[i]); - nm_ip_route_set_attribute (route, buffer, - g_variant_new_boolean (TRUE)); + info->has = TRUE; + switch (info->type) { + case PARSE_LINE_TYPE_UINT8: + i_words++; + goto parse_line_type_uint8; + case PARSE_LINE_TYPE_UINT32: + i_words++; + goto parse_line_type_uint32; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + i_words++; + goto parse_line_type_uint32_with_lock; + case PARSE_LINE_TYPE_ADDR: + i_words++; + goto parse_line_type_addr; + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + i_words++; + goto parse_line_type_addr_with_prefix; + case PARSE_LINE_TYPE_IFNAME: + i_words++; + goto parse_line_type_ifname; + default: + nm_assert_not_reached (); } } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); - } - /* tos */ - regex = g_regex_new ("(?:\\s|^)tos\\s+(\\S+)(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gint64 num = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT8, -1); + /* "to" is also accepted unqualified... (once) */ + info = &infos[PARSE_LINE_ATTR_ROUTE_TO]; + if (!info->has && !info->disabled) { + unqualified_addr = TRUE; + info->has = TRUE; + goto parse_line_type_addr; + } - if (num == -1) { - g_match_info_free (match_info); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (\"to\" is duplicate or \"%s\" is garbage)", w); + return -EINVAL; + +parse_line_type_uint8: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + info->v.uint8 = _nm_utils_ascii_str_to_int64 (s, + info->int_base_16 ? 16 : 10, + 0, + G_MAXUINT8, + 0);; + if (errno) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route %s '%s'", "tos", str); - goto out; + "Argument for \"%s\" is not a valid number", w); + return -EINVAL; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_TOS, - g_variant_new_byte ((guchar) num)); - } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); - - /* from */ - if (family == AF_INET6) { - regex = g_regex_new ("(?:\\s|^)from\\s+(" IPV6_ADDR_REGEX "(?:/\\d{1,3})?)(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gs_free_error GError *local_error = NULL; - GVariant *variant = g_variant_new_string (str); - - if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_FROM, variant, family, NULL, &local_error)) { - g_match_info_free (match_info); - g_variant_unref (variant); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route from '%s': %s", str, local_error->message); - goto out; + i_words++; + goto next; + +parse_line_type_uint32: +parse_line_type_uint32_with_lock: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + if (info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) { + if (nm_streq (s, "lock")) { + s = words[++i_words]; + if (!s) + goto err_word_missing_argument; + info->v.uint32_with_lock.lock = TRUE; + } else + info->v.uint32_with_lock.lock = FALSE; + info->v.uint32_with_lock.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);; + } else { + info->v.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0); + } + if (errno) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid number", w); + return -EINVAL; + } + i_words++; + goto next; + +parse_line_type_ifname: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + i_words++; + goto next; + +parse_line_type_addr: +parse_line_type_addr_with_prefix: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + { + int prefix = -1; + + if (info->type == PARSE_LINE_TYPE_ADDR) { + if (!nm_utils_parse_inaddr_bin (addr_family, + s, + &info->v.addr.addr)) { + if ( info == &infos[PARSE_LINE_ATTR_ROUTE_VIA] + && nm_streq (s, "(null)")) { + /* Due to a bug, would older versions of NM write "via (null)" + * (rh#1452648). Workaround that, and accept it.*/ + memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr)); + } else { + if (unqualified_addr) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (inet prefix is expected rather then \"%s\")", w); + return -EINVAL; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid IPv%c address", w, + addr_family == AF_INET ? '4' : '6'); + } + return -EINVAL; + } + } + } else { + nm_assert (info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX); + if ( info == &infos[PARSE_LINE_ATTR_ROUTE_TO] + && nm_streq (s, "default")) { + memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr)); + prefix = 0; + } else if (!nm_utils_parse_inaddr_prefix_bin (addr_family, + s, + &info->v.addr.addr, + &prefix)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not ADDR/PREFIX format", w); + return -EINVAL; + } + } + if (prefix == -1) + info->v.addr.has_plen = FALSE; + else { + info->v.addr.has_plen = TRUE; + info->v.addr.plen = prefix; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_FROM, variant); } - g_clear_pointer (®ex, g_regex_unref); - g_clear_pointer (&match_info, g_match_info_free); + i_words++; + goto next; + +err_word_missing_argument: + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing argument for \"%s\"", w); + return -EINVAL; +next: + ; } - if (family == AF_INET) - regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV4_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); - else - regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV6_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); - g_regex_match (regex, line, 0, &match_info); - if (g_match_info_matches (match_info)) { - gs_free char *str = g_match_info_fetch (match_info, 1); - gs_free_error GError *local_error = NULL; - GVariant *variant = g_variant_new_string (str); - - if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_SRC, variant, family, - NULL, &local_error)) { - g_match_info_free (match_info); - g_variant_unref (variant); + if (options_route) { + route = options_route; + nm_ip_route_ref (route); + } else { + ParseLineInfo *info_to = &infos[PARSE_LINE_ATTR_ROUTE_TO]; + ParseLineInfo *info_via = &infos[PARSE_LINE_ATTR_ROUTE_VIA]; + ParseLineInfo *info_metric = &infos[PARSE_LINE_ATTR_ROUTE_METRIC]; + guint prefix; + + if (!info_to->has) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid route src '%s': %s", str, local_error->message); - goto out; + "Missing destination prefix"); + return -EINVAL; } - nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_SRC, variant); + prefix = info_to->v.addr.has_plen + ? info_to->v.addr.plen + : (addr_family == AF_INET ? 32 : 128); + + if ( ( (addr_family == AF_INET && !info_to->v.addr.addr.addr4) + || (addr_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&info_to->v.addr.addr.addr6))) + && prefix == 0) { + /* we ignore default routes by returning -ERANGE. */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignore manual default route"); + return -ERANGE; + } + + route = nm_ip_route_new_binary (addr_family, + &info_to->v.addr.addr, + prefix, + info_via->has ? &info_via->v.addr.addr : NULL, + info_metric->has ? (gint64) info_metric->v.uint32 : (gint64) -1, + error); + info_to->has = FALSE; + info_via->has = FALSE; + info_metric->has = FALSE; + if (!route) + return -EINVAL; } - success = TRUE; -out: - if (regex) - g_regex_unref (regex); - if (match_info) - g_match_info_free (match_info); + for (i = 0; i < G_N_ELEMENTS (infos); i++) { + ParseLineInfo *info = &infos[i]; - return success; + if (!info->has) + continue; + if (info->ignore || info->disabled) + continue; + switch (info->type) { + case PARSE_LINE_TYPE_UINT8: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_byte (info->v.uint8)); + break; + case PARSE_LINE_TYPE_UINT32: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_uint32 (info->v.uint32)); + break; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + if (info->v.uint32_with_lock.lock) { + nm_ip_route_set_attribute (route, + nm_sprintf_buf (buf1, "lock-%s", info->key), + g_variant_new_boolean (TRUE)); + } + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_uint32 (info->v.uint32_with_lock.uint32)); + break; + case PARSE_LINE_TYPE_ADDR: + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + nm_ip_route_set_attribute (route, + info->key, + g_variant_new_printf ("%s%s", + inet_ntop (addr_family, &info->v.addr.addr, buf1, sizeof (buf1)), + info->v.addr.has_plen + ? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen) + : "")); + break; + default: + nm_assert_not_reached (); + break; + } + } + + nm_assert (_nm_ip_route_attribute_validate_all (route)); + + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return 0; } /* Returns TRUE on missing route or valid route */ @@ -576,13 +938,13 @@ read_one_ip4_route (shvarFile *ifcfg, guint32 next_hop; guint32 netmask; gboolean has_key; + const char *v; gs_free char *value = NULL; gint64 prefix, metric; char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; g_return_val_if_fail (ifcfg != NULL, FALSE); - g_return_val_if_fail (out_route != NULL, FALSE); - g_return_val_if_fail (*out_route == NULL, FALSE); + g_return_val_if_fail (out_route && !*out_route, FALSE); g_return_val_if_fail (!error || !*error, FALSE); /* Destination */ @@ -610,7 +972,7 @@ read_one_ip4_route (shvarFile *ifcfg, return FALSE; if (has_key) { prefix = nm_utils_ip4_netmask_to_prefix (netmask); - if (prefix == 0 || netmask != nm_utils_ip4_prefix_to_netmask (prefix)) { + if (prefix == 0 || netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf)); return FALSE; @@ -623,12 +985,12 @@ read_one_ip4_route (shvarFile *ifcfg, /* Metric */ nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "METRIC", which)); - if (value) { - metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); + v = svGetValueStr (ifcfg, numbered_tag (tag, "METRIC", which), &value); + if (v) { + metric = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXUINT32, -1); if (metric < 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route metric '%s'", value); + "Invalid IP4 route metric '%s'", v); return FALSE; } } else @@ -640,9 +1002,9 @@ read_one_ip4_route (shvarFile *ifcfg, /* Options */ nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "OPTIONS", which)); - if (value) { - if (!parse_route_options (*out_route, AF_INET, value, error)) { + v = svGetValueStr (ifcfg, numbered_tag (tag, "OPTIONS", which), &value); + if (v) { + if (parse_route_line (v, AF_INET, *out_route, NULL, error) < 0) { g_clear_pointer (out_route, nm_ip_route_unref); return FALSE; } @@ -652,156 +1014,65 @@ read_one_ip4_route (shvarFile *ifcfg, } static gboolean -read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error) +read_route_file (int addr_family, + const char *filename, + NMSettingIPConfig *s_ip, + GError **error) { - char *contents = NULL; + gs_free char *contents = NULL; + char *contents_rest = NULL; + const char *line; gsize len = 0; - char **lines = NULL, **iter; - GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric; - GMatchInfo *match_info; - int prefix_int; - gint64 metric_int; - gboolean success = FALSE; - - const char *pattern_empty = "^\\s*(\\#.*)?$"; - const char *pattern_to1 = "^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ - const char *pattern_to2 = "to\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ - const char *pattern_via = "via\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})"; /* IP of gateway */ - const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ + gsize line_num; - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip4 != NULL, FALSE); + g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail ( (addr_family == AF_INET && NM_IS_SETTING_IP4_CONFIG (s_ip)) + || (addr_family == AF_INET6 && NM_IS_SETTING_IP6_CONFIG (s_ip)), FALSE); g_return_val_if_fail (!error || !*error, FALSE); - /* Read the route file */ - if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) { - g_free (contents); + if ( !g_file_get_contents (filename, &contents, &len, NULL) + || !len) { return TRUE; /* missing/empty = success */ } - /* Create regexes for pieces to be matched */ - regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL); - regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL); - regex_via = g_regex_new (pattern_via, 0, 0, NULL); - regex_metric = g_regex_new (pattern_metric, 0, 0, NULL); + line_num = 0; + for (line = strtok_r (contents, "\n", &contents_rest); + line; + line = strtok_r (NULL, "\n", &contents_rest)) { + nm_auto_ip_route_unref NMIPRoute *route = NULL; + gs_free_error GError *local = NULL; + int e; - /* Iterate through file lines */ - lines = g_strsplit_set (contents, "\n\r", -1); - for (iter = lines; iter && *iter; iter++) { - gs_free char *next_hop = NULL, *dest = NULL; - char *prefix, *metric; - NMIPRoute *route; + line_num++; - /* Skip empty lines */ - if (g_regex_match_simple (pattern_empty, *iter, 0, 0)) + if (parse_route_line_is_comment (line)) continue; - /* Destination */ - g_regex_match (regex_to1, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_regex_match (regex_to2, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IP4 route destination address in record: '%s'", *iter); - goto error; - } - } - dest = g_match_info_fetch (match_info, 1); - if (!strcmp (dest, "default")) { - g_match_info_free (match_info); - PARSE_WARNING ("ignoring manual default route: '%s' (%s)", *iter, filename); - continue; - } - if (!nm_utils_ipaddr_valid (AF_INET, dest)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route destination address '%s'", dest); - g_match_info_free (match_info); - goto error; - } - - /* Prefix - is optional; 32 if missing */ - prefix = g_match_info_fetch (match_info, 2); - g_match_info_free (match_info); - prefix_int = 32; - if (prefix) { - prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 32, -1); - if (prefix_int == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route destination prefix '%s'", prefix); - g_free (prefix); - goto error; - } - } - g_free (prefix); + e = parse_route_line (line, addr_family, NULL, &route, &local); - /* Next hop */ - g_regex_match (regex_via, *iter, 0, &match_info); - if (g_match_info_matches (match_info)) { - next_hop = g_match_info_fetch (match_info, 1); - if (!nm_utils_ipaddr_valid (AF_INET, next_hop)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route gateway address '%s'", - next_hop); - g_match_info_free (match_info); - goto error; - } - } else { - /* we don't make distinction between missing GATEWAY IP and 0.0.0.0 */ - } - g_match_info_free (match_info); - - /* Metric */ - g_regex_match (regex_metric, *iter, 0, &match_info); - metric_int = -1; - if (g_match_info_matches (match_info)) { - metric = g_match_info_fetch (match_info, 1); - metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1); - if (metric_int == -1) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP4 route metric '%s'", metric); - g_free (metric); - goto error; + if (e < 0) { + if (e == -ERANGE) + PARSE_WARNING ("ignoring manual default route: '%s' (%s)", line, filename); + else { + /* we accept all unrecognized lines, because otherwise we would reject the + * entire connection. */ + PARSE_WARNING ("ignoring invalid route at \"%s\" (%s:%lu): %s", line, filename, (long unsigned) line_num, local->message); } - g_free (metric); - } - g_match_info_free (match_info); - - route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error); - if (!route) - goto error; - - if (!parse_route_options (route, AF_INET, *iter, error)) { - nm_ip_route_unref (route); - goto error; + continue; } - if (!nm_setting_ip_config_add_route (s_ip4, route)) - PARSE_WARNING ("duplicate IP4 route"); - nm_ip_route_unref (route); + if (!nm_setting_ip_config_add_route (s_ip, route)) + PARSE_WARNING ("duplicate IPv%c route", addr_family == AF_INET ? '4' : '6'); } - success = TRUE; - -error: - g_free (contents); - g_strfreev (lines); - g_regex_unref (regex_to1); - g_regex_unref (regex_to2); - g_regex_unref (regex_via); - g_regex_unref (regex_metric); - - return success; + return TRUE; } static void parse_dns_options (NMSettingIPConfig *ip_config, const char *value) { - char **options = NULL; + gs_free const char **options = NULL; + const char *const *item; g_return_if_fail (ip_config); @@ -811,16 +1082,12 @@ parse_dns_options (NMSettingIPConfig *ip_config, const char *value) if (!nm_setting_ip_config_has_dns_options (ip_config)) nm_setting_ip_config_clear_dns_options (ip_config, TRUE); - options = g_strsplit (value, " ", 0); + options = nm_utils_strsplit_set (value, " "); if (options) { - char **item; for (item = options; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) - PARSE_WARNING ("can't add DNS option '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) + PARSE_WARNING ("can't add DNS option '%s'", *item); } - g_strfreev (options); } } @@ -873,157 +1140,6 @@ error: return success; } -static gboolean -read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) -{ - char *contents = NULL; - gsize len = 0; - char **lines = NULL, **iter; - GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric; - GMatchInfo *match_info; - char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL; - int prefix_int; - gint64 metric_int; - gboolean success = FALSE; - - const char *pattern_empty = "^\\s*(\\#.*)?$"; - const char *pattern_to1 = "^\\s*(default|" IPV6_ADDR_REGEX ")" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,3}))?"; /* optional prefix */ - const char *pattern_to2 = "to\\s+(default|" IPV6_ADDR_REGEX ")" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,3}))?"; /* optional prefix */ - const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")"; /* IPv6 of gateway */ - const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ - - - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip6 != NULL, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - /* Read the route file */ - if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) { - g_free (contents); - return TRUE; /* missing/empty = success */ - } - - /* Create regexes for pieces to be matched */ - regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL); - regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL); - regex_via = g_regex_new (pattern_via, 0, 0, NULL); - regex_metric = g_regex_new (pattern_metric, 0, 0, NULL); - - /* Iterate through file lines */ - lines = g_strsplit_set (contents, "\n\r", -1); - for (iter = lines; iter && *iter; iter++) { - NMIPRoute *route; - - /* Skip empty lines */ - if (g_regex_match_simple (pattern_empty, *iter, 0, 0)) - continue; - - /* Destination */ - g_regex_match (regex_to1, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_regex_match (regex_to2, *iter, 0, &match_info); - if (!g_match_info_matches (match_info)) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IP6 route destination address in record: '%s'", *iter); - goto error; - } - } - dest = g_match_info_fetch (match_info, 1); - if (!g_strcmp0 (dest, "default")) { - /* Ignore default route - NM handles it internally */ - g_clear_pointer (&dest, g_free); - g_match_info_free (match_info); - PARSE_WARNING ("ignoring manual default route: '%s' (%s)", *iter, filename); - continue; - } - - /* Prefix - is optional; 128 if missing */ - prefix = g_match_info_fetch (match_info, 2); - g_match_info_free (match_info); - prefix_int = 128; - if (prefix) { - prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 128, -1); - if (prefix_int == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 route destination prefix '%s'", prefix); - g_free (dest); - g_free (prefix); - goto error; - } - } - g_free (prefix); - - /* Next hop */ - g_regex_match (regex_via, *iter, 0, &match_info); - if (g_match_info_matches (match_info)) { - next_hop = g_match_info_fetch (match_info, 1); - if (!nm_utils_ipaddr_valid (AF_INET6, next_hop)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IPv6 route nexthop address '%s'", - next_hop); - g_match_info_free (match_info); - g_free (dest); - g_free (next_hop); - goto error; - } - } else { - /* Missing "via" is taken as :: */ - next_hop = NULL; - } - g_match_info_free (match_info); - - /* Metric */ - g_regex_match (regex_metric, *iter, 0, &match_info); - metric_int = -1; - if (g_match_info_matches (match_info)) { - metric = g_match_info_fetch (match_info, 1); - metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1); - if (metric_int == -1) { - g_match_info_free (match_info); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 route metric '%s'", metric); - g_free (dest); - g_free (next_hop); - g_free (metric); - goto error; - } - g_free (metric); - } - g_match_info_free (match_info); - - route = nm_ip_route_new (AF_INET6, dest, prefix_int, next_hop, metric_int, error); - g_free (dest); - g_free (next_hop); - if (!route) - goto error; - - if (!parse_route_options (route, AF_INET6, *iter, error)) { - nm_ip_route_unref (route); - goto error; - } - - if (!nm_setting_ip_config_add_route (s_ip6, route)) - PARSE_WARNING ("duplicate IP6 route"); - nm_ip_route_unref (route); - } - - success = TRUE; - -error: - g_free (contents); - g_strfreev (lines); - g_regex_unref (regex_to1); - g_regex_unref (regex_to2); - g_regex_unref (regex_via); - g_regex_unref (regex_metric); - - return success; -} - static NMSetting * make_user_setting (shvarFile *ifcfg, GError **error) { @@ -1076,18 +1192,18 @@ static NMSetting * make_proxy_setting (shvarFile *ifcfg, GError **error) { NMSettingProxy *s_proxy = NULL; - char *value = NULL; + gs_free char *value = NULL; + const char *v; NMSettingProxyMethod method; - value = svGetValueStr_cp (ifcfg, "PROXY_METHOD"); - if (!value) + v = svGetValueStr (ifcfg, "PROXY_METHOD", &value); + if (!v) return NULL; - if (!g_ascii_strcasecmp (value, "auto")) + if (!g_ascii_strcasecmp (v, "auto")) method = NM_SETTING_PROXY_METHOD_AUTO; else method = NM_SETTING_PROXY_METHOD_NONE; - g_free (value); s_proxy = (NMSettingProxy *) nm_setting_proxy_new (); @@ -1097,19 +1213,15 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO, NULL); - value = svGetValueStr_cp (ifcfg, "PAC_URL"); - if (value) { - value = g_strstrip (value); - g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "PAC_URL", &value); + if (v) + g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, v, NULL); - value = svGetValueStr_cp (ifcfg, "PAC_SCRIPT"); - if (value) { - value = g_strstrip (value); - g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "PAC_SCRIPT", &value); + if (v) + g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, v, NULL); break; case NM_SETTING_PROXY_METHOD_NONE: @@ -1119,12 +1231,8 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) break; } - value = svGetValueStr_cp (ifcfg, "BROWSER_ONLY"); - if (value) { - if (!g_ascii_strcasecmp (value, "yes")) - g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL); - g_free (value); - } + if (svGetValueBoolean (ifcfg, "BROWSER_ONLY", FALSE)) + g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL); return NM_SETTING (s_proxy); } @@ -1132,12 +1240,14 @@ make_proxy_setting (shvarFile *ifcfg, GError **error) static NMSetting * make_ip4_setting (shvarFile *ifcfg, const char *network_file, + gboolean routes_read, gboolean *out_has_defroute, GError **error) { gs_unref_object NMSettingIPConfig *s_ip4 = NULL; gs_free char *route_path = NULL; - char *value = NULL; + gs_free char *value = NULL; + const char *v; char *method; gs_free char *dns_options_free = NULL; const char *dns_options = NULL; @@ -1151,6 +1261,8 @@ make_ip4_setting (shvarFile *ifcfg, gint64 timeout; gint priority; char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + const char *const *item; + guint32 route_table; nm_assert (out_has_defroute && !*out_has_defroute); @@ -1172,44 +1284,43 @@ make_ip4_setting (shvarFile *ifcfg, /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */ network_ifcfg = svOpenFile (network_file, NULL); if (network_ifcfg) { - char *gatewaydev; + gs_free char *gatewaydev_value = NULL; + const char *gatewaydev; /* Get the connection ifcfg device name and the global gateway device */ - value = svGetValueStr_cp (ifcfg, "DEVICE"); - gatewaydev = svGetValueStr_cp (network_ifcfg, "GATEWAYDEV"); + v = svGetValueStr (ifcfg, "DEVICE", &value); + gatewaydev = svGetValueStr (network_ifcfg, "GATEWAYDEV", &gatewaydev_value); dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free); /* If there was a global gateway device specified, then only connections * for that device can be the default connection. */ - if (gatewaydev && value) - never_default = !!strcmp (value, gatewaydev); + if (gatewaydev && v) + never_default = !!strcmp (v, gatewaydev); - g_free (gatewaydev); - g_free (value); + nm_clear_g_free (&value); svCloseFile (network_ifcfg); } - value = svGetValueStr_cp (ifcfg, "BOOTPROTO"); + v = svGetValueStr (ifcfg, "BOOTPROTO", &value); - if (!value || !*value || !g_ascii_strcasecmp (value, "none")) { + if (!v || !*v || !g_ascii_strcasecmp (v, "none")) { if (is_any_ip4_address_defined (ifcfg, NULL)) method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; else method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - } else if (!g_ascii_strcasecmp (value, "bootp") || !g_ascii_strcasecmp (value, "dhcp")) { + } else if (!g_ascii_strcasecmp (v, "bootp") || !g_ascii_strcasecmp (v, "dhcp")) { method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - } else if (!g_ascii_strcasecmp (value, "static")) { + } else if (!g_ascii_strcasecmp (v, "static")) { if (is_any_ip4_address_defined (ifcfg, NULL)) method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; else method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - } else if (!g_ascii_strcasecmp (value, "autoip")) { + } else if (!g_ascii_strcasecmp (v, "autoip")) { method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL; - } else if (!g_ascii_strcasecmp (value, "shared")) { + } else if (!g_ascii_strcasecmp (v, "shared")) { int idx; - g_free (value); g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, @@ -1233,11 +1344,18 @@ make_ip4_setting (shvarFile *ifcfg, return g_steal_pointer (&s_ip4); } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown BOOTPROTO '%s'", value); - g_free (value); + "Unknown BOOTPROTO '%s'", v); return NULL; } - g_free (value); + + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64 (ifcfg, "IPV4_ROUTE_TABLE", 10, + 0, G_MAXUINT32, 0); + if ( route_table != 0 + && !routes_read) { + PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV4_ROUTE_TABLE) is ignored"); + route_table = 0; + } g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, @@ -1247,44 +1365,43 @@ make_ip4_setting (shvarFile *ifcfg, NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV4_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, NULL); if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) return g_steal_pointer (&s_ip4); /* Handle DHCP settings */ - value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME"); - if (value) { - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_HOSTNAME", &value); + if (v) + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); - value = svGetValueStr_cp (ifcfg, "DHCP_FQDN"); - if (value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_FQDN", &value); + if (v) { g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL, - NM_SETTING_IP4_CONFIG_DHCP_FQDN, value, + NM_SETTING_IP4_CONFIG_DHCP_FQDN, v, NULL); - g_free (value); } g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), - NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), - NULL); + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE), + NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NULL); - value = svGetValueStr_cp (ifcfg, "DHCP_CLIENT_ID"); - if (value) { - g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL); - g_free (value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_CLIENT_ID", &value); + if (v) + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, v, NULL); /* Read static IP addresses. * Read them even for AUTO method - in this case the addresses are * added to the automatic ones. Note that this is not currently supported by * the legacy 'network' service (ifup-eth). */ - for (i = -1; i < 256; i++) { + for (i = -1;; i++) { NMIPAddress *addr = NULL; /* gateway will only be set if still unset. Hence, we don't leak gateway @@ -1337,46 +1454,40 @@ make_ip4_setting (shvarFile *ifcfg, char tag[256]; numbered_tag (tag, "DNS", i); - value = svGetValueStr_cp (ifcfg, tag); - if (value) { - if (nm_utils_ipaddr_valid (AF_INET, value)) { - if (!nm_setting_ip_config_add_dns (s_ip4, value)) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, tag, &value); + if (v) { + if (nm_utils_ipaddr_valid (AF_INET, v)) { + if (!nm_setting_ip_config_add_dns (s_ip4, v)) PARSE_WARNING ("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_valid (AF_INET6, value)) { + } else if (nm_utils_ipaddr_valid (AF_INET6, v)) { /* Ignore IPv6 addresses */ } else { - PARSE_WARNING ("invalid DNS server address %s", value); - g_free (value); + PARSE_WARNING ("invalid DNS server address %s", v); return NULL; } - - g_free (value); } } /* DNS searches */ - value = svGetValueStr_cp (ifcfg, "DOMAIN"); - if (value) { - char **searches = NULL; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; - searches = g_strsplit (value, " ", 0); + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) - PARSE_WARNING ("duplicate DNS domain '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) + PARSE_WARNING ("duplicate DNS domain '%s'", *item); } - g_strfreev (searches); } - g_free (value); } /* DNS options */ + nm_clear_g_free (&value); parse_dns_options (s_ip4, svGetValue (ifcfg, "RES_OPTIONS", &value)); parse_dns_options (s_ip4, dns_options); - g_free (value); /* DNS priority */ priority = svGetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); @@ -1388,13 +1499,13 @@ make_ip4_setting (shvarFile *ifcfg, /* Static routes - route-<name> file */ route_path = utils_get_route_path (svFileGetName (ifcfg)); - if (utils_has_complex_routes (route_path)) { - PARSE_WARNING ("'rule-' or 'rule6-' file is present; you will need to use a dispatcher script to apply these routes"); + if (!routes_read) { + /* NOP */ } else if (utils_has_route_file_new_syntax (route_path)) { /* Parse route file in new syntax */ route_ifcfg = utils_get_route_ifcfg (svFileGetName (ifcfg), FALSE); if (route_ifcfg) { - for (i = 0; i < 256; i++) { + for (i = 0;; i++) { NMIPRoute *route = NULL; if (!read_one_ip4_route (route_ifcfg, i, &route, error)) { @@ -1412,28 +1523,24 @@ make_ip4_setting (shvarFile *ifcfg, svCloseFile (route_ifcfg); } } else { - if (!read_route_file_legacy (route_path, s_ip4, error)) + if (!read_route_file (AF_INET, route_path, s_ip4, error)) return NULL; } /* Legacy value NM used for a while but is incorrect (rh #459370) */ if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) { - value = svGetValueStr_cp (ifcfg, "SEARCH"); - if (value) { - char **searches = NULL; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SEARCH", &value); + if (v) { + gs_free const char **searches = NULL; - searches = g_strsplit (value, " ", 0); + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) - PARSE_WARNING ("duplicate DNS search '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) + PARSE_WARNING ("duplicate DNS search '%s'", *item); } - g_strfreev (searches); } - g_free (value); } } @@ -1450,7 +1557,6 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file { GDir *dir; char *dirname, *base; - shvarFile *parsed; NMIPAddress *base_addr = NULL; GError *err = NULL; @@ -1472,8 +1578,11 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file gboolean ok; while ((item = g_dir_read_name (dir))) { + nm_auto_shvar_file_close shvarFile *parsed = NULL; gs_free char *gateway = NULL; - char *full_path, *device; + gs_free char *device_value = NULL; + gs_free char *full_path = NULL; + const char *device; const char *p; if (!utils_is_ifcfg_alias_file (item, base)) @@ -1489,32 +1598,25 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file } if (*p) { PARSE_WARNING ("ignoring alias file '%s' with invalid name", full_path); - g_free (full_path); continue; } parsed = svOpenFile (full_path, &err); if (!parsed) { PARSE_WARNING ("couldn't parse alias file '%s': %s", full_path, err->message); - g_free (full_path); g_clear_error (&err); continue; } - device = svGetValueStr_cp (parsed, "DEVICE"); + device = svGetValueStr (parsed, "DEVICE", &device_value); if (!device) { PARSE_WARNING ("alias file '%s' has no DEVICE", full_path); - svCloseFile (parsed); - g_free (full_path); continue; } /* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */ if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) { PARSE_WARNING ("alias file '%s' has invalid DEVICE (%s) for filename", full_path, device); - g_free (device); - svCloseFile (parsed); - g_free (full_path); continue; } @@ -1549,11 +1651,6 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file g_clear_error (&err); } nm_ip_address_unref (addr); - - svCloseFile (parsed); - - g_free (device); - g_free (full_path); } g_dir_close (dir); @@ -1569,6 +1666,7 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file static NMSetting * make_ip6_setting (shvarFile *ifcfg, const char *network_file, + gboolean routes_read, GError **error) { NMSettingIPConfig *s_ip6 = NULL; @@ -1580,14 +1678,17 @@ make_ip6_setting (shvarFile *ifcfg, gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; char *ipv6addr, *ipv6addr_secondaries; - char **list = NULL, **iter; + gs_free const char **list = NULL; + const char *const *iter; guint32 i; + int i_val; + GError *local = NULL; gint priority; shvarFile *network_ifcfg; gboolean never_default = FALSE; gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip; NMSettingIP6ConfigPrivacy ip6_privacy_val; - NMSettingIP6ConfigAddrGenMode addr_gen_mode; + guint32 route_table; s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); @@ -1689,6 +1790,15 @@ make_ip6_setting (shvarFile *ifcfg, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; g_free (str_value); + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64 (ifcfg, "IPV6_ROUTE_TABLE", 10, + 0, G_MAXUINT32, 0); + if ( route_table != 0 + && !routes_read) { + PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV6_ROUTE_TABLE) is ignored"); + route_table = 0; + } + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "IPV6_PEERDNS", TRUE), @@ -1697,6 +1807,7 @@ make_ip6_setting (shvarFile *ifcfg, NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV6_FAILURE_FATAL", FALSE), NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV6_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val, NULL); @@ -1736,21 +1847,18 @@ make_ip6_setting (shvarFile *ifcfg, g_free (ipv6addr); g_free (ipv6addr_secondaries); - list = g_strsplit_set (value, " ", 0); + list = nm_utils_strsplit_set (value, " "); g_free (value); for (iter = list, i = 0; iter && *iter; iter++, i++) { NMIPAddress *addr = NULL; - if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error)) { - g_strfreev (list); + if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error)) goto error; - } if (!nm_setting_ip_config_add_address (s_ip6, addr)) PARSE_WARNING ("duplicate IP6 address"); nm_ip_address_unref (addr); } - g_strfreev (list); /* Gateway */ if (nm_setting_ip_config_get_num_addresses (s_ip6)) { @@ -1779,21 +1887,14 @@ make_ip6_setting (shvarFile *ifcfg, } } - /* IPv6 addressing mode configuration */ - str_value = svGetValueStr_cp (ifcfg, "IPV6_ADDR_GEN_MODE"); - if (str_value) { - if (nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), str_value, - (int *) &addr_gen_mode, NULL)) - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, addr_gen_mode, NULL); - else - PARSE_WARNING ("Invalid IPV6_ADDR_GEN_MODE"); - g_free (str_value); - } else { - g_object_set (s_ip6, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, - NULL); + i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + if (!svGetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE", + nm_setting_ip6_config_addr_gen_mode_get_type (), + &i_val, &local)) { + PARSE_WARNING ("%s", local->message); + g_clear_error (&local); } + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL); /* IPv6 tokenized interface identifier */ str_value = svGetValueStr_cp (ifcfg, "IPV6_TOKEN"); @@ -1831,12 +1932,13 @@ make_ip6_setting (shvarFile *ifcfg, /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */ - if (!utils_has_complex_routes (svFileGetName (ifcfg))) { + if (!routes_read) { + /* NOP */ + } else { /* Read static routes from route6-<interface> file */ route6_path = utils_get_route6_path (svFileGetName (ifcfg)); - if (!read_route6_file (route6_path, s_ip6, error)) + if (!read_route_file (AF_INET6, route6_path, s_ip6, error)) goto error; - g_free (route6_path); } @@ -1860,57 +1962,6 @@ error: return NULL; } -static void -check_if_bond_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - char *value; - - value = svGetValueStr_cp (ifcfg, "MASTER_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "MASTER"); - - if (value) { - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); - g_object_set (s_con, - NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, - NULL); - g_free (value); - } - - /* We should be checking for SLAVE=yes as well, but NM used to not set that, - * so for backward-compatibility, we don't check. - */ -} - -static gboolean -check_if_team_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - gs_free char *value = NULL; - - value = svGetValueStr_cp (ifcfg, "TEAM_MASTER_UUID"); - if (!value) - value = svGetValueStr_cp (ifcfg, "TEAM_MASTER"); - if (!value) - return FALSE; - - g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL); - g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); - return TRUE; -} - -static void -check_if_slave (shvarFile *ifcfg, - NMSettingConnection *s_con) -{ - g_return_if_fail (NM_IS_SETTING_CONNECTION (s_con)); - - if (check_if_team_slave (ifcfg, s_con)) - return; - check_if_bond_slave (ifcfg, s_con); -} - typedef struct { const char *enable_key; const char *advertise_key; @@ -1959,29 +2010,29 @@ read_dcb_app (shvarFile *ifcfg, GError **error) { NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; - char *tmp, *val; + gs_free char *value = NULL; + const char *v; gboolean success = TRUE; int priority = -1; + char key[255]; flags = read_dcb_flags (ifcfg, flags_prop); /* Priority */ - tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app); - val = svGetValueStr_cp (ifcfg, tmp); - if (val) { - priority = _nm_utils_ascii_str_to_int64 (val, 0, 0, 7, -1); + nm_sprintf_buf (key, "DCB_APP_%s_PRIORITY", app); + v = svGetValueStr (ifcfg, key, &value); + if (v) { + priority = _nm_utils_ascii_str_to_int64 (v, 0, 0, 7, -1); if (priority < 0) { success = FALSE; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid %s value '%s' (expected 0 - 7)", - tmp, val); + key, v); } - g_free (val); if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) PARSE_WARNING ("ignoring DCB %s priority; app not enabled", app); } - g_free (tmp); if (success) { g_object_set (G_OBJECT (s_dcb), @@ -2004,11 +2055,12 @@ read_dcb_bool_array (shvarFile *ifcfg, DcbSetBoolFunc set_func, GError **error) { - gs_free char *val = NULL; + gs_free char *value = NULL; + const char *v; guint i; - val = svGetValueStr_cp (ifcfg, prop); - if (!val) + v = svGetValueStr (ifcfg, prop, &value); + if (!v) return TRUE; if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) { @@ -2016,8 +2068,8 @@ read_dcb_bool_array (shvarFile *ifcfg, return TRUE; } - if (strlen (val) != 8) { - PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val); + if (strlen (v) != 8) { + PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, v); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "boolean array must be 8 characters"); return FALSE; @@ -2025,13 +2077,13 @@ read_dcb_bool_array (shvarFile *ifcfg, /* All characters must be either 0 or 1 */ for (i = 0; i < 8; i++) { - if (val[i] != '0' && val[i] != '1') { - PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, val); + if (v[i] != '0' && v[i] != '1') { + PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, v); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "invalid boolean digit"); return FALSE; } - set_func (s_dcb, i, (val[i] == '1')); + set_func (s_dcb, i, (v[i] == '1')); } return TRUE; } @@ -2096,8 +2148,8 @@ read_dcb_percent_array (shvarFile *ifcfg, GError **error) { gs_free char *val = NULL; - gs_strfreev char **split = NULL; - char **iter; + gs_free const char **split = NULL; + const char *const *iter; guint i, sum = 0; val = svGetValueStr_cp (ifcfg, prop); @@ -2109,8 +2161,8 @@ read_dcb_percent_array (shvarFile *ifcfg, return TRUE; } - split = g_strsplit_set (val, ",", 0); - if (!split || (g_strv_length (split) != 8)) { + split = nm_utils_strsplit_set (val, ","); + if (NM_PTRARRAY_LEN (split) != 8) { PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "percent array must be 8 elements"); @@ -2531,15 +2583,17 @@ fill_wpa_ciphers (shvarFile *ifcfg, gboolean group, gboolean adhoc) { - char *value = NULL, *p; - char **list = NULL, **iter; + gs_free char *value = NULL; + const char *p; + gs_free const char **list = NULL; + const char *const *iter; int i = 0; - p = value = svGetValueStr_cp (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE"); - if (!value) + p = svGetValueStr (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", &value); + if (!p) return TRUE; - list = g_strsplit_set (p, " ", 0); + list = nm_utils_strsplit_set (p, " "); for (iter = list; iter && *iter; iter++, i++) { /* Ad-Hoc configurations cannot have pairwise ciphers, and can only * have one group cipher. Ignore any additional group ciphers and @@ -2578,9 +2632,6 @@ fill_wpa_ciphers (shvarFile *ifcfg, } } - if (list) - g_strfreev (list); - g_free (value); return TRUE; } @@ -2637,6 +2688,23 @@ parse_wpa_psk (shvarFile *ifcfg, return g_steal_pointer (&psk); } +static void +read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name, + char **value, NMSettingSecretFlags *flags) +{ + gs_free char *flags_key = NULL; + + *value = NULL; + flags_key = g_strdup_printf ("%s_FLAGS", name); + *flags = read_secret_flags (ifcfg, flags_key); + + if (*flags == NM_SETTING_SECRET_FLAG_NONE) { + *value = svGetValueStr_cp (ifcfg, name); + if (!*value && keys_ifcfg) + *value = svGetValueStr_cp (keys_ifcfg, name); + } +} + static gboolean eap_simple_reader (const char *eap_method, shvarFile *ifcfg, @@ -2646,6 +2714,7 @@ eap_simple_reader (const char *eap_method, GError **error) { NMSettingSecretFlags flags; + GBytes *bytes; char *value; value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); @@ -2656,28 +2725,29 @@ eap_simple_reader (const char *eap_method, return FALSE; } g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - g_free (value); + nm_clear_g_free (&value); - flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); + read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD", &value, &flags); g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL); + if (value) { + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); + nm_clear_g_free (&value); + } - /* Only read the password if it's system-owned */ - if (flags == NM_SETTING_SECRET_FLAG_NONE) { - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD"); - if (!value && keys) { - /* Try the lookaside keys file */ - value = svGetValueStr_cp (keys, "IEEE_8021X_PASSWORD"); - } - - if (!value) { + read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD_RAW", &value, &flags); + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL); + if (value) { + bytes = nm_utils_hexstr2bin (value); + if (!bytes) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", - eap_method); + "Invalid hex string '%s' in IEEE_8021X_PASSWORD_RAW.", + value); + g_free (value); return FALSE; } - - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); - g_free (value); + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW, bytes, NULL); + g_bytes_unref (bytes); + nm_clear_g_free (&value); } return TRUE; @@ -2795,14 +2865,6 @@ eap_tls_reader (const char *eap_method, /* Try the lookaside keys file */ privkey_password = svGetValueStr_cp (keys, pk_pw_key); } - - if (!privkey_password) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - pk_pw_key, - eap_method); - return FALSE; - } } /* The private key itself */ @@ -2843,8 +2905,7 @@ eap_tls_reader (const char *eap_method, * then nm_setting_802_1x_set_private_key() already set the client certificate * to the same value as the private key. */ - if ( privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY - || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { + if (privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { gs_free char *real_cert_value = NULL; gs_free char *client_cert = NULL; @@ -2888,98 +2949,89 @@ eap_peap_reader (const char *eap_method, gboolean phase2, GError **error) { - char *anon_ident = NULL; - char *ca_cert = NULL; - char *real_cert_value = NULL; - char *inner_auth = NULL; - char *peapver = NULL; - char *lower; - char **list = NULL, **iter; - gboolean success = FALSE; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; NMSetting8021xCKScheme scheme; - ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT"); - if (ca_cert) { - real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme); + v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); + if (v) { + gs_free char *real_cert_value = NULL; + + real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - goto done; + return FALSE; } else { PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); } - peapver = svGetValueStr_cp (ifcfg, "IEEE_8021X_PEAP_VERSION"); - if (peapver) { - if (!strcmp (peapver, "0")) + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value); + if (v) { + if (!strcmp (v, "0")) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL); - else if (!strcmp (peapver, "1")) + else if (!strcmp (v, "1")) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL); else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_PEAP_VERSION value '%s'", - peapver); - goto done; + v); + return FALSE; } } if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL); - anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY"); - if (anon_ident) - g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); - inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS"); - if (!inner_auth) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; + return FALSE; } /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - - if ( !strcmp (*iter, "MSCHAPV2") - || !strcmp (*iter, "MD5") - || !strcmp (*iter, "GTC")) { + list = nm_utils_strsplit_set (v, " "); + iter = list; + if (iter) { + if (NM_IN_STRSET (*iter, "MSCHAPV2", + "MD5", + "GTC")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; - } else if (!strcmp (*iter, "TLS")) { + return FALSE; + } else if (nm_streq (*iter, "TLS")) { if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); - goto done; + return FALSE; } - lower = g_ascii_strdown (*iter, -1); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); - g_free (lower); - break; + { + gs_free char *lower = NULL; + + lower = g_ascii_strdown (*iter, -1); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); + } } if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "No valid IEEE_8021X_INNER_AUTH_METHODS found."); - goto done; + return FALSE; } - success = TRUE; - -done: - if (list) - g_strfreev (list); - g_free (inner_auth); - g_free (peapver); - g_free (real_cert_value); - g_free (ca_cert); - g_free (anon_ident); - return success; + return TRUE; } static gboolean @@ -2990,81 +3042,70 @@ eap_ttls_reader (const char *eap_method, gboolean phase2, GError **error) { - gboolean success = FALSE; - char *anon_ident = NULL; - char *ca_cert = NULL; - char *real_cert_value = NULL; - char *inner_auth = NULL; - char *tmp; - char **list = NULL, **iter; + gs_free char *inner_auth = NULL; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; NMSetting8021xCKScheme scheme; - ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT"); - if (ca_cert) { - real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme); + v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); + if (v) { + gs_free char *real_cert_value = NULL; + + real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - goto done; + return FALSE; } else { PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); } - anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY"); - if (anon_ident) - g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); - tmp = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS"); - if (!tmp) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; + return FALSE; } - inner_auth = g_ascii_strdown (tmp, -1); - g_free (tmp); + inner_auth = g_ascii_strdown (v, -1); /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - - if ( !strcmp (*iter, "mschapv2") - || !strcmp (*iter, "mschap") - || !strcmp (*iter, "pap") - || !strcmp (*iter, "chap")) { + list = nm_utils_strsplit_set (inner_auth, " "); + iter = list; + if (iter) { + if (NM_IN_STRSET (*iter, "mschapv2", + "mschap", + "pap", + "chap")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL); - } else if (!strcmp (*iter, "eap-tls")) { + } else if (nm_streq (*iter, "eap-tls")) { if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL); - } else if ( !strcmp (*iter, "eap-mschapv2") - || !strcmp (*iter, "eap-md5") - || !strcmp (*iter, "eap-gtc")) { + } else if (NM_IN_STRSET (*iter, "eap-mschapv2", + "eap-md5", + "eap-gtc")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) - goto done; + return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL); } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); - goto done; + return FALSE; } - break; } - success = TRUE; - -done: - if (list) - g_strfreev (list); - g_free (inner_auth); - g_free (real_cert_value); - g_free (ca_cert); - g_free (anon_ident); - return success; + return TRUE; } static gboolean @@ -3081,7 +3122,8 @@ eap_fast_reader (const char *eap_method, char *inner_auth = NULL; char *fast_provisioning = NULL; char *lower; - char **list = NULL, **iter; + gs_free const char **list = NULL; + const char *const *iter; const char *pac_prov_str; gboolean allow_unauth = FALSE, allow_auth = FALSE; gboolean success = FALSE; @@ -3094,10 +3136,10 @@ eap_fast_reader (const char *eap_method, fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING"); if (fast_provisioning) { - list = g_strsplit_set (fast_provisioning, " \t", 0); - for (iter = list; iter && *iter; iter++) { - if (**iter == '\0') - continue; + gs_free const char **list1 = NULL; + + list1 = nm_utils_strsplit_set (fast_provisioning, " \t"); + for (iter = list1; iter && *iter; iter++) { if (strcmp (*iter, "allow-unauth") == 0) allow_unauth = TRUE; else if (strcmp (*iter, "allow-auth") == 0) @@ -3108,8 +3150,6 @@ eap_fast_reader (const char *eap_method, *iter); } } - g_strfreev (list); - list = NULL; } pac_prov_str = allow_unauth ? (allow_auth ? "3" : "1") : (allow_auth ? "2" : "0"); g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, pac_prov_str, NULL); @@ -3132,11 +3172,9 @@ eap_fast_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - if (!strlen (*iter)) - continue; - + list = nm_utils_strsplit_set (inner_auth, " "); + iter = list; + if (iter) { if ( !strcmp (*iter, "MSCHAPV2") || !strcmp (*iter, "GTC")) { if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) @@ -3151,7 +3189,6 @@ eap_fast_reader (const char *eap_method, lower = g_ascii_strdown (*iter, -1); g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL); g_free (lower); - break; } if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { @@ -3163,7 +3200,6 @@ eap_fast_reader (const char *eap_method, success = TRUE; done: - g_strfreev (list); g_free (inner_auth); g_free (fast_provisioning); g_free (real_pac_path); @@ -3204,22 +3240,21 @@ read_8021x_list_value (shvarFile *ifcfg, NMSetting8021x *setting, const char *prop_name) { - char *value; - char **strv; + gs_free char *value = NULL; + gs_free const char **strv = NULL; + const char *v; g_return_if_fail (ifcfg != NULL); g_return_if_fail (ifcfg_var_name != NULL); g_return_if_fail (prop_name != NULL); - value = svGetValueStr_cp (ifcfg, ifcfg_var_name); - if (!value) + v = svGetValueStr (ifcfg, ifcfg_var_name, &value); + if (!v) return; - strv = g_strsplit_set (value, " \t", 0); - if (strv && strv[0]) + strv = nm_utils_strsplit_set (v, " \t"); + if (strv) g_object_set (setting, prop_name, strv, NULL); - g_strfreev (strv); - g_free (value); } static NMSetting8021x * @@ -3230,21 +3265,23 @@ fill_8021x (shvarFile *ifcfg, GError **error) { nm_auto_shvar_file_close shvarFile *keys = NULL; - NMSetting8021x *s_8021x; - char *value; - char **list = NULL, **iter; + gs_unref_object NMSetting8021x *s_8021x = NULL; + gs_free char *value = NULL; + const char *v; + gs_free const char **list = NULL; + const char *const *iter; gint64 timeout; + int i_val; - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_EAP_METHODS"); - if (!value) { + v = svGetValueStr (ifcfg, "IEEE_8021X_EAP_METHODS", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing IEEE_8021X_EAP_METHODS for key management '%s'", key_mgmt); return NULL; } - list = g_strsplit (value, " ", 0); - g_free (value); + list = nm_utils_strsplit_set (v, " "); s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); @@ -3255,7 +3292,7 @@ fill_8021x (shvarFile *ifcfg, for (iter = list; iter && *iter; iter++) { EAPReader *eap = &eap_readers[0]; gboolean found = FALSE; - char *lower = NULL; + gs_free char *lower = NULL; lower = g_ascii_strdown (*iter, -1); while (eap->method) { @@ -3273,10 +3310,9 @@ fill_8021x (shvarFile *ifcfg, } /* Parse EAP method specific options */ - if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) { - g_free (lower); - goto error; - } + if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) + return NULL; + nm_setting_802_1x_add_eap_method (s_8021x, lower); found = TRUE; break; @@ -3287,65 +3323,46 @@ next: if (!found) PARSE_WARNING ("ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower); - g_free (lower); } if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); - goto error; + return NULL; } - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_SUBJECT_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, value, NULL); - g_free (value); - - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, value, NULL); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_SUBJECT_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, v, NULL); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS"); - if (value) { - NMSetting8021xAuthFlags flags; - char *token; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, v, NULL); - if (nm_utils_enum_from_str (nm_setting_802_1x_auth_flags_get_type (), value, - (int *) &flags, &token)) { - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, flags, NULL); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IEEE_8021X_PHASE1_AUTH_FLAGS flag '%s'", token); - g_free (token); - g_free (value); - goto error; - } - g_free (value); - } + i_val = NM_SETTING_802_1X_AUTH_FLAGS_NONE; + if (!svGetValueEnum (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type (), + &i_val, error)) + return NULL; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, (guint) i_val, NULL); read_8021x_list_value (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", s_8021x, NM_SETTING_802_1X_ALTSUBJECT_MATCHES); read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, value, NULL); - g_free (value); - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH"); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, value, NULL); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, v, NULL); - timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0); - g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint32) timeout, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", &value); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, v, NULL); - if (list) - g_strfreev (list); - return s_8021x; + timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0); + g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint) timeout, NULL); -error: - if (list) - g_strfreev (list); - g_object_unref (s_8021x); - return NULL; + return g_steal_pointer (&s_8021x); } static NMSetting * @@ -3356,18 +3373,31 @@ make_wpa_setting (shvarFile *ifcfg, NMSetting8021x **s_8021x, GError **error) { - NMSettingWirelessSecurity *wsec; - char *value, *psk, *lower; + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; + gs_free char *value = NULL; + const char *v; gboolean wpa_psk = FALSE, wpa_eap = FALSE, ieee8021x = FALSE; + int i_val; + GError *local = NULL; wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); - value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); - wpa_psk = !g_strcmp0 (value, "WPA-PSK"); - wpa_eap = !g_strcmp0 (value, "WPA-EAP"); - ieee8021x = !g_strcmp0 (value, "IEEE8021X"); + v = svGetValueStr (ifcfg, "KEY_MGMT", &value); + wpa_psk = nm_streq0 (v, "WPA-PSK"); + wpa_eap = nm_streq0 (v, "WPA-EAP"); + ieee8021x = nm_streq0 (v, "IEEE8021X"); if (!wpa_psk && !wpa_eap && !ieee8021x) - goto error; /* Not WPA or Dynamic WEP */ + return NULL; /* Not WPA or Dynamic WEP */ + + /* WPS */ + i_val = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT; + if (!svGetValueEnum (ifcfg, "WPS_METHOD", + nm_setting_wireless_security_wps_method_get_type (), + &i_val, error)) + return NULL; + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, (guint) i_val, + NULL); /* Pairwise and Group ciphers (only relevant for WPA/RSN) */ if (wpa_psk || wpa_eap) { @@ -3380,18 +3410,17 @@ make_wpa_setting (shvarFile *ifcfg, /* Ad-Hoc mode only supports WPA proto for now */ nm_setting_wireless_security_add_proto (wsec, "wpa"); } else { - char *allow_wpa, *allow_rsn; + gs_free char *value2 = NULL; + const char *v2; - allow_wpa = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA"); - allow_rsn = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA2"); - - if (allow_wpa && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA", TRUE)) + v2 = svGetValueStr (ifcfg, "WPA_ALLOW_WPA", &value2); + if (v2 && svParseBoolean (v2, TRUE)) nm_setting_wireless_security_add_proto (wsec, "wpa"); - if (allow_rsn && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA2", TRUE)) - nm_setting_wireless_security_add_proto (wsec, "rsn"); - g_free (allow_wpa); - g_free (allow_rsn); + nm_clear_g_free (&value2); + v2 = svGetValueStr (ifcfg, "WPA_ALLOW_WPA2", &value2); + if (v2 && svParseBoolean (v2, TRUE)) + nm_setting_wireless_security_add_proto (wsec, "rsn"); } if (wpa_psk) { @@ -3402,12 +3431,15 @@ make_wpa_setting (shvarFile *ifcfg, /* Read PSK if it's system-owned */ if (psk_flags == NM_SETTING_SECRET_FLAG_NONE) { - psk = parse_wpa_psk (ifcfg, file, ssid, error); - if (psk) { + gs_free char *psk = NULL; + + psk = parse_wpa_psk (ifcfg, file, ssid, &local); + if (psk) g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL); - g_free (psk); - } else if (error) - goto error; + else if (local) { + g_propagate_error (error, local); + return NULL; + } } if (adhoc) @@ -3418,37 +3450,38 @@ make_wpa_setting (shvarFile *ifcfg, /* Adhoc mode is mutually exclusive with any 802.1x-based authentication */ if (adhoc) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", value); - goto error; + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", v); + return NULL; } - *s_8021x = fill_8021x (ifcfg, file, value, TRUE, error); + *s_8021x = fill_8021x (ifcfg, file, v, TRUE, error); if (!*s_8021x) - goto error; + return NULL; - lower = g_ascii_strdown (value, -1); - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); - g_free (lower); + { + gs_free char *lower = g_ascii_strdown (v, -1); + + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); + } } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wireless KEY_MGMT type '%s'", value); - goto error; + "Unknown wireless KEY_MGMT type '%s'", v); + return NULL; } - g_free (value); - - value = svGetValueStr_cp (ifcfg, "SECURITYMODE"); - if (NM_IN_STRSET (value, NULL, "open")) - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, value, NULL); + i_val = NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT; + if (!svGetValueEnum (ifcfg, "PMF", + nm_setting_wireless_security_pmf_get_type (), + &i_val, error)) + return NULL; + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PMF, i_val, NULL); - g_free (value); - return (NMSetting *) wsec; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SECURITYMODE", &value); + if (NM_IN_STRSET (v, NULL, "open")) + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, v, NULL); -error: - g_free (value); - if (wsec) - g_object_unref (wsec); - return NULL; + return (NMSetting *) g_steal_pointer (&wsec); } static NMSetting * @@ -3456,23 +3489,22 @@ make_leap_setting (shvarFile *ifcfg, const char *file, GError **error) { - NMSettingWirelessSecurity *wsec; + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; shvarFile *keys_ifcfg; - char *value; + gs_free char *value = NULL; NMSettingSecretFlags flags; wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); if (!value || strcmp (value, "IEEE8021X")) - goto error; /* Not LEAP */ + return NULL; + nm_clear_g_free (&value); - g_free (value); value = svGetValueStr_cp (ifcfg, "SECURITYMODE"); if (!value || strcasecmp (value, "leap")) - goto error; /* Not LEAP */ - - g_free (value); + return NULL; /* Not LEAP */ + nm_clear_g_free (&value); flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL); @@ -3490,30 +3522,24 @@ make_leap_setting (shvarFile *ifcfg, } if (value && strlen (value)) g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); if (!value) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing LEAP identity"); - goto error; + return NULL; } g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL); - g_free (value); + nm_clear_g_free (&value); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); - return (NMSetting *) wsec; - -error: - g_free (value); - if (wsec) - g_object_unref (wsec); - return NULL; + return (NMSetting *) g_steal_pointer (&wsec); } static NMSetting * @@ -3980,39 +4006,29 @@ parse_ethtool_option (const char *value, guint32 *out_speed, const char **out_duplex) { - gs_strfreev char **words = NULL; - const char **iter = NULL, *opt_val, *opt; + gs_free const char **words = NULL; + const char *const *iter; + const char *opt_val, *opt; - if (!value || !value[0]) + words = nm_utils_strsplit_set (value, "\t "); + if (!words) return; - words = g_strsplit_set (value, "\t ", 0); - iter = (const char **) words; + iter = words; while (iter[0]) { - /* g_strsplit_set() returns empty tokens when extra spaces are found: skip them */ - if (!*iter[0]) { - iter++; - continue; - } - opt = iter++[0]; - - /* skip over repeated space characters like to parse "wol d". */ - while (iter[0] && !*iter[0]) - iter++; - opt_val = iter[0]; - if (g_str_equal (opt, "autoneg")) + if (nm_streq (opt, "autoneg")) parse_ethtool_option_autoneg (opt_val, out_autoneg); - else if (g_str_equal (opt, "speed")) + else if (nm_streq (opt, "speed")) parse_ethtool_option_speed (opt_val, out_speed); - else if (g_str_equal (opt, "duplex")) + else if (nm_streq (opt, "duplex")) parse_ethtool_option_duplex (opt_val, out_duplex); - else if (g_str_equal (opt, "wol")) + else if (nm_streq (opt, "wol")) parse_ethtool_option_wol (opt_val, out_flags); - else if (g_str_equal (opt, "sopass")) + else if (nm_streq (opt, "sopass")) parse_ethtool_option_sopass (opt_val, out_password); else { /* Silently skip unknown options */ @@ -4034,15 +4050,15 @@ parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *va const char *duplex = NULL; if (value) { - gs_strfreev char **opts = NULL; - const char **iter; + gs_free const char **opts = NULL; + const char *const *iter; /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ if (!value[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; - opts = g_strsplit_set (value, ";", 0); - for (iter = (const char **) opts; iter[0]; iter++) { + opts = nm_utils_strsplit_set (value, ";"); + for (iter = opts; iter && iter[0]; iter++) { /* in case of repeated wol_passwords, parse_ethtool_option() * will do the right thing and clear wol_password before resetting. */ parse_ethtool_option (iter[0], &wol_flags, &wol_password, &autoneg, &speed, &duplex); @@ -4079,8 +4095,8 @@ make_wired_setting (shvarFile *ifcfg, NMSetting8021x **s_8021x, GError **error) { - NMSettingWired *s_wired; - char *value = NULL; + gs_unref_object NMSettingWired *s_wired = NULL; + gs_free char *value = NULL; char *nettype; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); @@ -4094,21 +4110,20 @@ make_wired_setting (shvarFile *ifcfg, g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); else PARSE_WARNING ("invalid MTU '%s'", value); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "HWADDR"); if (value) { value = g_strstrip (value); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); if (value) { const char *p = value; gboolean success = TRUE; - char **chans = NULL; /* basic sanity checks */ while (*p) { @@ -4121,30 +4136,30 @@ make_wired_setting (shvarFile *ifcfg, } if (success) { + gs_free const char **chans = NULL; guint32 num_chans; - chans = g_strsplit_set (value, ",", 0); - num_chans = g_strv_length (chans); + chans = nm_utils_strsplit_set (value, ","); + num_chans = NM_PTRARRAY_LEN (chans); if (num_chans < 2 || num_chans > 3) { - PARSE_WARNING ("invalid SUBCHANNELS '%s' (%d channels, 2 or 3 expected)", - value, g_strv_length (chans)); + PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", + value, (unsigned) NM_PTRARRAY_LEN (chans)); } else g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); - g_strfreev (chans); } - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "PORTNAME"); if (value) { nm_setting_wired_add_s390_option (s_wired, "portname", value); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "CTCPROT"); if (value) { nm_setting_wired_add_s390_option (s_wired, "ctcprot", value); - g_free (value); + nm_clear_g_free (&value); } nettype = svGetValueStr_cp (ifcfg, "NETTYPE"); @@ -4174,28 +4189,28 @@ make_wired_setting (shvarFile *ifcfg, iter++; } g_strfreev (options); - g_free (value); + nm_clear_g_free (&value); } - value = svGetValueStr_cp (ifcfg, "MACADDR"); - if (value) { - value = g_strstrip (value); - g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, value, NULL); - g_free (value); - } + g_object_set (s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + svGetValueStr (ifcfg, "MACADDR", &value), + NULL); + nm_clear_g_free (&value); - value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK"); - g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL); - g_free (value); + g_object_set (s_wired, + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, + svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value), + NULL); + nm_clear_g_free (&value); value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST"); if (value) { - char **strv; + gs_strfreev char **strv = NULL; strv = transform_hwaddr_blacklist (value); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); - g_strfreev (strv); - g_free (value); + nm_clear_g_free (&value); } value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); @@ -4203,25 +4218,20 @@ make_wired_setting (shvarFile *ifcfg, if (!strcmp (value, "IEEE8021X")) { *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error); if (!*s_8021x) - goto error; + return NULL; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown wired KEY_MGMT type '%s'", value); - goto error; + return NULL; } - g_free (value); + nm_clear_g_free (&value); } parse_ethtool_options (ifcfg, s_wired, svGetValue (ifcfg, "ETHTOOL_OPTS", &value)); - g_free (value); - - return (NMSetting *) s_wired; + nm_clear_g_free (&value); -error: - g_free (value); - g_object_unref (s_wired); - return NULL; + return (NMSetting *) g_steal_pointer (&s_wired); } static NMConnection * @@ -4246,7 +4256,6 @@ wired_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); @@ -4396,7 +4405,6 @@ infiniband_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); infiniband_setting = make_infiniband_setting (ifcfg, file, error); @@ -4441,40 +4449,37 @@ make_bond_setting (shvarFile *ifcfg, GError **error) { NMSettingBond *s_bond; - char *value; + gs_free char *value = NULL; + const char *v; - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (!value) { + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (!v) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "mandatory DEVICE keyword missing"); return NULL; } - g_free (value); s_bond = NM_SETTING_BOND (nm_setting_bond_new ()); - value = svGetValueStr_cp (ifcfg, "BONDING_OPTS"); - if (value) { - char **items, **iter; + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "BONDING_OPTS", &value); + if (v) { + gs_free const char **items = NULL; + const char *const *iter; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (v, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && key[0] && val[0]) - handle_bond_option (s_bond, key, val); - } + gs_strfreev char **keys = NULL; + const char *key, *val; - g_strfreev (keys); + keys = g_strsplit_set (*iter, "=", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && key[0] && val[0]) + handle_bond_option (s_bond, key, val); } } - g_free (value); - g_strfreev (items); } return (NMSetting *) s_bond; @@ -4673,6 +4678,12 @@ handle_bridge_option (NMSetting *setting, (gboolean) u, NULL); else PARSE_WARNING ("invalid multicast_snooping value '%s'", value); + } else if (!strcmp (key, "group_fwd_mask")) { + if (get_uint (value, &u) && u <= 0xFFFF && !NM_FLAGS_ANY (u, 7)) + g_object_set (setting, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, + (gboolean) u, NULL); + else + PARSE_WARNING ("invalid group_fwd_mask value '%s'", value); } else PARSE_WARNING ("unhandled bridge option '%s'", key); } @@ -4683,25 +4694,22 @@ handle_bridging_opts (NMSetting *setting, const char *value, BridgeOptFunc func) { - char **items, **iter; + gs_free const char **items = NULL; + const char *const *iter; - items = g_strsplit_set (value, " ", -1); + items = nm_utils_strsplit_set (value, " "); for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && strlen (key) && strlen (val)) - func (setting, stp, key, val); - } - - g_strfreev (keys); + gs_strfreev char **keys = NULL; + const char *key, *val; + + keys = g_strsplit_set (*iter, "=", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && key[0] && val[0]) + func (setting, stp, key, val); } } - g_strfreev (items); } static NMSetting * @@ -4923,24 +4931,22 @@ parse_prio_map_list (NMSettingVlan *s_vlan, const char *key, NMVlanPriorityMap map) { - char *value; - gchar **list = NULL, **iter; + gs_free char *value = NULL; + gs_free const char **list = NULL; + const char *const *iter; + const char *v; - value = svGetValueStr_cp (ifcfg, key); - if (!value) + v = svGetValueStr (ifcfg, key, &value); + if (!v) return; - - list = g_strsplit_set (value, ",", -1); - g_free (value); + list = nm_utils_strsplit_set (v, ","); for (iter = list; iter && *iter; iter++) { - if (!*iter || !strchr (*iter, ':')) + if (!strchr (*iter, ':')) continue; - if (!nm_setting_vlan_add_priority_str (s_vlan, map, *iter)) PARSE_WARNING ("invalid %s priority map item '%s'", key, *iter); } - g_strfreev (list); } static NMSetting * @@ -4951,22 +4957,20 @@ make_vlan_setting (shvarFile *ifcfg, gs_unref_object NMSettingVlan *s_vlan = NULL; gs_free char *parent = NULL; gs_free char *iface_name = NULL; - char *value = NULL; - const char *p = NULL; + gs_free char *value = NULL; + const char *v = NULL; int vlan_id = -1; guint32 vlan_flags = 0; gint gvrp, reorder_hdr; - value = svGetValueStr_cp (ifcfg, "VLAN_ID"); - if (value) { - vlan_id = _nm_utils_ascii_str_to_int64 (value, 10, 0, 4095, -1); + v = svGetValueStr (ifcfg, "VLAN_ID", &value); + if (v) { + vlan_id = _nm_utils_ascii_str_to_int64 (v, 10, 0, 4095, -1); if (vlan_id == -1) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid VLAN_ID '%s'", value); - g_free (value); + "Invalid VLAN_ID '%s'", v); return NULL; } - g_free (value); } /* Need DEVICE if we don't have a separate VLAN_ID property */ @@ -4983,11 +4987,11 @@ make_vlan_setting (shvarFile *ifcfg, parent = svGetValueStr_cp (ifcfg, "PHYSDEV"); if (iface_name) { - p = strchr (iface_name, '.'); - if (p) { + v = strchr (iface_name, '.'); + if (v) { /* eth0.43; PHYSDEV is assumed from it if unknown */ if (!parent) { - parent = g_strndup (iface_name, p - iface_name); + parent = g_strndup (iface_name, v - iface_name); if (g_str_has_prefix (parent, "vlan")) { /* Like initscripts, if no PHYSDEV and we get an obviously * invalid parent interface from DEVICE, fail. @@ -4995,20 +4999,20 @@ make_vlan_setting (shvarFile *ifcfg, nm_clear_g_free (&parent); } } - p++; + v++; } else { /* format like vlan43; PHYSDEV must be set */ if (g_str_has_prefix (iface_name, "vlan")) - p = iface_name + 4; + v = iface_name + 4; } - if (p) { + if (v) { int device_vlan_id; /* Grab VLAN ID from interface name; this takes precedence over the * separate VLAN_ID property for backwards compat. */ - device_vlan_id = _nm_utils_ascii_str_to_int64 (p, 10, 0, 4095, -1); + device_vlan_id = _nm_utils_ascii_str_to_int64 (v, 10, 0, 4095, -1); if (device_vlan_id != -1) vlan_id = device_vlan_id; } @@ -5034,13 +5038,13 @@ make_vlan_setting (shvarFile *ifcfg, if (gvrp > 0) vlan_flags |= NM_VLAN_FLAG_GVRP; - value = svGetValueStr_cp (ifcfg, "VLAN_FLAGS"); - if (value) { - gs_strfreev char **strv = NULL; - char **ptr; - - strv = g_strsplit_set (value, ", ", 0); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "VLAN_FLAGS", &value); + if (v) { + gs_free const char **strv = NULL; + const char *const *ptr; + strv = nm_utils_strsplit_set (v, ", "); for (ptr = strv; ptr && *ptr; ptr++) { if (nm_streq (*ptr, "GVRP") && gvrp == -1) vlan_flags |= NM_VLAN_FLAG_GVRP; @@ -5060,7 +5064,6 @@ make_vlan_setting (shvarFile *ifcfg, vlan_flags |= NM_VLAN_FLAG_MVRP; g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, vlan_flags, NULL); - g_free (value); parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP); parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP); @@ -5091,7 +5094,6 @@ vlan_connection_from_ifcfg (const char *file, g_object_unref (connection); return NULL; } - check_if_slave (ifcfg, (NMSettingConnection *) con_setting); nm_connection_add_setting (connection, con_setting); vlan_setting = make_vlan_setting (ifcfg, file, error); @@ -5120,7 +5122,8 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, { NMConnection *connection; NMSetting *s_con; - char *value; + gs_free char *value = NULL; + const char *v; nm_assert (out_spec && !*out_spec); @@ -5137,26 +5140,25 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, nm_connection_add_setting (connection, nm_setting_generic_new ()); /* Get a spec */ - value = svGetValueStr_cp (ifcfg, "HWADDR"); - if (value) { - char *lower = g_ascii_strdown (value, -1); + v = svGetValueStr (ifcfg, "HWADDR", &value); + if (v) { + gs_free char *lower = g_ascii_strdown (v, -1); + *out_spec = g_strdup_printf ("%s:mac:%s", type, lower); - g_free (lower); - g_free (value); return connection; } - value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); - if (value) { - *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, value); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "SUBCHANNELS", &value); + if (v) { + *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, v); return connection; } - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (value) { - *out_spec = g_strdup_printf ("%s:interface-name:%s", type, value); - g_free (value); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVICE", &value); + if (v) { + *out_spec = g_strdup_printf ("%s:interface-name:%s", type, v); return connection; } @@ -5164,32 +5166,6 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, return NULL; } -char * -uuid_from_file (const char *filename) -{ - const char *ifcfg_name = NULL; - shvarFile *ifcfg; - char *uuid; - - g_return_val_if_fail (filename != NULL, NULL); - - ifcfg_name = utils_get_ifcfg_name (filename, TRUE); - if (!ifcfg_name) - return NULL; - - ifcfg = svOpenFile (filename, NULL); - if (!ifcfg) - return NULL; - - /* Try for a UUID key before falling back to hashing the file name */ - uuid = svGetValueStr_cp (ifcfg, "UUID"); - if (!uuid) - uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); - - svCloseFile (ifcfg); - return uuid; -} - static void check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) { @@ -5199,23 +5175,24 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) /* If there is no IPv4 config or it doesn't contain DNS searches, * read DOMAIN and put the domains into IPv6. */ - if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) { + if ( !s_ip4 + || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) { /* DNS searches */ - char *value = svGetValueStr_cp (ifcfg, "DOMAIN"); + gs_free char *value = NULL; + const char *v; - if (value) { - char **searches = g_strsplit (value, " ", 0); + v = svGetValueStr (ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; + const char *const *item; + + searches = nm_utils_strsplit_set (v, " "); if (searches) { - char **item; for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) - PARSE_WARNING ("duplicate DNS domain '%s'", *item); - } + if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) + PARSE_WARNING ("duplicate DNS domain '%s'", *item); } - g_strfreev (searches); } - g_free (value); } } } @@ -5235,6 +5212,8 @@ connection_from_file_full (const char *filename, NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL, *s_user; const char *ifcfg_name = NULL; gboolean has_ip4_defroute = FALSE; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL); @@ -5446,13 +5425,32 @@ connection_from_file_full (const char *filename, if (!connection) return NULL; - s_ip6 = make_ip6_setting (parsed, network_file, error); + has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + if (has_complex_routes_v4 && !has_complex_routes_v6) + PARSE_WARNING ("'rule-' file is present; you will need to use a dispatcher script to apply these routes"); + else if (has_complex_routes_v6 && !has_complex_routes_v4) + PARSE_WARNING ("'rule6-' file is present; you will need to use a dispatcher script to apply these routes"); + else + PARSE_WARNING ("'rule-' and 'rule6-' files are present; you will need to use a dispatcher script to apply these routes"); + } + + s_ip6 = make_ip6_setting (parsed, + network_file, + !has_complex_routes_v4 && !has_complex_routes_v6, + error); if (!s_ip6) return NULL; else nm_connection_add_setting (connection, s_ip6); - s_ip4 = make_ip4_setting (parsed, network_file, &has_ip4_defroute, error); + s_ip4 = make_ip4_setting (parsed, + network_file, + !has_complex_routes_v4 && !has_complex_routes_v6, + &has_ip4_defroute, + error); if (!s_ip4) return NULL; else { @@ -5510,11 +5508,11 @@ connection_from_file (const char *filename, } NMConnection * -connection_from_file_test (const char *filename, - const char *network_file, - const char *test_type, - char **out_unhandled, - GError **error) +nmtst_connection_from_file (const char *filename, + const char *network_file, + const char *test_type, + char **out_unhandled, + GError **error) { return connection_from_file_full (filename, network_file, @@ -5528,7 +5526,6 @@ guint devtimeout_from_file (const char *filename) { shvarFile *ifcfg; - char *devtimeout_str; guint devtimeout; g_return_val_if_fail (filename != NULL, 0); @@ -5537,14 +5534,7 @@ devtimeout_from_file (const char *filename) if (!ifcfg) return 0; - devtimeout_str = svGetValueStr_cp (ifcfg, "DEVTIMEOUT"); - if (devtimeout_str) { - devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0); - g_free (devtimeout_str); - } else - devtimeout = 0; - + devtimeout = svGetValueInt64 (ifcfg, "DEVTIMEOUT", 10, 0, G_MAXUINT, 0); svCloseFile (ifcfg); - return devtimeout; } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h index 35464474..a8937ac8 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -28,15 +28,12 @@ NMConnection *connection_from_file (const char *filename, GError **error, gboolean *out_ignore_error); -char *uuid_from_file (const char *filename); - guint devtimeout_from_file (const char *filename); -/* for test-ifcfg-rh */ -NMConnection *connection_from_file_test (const char *filename, - const char *network_file, - const char *test_type, - char **out_unhandled, - GError **error); +NMConnection *nmtst_connection_from_file (const char *filename, + const char *network_file, + const char *test_type, + char **out_unhandled, + GError **error); #endif /* __READER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index e82ef60c..6434ad7d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -173,7 +173,7 @@ utils_get_extra_path (const char *parent, const char *tag) dirname = g_path_get_dirname (parent); if (!dirname) - return NULL; + g_return_val_if_reached (NULL); name = utils_get_ifcfg_name (parent, FALSE); if (name) { @@ -280,25 +280,22 @@ gone: } gboolean -utils_has_complex_routes (const char *filename) +utils_has_complex_routes (const char *filename, int addr_family) { - char *rules; + g_return_val_if_fail (filename, TRUE); - g_return_val_if_fail (filename != NULL, TRUE); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + gs_free char *rules = utils_get_extra_path (filename, RULE_TAG); - rules = utils_get_extra_path (filename, RULE_TAG); - if (g_file_test (rules, G_FILE_TEST_EXISTS)) { - g_free (rules); - return TRUE; + if (g_file_test (rules, G_FILE_TEST_EXISTS)) + return TRUE; } - g_free (rules); - rules = utils_get_extra_path (filename, RULE6_TAG); - if (g_file_test (rules, G_FILE_TEST_EXISTS)) { - g_free (rules); - return TRUE; + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + gs_free char *rules = utils_get_extra_path (filename, RULE6_TAG); + if (g_file_test (rules, G_FILE_TEST_EXISTS)) + return TRUE; } - g_free (rules); return FALSE; } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index 2b2c2755..e7abf4d8 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -48,7 +48,7 @@ shvarFile *utils_get_route_ifcfg (const char *parent, gboolean should_create); shvarFile *utils_get_route6_ifcfg (const char *parent, gboolean should_create); gboolean utils_has_route_file_new_syntax (const char *filename); -gboolean utils_has_complex_routes (const char *filename); +gboolean utils_has_complex_routes (const char *filename, int addr_family); gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg); @@ -57,4 +57,28 @@ char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg); void nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer); gboolean nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer); +static inline const char * +_nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which) +{ + gsize l; + + l = g_strlcpy (buf, tag_name, buf_len); + nm_assert (l < buf_len); + if (which != -1) { + buf_len -= l; + l = g_snprintf (&buf[l], buf_len, "%d", which); + nm_assert (l < buf_len); + } + return buf; +} +#define numbered_tag(buf, tag_name, which) \ + ({ \ + _nm_unused char *const _buf = (buf); \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \ + _nms_ifcfg_rh_utils_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \ + }) + #endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index d2b7ff67..d16f46be 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -32,6 +32,7 @@ #include <unistd.h> #include <stdio.h> +#include "nm-utils/nm-enum-utils.h" #include "nm-manager.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" @@ -48,7 +49,7 @@ #include "nm-utils.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" -#include "nm-setting-metadata.h" +#include "nm-meta-setting.h" #include "nms-ifcfg-rh-common.h" #include "nms-ifcfg-rh-reader.h" @@ -107,45 +108,75 @@ save_secret_flags (shvarFile *ifcfg, static void set_secret (shvarFile *ifcfg, + GHashTable *secrets, const char *key, const char *value, const char *flags_key, NMSettingSecretFlags flags) { - shvarFile *keyfile; - GError *error = NULL; - /* Clear the secret from the ifcfg and the associated "keys" file */ svUnsetValue (ifcfg, key); /* Save secret flags */ save_secret_flags (ifcfg, flags_key, flags); + /* Only write the secret if it's system owned and supposed to be saved */ + if (flags != NM_SETTING_SECRET_FLAG_NONE) + value = NULL; + + g_hash_table_replace (secrets, g_strdup (key), g_strdup (value)); +} + +static gboolean +write_secrets (shvarFile *ifcfg, + GHashTable *secrets, + GError **error) +{ + nm_auto_shvar_file_close shvarFile *keyfile = NULL; + gs_free const char **secrets_keys = NULL; + guint i, secrets_keys_n; + GError *local = NULL; + gboolean any_secrets = FALSE; + keyfile = utils_get_keys_ifcfg (svFileGetName (ifcfg), TRUE); if (!keyfile) { - _LOGW ("could not create ifcfg file for '%s'", svFileGetName (ifcfg)); - goto error; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Failure to create secrets file for '%s'", svFileGetName (ifcfg)); + return FALSE; } - /* Only write the secret if it's system owned and supposed to be saved */ - if (flags == NM_SETTING_SECRET_FLAG_NONE) - svSetValueStr (keyfile, key, value); - else - svUnsetValue (keyfile, key); + /* we purge all existing secrets. */ + svUnsetAll (keyfile, SV_KEY_TYPE_ANY); + + /* sort the keys. */ + secrets_keys = (const char **) g_hash_table_get_keys_as_array (secrets, &secrets_keys_n); + if (secrets_keys) { + g_qsort_with_data (secrets_keys, + secrets_keys_n, + sizeof (const char *), + nm_strcmp_p_with_data, + NULL); + } + + for (i = 0; i < secrets_keys_n; i++) { + const char *k = secrets_keys[i]; + const char *v = g_hash_table_lookup (secrets, k); + + if (v) { + svSetValueStr (keyfile, k, v); + any_secrets = TRUE; + } + } - if (!svWriteFile (keyfile, 0600, &error)) { - _LOGW ("could not update ifcfg file '%s': %s", - svFileGetName (keyfile), error->message); - g_clear_error (&error); - svCloseFile (keyfile); - goto error; + if (!any_secrets) + (void) unlink (svFileGetName (keyfile)); + else if (!svWriteFile (keyfile, 0600, &local)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Failure to write secrets to '%s': %s", svFileGetName (keyfile), local->message); + return FALSE; } - svCloseFile (keyfile); - return; -error: - /* Try setting the secret in the actual ifcfg */ - svSetValueStr (ifcfg, key, value); + return TRUE; } typedef struct { @@ -183,6 +214,8 @@ static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { static gboolean write_object (NMSetting8021x *s_8021x, shvarFile *ifcfg, + GHashTable *secrets, + GHashTable *blobs, const Setting8021xSchemeVtable *objtype, GError **error) { @@ -193,6 +226,7 @@ write_object (NMSetting8021x *s_8021x, NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; char *secret_name, *secret_flags; const char *extension; + char *standard_file; g_return_val_if_fail (ifcfg != NULL, FALSE); g_return_val_if_fail (objtype != NULL, FALSE); @@ -221,7 +255,7 @@ write_object (NMSetting8021x *s_8021x, secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); password = (*(objtype->vtable->passwd_func))(s_8021x); flags = (*(objtype->vtable->pwflag_func))(s_8021x); - set_secret (ifcfg, secret_name, password, secret_flags, flags); + set_secret (ifcfg, secrets, secret_name, password, secret_flags, flags); g_free (secret_name); g_free (secret_flags); @@ -232,28 +266,6 @@ write_object (NMSetting8021x *s_8021x, else extension = "pem"; - /* If certificate/private key wasn't sent, the connection may no longer be - * 802.1x and thus we clear out the paths and certs. - */ - if (!value && !blob) { - char *standard_file; - int ignored; - - /* Since no cert/private key is now being used, delete any standard file - * that was created for this connection, but leave other files alone. - * Thus, for example, - * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der - * will be deleted, but /etc/pki/tls/cert.pem will not. - */ - standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); - if (g_file_test (standard_file, G_FILE_TEST_EXISTS)) - ignored = unlink (standard_file); - g_free (standard_file); - - svUnsetValue (ifcfg, objtype->ifcfg_rh_key); - return TRUE; - } - /* If the object path was specified, prefer that over any raw cert data that * may have been sent. */ @@ -264,46 +276,72 @@ write_object (NMSetting8021x *s_8021x, /* If it's raw certificate data, write the data out to the standard file */ if (blob) { - gboolean success; char *new_file; - GError *write_error = NULL; new_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); - if (!new_file) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not create file path for %s / %s", - NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key); - return FALSE; + g_hash_table_replace (blobs, new_file, g_bytes_ref (blob)); + svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file); + return TRUE; + } + + /* If certificate/private key wasn't sent, the connection may no longer be + * 802.1x and thus we clear out the paths and certs. + * + * Since no cert/private key is now being used, delete any standard file + * that was created for this connection, but leave other files alone. + * Thus, for example, + * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der + * will be deleted, but /etc/pki/tls/cert.pem will not. + */ + standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); + g_hash_table_replace (blobs, standard_file, NULL); + svUnsetValue (ifcfg, objtype->ifcfg_rh_key); + return TRUE; +} + +static gboolean +write_blobs (GHashTable *blobs, GError **error) +{ + GHashTableIter iter; + const char *filename; + GBytes *blob; + + if (!blobs) + return TRUE; + + g_hash_table_iter_init (&iter, blobs); + while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &blob)) { + GError *write_error = NULL; + + if (!blob) { + (void) unlink (filename); + continue; } /* Write the raw certificate data out to the standard file so that we * can use paths from now on instead of pushing around the certificate * data itself. */ - success = nm_utils_file_set_contents (new_file, - (const char *) g_bytes_get_data (blob, NULL), - g_bytes_get_size (blob), - 0600, - &write_error); - if (success) { - svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file); - g_free (new_file); - return TRUE; - } else { + if (!nm_utils_file_set_contents (filename, + (const char *) g_bytes_get_data (blob, NULL), + g_bytes_get_size (blob), + 0600, + &write_error)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not write certificate/key for %s / %s: %s", - NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key, - (write_error && write_error->message) ? write_error->message : "(unknown)"); - g_clear_error (&write_error); + "Could not write certificate to file \"%s\": %s", + filename, + write_error->message); + return FALSE; } - g_free (new_file); } - return FALSE; + return TRUE; } static gboolean write_8021x_certs (NMSetting8021x *s_8021x, + GHashTable *secrets, + GHashTable *blobs, gboolean phase2, shvarFile *ifcfg, GError **error) @@ -311,7 +349,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, const Setting8021xSchemeVtable *otype = NULL; /* CA certificate */ - if (!write_object (s_8021x, ifcfg, + if (!write_object (s_8021x, ifcfg, secrets, blobs, phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], @@ -325,7 +363,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; /* Save the private key */ - if (!write_object (s_8021x, ifcfg, otype, error)) + if (!write_object (s_8021x, ifcfg, secrets, blobs, otype, error)) return FALSE; /* Client certificate */ @@ -338,7 +376,7 @@ write_8021x_certs (NMSetting8021x *s_8021x, NULL); } else { /* Save the client certificate */ - if (!write_object (s_8021x, ifcfg, + if (!write_object (s_8021x, ifcfg, secrets, blobs, phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], @@ -352,17 +390,22 @@ write_8021x_certs (NMSetting8021x *s_8021x, static gboolean write_8021x_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, + GHashTable *blobs, gboolean wired, GError **error) { NMSetting8021x *s_8021x; NMSetting8021xAuthFlags auth_flags; const char *value, *match; + gconstpointer ptr; + GBytes* bytes; char *tmp = NULL; GString *phase2_auth; GString *str; guint32 i, num; - gint timeout; + gsize size; + int vint; s_8021x = nm_connection_get_setting_802_1x (connection); if (!s_8021x) { @@ -392,11 +435,26 @@ write_8021x_setting (NMConnection *connection, nm_setting_802_1x_get_anonymous_identity (s_8021x)); set_secret (ifcfg, + secrets, "IEEE_8021X_PASSWORD", nm_setting_802_1x_get_password (s_8021x), "IEEE_8021X_PASSWORD_FLAGS", nm_setting_802_1x_get_password_flags (s_8021x)); + tmp = NULL; + bytes = nm_setting_802_1x_get_password_raw (s_8021x); + if (bytes) { + ptr = g_bytes_get_data (bytes, &size); + tmp = nm_utils_bin2hexstr (ptr, size, -1); + } + set_secret (ifcfg, + secrets, + "IEEE_8021X_PASSWORD_RAW", + tmp, + "IEEE_8021X_PASSWORD_RAW_FLAGS", + nm_setting_802_1x_get_password_raw_flags (s_8021x)); + g_free (tmp); + /* PEAP version */ value = nm_setting_802_1x_get_phase1_peapver (s_8021x); svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION"); @@ -452,11 +510,9 @@ write_8021x_setting (NMConnection *connection, if (auth_flags == NM_SETTING_802_1X_AUTH_FLAGS_NONE) { svUnsetValue (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS"); } else { - gs_free char *flags_str = NULL; - - flags_str = _nm_utils_enum_to_str_full (nm_setting_802_1x_auth_flags_get_type (), - auth_flags, " "); - svSetValueStr (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", flags_str); + svSetValueEnum (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type(), + auth_flags); } svSetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", @@ -501,17 +557,14 @@ write_8021x_setting (NMConnection *connection, svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x)); - timeout = nm_setting_802_1x_get_auth_timeout (s_8021x); - if (timeout > 0) - svSetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", timeout); - else - svUnsetValue (ifcfg, "IEEE_8021X_AUTH_TIMEOUT"); + vint = nm_setting_802_1x_get_auth_timeout (s_8021x); + svSetValueInt64_cond (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", vint > 0, vint); - if (!write_8021x_certs (s_8021x, FALSE, ifcfg, error)) + if (!write_8021x_certs (s_8021x, secrets, blobs, FALSE, ifcfg, error)) return FALSE; /* phase2/inner certs */ - if (!write_8021x_certs (s_8021x, TRUE, ifcfg, error)) + if (!write_8021x_certs (s_8021x, secrets, blobs, TRUE, ifcfg, error)) return FALSE; return TRUE; @@ -520,6 +573,7 @@ write_8021x_setting (NMConnection *connection, static gboolean write_wireless_security_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, gboolean adhoc, gboolean *no_8021x, GError **error) @@ -528,6 +582,7 @@ write_wireless_security_setting (NMConnection *connection, const char *key_mgmt, *auth_alg, *key, *proto, *cipher; const char *psk = NULL; gboolean wep = FALSE, wpa = FALSE, dynamic_wep = FALSE; + NMSettingWirelessSecurityWpsMethod wps_method; char *tmp; guint32 i, num; GString *str; @@ -573,6 +628,7 @@ write_wireless_security_setting (NMConnection *connection, svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY", nm_setting_wireless_security_get_leap_username (s_wsec)); set_secret (ifcfg, + secrets, "IEEE_8021X_PASSWORD", nm_setting_wireless_security_get_leap_password (s_wsec), "IEEE_8021X_PASSWORD_FLAGS", @@ -581,35 +637,42 @@ write_wireless_security_setting (NMConnection *connection, } } + /* WPS */ + wps_method = nm_setting_wireless_security_get_wps_method (s_wsec); + if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + svUnsetValue (ifcfg, "WPS_METHOD"); + else + svSetValueEnum (ifcfg, "WPS_METHOD", nm_setting_wireless_security_wps_method_get_type (), wps_method); + /* WEP keys */ /* Clear any default key */ - set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); /* Clear existing keys */ for (i = 0; i < 4; i++) { - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + char tag[64]; - tmp = g_strdup_printf ("KEY%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag (tag, "KEY", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } /* And write the new ones out */ if (wep) { /* Default WEP TX key index */ - tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1); - svSetValueStr (ifcfg, "DEFAULTKEY", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "DEFAULTKEY", nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) + 1); for (i = 0; i < 4; i++) { NMWepKeyType key_type; key = nm_setting_wireless_security_get_wep_key (s_wsec, i); if (key) { - char *ascii_key = NULL; + gs_free char *ascii_key = NULL; + char tag[64]; + gboolean key_valid = TRUE; /* Passphrase needs a different ifcfg key since with WEP, there * are some passphrases that are indistinguishable from WEP hex @@ -622,10 +685,11 @@ write_wireless_security_setting (NMConnection *connection, else if (nm_utils_wep_key_valid (key, NM_WEP_KEY_TYPE_PASSPHRASE)) key_type = NM_WEP_KEY_TYPE_PASSPHRASE; } + if (key_type == NM_WEP_KEY_TYPE_PASSPHRASE) - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); else if (key_type == NM_WEP_KEY_TYPE_KEY) { - tmp = g_strdup_printf ("KEY%d", i + 1); + numbered_tag (tag, "KEY", i + 1); /* Add 's:' prefix for ASCII keys */ if (strlen (key) == 5 || strlen (key) == 13) { @@ -633,19 +697,18 @@ write_wireless_security_setting (NMConnection *connection, key = ascii_key; } } else { - _LOGW ("invalid WEP key '%s'", key); - tmp = NULL; + g_warn_if_reached (); + key_valid = FALSE; } - if (tmp) { + if (key_valid) { set_secret (ifcfg, - tmp, + secrets, + tag, key, "WEP_KEY_FLAGS", nm_setting_wireless_security_get_wep_key_flags (s_wsec)); } - g_free (tmp); - g_free (ascii_key); } } } @@ -704,22 +767,31 @@ write_wireless_security_setting (NMConnection *connection, psk = nm_setting_wireless_security_get_psk (s_wsec); set_secret (ifcfg, + secrets, "WPA_PSK", psk, "WPA_PSK_FLAGS", wpa ? nm_setting_wireless_security_get_psk_flags (s_wsec) : NM_SETTING_SECRET_FLAG_NONE); + + if (nm_setting_wireless_security_get_pmf (s_wsec) == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) + svUnsetValue (ifcfg, "PMF"); + else { + svSetValueEnum (ifcfg, "PMF", nm_setting_wireless_security_pmf_get_type (), + nm_setting_wireless_security_get_pmf (s_wsec)); + } + return TRUE; } static gboolean write_wireless_setting (NMConnection *connection, shvarFile *ifcfg, + GHashTable *secrets, gboolean *no_8021x, GError **error) { NMSettingWireless *s_wireless; - char *tmp; GBytes *ssid; const guint8 *ssid_data; gsize ssid_len; @@ -755,13 +827,8 @@ write_wireless_setting (NMConnection *connection, g_free (blacklist_str); } - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_wireless_get_mtu (s_wireless); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); ssid = nm_setting_wireless_get_ssid (s_wireless); if (!ssid) { @@ -837,9 +904,7 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "BAND"); chan = nm_setting_wireless_get_channel (s_wireless); if (chan) { - tmp = g_strdup_printf ("%u", chan); - svSetValueStr (ifcfg, "CHANNEL", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "CHANNEL", chan); } else { /* Band only set if channel is not, since channel implies band */ svSetValueStr (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless)); @@ -856,27 +921,25 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "SECURITYMODE"); if (nm_connection_get_setting_wireless_security (connection)) { - if (!write_wireless_security_setting (connection, ifcfg, adhoc, no_8021x, error)) + if (!write_wireless_security_setting (connection, ifcfg, secrets, adhoc, no_8021x, error)) return FALSE; } else { - char *keys_path; - /* Clear out wifi security keys */ svUnsetValue (ifcfg, "KEY_MGMT"); svUnsetValue (ifcfg, "IEEE_8021X_IDENTITY"); - set_secret (ifcfg, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE); svUnsetValue (ifcfg, "SECURITYMODE"); /* Clear existing keys */ - set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + set_secret (ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); for (i = 0; i < 4; i++) { - tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + char tag[64]; - tmp = g_strdup_printf ("KEY%d", i + 1); - set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - g_free (tmp); + numbered_tag (tag, "KEY_PASSPHRASE", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag (tag, "KEY", i + 1); + set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } svUnsetValue (ifcfg, "DEFAULTKEY"); @@ -884,12 +947,7 @@ write_wireless_setting (NMConnection *connection, svUnsetValue (ifcfg, "WPA_ALLOW_WPA2"); svUnsetValue (ifcfg, "CIPHER_PAIRWISE"); svUnsetValue (ifcfg, "CIPHER_GROUP"); - set_secret (ifcfg, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE); - - /* Kill any old keys file */ - keys_path = utils_get_keys_path (svFileGetName (ifcfg)); - (void) unlink (keys_path); - g_free (keys_path); + set_secret (ifcfg, secrets, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE); } svSetValueStr (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL); @@ -932,7 +990,6 @@ static gboolean write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingInfiniband *s_infiniband; - char *tmp; const char *mac, *transport_mode, *parent; guint32 mtu; int p_key; @@ -947,13 +1004,8 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e mac = nm_setting_infiniband_get_mac_address (s_infiniband); svSetValueStr (ifcfg, "HWADDR", mac); - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_infiniband_get_mtu (s_infiniband); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband); svSetValueBoolean (ifcfg, "CONNECTED_MODE", nm_streq (transport_mode, "connected")); @@ -961,9 +1013,7 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e p_key = nm_setting_infiniband_get_p_key (s_infiniband); if (p_key != -1) { svSetValueStr (ifcfg, "PKEY", "yes"); - tmp = g_strdup_printf ("%u", p_key); - svSetValueStr (ifcfg, "PKEY_ID", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "PKEY_ID", p_key); parent = nm_setting_infiniband_get_parent (s_infiniband); if (parent) @@ -1016,13 +1066,8 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (blacklist_str); } - svUnsetValue (ifcfg, "MTU"); mtu = nm_setting_wired_get_mtu (s_wired); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); svUnsetValue (ifcfg, "SUBCHANNELS"); s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired); @@ -1176,7 +1221,6 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) s_wired = nm_connection_get_setting_wired (connection); if (s_wired) { const char *device_mac, *cloned_mac; - char *tmp; guint32 mtu; has_wired = TRUE; @@ -1191,12 +1235,7 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) nm_setting_wired_get_generate_mac_address_mask (s_wired)); mtu = nm_setting_wired_get_mtu (s_wired); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - svSetValueStr (ifcfg, "MTU", tmp); - g_free (tmp); - } else - svUnsetValue (ifcfg, "MTU"); + svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu); } return has_wired; } @@ -1205,19 +1244,11 @@ static gboolean write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingVlan *s_vlan; - NMSettingConnection *s_con; char *tmp; guint32 vlan_flags = 0; gsize s_buf_len; char s_buf[50], *s_buf_ptr; - s_con = nm_connection_get_setting_connection (connection); - if (!s_con) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing connection setting"); - return FALSE; - } - s_vlan = nm_connection_get_setting_vlan (connection); if (!s_vlan) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -1227,12 +1258,8 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, svSetValueStr (ifcfg, "VLAN", "yes"); svSetValueStr (ifcfg, "TYPE", TYPE_VLAN); - svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con)); svSetValueStr (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan)); - - tmp = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan)); - svSetValueStr (ifcfg, "VLAN_ID", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "VLAN_ID", nm_setting_vlan_get_id (s_vlan)); vlan_flags = nm_setting_vlan_get_flags (s_vlan); svSetValueBoolean (ifcfg, "REORDER_HDR", NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)); @@ -1267,10 +1294,9 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, } static gboolean -write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +write_bond_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingBond *s_bond; - const char *iface; guint32 i, num_opts; s_bond = nm_connection_get_setting_bond (connection); @@ -1280,14 +1306,6 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); svUnsetValue (ifcfg, "BONDING_OPTS"); num_opts = nm_setting_bond_get_num_options (s_bond); @@ -1324,7 +1342,6 @@ static gboolean write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingTeam *s_team; - const char *iface; const char *config; s_team = nm_connection_get_setting_team (connection); @@ -1334,14 +1351,6 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); config = nm_setting_team_get_config (s_team); svSetValueStr (ifcfg, "TEAM_CONFIG", config); @@ -1385,15 +1394,13 @@ get_setting_default_boolean (NMSetting *setting, const char *prop) } static gboolean -write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) { NMSettingBridge *s_bridge; - const char *iface; guint32 i; gboolean b; GString *opts; const char *mac; - char *s; s_bridge = nm_connection_get_setting_bridge (connection); if (!s_bridge) { @@ -1402,14 +1409,6 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error return FALSE; } - iface = nm_connection_get_interface_name (connection); - if (!iface) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Missing interface name"); - return FALSE; - } - - svSetValueStr (ifcfg, "DEVICE", iface); svUnsetValue (ifcfg, "BRIDGING_OPTS"); svSetValueBoolean (ifcfg, "STP", FALSE); svUnsetValue (ifcfg, "DELAY"); @@ -1424,11 +1423,8 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error svSetValueStr (ifcfg, "STP", "yes"); i = nm_setting_bridge_get_forward_delay (s_bridge); - if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) { - s = g_strdup_printf ("%u", i); - svSetValueStr (ifcfg, "DELAY", s); - g_free (s); - } + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) + svSetValueInt64 (ifcfg, "DELAY", i); g_string_append_printf (opts, "priority=%u", nm_setting_bridge_get_priority (s_bridge)); @@ -1454,6 +1450,13 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error g_string_append_printf (opts, "ageing_time=%u", i); } + i = nm_setting_bridge_get_group_forward_mask (s_bridge); + if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_GROUP_FORWARD_MASK)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "group_fwd_mask=%u", i); + } + b = nm_setting_bridge_get_multicast_snooping (s_bridge); if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MULTICAST_SNOOPING)) { if (opts->len) @@ -1467,6 +1470,8 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE); + *wired = write_wired_for_virtual (connection, ifcfg); + return TRUE; } @@ -1715,8 +1720,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) guint32 n, i; GString *str; const char *master, *master_iface = NULL, *type; - char *tmp; - gint i_int; + gint vint; + guint32 vuint32; + const char *tmp; svSetValueStr (ifcfg, "NAME", nm_setting_connection_get_id (s_con)); svSetValueStr (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con)); @@ -1724,16 +1730,15 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con)); svSetValueBoolean (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con)); - i_int = nm_setting_connection_get_autoconnect_priority (s_con); - tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT - ? g_strdup_printf ("%d", i_int) : NULL; - svSetValueStr (ifcfg, "AUTOCONNECT_PRIORITY", tmp); - g_free (tmp); + vint = nm_setting_connection_get_autoconnect_priority (s_con); + svSetValueInt64_cond (ifcfg, "AUTOCONNECT_PRIORITY", + vint != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT, + vint); - i_int = nm_setting_connection_get_autoconnect_retries (s_con); - tmp = i_int != -1 ? g_strdup_printf ("%d", i_int) : NULL; - svSetValueStr (ifcfg, "AUTOCONNECT_RETRIES", tmp); - g_free (tmp); + vint = nm_setting_connection_get_autoconnect_retries (s_con); + svSetValueInt64_cond (ifcfg, "AUTOCONNECT_RETRIES", + vint != -1, + vint); /* Only save the value for master connections */ type = nm_setting_connection_get_connection_type (s_con); @@ -1849,12 +1854,10 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) g_string_free (str, TRUE); } - svUnsetValue (ifcfg, "GATEWAY_PING_TIMEOUT"); - if (nm_setting_connection_get_gateway_ping_timeout (s_con)) { - tmp = g_strdup_printf ("%" G_GUINT32_FORMAT, nm_setting_connection_get_gateway_ping_timeout (s_con)); - svSetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", tmp); - g_free (tmp); - } + vuint32 = nm_setting_connection_get_gateway_ping_timeout (s_con); + svSetValueInt64_cond (ifcfg, "GATEWAY_PING_TIMEOUT", + vuint32 != 0, + vuint32); switch (nm_setting_connection_get_metered (s_con)) { case NM_METERED_YES: @@ -1866,23 +1869,26 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) default: svUnsetValue (ifcfg, "CONNECTION_METERED"); } + + vint = nm_setting_connection_get_auth_retries (s_con); + svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); } static char * get_route_attributes_string (NMIPRoute *route, int family) { - gs_strfreev char **names = NULL; + gs_free const char **names = NULL; GVariant *attr, *lock; GString *str; - int i; + guint i, len; - names = nm_ip_route_get_attribute_names (route); - if (!names || !names[0]) + names = _nm_ip_route_get_attribute_names (route, TRUE, &len); + if (!len) return NULL; str = g_string_new (""); - for (i = 0; names[i]; i++) { + for (i = 0; i < len; i++) { attr = nm_ip_route_get_attribute (route, names[i]); if (!nm_ip_route_attribute_validate (names[i], attr, family, NULL, NULL)) @@ -1904,16 +1910,28 @@ get_route_attributes_string (NMIPRoute *route, int family) (lock && g_variant_get_boolean (lock)) ? "lock " : "", g_variant_get_uint32 (attr)); } else if (strstr (names[i], "lock-")) { - /* handled above */ + const char *n = &(names[i])[NM_STRLEN ("lock-")]; + + attr = nm_ip_route_get_attribute (route, n); + if (!attr) { + g_string_append_printf (str, + "%s lock 0", + n); + } else { + /* we also have a corresponding attribute with the numeric value. The + * lock setting is handled above. */ + } } else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { - g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr)); + g_string_append_printf (str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte (attr)); + } else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) { + g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_uint32 (attr)); } else if ( nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) { char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from"; g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL)); } else { - _LOGW ("unknown route option '%s'", names[i]); + g_warn_if_reached (); continue; } if (names[i + 1]) @@ -1923,35 +1941,86 @@ get_route_attributes_string (NMIPRoute *route, int family) return g_string_free (str, FALSE); } -static gboolean -write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error) +static shvarFile * +write_route_file_svformat (const char *filename, NMSettingIPConfig *s_ip4) { - nm_auto_free_gstring GString *contents = NULL; - NMIPRoute *route; - guint32 i, num; + shvarFile *routefile; + guint i, num; - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip4 != NULL, FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); + routefile = utils_get_route_ifcfg (filename, TRUE); + + svUnsetAll (routefile, SV_KEY_TYPE_ROUTE_SVFORMAT); num = nm_setting_ip_config_get_num_routes (s_ip4); - if (num == 0) { - unlink (filename); - return TRUE; + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN]; + NMIPRoute *route; + guint32 netmask; + gint64 metric; + char addr_key[64]; + char gw_key[64]; + char netmask_key[64]; + char metric_key[64]; + char options_key[64]; + gs_free char *options = NULL; + + numbered_tag (addr_key, "ADDRESS", i); + numbered_tag (netmask_key, "NETMASK", i); + numbered_tag (gw_key, "GATEWAY", i); + + route = nm_setting_ip_config_get_route (s_ip4, i); + + svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route)); + + netmask = _nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route)); + svSetValueStr (routefile, netmask_key, + nm_utils_inet4_ntop (netmask, buf)); + + svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route)); + + metric = nm_ip_route_get_metric (route); + if (metric != -1) { + svSetValueInt64 (routefile, + numbered_tag (metric_key, "METRIC", i), + metric); + } + + options = get_route_attributes_string (route, AF_INET); + if (options) { + svSetValueStr (routefile, + numbered_tag (options_key, "OPTIONS", i), + options); + } } + return routefile; +} + +static GString * +write_route_file (NMSettingIPConfig *s_ip) +{ + GString *contents; + NMIPRoute *route; + guint32 i, num; + int addr_family; + + addr_family = nm_setting_ip_config_get_addr_family (s_ip); + + num = nm_setting_ip_config_get_num_routes (s_ip); + if (num == 0) + return NULL; + contents = g_string_new (""); for (i = 0; i < num; i++) { - const char *next_hop; gs_free char *options = NULL; + const char *next_hop; gint64 metric; - route = nm_setting_ip_config_get_route (s_ip4, i); + route = nm_setting_ip_config_get_route (s_ip, i); next_hop = nm_ip_route_get_next_hop (route); metric = nm_ip_route_get_metric (route); - options = get_route_attributes_string (route, AF_INET); + options = get_route_attributes_string (route, addr_family); g_string_append_printf (contents, "%s/%u", nm_ip_route_get_dest (route), @@ -1968,13 +2037,7 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError g_string_append_c (contents, '\n'); } - if (!g_file_set_contents (filename, contents->str, contents->len, NULL)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Writing route file '%s' failed", filename); - return FALSE; - } - - return TRUE; + return contents; } static gboolean @@ -2025,7 +2088,7 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER)); - svUnsetValuesWithPrefix (ifcfg, "NM_USER_"); + svUnsetAll (ifcfg, SV_KEY_TYPE_USER); if (!s_user) return TRUE; @@ -2050,25 +2113,29 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) } static gboolean -write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_ip4_setting (NMConnection *connection, + shvarFile *ifcfg, + shvarFile **out_route_content_svformat, + GString **out_route_content, + GError **error) { NMSettingIPConfig *s_ip4; const char *value; char *tmp; - char addr_key[64]; - char prefix_key[64]; - char netmask_key[64]; - char gw_key[64]; - char *route_path = NULL; + char tag[64]; gint j; guint i, num, n; gint64 route_metric; + NMIPRouteTableSyncMode route_table; gint priority; int timeout; GString *searches; const char *method = NULL; gboolean has_netmask; + NM_SET_OUT (out_route_content_svformat, NULL); + NM_SET_OUT (out_route_content, NULL); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (!s_ip4) { /* slave-type: clear IPv4 settings. @@ -2076,16 +2143,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) * Some IPv4 setting related options are not cleared, * for no strong reason. */ svUnsetValue (ifcfg, "BOOTPROTO"); - - svUnsetValue (ifcfg, "IPADDR"); - svUnsetValue (ifcfg, "PREFIX"); - svUnsetValue (ifcfg, "NETMASK"); - svUnsetValue (ifcfg, "GATEWAY"); - - svUnsetValue (ifcfg, "IPADDR0"); - svUnsetValue (ifcfg, "PREFIX0"); - svUnsetValue (ifcfg, "NETMASK0"); - svUnsetValue (ifcfg, "GATEWAY0"); + svUnsetAll (ifcfg, SV_KEY_TYPE_IP4_ADDRESS); return TRUE; } @@ -2096,32 +2154,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { - int result; - /* IPv4 disabled, clear IPv4 related parameters */ svUnsetValue (ifcfg, "BOOTPROTO"); for (j = -1; j < 256; j++) { - if (j == -1) { - nm_sprintf_buf (addr_key, "IPADDR"); - nm_sprintf_buf (prefix_key, "PREFIX"); - nm_sprintf_buf (netmask_key, "NETMASK"); - nm_sprintf_buf (gw_key, "GATEWAY"); - } else { - nm_sprintf_buf (addr_key, "IPADDR%d", (guint) j); - nm_sprintf_buf (prefix_key, "PREFIX%u", (guint) j); - nm_sprintf_buf (netmask_key, "NETMASK%u", (guint) j); - nm_sprintf_buf (gw_key, "GATEWAY%u", (guint) j); - } - - svUnsetValue (ifcfg, addr_key); - svUnsetValue (ifcfg, prefix_key); - svUnsetValue (ifcfg, netmask_key); - svUnsetValue (ifcfg, gw_key); + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j)); } - - route_path = utils_get_route_path (svFileGetName (ifcfg)); - result = unlink (route_path); - g_free (route_path); return TRUE; } @@ -2165,72 +2205,60 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) * See https://bugzilla.redhat.com/show_bug.cgi?id=771673 * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770 */ - nm_sprintf_buf (addr_key, "IPADDR"); - nm_sprintf_buf (prefix_key, "PREFIX"); - nm_sprintf_buf (netmask_key, "NETMASK"); - nm_sprintf_buf (gw_key, "GATEWAY"); - } else { - nm_sprintf_buf (addr_key, "IPADDR%u", n); - nm_sprintf_buf (prefix_key, "PREFIX%u", n); - nm_sprintf_buf (netmask_key, "NETMASK%u", n); - nm_sprintf_buf (gw_key, "GATEWAY%u", n); - } + j = -1; + } else + j = n; - svSetValueStr (ifcfg, addr_key, nm_ip_address_get_address (addr)); + svSetValueStr (ifcfg, + numbered_tag (tag, "IPADDR", j), + nm_ip_address_get_address (addr)); prefix = nm_ip_address_get_prefix (addr); - tmp = g_strdup_printf ("%u", prefix); - svSetValueStr (ifcfg, prefix_key, tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, numbered_tag (tag, "PREFIX", j), prefix); /* If the legacy "NETMASK" is present, keep it. */ + numbered_tag (tag, "NETMASK", j); if (has_netmask) { char buf[INET_ADDRSTRLEN]; - svSetValueStr (ifcfg, netmask_key, - nm_utils_inet4_ntop (nm_utils_ip4_prefix_to_netmask (prefix), buf)); + svSetValueStr (ifcfg, tag, + nm_utils_inet4_ntop (_nm_utils_ip4_prefix_to_netmask (prefix), buf)); } else - svUnsetValue (ifcfg, netmask_key); + svUnsetValue (ifcfg, tag); - svUnsetValue (ifcfg, gw_key); n++; } - svUnsetValue (ifcfg, "IPADDR0"); - svUnsetValue (ifcfg, "PREFIX0"); - svUnsetValue (ifcfg, "NETMASK0"); - svUnsetValue (ifcfg, "GATEWAY0"); + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", 0)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", 0)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", 0)); if (n == 0) { svUnsetValue (ifcfg, "IPADDR"); svUnsetValue (ifcfg, "PREFIX"); svUnsetValue (ifcfg, "NETMASK"); - i = 1; - } else - i = n; - for (; i < 256; i++) { - nm_sprintf_buf (addr_key, "IPADDR%u", i); - nm_sprintf_buf (prefix_key, "PREFIX%u", i); - nm_sprintf_buf (netmask_key, "NETMASK%u", i); - nm_sprintf_buf (gw_key, "GATEWAY%u", i); - - svUnsetValue (ifcfg, addr_key); - svUnsetValue (ifcfg, prefix_key); - svUnsetValue (ifcfg, netmask_key); - svUnsetValue (ifcfg, gw_key); + } + for (j = n; j < 256; j++) { + svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j)); + svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j)); } + for (j = -1; j < 256; j++) { + if (j != 0) + svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j)); + } svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4)); num = nm_setting_ip_config_get_num_dns (s_ip4); for (i = 0; i < 254; i++) { const char *dns; - nm_sprintf_buf (addr_key, "DNS%u", i + 1); + numbered_tag (tag, "DNS", i + 1); if (i >= num) - svUnsetValue (ifcfg, addr_key); + svUnsetValue (ifcfg, tag); else { dns = nm_setting_ip_config_get_dns (s_ip4, i); - svSetValueStr (ifcfg, addr_key, dns); + svSetValueStr (ifcfg, tag, dns); } } @@ -2273,98 +2301,27 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svSetValueStr (ifcfg, "DHCP_CLIENT_ID", value); timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip4); - tmp = timeout ? g_strdup_printf ("%d", timeout) : NULL; - svSetValueStr (ifcfg, "IPV4_DHCP_TIMEOUT", tmp); - g_free (tmp); + svSetValueInt64_cond (ifcfg, + "IPV4_DHCP_TIMEOUT", + timeout != 0, + timeout); svSetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail (s_ip4)); route_metric = nm_setting_ip_config_get_route_metric (s_ip4); - tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL; - svSetValueStr (ifcfg, "IPV4_ROUTE_METRIC", tmp); - g_free (tmp); - - /* Static routes - route-<name> file */ - route_path = utils_get_route_path (svFileGetName (ifcfg)); - if (!route_path) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not get route file path for '%s'", svFileGetName (ifcfg)); - return FALSE; - } - - if (utils_has_route_file_new_syntax (route_path)) { - shvarFile *routefile; - - routefile = utils_get_route_ifcfg (svFileGetName (ifcfg), TRUE); - if (!routefile) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not create route file '%s'", route_path); - g_free (route_path); - return FALSE; - } - g_free (route_path); + svSetValueInt64_cond (ifcfg, + "IPV4_ROUTE_METRIC", + route_metric != -1, + route_metric); - num = nm_setting_ip_config_get_num_routes (s_ip4); - for (i = 0; i < 256; i++) { - char buf[INET_ADDRSTRLEN]; - NMIPRoute *route; - guint32 netmask; - gint64 metric; - char metric_key[64]; - char options_key[64]; - - nm_sprintf_buf (addr_key, "ADDRESS%u", i); - nm_sprintf_buf (netmask_key, "NETMASK%u", i); - nm_sprintf_buf (gw_key, "GATEWAY%u", i); - nm_sprintf_buf (metric_key, "METRIC%u", i); - nm_sprintf_buf (options_key, "OPTIONS%u", i); - - if (i >= num) { - svUnsetValue (routefile, addr_key); - svUnsetValue (routefile, netmask_key); - svUnsetValue (routefile, gw_key); - svUnsetValue (routefile, metric_key); - svUnsetValue (routefile, options_key); - } else { - gs_free char *options = NULL; - - route = nm_setting_ip_config_get_route (s_ip4, i); + route_table = nm_setting_ip_config_get_route_table (s_ip4); + svSetValueInt64_cond (ifcfg, + "IPV4_ROUTE_TABLE", + route_table != 0, + route_table); - svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route)); - - memset (buf, 0, sizeof (buf)); - netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route)); - inet_ntop (AF_INET, (const void *) &netmask, &buf[0], sizeof (buf)); - svSetValueStr (routefile, netmask_key, &buf[0]); - - svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route)); - - memset (buf, 0, sizeof (buf)); - metric = nm_ip_route_get_metric (route); - if (metric == -1) - svUnsetValue (routefile, metric_key); - else { - tmp = g_strdup_printf ("%u", (guint32) metric); - svSetValueStr (routefile, metric_key, tmp); - g_free (tmp); - } - - options = get_route_attributes_string (route, AF_INET); - if (options) - svSetValueStr (routefile, options_key, options); - } - } - if (!svWriteFile (routefile, 0644, error)) { - svCloseFile (routefile); - return FALSE; - } - svCloseFile (routefile); - } else { - write_route_file_legacy (route_path, s_ip4, error); - g_free (route_path); - if (error && *error) - return FALSE; - } + NM_SET_OUT (out_route_content_svformat, write_route_file_svformat (svFileGetName (ifcfg), s_ip4)); + NM_SET_OUT (out_route_content, write_route_file (s_ip4)); timeout = nm_setting_ip_config_get_dad_timeout (s_ip4); if (timeout < 0) @@ -2386,7 +2343,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) } static void -write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) +write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path) { NMSettingIPConfig *s_ip4; gs_free char *base_ifcfg_dir = NULL, *base_ifcfg_name = NULL; @@ -2432,7 +2389,7 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) for (i = 0; i < num; i++) { GVariant *label_var; const char *label, *p; - char *path, *tmp; + char *path; NMIPAddress *addr; shvarFile *ifcfg; @@ -2462,68 +2419,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) addr = nm_setting_ip_config_get_address (s_ip4, i); svSetValueStr (ifcfg, "IPADDR", nm_ip_address_get_address (addr)); - tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr)); - svSetValueStr (ifcfg, "PREFIX", tmp); - g_free (tmp); + svSetValueInt64 (ifcfg, "PREFIX", nm_ip_address_get_prefix(addr)); svWriteFile (ifcfg, 0644, NULL); svCloseFile (ifcfg); } } -static gboolean -write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) -{ - nm_auto_free_gstring GString *contents = NULL; - NMIPRoute *route; - guint32 i, num; - - g_return_val_if_fail (filename, FALSE); - g_return_val_if_fail (s_ip6, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - num = nm_setting_ip_config_get_num_routes (s_ip6); - if (num == 0) { - unlink (filename); - return TRUE; - } - - contents = g_string_new (""); - - for (i = 0; i < num; i++) { - gs_free char *options = NULL; - const char *next_hop; - gint64 metric; - - route = nm_setting_ip_config_get_route (s_ip6, i); - next_hop = nm_ip_route_get_next_hop (route); - metric = nm_ip_route_get_metric (route); - options = get_route_attributes_string (route, AF_INET6); - - g_string_append_printf (contents, "%s/%u", - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route)); - if (next_hop) - g_string_append_printf (contents, " via %s", next_hop); - if (metric >= 0) - g_string_append_printf (contents, " metric %u", (guint) metric); - if (options) { - g_string_append_c (contents, ' '); - g_string_append (contents, options); - } - - g_string_append_c (contents, '\n'); - } - - if (!g_file_set_contents (filename, contents->str, contents->len, NULL)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Writing route6 file '%s' failed", filename); - return FALSE; - } - - return TRUE; -} - static void write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg) { @@ -2542,21 +2444,25 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg) } static gboolean -write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +write_ip6_setting (NMConnection *connection, + shvarFile *ifcfg, + GString **out_route6_content, + GError **error) { NMSettingIPConfig *s_ip6; NMSettingIPConfig *s_ip4; const char *value; - char *tmp; guint i, num, num4; gint priority; NMIPAddress *addr; const char *dns; gint64 route_metric; + NMIPRouteTableSyncMode route_table; GString *ip_str1, *ip_str2, *ip_ptr; - char *route6_path; NMSettingIP6ConfigAddrGenMode addr_gen_mode; + NM_SET_OUT (out_route6_content, NULL); + s_ip6 = nm_connection_get_setting_ip6_config (connection); if (!s_ip6) { /* slave-type: clear IPv6 settings @@ -2636,15 +2542,15 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */ num = nm_setting_ip_config_get_num_dns (s_ip6); for (i = 0; i < 254; i++) { - char addr_key[64]; + char tag[64]; - nm_sprintf_buf (addr_key, "DNS%u", i + num4 + 1); + numbered_tag (tag, "DNS", i + num4 + 1); if (i >= num) - svUnsetValue (ifcfg, addr_key); + svUnsetValue (ifcfg, tag); else { dns = nm_setting_ip_config_get_dns (s_ip6, i); - svSetValueStr (ifcfg, addr_key, dns); + svSetValueStr (ifcfg, tag, dns); } } @@ -2681,9 +2587,16 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes"); route_metric = nm_setting_ip_config_get_route_metric (s_ip6); - tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL; - svSetValueStr (ifcfg, "IPV6_ROUTE_METRIC", tmp); - g_free (tmp); + svSetValueInt64_cond (ifcfg, + "IPV6_ROUTE_METRIC", + route_metric != -1, + route_metric); + + route_table = nm_setting_ip_config_get_route_table (s_ip6); + svSetValueInt64_cond (ifcfg, + "IPV6_ROUTE_TABLE", + route_table != 0, + route_table); /* IPv6 Privacy Extensions */ svUnsetValue (ifcfg, "IPV6_PRIVACY"); @@ -2706,10 +2619,8 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) /* IPv6 Address generation mode */ addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6)); if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { - tmp = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (), - addr_gen_mode); - svSetValueStr (ifcfg, "IPV6_ADDR_GEN_MODE", tmp); - g_free (tmp); + svSetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE", nm_setting_ip6_config_addr_gen_mode_get_type (), + addr_gen_mode); } else { svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE"); } @@ -2724,17 +2635,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) else svUnsetValue (ifcfg, "IPV6_DNS_PRIORITY"); - /* Static routes go to route6-<dev> file */ - route6_path = utils_get_route6_path (svFileGetName (ifcfg)); - if (!route6_path) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not get route6 file path for '%s'", svFileGetName (ifcfg)); - return FALSE; - } - write_route6_file (route6_path, s_ip6, error); - g_free (route6_path); - if (error && *error) - return FALSE; + NM_SET_OUT (out_route6_content, write_route_file (s_ip6)); return TRUE; } @@ -2817,30 +2718,43 @@ escape_id (const char *id) } static gboolean -write_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +do_write_construct (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + shvarFile **out_ifcfg, + GHashTable **out_blobs, + GHashTable **out_secrets, + gboolean *out_route_ignore, + shvarFile **out_route_content_svformat, + GString **out_route_content, + GString **out_route6_content, + GError **error) { NMSettingConnection *s_con; nm_auto_shvar_file_close shvarFile *ifcfg = NULL; gs_free char *ifcfg_name = NULL; + gs_free char *route_path = NULL; + gs_free char *route6_path = NULL; const char *type; - gboolean no_8021x = FALSE; - gboolean wired = FALSE; + gs_unref_hashtable GHashTable *blobs = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gboolean wired; + gboolean no_8021x; + gboolean route_path_is_svformat; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; + gboolean route_ignore; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + nm_auto_free_gstring GString *route_content = NULL; + nm_auto_free_gstring GString *route6_content = NULL; nm_assert (NM_IS_CONNECTION (connection)); nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); - nm_assert (!out_reread || !*out_reread); - if (!writer_can_write_connection (connection, error)) + if (!nms_ifcfg_rh_writer_can_write_connection (connection, error)) return FALSE; s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); if (filename) { /* For existing connections, 'filename' should be full path to ifcfg file */ @@ -2849,7 +2763,7 @@ write_connection (NMConnection *connection, return FALSE; ifcfg_name = g_strdup (filename); - } else { + } else if (ifcfg_dir) { char *escaped; escaped = escape_id (nm_setting_connection_get_id (s_con)); @@ -2880,6 +2794,21 @@ write_connection (NMConnection *connection, } ifcfg = svCreateFile (ifcfg_name); + } else + ifcfg = svCreateFile ("/tmp/ifcfg-dummy"); + + route_path = utils_get_route_path (svFileGetName (ifcfg)); + if (!route_path) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not get route file path for '%s'", svFileGetName (ifcfg)); + return FALSE; + } + + route6_path = utils_get_route6_path (svFileGetName (ifcfg)); + if (!route6_path) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not get route6 file path for '%s'", svFileGetName (ifcfg)); + return FALSE; } type = nm_setting_connection_get_connection_type (s_con); @@ -2889,6 +2818,10 @@ write_connection (NMConnection *connection, return FALSE; } + secrets = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); + + wired = FALSE; + no_8021x = FALSE; if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { // FIXME: can't write PPPoE at this time if (nm_connection_get_setting_pppoe (connection)) { @@ -2905,19 +2838,19 @@ write_connection (NMConnection *connection, if (!write_vlan_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { - if (!write_wireless_setting (connection, ifcfg, &no_8021x, error)) + if (!write_wireless_setting (connection, ifcfg, secrets, &no_8021x, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) { if (!write_infiniband_setting (connection, ifcfg, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) { - if (!write_bonding_setting (connection, ifcfg, &wired, error)) + if (!write_bond_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) { if (!write_team_setting (connection, ifcfg, &wired, error)) return FALSE; } else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) { - if (!write_bridge_setting (connection, ifcfg, error)) + if (!write_bridge_setting (connection, ifcfg, &wired, error)) return FALSE; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -2926,7 +2859,8 @@ write_connection (NMConnection *connection, } if (!no_8021x) { - if (!write_8021x_setting (connection, ifcfg, wired, error)) + blobs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref); + if (!write_8021x_setting (connection, ifcfg, secrets, blobs, wired, error)) return FALSE; } @@ -2948,11 +2882,49 @@ write_connection (NMConnection *connection, svUnsetValue (ifcfg, "DHCP_HOSTNAME"); svUnsetValue (ifcfg, "DHCP_FQDN"); - if (!write_ip4_setting (connection, ifcfg, error)) + route_path_is_svformat = utils_has_route_file_new_syntax (route_path); + + has_complex_routes_v4 = utils_has_complex_routes (ifcfg_name, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes (ifcfg_name, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + NMSettingIPConfig *s_ip4, *s_ip6; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if ( ( s_ip4 + && nm_setting_ip_config_get_num_routes (s_ip4) > 0) + || ( s_ip6 + && nm_setting_ip_config_get_num_routes (s_ip6) > 0)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Cannot configure static routes on a connection that has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + if ( ( s_ip4 + && nm_setting_ip_config_get_route_table (s_ip4) != 0) + || ( s_ip6 + && nm_setting_ip_config_get_route_table (s_ip6) != 0)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Cannot configure a route table for policy routing on a connection that has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + route_ignore = TRUE; + } else + route_ignore = FALSE; + + if (!write_ip4_setting (connection, + ifcfg, + !route_ignore && route_path_is_svformat ? &route_content_svformat : NULL, + !route_ignore && route_path_is_svformat ? NULL :&route_content, + error)) return FALSE; - write_ip4_aliases (connection, ifcfg_name); - if (!write_ip6_setting (connection, ifcfg, error)) + if (!write_ip6_setting (connection, + ifcfg, + !route_ignore ? &route6_content : NULL, + error)) return FALSE; if (!write_res_options (connection, ifcfg, error)) @@ -2960,100 +2932,228 @@ write_connection (NMConnection *connection, write_connection_setting (s_con, ifcfg); + NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg)); + NM_SET_OUT (out_blobs, g_steal_pointer (&blobs)); + NM_SET_OUT (out_secrets, g_steal_pointer (&secrets)); + NM_SET_OUT (out_route_ignore, route_ignore); + NM_SET_OUT (out_route_content_svformat, g_steal_pointer (&route_content_svformat)); + NM_SET_OUT (out_route_content, g_steal_pointer (&route_content)); + NM_SET_OUT (out_route6_content, g_steal_pointer (&route6_content)); + return TRUE; +} + +static gboolean +do_write_to_disk (NMConnection *connection, + shvarFile *ifcfg, + GHashTable *blobs, + GHashTable *secrets, + gboolean route_ignore, + shvarFile *route_content_svformat, + GString *route_content, + GString *route6_content, + GError **error) +{ + /* From here on, we persist data to disk. Before, it was all in-memory + * only. But we loaded the ifcfg files from disk, and managled our + * new settings (in-momory). */ + if (!svWriteFile (ifcfg, 0644, error)) return FALSE; - if (out_reread || out_reread_same) { - gs_unref_object NMConnection *reread = NULL; - gs_free_error GError *local = NULL; - gs_free char *unhandled = NULL; - gboolean reread_same = FALSE; - - reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); - - if (unhandled) { - _LOGW ("failure to re-read the new connection from file \"%s\": %s", - ifcfg_name, "connection is unhandled"); - g_clear_object (&reread); - } else if (!reread) { - _LOGW ("failure to re-read the new connection from file \"%s\": %s", - ifcfg_name, local ? local->message : "<unknown>"); - } else { - if (out_reread_same) { - if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) - reread_same = TRUE; + write_ip4_aliases (connection, svFileGetName (ifcfg)); - nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_assert (reread_same == ({ - gs_unref_hashtable GHashTable *_settings = NULL; + if (!write_blobs (blobs, error)) + return FALSE; - ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) - && !_settings); - })); + if (!write_secrets (ifcfg, secrets, error)) + return FALSE; + + if (!route_ignore) { + gs_free char *route_path = utils_get_route_path (svFileGetName (ifcfg)); + + if (!route_content && !route_content_svformat) + (void) unlink (route_path); + else { + nm_assert (route_content_svformat || route_content); + if (route_content_svformat) { + if (!svWriteFile (route_content_svformat, 0644, error)) + return FALSE; + } else { + if (!g_file_set_contents (route_path, route_content->str, route_content->len, NULL)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Writing route file '%s' failed", route_path); + return FALSE; + } } } - - NM_SET_OUT (out_reread, g_steal_pointer (&reread)); - NM_SET_OUT (out_reread_same, reread_same); } - /* Only return the filename if this was a newly written ifcfg */ - if (out_filename && !filename) - *out_filename = g_steal_pointer (&ifcfg_name); + if (!route_ignore) { + gs_free char *route6_path = utils_get_route6_path (svFileGetName (ifcfg)); + + if (!route6_content) + (void) unlink (route6_path); + else { + if (!g_file_set_contents (route6_path, route6_content->str, route6_content->len, NULL)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Writing route6 file '%s' failed", route6_path); + return FALSE; + } + } + } return TRUE; } -gboolean -writer_can_write_connection (NMConnection *connection, GError **error) +static gboolean +do_write_reread (NMConnection *connection, + const char *ifcfg_name, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error) { - NMSettingConnection *s_con; + gs_unref_object NMConnection *reread = NULL; + gs_free_error GError *local = NULL; + gs_free char *unhandled = NULL; + gboolean reread_same = FALSE; - if ( ( nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME) - && !nm_connection_get_setting_pppoe (connection)) - || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) - return TRUE; + nm_assert (!out_reread || !*out_reread); - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "The ifcfg-rh plugin cannot write the connection '%s' (type '%s' pppoe %d)", - nm_connection_get_id (connection), - nm_setting_connection_get_connection_type (s_con), - !!nm_connection_get_setting_pppoe (connection)); - return FALSE; + reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); + + if (!reread) { + g_propagate_error (error, local); + local = NULL; + return FALSE; + } + if (unhandled) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "connection is unhandled"); + return FALSE; + } + if (out_reread_same) { + if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) + reread_same = TRUE; + + nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + nm_assert (reread_same == ({ + gs_unref_hashtable GHashTable *_settings = NULL; + + ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) + && !_settings); + })); + } + + NM_SET_OUT (out_reread, g_steal_pointer (&reread)); + NM_SET_OUT (out_reread_same, reread_same); + return TRUE; } gboolean -writer_new_connection (NMConnection *connection, - const char *ifcfg_dir, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +nms_ifcfg_rh_writer_write_connection (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + char **out_filename, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error) { - return write_connection (connection, ifcfg_dir, NULL, out_filename, out_reread, out_reread_same, error); + nm_auto_shvar_file_close shvarFile *ifcfg = NULL; + nm_auto_free_gstring GString *route_content = NULL; + gboolean route_ignore = FALSE; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + nm_auto_free_gstring GString *route6_content = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gs_unref_hashtable GHashTable *blobs = NULL; + GError *local = NULL; + + nm_assert (!out_reread || !*out_reread); + + if (!do_write_construct (connection, + ifcfg_dir, + filename, + &ifcfg, + &blobs, + &secrets, + &route_ignore, + &route_content_svformat, + &route_content, + &route6_content, + error)) + return FALSE; + + _LOGT ("write: write connection %s (%s) to file \"%s\"", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + + if (!do_write_to_disk (connection, + ifcfg, + blobs, + secrets, + route_ignore, + route_content_svformat, + route_content, + route6_content, + error)) + return FALSE; + + /* Note that we just wrote the connection to disk, and re-read it from there. + * That is racy if somebody else modifies the connection. + * + * A better solution might be, to re-read the connection only based on the + * in-memory representation of what we collected above. But the reader + * does not yet allow to inject the configuration. */ + if (out_reread || out_reread_same) { + if (!do_write_reread (connection, + svFileGetName (ifcfg), + out_reread, + out_reread_same, + &local)) { + _LOGW ("write: failure to re-read connection \"%s\": %s", + svFileGetName (ifcfg), local->message); + g_clear_error (&local); + } else { + if ( out_reread_same + && !*out_reread_same) { + _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + } + } + } + + /* Only return the filename if this was a newly written ifcfg */ + if (out_filename && !filename) + *out_filename = g_strdup (svFileGetName (ifcfg)); + + return TRUE; } gboolean -writer_update_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) +nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **error) { - if (utils_has_complex_routes (filename)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot modify a connection that has an associated 'rule-' or 'rule6-' file"); - return FALSE; - } + const char *type, *id; + + type = nm_connection_get_connection_type (connection); + if (NM_IN_STRSET (type, + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_INFINIBAND_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME)) + return TRUE; + if ( nm_streq0 (type, NM_SETTING_WIRED_SETTING_NAME) + && !nm_connection_get_setting_pppoe (connection)) + return TRUE; - return write_connection (connection, ifcfg_dir, filename, NULL, out_reread, out_reread_same, error); + id = nm_connection_get_id (connection); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The ifcfg-rh plugin cannot write the connection %s%s%s (type %s%s%s)", + NM_PRINT_FMT_QUOTE_STRING (id), + NM_PRINT_FMT_QUOTE_STRING (type)); + return FALSE; } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h index 9cd9513e..d7a255a9 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -18,26 +18,20 @@ * Copyright (C) 2009 Red Hat, Inc. */ -#ifndef _WRITER_H_ -#define _WRITER_H_ +#ifndef __NMS_IFCFG_RH_WRITER_H__ +#define __NMS_IFCFG_RH_WRITER_H__ #include "nm-connection.h" -gboolean writer_can_write_connection (NMConnection *connection, - GError **error); +gboolean nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, + GError **error); -gboolean writer_new_connection (NMConnection *connection, - const char *ifcfg_dir, - char **out_filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error); +gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, + const char *ifcfg_dir, + const char *filename, + char **out_filename, + NMConnection **out_reread, + gboolean *out_reread_same, + GError **error); -gboolean writer_update_connection (NMConnection *connection, - const char *ifcfg_dir, - const char *filename, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error); - -#endif /* _WRITER_H_ */ +#endif /* __NMS_IFCFG_RH_WRITER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 47ad5a23..df03bf65 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -38,10 +38,15 @@ #include "nm-core-internal.h" #include "nm-core-utils.h" +#include "nm-utils/nm-enum-utils.h" +#include "nm-utils/c-list.h" /*****************************************************************************/ struct _shvarLine { + + CList lst; + /* There are three cases: * * 1) the line is not a valid variable assignment (that is, it doesn't @@ -68,7 +73,7 @@ typedef struct _shvarLine shvarLine; struct _shvarFile { char *fileName; int fd; - GList *lineList; + CList lst_head; gboolean modified; }; @@ -627,6 +632,7 @@ svFile_new (const char *name) s = g_slice_new0 (shvarFile); s->fd = -1; s->fileName = g_strdup (name); + c_list_init (&s->lst_head); return s; } @@ -639,7 +645,7 @@ svFileGetName (const shvarFile *s) } void -svFileSetName_test_only (shvarFile *s, const char *fileName) +_nmtst_svFileSetName (shvarFile *s, const char *fileName) { /* changing the file name is not supported for regular * operation. Only allowed to use in tests, othewise, @@ -649,7 +655,7 @@ svFileSetName_test_only (shvarFile *s, const char *fileName) } void -svFileSetModified_test_only (shvarFile *s) +_nmtst_svFileSetModified (shvarFile *s) { /* marking a file as modified is only for testing. */ s->modified = TRUE; @@ -687,6 +693,7 @@ line_new_parse (const char *value, gsize len) nm_assert (value); line = g_slice_new0 (shvarLine); + c_list_init (&line->lst); for (k = 0; k < len; k++) { if (g_ascii_isspace (value[k])) @@ -724,6 +731,7 @@ line_new_build (const char *key, const char *value) value = svEscape (value, &value_escaped); line = g_slice_new (shvarLine); + c_list_init (&line->lst); line->line = value_escaped ?: g_strdup (value); line->key_with_prefix = g_strdup (key); line->key = line->key_with_prefix; @@ -768,6 +776,7 @@ line_free (shvarLine *line) ASSERT_shvarLine (line); g_free (line->line); g_free (line->key_with_prefix); + c_list_unlink (&line->lst); g_slice_free (shvarLine, line); } @@ -785,9 +794,8 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) int errsv = 0; char *arena; const char *p, *q; - GError *local = NULL; + gs_free_error GError *local = NULL; nm_auto_close int fd = -1; - GList *lineList = NULL; if (create) fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */ @@ -810,34 +818,35 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) return NULL; } - if (nm_utils_fd_get_contents (fd, + if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd, + closefd, 10 * 1024 * 1024, &arena, NULL, &local) < 0) { + if (create) + return svFile_new (name); + g_set_error (error, G_FILE_ERROR, local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED, "Could not read file '%s': %s", name, local->message); - g_error_free (local); return NULL; } + s = svFile_new (name); + for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1) - lineList = g_list_prepend (lineList, line_new_parse (p, q - p)); + c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst); if (p[0]) - lineList = g_list_prepend (lineList, line_new_parse (p, strlen (p))); + c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst); g_free (arena); - lineList = g_list_reverse (lineList); - - s = svFile_new (name); - s->lineList = lineList; /* closefd is set if we opened the file read-only, so go ahead and * close it, because we can't write to it anyway */ if (!closefd) { - s->fd = fd; - fd = -1; + nm_assert (fd > 0); + s->fd = nm_steal_fd (&fd); } return s; @@ -861,42 +870,22 @@ svCreateFile (const char *name) /*****************************************************************************/ -static const GList * -shlist_find (const GList *current, const char *key) -{ - nm_assert (_shell_is_name (key, -1)); - - if (current) { - do { - shvarLine *line = current->data; - - ASSERT_shvarLine (line); - if (line->key && nm_streq (line->key, key)) - return current; - current = current->next; - } while (current); - } - return NULL; -} - -/*****************************************************************************/ - GHashTable * svGetKeys (shvarFile *s) { GHashTable *keys = NULL; - const GList *current; + CList *current; const shvarLine *line; nm_assert (s); - for (current = s->lineList; current; current = current->next) { - line = current->data; + c_list_for_each (current, &s->lst_head) { + line = c_list_entry (current, shvarLine, lst); if (line->key && line->line) { /* we don't clone the keys. The keys are only valid * until @s gets modified. */ if (!keys) - keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + keys = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); g_hash_table_add (keys, (gpointer) line->key); } } @@ -908,14 +897,14 @@ svGetKeys (shvarFile *s) const char * svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix) { - const GList *current; + CList *current; const shvarLine *l; g_return_val_if_fail (s, NULL); g_return_val_if_fail (key_prefix, NULL); - for (current = s->lineList; current; current = current->next) { - l = current->data; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); if ( l->key && l->line && g_str_has_prefix (l->key, key_prefix)) @@ -930,34 +919,31 @@ svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix) static const char * _svGetValue (shvarFile *s, const char *key, char **to_free) { - const GList *current, *last; - const shvarLine *line; + CList *current; + const shvarLine *line, *l; + const char *v; nm_assert (s); nm_assert (_shell_is_name (key, -1)); nm_assert (to_free); - last = NULL; - current = s->lineList; - while ((current = shlist_find (current, key))) { - last = current; - current = current->next; + line = NULL; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); + if (l->key && nm_streq (l->key, key)) + line = l; } - if (last) { - line = last->data; - if (line->line) { - const char *v; - - v = svUnescape (line->line, to_free); - if (!v) { - /* a wrongly quoted value is treated like the empty string. - * See also svWriteFile(), which handles unparsable values - * that way. */ - nm_assert (!*to_free); - return ""; - } - return v; + + if (line && line->line) { + v = svUnescape (line->line, to_free); + if (!v) { + /* a wrongly quoted value is treated like the empty string. + * See also svWriteFile(), which handles unparsable values + * that way. */ + nm_assert (!*to_free); + return ""; } + return v; } *to_free = NULL; return NULL; @@ -1104,50 +1090,148 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m return result; } +gboolean +svGetValueEnum (shvarFile *s, const char *key, + GType gtype, int *out_value, + GError **error) +{ + gs_free char *to_free = NULL; + const char *svalue; + gs_free char *err_token = NULL; + int value; + + svalue = _svGetValue (s, key, &to_free); + if (!svalue) { + /* don't touch out_value. The caller is supposed + * to initialize it with the default value. */ + return TRUE; + } + + if (!nm_utils_enum_from_str (gtype, svalue, &value, &err_token)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Invalid token \"%s\" in \"%s\" for %s", + err_token, svalue, key); + return FALSE; + } + + NM_SET_OUT (out_value, value); + return TRUE; +} + /*****************************************************************************/ +static gboolean +_is_all_digits (const char *str) +{ + return str[0] + && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch)); +} + +#define IS_NUMBERED_TAG(key, tab_name) \ + ({ \ + const char *_key = (key); \ + \ + ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \ + && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \ + }) + +gboolean +svUnsetAll (shvarFile *s, SvKeyType match_key_type) +{ + CList *current; + shvarLine *line; + gboolean changed = FALSE; + + g_return_val_if_fail (s, FALSE); + + c_list_for_each (current, &s->lst_head) { + line = c_list_entry (current, shvarLine, lst); + ASSERT_shvarLine (line); + if (!line->key) + continue; + + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY)) + goto do_clear; + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) { + if ( IS_NUMBERED_TAG (line->key, "ADDRESS") + || IS_NUMBERED_TAG (line->key, "NETMASK") + || IS_NUMBERED_TAG (line->key, "GATEWAY") + || IS_NUMBERED_TAG (line->key, "METRIC") + || IS_NUMBERED_TAG (line->key, "OPTIONS")) + goto do_clear; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) { + if ( IS_NUMBERED_TAG (line->key, "IPADDR") + || IS_NUMBERED_TAG (line->key, "PREFIX") + || IS_NUMBERED_TAG (line->key, "NETMASK") + || IS_NUMBERED_TAG (line->key, "GATEWAY")) + goto do_clear; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) { + if (g_str_has_prefix (line->key, "NM_USER_")) + goto do_clear; + } + + continue; +do_clear: + if (nm_clear_g_free (&line->line)) { + ASSERT_shvarLine (line); + changed = TRUE; + } + } + + if (changed) + s->modified = TRUE; + return changed; +} + /* Same as svSetValueStr() but it preserves empty @value -- contrary to * svSetValueStr() for which "" effectively means to remove the value. */ -void +gboolean svSetValue (shvarFile *s, const char *key, const char *value) { - GList *current, *last; + CList *current; + shvarLine *line, *l; + gboolean changed = FALSE; - g_return_if_fail (s != NULL); - g_return_if_fail (key != NULL); + g_return_val_if_fail (s, FALSE); + g_return_val_if_fail (key, FALSE); nm_assert (_shell_is_name (key, -1)); - last = NULL; - current = s->lineList; - while ((current = (GList *) shlist_find (current, key))) { - if (last) { - /* if we find multiple entries for the same key, we can - * delete all but the last. */ - line_free (last->data); - s->lineList = g_list_delete_link (s->lineList, last); - s->modified = TRUE; + line = NULL; + c_list_for_each (current, &s->lst_head) { + l = c_list_entry (current, shvarLine, lst); + if (l->key && nm_streq (l->key, key)) { + if (line) { + /* if we find multiple entries for the same key, we can + * delete all but the last. */ + line_free (line); + changed = TRUE; + } + line = l; } - last = current; - current = current->next; } if (!value) { - if (last) { - shvarLine *line = last->data; - - if (nm_clear_g_free (&line->line)) - s->modified = TRUE; + if (line) { + if (nm_clear_g_free (&line->line)) { + changed = TRUE; + } } } else { - if (!last) { - s->lineList = g_list_append (s->lineList, line_new_build (key, value)); - s->modified = TRUE; + if (!line) { + c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst); + changed = TRUE; } else { - if (line_set (last->data, value)) - s->modified = TRUE; + if (line_set (line, value)) + changed = TRUE; } } + + if (changed) + s->modified = TRUE; + return changed; } /* Set the variable <key> equal to the value <value>. @@ -1155,51 +1239,48 @@ svSetValue (shvarFile *s, const char *key, const char *value) * the key=value pair after that line. Otherwise, append the pair * to the bottom of the file. */ -void +gboolean svSetValueStr (shvarFile *s, const char *key, const char *value) { - svSetValue (s, key, value && value[0] ? value : NULL); + return svSetValue (s, key, value && value[0] ? value : NULL); } -void +gboolean svSetValueInt64 (shvarFile *s, const char *key, gint64 value) { char buf[NM_DECIMAL_STR_MAX (value)]; - svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value)); + return svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value)); } -void -svSetValueBoolean (shvarFile *s, const char *key, gboolean value) +gboolean +svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value) { - svSetValue (s, key, value ? "yes" : "no"); + if (do_set) + return svSetValueInt64 (s, key, value); + else + return svUnsetValue (s, key); } -void -svUnsetValue (shvarFile *s, const char *key) +gboolean +svSetValueBoolean (shvarFile *s, const char *key, gboolean value) { - svSetValue (s, key, NULL); + return svSetValue (s, key, value ? "yes" : "no"); } -void -svUnsetValuesWithPrefix (shvarFile *s, const char *prefix) +gboolean +svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value) { - GList *current; - - g_return_if_fail (s); - g_return_if_fail (prefix); + gs_free char *v = NULL; - for (current = s->lineList; current; current = current->next) { - shvarLine *line = current->data; + v = _nm_utils_enum_to_str_full (gtype, value, " "); + return svSetValueStr (s, key, v); +} - ASSERT_shvarLine (line); - if ( line->key - && g_str_has_prefix (line->key, prefix)) { - if (nm_clear_g_free (&line->line)) - s->modified = TRUE; - } - ASSERT_shvarLine (line); - } +gboolean +svUnsetValue (shvarFile *s, const char *key) +{ + return svSetValue (s, key, NULL); } /*****************************************************************************/ @@ -1215,7 +1296,7 @@ svWriteFile (shvarFile *s, int mode, GError **error) { FILE *f; int tmpfd; - const GList *current; + CList *current; if (s->modified) { if (s->fd == -1) @@ -1248,8 +1329,8 @@ svWriteFile (shvarFile *s, int mode, GError **error) } f = fdopen (tmpfd, "w"); fseek (f, 0, SEEK_SET); - for (current = s->lineList; current; current = current->next) { - const shvarLine *line = current->data; + c_list_for_each (current, &s->lst_head) { + const shvarLine *line = c_list_entry (current, shvarLine, lst); const char *str; char *s_tmp; gboolean valid_value; @@ -1288,11 +1369,13 @@ svWriteFile (shvarFile *s, int mode, GError **error) void svCloseFile (shvarFile *s) { + CList *current, *safe; + g_return_if_fail (s != NULL); - if (s->fd != -1) - close (s->fd); + nm_close (s->fd); g_free (s->fileName); - g_list_free_full (s->lineList, (GDestroyNotify) line_free); + c_list_for_each_safe (current, safe, &s->lst_head) + line_free (c_list_entry (current, shvarLine, lst)); g_slice_free (shvarFile, s); } diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index a13920a1..c48bbfd3 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -35,8 +35,8 @@ typedef struct _shvarFile shvarFile; const char *svFileGetName (const shvarFile *s); -void svFileSetName_test_only (shvarFile *s, const char *fileName); -void svFileSetModified_test_only (shvarFile *s); +void _nmtst_svFileSetName (shvarFile *s, const char *fileName); +void _nmtst_svFileSetModified (shvarFile *s); /* Create the file <name>, return a shvarFile (never fails) */ shvarFile *svCreateFile (const char *name); @@ -68,19 +68,32 @@ gint svGetValueBoolean (shvarFile *s, const char *key, gint def); gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); +gboolean svGetValueEnum (shvarFile *s, const char *key, + GType gtype, int *out_value, + GError **error); + /* Set the variable <key> equal to the value <value>. * If <key> does not exist, and the <current> pointer is set, append * the key=value pair after that line. Otherwise, prepend the pair * to the top of the file. */ -void svSetValue (shvarFile *s, const char *key, const char *value); -void svSetValueStr (shvarFile *s, const char *key, const char *value); -void svSetValueBoolean (shvarFile *s, const char *key, gboolean value); -void svSetValueInt64 (shvarFile *s, const char *key, gint64 value); - -void svUnsetValue (shvarFile *s, const char *key); - -void svUnsetValuesWithPrefix (shvarFile *s, const char *prefix); +gboolean svSetValue (shvarFile *s, const char *key, const char *value); +gboolean svSetValueStr (shvarFile *s, const char *key, const char *value); +gboolean svSetValueBoolean (shvarFile *s, const char *key, gboolean value); +gboolean svSetValueInt64 (shvarFile *s, const char *key, gint64 value); +gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value); +gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value); + +gboolean svUnsetValue (shvarFile *s, const char *key); + +typedef enum { + SV_KEY_TYPE_ANY = (1LL << 0), + SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1), + SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), + SV_KEY_TYPE_USER = (1LL << 3), +} SvKeyType; + +gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type); /* Write the current contents iff modified. Returns FALSE on error * and TRUE on success. Do not write if no values have been modified. diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected index 3956003d..854d2490 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -1,4 +1,3 @@ -DEVICE=bond0 BONDING_OPTS=mode=balance-rr TYPE=Bond BONDING_MASTER=yes @@ -13,4 +12,5 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no NAME="Test Write Bond Main" UUID=${UUID} +DEVICE=bond0 ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected index 60091b7b..793713ea 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -1,6 +1,5 @@ VLAN=yes TYPE=Vlan -DEVICE=vlan43 PHYSDEV=eth9 VLAN_ID=43 REORDER_HDR=yes @@ -19,4 +18,5 @@ IPV4_FAILURE_FATAL=no IPV6INIT=no NAME="Vlan test-vlan-interface" UUID=${UUID} +DEVICE=vlan43 ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected index 367d0ddb..87980dc4 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected @@ -1,5 +1,4 @@ VLAN=yes -DEVICE=enp31s0f1-142 PHYSDEV=enp31s0f1 VLAN_ID=142 REORDER_HDR=yes @@ -7,6 +6,7 @@ GVRP=no MVRP=no NAME=team-slave-enp31s0f1-142 UUID=74f435bb-ede4-415a-9d48-f580b60eba04 +DEVICE=enp31s0f1-142 ONBOOT=no TEAM_MASTER=team142 DEVICETYPE=TeamPort diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index 1d31b5ba..2bc987c2 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -4,5 +4,5 @@ TYPE=Bridge BOOTPROTO=dhcp STP=on DELAY=2 -BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0" +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24" MACADDR=00:16:41:11:22:33 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw new file mode 100644 index 00000000..181ffbef --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TTLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=EAP-GTC +IEEE_8021X_PASSWORD_RAW=0408151623420001 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy index 3f02032a..faa247d8 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy @@ -5,3 +5,4 @@ via 8.8.8.8 to 32.42.52.62 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000 initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4 +7.7.7.8/32 via (null) metric 18 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 496a164b..52dbd932 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -232,7 +232,7 @@ _assert_expected_content (NMConnection *connection, const char *filename, const g_assert (_ifcfg_dir && _ifcfg_dir[0]); \ g_assert (_filename && _filename[0]); \ \ - _success = writer_update_connection (_connection, _ifcfg_dir, _filename, _out_reread, _out_reread_same, &_error); \ + _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, _out_reread, _out_reread_same, &_error); \ nmtst_assert_success (_success, _error); \ _assert_expected_content (_connection, _filename, _expected); \ } G_STMT_END @@ -260,8 +260,8 @@ _connection_from_file (const char *filename, g_assert (!out_unhandled || !*out_unhandled); - connection = connection_from_file_test (filename, network_file, test_type, - out_unhandled ?: &unhandled_fallback, &error); + connection = nmtst_connection_from_file (filename, network_file, test_type, + out_unhandled ?: &unhandled_fallback, &error); g_assert_no_error (error); g_assert (!unhandled_fallback); @@ -282,7 +282,7 @@ _connection_from_file_fail (const char *filename, GError *local = NULL; char *unhandled = NULL; - connection = connection_from_file_test (filename, network_file, test_type, &unhandled, &local); + connection = nmtst_connection_from_file (filename, network_file, test_type, &unhandled, &local); g_assert (!connection); g_assert (local); @@ -310,12 +310,13 @@ _writer_new_connection_reread (NMConnection *connection, con_verified = nmtst_connection_duplicate_and_normalize (connection); - success = writer_new_connection (con_verified, - ifcfg_dir, - &filename, - reread, - out_reread_same, - &error); + success = nms_ifcfg_rh_writer_write_connection (con_verified, + ifcfg_dir, + NULL, + &filename, + reread, + out_reread_same, + &error); nmtst_assert_success (success, error); g_assert (filename && filename[0]); @@ -384,12 +385,13 @@ _writer_new_connection_fail (NMConnection *connection, connection_normalized = nmtst_connection_duplicate_and_normalize (connection); - success = writer_new_connection (connection_normalized, - ifcfg_dir, - &filename, - &reread, - NULL, - &local); + success = nms_ifcfg_rh_writer_write_connection (connection_normalized, + ifcfg_dir, + NULL, + &filename, + &reread, + NULL, + &local); nmtst_assert_no_success (success, local); g_assert (!filename); g_assert (!reread); @@ -1376,7 +1378,7 @@ test_read_wired_static_routes_legacy (void) g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); /* Routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 3); + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 4); /* Route #1 */ ip4_route = nm_setting_ip_config_get_route (s_ip4, 0); @@ -1410,6 +1412,13 @@ test_read_wired_static_routes_legacy (void) nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.2.3.4"); + ip4_route = nm_setting_ip_config_get_route (s_ip4, 3); + g_assert (ip4_route != NULL); + g_assert_cmpstr (nm_ip_route_get_dest (ip4_route), ==, "7.7.7.8"); + g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32); + g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, NULL); + g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 18); + g_object_unref (connection); } @@ -1954,6 +1963,46 @@ test_read_802_1x_ttls_eapgtc (void) } static void +test_read_write_802_1x_password_raw (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSetting8021x *s_8021x; + GBytes *bytes; + gconstpointer data; + gsize size; + + /* Test that the 802-1x.password-raw is correctly read and written. */ + + connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1x-password-raw", + NULL, TYPE_ETHERNET, NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + bytes = nm_setting_802_1x_get_password_raw (s_8021x); + g_assert (bytes); + data = g_bytes_get_data (bytes, &size); + g_assert_cmpmem (data, size, "\x04\x08\x15\x16\x23\x42\x00\x01", 8); + + g_assert_cmpint (nm_setting_802_1x_get_password_raw_flags (s_8021x), + ==, + NM_SETTING_SECRET_FLAG_NONE); + + _writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile); + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + keyfile = utils_get_keys_path (testfile); + g_assert (g_file_test (keyfile, G_FILE_TEST_EXISTS)); + + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + +static void test_read_wired_aliases_good (gconstpointer test_data) { const int N = GPOINTER_TO_INT (test_data); @@ -4108,7 +4157,6 @@ test_write_wired_static (void) route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); g_assert_no_error (error); - nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_TOS, g_variant_new_byte (0xb8)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280)); nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE)); @@ -4154,6 +4202,171 @@ test_write_wired_static (void) } static void +test_write_wired_static_with_generic (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *route6file = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4, *reread_s_ip4; + NMSettingIPConfig *s_ip6, *reread_s_ip6; + NMIPAddress *addr; + NMIPAddress *addr6; + NMIPRoute *route6; + GError *error = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired Static", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, 1, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_object_set (s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, "31:33:33:37:be:cd", + NM_SETTING_WIRED_MTU, (guint32) 1492, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) 204, + NULL); + + addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + addr = nm_ip_address_new (AF_INET, "1.1.1.5", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + nm_setting_ip_config_add_dns (s_ip4, "4.2.2.1"); + nm_setting_ip_config_add_dns (s_ip4, "4.2.2.2"); + + nm_setting_ip_config_add_dns_search (s_ip4, "foobar.com"); + nm_setting_ip_config_add_dns_search (s_ip4, "lab.foobar.com"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) 206, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new (AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + addr6 = nm_ip_address_new (AF_INET6, "2003:1234:abcd::2", 22, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + addr6 = nm_ip_address_new (AF_INET6, "3003:1234:abcd::3", 33, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip6, addr6); + nm_ip_address_unref (addr6); + + /* Add routes */ + route6 = nm_ip_route_new (AF_INET6, + "2222:aaaa:bbbb:cccc::", 64, + "2222:aaaa:bbbb:cccc:dddd:eeee:5555:6666", 99, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_route (s_ip6, route6); + nm_ip_route_unref (route6); + + route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); + g_assert_no_error (error); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_FROM, g_variant_new_string ("2222::bbbb/32")); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("::42")); + nm_setting_ip_config_add_route (s_ip6, route6); + nm_ip_route_unref (route6); + + /* DNS servers */ + nm_setting_ip_config_add_dns (s_ip6, "fade:0102:0103::face"); + nm_setting_ip_config_add_dns (s_ip6, "cafe:ffff:eeee:dddd:cccc:bbbb:aaaa:feed"); + + /* DNS domains */ + nm_setting_ip_config_add_dns_search (s_ip6, "foobar6.com"); + nm_setting_ip_config_add_dns_search (s_ip6, "lab6.foobar.com"); + + nm_connection_add_setting (connection, nm_setting_generic_new ()); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connection_FIXME (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile); + route6file = utils_get_route6_path (testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + + /* FIXME: currently DNS domains from IPv6 setting are stored in 'DOMAIN' key in ifcfg-file + * However after re-reading they are dropped into IPv4 setting. + * So, in order to comparison succeeded, move DNS domains back to IPv6 setting. + */ + reread_s_ip4 = nm_connection_get_setting_ip4_config (reread); + reread_s_ip6 = nm_connection_get_setting_ip6_config (reread); + nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 2)); + nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 3)); + nm_setting_ip_config_remove_dns_search (reread_s_ip4, 3); + nm_setting_ip_config_remove_dns_search (reread_s_ip4, 2); + + g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip4), ==, 204); + g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip6), ==, 206); + + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert (!nm_connection_diff (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert (diffs); + g_assert (g_hash_table_size (diffs) == 1); + g_assert (g_hash_table_lookup (diffs, "generic")); + g_assert (!nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } + g_assert (!nm_connection_get_setting (reread, NM_TYPE_SETTING_GENERIC)); + nm_connection_add_setting (reread, nm_setting_generic_new ()); + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert (nm_connection_diff (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert (!diffs); + g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } +} + +static void test_write_wired_dhcp (void) { nmtst_auto_unlinkfile char *testfile = NULL; @@ -5867,6 +6080,7 @@ test_write_wifi_wpa_psk (gconstpointer test_data) g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NM_SETTING_WIRELESS_SECURITY_PSK, args.psk, + NM_SETTING_WIRELESS_SECURITY_PMF, (int) NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED, NULL); if (GPOINTER_TO_INT (args.wep_group_p)) { @@ -7172,6 +7386,7 @@ test_read_bridge_main (void) g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); + g_assert_cmpuint (nm_setting_bridge_get_group_forward_mask (s_bridge), ==, 24); g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge)); /* MAC address */ @@ -7217,6 +7432,7 @@ test_write_bridge_main (void) g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, + NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, 19008, NULL); /* IP4 setting */ @@ -9147,8 +9363,8 @@ test_write_unknown (gconstpointer test_data) sv = _svOpenFile (testfile); - svFileSetName_test_only (sv, filename_tmp_1); - svFileSetModified_test_only (sv); + _nmtst_svFileSetName (sv, filename_tmp_1); + _nmtst_svFileSetModified (sv); if (g_str_has_suffix (testfile, "ifcfg-test-write-unknown-4")) { _svGetValue_check (sv, "NAME", "l4x"); @@ -9429,6 +9645,7 @@ int main (int argc, char **argv) g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); + g_test_add_func (TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw); g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good); g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good); g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1); @@ -9492,6 +9709,7 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wired/read/unkwnown-ethtool-opt", test_read_wired_unknown_ethtool_opt); g_test_add_func (TPATH "wired/write/static", test_write_wired_static); + g_test_add_func (TPATH "wired/write/static-with-generic", test_write_wired_static_with_generic); g_test_add_func (TPATH "wired/write/static-ip6-only", test_write_wired_static_ip6_only); g_test_add_func (TPATH "wired/write-static-routes", test_write_wired_static_routes); g_test_add_func (TPATH "wired/read-write-static-routes-legacy", test_read_write_static_routes_legacy); diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c index c5129fea..ed0a757f 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c @@ -31,7 +31,7 @@ #include "settings/nm-settings-plugin.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" -#include "nm-setting-metadata.h" +#include "nm-meta-setting.h" #include "nms-ifnet-net-utils.h" #include "nms-ifnet-wpa-parser.h" diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c index ba87d46c..5dbb124c 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-connection.c +++ b/src/settings/plugins/ifnet/nms-ifnet-connection.c @@ -74,89 +74,81 @@ nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection) return NM_IFNET_CONNECTION_GET_PRIVATE (connection)->conn_name; } -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - GError *error = NULL; NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - gchar *new_name = NULL; + char *new_name = NULL; gboolean success = FALSE; + gboolean added = FALSE; + + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); if (priv->conn_name) { - /* Existing connection; update it */ success = ifnet_update_parsers_by_connection (NM_CONNECTION (connection), priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, &new_name, NULL, - &error); + error); } else { - /* New connection, add it */ + added = TRUE; success = ifnet_add_new_connection (NM_CONNECTION (connection), CONF_NET_FILE, WPA_SUPPLICANT_CONF, &new_name, NULL, - &error); - if (success) - reload_parsers (); + error); } + g_assert (!!success == (new_name != NULL)); if (success) { - /* update connection name */ - g_assert (new_name); g_free (priv->conn_name); priv->conn_name = new_name; - - NM_SETTINGS_CONNECTION_CLASS (nm_ifnet_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data); - nm_log_info (LOGD_SETTINGS, "Successfully updated %s", priv->conn_name); - } else { - nm_log_warn (LOGD_SETTINGS, "Failed to update %s", - priv->conn_name ? priv->conn_name : - nm_connection_get_id (NM_CONNECTION (connection))); - reload_parsers (); - callback (connection, error, user_data); - g_error_free (error); } + reload_parsers (); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + + if (success) { + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("ifcfg-rh: %s %s", + added ? "persist" : "updated", + new_name)); + } + return success; } -static void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { - GError *error = NULL; NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); - /* Only connections which exist in /etc/conf.d/net will have a conn_name */ if (priv->conn_name) { + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + if (!ifnet_delete_connection_in_parsers (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL)) { nm_log_warn (LOGD_SETTINGS, "Failed to delete %s", priv->conn_name); reload_parsers (); - callback (connection, error, user_data); - g_error_free (error); - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); - return; + /* let's not return an error. */ } - } - - NM_SETTINGS_CONNECTION_CLASS (nm_ifnet_connection_parent_class)->delete (connection, callback, user_data); - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + } - nm_log_info (LOGD_SETTINGS, "Successfully deleted %s", - priv->conn_name ? priv->conn_name : - nm_connection_get_id (NM_CONNECTION (connection))); + return TRUE; } /*****************************************************************************/ @@ -222,7 +214,7 @@ nm_ifnet_connection_class_init (NMIfnetConnectionClass * ifnet_connection_class) object_class->finalize = finalize; - settings_class->delete = do_delete; + settings_class->delete = delete; settings_class->commit_changes = commit_changes; signals[IFNET_SETUP_MONITORS] = diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c index 0007f9cd..d3e47219 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c @@ -58,7 +58,7 @@ add_new_connection_config (const gchar * type, const gchar * name) /* Return existing connection */ if ((new_conn = g_hash_table_lookup (conn_table, name)) != NULL) return new_conn; - new_conn = g_hash_table_new (g_str_hash, g_str_equal); + new_conn = g_hash_table_new (nm_str_hash, g_str_equal); new_name = g_strdup (name); g_hash_table_insert (new_conn, g_strdup ("name"), new_name); g_hash_table_insert (new_conn, g_strdup ("type"), g_strdup (type)); @@ -302,8 +302,8 @@ ifnet_init (gchar * config_file) net_parser_data_changed = FALSE; - conn_table = g_hash_table_new (g_str_hash, g_str_equal); - global_settings_table = g_hash_table_new (g_str_hash, g_str_equal); + conn_table = g_hash_table_new (nm_str_hash, g_str_equal); + global_settings_table = g_hash_table_new (nm_str_hash, g_str_equal); functions_list = NULL; if (g_file_test (config_file, G_FILE_TEST_IS_REGULAR)) diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c index 5a6a8ce8..998b04b4 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-plugin.c +++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.c @@ -138,9 +138,9 @@ monitor_file_changes (const char *filename, info->callback = callback; info->user_data = user_data; g_object_weak_ref (G_OBJECT (monitor), (GWeakNotify) g_free, - info); + info); g_signal_connect (monitor, "changed", G_CALLBACK (file_changed), - info); + info); } else { nm_log_warn (LOGD_SETTINGS, "Monitoring %s failed, error: %s", filename, error == NULL ? "nothing" : (*error)->message); @@ -150,34 +150,38 @@ monitor_file_changes (const char *filename, } static void -setup_monitors (NMIfnetConnection * connection, gpointer user_data) +setup_monitors (NMIfnetConnection *connection, gpointer user_data) { SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - if (nm_config_get_monitor_connection_files (nm_config_get ())) { - priv->net_monitor = - monitor_file_changes (CONF_NET_FILE, (FileChangedFn) reload_connections, - user_data); - priv->wpa_monitor = - monitor_file_changes (WPA_SUPPLICANT_CONF, (FileChangedFn) reload_connections, - user_data); - } + if (!nm_config_get_monitor_connection_files (nm_config_get ())) + return; + + if (priv->net_monitor || priv->wpa_monitor) + return; + + priv->net_monitor = monitor_file_changes (CONF_NET_FILE, + (FileChangedFn) reload_connections, + user_data); + priv->wpa_monitor = monitor_file_changes (WPA_SUPPLICANT_CONF, + (FileChangedFn) reload_connections, + user_data); } static void -cancel_monitors (NMIfnetConnection * connection, gpointer user_data) +cancel_monitors (NMIfnetConnection *connection, gpointer user_data) { SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); if (priv->net_monitor) { g_file_monitor_cancel (priv->net_monitor); - g_object_unref (priv->net_monitor); + g_clear_object (&priv->net_monitor); } if (priv->wpa_monitor) { g_file_monitor_cancel (priv->wpa_monitor); - g_object_unref (priv->wpa_monitor); + g_clear_object (&priv->wpa_monitor); } } @@ -226,7 +230,7 @@ reload_connections (NMSettingsPlugin *config) NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH, FALSE); - new_connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + new_connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); /* Reread on-disk data and refresh in-memory connections from it */ conn_names = ifnet_get_connection_names (); @@ -321,11 +325,11 @@ add_connection (NMSettingsPlugin *config, * asked to write it to disk. */ if (!ifnet_can_write_connection (source, error)) - return NULL; + goto out; if (save_to_disk) { if (!ifnet_add_new_connection (source, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL, NULL, error)) - return NULL; + goto out; reload_connections (config); new = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (source)); } else { @@ -337,6 +341,11 @@ add_connection (NMSettingsPlugin *config, } } +out: + if (!new && error && !*error) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The ifnet plugin cannot add the connection (unknown error)."); + } return (NMSettingsConnection *) new; } @@ -439,7 +448,7 @@ init (NMSettingsPlugin *config) nm_log_info (LOGD_SETTINGS, "Initializing!"); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); priv->unmanaged_well_known = !is_managed_plugin (); nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanaged_well_known ? "unmanaged" : "managed"); diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c index 61e4da7c..2b62e886 100644 --- a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c +++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c @@ -26,6 +26,7 @@ #include <string.h> #include <stdlib.h> +#include "nm-utils/nm-hash-utils.h" #include "settings/nm-settings-plugin.h" #include "nms-ifnet-net-parser.h" @@ -270,8 +271,8 @@ wpa_parser_init (const char *wpa_supplicant_conf) gboolean complete = FALSE; wpa_parser_data_changed = FALSE; - wsec_table = g_hash_table_new (g_str_hash, g_str_equal); - wsec_global_table = g_hash_table_new (g_str_hash, g_str_equal); + wsec_table = g_hash_table_new (nm_str_hash, g_str_equal); + wsec_global_table = g_hash_table_new (nm_str_hash, g_str_equal); if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR)) channel = @@ -292,7 +293,7 @@ wpa_parser_init (const char *wpa_supplicant_conf) continue; } else { GHashTable *network = - g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_new (nm_str_hash, g_str_equal); do { gchar *quote_start, *quote_end = NULL, *comment; @@ -512,7 +513,7 @@ wpa_add_security (const char *ssid) return TRUE; else { GHashTable *security = - g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_new (nm_str_hash, g_str_equal); gchar *ssid_i; nm_log_info (LOGD_SETTINGS, "Adding security for %s", ssid); diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index 189b6e69..0928772a 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -139,7 +139,10 @@ bind_device_to_connection (SettingsPluginIfupdown *self, g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, address, NULL); } - nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (exported), NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, NULL, NULL); + nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (exported), + NULL, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL); } static void @@ -338,16 +341,16 @@ init (NMSettingsPlugin *config) const char *block_name; NMIfupdownConnection *connection; - auto_ifaces = g_hash_table_new (g_str_hash, g_str_equal); + auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); if(!priv->connections) - priv->connections = g_hash_table_new (g_str_hash, g_str_equal); + priv->connections = g_hash_table_new (nm_str_hash, g_str_equal); if(!priv->kernel_ifaces) - priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _udev_device_unref); + priv->kernel_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _udev_device_unref); if(!priv->eni_ifaces) - priv->eni_ifaces = g_hash_table_new (g_str_hash, g_str_equal); + priv->eni_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); nm_log_info (LOGD_SETTINGS, "init!"); @@ -409,7 +412,7 @@ init (NMSettingsPlugin *config) exported = g_hash_table_lookup (priv->connections, block->name); if (exported) { nm_log_info (LOGD_SETTINGS, "deleting %s from connections", block->name); - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (exported), NULL, NULL); + nm_settings_connection_delete (NM_SETTINGS_CONNECTION (exported), NULL); g_hash_table_remove (priv->connections, block->name); } diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c index bd07d263..300aa9f7 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c @@ -50,29 +50,30 @@ G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CO /*****************************************************************************/ -static void +static gboolean commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, NMSettingsConnectionCommitReason commit_reason, - NMSettingsConnectionCommitFunc callback, - gpointer user_data) + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) { - char *path = NULL; - GError *error = NULL; + gs_free char *path = NULL; gs_unref_object NMConnection *reread = NULL; gboolean reread_same = FALSE; - if (!nms_keyfile_writer_connection (NM_CONNECTION (connection), + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); + + if (!nms_keyfile_writer_connection (new_connection ?: NM_CONNECTION (connection), nm_settings_connection_get_filename (connection), NM_FLAGS_ALL (commit_reason, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED), &path, &reread, &reread_same, - &error)) { - callback (connection, error, user_data); - g_clear_error (&error); - return; - } + error)) + return FALSE; /* Update the filename if it changed */ if ( path @@ -81,52 +82,37 @@ commit_changes (NMSettingsConnection *connection, nm_settings_connection_set_filename (connection, path); if (old_path) { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), - old_path); + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"", + NMS_KEYFILE_CONNECTION_LOG_ARG (connection), + old_path)); } else { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection", + NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); } } else { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); - } - - if (reread && !reread_same) { - gs_free_error GError *local = NULL; - - if (!nm_settings_connection_replace_settings (connection, reread, FALSE, "update-during-write", &local)) { - nm_log_warn (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection failed: %s", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), local->message); - } else { - nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); - } + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, + NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); } - g_free (path); + if (reread && !reread_same) + *out_reread_connection = g_steal_pointer (&reread); - NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->commit_changes (connection, - commit_reason, - callback, - user_data); + return TRUE; } -static void -do_delete (NMSettingsConnection *connection, - NMSettingsConnectionDeleteFunc callback, - gpointer user_data) +static gboolean +delete (NMSettingsConnection *connection, + GError **error) { const char *path; path = nm_settings_connection_get_filename (connection); if (path) g_unlink (path); - - NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->delete (connection, - callback, - user_data); + return TRUE; } /*****************************************************************************/ @@ -192,5 +178,5 @@ nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class); settings_class->commit_changes = commit_changes; - settings_class->delete = do_delete; + settings_class->delete = delete; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index 4af80142..ee4db320 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -378,7 +378,7 @@ _paths_from_connections (GHashTable *connections) { GHashTableIter iter; NMSKeyfileConnection *connection; - GHashTable *paths = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); g_hash_table_iter_init (&iter, connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { @@ -588,7 +588,7 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin) NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin); priv->config = g_object_ref (nm_config_get ()); - priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 92ed2849..270a217e 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -213,6 +213,9 @@ _internal_write_connection (NMConnection *connection, if (!data) return FALSE; + if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR)) + (void) g_mkdir_with_parents (keyfile_dir, 0755); + /* If we have existing file path, use it. Else generate one from * connection's ID. */ diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index d9da5317..2584a722 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -312,11 +312,11 @@ test_read_valid_wired_connection (void) check_ip_route (s_ip4, 3, "1.1.1.3", 13, NULL, -1); check_ip_route (s_ip4, 4, "1.1.1.4", 14, "2.2.2.4", -1); check_ip_route (s_ip4, 5, "1.1.1.5", 15, "2.2.2.5", -1); - check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", -1); + check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", 0); check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1); check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1); - check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, -1); - check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, -1); + check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0); + check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, 0); check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21); /* Route attributes */ diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index f9a84620..16e7851a 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -31,6 +31,7 @@ #include "nm-auth-subject.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" +#include "nm-setting-ip4-config.h" typedef struct { char *value; @@ -87,11 +88,11 @@ nm_supplicant_config_init (NMSupplicantConfig * self) { NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); - priv->config = g_hash_table_new_full (g_str_hash, g_str_equal, + priv->config = g_hash_table_new_full (nm_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) config_option_free); - priv->blobs = g_hash_table_new_full (g_str_hash, g_str_equal, + priv->blobs = g_hash_table_new_full (nm_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) blob_free); @@ -537,6 +538,56 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, return TRUE; } +gboolean +nm_supplicant_config_add_bgscan (NMSupplicantConfig *self, + NMConnection *connection, + GError **error) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char *bgscan; + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + /* Don't scan when a shared connection (either AP or Ad-Hoc) is active; + * it will disrupt connected clients. + */ + if (NM_IN_STRSET (nm_setting_wireless_get_mode (s_wifi), + NM_SETTING_WIRELESS_MODE_AP, + NM_SETTING_WIRELESS_MODE_ADHOC)) + return TRUE; + + /* Don't scan when the connection is locked to a specifc AP, since + * intra-ESS roaming (which requires periodic scanning) isn't being + * used due to the specific AP lock. (bgo #513820) + */ + if (nm_setting_wireless_get_bssid (s_wifi)) + return TRUE; + + /* Default to a very long bgscan interval when signal is OK on the assumption + * that either (a) there aren't multiple APs and we don't need roaming, or + * (b) since EAP/802.1x isn't used and thus there are fewer steps to fail + * during a roam, we can wait longer before scanning for roam candidates. + */ + bgscan = "simple:30:-80:86400"; + + /* If using WPA Enterprise or Dynamic WEP use a shorter bgscan interval on + * the assumption that this is a multi-AP ESS in which we want more reliable + * roaming between APs. Thus trigger scans when the signal is still somewhat + * OK so we have an up-to-date roam candidate list when the signal gets bad. + */ + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (s_wsec) { + if (NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wsec), + "ieee8021x", + "wpa-eap")) + bgscan = "simple:30:-65:300"; + } + + return nm_supplicant_config_add_option (self, "bgscan", bgscan, -1, FALSE, error); +} + static gboolean add_string_val (NMSupplicantConfig *self, const char *field, @@ -684,9 +735,10 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, NMSetting8021x *setting_8021x, const char *con_uuid, guint32 mtu, + NMSettingWirelessSecurityPmf pmf, GError **error) { - const char *key_mgmt, *auth_alg; + const char *key_mgmt, *key_mgmt_conf, *auth_alg; const char *psk; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); @@ -694,8 +746,19 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, g_return_val_if_fail (con_uuid != NULL, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); - if (!add_string_val (self, key_mgmt, "key_mgmt", TRUE, NULL, error)) + key_mgmt = key_mgmt_conf = nm_setting_wireless_security_get_key_mgmt (setting); + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) { + if (nm_streq (key_mgmt_conf, "wpa-psk")) + key_mgmt_conf = "wpa-psk wpa-psk-sha256"; + else if (nm_streq (key_mgmt_conf, "wpa-eap")) + key_mgmt_conf = "wpa-eap wpa-eap-sha256"; + } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { + if (nm_streq (key_mgmt_conf, "wpa-psk")) + key_mgmt_conf = "wpa-psk-sha256"; + else if (nm_streq (key_mgmt_conf, "wpa-eap")) + key_mgmt_conf = "wpa-eap-sha256"; + } + if (!add_string_val (self, key_mgmt_conf, "key_mgmt", TRUE, NULL, error)) return FALSE; auth_alg = nm_setting_wireless_security_get_auth_alg (setting); @@ -750,6 +813,19 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, return FALSE; if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, group, groups, "group", ' ', TRUE, NULL, error)) return FALSE; + + if ( !nm_streq (key_mgmt, "wpa-none") + && NM_IN_SET (pmf, + NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL, + NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)) { + if (!nm_supplicant_config_add_option (self, + "ieee80211w", + pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "1" : "2", + -1, + NULL, + error)) + return FALSE; + } } /* WEP keys if required */ @@ -811,12 +887,6 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, } if (!strcmp (key_mgmt, "wpa-eap")) { - /* If using WPA Enterprise, enable optimized background scanning - * to ensure roaming within an ESS works well. - */ - if (!nm_supplicant_config_add_option (self, "bgscan", "simple:30:-65:300", -1, NULL, error)) - return FALSE; - /* When using WPA-Enterprise, we want to use Proactive Key Caching (also * called Opportunistic Key Caching) to avoid full EAP exchanges when * roaming between access points in the same mobility group. @@ -866,11 +936,11 @@ add_pkcs11_uri_with_pin (NMSupplicantConfig *self, } tmp = g_strdup_printf ("%s%s%s", split[0], - (pin_qattr ? "&" : ""), + (pin_qattr ? "?" : ""), (pin_qattr ? pin_qattr : "")); tmp_log = g_strdup_printf ("%s%s%s", split[0], - (pin_qattr ? "&" : ""), + (pin_qattr ? "?" : ""), (pin_qattr ? "pin-value=<hidden>" : "")); return add_string_val (self, tmp, name, FALSE, tmp_log, error); diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h index 6acfb7ee..d90d82b8 100644 --- a/src/supplicant/nm-supplicant-config.h +++ b/src/supplicant/nm-supplicant-config.h @@ -55,11 +55,16 @@ gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self, guint32 fixed_freq, GError **error); +gboolean nm_supplicant_config_add_bgscan (NMSupplicantConfig *self, + NMConnection *connection, + GError **error); + gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, NMSettingWirelessSecurity *setting, NMSetting8021x *setting_8021x, const char *con_uuid, guint32 mtu, + NMSettingWirelessSecurityPmf pmf, GError **error); gboolean nm_supplicant_config_add_no_security (NMSupplicantConfig *self, @@ -76,4 +81,6 @@ gboolean nm_supplicant_config_add_setting_macsec (NMSupplicantConfig *self, NMSettingMacsec *setting, GError **error); +gboolean nm_supplicant_config_enable_pmf_akm (NMSupplicantConfig *self, + GError **error); #endif /* __NETWORKMANAGER_SUPPLICANT_CONFIG_H__ */ diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index ab8a0670..44f887cb 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2006 - 2012 Red Hat, Inc. + * Copyright (C) 2006 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -31,11 +31,12 @@ #include "nm-core-internal.h" #include "nm-dbus-compat.h" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" -#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" -#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" -#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" +#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" +#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" +#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" +#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" /*****************************************************************************/ @@ -48,6 +49,16 @@ struct _AddNetworkData; typedef struct { NMSupplicantInterface *self; + char *type; + char *bssid; + char *pin; + GDBusProxy *proxy; + GCancellable *cancellable; + bool is_cancelling; +} WpsData; + +typedef struct { + NMSupplicantInterface *self; NMSupplicantConfig *cfg; GCancellable *cancellable; NMSupplicantInterfaceAssocCb callback; @@ -69,6 +80,7 @@ enum { BSS_REMOVED, /* supplicant removed BSS from its scan list */ SCAN_DONE, /* wifi scan is complete */ CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + WPS_CREDENTIALS, /* WPS credentials received */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -80,6 +92,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_DRIVER, PROP_FAST_SUPPORT, PROP_AP_SUPPORT, + PROP_PMF_SUPPORT, ); typedef struct { @@ -88,6 +101,7 @@ typedef struct { gboolean has_credreq; /* Whether querying 802.1x credentials is supported */ NMSupplicantFeature fast_support; NMSupplicantFeature ap_support; /* Lightweight AP mode support */ + NMSupplicantFeature pmf_support; guint32 max_scan_ssids; guint32 ready_count; @@ -105,6 +119,8 @@ typedef struct { GDBusProxy * iface_proxy; GCancellable * other_cancellable; + WpsData *wps_data; + AssocData * assoc_data; char * net_path; @@ -543,6 +559,12 @@ nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ap_support; } +NMSupplicantFeature +nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->pmf_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -565,6 +587,315 @@ nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self, priv->fast_support = fast_support; } +void +nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, + NMSupplicantFeature pmf_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->pmf_support = pmf_support; +} + +/*****************************************************************************/ + +static void +_wps_data_free (WpsData *data) +{ + g_free (data->type); + g_free (data->pin); + g_free (data->bssid); + g_clear_object (&data->cancellable); + if (data->proxy && data->self) + g_signal_handlers_disconnect_by_data (data->proxy, data->self); + g_clear_object (&data->proxy); + g_slice_free (WpsData, data); +} + +static void +_wps_credentials_changed_cb (GDBusProxy *proxy, + GVariant *props, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + _LOGT ("wps: new credentials"); + g_signal_emit (self, signals[WPS_CREDENTIALS], 0, props); +} + +static void +_wps_handle_start_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMSupplicantInterface *self; + WpsData *data; + gs_unref_variant GVariant *result = NULL; + gs_free_error GError *error = NULL; + + result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); + if ( !result + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + data = user_data; + self = data->self; + + if (result) + _LOGT ("wps: started with success"); + else + _LOGW ("wps: start failed with %s", error->message); + + g_clear_object (&data->cancellable); + nm_clear_g_free (&data->type); + nm_clear_g_free (&data->pin); + nm_clear_g_free (&data->bssid); +} + +static void +_wps_handle_set_pc_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + WpsData *data; + NMSupplicantInterface *self; + gs_unref_variant GVariant *result = NULL; + gs_free_error GError *error = NULL; + GVariantBuilder start_args; + guint8 bssid_buf[ETH_ALEN]; + + result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); + if ( !result + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + data = user_data; + self = data->self; + + if (result) + _LOGT ("wps: ProcessCredentials successfully set, starting..."); + else + _LOGW ("wps: ProcessCredentials failed to set (%s), starting...", error->message); + + _nm_dbus_signal_connect (data->proxy, "Credentials", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (_wps_credentials_changed_cb), self); + + g_variant_builder_init (&start_args, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&start_args, "{sv}", "Role", g_variant_new_string ("enrollee")); + g_variant_builder_add (&start_args, "{sv}", "Type", g_variant_new_string (data->type)); + if (data->pin) + g_variant_builder_add (&start_args, "{sv}", "Pin", g_variant_new_string (data->pin)); + + if (data->bssid) { + /* The BSSID is in fact not mandatory. If it is not set the supplicant would + * enroll with any BSS in range. */ + if (!nm_utils_hwaddr_aton (data->bssid, bssid_buf, sizeof (bssid_buf))) + nm_assert_not_reached (); + g_variant_builder_add (&start_args, "{sv}", "Bssid", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid_buf, + ETH_ALEN, sizeof (guint8))); + } + + g_dbus_proxy_call (data->proxy, + "Start", + g_variant_new ("(a{sv})", &start_args), + G_DBUS_CALL_FLAGS_NONE, + -1, + data->cancellable, + _wps_handle_start_cb, + data); +} + +static void +_wps_call_set_pc (WpsData *data) +{ + g_dbus_proxy_call (data->proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE_WPS, + "ProcessCredentials", + g_variant_new_boolean (TRUE)), + G_DBUS_CALL_FLAGS_NONE, + -1, + data->cancellable, + _wps_handle_set_pc_cb, + data); +} + +static void +_wps_handle_proxy_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + WpsData *data; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + data = user_data; + self = data->self; + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!proxy) { + _LOGW ("wps: failure to create D-Bus proxy: %s", error->message); + _wps_data_free (data); + priv->wps_data = NULL; + return; + } + + data->proxy = proxy; + _LOGT ("wps: D-Bus proxy created. set ProcessCredentials..."); + _wps_call_set_pc (data); +} + +static void +_wps_handle_cancel_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + WpsData *data; + gs_unref_variant GVariant *result = NULL; + gs_free_error GError *error = NULL; + + result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); + if ( !result + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + data = user_data; + self = data->self; + + if (!self) { + _wps_data_free (data); + if (result) + _LOGT ("wps: cancel completed successfully, after supplicant interface is gone"); + else + _LOGW ("wps: cancel failed (%s), after supplicant interface is gone", error->message); + return; + } + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + data->is_cancelling = FALSE; + + if (!data->type) { + priv->wps_data = NULL; + _wps_data_free (data); + if (result) + _LOGT ("wps: cancel completed successfully"); + else + _LOGW ("wps: cancel failed (%s)", error->message); + return; + } + + if (result) + _LOGT ("wps: cancel completed successfully, setting ProcessCredentials now..."); + else + _LOGW ("wps: cancel failed (%s), setting ProcessCredentials now...", error->message); + _wps_call_set_pc (data); +} + +static void +_wps_start (NMSupplicantInterface *self, + const char *type, + const char *bssid, + const char *pin) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + WpsData *data = priv->wps_data; + + if (type) + _LOGI ("wps: type %s start...", type); + + if (!data) { + if (!type) + return; + + data = g_slice_new0 (WpsData); + data->self = self; + data->type = g_strdup (type); + data->bssid = g_strdup (bssid); + data->pin = g_strdup (pin); + data->cancellable = g_cancellable_new (); + + priv->wps_data = data; + + _LOGT ("wps: create D-Bus proxy..."); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + WPAS_DBUS_SERVICE, + priv->object_path, + WPAS_DBUS_IFACE_INTERFACE_WPS, + data->cancellable, + _wps_handle_proxy_cb, + data); + return; + } + + g_free (data->type); + g_free (data->bssid); + g_free (data->pin); + data->type = g_strdup (type); + data->bssid = g_strdup (bssid); + data->pin = g_strdup (pin); + + if (!data->proxy) { + if (!type) { + nm_clear_g_cancellable (&data->cancellable); + priv->wps_data = NULL; + _wps_data_free (data); + + _LOGT ("wps: abort creation of D-Bus proxy"); + } else + _LOGT ("wps: new enrollment. Wait for D-Bus proxy..."); + return; + } + + if (data->is_cancelling) + return; + + _LOGT ("wps: cancel previous enrollment..."); + + data->is_cancelling = TRUE; + nm_clear_g_cancellable (&data->cancellable); + data->cancellable = g_cancellable_new (); + g_signal_handlers_disconnect_by_data (data->proxy, self); + g_dbus_proxy_call (data->proxy, + "Cancel", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + data->cancellable, + _wps_handle_cancel_cb, + data); +} + +void +nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, + const char *type, + const char *bssid, + const char *pin) +{ + _wps_start (self, type, bssid, pin); +} + +void +nm_supplicant_interface_cancel_wps (NMSupplicantInterface *self) +{ + _wps_start (self, NULL, NULL, NULL); +} + +/*****************************************************************************/ + static void iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { @@ -786,7 +1117,7 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ /* Scan result aging parameters */ g_dbus_proxy_call (priv->iface_proxy, - "org.freedesktop.DBus.Properties.Set", + DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", WPAS_DBUS_IFACE_INTERFACE, "BSSExpireAge", @@ -797,7 +1128,7 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ NULL, NULL); g_dbus_proxy_call (priv->iface_proxy, - "org.freedesktop.DBus.Properties.Set", + DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", WPAS_DBUS_IFACE_INTERFACE, "BSSExpireCount", @@ -1158,6 +1489,9 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) g_free (priv->net_path); priv->net_path = NULL; } + + /* Cancel any WPS enrollment, if any */ + nm_supplicant_interface_cancel_wps (self); } static void @@ -1455,6 +1789,7 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArr /* Scan parameters */ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "Type", g_variant_new_string ("active")); + g_variant_builder_add (&builder, "{sv}", "AllowRoam", g_variant_new_boolean (FALSE)); if (ssids) { GVariantBuilder ssids_builder; @@ -1561,6 +1896,10 @@ set_property (GObject *object, /* construct-only */ priv->ap_support = g_value_get_int (value); break; + case PROP_PMF_SUPPORT: + /* construct-only */ + priv->pmf_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1573,14 +1912,15 @@ nm_supplicant_interface_init (NMSupplicantInterface * self) NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; - priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bss_data_destroy); + priv->bss_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, bss_data_destroy); } NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, NMSupplicantDriver driver, NMSupplicantFeature fast_support, - NMSupplicantFeature ap_support) + NMSupplicantFeature ap_support, + NMSupplicantFeature pmf_support) { g_return_val_if_fail (ifname != NULL, NULL); @@ -1589,6 +1929,7 @@ nm_supplicant_interface_new (const char *ifname, NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver, NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support, NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support, + NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support, NULL); } @@ -1598,6 +1939,16 @@ dispose (GObject *object) NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + nm_supplicant_interface_cancel_wps (self); + if (priv->wps_data) { + /* we shut down, but an asynchronous Cancel request is pending. + * We don't want to cancel it, so mark wps-data that @self is gone. + * This way, _wps_handle_cancel_cb() knows it must no longer touch + * @self */ + priv->wps_data->self = NULL; + priv->wps_data = NULL; + } + if (priv->assoc_data) { gs_free_error GError *error = NULL; @@ -1670,6 +2021,14 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PMF_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); @@ -1720,5 +2079,12 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); -} + signals[WPS_CREDENTIALS] = + g_signal_new (NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_VARIANT); +} diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index d60d4a54..567cf96f 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2017 Red Hat, Inc. * Copyright (C) 2007 - 2008 Novell, Inc. */ @@ -60,6 +60,7 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT "ap-support" +#define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT "pmf-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" @@ -68,6 +69,7 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED "bss-removed" #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" +#define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; @@ -76,7 +78,8 @@ GType nm_supplicant_interface_get_type (void); NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, NMSupplicantDriver driver, NMSupplicantFeature fast_support, - NMSupplicantFeature ap_support); + NMSupplicantFeature ap_support, + NMSupplicantFeature pmf_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -119,6 +122,7 @@ gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, GError **error); NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -126,4 +130,14 @@ void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self, NMSupplicantFeature fast_support); +void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, + NMSupplicantFeature pmf_support); + +void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, + const char *const type, + const char *bssid, + const char *pin); + +void nm_supplicant_interface_cancel_wps (NMSupplicantInterface *self); + #endif /* __NM_SUPPLICANT_INTERFACE_H__ */ diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 49650ab7..0f2eb63a 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -39,6 +39,7 @@ typedef struct { GSList *ifaces; NMSupplicantFeature fast_support; NMSupplicantFeature ap_support; + NMSupplicantFeature pmf_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -159,7 +160,8 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, iface = nm_supplicant_interface_new (ifname, driver, priv->fast_support, - priv->ap_support); + priv->ap_support, + priv->pmf_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -193,28 +195,37 @@ update_capabilities (NMSupplicantManager *self) * dbus: Add global capabilities property */ priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { array = g_variant_get_strv (value, NULL); priv->ap_support = NM_SUPPLICANT_FEATURE_NO; + priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "pmf")) + priv->pmf_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP check */ - for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) + /* Tell all interfaces about results of the AP/PMF check */ + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); + nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support); + } _LOGD ("AP mode is %ssupported", (priv->ap_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->ap_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("PMF is %ssupported", + (priv->pmf_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->pmf_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -337,6 +348,7 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data) priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; set_running (self, FALSE); } diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c index ce3e46d8..14daf693 100644 --- a/src/supplicant/nm-supplicant-settings-verify.c +++ b/src/supplicant/nm-supplicant-settings-verify.c @@ -71,7 +71,9 @@ static const struct validate_entry validate_table[] = { const char * pairwise_allowed[] = { "CCMP", "TKIP", "NONE", NULL }; const char * group_allowed[] = { "CCMP", "TKIP", "WEP104", "WEP40", NULL }; const char * proto_allowed[] = { "WPA", "RSN", NULL }; -const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", +const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", + "WPA-EAP", "WPA-EAP-SHA256", + "IEEE8021X", "WPA-NONE", "NONE", NULL }; const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL }; const char * eap_allowed[] = { "LEAP", "MD5", "TLS", "PEAP", "TTLS", "SIM", @@ -149,6 +151,7 @@ static const struct Opt opt_table[] = { { "mka_cak", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "mka_ckn", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "macsec_port", TYPE_INT, 1, 65534, FALSE, NULL }, + { "ieee80211w", TYPE_INT, 0, 2, FALSE, NULL }, }; diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index ef6f2c64..4b4a4935 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -42,16 +42,11 @@ validate_opt (const char *detail, GVariant *config, const char *key, OptType val_type, - gconstpointer expected, - size_t expected_len) + gconstpointer expected) { char *config_key; GVariant *config_value; gboolean found = FALSE; - const guint8 *bytes; - gsize len; - const char *s; - const unsigned char *expected_array = expected; GVariantIter iter; g_assert (g_variant_is_of_type (config, G_VARIANT_TYPE_VARDICT)); @@ -61,25 +56,33 @@ validate_opt (const char *detail, if (!strcmp (key, config_key)) { found = TRUE; switch (val_type) { - case TYPE_INT: + case TYPE_INT: { g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_INT32)); g_assert_cmpint (g_variant_get_int32 (config_value), ==, GPOINTER_TO_INT (expected)); break; - case TYPE_BYTES: + } + case TYPE_BYTES: { + const guint8 *expected_bytes; + gsize expected_len = 0; + const guint8 *config_bytes; + gsize config_len = 0; + + expected_bytes = g_bytes_get_data ((GBytes *) expected, &expected_len); g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_BYTESTRING)); - bytes = g_variant_get_fixed_array (config_value, &len, 1); - g_assert_cmpint (len, ==, expected_len); - g_assert (memcmp (bytes, expected_array, expected_len) == 0); + config_bytes = g_variant_get_fixed_array (config_value, &config_len, 1); + g_assert_cmpmem (config_bytes, config_len, expected_bytes, expected_len); break; + } case TYPE_KEYWORD: - case TYPE_STRING: + case TYPE_STRING: { + const char *expected_str = expected; + const char *config_str; + g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_STRING)); - if (expected_len == -1) - expected_len = strlen ((const char *) expected); - s = g_variant_get_string (config_value, NULL); - g_assert_cmpint (strlen (s), ==, expected_len); - g_assert_cmpstr (s, ==, expected); + config_str = g_variant_get_string (config_value, NULL); + g_assert_cmpstr (config_str, ==, expected_str); break; + } default: g_assert_not_reached (); break; @@ -91,43 +94,80 @@ validate_opt (const char *detail, return found; } -static void -test_wifi_open (void) +static GVariant * +build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) { - gs_unref_object NMConnection *connection = NULL; gs_unref_object NMSupplicantConfig *config = NULL; - gs_unref_variant GVariant *config_dict = NULL; + gs_free_error GError *error = NULL; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + gboolean success; + + config = nm_supplicant_config_new (); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + success = nm_supplicant_config_add_setting_wireless (config, + s_wifi, + fixed_freq, + &error); + g_assert_no_error (error); + g_assert (success); + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (s_wsec) { + NMSettingWirelessSecurityPmf pmf = nm_setting_wireless_security_get_pmf (s_wsec); + s_8021x = nm_connection_get_setting_802_1x (connection); + success = nm_supplicant_config_add_setting_wireless_security (config, + s_wsec, + s_8021x, + nm_connection_get_uuid (connection), + mtu, + pmf, + &error); + } else { + success = nm_supplicant_config_add_no_security (config, &error); + } + g_assert_no_error (error); + g_assert (success); + + + success = nm_supplicant_config_add_bgscan (config, connection, &error); + g_assert_no_error (error); + g_assert (success); + + return nm_supplicant_config_to_variant (config); +} + +#define EXPECT(msg) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, msg) + +static NMConnection * +new_basic_connection (const char *id, + GBytes *ssid, + const char *bssid_str) +{ + NMConnection *connection; NMSettingConnection *s_con; NMSettingWireless *s_wifi; NMSettingIPConfig *s_ip4; - char *uuid; - gboolean success; - GError *error = NULL; - GBytes *ssid; - const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; - const char *bssid_str = "11:22:33:44:55:66"; + gs_free char *uuid = nm_utils_uuid_generate (); connection = nm_simple_connection_new (); /* Connection setting */ s_con = (NMSettingConnection *) nm_setting_connection_new (); nm_connection_add_setting (connection, NM_SETTING (s_con)); - - uuid = nm_utils_uuid_generate (); g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Wifi Open", + NM_SETTING_CONNECTION_ID, id, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NULL); - g_free (uuid); /* Wifi setting */ s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); - g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NM_SETTING_WIRELESS_BSSID, bssid_str, @@ -135,162 +175,106 @@ test_wifi_open (void) NM_SETTING_WIRELESS_BAND, "bg", NULL); - g_bytes_unref (ssid); - /* IP4 setting */ s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + return connection; +} - config = nm_supplicant_config_new (); +static void +test_wifi_open (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *config_dict = NULL; + gboolean success; + GError *error = NULL; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); + const char *bssid_str = "11:22:33:44:55:66"; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'ssid' value 'Test SSID'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'scan_ssid' value '1'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'bssid' value '11:22:33:44:55:66'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'freq_list' value *"); - g_assert (nm_supplicant_config_add_setting_wireless (config, - s_wifi, - 0, - &error)); + connection = new_basic_connection ("Test Wifi Open", ssid, bssid_str); + success = nm_connection_verify (connection, &error); g_assert_no_error (error); - g_test_assert_expected_messages (); + g_assert (success); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'key_mgmt' value 'NONE'"); - g_assert (nm_supplicant_config_add_no_security (config, &error)); - g_assert_no_error (error); + EXPECT ("*added 'ssid' value 'Test SSID'*"); + EXPECT ("*added 'scan_ssid' value '1'*"); + EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); + EXPECT ("*added 'freq_list' value *"); + EXPECT ("*added 'key_mgmt' value 'NONE'"); + config_dict = build_supplicant_config (connection, 1500, 0); g_test_assert_expected_messages (); - - config_dict = nm_supplicant_config_to_variant (config); g_assert (config_dict); - validate_opt ("wifi-open", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt ("wifi-open", config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt ("wifi-open", config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt ("wifi-open", config_dict, "key_mgmt", TYPE_KEYWORD, "NONE", -1); + validate_opt ("wifi-open", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt ("wifi-open", config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt ("wifi-open", config_dict, "bssid", TYPE_KEYWORD, bssid_str); + validate_opt ("wifi-open", config_dict, "key_mgmt", TYPE_KEYWORD, "NONE"); } static void test_wifi_wep_key (const char *detail, + gboolean test_bssid, NMWepKeyType wep_type, const char *key_data, const unsigned char *expected, size_t expected_size) { gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMSupplicantConfig *config = NULL; gs_unref_variant GVariant *config_dict = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; - NMSettingIPConfig *s_ip4; - char *uuid; gboolean success; GError *error = NULL; - GBytes *ssid; const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); const char *bssid_str = "11:22:33:44:55:66"; + gs_unref_bytes GBytes *wep_key_bytes = g_bytes_new (expected, expected_size); + const char *bgscan_data = "simple:30:-80:86400"; + gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data)); - connection = nm_simple_connection_new (); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - uuid = nm_utils_uuid_generate (); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - g_free (uuid); - - /* Wifi setting */ - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); - - g_object_set (s_wifi, - NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRELESS_BSSID, bssid_str, - NM_SETTING_WIRELESS_MODE, "infrastructure", - NM_SETTING_WIRELESS_BAND, "bg", - NULL); - - g_bytes_unref (ssid); + connection = new_basic_connection ("Test Wifi WEP Key", ssid, test_bssid ? bssid_str : NULL); /* Wifi Security setting */ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_type, NULL); - nm_setting_wireless_security_set_wep_key (s_wsec, 0, key_data); - - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_setting_wireless_security_set_wep_key (s_wsec, 0, key_data); success = nm_connection_verify (connection, &error); g_assert_no_error (error); g_assert (success); - config = nm_supplicant_config_new (); + EXPECT ("*added 'ssid' value 'Test SSID'*"); + EXPECT ("*added 'scan_ssid' value '1'*"); + if (test_bssid) + EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'ssid' value 'Test SSID'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'scan_ssid' value '1'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'bssid' value '11:22:33:44:55:66'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'freq_list' value *"); - g_assert (nm_supplicant_config_add_setting_wireless (config, - s_wifi, - 0, - &error)); - g_assert_no_error (error); - g_test_assert_expected_messages (); + EXPECT ("*added 'freq_list' value *"); + EXPECT ("*added 'key_mgmt' value 'NONE'"); + EXPECT ("*added 'wep_key0' value *"); + EXPECT ("*added 'wep_tx_keyidx' value '0'"); + if (!test_bssid) + EXPECT ("*added 'bgscan' value 'simple:30:-80:86400'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'key_mgmt' value 'NONE'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'wep_key0' value *"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'wep_tx_keyidx' value '0'"); - g_assert (nm_supplicant_config_add_setting_wireless_security (config, - s_wsec, - NULL, - "376aced7-b28c-46be-9a62-fcdf072571da", - 1500, - &error)); - g_assert_no_error (error); + config_dict = build_supplicant_config (connection, 1500, 0); g_test_assert_expected_messages (); - - config_dict = nm_supplicant_config_to_variant (config); g_assert (config_dict); - validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "NONE", -1); - validate_opt (detail, config_dict, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0), -1); - validate_opt (detail, config_dict, "wep_key0", TYPE_BYTES, expected, expected_size); + validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid); + if (test_bssid) + validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str); + else + validate_opt (detail, config_dict, "bgscan", TYPE_BYTES, bgscan); + + validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "NONE"); + validate_opt (detail, config_dict, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0)); + validate_opt (detail, config_dict, "wep_key0", TYPE_BYTES, wep_key_bytes); } static void @@ -307,13 +291,16 @@ test_wifi_wep (void) const char *key5 = "r34lly l33t w3p p4ssphr4s3 for t3st1ng"; const unsigned char key5_expected[] = { 0xce, 0x68, 0x8b, 0x35, 0xf6, 0x0a, 0x2b, 0xbf, 0xc9, 0x8f, 0xed, 0x10, 0xda }; - test_wifi_wep_key ("wifi-wep-ascii-40", NM_WEP_KEY_TYPE_KEY, key1, key1_expected, sizeof (key1_expected)); - test_wifi_wep_key ("wifi-wep-ascii-104", NM_WEP_KEY_TYPE_KEY, key2, key2_expected, sizeof (key2_expected)); - test_wifi_wep_key ("wifi-wep-hex-40", NM_WEP_KEY_TYPE_KEY, key3, key3_expected, sizeof (key3_expected)); - test_wifi_wep_key ("wifi-wep-hex-104", NM_WEP_KEY_TYPE_KEY, key4, key4_expected, sizeof (key4_expected)); - test_wifi_wep_key ("wifi-wep-passphrase-104", NM_WEP_KEY_TYPE_PASSPHRASE, key5, key5_expected, sizeof (key5_expected)); + test_wifi_wep_key ("wifi-wep-ascii-40", TRUE, NM_WEP_KEY_TYPE_KEY, key1, key1_expected, sizeof (key1_expected)); + test_wifi_wep_key ("wifi-wep-ascii-104", TRUE, NM_WEP_KEY_TYPE_KEY, key2, key2_expected, sizeof (key2_expected)); + test_wifi_wep_key ("wifi-wep-hex-40", TRUE, NM_WEP_KEY_TYPE_KEY, key3, key3_expected, sizeof (key3_expected)); + test_wifi_wep_key ("wifi-wep-hex-104", TRUE, NM_WEP_KEY_TYPE_KEY, key4, key4_expected, sizeof (key4_expected)); + test_wifi_wep_key ("wifi-wep-passphrase-104", TRUE, NM_WEP_KEY_TYPE_PASSPHRASE, key5, key5_expected, sizeof (key5_expected)); + + test_wifi_wep_key ("wifi-wep-old-hex-104", TRUE, NM_WEP_KEY_TYPE_UNKNOWN, key4, key4_expected, sizeof (key4_expected)); - test_wifi_wep_key ("wifi-wep-old-hex-104", NM_WEP_KEY_TYPE_UNKNOWN, key4, key4_expected, sizeof (key4_expected)); + /* Unlocked BSSID to test bgscan */ + test_wifi_wep_key ("wifi-wep-hex-40", FALSE, NM_WEP_KEY_TYPE_KEY, key3, key3_expected, sizeof (key3_expected)); } static void @@ -324,58 +311,25 @@ test_wifi_wpa_psk (const char *detail, size_t expected_size) { gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMSupplicantConfig *config = NULL; gs_unref_variant GVariant *config_dict = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; - NMSettingIPConfig *s_ip4; - char *uuid; gboolean success; GError *error = NULL; - GBytes *ssid; const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); const char *bssid_str = "11:22:33:44:55:66"; + gs_unref_bytes GBytes *wpa_psk_bytes = g_bytes_new (expected, expected_size); - connection = nm_simple_connection_new (); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - uuid = nm_utils_uuid_generate (); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - g_free (uuid); - - /* Wifi setting */ - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); - - g_object_set (s_wifi, - NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRELESS_BSSID, bssid_str, - NM_SETTING_WIRELESS_MODE, "infrastructure", - NM_SETTING_WIRELESS_BAND, "bg", - NULL); - - g_bytes_unref (ssid); + connection = new_basic_connection ("Test Wifi WPA PSK", ssid, bssid_str); /* Wifi Security setting */ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NM_SETTING_WIRELESS_SECURITY_PSK, key_data, + NM_SETTING_WIRELESS_SECURITY_PMF, (int) NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL, NULL); - nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_proto (s_wsec, "rsn"); nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); @@ -383,63 +337,38 @@ test_wifi_wpa_psk (const char *detail, nm_setting_wireless_security_add_group (s_wsec, "tkip"); nm_setting_wireless_security_add_group (s_wsec, "ccmp"); - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - success = nm_connection_verify (connection, &error); g_assert_no_error (error); g_assert (success); - config = nm_supplicant_config_new (); - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'ssid' value 'Test SSID'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'scan_ssid' value '1'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'bssid' value '11:22:33:44:55:66'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'freq_list' value *"); - g_assert (nm_supplicant_config_add_setting_wireless (config, - s_wifi, - 0, - &error)); - g_assert_no_error (error); - g_test_assert_expected_messages (); + EXPECT ("*added 'ssid' value 'Test SSID'*"); + EXPECT ("*added 'scan_ssid' value '1'*"); + EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); + EXPECT ("*added 'freq_list' value *"); + EXPECT ("*added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'"); + EXPECT ("*added 'psk' value *"); + EXPECT ("*added 'proto' value 'WPA RSN'"); + EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); + EXPECT ("*added 'group' value 'TKIP CCMP'"); + EXPECT ("*added 'ieee80211w' value '1'"); + config_dict = build_supplicant_config (connection, 1500, 0); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'key_mgmt' value 'WPA-PSK'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'psk' value *"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'proto' value 'WPA RSN'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'pairwise' value 'TKIP CCMP'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'group' value 'TKIP CCMP'"); - g_assert (nm_supplicant_config_add_setting_wireless_security (config, - s_wsec, - NULL, - "376aced7-b28c-46be-9a62-fcdf072571da", - 1500, - &error)); - g_assert_no_error (error); g_test_assert_expected_messages (); - - config_dict = nm_supplicant_config_to_variant (config); g_assert (config_dict); - validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-PSK", -1); - validate_opt (detail, config_dict, "proto", TYPE_KEYWORD, "WPA RSN", -1); - validate_opt (detail, config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt (detail, config_dict, "group", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt (detail, config_dict, "psk", key_type, expected, expected_size); + validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str); + validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-PSK WPA-PSK-SHA256"); + validate_opt (detail, config_dict, "proto", TYPE_KEYWORD, "WPA RSN"); + validate_opt (detail, config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt (detail, config_dict, "group", TYPE_KEYWORD, "TKIP CCMP"); + if (key_type == TYPE_BYTES) + validate_opt (detail, config_dict, "psk", key_type, wpa_psk_bytes); + else if (key_type == TYPE_STRING) + validate_opt (detail, config_dict, "psk", key_type, expected); + else + g_assert_not_reached (); } static void @@ -456,63 +385,23 @@ test_wifi_wpa_psk_types (void) test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, (gconstpointer) key2, strlen (key2)); } -static void -test_wifi_eap (void) +static NMConnection * +generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_str) { - gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMSupplicantConfig *config = NULL; - gs_unref_variant GVariant *config_dict = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; + NMConnection *connection = NULL; NMSettingWirelessSecurity *s_wsec; NMSetting8021x *s_8021x; - NMSettingIPConfig *s_ip4; - char *uuid; gboolean success; GError *error = NULL; - GBytes *ssid; - const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; - const char *bssid_str = "11:22:33:44:55:66"; - guint32 mtu = 1100; - connection = nm_simple_connection_new (); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - uuid = nm_utils_uuid_generate (); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Wifi EAP-TLS", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - g_free (uuid); - - /* Wifi setting */ - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); - - g_object_set (s_wifi, - NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRELESS_BSSID, bssid_str, - NM_SETTING_WIRELESS_MODE, "infrastructure", - NM_SETTING_WIRELESS_BAND, "bg", - NULL); - - g_bytes_unref (ssid); + connection = new_basic_connection (id, ssid, bssid_str); /* Wifi Security setting */ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL); - nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_proto (s_wsec, "rsn"); nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); @@ -528,74 +417,92 @@ test_wifi_eap (void) g_assert (nm_setting_802_1x_set_ca_cert (s_8021x, TEST_CERT_DIR "/test-ca-cert.pem", NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL)); nm_setting_802_1x_set_private_key (s_8021x, TEST_CERT_DIR "/test-cert.p12", NULL, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL); - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - success = nm_connection_verify (connection, &error); g_assert_no_error (error); g_assert (success); - config = nm_supplicant_config_new (); + return connection; +} - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'ssid' value 'Test SSID'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'scan_ssid' value '1'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'bssid' value '11:22:33:44:55:66'*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'freq_list' value *"); - g_assert (nm_supplicant_config_add_setting_wireless (config, - s_wifi, - 0, - &error)); - g_assert_no_error (error); - g_test_assert_expected_messages (); +static void +test_wifi_eap_locked_bssid (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *config_dict = NULL; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); + const char *bssid_str = "11:22:33:44:55:66"; + guint32 mtu = 1100; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'key_mgmt' value 'WPA-EAP'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'proto' value 'WPA RSN'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'pairwise' value 'TKIP CCMP'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*added 'group' value 'TKIP CCMP'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*Config: added 'eap' value 'TLS'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*Config: added 'fragment_size' value '1086'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "* Config: added 'ca_cert' value '*/test-ca-cert.pem'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "* Config: added 'private_key' value '*/test-cert.p12'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*Config: added 'bgscan' value 'simple:30:-65:300'"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*Config: added 'proactive_key_caching' value '1'"); - g_assert (nm_supplicant_config_add_setting_wireless_security (config, - s_wsec, - s_8021x, - "d5b488af-9cab-41ed-bad4-97709c58430f", - mtu, - &error)); - g_assert_no_error (error); + connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Locked", ssid, bssid_str); + + EXPECT ("*added 'ssid' value 'Test SSID'*"); + EXPECT ("*added 'scan_ssid' value '1'*"); + EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); + EXPECT ("*added 'freq_list' value *"); + EXPECT ("*added 'key_mgmt' value 'WPA-EAP'"); + EXPECT ("*added 'proto' value 'WPA RSN'"); + EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); + EXPECT ("*added 'group' value 'TKIP CCMP'"); + EXPECT ("*Config: added 'eap' value 'TLS'"); + EXPECT ("*Config: added 'fragment_size' value '1086'"); + EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'"); + EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'"); + EXPECT ("*Config: added 'proactive_key_caching' value '1'"); + config_dict = build_supplicant_config (connection, mtu, 0); g_test_assert_expected_messages (); + g_assert (config_dict); + + validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt ("wifi-eap", config_dict, "bssid", TYPE_KEYWORD, bssid_str); + validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP"); + validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS"); + validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN"); + validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "fragment_size", TYPE_INT, GINT_TO_POINTER(mtu-14)); +} - config_dict = nm_supplicant_config_to_variant (config); +static void +test_wifi_eap_unlocked_bssid (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *config_dict = NULL; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); + const char *bgscan_data = "simple:30:-65:300"; + gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data)); + guint32 mtu = 1100; + + connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Unlocked", ssid, NULL); + + EXPECT ("*added 'ssid' value 'Test SSID'*"); + EXPECT ("*added 'scan_ssid' value '1'*"); + EXPECT ("*added 'freq_list' value *"); + EXPECT ("*added 'key_mgmt' value 'WPA-EAP'"); + EXPECT ("*added 'proto' value 'WPA RSN'"); + EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); + EXPECT ("*added 'group' value 'TKIP CCMP'"); + EXPECT ("*Config: added 'eap' value 'TLS'"); + EXPECT ("*Config: added 'fragment_size' value '1086'"); + EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'"); + EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'"); + EXPECT ("*Config: added 'proactive_key_caching' value '1'"); + EXPECT ("*Config: added 'bgscan' value 'simple:30:-65:300'"); + config_dict = build_supplicant_config (connection, mtu, 0); + g_test_assert_expected_messages (); g_assert (config_dict); - validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt ("wifi-eap", config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP", -1); - validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS", -1); - validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN", -1); - validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt ("wifi-eap", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt ("wifi-eap", config_dict, "fragment_size", TYPE_INT, GINT_TO_POINTER(mtu-14), -1); + validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP"); + validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS"); + validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN"); + validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "fragment_size", TYPE_INT, GINT_TO_POINTER(mtu-14)); + validate_opt ("wifi-eap", config_dict, "bgscan", TYPE_BYTES, bgscan); } NMTST_DEFINE (); @@ -607,8 +514,8 @@ int main (int argc, char **argv) g_test_add_func ("/supplicant-config/wifi-open", test_wifi_open); g_test_add_func ("/supplicant-config/wifi-wep", test_wifi_wep); g_test_add_func ("/supplicant-config/wifi-wpa-psk-types", test_wifi_wpa_psk_types); - g_test_add_func ("/supplicant-config/wifi-eap", test_wifi_eap); + g_test_add_func ("/supplicant-config/wifi-eap/locked-bssid", test_wifi_eap_locked_bssid); + g_test_add_func ("/supplicant-config/wifi-eap/unlocked-bssid", test_wifi_eap_unlocked_bssid); return g_test_run (); } - diff --git a/src/systemd/sd-adapt/process-util.h b/src/systemd/sd-adapt/architecture.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/process-util.h +++ b/src/systemd/sd-adapt/architecture.h diff --git a/src/systemd/sd-adapt/btrfs-util.h b/src/systemd/sd-adapt/btrfs-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/btrfs-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/ioprio.h b/src/systemd/sd-adapt/ioprio.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/ioprio.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h index a8ff18bc..0d291e26 100644 --- a/src/systemd/sd-adapt/nm-sd-adapt.h +++ b/src/systemd/sd-adapt/nm-sd-adapt.h @@ -32,12 +32,24 @@ #define CLOCK_BOOTTIME 7 #endif +#if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1 +#define HAVE_EXPLICIT_BZERO 1 +#else +#define HAVE_EXPLICIT_BZERO 0 +#endif + +#define ENABLE_DEBUG_HASHMAP 0 + +#ifndef HAVE_SYS_AUXV_H +#define HAVE_SYS_AUXV_H 0 +#endif + /*****************************************************************************/ static inline NMLogLevel _slog_level_to_nm (int slevel) { - switch (slevel) { + switch (LOG_PRI (slevel)) { case LOG_DEBUG: return LOGL_DEBUG; case LOG_WARNING: return LOGL_WARN; case LOG_CRIT: @@ -48,7 +60,15 @@ _slog_level_to_nm (int slevel) } } -#define log_internal(level, error, file, line, func, format, ...) \ +static inline int +_nm_log_get_max_level_realm (void) +{ + /* inline function, to avoid coverity warning about constant expression. */ + return LOG_DEBUG; +} +#define log_get_max_level_realm(realm) _nm_log_get_max_level_realm () + +#define log_internal_realm(level, error, file, line, func, format, ...) \ ({ \ const int _nm_e = (error); \ const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \ @@ -61,11 +81,6 @@ _slog_level_to_nm (int slevel) (_nm_e > 0 ? -_nm_e : _nm_e); \ }) -#define log_full_errno(level, error, ...) \ -({ \ - log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \ -}) - #define log_assert_failed(text, file, line, func) \ G_STMT_START { \ log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \ @@ -171,10 +186,6 @@ static inline pid_t gettid(void) { return (pid_t) syscall(SYS_gettid); } -static inline bool is_main_thread(void) { - return TRUE; -} - #endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */ #endif /* NM_SD_ADAPT_H */ diff --git a/src/systemd/sd-adapt/raw-clone.h b/src/systemd/sd-adapt/raw-clone.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/raw-clone.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/udev.h b/src/systemd/sd-adapt/udev.h index 00f60f92..419abcbf 100644 --- a/src/systemd/sd-adapt/udev.h +++ b/src/systemd/sd-adapt/udev.h @@ -3,3 +3,4 @@ /* dummy header */ #include "libudev.h" +#include "strv.h" diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c index 5fae60c9..97588312 100644 --- a/src/systemd/src/basic/alloc-util.c +++ b/src/systemd/src/basic/alloc-util.c @@ -27,16 +27,31 @@ #include "util.h" void* memdup(const void *p, size_t l) { - void *r; + void *ret; - assert(p); + assert(l == 0 || p); + + ret = malloc(l); + if (!ret) + return NULL; + + memcpy(ret, p, l); + return ret; +} + +void* memdup_suffix0(const void*p, size_t l) { + void *ret; + + assert(l == 0 || p); + + /* The same as memdup() but place a safety NUL byte after the allocated memory */ - r = malloc(l); - if (!r) + ret = malloc(l + 1); + if (!ret) return NULL; - memcpy(r, p, l); - return r; + *((uint8_t*) mempcpy(ret, p, l)) = 0; + return ret; } void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index a44dd473..0a89691b 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -36,6 +36,8 @@ #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) +#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) + #define malloc0(n) (calloc(1, (n))) static inline void *mfree(void *memory) { @@ -52,6 +54,7 @@ static inline void *mfree(void *memory) { }) void* memdup(const void *p, size_t l) _alloc_(2); +void* memdup_suffix0(const void*p, size_t l) _alloc_(2); static inline void freep(void *p) { free(*(void**) p); @@ -84,6 +87,13 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si return memdup(p, size * need); } +_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) + return NULL; + + return memdup_suffix0(p, size * need); +} + void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c index ac96f0ee..27a20702 100644 --- a/src/systemd/src/basic/escape.c +++ b/src/systemd/src/basic/escape.c @@ -316,7 +316,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi /* Undoes C style string escaping, and optionally prefixes it. */ - pl = prefix ? strlen(prefix) : 0; + pl = strlen_ptr(prefix); r = new(char, pl+length+1); if (!r) @@ -428,7 +428,7 @@ char *octescape(const char *s, size_t len) { for (f = s, t = r; f < s + len; f++) { - if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { + if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { *(t++) = '\\'; *(t++) = '0' + (*f >> 6); *(t++) = '0' + ((*f >> 3) & 8); @@ -443,10 +443,16 @@ char *octescape(const char *s, size_t len) { } -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { +static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { assert(bad); for (; *s; s++) { + if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { + *(t++) = '\\'; + *(t++) = *s == '\n' ? 'n' : 't'; + continue; + } + if (*s == '\\' || strchr(bad, *s)) *(t++) = '\\'; @@ -463,20 +469,21 @@ char *shell_escape(const char *s, const char *bad) { if (!r) return NULL; - t = strcpy_backslash_escaped(r, s, bad); + t = strcpy_backslash_escaped(r, s, bad, false); *t = 0; return r; } -char *shell_maybe_quote(const char *s) { +char* shell_maybe_quote(const char *s, EscapeStyle style) { const char *p; char *r, *t; assert(s); - /* Encloses a string in double quotes if necessary to make it - * OK as shell string. */ + /* Encloses a string in quotes if necessary to make it OK as a shell + * string. Note that we treat benign UTF-8 characters as needing + * escaping too, but that should be OK. */ for (p = s; *p; p++) if (*p <= ' ' || @@ -487,17 +494,30 @@ char *shell_maybe_quote(const char *s) { if (!*p) return strdup(s); - r = new(char, 1+strlen(s)*2+1+1); + r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); if (!r) return NULL; t = r; - *(t++) = '"'; + if (style == ESCAPE_BACKSLASH) + *(t++) = '"'; + else if (style == ESCAPE_POSIX) { + *(t++) = '$'; + *(t++) = '\''; + } else + assert_not_reached("Bad EscapeStyle"); + t = mempcpy(t, s, p - s); - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE); + if (style == ESCAPE_BACKSLASH) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + else + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - *(t++)= '"'; + if (style == ESCAPE_BACKSLASH) + *(t++) = '"'; + else + *(t++) = '\''; *t = 0; return r; diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h index 24729dc1..e62347af 100644 --- a/src/systemd/src/basic/escape.h +++ b/src/systemd/src/basic/escape.h @@ -33,13 +33,30 @@ /* What characters are special in the shell? */ /* must be escaped outside and inside double-quotes */ #define SHELL_NEED_ESCAPE "\"\\`$" -/* can be escaped or double-quoted */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;" + +/* Those that can be escaped or double-quoted. + * + * Stricly speaking, ! does not need to be escaped, except in interactive + * mode, but let's be extra nice to the user and quote ! in case this + * output is ever used in interactive mode. */ +#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" + +/* Note that we assume control characters would need to be escaped too in + * addition to the "special" characters listed here, if they appear in the + * string. Current users disallow control characters. Also '"' shall not + * be escaped. + */ +#define SHELL_NEED_ESCAPE_POSIX "\\\'" typedef enum UnescapeFlags { UNESCAPE_RELAX = 1, } UnescapeFlags; +typedef enum EscapeStyle { + ESCAPE_BACKSLASH = 1, + ESCAPE_POSIX = 2, +} EscapeStyle; + char *cescape(const char *s); char *cescape_length(const char *s, size_t n); size_t cescape_char(char c, char *buf); @@ -53,4 +70,4 @@ char *xescape(const char *s, const char *bad); char *octescape(const char *s, size_t len); char *shell_escape(const char *s, const char *bad); -char *shell_maybe_quote(const char *s); +char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c index 734712dc..69d8c48d 100644 --- a/src/systemd/src/basic/extract-word.c +++ b/src/systemd/src/basic/extract-word.c @@ -154,7 +154,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; - else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { + else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) { quote = c; break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { @@ -244,7 +244,12 @@ int extract_first_word_and_warn( return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); } -int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) { +/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing + * an object that undergoes default argument promotion as an argument to va_start). + * Let's make sure that ExtractFlags fits into an unsigned int. */ +assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); + +int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { va_list ap; char **l; int n = 0, i, c, r; diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h index 21db5ef3..04746c6d 100644 --- a/src/systemd/src/basic/extract-word.h +++ b/src/systemd/src/basic/extract-word.h @@ -32,4 +32,4 @@ typedef enum ExtractFlags { int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_; +int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index d1c988e1..1c327d83 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -33,6 +33,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "stdio-util.h" #include "util.h" @@ -285,7 +286,7 @@ int same_fd(int a, int b) { return true; /* Try to use kcmp() if we have it. */ - pid = getpid(); + pid = getpid_cached(); r = kcmp(pid, pid, KCMP_FILE, a, b); if (r == 0) return true; diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 711580a4..51d1c052 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -32,6 +32,7 @@ #include "alloc-util.h" #include "ctype.h" +#include "def.h" #include "env-util.h" #include "escape.h" #include "fd-util.h" @@ -43,6 +44,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "random-util.h" #include "stdio-util.h" #include "string-util.h" @@ -53,19 +55,39 @@ #define READ_FULL_BYTES_MAX (4U*1024U*1024U) -int write_string_stream(FILE *f, const char *line, bool enforce_newline) { +#if 0 /* NM_IGNORED */ +int write_string_stream_ts( + FILE *f, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { assert(f); assert(line); fputs(line, f); - if (enforce_newline && !endswith(line, "\n")) + if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) fputc('\n', f); - return fflush_and_check(f); + if (ts) { + struct timespec twice[2] = {*ts, *ts}; + + if (futimens(fileno(f), twice) < 0) + return -errno; + } + + if (flags & WRITE_STRING_FILE_SYNC) + return fflush_sync_and_check(f); + else + return fflush_and_check(f); } -static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) { +static int write_string_file_atomic( + const char *fn, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { + _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; int r; @@ -79,34 +101,47 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor (void) fchmod_umask(fileno(f), 0644); - r = write_string_stream(f, line, enforce_newline); - if (r >= 0) { - if (rename(p, fn) < 0) - r = -errno; + r = write_string_stream_ts(f, line, flags, ts); + if (r < 0) + goto fail; + + if (rename(p, fn) < 0) { + r = -errno; + goto fail; } - if (r < 0) - (void) unlink(p); + return 0; +fail: + (void) unlink(p); return r; } -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { +int write_string_file_ts( + const char *fn, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { + _cleanup_fclose_ FILE *f = NULL; int q, r; assert(fn); assert(line); + /* We don't know how to verify whether the file contents was already on-disk. */ + assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); + if (flags & WRITE_STRING_FILE_ATOMIC) { assert(flags & WRITE_STRING_FILE_CREATE); - r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_file_atomic(fn, line, flags, ts); if (r < 0) goto fail; return r; - } + } else + assert(ts == NULL); if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); @@ -133,7 +168,7 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla } } - r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_stream_ts(f, line, flags, ts); if (r < 0) goto fail; @@ -157,7 +192,7 @@ fail: int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; - char t[LINE_MAX], *c; + int r; assert(fn); assert(line); @@ -166,22 +201,10 @@ int read_one_line_file(const char *fn, char **line) { if (!f) return -errno; - if (!fgets(t, sizeof(t), f)) { - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - t[0] = 0; - } - - c = strdup(t); - if (!c) - return -ENOMEM; - truncate_nl(c); - - *line = c; - return 0; + r = read_line(f, LONG_LINE_MAX, line); + return r < 0 ? r : 0; } +#endif /* NM_IGNORED */ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { _cleanup_fclose_ FILE *f = NULL; @@ -239,11 +262,11 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { if (st.st_size > READ_FULL_BYTES_MAX) return -E2BIG; - /* Start with the right file size, but be prepared for - * files from /proc which generally report a file size - * of 0 */ + /* Start with the right file size, but be prepared for files from /proc which generally report a file + * size of 0. Note that we increase the size to read here by one, so that the first read attempt + * already makes us notice the EOF. */ if (st.st_size > 0) - n = st.st_size; + n = st.st_size + 1; } l = 0; @@ -256,12 +279,13 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { return -ENOMEM; buf = t; + errno = 0; k = fread(buf + l, 1, n - l, f); if (k > 0) l += k; if (ferror(f)) - return -errno; + return errno > 0 ? -errno : -EIO; if (feof(f)) break; @@ -818,29 +842,29 @@ static void write_env_var(FILE *f, const char *v) { p = strchr(v, '='); if (!p) { /* Fallback */ - fputs(v, f); - fputc('\n', f); + fputs_unlocked(v, f); + fputc_unlocked('\n', f); return; } p++; - fwrite(v, 1, p-v, f); + fwrite_unlocked(v, 1, p-v, f); if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc('\"', f); + fputc_unlocked('\"', f); for (; *p; p++) { if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc('\\', f); + fputc_unlocked('\\', f); - fputc(*p, f); + fputc_unlocked(*p, f); } - fputc('\"', f); + fputc_unlocked('\"', f); } else - fputs(p, f); + fputs_unlocked(p, f); - fputc('\n', f); + fputc_unlocked('\n', f); } int write_env_file(const char *fname, char **l) { @@ -871,7 +895,6 @@ int write_env_file(const char *fname, char **l) { unlink(p); return r; } -#endif /* NM_IGNORED */ int executable_is_script(const char *path, char **interpreter) { int r; @@ -901,6 +924,7 @@ int executable_is_script(const char *path, char **interpreter) { *interpreter = ans; return 1; } +#endif /* NM_IGNORED */ /** * Retrieve one field from a file like /proc/self/status. pattern @@ -1123,6 +1147,21 @@ int fflush_and_check(FILE *f) { return 0; } +int fflush_sync_and_check(FILE *f) { + int r; + + assert(f); + + r = fflush_and_check(f); + if (r < 0) + return r; + + if (fsync(fileno(f)) < 0) + return -errno; + + return 0; +} + /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { _cleanup_umask_ mode_t u = 0; @@ -1171,6 +1210,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { return 0; } +#if 0 /* NM_IGNORED */ int tempfn_random(const char *p, const char *extra, char **ret) { const char *fn; char *t, *x; @@ -1213,7 +1253,6 @@ int tempfn_random(const char *p, const char *extra, char **ret) { return 0; } -#if 0 /* NM_IGNORED */ int tempfn_random_child(const char *p, const char *extra, char **ret) { char *t, *x; uint64_t u; @@ -1400,7 +1439,7 @@ int open_serialization_fd(const char *ident) { if (fd < 0) { const char *path; - path = getpid() == 1 ? "/run/systemd" : "/tmp"; + path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); if (fd < 0) return fd; @@ -1498,4 +1537,78 @@ int mkdtemp_malloc(const char *template, char **ret) { *ret = p; return 0; } + +static inline void funlockfilep(FILE **f) { + funlockfile(*f); +} + +int read_line(FILE *f, size_t limit, char **ret) { + _cleanup_free_ char *buffer = NULL; + size_t n = 0, allocated = 0, count = 0; + + assert(f); + + /* Something like a bounded version of getline(). + * + * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string + * returned. + * + * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from + * the number of characters in the returned string). When EOF is hit, 0 is returned. + * + * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding + * delimiters. If the limit is hit we fail and return -ENOBUFS. + * + * If a line shall be skipped ret may be initialized as NULL. */ + + if (ret) { + if (!GREEDY_REALLOC(buffer, allocated, 1)) + return -ENOMEM; + } + + { + _cleanup_(funlockfilep) FILE *flocked = f; + flockfile(f); + + for (;;) { + int c; + + if (n >= limit) + return -ENOBUFS; + + errno = 0; + c = fgetc_unlocked(f); + if (c == EOF) { + /* if we read an error, and have no data to return, then propagate the error */ + if (ferror_unlocked(f) && n == 0) + return errno > 0 ? -errno : -EIO; + + break; + } + + count++; + + if (IN_SET(c, '\n', 0)) /* Reached a delimiter */ + break; + + if (ret) { + if (!GREEDY_REALLOC(buffer, allocated, n + 2)) + return -ENOMEM; + + buffer[n] = (char) c; + } + + n++; + } + } + + if (ret) { + buffer[n] = 0; + + *ret = buffer; + buffer = NULL; + } + + return (int) count; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index e547614c..eba05be2 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -29,14 +29,21 @@ #include "time-util.h" typedef enum { - WRITE_STRING_FILE_CREATE = 1, - WRITE_STRING_FILE_ATOMIC = 2, - WRITE_STRING_FILE_AVOID_NEWLINE = 4, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, + WRITE_STRING_FILE_CREATE = 1<<0, + WRITE_STRING_FILE_ATOMIC = 1<<1, + WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3, + WRITE_STRING_FILE_SYNC = 1<<4, } WriteStringFileFlags; -int write_string_stream(FILE *f, const char *line, bool enforce_newline); -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); +int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); +static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { + return write_string_stream_ts(f, line, flags, NULL); +} +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); +static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { + return write_string_file_ts(fn, line, flags, NULL); +} int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); @@ -71,6 +78,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root } else int fflush_and_check(FILE *f); +int fflush_sync_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern); @@ -93,3 +101,5 @@ int link_tmpfile(int fd, const char *path, const char *target); int read_nul_string(FILE *f, char **ret); int mkdtemp_malloc(const char *template, char **ret); + +int read_line(FILE *f, size_t limit, char **ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index 5e980329..ff4ad5ab 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <linux/magic.h> #include <time.h> #include <unistd.h> @@ -314,7 +315,7 @@ int fd_warn_permissions(const char *path, int fd) { if (st.st_mode & 0002) log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - if (getpid() == 1 && (st.st_mode & 0044) != 0044) + if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; @@ -330,7 +331,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi mkdir_parents(path, 0755); fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - (mode == 0 || mode == MODE_INVALID) ? 0644 : mode); + IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); if (fd < 0) return -errno; @@ -365,22 +366,25 @@ int touch(const char *path) { } int symlink_idempotent(const char *from, const char *to) { - _cleanup_free_ char *p = NULL; int r; assert(from); assert(to); if (symlink(from, to) < 0) { + _cleanup_free_ char *p = NULL; + if (errno != EEXIST) return -errno; r = readlink_malloc(to, &p); - if (r < 0) + if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ + return -EEXIST; + if (r < 0) /* Any other error? In that case propagate it as is */ return r; - if (!streq(p, from)) - return -EINVAL; + if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ + return -EEXIST; } return 0; @@ -728,6 +732,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; + if ((flags & CHASE_NO_AUTOFS) && + fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0) + return -EREMOTE; if (S_ISLNK(st.st_mode)) { char *joined; diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index 094acf17..d3342d5c 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -81,6 +81,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */ }; int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index dc6bcab0..c95d4991 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -36,7 +36,7 @@ #include "strv.h" #include "util.h" -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP #include <pthread.h> #include "list.h" #endif @@ -144,7 +144,7 @@ typedef uint8_t dib_raw_t; #define DIB_FREE UINT_MAX -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP struct hashmap_debug_info { LIST_FIELDS(struct hashmap_debug_info, debug_list); unsigned max_entries; /* high watermark of n_entries */ @@ -501,7 +501,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) { dibs = dib_raw_ptr(h); assert(dibs[idx] != DIB_RAW_FREE); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.rem_count++; h->debug.last_rem_idx = idx; #endif @@ -510,7 +510,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) { /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ for (right = next_idx(h, left); ; right = next_idx(h, right)) { raw_dib = dibs[right]; - if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) + if (IN_SET(raw_dib, 0, DIB_RAW_FREE)) break; /* The buckets are not supposed to be all occupied and with DIB > 0. @@ -580,7 +580,7 @@ static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator * assert(e->p.b.key == i->next_key); } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif @@ -637,7 +637,7 @@ static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { } idx = i->idx; -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif @@ -660,7 +660,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { return IDX_NIL; } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP if (i->idx == IDX_FIRST) { i->put_count = h->debug.put_count; i->rem_count = h->debug.rem_count; @@ -752,7 +752,7 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu shared_hash_key_initialized= true; } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.func = func; h->debug.file = file; h->debug.line = line; @@ -809,7 +809,7 @@ static void hashmap_free_no_clear(HashmapBase *h) { assert(!h->has_indirect); assert(!h->n_direct_entries); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); @@ -921,7 +921,7 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, dib_raw_t raw_dib, *dibs; unsigned dib, distance; -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.put_count++; #endif @@ -929,7 +929,7 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, for (distance = 0; ; distance++) { raw_dib = dibs[idx]; - if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { + if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) { if (raw_dib == DIB_RAW_REHASH) bucket_move_entry(h, swap, idx, IDX_TMP); @@ -1014,7 +1014,7 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, assert_se(hashmap_put_robin_hood(h, idx, swap) == false); n_entries_inc(h); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); #endif @@ -1242,7 +1242,7 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) { idx = bucket_scan(h, hash, key); if (idx != IDX_NIL) { e = plain_bucket_at(h, idx); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP /* Although the key is equal, the key pointer may have changed, * and this would break our assumption for iterating. So count * this operation as incompatible with iteration. */ diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index 6d1ae48b..c1089652 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -58,7 +58,7 @@ typedef struct Set Set; /* Stores just keys */ typedef struct { unsigned idx; /* index of an entry to be iterated next */ const void *next_key; /* expected value of that entry's key pointer */ -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP unsigned put_count; /* hashmap's put_count recorded at start of iteration */ unsigned rem_count; /* hashmap's rem_count in previous iteration */ unsigned prev_idx; /* idx in previous iteration */ @@ -89,7 +89,7 @@ typedef struct { (Hashmap*)(h), \ (void)0) -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP # define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line # define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ # define HASHMAP_DEBUG_PASS_ARGS , func, file, line diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c index 2da8f8a2..e0ae83c1 100644 --- a/src/systemd/src/basic/hexdecoct.c +++ b/src/systemd/src/basic/hexdecoct.c @@ -27,6 +27,7 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "string-util.h" #include "util.h" char octchar(int x) { @@ -571,7 +572,7 @@ static int base64_append_width(char **prefix, int plen, lines = (len + width - 1) / width; - slen = sep ? strlen(sep) : 0; + slen = strlen_ptr(sep); t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); if (!t) return -ENOMEM; diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index 823aa26a..be6e9e58 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -94,9 +94,7 @@ static bool hostname_valid_char(char c) { (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - c == '-' || - c == '_' || - c == '.'; + IN_SET(c, '-', '_', '.'); } /** @@ -201,8 +199,11 @@ bool is_gateway_hostname(const char *hostname) { * synthetic "gateway" host. */ return - strcaseeq(hostname, "gateway") || - strcaseeq(hostname, "gateway."); + strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.") +#if ENABLE_COMPAT_GATEWAY_HOSTNAME + || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.") +#endif + ; } int sethostname_idempotent(const char *s) { @@ -237,7 +238,7 @@ int read_hostname_config(const char *path, char **hostname) { /* may have comments, ignore them */ FOREACH_LINE(l, f, return -errno) { truncate_nl(l); - if (l[0] != '\0' && l[0] != '#') { + if (!IN_SET(l[0], '\0', '#')) { /* found line with value */ name = hostname_cleanup(l); name = strdup(name); diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c index 1140ca76..2a02d90b 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -310,22 +310,22 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { return 0; } -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { +int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) { int r; assert(s); r = in_addr_from_string(AF_INET, s, ret); if (r >= 0) { - if (family) - *family = AF_INET; + if (ret_family) + *ret_family = AF_INET; return 0; } r = in_addr_from_string(AF_INET6, s, ret); if (r >= 0) { - if (family) - *family = AF_INET6; + if (ret_family) + *ret_family = AF_INET6; return 0; } @@ -373,13 +373,13 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u return r; } -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { +unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); return 32 - u32ctz(be32toh(addr->s_addr)); } -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { +struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { assert(addr); assert(prefixlen <= 32); @@ -392,7 +392,7 @@ struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char return addr; } -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { +int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; /* addr may not be aligned, so make sure we only access it byte-wise */ @@ -416,28 +416,29 @@ int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixl return 0; } -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { +int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { unsigned char prefixlen; int r; assert(addr); assert(mask); - r = in_addr_default_prefixlen(addr, &prefixlen); + r = in4_addr_default_prefixlen(addr, &prefixlen); if (r < 0) return r; - in_addr_prefixlen_to_netmask(mask, prefixlen); + in4_addr_prefixlen_to_netmask(mask, prefixlen); return 0; } +#if 0 /* NM_IGNORED */ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { assert(addr); if (family == AF_INET) { struct in_addr mask; - if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) + if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) return -EINVAL; addr->in.s_addr &= mask.s_addr; @@ -466,3 +467,128 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) return -EAFNOSUPPORT; } + +int in_addr_prefix_covers(int family, + const union in_addr_union *prefix, + unsigned char prefixlen, + const union in_addr_union *address) { + + union in_addr_union masked_prefix, masked_address; + int r; + + assert(prefix); + assert(address); + + masked_prefix = *prefix; + r = in_addr_mask(family, &masked_prefix, prefixlen); + if (r < 0) + return r; + + masked_address = *address; + r = in_addr_mask(family, &masked_address, prefixlen); + if (r < 0) + return r; + + return in_addr_equal(family, &masked_prefix, &masked_address); +} + +int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { + uint8_t u; + int r; + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + r = safe_atou8(p, &u); + if (r < 0) + return r; + + if (u > FAMILY_ADDRESS_SIZE(family) * 8) + return -ERANGE; + + *ret = u; + return 0; +} + +int in_addr_prefix_from_string( + const char *p, + int family, + union in_addr_union *ret_prefix, + unsigned char *ret_prefixlen) { + + union in_addr_union buffer; + const char *e, *l; + unsigned char k; + int r; + + assert(p); + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + e = strchr(p, '/'); + if (e) + l = strndupa(p, e - p); + else + l = p; + + r = in_addr_from_string(family, l, &buffer); + if (r < 0) + return r; + + if (e) { + r = in_addr_parse_prefixlen(family, e+1, &k); + if (r < 0) + return r; + } else + k = FAMILY_ADDRESS_SIZE(family) * 8; + + if (ret_prefix) + *ret_prefix = buffer; + if (ret_prefixlen) + *ret_prefixlen = k; + + return 0; +} + +int in_addr_prefix_from_string_auto( + const char *p, + int *ret_family, + union in_addr_union *ret_prefix, + unsigned char *ret_prefixlen) { + + union in_addr_union buffer; + const char *e, *l; + unsigned char k; + int family, r; + + assert(p); + + e = strchr(p, '/'); + if (e) + l = strndupa(p, e - p); + else + l = p; + + r = in_addr_from_string_auto(l, &family, &buffer); + if (r < 0) + return r; + + if (e) { + r = in_addr_parse_prefixlen(family, e+1, &k); + if (r < 0) + return r; + } else + k = FAMILY_ADDRESS_SIZE(family) * 8; + + if (ret_family) + *ret_family = family; + if (ret_prefix) + *ret_prefix = buffer; + if (ret_prefixlen) + *ret_prefixlen = k; + + return 0; + +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h index 51a5aa67..59f8eb7e 100644 --- a/src/systemd/src/basic/in-addr-util.h +++ b/src/systemd/src/basic/in-addr-util.h @@ -53,16 +53,20 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); +int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); +unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); +struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); +int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); +int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); +int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); +int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); +int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); +int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(family == AF_INET || family == AF_INET6); + assert(IN_SET(family, AF_INET, AF_INET6)); return family == AF_INET6 ? 16 : 4; } diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h index 4684ed3b..d9b69add 100644 --- a/src/systemd/src/basic/io-util.h +++ b/src/systemd/src/basic/io-util.h @@ -40,14 +40,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); -#define IOVEC_SET_STRING(i, s) \ - do { \ - struct iovec *_i = &(i); \ - char *_s = (char *)(s); \ - _i->iov_base = _s; \ - _i->iov_len = strlen(_s); \ - } while (false) - static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { unsigned j; size_t r = 0; @@ -93,3 +85,8 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { return FILE_SIZE_VALID(l); } + +#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) } +#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) +#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) +#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h index d8335d12..67fda3f9 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -30,6 +30,17 @@ #include "sd-id128.h" #include "macro.h" +#include "process-util.h" + +typedef enum LogRealm { + LOG_REALM_SYSTEMD, + LOG_REALM_UDEV, + _LOG_REALM_MAX, +} LogRealm; + +#ifndef LOG_REALM +# define LOG_REALM LOG_REALM_SYSTEMD +#endif typedef enum LogTarget{ LOG_TARGET_CONSOLE, @@ -44,14 +55,24 @@ typedef enum LogTarget{ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 -} LogTarget; +} LogTarget; + +#define LOG_REALM_PLUS_LEVEL(realm, level) \ + ((realm) << 10 | (level)) +#define LOG_REALM_REMOVE_LEVEL(realm_level) \ + ((realm_level >> 10)) void log_set_target(LogTarget target); -void log_set_max_level(int level); +void log_set_max_level_realm(LogRealm realm, int level); +#define log_set_max_level(level) \ + log_set_max_level_realm(LOG_REALM, (level)) + void log_set_facility(int facility); int log_set_target_from_string(const char *e); -int log_set_max_level_from_string(const char *e); +int log_set_max_level_from_string_realm(LogRealm realm, const char *e); +#define log_set_max_level_from_string(e) \ + log_set_max_level_from_string_realm(LOG_REALM, (e)) void log_show_color(bool b); bool log_get_show_color(void) _pure_; @@ -62,7 +83,16 @@ int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); LogTarget log_get_target(void) _pure_; -int log_get_max_level(void) _pure_; +#if 0 /* NM_IGNORED */ +int log_get_max_level_realm(LogRealm realm) _pure_; +#endif /* NM_IGNORED */ +#define log_get_max_level() \ + log_get_max_level_realm(LOG_REALM) + +/* Functions below that open and close logs or configure logging based on the + * environment should not be called from library code — this is always a job + * for the application itself. + */ int log_open(void); void log_close(void); @@ -73,18 +103,36 @@ void log_close_journal(void); void log_close_kmsg(void); void log_close_console(void); -void log_parse_environment(void); +void log_parse_environment_realm(LogRealm realm); +#define log_parse_environment() \ + log_parse_environment_realm(LOG_REALM) #if 0 /* NM_IGNORED */ -int log_internal( +int log_dispatch_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *extra, + const char *extra_field, + char *buffer); + +int log_internal_realm( int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(6,7); +#endif /* NM_IGNORED */ +#define log_internal(level, ...) \ + log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) -int log_internalv( +#if 0 /* NM_IGNORED */ +int log_internalv_realm( int level, int error, const char *file, @@ -92,7 +140,10 @@ int log_internalv( const char *func, const char *format, va_list ap) _printf_(6,0); +#define log_internalv(level, ...) \ + log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) +/* Realm is fixed to LOG_REALM_SYSTEMD for those */ int log_object_internal( int level, int error, @@ -116,7 +167,7 @@ int log_object_internalv( const char *extra_field, const char *extra, const char *format, - va_list ap) _printf_(9,0); + va_list ap) _printf_(10,0); int log_struct_internal( int level, @@ -127,6 +178,7 @@ int log_struct_internal( const char *format, ...) _printf_(6,0) _sentinel_; int log_oom_internal( + LogRealm realm, const char *file, int line, const char *func); @@ -138,7 +190,16 @@ int log_format_iovec( bool newline_separator, int error, const char *format, - va_list ap); + va_list ap) _printf_(6, 0); + +int log_struct_iovec_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const struct iovec input_iovec[], + size_t n_input_iovec); /* This modifies the buffer passed! */ int log_dump_internal( @@ -150,35 +211,51 @@ int log_dump_internal( char *buffer); /* Logging for various assertions */ -noreturn void log_assert_failed( +noreturn void log_assert_failed_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed(text, ...) \ + log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) -noreturn void log_assert_failed_unreachable( +noreturn void log_assert_failed_unreachable_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed_unreachable(text, ...) \ + log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) -void log_assert_failed_return( +void log_assert_failed_return_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed_return(text, ...) \ + log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) + +#define log_dispatch(level, error, buffer) \ + log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) +#endif /* NM_IGNORED */ /* Logging with level */ -#define log_full_errno(level, error, ...) \ +#define log_full_errno_realm(realm, level, error, ...) \ ({ \ int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \ + ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \ + __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) -#endif /* NM_IGNORED */ -#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) +#define log_full_errno(level, error, ...) \ + log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) + +#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) /* Normal logging */ #define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) @@ -186,7 +263,7 @@ void log_assert_failed_return( #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) #define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) /* Logging triggered by an errno-like error */ #define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) @@ -194,7 +271,7 @@ void log_assert_failed_return( #define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) #define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) @@ -203,13 +280,22 @@ void log_assert_failed_return( #endif /* Structured logging */ -#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) -#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct_errno(level, error, ...) \ + log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + error, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) + +#define log_struct_iovec_errno(level, error, iovec, n_iovec) \ + log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + error, __FILE__, __LINE__, __func__, iovec, n_iovec) +#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) /* This modifies the buffer passed! */ -#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) +#define log_dump(level, buffer) \ + log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + 0, __FILE__, __LINE__, __func__, buffer) -#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) +#define log_oom() log_oom_internal(LOG_REALM, __FILE__, __LINE__, __func__) bool log_on_console(void) _pure_; @@ -223,6 +309,7 @@ void log_received_signal(int level, const struct signalfd_siginfo *si); void log_set_upgrade_syslog_to_journal(bool b); void log_set_always_reopen_console(bool b); +void log_set_open_when_needed(bool b); int log_syntax_internal( const char *unit, diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index 7db87b49..afcde459 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c index 8ffd9464..6d978e93 100644 --- a/src/systemd/src/basic/parse-util.c +++ b/src/systemd/src/basic/parse-util.c @@ -44,6 +44,7 @@ int parse_boolean(const char *v) { return -EINVAL; } +#if 0 /* NM_IGNORED */ int parse_pid(const char *s, pid_t* ret_pid) { unsigned long ul = 0; pid_t pid; @@ -61,12 +62,13 @@ int parse_pid(const char *s, pid_t* ret_pid) { if ((unsigned long) pid != ul) return -ERANGE; - if (pid <= 0) + if (!pid_is_valid(pid)) return -ERANGE; *ret_pid = pid; return 0; } +#endif /* NM_IGNORED */ int parse_mode(const char *s, mode_t *ret) { char *x; @@ -154,7 +156,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { unsigned n_entries, start_pos = 0; assert(t); - assert(base == 1000 || base == 1024); + assert(IN_SET(base, 1000, 1024)); assert(size); if (base == 1000) { @@ -594,4 +596,19 @@ int parse_ip_port(const char *s, uint16_t *ret) { return 0; } + +int parse_dev(const char *s, dev_t *ret) { + unsigned x, y; + dev_t d; + + if (sscanf(s, "%u:%u", &x, &y) != 2) + return -EINVAL; + + d = makedev(x, y); + if ((unsigned) major(d) != x || (unsigned) minor(d) != y) + return -EINVAL; + + *ret = d; + return 0; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h index 4d132f0d..dc09782c 100644 --- a/src/systemd/src/basic/parse-util.h +++ b/src/systemd/src/basic/parse-util.h @@ -30,6 +30,7 @@ #define MODE_INVALID ((mode_t) -1) int parse_boolean(const char *v) _pure_; +int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); int parse_ifindex(const char *s, int *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 12ba6ae7..1bdcc653 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -134,8 +134,7 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Skip the common part. */ for (;;) { - size_t a; - size_t b; + size_t a, b; from_dir += strspn(from_dir, "/"); to_path += strspn(to_path, "/"); @@ -147,7 +146,6 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { else /* from_dir is a parent directory of to_path. */ r = strdup(to_path); - if (!r) return -ENOMEM; @@ -178,21 +176,32 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Count the number of necessary ".." elements. */ for (n_parents = 0;;) { + size_t w; + from_dir += strspn(from_dir, "/"); if (!*from_dir) break; - from_dir += strcspn(from_dir, "/"); - n_parents++; + w = strcspn(from_dir, "/"); + + /* If this includes ".." we can't do a simple series of "..", refuse */ + if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.') + return -EINVAL; + + /* Count number of elements, except if they are "." */ + if (w != 1 || from_dir[0] != '.') + n_parents++; + + from_dir += w; } - r = malloc(n_parents * 3 + strlen(to_path) + 1); + r = new(char, n_parents * 3 + strlen(to_path) + 1); if (!r) return -ENOMEM; - for (p = r; n_parents > 0; n_parents--, p += 3) - memcpy(p, "../", 3); + for (p = r; n_parents > 0; n_parents--) + p = mempcpy(p, "../", 3); strcpy(p, to_path); path_kill_slashes(r); @@ -447,8 +456,8 @@ bool path_equal(const char *a, const char *b) { return path_compare(a, b) == 0; } -bool path_equal_or_files_same(const char *a, const char *b) { - return path_equal(a, b) || files_same(a, b) > 0; +bool path_equal_or_files_same(const char *a, const char *b, int flags) { + return path_equal(a, b) || files_same(a, b, flags) > 0; } char* path_join(const char *root, const char *path, const char *rest) { diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 35aef3ad..399ed5f9 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -27,14 +27,16 @@ #include "string-util.h" #include "time-util.h" +#if 0 /* NM_IGNORED */ #define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" #define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" -#ifdef HAVE_SPLIT_USR +#if HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR #else # define DEFAULT_PATH DEFAULT_PATH_NORMAL #endif +#endif /* NM_IGNORED */ bool is_path(const char *p) _pure_; int path_split_and_make_absolute(const char *p, char ***ret); @@ -46,7 +48,7 @@ char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b); +bool path_equal_or_files_same(const char *a, const char *b, int flags); char* path_join(const char *root, const char *path, const char *rest); static inline bool path_equal_ptr(const char *a, const char *b) { @@ -143,3 +145,13 @@ bool is_deviceallow_pattern(const char *path); int systemd_installation_has_version(const char *root, unsigned minimal_version); bool dot_or_dot_dot(const char *path); + +static inline const char *skip_dev_prefix(const char *p) { + const char *e; + + /* Drop any /dev prefix if there is any */ + + e = path_startswith(p, "/dev/"); + + return e ?: p; +} diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c new file mode 100644 index 00000000..272030d1 --- /dev/null +++ b/src/systemd/src/basic/process-util.c @@ -0,0 +1,1093 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <linux/oom.h> +#include <sched.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/personality.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <syslog.h> +#include <unistd.h> +#if 0 /* NM_IGNORED */ +#if HAVE_VALGRIND_VALGRIND_H +#include <valgrind/valgrind.h> +#endif +#endif /* NM_IGNORED */ + +#include "alloc-util.h" +#include "architecture.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "ioprio.h" +#include "log.h" +#include "macro.h" +#include "missing.h" +#include "process-util.h" +#include "raw-clone.h" +#include "signal-util.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" +#include "user-util.h" +#include "util.h" + +#if 0 /* NM_IGNORED */ +int get_process_state(pid_t pid) { + const char *p; + char state; + int r; + _cleanup_free_ char *line = NULL; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "stat"); + + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + if (sscanf(p, " %c", &state) != 1) + return -EIO; + + return (unsigned char) state; +} + +int get_process_comm(pid_t pid, char **name) { + const char *p; + int r; + + assert(name); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "comm"); + + r = read_one_line_file(p, name); + if (r == -ENOENT) + return -ESRCH; + + return r; +} + +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + _cleanup_fclose_ FILE *f = NULL; + bool space = false; + char *k, *ans = NULL; + const char *p; + int c; + + assert(line); + assert(pid >= 0); + + /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing + * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most + * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If + * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a + * command line that resolves to the empty string will return the "comm" name of the process instead. + * + * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and + * comm_fallback is false). Returns 0 and sets *line otherwise. */ + + p = procfs_file_alloca(pid, "cmdline"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + if (max_length == 1) { + + /* If there's only room for one byte, return the empty string */ + ans = new0(char, 1); + if (!ans) + return -ENOMEM; + + *line = ans; + return 0; + + } else if (max_length == 0) { + size_t len = 0, allocated = 0; + + while ((c = getc(f)) != EOF) { + + if (!GREEDY_REALLOC(ans, allocated, len+3)) { + free(ans); + return -ENOMEM; + } + + if (isprint(c)) { + if (space) { + ans[len++] = ' '; + space = false; + } + + ans[len++] = c; + } else if (len > 0) + space = true; + } + + if (len > 0) + ans[len] = '\0'; + else + ans = mfree(ans); + + } else { + bool dotdotdot = false; + size_t left; + + ans = new(char, max_length); + if (!ans) + return -ENOMEM; + + k = ans; + left = max_length; + while ((c = getc(f)) != EOF) { + + if (isprint(c)) { + + if (space) { + if (left <= 2) { + dotdotdot = true; + break; + } + + *(k++) = ' '; + left--; + space = false; + } + + if (left <= 1) { + dotdotdot = true; + break; + } + + *(k++) = (char) c; + left--; + } else if (k > ans) + space = true; + } + + if (dotdotdot) { + if (max_length <= 4) { + k = ans; + left = max_length; + } else { + k = ans + max_length - 4; + left = 4; + + /* Eat up final spaces */ + while (k > ans && isspace(k[-1])) { + k--; + left++; + } + } + + strncpy(k, "...", left-1); + k[left-1] = 0; + } else + *k = 0; + } + + /* Kernel threads have no argv[] */ + if (isempty(ans)) { + _cleanup_free_ char *t = NULL; + int h; + + free(ans); + + if (!comm_fallback) + return -ENOENT; + + h = get_process_comm(pid, &t); + if (h < 0) + return h; + + if (max_length == 0) + ans = strjoin("[", t, "]"); + else { + size_t l; + + l = strlen(t); + + if (l + 3 <= max_length) + ans = strjoin("[", t, "]"); + else if (max_length <= 6) { + + ans = new(char, max_length); + if (!ans) + return -ENOMEM; + + memcpy(ans, "[...]", max_length-1); + ans[max_length-1] = 0; + } else { + char *e; + + t[max_length - 6] = 0; + + /* Chop off final spaces */ + e = strchr(t, 0); + while (e > t && isspace(e[-1])) + e--; + *e = 0; + + ans = strjoin("[", t, "...]"); + } + } + if (!ans) + return -ENOMEM; + } + + *line = ans; + return 0; +} + +int rename_process(const char name[]) { + static size_t mm_size = 0; + static char *mm = NULL; + bool truncated = false; + size_t l; + + /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's + * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in + * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; + * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be + * truncated. + * + * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ + + if (isempty(name)) + return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + + l = strlen(name); + + /* First step, change the comm field. */ + (void) prctl(PR_SET_NAME, name); + if (l > 15) /* Linux process names can be 15 chars at max */ + truncated = true; + + /* Second step, change glibc's ID of the process name. */ + if (program_invocation_name) { + size_t k; + + k = strlen(program_invocation_name); + strncpy(program_invocation_name, name, k); + if (l > k) + truncated = true; + } + + /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but + * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at + * the end. This is the best option for changing /proc/self/cmdline. */ + + /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the + * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is + * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if + * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but + * mmap() is not. */ + if (geteuid() != 0) + log_debug("Skipping PR_SET_MM, as we don't have privileges."); + else if (mm_size < l+1) { + size_t nn_size; + char *nn; + + nn_size = PAGE_ALIGN(l+1); + nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (nn == MAP_FAILED) { + log_debug_errno(errno, "mmap() failed: %m"); + goto use_saved_argv; + } + + strncpy(nn, name, nn_size); + + /* Now, let's tell the kernel about this new memory */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m"); + (void) munmap(nn, nn_size); + goto use_saved_argv; + } + + /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's + * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + + if (mm) + (void) munmap(mm, mm_size); + + mm = nn; + mm_size = nn_size; + } else { + strncpy(mm, name, mm_size); + + /* Update the end pointer, continuing regardless of any failure. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } + +use_saved_argv: + /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if + * it still looks here */ + + if (saved_argc > 0) { + int i; + + if (saved_argv[0]) { + size_t k; + + k = strlen(saved_argv[0]); + strncpy(saved_argv[0], name, k); + if (l > k) + truncated = true; + } + + for (i = 1; i < saved_argc; i++) { + if (!saved_argv[i]) + break; + + memzero(saved_argv[i], strlen(saved_argv[i])); + } + } + + return !truncated; +} + +int is_kernel_thread(pid_t pid) { + const char *p; + size_t count; + char c; + bool eof; + FILE *f; + + if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ + return 0; + + assert(pid > 1); + + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + count = fread(&c, 1, 1, f); + eof = feof(f); + fclose(f); + + /* Kernel threads have an empty cmdline */ + + if (count <= 0) + return eof ? 1 : -errno; + + return 0; +} + +int get_process_capeff(pid_t pid, char **capeff) { + const char *p; + int r; + + assert(capeff); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "CapEff", WHITESPACE, capeff); + if (r == -ENOENT) + return -ESRCH; + + return r; +} + +static int get_process_link_contents(const char *proc_file, char **name) { + int r; + + assert(proc_file); + assert(name); + + r = readlink_malloc(proc_file, name); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + return 0; +} + +int get_process_exe(pid_t pid, char **name) { + const char *p; + char *d; + int r; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "exe"); + r = get_process_link_contents(p, name); + if (r < 0) + return r; + + d = endswith(*name, " (deleted)"); + if (d) + *d = '\0'; + + return 0; +} + +static int get_process_id(pid_t pid, const char *field, uid_t *uid) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + const char *p; + + assert(field); + assert(uid); + + if (pid < 0) + return -EINVAL; + + p = procfs_file_alloca(pid, "status"); + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + FOREACH_LINE(line, f, return -errno) { + char *l; + + l = strstrip(line); + + if (startswith(l, field)) { + l += strlen(field); + l += strspn(l, WHITESPACE); + + l[strcspn(l, WHITESPACE)] = 0; + + return parse_uid(l, uid); + } + } + + return -EIO; +} + +int get_process_uid(pid_t pid, uid_t *uid) { + + if (pid == 0 || pid == getpid_cached()) { + *uid = getuid(); + return 0; + } + + return get_process_id(pid, "Uid:", uid); +} + +int get_process_gid(pid_t pid, gid_t *gid) { + + if (pid == 0 || pid == getpid_cached()) { + *gid = getgid(); + return 0; + } + + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + return get_process_id(pid, "Gid:", gid); +} + +int get_process_cwd(pid_t pid, char **cwd) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "cwd"); + + return get_process_link_contents(p, cwd); +} + +int get_process_root(pid_t pid, char **root) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "root"); + + return get_process_link_contents(p, root); +} + +int get_process_environ(pid_t pid, char **env) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *outcome = NULL; + int c; + const char *p; + size_t allocated = 0, sz = 0; + + assert(pid >= 0); + assert(env); + + p = procfs_file_alloca(pid, "environ"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + while ((c = fgetc(f)) != EOF) { + if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) + return -ENOMEM; + + if (c == '\0') + outcome[sz++] = '\n'; + else + sz += cescape_char(c, outcome + sz); + } + + if (!outcome) { + outcome = strdup(""); + if (!outcome) + return -ENOMEM; + } else + outcome[sz] = '\0'; + + *env = outcome; + outcome = NULL; + + return 0; +} + +int get_process_ppid(pid_t pid, pid_t *_ppid) { + int r; + _cleanup_free_ char *line = NULL; + long unsigned ppid; + const char *p; + + assert(pid >= 0); + assert(_ppid); + + if (pid == 0 || pid == getpid_cached()) { + *_ppid = getppid(); + return 0; + } + + p = procfs_file_alloca(pid, "stat"); + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + /* Let's skip the pid and comm fields. The latter is enclosed + * in () but does not escape any () in its value, so let's + * skip over it manually */ + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%lu ", /* ppid */ + &ppid) != 1) + return -EIO; + + if ((long unsigned) (pid_t) ppid != ppid) + return -ERANGE; + + *_ppid = (pid_t) ppid; + + return 0; +} + +int wait_for_terminate(pid_t pid, siginfo_t *status) { + siginfo_t dummy; + + assert(pid >= 1); + + if (!status) + status = &dummy; + + for (;;) { + zero(*status); + + if (waitid(P_PID, pid, status, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + return negative_errno(); + } + + return 0; + } +} + +/* + * Return values: + * < 0 : wait_for_terminate() failed to get the state of the + * process, the process was terminated by a signal, or + * failed for an unknown reason. + * >=0 : The process terminated normally, and its exit code is + * returned. + * + * That is, success is indicated by a return value of zero, and an + * error is indicated by a non-zero value. + * + * A warning is emitted if the process terminates abnormally, + * and also if it returns non-zero unless check_exit_code is true. + */ +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { + int r; + siginfo_t status; + + assert(name); + assert(pid > 1); + + r = wait_for_terminate(pid, &status); + if (r < 0) + return log_warning_errno(r, "Failed to wait for %s: %m", name); + + if (status.si_code == CLD_EXITED) { + if (status.si_status != 0) + log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, + "%s failed with error code %i.", name, status.si_status); + else + log_debug("%s succeeded.", name); + + return status.si_status; + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { + + log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + return -EPROTO; + } + + log_warning("%s failed due to unknown reason.", name); + return -EPROTO; +} + +void sigkill_wait(pid_t pid) { + assert(pid > 1); + + if (kill(pid, SIGKILL) > 0) + (void) wait_for_terminate(pid, NULL); +} + +void sigkill_waitp(pid_t *pid) { + if (!pid) + return; + if (*pid <= 1) + return; + + sigkill_wait(*pid); +} + +int kill_and_sigcont(pid_t pid, int sig) { + int r; + + r = kill(pid, sig) < 0 ? -errno : 0; + + /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't + * affected by a process being suspended anyway. */ + if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)) + (void) kill(pid, SIGCONT); + + return r; +} + +int getenv_for_pid(pid_t pid, const char *field, char **_value) { + _cleanup_fclose_ FILE *f = NULL; + char *value = NULL; + int r; + bool done = false; + size_t l; + const char *path; + + assert(pid >= 0); + assert(field); + assert(_value); + + path = procfs_file_alloca(pid, "environ"); + + f = fopen(path, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + l = strlen(field); + r = 0; + + do { + char line[LINE_MAX]; + unsigned i; + + for (i = 0; i < sizeof(line)-1; i++) { + int c; + + c = getc(f); + if (_unlikely_(c == EOF)) { + done = true; + break; + } else if (c == 0) + break; + + line[i] = c; + } + line[i] = 0; + + if (strneq(line, field, l) && line[l] == '=') { + value = strdup(line + l + 1); + if (!value) + return -ENOMEM; + + r = 1; + break; + } + + } while (!done); + + *_value = value; + return r; +} + +bool pid_is_unwaited(pid_t pid) { + /* Checks whether a PID is still valid at all, including a zombie */ + + if (pid < 0) + return false; + + if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ + return true; + + if (pid == getpid_cached()) + return true; + + if (kill(pid, 0) >= 0) + return true; + + return errno != ESRCH; +} + +bool pid_is_alive(pid_t pid) { + int r; + + /* Checks whether a PID is still valid and not a zombie */ + + if (pid < 0) + return false; + + if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ + return true; + + if (pid == getpid_cached()) + return true; + + r = get_process_state(pid); + if (IN_SET(r, -ESRCH, 'Z')) + return false; + + return true; +} + +int pid_from_same_root_fs(pid_t pid) { + const char *root; + + if (pid < 0) + return false; + + if (pid == 0 || pid == getpid_cached()) + return true; + + root = procfs_file_alloca(pid, "root"); + + return files_same(root, "/proc/1/root", 0); +} +#endif /* NM_IGNORED */ + +bool is_main_thread(void) { + static thread_local int cached = 0; + + if (_unlikely_(cached == 0)) + cached = getpid_cached() == gettid() ? 1 : -1; + + return cached > 0; +} + +#if 0 /* NM_IGNORED */ +noreturn void freeze(void) { + + log_close(); + + /* Make sure nobody waits for us on a socket anymore */ + close_all_fds(NULL, 0); + + sync(); + + for (;;) + pause(); +} + +bool oom_score_adjust_is_valid(int oa) { + return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; +} + +unsigned long personality_from_string(const char *p) { + int architecture; + + if (!p) + return PERSONALITY_INVALID; + + /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just + * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for + * the same register size. */ + + architecture = architecture_from_string(p); + if (architecture < 0) + return PERSONALITY_INVALID; + + if (architecture == native_architecture()) + return PER_LINUX; +#ifdef SECONDARY_ARCHITECTURE + if (architecture == SECONDARY_ARCHITECTURE) + return PER_LINUX32; +#endif + + return PERSONALITY_INVALID; +} + +const char* personality_to_string(unsigned long p) { + int architecture = _ARCHITECTURE_INVALID; + + if (p == PER_LINUX) + architecture = native_architecture(); +#ifdef SECONDARY_ARCHITECTURE + else if (p == PER_LINUX32) + architecture = SECONDARY_ARCHITECTURE; +#endif + + if (architecture < 0) + return NULL; + + return architecture_to_string(architecture); +} + +int safe_personality(unsigned long p) { + int ret; + + /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno, + * and in others as negative return value containing an errno-like value. Let's work around this: this is a + * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and + * the return value indicating the same issue, so that we are definitely on the safe side. + * + * See https://github.com/systemd/systemd/issues/6737 */ + + errno = 0; + ret = personality(p); + if (ret < 0) { + if (errno != 0) + return -errno; + + errno = -ret; + } + + return ret; +} + +int opinionated_personality(unsigned long *ret) { + int current; + + /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit + * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the + * two most relevant personalities: PER_LINUX and PER_LINUX32. */ + + current = safe_personality(PERSONALITY_INVALID); + if (current < 0) + return current; + + if (((unsigned long) current & 0xffff) == PER_LINUX32) + *ret = PER_LINUX32; + else + *ret = PER_LINUX; + + return 0; +} + +void valgrind_summary_hack(void) { +#if HAVE_VALGRIND_VALGRIND_H + if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { + pid_t pid; + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); + else if (pid == 0) + exit(EXIT_SUCCESS); + else { + log_info("Spawned valgrind helper as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } +#endif +} + +int pid_compare_func(const void *a, const void *b) { + const pid_t *p = a, *q = b; + + /* Suitable for usage in qsort() */ + + if (*p < *q) + return -1; + if (*p > *q) + return 1; + return 0; +} + +int ioprio_parse_priority(const char *s, int *ret) { + int i, r; + + assert(s); + assert(ret); + + r = safe_atoi(s, &i); + if (r < 0) + return r; + + if (!ioprio_priority_is_valid(i)) + return -EINVAL; + + *ret = i; + return 0; +} +#endif /* NM_IGNORED */ + +/* The cached PID, possible values: + * + * == UNSET [0] → cache not initialized yet + * == BUSY [-1] → some thread is initializing it at the moment + * any other → the cached PID + */ + +#define CACHED_PID_UNSET ((pid_t) 0) +#define CACHED_PID_BUSY ((pid_t) -1) + +static pid_t cached_pid = CACHED_PID_UNSET; + +static void reset_cached_pid(void) { + /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ + cached_pid = CACHED_PID_UNSET; +} + +/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc + * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against + * libpthread, as it is part of glibc anyway. */ +extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle); +extern void* __dso_handle __attribute__ ((__weak__)); + +pid_t getpid_cached(void) { + pid_t current_value; + + /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a + * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally + * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when + * objects were used across fork()s. With this caching the old behaviour is somewhat restored. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 + * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e + */ + + current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); + + switch (current_value) { + + case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ + pid_t new_pid; + + new_pid = getpid(); + + if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + /* OOM? Let's try again later */ + cached_pid = CACHED_PID_UNSET; + return new_pid; + } + + cached_pid = new_pid; + return new_pid; + } + + case CACHED_PID_BUSY: /* Somebody else is currently initializing */ + return getpid(); + + default: /* Properly initialized */ + return current_value; + } +} + +#if 0 /* NM_IGNORED */ +static const char *const ioprio_class_table[] = { + [IOPRIO_CLASS_NONE] = "none", + [IOPRIO_CLASS_RT] = "realtime", + [IOPRIO_CLASS_BE] = "best-effort", + [IOPRIO_CLASS_IDLE] = "idle" +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX); + +static const char *const sigchld_code_table[] = { + [CLD_EXITED] = "exited", + [CLD_KILLED] = "killed", + [CLD_DUMPED] = "dumped", + [CLD_TRAPPED] = "trapped", + [CLD_STOPPED] = "stopped", + [CLD_CONTINUED] = "continued", +}; + +DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); + +static const char* const sched_policy_table[] = { + [SCHED_OTHER] = "other", + [SCHED_BATCH] = "batch", + [SCHED_IDLE] = "idle", + [SCHED_FIFO] = "fifo", + [SCHED_RR] = "rr" +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h new file mode 100644 index 00000000..e1bd2c5b --- /dev/null +++ b/src/systemd/src/basic/process-util.h @@ -0,0 +1,141 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <alloca.h> +#include <sched.h> +#include <signal.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <sys/resource.h> +#include <sys/types.h> + +#include "format-util.h" +#include "ioprio.h" +#include "macro.h" + +#define procfs_file_alloca(pid, field) \ + ({ \ + pid_t _pid_ = (pid); \ + const char *_r_; \ + if (_pid_ == 0) { \ + _r_ = ("/proc/self/" field); \ + } else { \ + _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ + sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ + } \ + _r_; \ + }) + +int get_process_state(pid_t pid); +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); +int get_process_uid(pid_t pid, uid_t *uid); +int get_process_gid(pid_t pid, gid_t *gid); +int get_process_capeff(pid_t pid, char **capeff); +int get_process_cwd(pid_t pid, char **cwd); +int get_process_root(pid_t pid, char **root); +int get_process_environ(pid_t pid, char **environ); +int get_process_ppid(pid_t pid, pid_t *ppid); + +int wait_for_terminate(pid_t pid, siginfo_t *status); +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); + +void sigkill_wait(pid_t pid); +void sigkill_waitp(pid_t *pid); + +int kill_and_sigcont(pid_t pid, int sig); + +int rename_process(const char name[]); +int is_kernel_thread(pid_t pid); + +int getenv_for_pid(pid_t pid, const char *field, char **_value); + +bool pid_is_alive(pid_t pid); +bool pid_is_unwaited(pid_t pid); +int pid_from_same_root_fs(pid_t pid); + +bool is_main_thread(void); + +noreturn void freeze(void); + +bool oom_score_adjust_is_valid(int oa); + +#ifndef PERSONALITY_INVALID +/* personality(7) documents that 0xffffffffUL is used for querying the + * current personality, hence let's use that here as error + * indicator. */ +#define PERSONALITY_INVALID 0xffffffffLU +#endif + +unsigned long personality_from_string(const char *p); +const char *personality_to_string(unsigned long); + +int safe_personality(unsigned long p); +int opinionated_personality(unsigned long *ret); + +int ioprio_class_to_string_alloc(int i, char **s); +int ioprio_class_from_string(const char *s); + +const char *sigchld_code_to_string(int i) _const_; +int sigchld_code_from_string(const char *s) _pure_; + +int sched_policy_to_string_alloc(int i, char **s); +int sched_policy_from_string(const char *s); + +#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) +#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) + +void valgrind_summary_hack(void); + +int pid_compare_func(const void *a, const void *b); + +#if 0 /* NM_IGNORED */ +static inline bool nice_is_valid(int n) { + return n >= PRIO_MIN && n < PRIO_MAX; +} + +static inline bool sched_policy_is_valid(int i) { + return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); +} + +static inline bool sched_priority_is_valid(int i) { + return i >= 0 && i <= sched_get_priority_max(SCHED_RR); +} + +static inline bool ioprio_class_is_valid(int i) { + return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); +} + +static inline bool ioprio_priority_is_valid(int i) { + return i >= 0 && i < IOPRIO_BE_NR; +} + +static inline bool pid_is_valid(pid_t p) { + return p > 0; +} +#endif /* NM_IGNORED */ + +int ioprio_parse_priority(const char *s, int *ret); + +pid_t getpid_cached(void); diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index 1d8ca882..3b6ddb7d 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -28,8 +28,14 @@ #include <linux/random.h> #include <stdint.h> -#ifdef HAVE_SYS_AUXV_H -#include <sys/auxv.h> +#if HAVE_SYS_AUXV_H +# include <sys/auxv.h> +#endif + +#if USE_SYS_RANDOM_H +# include <sys/random.h> +#else +# include <linux/random.h> #endif #include "fd-util.h" @@ -38,76 +44,86 @@ #include "random-util.h" #include "time-util.h" -int dev_urandom(void *p, size_t n) { -#if 0 /* NM_IGNORED */ +int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { static int have_syscall = -1; _cleanup_close_ int fd = -1; + unsigned already_done = 0; int r; - /* Gathers some randomness from the kernel. This call will - * never block, and will always return some data from the - * kernel, regardless if the random pool is fully initialized - * or not. It thus makes no guarantee for the quality of the - * returned entropy, but is good enough for our usual usecases - * of seeding the hash functions for hashtable */ - - /* Use the getrandom() syscall unless we know we don't have - * it, or when the requested size is too large for it. */ - if (have_syscall != 0 || (size_t) (int) n != n) { + /* Gathers some randomness from the kernel. This call will never block. If + * high_quality_required, it will always return some data from the kernel, + * regardless of whether the random pool is fully initialized or not. + * Otherwise, it will return success if at least some random bytes were + * successfully acquired, and an error if the kernel has no entropy whatsover + * for us. */ + + /* Use the getrandom() syscall unless we know we don't have it. */ + if (have_syscall != 0) { +#if !HAVE_GETRANDOM + /* XXX: NM: systemd calls the syscall directly in this case. Don't add that workaround. + * If you don't compile against a libc that provides getrandom(), you don't get it. */ + r = -1; + errno = ENOSYS; +#else r = getrandom(p, n, GRND_NONBLOCK); - if (r == (int) n) { +#endif + if (r > 0) { + have_syscall = true; + if ((size_t) r == n) + return 0; + if (!high_quality_required) { + /* Fill in the remaining bytes using pseudorandom values */ + pseudorandom_bytes((uint8_t*) p + r, n - r); + return 0; + } + + already_done = r; + } else if (errno == ENOSYS) + /* We lack the syscall, continue with reading from /dev/urandom. */ + have_syscall = false; + else if (errno == EAGAIN) { + /* The kernel has no entropy whatsoever. Let's remember to + * use the syscall the next time again though. + * + * If high_quality_required is false, return an error so that + * random_bytes() can produce some pseudorandom + * bytes. Otherwise, fall back to /dev/urandom, which we know + * is empty, but the kernel will produce some bytes for us on + * a best-effort basis. */ have_syscall = true; - return 0; - } - - if (r < 0) { - if (errno == ENOSYS) - /* we lack the syscall, continue with - * reading from /dev/urandom */ - have_syscall = false; - else if (errno == EAGAIN) - /* not enough entropy for now. Let's - * remember to use the syscall the - * next time, again, but also read - * from /dev/urandom for now, which - * doesn't care about the current - * amount of entropy. */ - have_syscall = true; - else - return -errno; + + if (!high_quality_required) + return -ENODATA; } else - /* too short read? */ - return -ENODATA; + return -errno; } -#else /* NM_IGNORED */ - _cleanup_close_ int fd = -1; -#endif /* NM_IGNORED */ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return errno == ENOENT ? -ENOSYS : -errno; - return loop_read_exact(fd, p, n, true); + return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); } void initialize_srand(void) { static bool srand_called = false; unsigned x; -#ifdef HAVE_SYS_AUXV_H +#if HAVE_SYS_AUXV_H void *auxv; #endif if (srand_called) return; -#ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than nothing... */ +#if HAVE_SYS_AUXV_H + /* The kernel provides us with 16 bytes of entropy in auxv, so let's + * try to make use of that to seed the pseudo-random generator. It's + * better than nothing... */ auxv = (void*) getauxval(AT_RANDOM); if (auxv) { - assert_cc(sizeof(x) < 16); + assert_cc(sizeof(x) <= 16); memcpy(&x, auxv, sizeof(x)); } else #endif @@ -121,19 +137,44 @@ void initialize_srand(void) { srand_called = true; } -void random_bytes(void *p, size_t n) { +/* INT_MAX gives us only 31 bits, so use 24 out of that. */ +#if RAND_MAX >= INT_MAX +# define RAND_STEP 3 +#else +/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ +# define RAND_STEP 1 +#endif + +void pseudorandom_bytes(void *p, size_t n) { uint8_t *q; + + initialize_srand(); + + for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { + unsigned rr; + + rr = (unsigned) rand(); + +#if RAND_STEP >= 3 + if ((size_t) (q - (uint8_t*) p + 2) < n) + q[2] = rr >> 16; +#endif +#if RAND_STEP >= 2 + if ((size_t) (q - (uint8_t*) p + 1) < n) + q[1] = rr >> 8; +#endif + q[0] = rr; + } +} + +void random_bytes(void *p, size_t n) { int r; - r = dev_urandom(p, n); + r = acquire_random_bytes(p, n, false); if (r >= 0) return; - /* If some idiot made /dev/urandom unavailable to us, he'll - * get a PRNG instead. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q ++) - *q = rand(); + /* If some idiot made /dev/urandom unavailable to us, or the + * kernel has no entropy, use a PRNG instead. */ + return pseudorandom_bytes(p, n); } diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h index 3cee4c50..804e225f 100644 --- a/src/systemd/src/basic/random-util.h +++ b/src/systemd/src/basic/random-util.h @@ -19,10 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> #include <stddef.h> #include <stdint.h> -int dev_urandom(void *p, size_t n); +int acquire_random_bytes(void *p, size_t n, bool high_quality_required); +void pseudorandom_bytes(void *p, size_t n); void random_bytes(void *p, size_t n); void initialize_srand(void); diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index a5f8beb0..12d0fda1 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -136,3 +136,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) + +int set_make(Set **ret, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS, void *add, ...); diff --git a/src/systemd/src/basic/siphash24.c b/src/systemd/src/basic/siphash24.c deleted file mode 100644 index e4b1cb10..00000000 --- a/src/systemd/src/basic/siphash24.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - SipHash reference C implementation - - Written in 2012 by - Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com> - Daniel J. Bernstein <djb@cr.yp.to> - - To the extent possible under law, the author(s) have dedicated all copyright - and related and neighboring rights to this software to the public domain - worldwide. This software is distributed without any warranty. - - You should have received a copy of the CC0 Public Domain Dedication along with - this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. - - (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd) - (Refactored by Tom Gundersen to split up in several functions and follow systemd - coding style) -*/ - -#include "nm-sd-adapt.h" - -#include <stdio.h> - -#include "macro.h" -#include "siphash24.h" -#include "unaligned.h" - -static inline uint64_t rotate_left(uint64_t x, uint8_t b) { - assert(b < 64); - - return (x << b) | (x >> (64 - b)); -} - -static inline void sipround(struct siphash *state) { - assert(state); - - state->v0 += state->v1; - state->v1 = rotate_left(state->v1, 13); - state->v1 ^= state->v0; - state->v0 = rotate_left(state->v0, 32); - state->v2 += state->v3; - state->v3 = rotate_left(state->v3, 16); - state->v3 ^= state->v2; - state->v0 += state->v3; - state->v3 = rotate_left(state->v3, 21); - state->v3 ^= state->v0; - state->v2 += state->v1; - state->v1 = rotate_left(state->v1, 17); - state->v1 ^= state->v2; - state->v2 = rotate_left(state->v2, 32); -} - -void siphash24_init(struct siphash *state, const uint8_t k[16]) { - uint64_t k0, k1; - - assert(state); - assert(k); - - k0 = unaligned_read_le64(k); - k1 = unaligned_read_le64(k + 8); - - *state = (struct siphash) { - /* "somepseudorandomlygeneratedbytes" */ - .v0 = 0x736f6d6570736575ULL ^ k0, - .v1 = 0x646f72616e646f6dULL ^ k1, - .v2 = 0x6c7967656e657261ULL ^ k0, - .v3 = 0x7465646279746573ULL ^ k1, - .padding = 0, - .inlen = 0, - }; -} - -void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { - - const uint8_t *in = _in; - const uint8_t *end = in + inlen; - size_t left = state->inlen & 7; - uint64_t m; - - assert(in); - assert(state); - - /* Update total length */ - state->inlen += inlen; - - /* If padding exists, fill it out */ - if (left > 0) { - for ( ; in < end && left < 8; in ++, left ++) - state->padding |= ((uint64_t) *in) << (left * 8); - - if (in == end && left < 8) - /* We did not have enough input to fill out the padding completely */ - return; - -#ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); - printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); -#endif - - state->v3 ^= state->padding; - sipround(state); - sipround(state); - state->v0 ^= state->padding; - - state->padding = 0; - } - - end -= (state->inlen % sizeof(uint64_t)); - - for ( ; in < end; in += 8) { - m = unaligned_read_le64(in); -#ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); - printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m); -#endif - state->v3 ^= m; - sipround(state); - sipround(state); - state->v0 ^= m; - } - - left = state->inlen & 7; - switch (left) { - case 7: - state->padding |= ((uint64_t) in[6]) << 48; - /* fall through */ - case 6: - state->padding |= ((uint64_t) in[5]) << 40; - /* fall through */ - case 5: - state->padding |= ((uint64_t) in[4]) << 32; - /* fall through */ - case 4: - state->padding |= ((uint64_t) in[3]) << 24; - /* fall through */ - case 3: - state->padding |= ((uint64_t) in[2]) << 16; - /* fall through */ - case 2: - state->padding |= ((uint64_t) in[1]) << 8; - /* fall through */ - case 1: - state->padding |= ((uint64_t) in[0]); - /* fall through */ - case 0: - break; - } -} - -uint64_t siphash24_finalize(struct siphash *state) { - uint64_t b; - - assert(state); - - b = state->padding | (((uint64_t) state->inlen) << 56); - -#ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); - printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); -#endif - - state->v3 ^= b; - sipround(state); - sipround(state); - state->v0 ^= b; - -#ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); -#endif - state->v2 ^= 0xff; - - sipround(state); - sipround(state); - sipround(state); - sipround(state); - - return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; -} - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) { - struct siphash state; - - assert(in); - assert(k); - - siphash24_init(&state, k); - siphash24_compress(in, inlen, &state); - - return siphash24_finalize(&state); -} diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h deleted file mode 100644 index 54e2420c..00000000 --- a/src/systemd/src/basic/siphash24.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; -}; - -void siphash24_init(struct siphash *state, const uint8_t k[16]); -void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) - -uint64_t siphash24_finalize(struct siphash *state); - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index e63dd0db..798ab16e 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -51,6 +51,12 @@ #include "util.h" #if 0 /* NM_IGNORED */ +#if ENABLE_IDN +# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES) +#else +# define IDN_FLAGS 0 +#endif + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; @@ -265,7 +271,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->sockaddr.in.sin_port == 0) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -277,7 +283,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->sockaddr.in6.sin6_port == 0) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -301,7 +307,7 @@ int socket_address_verify(const SocketAddress *a) { } } - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) return -EINVAL; return 0; @@ -311,7 +317,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->size != sizeof(struct sockaddr_nl)) return -EINVAL; - if (a->type != SOCK_RAW && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) return -EINVAL; return 0; @@ -320,7 +326,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->size != sizeof(struct sockaddr_vm)) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -361,8 +367,7 @@ bool socket_address_can_accept(const SocketAddress *a) { assert(a); return - a->type == SOCK_STREAM || - a->type == SOCK_SEQPACKET; + IN_SET(a->type, SOCK_STREAM, SOCK_SEQPACKET); } bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { @@ -409,7 +414,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { return false; if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path)) + if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) return false; } else { if (a->size != b->size) @@ -726,8 +731,7 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) assert(_ret); - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, - NI_IDN|NI_IDN_USE_STD3_ASCII_RULES); + r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); if (r != 0) { int saved_errno = errno; @@ -791,7 +795,8 @@ static const char* const netlink_family_table[] = { [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", [NETLINK_GENERIC] = "generic", [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs" + [NETLINK_ECRYPTFS] = "ecryptfs", + [NETLINK_RDMA] = "rdma", }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); @@ -890,7 +895,7 @@ bool ifname_valid(const char *p) { if ((unsigned char) *p <= 32U) return false; - if (*p == ':' || *p == '/') + if (IN_SET(*p, ':', '/')) return false; numeric = numeric && (*p >= '0' && *p <= '9'); @@ -1079,7 +1084,7 @@ ssize_t next_datagram_size_fd(int fd) { l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); if (l < 0) { - if (errno == EOPNOTSUPP || errno == EFAULT) + if (IN_SET(errno, EOPNOTSUPP, EFAULT)) goto fallback; return -errno; diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index 19a9ddb2..d7e2d85f 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -27,6 +27,7 @@ #include <sys/types.h> #include <sys/un.h> #include <linux/netlink.h> +#include <linux/if_infiniband.h> #include <linux/if_packet.h> #include "macro.h" @@ -44,6 +45,8 @@ union sockaddr_union { #if 0 /* NM_IGNORED */ struct sockaddr_vm vm; #endif /* NM_IGNORED */ + /* Ensure there is enough space to store Infiniband addresses */ + uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; }; typedef struct SocketAddress { @@ -149,6 +152,23 @@ int flush_accept(int fd); struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); +/* + * Certain hardware address types (e.g Infiniband) do not fit into sll_addr + * (8 bytes) and run over the structure. This macro returns the correct size that + * must be passed to kernel. + */ +#define SOCKADDR_LL_LEN(sa) \ + ({ \ + const struct sockaddr_ll *_sa = &(sa); \ + size_t _mac_len = sizeof(_sa->sll_addr); \ + assert(_sa->sll_family == AF_PACKET); \ + if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \ + _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \ + if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \ + _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \ + offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \ + }) + /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ #define SOCKADDR_UN_LEN(sa) \ ({ \ diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index 406d6d3c..047eb162 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -217,7 +217,7 @@ char *strnappend(const char *s, const char *suffix, size_t b) { } char *strappend(const char *s, const char *suffix) { - return strnappend(s, suffix, suffix ? strlen(suffix) : 0); + return strnappend(s, suffix, strlen_ptr(suffix)); } char *strjoin_real(const char *x, ...) { @@ -546,7 +546,7 @@ char *ellipsize(const char *s, size_t length, unsigned percent) { } #endif /* NM_IGNORED */ -bool nulstr_contains(const char*nulstr, const char *needle) { +bool nulstr_contains(const char *nulstr, const char *needle) { const char *i; if (!nulstr) @@ -562,7 +562,7 @@ bool nulstr_contains(const char*nulstr, const char *needle) { char* strshorten(char *s, size_t l) { assert(s); - if (l < strlen(s)) + if (strnlen(s, l+1) > l) s[l] = 0; return s; @@ -639,6 +639,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (!f) return NULL; + /* Note we use the _unlocked() stdio variants on f for performance + * reasons. It's safe to do so since we created f here and it + * doesn't leave our scope. + */ + for (i = *ibuf; i < *ibuf + isz + 1; i++) { switch (state) { @@ -649,21 +654,21 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { else if (*i == '\x1B') state = STATE_ESCAPE; else if (*i == '\t') - fputs(" ", f); + fputs_unlocked(" ", f); else - fputc(*i, f); + fputc_unlocked(*i, f); break; case STATE_ESCAPE: if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); + fputc_unlocked('\x1B', f); break; } else if (*i == '[') { state = STATE_BRACKET; begin = i + 1; } else { - fputc('\x1B', f); - fputc(*i, f); + fputc_unlocked('\x1B', f); + fputc_unlocked(*i, f); state = STATE_OTHER; } @@ -672,9 +677,9 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { case STATE_BRACKET: if (i >= *ibuf + isz || /* EOT */ - (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) { - fputc('\x1B', f); - fputc('[', f); + (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { + fputc_unlocked('\x1B', f); + fputc_unlocked('[', f); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') @@ -706,7 +711,7 @@ char *strextend(char **x, ...) { assert(x); - l = f = *x ? strlen(*x) : 0; + l = f = strlen_ptr(*x); va_start(ap, x); for (;;) { @@ -825,7 +830,7 @@ int free_and_strdup(char **p, const char *s) { return 1; } -#if !HAVE_DECL_EXPLICIT_BZERO +#if !HAVE_EXPLICIT_BZERO /* * Pointer to memset is volatile so that compiler must de-reference * the pointer and can't assume that it points to any function in diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h index be44dedf..4c94b182 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -120,7 +120,7 @@ char *strjoin_real(const char *x, ...) _sentinel_; ({ \ const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ - int _len_ = 0; \ + size_t _len_ = 0; \ unsigned _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ @@ -158,7 +158,7 @@ bool string_has_cc(const char *p, const char *ok) _pure_; char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); char *ellipsize(const char *s, size_t length, unsigned percent); -bool nulstr_contains(const char*nulstr, const char *needle); +bool nulstr_contains(const char *nulstr, const char *needle); char* strshorten(char *s, size_t l); @@ -189,7 +189,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -#if !HAVE_DECL_EXPLICIT_BZERO +#if !HAVE_EXPLICIT_BZERO void explicit_bzero(void *p, size_t l); #endif @@ -200,3 +200,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); #define _cleanup_string_free_erase_ _cleanup_(string_free_erasep) bool string_is_safe(const char *p) _pure_; + +static inline size_t strlen_ptr(const char *s) { + if (!s) + return 0; + + return strlen(s); +} diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index a3660f08..08bcff6e 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -774,11 +774,7 @@ static int str_compare(const void *_a, const void *_b) { } char **strv_sort(char **l) { - - if (strv_isempty(l)) - return l; - - qsort(l, strv_length(l), sizeof(char*), str_compare); + qsort_safe(l, strv_length(l), sizeof(char*), str_compare); return l; } diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index e8158d55..7f1c3f7c 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -23,6 +23,7 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/timerfd.h> @@ -109,7 +110,7 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { ts->realtime = u; delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); + ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); return ts; } @@ -126,8 +127,8 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) ts->realtime = u; delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; + ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); + ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; return ts; } @@ -143,7 +144,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { ts->monotonic = u; delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub(now(CLOCK_REALTIME), delta); + ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); return ts; } @@ -158,8 +159,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us dual_timestamp_get(ts); delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub(ts->realtime, delta); - ts->monotonic = usec_sub(ts->monotonic, delta); + ts->realtime = usec_sub_signed(ts->realtime, delta); + ts->monotonic = usec_sub_signed(ts->monotonic, delta); return ts; } @@ -244,7 +245,7 @@ usec_t timeval_load(const struct timeval *tv) { struct timeval *timeval_store(struct timeval *tv, usec_t u) { assert(tv); - if (u == USEC_INFINITY|| + if (u == USEC_INFINITY || u / USEC_PER_SEC > TIME_T_MAX) { tv->tv_sec = (time_t) -1; tv->tv_usec = (suseconds_t) -1; @@ -560,15 +561,29 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { uint64_t a, b; + int r, pos; assert(value); assert(t); - if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) { - log_debug("Failed to parse dual timestamp value \"%s\": %m", value); + pos = strspn(value, WHITESPACE); + if (value[pos] == '-') + return -EINVAL; + pos += strspn(value + pos, DIGITS); + pos += strspn(value + pos, WHITESPACE); + if (value[pos] == '-') + return -EINVAL; + + r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); + if (r != 2) { + log_debug("Failed to parse dual timestamp value \"%s\".", value); return -EINVAL; } + if (value[pos] != '\0') + /* trailing garbage */ + return -EINVAL; + t->realtime = a; t->monotonic = b; @@ -587,7 +602,7 @@ int timestamp_deserialize(const char *value, usec_t *timestamp) { return r; } -int parse_timestamp(const char *t, usec_t *usec) { +static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; const int nr; @@ -608,7 +623,7 @@ int parse_timestamp(const char *t, usec_t *usec) { { "Sat", 6 }, }; - const char *k, *utc, *tzn = NULL; + const char *k, *utc = NULL, *tzn = NULL; struct tm tm, copy; time_t x; usec_t x_usec, plus = 0, minus = 0, ret; @@ -636,84 +651,86 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - if (t[0] == '@') + if (t[0] == '@' && !with_tz) return parse_sec(t + 1, usec); ret = now(CLOCK_REALTIME); - if (streq(t, "now")) - goto finish; + if (!with_tz) { + if (streq(t, "now")) + goto finish; - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; + else if (t[0] == '+') { + r = parse_sec(t+1, &plus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; + } else if (t[0] == '-') { + r = parse_sec(t+1, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " ago"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &minus); - if (r < 0) - return r; + r = parse_sec(t, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " left"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &plus); - if (r < 0) - return r; + r = parse_sec(t, &plus); + if (r < 0) + return r; - goto finish; - } + goto finish; + } - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; + /* See if the timestamp is suffixed with UTC */ + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, utc - t); + else { + const char *e = NULL; + int j; - tzset(); + tzset(); - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ - for (j = 0; j <= 1; j++) { + for (j = 0; j <= 1; j++) { - if (isempty(tzname[j])) - continue; + if (isempty(tzname[j])) + continue; - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; + e = endswith_no_case(t, tzname[j]); + if (!e) + continue; + if (e == t) + continue; + if (e[-1] != ' ') + continue; - break; - } + break; + } - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; + if (IN_SET(j, 0, 1)) { + /* Found one of the two timezones specified. */ + t = strndupa(t, e - t - 1); + dst = j; + tzn = tzname[j]; + } } } @@ -724,7 +741,7 @@ int parse_timestamp(const char *t, usec_t *usec) { return -EINVAL; tm.tm_isdst = dst; - if (tzn) + if (!with_tz && tzn) tm.tm_zone = tzn; if (streq(t, "today")) { @@ -837,11 +854,11 @@ parse_usec: } from_tm: - x = mktime_or_timegm(&tm, utc); - if (x < 0) + if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - if (weekday >= 0 && tm.tm_wday != weekday) + x = mktime_or_timegm(&tm, utc); + if (x < 0) return -EINVAL; ret = (usec_t) x * USEC_PER_SEC + x_usec; @@ -855,16 +872,85 @@ finish: if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) return -EINVAL; - if (ret > minus) + if (ret >= minus) ret -= minus; else - ret = 0; + return -EINVAL; *usec = ret; return 0; } +typedef struct ParseTimestampResult { + usec_t usec; + int return_value; +} ParseTimestampResult; + +int parse_timestamp(const char *t, usec_t *usec) { + char *last_space, *tz = NULL; + ParseTimestampResult *shared, tmp; + int r; + pid_t pid; + + last_space = strrchr(t, ' '); + if (last_space != NULL && timezone_is_valid(last_space + 1)) + tz = last_space + 1; + + if (tz == NULL || endswith_no_case(t, " UTC")) + return parse_timestamp_impl(t, usec, false); + + shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + return negative_errno(); + + pid = fork(); + + if (pid == -1) { + int fork_errno = errno; + (void) munmap(shared, sizeof *shared); + return -fork_errno; + } + + if (pid == 0) { + bool with_tz = true; + + if (setenv("TZ", tz, 1) != 0) { + shared->return_value = negative_errno(); + _exit(EXIT_FAILURE); + } + + tzset(); + + /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. + * Otherwise just cut it off */ + with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); + + /*cut off the timezone if we dont need it*/ + if (with_tz) + t = strndupa(t, last_space - t); + + shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(pid, NULL); + if (r < 0) { + (void) munmap(shared, sizeof *shared); + return r; + } + + tmp = *shared; + if (munmap(shared, sizeof *shared) != 0) + return negative_errno(); + + if (tmp.return_value == 0) + *usec = tmp.usec; + + return tmp.return_value; +} + static char* extract_multiplier(char *p, usec_t *multiplier) { static const struct { const char *suffix; @@ -1000,6 +1086,20 @@ int parse_sec(const char *t, usec_t *usec) { return parse_time(t, usec, USEC_PER_SEC); } +int parse_sec_fix_0(const char *t, usec_t *usec) { + assert(t); + assert(usec); + + t += strspn(t, WHITESPACE); + + if (streq(t, "0")) { + *usec = USEC_INFINITY; + return 0; + } + + return parse_sec(t, usec); +} + int parse_nsec(const char *t, nsec_t *nsec) { static const struct { const char *suffix; @@ -1218,7 +1318,7 @@ bool timezone_is_valid(const char *name) { if (!(*p >= '0' && *p <= '9') && !(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && - !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) + !IN_SET(*p, '-', '_', '+', '/')) return false; if (*p == '/') { @@ -1344,4 +1444,23 @@ unsigned long usec_to_jiffies(usec_t u) { return DIV_ROUND_UP(u , USEC_PER_SEC / hz); } + +usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { + usec_t a, b; + + if (x == USEC_INFINITY) + return USEC_INFINITY; + if (map_clock_id(from) == map_clock_id(to)) + return x; + + a = now(from); + b = now(to); + + if (x > a) + /* x lies in the future */ + return usec_add(b, usec_sub_unsigned(x, a)); + else + /* x lies in the past */ + return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index 7463507f..73f7e400 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -133,6 +133,7 @@ int timestamp_deserialize(const char *value, usec_t *timestamp); int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); +int parse_sec_fix_0(const char *t, usec_t *usec); int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); @@ -145,9 +146,7 @@ bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); clockid_t clock_boottime_or_monotonic(void); -#define xstrftime(buf, fmt, tm) \ - assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ - "xstrftime: " #buf "[] must be big enough") +usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); int get_timezone(char **timezone); @@ -169,19 +168,23 @@ static inline usec_t usec_add(usec_t a, usec_t b) { return c; } -static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); +static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ return USEC_INFINITY; - - if (timestamp < (usec_t) delta) + if (timestamp < delta) return 0; return timestamp - delta; } +static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { + if (delta < 0) + return usec_add(timestamp, (usec_t) (-delta)); + else + return usec_sub_unsigned(timestamp, (usec_t) delta); +} + #if SIZEOF_TIME_T == 8 /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year * territory. However, since we want to stay away from this in all timezones we take one day off. */ diff --git a/src/systemd/src/basic/unaligned.h b/src/systemd/src/basic/unaligned.h deleted file mode 100644 index 7c847a3c..00000000 --- a/src/systemd/src/basic/unaligned.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <endian.h> -#include <stdint.h> - -/* BE */ - -static inline uint16_t unaligned_read_be16(const void *_u) { - const uint8_t *u = _u; - - return (((uint16_t) u[0]) << 8) | - ((uint16_t) u[1]); -} - -static inline uint32_t unaligned_read_be32(const void *_u) { - const uint8_t *u = _u; - - return (((uint32_t) unaligned_read_be16(u)) << 16) | - ((uint32_t) unaligned_read_be16(u + 2)); -} - -static inline uint64_t unaligned_read_be64(const void *_u) { - const uint8_t *u = _u; - - return (((uint64_t) unaligned_read_be32(u)) << 32) | - ((uint64_t) unaligned_read_be32(u + 4)); -} - -static inline void unaligned_write_be16(void *_u, uint16_t a) { - uint8_t *u = _u; - - u[0] = (uint8_t) (a >> 8); - u[1] = (uint8_t) a; -} - -static inline void unaligned_write_be32(void *_u, uint32_t a) { - uint8_t *u = _u; - - unaligned_write_be16(u, (uint16_t) (a >> 16)); - unaligned_write_be16(u + 2, (uint16_t) a); -} - -static inline void unaligned_write_be64(void *_u, uint64_t a) { - uint8_t *u = _u; - - unaligned_write_be32(u, (uint32_t) (a >> 32)); - unaligned_write_be32(u + 4, (uint32_t) a); -} - -/* LE */ - -static inline uint16_t unaligned_read_le16(const void *_u) { - const uint8_t *u = _u; - - return (((uint16_t) u[1]) << 8) | - ((uint16_t) u[0]); -} - -static inline uint32_t unaligned_read_le32(const void *_u) { - const uint8_t *u = _u; - - return (((uint32_t) unaligned_read_le16(u + 2)) << 16) | - ((uint32_t) unaligned_read_le16(u)); -} - -static inline uint64_t unaligned_read_le64(const void *_u) { - const uint8_t *u = _u; - - return (((uint64_t) unaligned_read_le32(u + 4)) << 32) | - ((uint64_t) unaligned_read_le32(u)); -} - -static inline void unaligned_write_le16(void *_u, uint16_t a) { - uint8_t *u = _u; - - u[0] = (uint8_t) a; - u[1] = (uint8_t) (a >> 8); -} - -static inline void unaligned_write_le32(void *_u, uint32_t a) { - uint8_t *u = _u; - - unaligned_write_le16(u, (uint16_t) a); - unaligned_write_le16(u + 2, (uint16_t) (a >> 16)); -} - -static inline void unaligned_write_le64(void *_u, uint64_t a) { - uint8_t *u = _u; - - unaligned_write_le32(u, (uint32_t) a); - unaligned_write_le32(u + 4, (uint32_t) (a >> 32)); -} - -#if __BYTE_ORDER == __BIG_ENDIAN -#define unaligned_read_ne16 unaligned_read_be16 -#define unaligned_read_ne32 unaligned_read_be32 -#define unaligned_read_ne64 unaligned_read_be64 - -#define unaligned_write_ne16 unaligned_write_be16 -#define unaligned_write_ne32 unaligned_write_be32 -#define unaligned_write_ne64 unaligned_write_be64 -#else -#define unaligned_read_ne16 unaligned_read_le16 -#define unaligned_read_ne32 unaligned_read_le32 -#define unaligned_read_ne64 unaligned_read_le64 - -#define unaligned_write_ne16 unaligned_write_le16 -#define unaligned_write_ne32 unaligned_write_le32 -#define unaligned_write_ne64 unaligned_write_le64 -#endif diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c index a6bdda6e..ff281e43 100644 --- a/src/systemd/src/basic/utf8.c +++ b/src/systemd/src/basic/utf8.c @@ -75,7 +75,7 @@ static bool unichar_is_control(char32_t ch) { '\t' is in C0 range, but more or less harmless and commonly used. */ - return (ch < ' ' && ch != '\t' && ch != '\n') || + return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || (0x7F <= ch && ch <= 0x9F); } diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 563ee87e..c8a22d68 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -36,6 +36,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "btrfs-util.h" #include "build.h" #include "cgroup-util.h" #include "def.h" @@ -181,15 +182,12 @@ int block_get_whole_disk(dev_t d, dev_t *ret) { } bool kexec_loaded(void) { - bool loaded = false; - char *s; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { - if (s[0] == '1') - loaded = true; - free(s); - } - return loaded; + _cleanup_free_ char *s = NULL; + + if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) + return false; + + return s[0] == '1'; } int prot_from_flags(int flags) { @@ -224,7 +222,7 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa /* Spawns a temporary TTY agent, making sure it goes away when * we go away */ - parent_pid = getpid(); + parent_pid = getpid_cached(); /* First we temporarily block all signals, so that the new * child has them blocked initially. This way, we can be sure @@ -382,7 +380,7 @@ int on_ac_power(void) { device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); if (device < 0) { - if (errno == ENOENT || errno == ENOTDIR) + if (IN_SET(errno, ENOENT, ENOTDIR)) continue; return -errno; @@ -546,7 +544,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) return -ENOMEM; - r = files_same(userns_fd_path, "/proc/self/ns/user"); + r = files_same(userns_fd_path, "/proc/self/ns/user", 0); if (r < 0) return r; if (r) @@ -724,4 +722,134 @@ int version(void) { SYSTEMD_FEATURES); return 0; } + +int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) { + _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; + + /* We found a device backed by multiple other devices. We don't really support automatic + * discovery on such setups, with the exception of dm-verity partitions. In this case there are + * two backing devices: the data partition and the hash partition. We are fine with such + * setups, however, only if both partitions are on the same physical device. Hence, let's + * verify this. */ + + u = strjoin(p, "/", de->d_name, "/../dev"); + if (!u) + return -ENOMEM; + + v = strjoin(p, "/", found->d_name, "/../dev"); + if (!v) + return -ENOMEM; + + r = read_one_line_file(u, &a); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", u); + goto fallback; + } + + r = read_one_line_file(v, &b); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", v); + goto fallback; + } + + /* Check if the parent device is the same. If not, then the two backing devices are on + * different physical devices, and we don't support that. */ + if (!streq(a, b)) + goto fallback; + } + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index c7da6c39..b31dfd1c 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -192,3 +192,6 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); int update_reboot_parameter_and_warn(const char *param); int version(void); + +int get_block_device(const char *path, dev_t *dev); +int get_block_device_harder(const char *path, dev_t *dev); diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 82cae230..7847ce07 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -75,6 +75,7 @@ struct sd_dhcp_lease { uint16_t mtu; /* 0 if unset */ char *domainname; + char **search_domains; char *hostname; char *root_path; @@ -92,6 +93,7 @@ struct sd_dhcp_lease { int dhcp_lease_new(sd_dhcp_lease **ret); int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); +int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains); int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 7ad0ec37..f01b2cfe 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -110,14 +110,16 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (r < 0) return -errno; - link->ll.sll_family = AF_PACKET; - link->ll.sll_protocol = htobe16(ETH_P_IP); - link->ll.sll_ifindex = ifindex; - link->ll.sll_hatype = htobe16(arp_type); - link->ll.sll_halen = mac_addr_len; + link->ll = (struct sockaddr_ll) { + .sll_family = AF_PACKET, + .sll_protocol = htobe16(ETH_P_IP), + .sll_ifindex = ifindex, + .sll_hatype = htobe16(arp_type), + .sll_halen = mac_addr_len, + }; memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len); - r = bind(s, &link->sa, sizeof(link->ll)); + r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll)); if (r < 0) return -errno; @@ -223,7 +225,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, assert(packet); assert(len); - r = sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)); + r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)); if (r < 0) return -errno; diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 27410520..1bb1bfcf 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -36,8 +36,8 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, size_t offset = 0; int r; - assert(op == BOOTREQUEST || op == BOOTREPLY); - assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND); + assert(IN_SET(op, BOOTREQUEST, BOOTREPLY)); + assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND)); message->op = op; message->htype = arp_type; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index afede1e7..c560a864 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -251,10 +251,9 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { log_lldp("End marker TLV not zero-sized, ignoring datagram."); return -EBADMSG; } - if (left != 0) { - log_lldp("Trailing garbage in datagram, ignoring datagram."); - return -EBADMSG; - } + + /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 + * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ goto end_marker; diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index 285a73e3..de37b9f0 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -351,8 +351,47 @@ int config_parse_iaid(const char *unit, return 0; } + +int config_parse_bridge_port_priority( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint16_t i; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou16(rvalue, &i); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse bridge port priority, ignoring: %s", rvalue); + return 0; + } + + if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Bridge port priority is larger than maximum %u, ignoring: %s", LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue); + return 0; + } + + *((uint16_t *)data) = i; + + return 0; +} #endif /* NM_IGNORED */ + void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { unsigned i; diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 5bcd5771..4666f174 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -26,6 +26,9 @@ #include "condition.h" #include "udev.h" +#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 +#define LINK_BRIDGE_PORT_PRIORITY_MAX 63 + bool net_match_config(const struct ether_addr *match_mac, char * const *match_path, char * const *match_driver, @@ -62,6 +65,10 @@ int config_parse_iaid(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bridge_port_priority(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result); const char *net_get_name(struct udev_device *device); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 17393e20..fc6ad422 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -64,6 +64,7 @@ struct sd_dhcp_client { uint8_t *req_opts; size_t req_opts_allocated; size_t req_opts_size; + bool anonymize; be32_t last_addr; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; @@ -118,6 +119,32 @@ static const uint8_t default_req_opts[] = { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, }; +/* RFC7844 section 3: + MAY contain the Parameter Request List option. + RFC7844 section 3.6: + The client intending to protect its privacy SHOULD only request a + minimal number of options in the PRL and SHOULD also randomly shuffle + the ordering of option codes in the PRL. If this random ordering + cannot be implemented, the client MAY order the option codes in the + PRL by option code number (lowest to highest). +*/ +/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */ +static const uint8_t default_req_opts_anonymize[] = { + SD_DHCP_OPTION_SUBNET_MASK, /* 1 */ + SD_DHCP_OPTION_ROUTER, /* 3 */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ + SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ + SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ + SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ + SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */ +}; + static int client_receive_message_raw( sd_event_source *s, int fd, @@ -432,9 +459,7 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); - if (client->state != DHCP_STATE_BOUND && - client->state != DHCP_STATE_RENEWING && - client->state != DHCP_STATE_REBINDING) + if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) return -EADDRNOTAVAIL; if (ret) @@ -507,7 +532,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(type == DHCP_DISCOVER || type == DHCP_REQUEST); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -592,11 +617,18 @@ static int client_message_init( it MUST include that list in any subsequent DHCPREQUEST messages. */ - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, - client->req_opts_size, client->req_opts); - if (r < 0) - return r; + + /* RFC7844 section 3: + MAY contain the Parameter Request List option. */ + /* NOTE: in case that there would be an option to do not send + * any PRL at all, the size should be checked before sending */ + if (client->req_opts_size > 0) { + r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, + client->req_opts_size, client->req_opts); + if (r < 0) + return r; + } /* RFC2131 section 3.5: The client SHOULD include the ’maximum DHCP message size’ option to @@ -620,12 +652,16 @@ static int client_message_init( Maximum DHCP Message Size option is the total maximum packet size, including IP and UDP headers.) */ - max_size = htobe16(size); - r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, - SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, - 2, &max_size); - if (r < 0) - return r; + /* RFC7844 section 3: + SHOULD NOT contain any other option. */ + if (!client->anonymize) { + max_size = htobe16(size); + r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, + 2, &max_size); + if (r < 0) + return r; + } *_optlen = optlen; *_optoffset = optoffset; @@ -675,8 +711,7 @@ static int client_send_discover(sd_dhcp_client *client) { int r; assert(client); - assert(client->state == DHCP_STATE_INIT || - client->state == DHCP_STATE_SELECTING); + assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING)); r = client_message_init(client, &discover, DHCP_DISCOVER, &optlen, &optoffset); @@ -1130,7 +1165,7 @@ static int client_start_delayed(sd_dhcp_client *client) { } client->fd = r; - if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT) + if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT)) client->start_time = now(clock_boottime_or_monotonic()); return client_initialize_events(client, client_receive_message_raw); @@ -1656,10 +1691,11 @@ static int client_receive_message_udp( len = recv(fd, message, buflen, 0); if (len < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; - return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m"); + return log_dhcp_client_errno(client, errno, + "Could not receive message from UDP socket: %m"); } if ((size_t) len < sizeof(DHCPMessage)) { log_dhcp_client(client, "Too small to be a DHCP message: ignoring"); @@ -1749,12 +1785,11 @@ static int client_receive_message_raw( len = recvmsg(fd, &msg, 0); if (len < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; - log_dhcp_client(client, "Could not receive message from raw socket: %m"); - - return -errno; + return log_dhcp_client_errno(client, errno, + "Could not receive message from raw socket: %m"); } else if ((size_t)len < sizeof(DHCPPacket)) return 0; @@ -1787,7 +1822,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { if (r < 0) return r; - if (client->last_addr) + /* RFC7844 section 3.3: + SHOULD perform a complete four-way handshake, starting with a + DHCPDISCOVER, to obtain a new address lease. If the client can + ascertain that this is exactly the same network to which it was + previously connected, and if the link-layer address did not change, + the client MAY issue a DHCPREQUEST to try to reclaim the current + address. */ + if (client->last_addr && !client->anonymize) client->state = DHCP_STATE_INIT_REBOOT; r = client_start(client); @@ -1879,7 +1921,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { return mfree(client); } -int sd_dhcp_client_new(sd_dhcp_client **ret) { +int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; assert_return(ret, -EINVAL); @@ -1896,8 +1938,15 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) { client->mtu = DHCP_DEFAULT_MIN_SIZE; client->port = DHCP_PORT_CLIENT; - client->req_opts_size = ELEMENTSOF(default_req_opts); - client->req_opts = memdup(default_req_opts, client->req_opts_size); + client->anonymize = !!anonymize; + /* NOTE: this could be moved to a function. */ + if (anonymize) { + client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize); + client->req_opts = memdup(default_req_opts_anonymize, client->req_opts_size); + } else { + client->req_opts_size = ELEMENTSOF(default_req_opts); + client->req_opts = memdup(default_req_opts, client->req_opts_size); + } if (!client->req_opts) return -ENOMEM; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index 5a3bff2f..c00190b5 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -233,6 +233,21 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { return (int) lease->static_route_size; } +int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { + unsigned r; + + assert_return(lease, -EINVAL); + assert_return(domains, -EINVAL); + + r = strv_length(lease->search_domains); + if (r > 0) { + *domains = lease->search_domains; + return (int) r; + } + + return -ENODATA; +} + int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { assert_return(lease, -EINVAL); assert_return(data, -EINVAL); @@ -284,6 +299,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { free(lease->static_route); free(lease->client_id); free(lease->vendor_specific); + strv_free(lease->search_domains); return mfree(lease); } @@ -457,7 +473,7 @@ static int lease_parse_routes( struct sd_dhcp_route *route = *routes + *routes_size; int r; - r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); + r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); if (r < 0) { log_debug("Failed to determine destination prefix length from class based IP, ignoring"); continue; @@ -596,6 +612,11 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); + if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) { + log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE); + lease->mtu = DHCP_DEFAULT_MIN_SIZE; + } + break; case SD_DHCP_OPTION_DOMAIN_NAME: @@ -607,6 +628,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; + case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST: + r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains); + if (r < 0) + log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m"); + break; + case SD_DHCP_OPTION_HOST_NAME: r = lease_parse_domain(option, len, &lease->hostname); if (r < 0) { @@ -698,6 +725,96 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void return 0; } +/* Parses compressed domain names. */ +int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) { + _cleanup_strv_free_ char **names = NULL; + size_t pos = 0, cnt = 0; + int r; + + assert(domains); + assert_return(option && len > 0, -ENODATA); + + while (pos < len) { + _cleanup_free_ char *name = NULL; + size_t n = 0, allocated = 0; + size_t jump_barrier = pos, next_chunk = 0; + bool first = true; + + for (;;) { + uint8_t c; + c = option[pos++]; + + if (c == 0) { + /* End of name */ + break; + } else if (c <= 63) { + const char *label; + + /* Literal label */ + label = (const char*) (option + pos); + pos += c; + if (pos >= len) + return -EBADMSG; + + if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + name[n++] = '.'; + + r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + } else if ((c & 0xc0) == 0xc0) { + /* Pointer */ + + uint8_t d; + uint16_t ptr; + + if (pos >= len) + return -EBADMSG; + + d = option[pos++]; + ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; + + /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ + if (ptr >= jump_barrier) + return -EBADMSG; + jump_barrier = ptr; + + /* Save current location so we don't end up re-parsing what's parsed so far. */ + if (next_chunk == 0) + next_chunk = pos; + + pos = ptr; + } else + return -EBADMSG; + } + + if (!GREEDY_REALLOC(name, allocated, n + 1)) + return -ENOMEM; + name[n] = 0; + + r = strv_extend(&names, name); + if (r < 0) + return r; + + cnt++; + + if (next_chunk != 0) + pos = next_chunk; + } + + *domains = names; + names = NULL; + + return cnt; +} + int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { struct sd_dhcp_raw_option *cur, *option; @@ -753,6 +870,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; + char **search_domains = NULL; uint32_t t1, t2, lifetime; int r; @@ -810,22 +928,29 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_dns(lease, &addresses); if (r > 0) { - fputs("DNS=", f); + fputs_unlocked("DNS=", f); serialize_in_addrs(f, addresses, r); - fputs("\n", f); + fputs_unlocked("\n", f); } r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { - fputs("NTP=", f); + fputs_unlocked("NTP=", f); serialize_in_addrs(f, addresses, r); - fputs("\n", f); + fputs_unlocked("\n", f); } r = sd_dhcp_lease_get_domainname(lease, &string); if (r >= 0) fprintf(f, "DOMAINNAME=%s\n", string); + r = sd_dhcp_lease_get_search_domains(lease, &search_domains); + if (r > 0) { + fputs_unlocked("DOMAIN_SEARCH_LIST=", f); + fputstrv(f, search_domains, NULL, NULL); + fputs_unlocked("\n", f); + } + r = sd_dhcp_lease_get_hostname(lease, &string); if (r >= 0) fprintf(f, "HOSTNAME=%s\n", string); @@ -907,6 +1032,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *ntp = NULL, *mtu = NULL, *routes = NULL, + *domains = NULL, *client_id_hex = NULL, *vendor_specific_hex = NULL, *lifetime = NULL, @@ -935,6 +1061,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "MTU", &mtu, "DOMAINNAME", &lease->domainname, "HOSTNAME", &lease->hostname, + "DOMAIN_SEARCH_LIST", &domains, "ROOT_PATH", &lease->root_path, "ROUTES", &routes, "CLIENTID", &client_id_hex, @@ -1040,6 +1167,18 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu); } + if (domains) { + _cleanup_strv_free_ char **a = NULL; + a = strv_split(domains, " "); + if (!a) + return -ENOMEM; + + if (!strv_isempty(a)) { + lease->search_domains = a; + a = NULL; + } + } + if (routes) { r = deserialize_dhcp_routes( &lease->static_route, @@ -1116,7 +1255,7 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { address.s_addr = lease->address; /* fall back to the default subnet masks based on address class */ - r = in_addr_default_subnet_mask(&address, &mask); + r = in4_addr_default_subnet_mask(&address, &mask); if (r < 0) return r; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index fa508d68..f512b65b 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -944,7 +944,7 @@ static int client_receive_message( len = recv(fd, message, buflen, 0); if (len < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index 3976768b..694384b5 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -356,7 +356,7 @@ static int ipv4acd_on_packet( n = recv(fd, &packet, sizeof(struct ether_arp), 0); if (n < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m"); diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 2a64a99b..31e24486 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -220,7 +220,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); if (length < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 3f7b703e..9dfe6847 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -438,7 +438,7 @@ _public_ int sd_event_new(sd_event** ret) { e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; - e->original_pid = getpid(); + e->original_pid = getpid_cached(); e->perturb = USEC_INFINITY; r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); @@ -495,7 +495,7 @@ static bool event_pid_changed(sd_event *e) { /* We don't support people creating an event loop and keeping * it around over a fork(). Let's complain. */ - return e->original_pid != getpid(); + return e->original_pid != getpid_cached(); } static void source_io_unregister(sd_event_source *s) { @@ -1601,7 +1601,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { int r; assert_return(s, -EINVAL); - assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL); + assert_return(IN_SET(m, SD_EVENT_OFF, SD_EVENT_ON, SD_EVENT_ONESHOT), -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); /* If we are dead anyway, we are fine with turning off @@ -2054,7 +2054,7 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) { ss = read(fd, &x, sizeof(x)); if (ss < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return -errno; @@ -2143,10 +2143,7 @@ static int process_child(sd_event *e) { return -errno; if (s->child.siginfo.si_pid != 0) { - bool zombie = - s->child.siginfo.si_code == CLD_EXITED || - s->child.siginfo.si_code == CLD_KILLED || - s->child.siginfo.si_code == CLD_DUMPED; + bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED); if (!zombie && (s->child.options & WEXITED)) { /* If the child isn't dead then let's @@ -2197,7 +2194,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { n = read(d->fd, &si, sizeof(si)); if (n < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return read_one; return -errno; @@ -2239,7 +2236,7 @@ static int source_dispatch(sd_event_source *s) { * the event. */ saved_type = s->type; - if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) { + if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { r = source_set_pending(s, false); if (r < 0) return r; @@ -2291,9 +2288,7 @@ static int source_dispatch(sd_event_source *s) { case SOURCE_CHILD: { bool zombie; - zombie = s->child.siginfo.si_code == CLD_EXITED || - s->child.siginfo.si_code == CLD_KILLED || - s->child.siginfo.si_code == CLD_DUMPED; + zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED); r = s->child.callback(s, &s->child.siginfo, s->userdata); diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index 57525ddb..19277021 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -78,7 +78,7 @@ bool id128_is_valid(const char *s) { for (i = 0; i < l; i++) { char c = s[i]; - if ((i == 8 || i == 13 || i == 18 || i == 23)) { + if (IN_SET(i, 8, 13, 18, 23)) { if (c != '-') return false; } else { diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 9920bfdf..052110d5 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -70,7 +70,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { if (i == 8) is_guid = true; - else if (i == 13 || i == 18 || i == 23) { + else if (IN_SET(i, 13, 18, 23)) { if (!is_guid) return -EINVAL; } else @@ -294,7 +294,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - r = dev_urandom(&t, sizeof(t)); + r = acquire_random_bytes(&t, sizeof t, true); if (r < 0) return r; diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index 5ed27191..c313a033 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -19,9 +19,13 @@ #include "nm-sd-adapt.h" -#ifdef HAVE_LIBIDN -#include <idna.h> -#include <stringprep.h> +#if 0 /* NM_IGNORED */ +#if HAVE_LIBIDN2 +# include <idn2.h> +#elif HAVE_LIBIDN +# include <idna.h> +# include <stringprep.h> +#endif #endif #include <endian.h> @@ -76,7 +80,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { /* Ending NUL */ return -EINVAL; - else if (*n == '\\' || *n == '.') { + else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ if (d) @@ -144,6 +148,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { return r; } +#if 0 /* NM_IGNORED */ /* @label_terminal: terminal character of a label, updated to point to the terminal character of * the previous label (always skipping one dot) or to NULL if there are no more * labels. */ @@ -164,7 +169,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha } terminal = *label_terminal; - assert(*terminal == '.' || *terminal == 0); + assert(IN_SET(*terminal, 0, '.')); /* Skip current terminal character (and accept domain names ending it ".") */ if (*terminal == 0) @@ -209,6 +214,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha return r; } +#endif /* NM_IGNORED */ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; @@ -228,7 +234,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { q = dest; while (l > 0) { - if (*p == '.' || *p == '\\') { + if (IN_SET(*p, '.', '\\')) { /* Dot or backslash */ @@ -240,8 +246,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { sz -= 2; - } else if (*p == '_' || - *p == '-' || + } else if (IN_SET(*p, '_', '-') || (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) { @@ -277,6 +282,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { return (int) (q - dest); } +#if 0 /* NM_IGNORED */ int dns_label_escape_new(const char *p, size_t l, char **ret) { _cleanup_free_ char *s = NULL; int r; @@ -301,8 +307,8 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { return r; } +#if HAVE_LIBIDN int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN _cleanup_free_ uint32_t *input = NULL; size_t input_size, l; const char *p; @@ -350,13 +356,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded decoded[l] = 0; return (int) l; -#else - return 0; -#endif } int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN size_t input_size, output_size; _cleanup_free_ uint32_t *input = NULL; _cleanup_free_ char *result = NULL; @@ -401,10 +403,9 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, decoded[w] = 0; return w; -#else - return 0; -#endif } +#endif +#endif /* NM_IGNORED */ int dns_name_concat(const char *a, const char *b, char **_ret) { _cleanup_free_ char *ret = NULL; @@ -1279,6 +1280,49 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } int dns_name_apply_idna(const char *name, char **ret) { + /* Return negative on error, 0 if not implemented, positive on success. */ + +#if HAVE_LIBIDN2 + int r; + _cleanup_free_ char *t = NULL; + + assert(name); + assert(ret); + + r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, + IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + log_debug("idn2_lookup_u8: %s → %s", name, t); + if (r == IDN2_OK) { + if (!startswith(name, "xn--")) { + _cleanup_free_ char *s = NULL; + + r = idn2_to_unicode_8z8z(t, &s, 0); + if (r != IDN2_OK) { + log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s", + t, r, idn2_strerror(r)); + return 0; + } + + if (!streq_ptr(name, s)) { + log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.", + name, t, s); + return 0; + } + } + + *ret = t; + t = NULL; + return 1; /* *ret has been written */ + } + + log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r)); + if (r == IDN2_2HYPHEN) + /* The name has two hypens — forbidden by IDNA2008 in some cases */ + return 0; + if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) + return -ENOSPC; + return -EINVAL; +#elif HAVE_LIBIDN _cleanup_free_ char *buf = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -1314,7 +1358,7 @@ int dns_name_apply_idna(const char *name, char **ret) { else buf[n++] = '.'; - n +=r; + n += r; } if (n > DNS_HOSTNAME_MAX) @@ -1327,7 +1371,10 @@ int dns_name_apply_idna(const char *name, char **ret) { *ret = buf; buf = NULL; - return (int) n; + return 1; +#else + return 0; +#endif } int dns_name_is_valid_or_address(const char *name) { diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 03f16036..d1a99be7 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -51,8 +51,12 @@ static inline int dns_name_parent(const char **name) { return dns_label_unescape(name, NULL, DNS_LABEL_MAX); } +#if 0 /* NM_IGNORED */ +#if HAVE_LIBIDN int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); +#endif +#endif /* NM_IGNORED */ int dns_name_concat(const char *a, const char *b, char **ret); diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index ffe7f836..5e46d8d0 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -58,9 +58,17 @@ enum { SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, SD_DHCP_OPTION_INTERFACE_MTU = 26, SD_DHCP_OPTION_BROADCAST = 28, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_ROUTER_DISCOVER = 31, SD_DHCP_OPTION_STATIC_ROUTE = 33, SD_DHCP_OPTION_NTP_SERVER = 42, SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_NETBIOS_NODETYPE = 46, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_NETBIOS_SCOPE = 47, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, SD_DHCP_OPTION_OVERLOAD = 52, @@ -76,8 +84,13 @@ enum { SD_DHCP_OPTION_FQDN = 81, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, + SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, SD_DHCP_OPTION_PRIVATE_BASE = 224, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, + /* Windows 10 option to send when Anonymize=true */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, SD_DHCP_OPTION_PRIVATE_LAST = 254, SD_DHCP_OPTION_END = 255, }; @@ -145,7 +158,9 @@ int sd_dhcp_client_start(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); -int sd_dhcp_client_new(sd_dhcp_client **ret); +/* NOTE: anonymize parameter is used to initialize PRL memory with different + * options when using RFC7844 Anonymity Profiles */ +int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize); int sd_dhcp_client_attach_event( sd_dhcp_client *client, diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 2f565ca8..7ab99ccc 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -49,6 +49,7 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); +int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index edb5c1aa..80e17d01 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -26,6 +26,7 @@ #include "nm-test-device.h" #include "platform/nm-fake-platform.h" #include "nm-bus-manager.h" +#include "nm-connectivity.h" #include "nm-test-utils-core.h" @@ -318,6 +319,40 @@ test_config_global_dns (void) } static void +test_config_connectivity_check (void) +{ + const char *CONFIG_INTERN = BUILDDIR"/test-connectivity-check-intern.conf"; + NMConfig *config; + NMConnectivity *connectivity; + + g_assert (g_file_set_contents (CONFIG_INTERN, "", 0, NULL)); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", CONFIG_INTERN, NULL, + "/no/such/dir", "", NULL); + connectivity = nm_connectivity_get(); + + g_assert (nm_connectivity_check_enabled (connectivity)); + + /* disable connectivity checking */ + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + nm_config_set_connectivity_check_enabled (config, FALSE); + g_test_assert_expected_messages (); + + g_assert (!nm_connectivity_check_enabled (connectivity)); + + /* re-enable connectivity checking */ + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + nm_config_set_connectivity_check_enabled (config, TRUE); + g_test_assert_expected_messages (); + + g_assert (nm_connectivity_check_enabled (connectivity)); + + g_object_unref (connectivity); + g_object_unref (config); + + g_assert (remove (CONFIG_INTERN) == 0); +} + +static void test_config_no_auto_default (void) { NMConfig *config; @@ -1018,6 +1053,7 @@ main (int argc, char **argv) g_test_add_func ("/config/set-values", test_config_set_values); g_test_add_func ("/config/global-dns", test_config_global_dns); + g_test_add_func ("/config/connectivity-check", test_config_connectivity_check); g_test_add_func ("/config/signal", test_config_signal); diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 492e1b1f..c5d26163 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -29,7 +29,6 @@ #include <fcntl.h> #include "NetworkManagerUtils.h" -#include "nm-multi-index.h" #include "nm-test-utils-core.h" @@ -499,362 +498,6 @@ test_nm_ethernet_address_is_valid (void) /*****************************************************************************/ -typedef struct { - union { - NMMultiIndexId id_base; - guint bucket; - }; -} NMMultiIndexIdTest; - -typedef struct { - guint64 buckets; - gpointer ptr_value; -} NMMultiIndexTestValue; - -static gboolean -_mi_value_bucket_has (const NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - return (value->buckets & (((guint64) 0x01) << bucket)) != 0; -} - -static gboolean -_mi_value_bucket_set (NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - if (_mi_value_bucket_has (value, bucket)) - return FALSE; - - value->buckets |= (((guint64) 0x01) << bucket); - return TRUE; -} - -static gboolean -_mi_value_bucket_unset (NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - if (!_mi_value_bucket_has (value, bucket)) - return FALSE; - - value->buckets &= ~(((guint64) 0x01) << bucket); - return TRUE; -} - -static guint -_mi_idx_hash (const NMMultiIndexIdTest *id) -{ - g_assert (id && id->bucket < 64); - return id->bucket; -} - -static gboolean -_mi_idx_equal (const NMMultiIndexIdTest *a, const NMMultiIndexIdTest *b) -{ - g_assert (a && a->bucket < 64); - g_assert (b && b->bucket < 64); - - return a->bucket == b->bucket; -} - -static NMMultiIndexIdTest * -_mi_idx_clone (const NMMultiIndexIdTest *id) -{ - NMMultiIndexIdTest *n; - - g_assert (id && id->bucket < 64); - - n = g_new0 (NMMultiIndexIdTest, 1); - n->bucket = id->bucket; - return n; -} - -static void -_mi_idx_destroy (NMMultiIndexIdTest *id) -{ - g_assert (id && id->bucket < 64); - g_free (id); -} - -static NMMultiIndexTestValue * -_mi_create_array (guint num_values) -{ - NMMultiIndexTestValue *array = g_new0 (NMMultiIndexTestValue, num_values); - guint i; - - g_assert (num_values > 0); - - for (i = 0; i < num_values; i++) { - array[i].buckets = 0; - array[i].ptr_value = GUINT_TO_POINTER (i + 1); - } - return array; -} - -typedef struct { - guint num_values; - guint num_buckets; - NMMultiIndexTestValue *array; - int test_idx; -} NMMultiIndexAssertData; - -static gboolean -_mi_assert_index_equals_array_cb (const NMMultiIndexIdTest *id, void *const* values, guint len, NMMultiIndexAssertData *data) -{ - guint i; - gboolean has_test_idx = FALSE; - - g_assert (id && id->bucket < 64); - g_assert (data); - g_assert (values); - g_assert (len > 0); - g_assert (values[len] == NULL); - g_assert (data->test_idx >= -1 || data->test_idx < data->num_buckets); - - g_assert (id->bucket < data->num_buckets); - - for (i = 0; i < data->num_values; i++) - g_assert (!_mi_value_bucket_has (&data->array[i], id->bucket)); - - for (i = 0; i < len; i++) { - guint vi = GPOINTER_TO_UINT (values[i]); - - g_assert (vi >= 1); - g_assert (vi <= data->num_values); - vi--; - if (data->test_idx == vi) - has_test_idx = TRUE; - g_assert (data->array[vi].ptr_value == values[i]); - if (!_mi_value_bucket_set (&data->array[vi], id->bucket)) - g_assert_not_reached (); - } - g_assert ((data->test_idx == -1 && !has_test_idx) || has_test_idx); - return TRUE; -} - -static void -_mi_assert_index_equals_array (guint num_values, guint num_buckets, int test_idx, const NMMultiIndexTestValue *array, const NMMultiIndex *index) -{ - NMMultiIndexAssertData data = { - .num_values = num_values, - .num_buckets = num_buckets, - .test_idx = test_idx, - }; - NMMultiIndexIter iter; - const NMMultiIndexIdTest *id; - void *const* values; - guint len; - NMMultiIndexTestValue *v; - - data.array = _mi_create_array (num_values); - v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; - nm_multi_index_foreach (index, v, (NMMultiIndexFuncForeach) _mi_assert_index_equals_array_cb, &data); - if (test_idx >= 0) - g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); - else - g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); - g_free (data.array); - - - data.array = _mi_create_array (num_values); - v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; - nm_multi_index_iter_init (&iter, index, v); - while (nm_multi_index_iter_next (&iter, (gpointer) &id, &values, &len)) - _mi_assert_index_equals_array_cb (id, values, len, &data); - if (test_idx >= 0) - g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); - else - g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); - g_free (data.array); -} - -typedef enum { - MI_OP_ADD, - MI_OP_REMOVE, - MI_OP_MOVE, -} NMMultiIndexOperation; - -static void -_mi_rebucket (GRand *rand, guint num_values, guint num_buckets, NMMultiIndexOperation op, guint bucket, guint bucket_old, guint array_idx, NMMultiIndexTestValue *array, NMMultiIndex *index) -{ - NMMultiIndexTestValue *v; - NMMultiIndexIdTest id, id_old; - const NMMultiIndexIdTest *id_reverse; - guint64 buckets_old; - guint i; - gboolean had_bucket, had_bucket_old; - - g_assert (array_idx < num_values); - g_assert (bucket < (int) num_buckets); - - v = &array[array_idx]; - - buckets_old = v->buckets; - if (op == MI_OP_MOVE) - had_bucket_old = _mi_value_bucket_has (v, bucket_old); - else - had_bucket_old = FALSE; - had_bucket = _mi_value_bucket_has (v, bucket); - - switch (op) { - - case MI_OP_ADD: - _mi_value_bucket_set (v, bucket); - id.bucket = bucket; - if (nm_multi_index_add (index, &id.id_base, v->ptr_value)) - g_assert (!had_bucket); - else - g_assert (had_bucket); - break; - - case MI_OP_REMOVE: - _mi_value_bucket_unset (v, bucket); - id.bucket = bucket; - if (nm_multi_index_remove (index, &id.id_base, v->ptr_value)) - g_assert (had_bucket); - else - g_assert (!had_bucket); - break; - - case MI_OP_MOVE: - - _mi_value_bucket_unset (v, bucket_old); - _mi_value_bucket_set (v, bucket); - - id.bucket = bucket; - id_old.bucket = bucket_old; - - if (nm_multi_index_move (index, &id_old.id_base, &id.id_base, v->ptr_value)) { - if (bucket == bucket_old) - g_assert (had_bucket_old && had_bucket); - else - g_assert (had_bucket_old && !had_bucket); - } else { - if (bucket == bucket_old) - g_assert (!had_bucket_old && !had_bucket); - else - g_assert (!had_bucket_old || had_bucket); - } - break; - - default: - g_assert_not_reached (); - } - -#if 0 - g_print (">>> rebucket: idx=%3u, op=%3s, bucket=%3i%c -> %3i%c, buckets=%08llx -> %08llx %s\n", array_idx, - op == MI_OP_ADD ? "ADD" : (op == MI_OP_REMOVE ? "REM" : "MOV"), - bucket_old, had_bucket_old ? '*' : ' ', - bucket, had_bucket ? '*' : ' ', - (unsigned long long) buckets_old, (unsigned long long) v->buckets, - buckets_old != v->buckets ? "(changed)" : "(unchanged)"); -#endif - - id_reverse = (const NMMultiIndexIdTest *) nm_multi_index_lookup_first_by_value (index, v->ptr_value); - if (id_reverse) - g_assert (_mi_value_bucket_has (v, id_reverse->bucket)); - else - g_assert (v->buckets == 0); - - for (i = 0; i < 64; i++) { - id.bucket = i; - if (nm_multi_index_contains (index, &id.id_base, v->ptr_value)) - g_assert (_mi_value_bucket_has (v, i)); - else - g_assert (!_mi_value_bucket_has (v, i)); - } - - _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); - _mi_assert_index_equals_array (num_values, num_buckets, array_idx, array, index); - _mi_assert_index_equals_array (num_values, num_buckets, g_rand_int_range (rand, 0, num_values), array, index); -} - -static void -_mi_test_run (guint num_values, guint num_buckets) -{ - NMMultiIndex *index = nm_multi_index_new ((NMMultiIndexFuncHash) _mi_idx_hash, - (NMMultiIndexFuncEqual) _mi_idx_equal, - (NMMultiIndexFuncClone) _mi_idx_clone, - (NMMultiIndexFuncDestroy) _mi_idx_destroy); - gs_free NMMultiIndexTestValue *array = _mi_create_array (num_values); - GRand *rand = nmtst_get_rand (); - guint i, i_rd, i_idx, i_bucket; - guint num_buckets_all = num_values * num_buckets; - - g_assert (array[0].ptr_value == GUINT_TO_POINTER (1)); - - _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); - - _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 0, 0, 0, array, index); - - if (num_buckets >= 3) { - _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_MOVE, 2, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 2, 0, 0, array, index); - } - - g_assert (nm_multi_index_get_num_groups (index) == 0); - - /* randomly change the bucket of entries. */ - for (i = 0; i < 5 * num_values; i++) { - guint array_idx = g_rand_int_range (rand, 0, num_values); - guint bucket = g_rand_int_range (rand, 0, num_buckets); - NMMultiIndexOperation op = g_rand_int_range (rand, 0, MI_OP_MOVE + 1); - guint bucket_old = 0; - - if (op == MI_OP_MOVE) { - if ((g_rand_int (rand) % 2) && array[array_idx].buckets != 0) { - guint64 b; - - /* choose the highest (existing) bucket. */ - bucket_old = 0; - for (b = array[array_idx].buckets; b; b >>= 1) - bucket_old++; - } else { - /* choose a random bucket (even if the item is currently not in that bucket). */ - bucket_old = g_rand_int_range (rand, 0, num_buckets); - } - } - - _mi_rebucket (rand, num_values, num_buckets, op, bucket, bucket_old, array_idx, array, index); - } - - /* remove all elements from all buckets */ - i_rd = g_rand_int (rand); - for (i = 0; i < num_buckets_all; i++) { - i_rd = (i_rd + 101) % num_buckets_all; - i_idx = i_rd / num_buckets; - i_bucket = i_rd % num_buckets; - - if (_mi_value_bucket_has (&array[i_idx], i_bucket)) - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, i_bucket, 0, i_idx, array, index); - } - - g_assert (nm_multi_index_get_num_groups (index) == 0); - nm_multi_index_free (index); -} - -static void -test_nm_multi_index (void) -{ - guint i, j; - - for (i = 1; i < 7; i++) { - for (j = 1; j < 6; j++) - _mi_test_run (i, j); - } - _mi_test_run (50, 3); - _mi_test_run (50, 18); -} - -/*****************************************************************************/ - static void test_nm_utils_new_vlan_name (void) { @@ -909,7 +552,6 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); - g_test_add_func ("/general/nm_multi_index", test_nm_multi_index); g_test_add_func ("/general/nm_utils_new_vlan_name", test_nm_utils_new_vlan_name); return g_test_run (); diff --git a/src/tests/test-general.c b/src/tests/test-general.c index 37bbc5ca..d36a26ef 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -272,6 +272,32 @@ test_nm_utils_log_connection_diff (void) /*****************************************************************************/ +static void +do_test_sysctl_ip_conf (int addr_family, + const char *iface, + const char *property) +{ + char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + const char *pp; + + pp = nm_utils_sysctl_ip_conf_path (addr_family, path, iface, property); + g_assert (pp == path); + g_assert (path[0] == '/'); + + g_assert (nm_utils_sysctl_ip_conf_is_path (addr_family, path, iface, property)); + g_assert (nm_utils_sysctl_ip_conf_is_path (addr_family, path, NULL, property)); +} + +static void +test_nm_utils_sysctl_ip_conf_path (void) +{ + do_test_sysctl_ip_conf (AF_INET6, "a", "mtu"); + do_test_sysctl_ip_conf (AF_INET6, "eth0", "mtu"); + do_test_sysctl_ip_conf (AF_INET6, "e23456789012345", "mtu"); +} + +/*****************************************************************************/ + static NMConnection * _match_connection_new (void) { @@ -1716,6 +1742,8 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_ip6_address_same_prefix", test_nm_utils_ip6_address_same_prefix); g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); + g_test_add_func ("/general/nm_utils_sysctl_ip_conf_path", test_nm_utils_sysctl_ip_conf_path); + g_test_add_func ("/general/exp10", test_nm_utils_exp10); g_test_add_func ("/general/connection-match/basic", test_connection_match_basic); diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index bff715e0..649b443f 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -36,18 +36,27 @@ build_test_config (void) NMPlatformIP4Route route; /* Build up the config to subtract */ - config = nm_ip4_config_new (1); + config = nmtst_ip4_config_new (1); addr = *nmtst_platform_ip4_address ("192.168.1.10", "1.2.3.4", 24); nm_ip4_config_add_address (config, &addr); route = *nmtst_platform_ip4_route ("10.0.0.0", 8, "192.168.1.1"); - nm_ip4_config_add_route (config, &route); + nm_ip4_config_add_route (config, &route, NULL); route = *nmtst_platform_ip4_route ("172.16.0.0", 16, "192.168.1.1"); - nm_ip4_config_add_route (config, &route); + nm_ip4_config_add_route (config, &route, NULL); - nm_ip4_config_set_gateway (config, nmtst_inet4_from_string ("192.168.1.1")); + { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = nmtst_inet4_from_string ("192.168.1.1"), + .table_coerced = 0, + .metric = 100, + }; + + nm_ip4_config_add_route (config, &r, NULL); + } nm_ip4_config_add_nameserver (config, nmtst_inet4_from_string ("4.2.2.1")); nm_ip4_config_add_nameserver (config, nmtst_inet4_from_string ("4.2.2.2")); @@ -75,7 +84,7 @@ test_subtract (void) const NMPlatformIP4Route *test_route; const char *expected_addr = "192.168.1.12"; guint32 expected_addr_plen = 24; - const char *expected_route_dest = "8.7.6.5"; + const char *expected_route_dest = "8.0.0.0"; guint32 expected_route_plen = 8; const char *expected_route_next_hop = "192.168.1.1"; guint32 expected_ns1 = nmtst_inet4_from_string ("8.8.8.8"); @@ -84,7 +93,6 @@ test_subtract (void) const char *expected_search = "somewhere.com"; guint32 expected_nis = nmtst_inet4_from_string ("1.2.3.13"); guint32 expected_wins = nmtst_inet4_from_string ("2.3.4.5"); - guint32 expected_mss = 1400; guint32 expected_mtu = 1492; src = build_test_config (); @@ -95,7 +103,7 @@ test_subtract (void) nm_ip4_config_add_address (dst, &addr); route = *nmtst_platform_ip4_route (expected_route_dest, expected_route_plen, expected_route_next_hop); - nm_ip4_config_add_route (dst, &route); + nm_ip4_config_add_route (dst, &route, NULL); nm_ip4_config_add_nameserver (dst, expected_ns1); nm_ip4_config_add_nameserver (dst, expected_ns2); @@ -105,23 +113,23 @@ test_subtract (void) nm_ip4_config_add_nis_server (dst, expected_nis); nm_ip4_config_add_wins (dst, expected_wins); - nm_ip4_config_set_mss (dst, expected_mss); nm_ip4_config_set_mtu (dst, expected_mtu, NM_IP_CONFIG_SOURCE_UNKNOWN); - nm_ip4_config_subtract (dst, src); + nm_ip4_config_subtract (dst, src, 0); /* ensure what's left is what we expect */ g_assert_cmpuint (nm_ip4_config_get_num_addresses (dst), ==, 1); - test_addr = nm_ip4_config_get_address (dst, 0); + test_addr = _nmtst_ip4_config_get_address (dst, 0); g_assert (test_addr != NULL); g_assert_cmpuint (test_addr->address, ==, nmtst_inet4_from_string (expected_addr)); g_assert_cmpuint (test_addr->peer_address, ==, test_addr->address); g_assert_cmpuint (test_addr->plen, ==, expected_addr_plen); - g_assert_cmpuint (nm_ip4_config_get_gateway (dst), ==, 0); + g_assert (!nm_ip4_config_best_default_route_get (dst)); + g_assert_cmpuint (nmtst_ip4_config_get_gateway (dst), ==, 0); g_assert_cmpuint (nm_ip4_config_get_num_routes (dst), ==, 1); - test_route = nm_ip4_config_get_route (dst, 0); + test_route = _nmtst_ip4_config_get_route (dst, 0); g_assert (test_route != NULL); g_assert_cmpuint (test_route->network, ==, nmtst_inet4_from_string (expected_route_dest)); g_assert_cmpuint (test_route->plen, ==, expected_route_plen); @@ -142,7 +150,6 @@ test_subtract (void) g_assert_cmpuint (nm_ip4_config_get_num_wins (dst), ==, 1); g_assert_cmpuint (nm_ip4_config_get_wins (dst, 0), ==, expected_wins); - g_assert_cmpuint (nm_ip4_config_get_mss (dst), ==, expected_mss); g_assert_cmpuint (nm_ip4_config_get_mtu (dst), ==, expected_mtu); g_object_unref (src); @@ -156,8 +163,8 @@ test_compare_with_source (void) NMPlatformIP4Address addr; NMPlatformIP4Route route; - a = nm_ip4_config_new (1); - b = nm_ip4_config_new (2); + a = nmtst_ip4_config_new (1); + b = nmtst_ip4_config_new (2); /* Address */ addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24); @@ -170,10 +177,10 @@ test_compare_with_source (void) /* Route */ route = *nmtst_platform_ip4_route ("10.0.0.0", 8, "192.168.1.1"); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_route (a, &route); + nm_ip4_config_add_route (a, &route, NULL); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_route (b, &route); + nm_ip4_config_add_route (b, &route, NULL); /* Assert that the configs are basically the same, eg that the source is ignored */ g_assert (nm_ip4_config_equal (a, b)); @@ -189,34 +196,34 @@ test_add_address_with_source (void) NMPlatformIP4Address addr; const NMPlatformIP4Address *test_addr; - a = nm_ip4_config_new (1); + a = nmtst_ip4_config_new (1); /* Test that a higher priority source is not overwritten */ addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24); addr.addr_source = NM_IP_CONFIG_SOURCE_USER; nm_ip4_config_add_address (a, &addr); - test_addr = nm_ip4_config_get_address (a, 0); + test_addr = _nmtst_ip4_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; nm_ip4_config_add_address (a, &addr); - test_addr = nm_ip4_config_get_address (a, 0); + test_addr = _nmtst_ip4_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); /* Test that a lower priority address source is overwritten */ - nm_ip4_config_del_address (a, 0); + _nmtst_ip4_config_del_address (a, 0); addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; nm_ip4_config_add_address (a, &addr); - test_addr = nm_ip4_config_get_address (a, 0); + test_addr = _nmtst_ip4_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); addr.addr_source = NM_IP_CONFIG_SOURCE_USER; nm_ip4_config_add_address (a, &addr); - test_addr = nm_ip4_config_get_address (a, 0); + test_addr = _nmtst_ip4_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); g_object_unref (a); @@ -225,50 +232,52 @@ test_add_address_with_source (void) static void test_add_route_with_source (void) { - NMIP4Config *a; + gs_unref_object NMIP4Config *a = NULL; NMPlatformIP4Route route; const NMPlatformIP4Route *test_route; - a = nm_ip4_config_new (1); + a = nmtst_ip4_config_new (1); /* Test that a higher priority source is not overwritten */ - route = *nmtst_platform_ip4_route ("1.2.3.4", 24, "1.2.3.1"); + route = *nmtst_platform_ip4_route ("1.2.3.0", 24, "1.2.3.1"); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_route (a, &route); + nm_ip4_config_add_route (a, &route, NULL); - test_route = nm_ip4_config_get_route (a, 0); + g_assert_cmpint (nm_ip4_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_route (a, &route); + nm_ip4_config_add_route (a, &route, NULL); - test_route = nm_ip4_config_get_route (a, 0); + g_assert_cmpint (nm_ip4_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + _nmtst_ip4_config_del_route (a, 0); + g_assert_cmpint (nm_ip4_config_get_num_routes (a), ==, 0); + /* Test that a lower priority address source is overwritten */ - nm_ip4_config_del_route (a, 0); - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip4_config_add_route (a, &route); + route.rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; + nm_ip4_config_add_route (a, &route, NULL); - test_route = nm_ip4_config_get_route (a, 0); - g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); + g_assert_cmpint (nm_ip4_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip4_config_get_route (a, 0); + g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_RTPROT_KERNEL); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_route (a, &route); - - test_route = nm_ip4_config_get_route (a, 0); - g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip4_config_add_route (a, &route, NULL); - g_object_unref (a); + g_assert_cmpint (nm_ip4_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip4_config_get_route (a, 0); + g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); } static void -test_merge_subtract_mss_mtu (void) +test_merge_subtract_mtu (void) { NMIP4Config *cfg1, *cfg2, *cfg3; - guint32 expected_mss2 = 1400; guint32 expected_mtu2 = 1492; - guint32 expected_mss3 = 555; guint32 expected_mtu3 = 666; cfg1 = build_test_config (); @@ -276,24 +285,19 @@ test_merge_subtract_mss_mtu (void) cfg3 = build_test_config (); /* add MSS, MTU to configs to test them */ - nm_ip4_config_set_mss (cfg2, expected_mss2); nm_ip4_config_set_mtu (cfg2, expected_mtu2, NM_IP_CONFIG_SOURCE_UNKNOWN); - nm_ip4_config_set_mss (cfg3, expected_mss3); nm_ip4_config_set_mtu (cfg3, expected_mtu3, NM_IP_CONFIG_SOURCE_UNKNOWN); - nm_ip4_config_merge (cfg1, cfg2, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip4_config_merge (cfg1, cfg2, NM_IP_CONFIG_MERGE_DEFAULT, 0); /* ensure MSS and MTU are in cfg1 */ - g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss2); g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu2); - nm_ip4_config_merge (cfg1, cfg3, NM_IP_CONFIG_MERGE_DEFAULT); - /* ensure again the MSS and MTU in cfg1 got overriden */ - g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss3); + nm_ip4_config_merge (cfg1, cfg3, NM_IP_CONFIG_MERGE_DEFAULT, 0); + /* ensure again the MSS and MTU in cfg1 got overridden */ g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu3); - nm_ip4_config_subtract (cfg1, cfg3); + nm_ip4_config_subtract (cfg1, cfg3, 0); /* ensure MSS and MTU are zero in cfg1 */ - g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, 0); g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, 0); g_object_unref (cfg1); @@ -306,7 +310,7 @@ test_strip_search_trailing_dot (void) { NMIP4Config *config; - config = nm_ip4_config_new (1); + config = nmtst_ip4_config_new (1); nm_ip4_config_add_search (config, "."); nm_ip4_config_add_search (config, "foo"); @@ -335,7 +339,7 @@ main (int argc, char **argv) g_test_add_func ("/ip4-config/compare-with-source", test_compare_with_source); g_test_add_func ("/ip4-config/add-address-with-source", test_add_address_with_source); g_test_add_func ("/ip4-config/add-route-with-source", test_add_route_with_source); - g_test_add_func ("/ip4-config/merge-subtract-mss-mtu", test_merge_subtract_mss_mtu); + g_test_add_func ("/ip4-config/merge-subtract-mtu", test_merge_subtract_mtu); g_test_add_func ("/ip4-config/strip-search-trailing-dot", test_strip_search_trailing_dot); return g_test_run (); diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index 7e83625c..bcbeee3e 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -34,13 +34,13 @@ build_test_config (void) NMIP6Config *config; /* Build up the config to subtract */ - config = nm_ip6_config_new (1); + config = nmtst_ip6_config_new (1); nm_ip6_config_add_address (config, nmtst_platform_ip6_address ("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); - nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL)); - nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234", NULL)); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL), NULL); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001::", 16, "2001:abba::2234", NULL), NULL); - nm_ip6_config_set_gateway (config, nmtst_inet6_from_string ("3001:abba::3234")); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("::", 0, "3001:abba::3234", NULL), NULL); nm_ip6_config_add_nameserver (config, nmtst_inet6_from_string ("1:2:3:4::1")); nm_ip6_config_add_nameserver (config, nmtst_inet6_from_string ("1:2:3:4::2")); @@ -60,7 +60,7 @@ test_subtract (void) const NMPlatformIP6Route *test_route; const char *expected_addr = "1122:3344:5566::7788"; guint32 expected_addr_plen = 96; - const char *expected_route_dest = "9991:8882:7773::"; + const char *expected_route_dest = "9991:8800::"; guint32 expected_route_plen = 24; const char *expected_route_next_hop = "1119:2228:3337:4446::5555"; struct in6_addr expected_ns1; @@ -74,7 +74,7 @@ test_subtract (void) /* add a couple more things to the test config */ dst = build_test_config (); nm_ip6_config_add_address (dst, nmtst_platform_ip6_address (expected_addr, NULL, expected_addr_plen)); - nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop, NULL)); + nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop, NULL), NULL); expected_ns1 = *nmtst_inet6_from_string ("2222:3333:4444::5555"); nm_ip6_config_add_nameserver (dst, &expected_ns1); @@ -84,21 +84,21 @@ test_subtract (void) nm_ip6_config_add_domain (dst, expected_domain); nm_ip6_config_add_search (dst, expected_search); - nm_ip6_config_subtract (dst, src); + nm_ip6_config_subtract (dst, src, 0); /* ensure what's left is what we expect */ g_assert_cmpuint (nm_ip6_config_get_num_addresses (dst), ==, 1); - test_addr = nm_ip6_config_get_address (dst, 0); + test_addr = _nmtst_ip6_config_get_address (dst, 0); g_assert (test_addr != NULL); tmp = *nmtst_inet6_from_string (expected_addr); g_assert (memcmp (&test_addr->address, &tmp, sizeof (tmp)) == 0); g_assert (memcmp (&test_addr->peer_address, &in6addr_any, sizeof (tmp)) == 0); g_assert_cmpuint (test_addr->plen, ==, expected_addr_plen); - g_assert (nm_ip6_config_get_gateway (dst) == NULL); + g_assert (nm_ip6_config_best_default_route_get (dst) == NULL); g_assert_cmpuint (nm_ip6_config_get_num_routes (dst), ==, 1); - test_route = nm_ip6_config_get_route (dst, 0); + test_route = _nmtst_ip6_config_get_route (dst, 0); g_assert (test_route != NULL); tmp = *nmtst_inet6_from_string (expected_route_dest); @@ -127,8 +127,8 @@ test_compare_with_source (void) NMPlatformIP6Address addr; NMPlatformIP6Route route; - a = nm_ip6_config_new (1); - b = nm_ip6_config_new (2); + a = nmtst_ip6_config_new (1); + b = nmtst_ip6_config_new (2); /* Address */ addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); @@ -139,12 +139,12 @@ test_compare_with_source (void) nm_ip6_config_add_address (b, &addr); /* Route */ - route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL); + route = *nmtst_platform_ip6_route ("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route (a, &route); + nm_ip6_config_add_route (a, &route, NULL); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_route (b, &route); + nm_ip6_config_add_route (b, &route, NULL); /* Assert that the configs are basically the same, eg that the source is ignored */ g_assert (nm_ip6_config_equal (a, b)); @@ -160,34 +160,34 @@ test_add_address_with_source (void) NMPlatformIP6Address addr; const NMPlatformIP6Address *test_addr; - a = nm_ip6_config_new (1); + a = nmtst_ip6_config_new (1); /* Test that a higher priority source is not overwritten */ addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); addr.addr_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_address (a, &addr); - test_addr = nm_ip6_config_get_address (a, 0); + test_addr = _nmtst_ip6_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; nm_ip6_config_add_address (a, &addr); - test_addr = nm_ip6_config_get_address (a, 0); + test_addr = _nmtst_ip6_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); /* Test that a lower priority address source is overwritten */ - nm_ip6_config_del_address (a, 0); + _nmtst_ip6_config_del_address (a, 0); addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; nm_ip6_config_add_address (a, &addr); - test_addr = nm_ip6_config_get_address (a, 0); + test_addr = _nmtst_ip6_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); addr.addr_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_address (a, &addr); - test_addr = nm_ip6_config_get_address (a, 0); + test_addr = _nmtst_ip6_config_get_address (a, 0); g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); g_object_unref (a); @@ -196,41 +196,45 @@ test_add_address_with_source (void) static void test_add_route_with_source (void) { - NMIP6Config *a; + gs_unref_object NMIP6Config *a = NULL; NMPlatformIP6Route route; const NMPlatformIP6Route *test_route; - a = nm_ip6_config_new (1); + a = nmtst_ip6_config_new (1); /* Test that a higher priority source is not overwritten */ - route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL); + route = *nmtst_platform_ip6_route ("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route (a, &route); + nm_ip6_config_add_route (a, &route, NULL); - test_route = nm_ip6_config_get_route (a, 0); + g_assert_cmpint (nm_ip6_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_route (a, &route); + nm_ip6_config_add_route (a, &route, NULL); - test_route = nm_ip6_config_get_route (a, 0); + g_assert_cmpint (nm_ip6_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + _nmtst_ip6_config_del_route (a, 0); + g_assert_cmpint (nm_ip6_config_get_num_routes (a), ==, 0); + /* Test that a lower priority address source is overwritten */ - nm_ip6_config_del_route (a, 0); route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip6_config_add_route (a, &route); + nm_ip6_config_add_route (a, &route, NULL); - test_route = nm_ip6_config_get_route (a, 0); + g_assert_cmpint (nm_ip6_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route (a, &route); + nm_ip6_config_add_route (a, &route, NULL); - test_route = nm_ip6_config_get_route (a, 0); + g_assert_cmpint (nm_ip6_config_get_num_routes (a), ==, 1); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); - - g_object_unref (a); } static void @@ -256,18 +260,18 @@ test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6Config int j = g_rand_int_range (nmtst_get_rand (), i, addr_count); NMTST_SWAP (idx[i], idx[j]); - nm_ip6_config_add_address (copy, nm_ip6_config_get_address (config, idx[i])); + nm_ip6_config_add_address (copy, _nmtst_ip6_config_get_address (config, idx[i])); } /* reorder them again */ - nm_ip6_config_addresses_sort (copy); + _nmtst_ip6_config_addresses_sort (copy); /* check equality using nm_ip6_config_equal() */ if (!nm_ip6_config_equal (copy, config)) { g_message ("%s", "SORTING yields unexpected output:"); for (i = 0; i < addr_count; i++) { - g_message (" >> [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0)); - g_message (" << [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (copy, i), NULL, 0)); + g_message (" >> [%d] = %s", i, nm_platform_ip6_address_to_string (_nmtst_ip6_config_get_address (config, i), NULL, 0)); + g_message (" << [%d] = %s", i, nm_platform_ip6_address_to_string (_nmtst_ip6_config_get_address (copy, i), NULL, 0)); } g_assert_not_reached (); } @@ -327,7 +331,7 @@ test_strip_search_trailing_dot (void) { NMIP6Config *config; - config = nm_ip6_config_new (1); + config = nmtst_ip6_config_new (1); nm_ip6_config_add_search (config, "."); nm_ip6_config_add_search (config, "foo"); @@ -345,6 +349,71 @@ test_strip_search_trailing_dot (void) /*****************************************************************************/ +static void +test_replace (gconstpointer user_data) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + const int TEST_IDX = GPOINTER_TO_INT (user_data); + const int IFINDEX = 1; + gs_unref_object NMIP6Config *src_conf = NULL; + gs_unref_object NMIP6Config *dst_conf = NULL; + NMPlatformIP6Address *addr; + NMPlatformIP6Address addrs[5] = { }; + guint addrs_n = 0; + guint i; + + dst_conf = nm_ip6_config_new (multi_idx, IFINDEX); + src_conf = nm_ip6_config_new (multi_idx, IFINDEX); + + switch (TEST_IDX) { + case 1: + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string ("fe80::78ec:7a6d:602d:20f2"); + addr->plen = 64; + addr->n_ifa_flags = IFA_F_PERMANENT; + addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + break; + case 2: + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string ("fe80::78ec:7a6d:602d:20f2"); + addr->plen = 64; + addr->n_ifa_flags = IFA_F_PERMANENT; + addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string ("1::1"); + addr->plen = 64; + addr->addr_source = NM_IP_CONFIG_SOURCE_USER; + + nm_ip6_config_add_address (dst_conf, addr); + break; + default: + g_assert_not_reached (); + } + + g_assert (addrs_n < G_N_ELEMENTS (addrs)); + + for (i = 0; i < addrs_n; i++) + nm_ip6_config_add_address (src_conf, &addrs[i]); + + nm_ip6_config_replace (dst_conf, src_conf, NULL); + + for (i = 0; i < addrs_n; i++) { + const NMPlatformIP6Address *a = _nmtst_ip6_config_get_address (dst_conf, i); + const NMPlatformIP6Address *b = _nmtst_ip6_config_get_address (src_conf, i); + + g_assert (nm_platform_ip6_address_cmp (&addrs[i], a) == 0); + g_assert (nm_platform_ip6_address_cmp (&addrs[i], b) == 0); + } + g_assert (addrs_n == nm_ip6_config_get_num_addresses (dst_conf)); + g_assert (addrs_n == nm_ip6_config_get_num_addresses (src_conf)); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -358,6 +427,8 @@ main (int argc, char **argv) g_test_add_func ("/ip6-config/add-route-with-source", test_add_route_with_source); g_test_add_func ("/ip6-config/test_nm_ip6_config_addresses_sort", test_nm_ip6_config_addresses_sort); g_test_add_func ("/ip6-config/strip-search-trailing-dot", test_strip_search_trailing_dot); + g_test_add_data_func ("/ip6-config/replace/1", GINT_TO_POINTER (1), test_replace); + g_test_add_data_func ("/ip6-config/replace/2", GINT_TO_POINTER (2), test_replace); return g_test_run (); } diff --git a/src/tests/test-resolvconf-capture.c b/src/tests/test-resolvconf-capture.c index ccbdafa6..2c34ff74 100644 --- a/src/tests/test-resolvconf-capture.c +++ b/src/tests/test-resolvconf-capture.c @@ -36,42 +36,24 @@ test_capture_empty (void) GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - g_assert (nm_ip4_config_capture_resolv_conf (ns4, NULL, "") == FALSE); + g_assert (!nm_utils_resolve_conf_parse (AF_INET, "", ns4, NULL)); g_assert_cmpint (ns4->len, ==, 0); - g_assert (nm_ip6_config_capture_resolv_conf (ns6, NULL, "") == FALSE); + g_assert (!nm_utils_resolve_conf_parse (AF_INET6, "", ns6, NULL)); g_assert_cmpint (ns6->len, ==, 0); g_array_free (ns4, TRUE); g_array_free (ns6, TRUE); } -static void -assert_dns4_entry (const GArray *a, guint i, const char *s) -{ - guint32 n, m; - - g_assert (inet_aton (s, (void *) &n) != 0); - m = g_array_index (a, guint32, i); - g_assert_cmpint (m, ==, n); -} - -static void -assert_dns6_entry (const GArray *a, guint i, const char *s) -{ - struct in6_addr n = IN6ADDR_ANY_INIT; - struct in6_addr *m; +#define assert_dns4_entry(a, i, s) \ + g_assert_cmpint ((g_array_index ((a), guint32, (i))), ==, nmtst_inet4_from_string (s)); - g_assert (inet_pton (AF_INET6, s, (void *) &n) == 1); - m = &g_array_index (a, struct in6_addr, i); - g_assert (IN6_ARE_ADDR_EQUAL (&n, m)); -} +#define assert_dns6_entry(a, i, s) \ + g_assert (IN6_ARE_ADDR_EQUAL (&g_array_index ((a), struct in6_addr, (i)), nmtst_inet6_from_string (s))) -static void -assert_dns_option (GPtrArray *a, guint i, const char *s) -{ - g_assert_cmpstr (a->pdata[i], ==, s); -} +#define assert_dns_option(a, i, s) \ + g_assert_cmpstr ((a)->pdata[(i)], ==, (s)); static void test_capture_basic4 (void) @@ -84,7 +66,7 @@ test_capture_basic4 (void) "nameserver 4.2.2.1\r\n" "nameserver 4.2.2.2\r\n"; - g_assert (nm_ip4_config_capture_resolv_conf (ns4, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); g_assert_cmpint (ns4->len, ==, 2); assert_dns4_entry (ns4, 0, "4.2.2.1"); assert_dns4_entry (ns4, 1, "4.2.2.2"); @@ -105,7 +87,7 @@ test_capture_dup4 (void) "nameserver 4.2.2.2\r\n"; /* Check that duplicates are ignored */ - g_assert (nm_ip4_config_capture_resolv_conf (ns4, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); g_assert_cmpint (ns4->len, ==, 2); assert_dns4_entry (ns4, 0, "4.2.2.1"); assert_dns4_entry (ns4, 1, "4.2.2.2"); @@ -124,7 +106,7 @@ test_capture_basic6 (void) "nameserver 2001:4860:4860::8888\r\n" "nameserver 2001:4860:4860::8844\r\n"; - g_assert (nm_ip6_config_capture_resolv_conf (ns6, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); g_assert_cmpint (ns6->len, ==, 2); assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); @@ -145,7 +127,7 @@ test_capture_dup6 (void) "nameserver 2001:4860:4860::8844\r\n"; /* Check that duplicates are ignored */ - g_assert (nm_ip6_config_capture_resolv_conf (ns6, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); g_assert_cmpint (ns6->len, ==, 2); assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); @@ -165,7 +147,7 @@ test_capture_addr4_with_6 (void) "nameserver 4.2.2.2\r\n" "nameserver 2001:4860:4860::8888\r\n"; - g_assert (nm_ip4_config_capture_resolv_conf (ns4, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); g_assert_cmpint (ns4->len, ==, 2); assert_dns4_entry (ns4, 0, "4.2.2.1"); assert_dns4_entry (ns4, 1, "4.2.2.2"); @@ -185,7 +167,7 @@ test_capture_addr6_with_4 (void) "nameserver 2001:4860:4860::8888\r\n" "nameserver 2001:4860:4860::8844\r\n"; - g_assert (nm_ip6_config_capture_resolv_conf (ns6, NULL, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); g_assert_cmpint (ns6->len, ==, 2); assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); @@ -200,15 +182,17 @@ test_capture_format (void) const char *rc = " nameserver 4.2.2.1\r\n" /* bad */ "nameserver4.2.2.1\r\n" /* bad */ -"nameserver 4.2.2.3\r" /* good */ +"nameserver 4.2.2.3\r" /* good */ "nameserver\t\t4.2.2.4\r\n" /* good */ -"nameserver 4.2.2.5\t\t\r\n"; /* good */ +"nameserver 4.2.2.5\t\t\r\n" /* good */ +"nameserver 4.2.2.6 \r\n"; /* good */ - g_assert (nm_ip4_config_capture_resolv_conf (ns4, NULL, rc)); - g_assert_cmpint (ns4->len, ==, 3); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); + g_assert_cmpint (ns4->len, ==, 4); assert_dns4_entry (ns4, 0, "4.2.2.3"); assert_dns4_entry (ns4, 1, "4.2.2.4"); assert_dns4_entry (ns4, 2, "4.2.2.5"); + assert_dns4_entry (ns4, 3, "4.2.2.6"); g_array_free (ns4, TRUE); } @@ -223,7 +207,7 @@ test_capture_dns_options (void) "options debug rotate timeout:5 \r\n" "options edns0\r\n"; - g_assert (nm_ip4_config_capture_resolv_conf (ns4, dns_options, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); g_assert_cmpint (dns_options->len, ==, 4); assert_dns_option (dns_options, 0, "debug"); assert_dns_option (dns_options, 1, "rotate"); @@ -244,7 +228,7 @@ test_capture_dns_options_dup (void) "options edns0 debug\r\n" "options timeout:5\r\n"; - g_assert (nm_ip4_config_capture_resolv_conf (ns4, dns_options, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); g_assert_cmpint (dns_options->len, ==, 4); assert_dns_option (dns_options, 0, "debug"); assert_dns_option (dns_options, 1, "rotate"); @@ -263,7 +247,7 @@ test_capture_dns_options_valid4 (void) const char *rc = "options debug: rotate:yes edns0 foobar : inet6\r\n"; - g_assert (nm_ip4_config_capture_resolv_conf (ns4, dns_options, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); g_assert_cmpint (dns_options->len, ==, 1); assert_dns_option (dns_options, 0, "edns0"); @@ -274,12 +258,12 @@ test_capture_dns_options_valid4 (void) static void test_capture_dns_options_valid6 (void) { - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (guint32)); + GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free); const char *rc = "options inet6 debug foobar rotate:\r\n"; - g_assert (nm_ip6_config_capture_resolv_conf (ns6, dns_options, rc)); + g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, dns_options)); g_assert_cmpint (dns_options->len, ==, 2); assert_dns_option (dns_options, 0, "inet6"); assert_dns_option (dns_options, 1, "debug"); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c deleted file mode 100644 index 6650d26c..00000000 --- a/src/tests/test-route-manager.c +++ /dev/null @@ -1,933 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include <arpa/inet.h> -#include <linux/rtnetlink.h> - -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" - -#include "platform/tests/test-common.h" - -typedef struct { - int ifindex0, ifindex1; -} test_fixture; - -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - -static void -setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_route) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - route.mss = mss_of_first_route; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - inet_pton (AF_INET, "6.6.6.1", &route.gateway); - route.metric = metric_of_second_route; - route.mss = 0; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip4_route_add (NM_PLATFORM_GET, - route.ifindex, - NM_IP_CONFIG_SOURCE_USER, - nmtst_inet4_from_string ("9.0.0.0"), - 8, - INADDR_ANY, - 0, - 10, - route.mss); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 22; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "8.0.0.0", &route.network); - route.plen = 8; - inet_pton (AF_INET, "6.6.6.2", &route.gateway); - route.metric = 22; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 21; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - - -static GArray * -ip4_routes (test_fixture *fixture) -{ - GArray *routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, - fixture->ifindex0, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - GArray *routes1 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, - fixture->ifindex1, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - - g_array_append_vals (routes, routes1->data, routes1->len); - g_array_free (routes1, TRUE); - - return routes; -} - -static void -test_ip4 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - - NMPlatformIP4Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 1000, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = nmtst_inet4_from_string ("6.6.6.1"), - .metric = 21021, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - /* this is a ghost entry because we synced ifindex0 and restore the route - * with metric 20 (above). But we don't remove the metric 21. */ - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - }; - - setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* - 6.6.6.0/24 on dev0 won over 6.6.6.0/24 on dev1 - * - 6.6.6.0/24 on dev1 has metric bumped. - * - 7.0.0.0/8 route, metric 21021 added - * - 7.0.0.0/8 route, metric 22 added - * - 8.0.0.0/8 could be added. */ - routes = ip4_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - setup_dev0_ip4 (fixture->ifindex0, 0, 21); - - /* Ensure nothing changed. */ - routes = ip4_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - state1[0].mss = 0; - state1[1].metric = 21; - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - update_dev0_ip4 (fixture->ifindex0); - - /* minor changes in the routes. Quite similar to state1. */ - routes = ip4_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 6.6.6.0/24 is now on dev1 - * 6.6.6.0/24 is also still on dev1 with bumped metric 21. - * 7.0.0.0/8 gone from dev0, still present on dev1 - * 8.0.0.0/8 is present on dev1 - * No dev0 routes left. */ - routes = ip4_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip4_routes (fixture); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -static void -setup_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::666"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - "2001:db8:8086::1", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip6_route_add (NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - *nmtst_inet6_from_string ("2001:db8:8088::"), - 48, - in6addr_any, - in6addr_any, - 10, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 1024, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:d34d::", - 64, - "2001:db8:8086::2", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 22, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::2"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static GArray * -ip6_routes (test_fixture *fixture) -{ - GArray *routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, - fixture->ifindex0, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - GArray *routes1 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, - fixture->ifindex1, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - - g_array_append_vals (routes, routes1->data, routes1->len); - g_array_free (routes1, TRUE); - - return routes; -} - -static void -test_ip6 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - - NMPlatformIP6Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - NMPlatformIP6Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - NMPlatformIP6Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - setup_dev0_ip6 (fixture->ifindex0); - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* 2001:db8:8086::/48 on dev0 won over 2001:db8:8086::/48 on dev1 - * 2001:db8:d34d::/64 on dev1 could not be added - * 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024 - * 2001:db8:abad:c0de::/64 routes did not clash */ - routes = ip6_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - setup_dev0_ip6 (fixture->ifindex0); - - /* Ensure nothing changed. */ - routes = ip6_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - update_dev0_ip6 (fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ - routes = ip6_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0 - * 2001:db8:8086::/48 is now on dev1 - * 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies - * 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1 - * No dev0 routes left. */ - routes = ip6_routes (fixture); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip6_routes (fixture); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -/*****************************************************************************/ - -static void -_assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route) -{ - const NMPlatformIPXRoute *r; - NMPlatformIPXRoute c; - - g_assert (route); - - if (vtable->is_ip4) - r = (const NMPlatformIPXRoute *) nm_platform_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric); - else - r = (const NMPlatformIPXRoute *) nm_platform_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r6.network, route->rx.plen, route->rx.metric); - - if (!has) { - g_assert (!r); - } else { - char buf[sizeof (_nm_utils_to_string_buffer)]; - - if (r) { - if (vtable->is_ip4) - c.r4 = route->r4; - else - c.r6 = route->r6; - c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source); - } - if (!r || vtable->route_cmp (r, &c, TRUE) != 0) { - g_error ("Invalid route. Expect %s, has %s", - vtable->route_to_string (&c, NULL, 0), - vtable->route_to_string (r, buf, sizeof (buf))); - } - } -} - -static void -test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data) -{ - const NMPlatformVTableRoute *vtable = &nm_platform_vtable_route_v4; - gs_unref_array GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route r01, r02, r03; - - nm_log_dbg (LOGD_CORE, "TEST start test_ip4_full_sync(): start"); - - r01 = *nmtst_platform_ip4_route_full ("12.3.4.0", 24, NULL, - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_LINK, NULL); - r02 = *nmtst_platform_ip4_route_full ("13.4.5.6", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_UNIVERSE, NULL); - r03 = *nmtst_platform_ip4_route_full ("14.5.6.7", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 110, 0, RT_SCOPE_UNIVERSE, NULL); - g_array_set_size (routes, 2); - g_array_index (routes, NMPlatformIP4Route, 0) = r01; - g_array_index (routes, NMPlatformIP4Route, 1) = r02; - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - vtable->route_add (NM_PLATFORM_GET, 0, (const NMPlatformIPXRoute *) &r03, -1); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - g_array_set_size (routes, 1); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - nm_log_dbg (LOGD_CORE, "TEST test_ip4_full_sync(): done"); -} - -/*****************************************************************************/ - -static void -fixture_setup (test_fixture *fixture, gconstpointer user_data) -{ - SignalData *link_added; - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device0"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device1"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); -} - -static void -fixture_teardown (test_fixture *fixture, gconstpointer user_data) -{ - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0); - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex1); -} - -/*****************************************************************************/ - -NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; - -void -_nmtstp_init_tests (int *argc, char ***argv) -{ - nmtst_init_assert_logging (argc, argv, "WARN", "ALL"); -} - -void -_nmtstp_setup_tests (void) -{ - g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown); - g_test_add ("/route-manager/ip6", test_fixture, NULL, fixture_setup, test_ip6, fixture_teardown); - - g_test_add ("/route-manager/ip4-full-sync", test_fixture, NULL, fixture_setup, test_ip4_full_sync, fixture_teardown); -} diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c index f6cf9b58..ab5fed22 100644 --- a/src/tests/test-systemd.c +++ b/src/tests/test-systemd.c @@ -78,7 +78,7 @@ test_dhcp_create (void) sd_dhcp_client *client4 = NULL; int r; - r = sd_dhcp_client_new (&client4); + r = sd_dhcp_client_new (&client4, FALSE); g_assert (r == 0); g_assert (client4); diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index ecc82068..2436ea33 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -31,6 +31,7 @@ #include <stdlib.h> #include <unistd.h> #include <syslog.h> +#include <linux/rtnetlink.h> #include "nm-proxy-config.h" #include "nm-ip4-config.h" @@ -44,8 +45,6 @@ #include "settings/nm-agent-manager.h" #include "nm-core-internal.h" #include "nm-pacrunner-manager.h" -#include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "nm-firewall-manager.h" #include "nm-config.h" #include "nm-vpn-plugin-info.h" @@ -123,6 +122,8 @@ typedef struct { NMNetns *netns; + GPtrArray *ip4_dev_route_blacklist; + GDBusProxy *proxy; GCancellable *cancellable; GVariant *connect_hash; @@ -186,6 +187,8 @@ static void get_secrets (NMVpnConnection *self, SecretsReq secrets_idx, const char **hints); +static guint32 get_route_table (NMVpnConnection *self, int addr_family, gboolean fallback_main); + static void plugin_interactive_secrets_required (NMVpnConnection *self, const char *message, const char **secrets); @@ -394,9 +397,11 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); if (priv->ip_ifindex) { - nm_platform_link_set_down (nm_netns_get_platform (priv->netns), priv->ip_ifindex); - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), priv->ip_ifindex); - nm_platform_address_flush (nm_netns_get_platform (priv->netns), priv->ip_ifindex); + NMPlatform *platform = nm_netns_get_platform (priv->netns); + + nm_platform_link_set_down (platform, priv->ip_ifindex); + nm_platform_ip_route_flush (platform, AF_UNSPEC, priv->ip_ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, priv->ip_ifindex); } remove_parent_device_config (self, parent_dev); @@ -497,9 +502,6 @@ _set_vpn_state (NMVpnConnection *self, dispatcher_cleanup (self); - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - /* The connection gets destroyed by the VPN manager when it enters the * disconnected/failed state, but we need to keep it around for a bit * to send out signals and handle the dispatcher. So ref it. @@ -704,44 +706,63 @@ device_state_changed (NMActiveConnection *active, } static void -add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 vpn_gw) +add_ip4_vpn_gateway_route (NMIP4Config *config, + NMDevice *parent_device, + in_addr_t vpn_gw, + NMPlatform *platform) { - NMIP4Config *parent_config; - guint32 parent_gw; + guint32 parent_gw = 0; + gboolean has_parent_gw = FALSE; NMPlatformIP4Route route; + int ifindex; guint32 route_metric; + nm_auto_nmpobj const NMPObject *route_resolved = NULL; g_return_if_fail (NM_IS_IP4_CONFIG (config)); g_return_if_fail (NM_IS_DEVICE (parent_device)); g_return_if_fail (vpn_gw != 0); - /* Set up a route to the VPN gateway's public IP address through the default - * network device if the VPN gateway is on a different subnet. - */ - parent_config = nm_device_get_ip4_config (parent_device); - g_return_if_fail (parent_config != NULL); - parent_gw = nm_ip4_config_get_gateway (parent_config); + ifindex = nm_ip4_config_get_ifindex (config); + + nm_assert (ifindex > 0); + nm_assert (ifindex == nm_device_get_ip_ifindex (parent_device)); + + /* Ask kernel how to reach @vpn_gw. We can only inject the route in + * @parent_device, so whatever we resolve, it can only be on @ifindex. */ + if (nm_platform_ip_route_get (platform, + AF_INET, + &vpn_gw, + ifindex, + (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (route_resolved); + + if (r->ifindex == ifindex) { + /* `ip route get` always resolves the route, even if the destination is unreachable. + * In which case, it pretends the destination is directly reachable. + * + * So, only accept direct routes, if @vpn_gw is a private network. */ + if ( nm_platform_route_table_is_main (r->table_coerced) + && ( r->gateway + || nm_utils_ip_is_site_local (AF_INET, &vpn_gw))) { + parent_gw = r->gateway; + has_parent_gw = TRUE; + } + } + } - route_metric = nm_device_get_ip4_route_metric (parent_device); + if (!has_parent_gw) + return; + + route_metric = nm_device_get_route_metric (parent_device, AF_INET); memset (&route, 0, sizeof (route)); + route.ifindex = ifindex; route.network = vpn_gw; route.plen = 32; route.gateway = parent_gw; - /* Set up a device route if the parent device has no gateway */ - if (!parent_gw) - route.ifindex = nm_device_get_ip_ifindex (parent_device); - - /* If the VPN gateway is in the same subnet as one of the parent device's - * IP addresses, don't add the host route to it, but a route through the - * parent device. - */ - if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32)) - route.gateway = 0; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; - nm_ip4_config_add_route (config, &route); + nm_ip4_config_add_route (config, &route, NULL); if (parent_gw) { /* Ensure there's a route to the parent device's gateway through the @@ -754,61 +775,83 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 route.plen = 32; route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; - - nm_ip4_config_add_route (config, &route); + nm_ip4_config_add_route (config, &route, NULL); } } static void add_ip6_vpn_gateway_route (NMIP6Config *config, NMDevice *parent_device, - const struct in6_addr *vpn_gw) + const struct in6_addr *vpn_gw, + NMPlatform *platform) { - NMIP6Config *parent_config; - const struct in6_addr *parent_gw; + const struct in6_addr *parent_gw = NULL; + gboolean has_parent_gw = FALSE; NMPlatformIP6Route route; + int ifindex; guint32 route_metric; + nm_auto_nmpobj const NMPObject *route_resolved = NULL; g_return_if_fail (NM_IS_IP6_CONFIG (config)); g_return_if_fail (NM_IS_DEVICE (parent_device)); g_return_if_fail (vpn_gw != NULL); - parent_config = nm_device_get_ip6_config (parent_device); - g_return_if_fail (parent_config != NULL); - parent_gw = nm_ip6_config_get_gateway (parent_config); - if (!parent_gw) + ifindex = nm_ip6_config_get_ifindex (config); + + nm_assert (ifindex > 0); + nm_assert (ifindex == nm_device_get_ip_ifindex (parent_device)); + + /* Ask kernel how to reach @vpn_gw. We can only inject the route in + * @parent_device, so whatever we resolve, it can only be on @ifindex. */ + if (nm_platform_ip_route_get (platform, + AF_INET6, + vpn_gw, + ifindex, + (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (route_resolved); + + if (r->ifindex == ifindex) { + /* `ip route get` always resolves the route, even if the destination is unreachable. + * In which case, it pretends the destination is directly reachable. + * + * So, only accept direct routes, if @vpn_gw is a private network. */ + if ( nm_platform_route_table_is_main (r->table_coerced) + && ( !IN6_IS_ADDR_UNSPECIFIED (&r->gateway) + || nm_utils_ip_is_site_local (AF_INET6, &vpn_gw))) { + parent_gw = &r->gateway; + has_parent_gw = TRUE; + } + } + } + + if (!has_parent_gw) return; - route_metric = nm_device_get_ip6_route_metric (parent_device); + route_metric = nm_device_get_route_metric (parent_device, AF_INET6); memset (&route, 0, sizeof (route)); + route.ifindex = ifindex; route.network = *vpn_gw; route.plen = 128; - route.gateway = *parent_gw; - - /* If the VPN gateway is in the same subnet as one of the parent device's - * IP addresses, don't add the host route to it, but a route through the - * parent device. - */ - if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128)) - route.gateway = in6addr_any; - + if (parent_gw) + route.gateway = *parent_gw; route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; - nm_ip6_config_add_route (config, &route); + nm_ip6_config_add_route (config, &route, NULL); /* Ensure there's a route to the parent device's gateway through the * parent device, since if the VPN claims the default route and the VPN * routes include a subnet that matches the parent device's subnet, * the parent device's gateway would get routed through the VPN and fail. */ - memset (&route, 0, sizeof (route)); - route.network = *parent_gw; - route.plen = 128; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - - nm_ip6_config_add_route (config, &route); + if (parent_gw && !IN6_IS_ADDR_UNSPECIFIED (parent_gw)) { + memset (&route, 0, sizeof (route)); + route.network = *parent_gw; + route.plen = 128; + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + route.metric = route_metric; + nm_ip6_config_add_route (config, &route, NULL); + } } NMVpnConnection * @@ -942,6 +985,7 @@ print_vpn_config (NMVpnConnection *self) char *dns_domain = NULL; guint32 num, i; char buf[NM_UTILS_INET_ADDRSTRLEN]; + NMDedupMultiIter ipconf_iter; if (priv->ip4_external_gw) { _LOGI ("Data: VPN Gateway: %s", @@ -954,30 +998,26 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: Tunnel Device: %s%s%s", NM_PRINT_FMT_QUOTE_STRING (priv->ip_iface)); if (priv->ip4_config) { + const NMPlatformIP4Route *route; + _LOGI ("Data: IPv4 configuration:"); - address4 = nm_ip4_config_get_address (priv->ip4_config, 0); + address4 = nm_ip4_config_get_first_address (priv->ip4_config); + nm_assert (address4); if (priv->ip4_internal_gw) _LOGI ("Data: Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, NULL)); _LOGI ("Data: Internal Address: %s", nm_utils_inet4_ntop (address4->address, NULL)); _LOGI ("Data: Internal Prefix: %d", address4->plen); _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL)); - _LOGI ("Data: Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config)); - - num = nm_ip4_config_get_num_routes (priv->ip4_config); - for (i = 0; i < num; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (priv->ip4_config, i); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", nm_utils_inet4_ntop (route->network, NULL), route->plen, nm_utils_inet4_ntop (route->gateway, buf)); } - _LOGI ("Data: Forbid Default Route: %s", - nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no"); - num = nm_ip4_config_get_num_nameservers (priv->ip4_config); for (i = 0; i < num; i++) { _LOGI ("Data: Internal DNS: %s", @@ -992,30 +1032,26 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: No IPv4 configuration"); if (priv->ip6_config) { + const NMPlatformIP6Route *route; + _LOGI ("Data: IPv6 configuration:"); - address6 = nm_ip6_config_get_address (priv->ip6_config, 0); + address6 = nm_ip6_config_get_first_address (priv->ip6_config); + nm_assert (address6); if (priv->ip6_internal_gw) _LOGI ("Data: Internal Gateway: %s", nm_utils_inet6_ntop (priv->ip6_internal_gw, NULL)); _LOGI ("Data: Internal Address: %s", nm_utils_inet6_ntop (&address6->address, NULL)); _LOGI ("Data: Internal Prefix: %d", address6->plen); _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, NULL)); - _LOGI ("Data: Maximum Segment Size (MSS): %d", nm_ip6_config_get_mss (priv->ip6_config)); - - num = nm_ip6_config_get_num_routes (priv->ip6_config); - for (i = 0; i < num; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (priv->ip6_config, i); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", nm_utils_inet6_ntop (&route->network, NULL), route->plen, nm_utils_inet6_ntop (&route->gateway, buf)); } - _LOGI ("Data: Forbid Default Route: %s", - nm_ip6_config_get_never_default (priv->ip6_config) ? "yes" : "no"); - num = nm_ip6_config_get_num_nameservers (priv->ip6_config); for (i = 0; i < num; i++) { _LOGI ("Data: Internal DNS: %s", @@ -1042,42 +1078,45 @@ apply_parent_device_config (NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self)); + int ifindex; NMIP4Config *vpn4_parent_config = NULL; NMIP6Config *vpn6_parent_config = NULL; - if (priv->ip_ifindex > 0) { - if (priv->ip4_config) - vpn4_parent_config = nm_ip4_config_new (priv->ip_ifindex); - if (priv->ip6_config) - vpn6_parent_config = nm_ip6_config_new (priv->ip_ifindex); - } else { - int ifindex; - + ifindex = nm_device_get_ip_ifindex (parent_dev); + if (ifindex > 0) { /* If the VPN didn't return a network interface, it is a route-based * VPN (like kernel IPSec) and all IP addressing and routing should * be done on the parent interface instead. */ - - /* Also clear the gateway. We don't configure the gateway as part of the - * vpn-config. Instead we tell NMDefaultRouteManager directly about the - * default route. */ - ifindex = nm_device_get_ip_ifindex (parent_dev); if (priv->ip4_config) { - vpn4_parent_config = nm_ip4_config_new (ifindex); - nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_NO_DNS); + vpn4_parent_config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); + if (priv->ip_ifindex <= 0) + nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_NO_DNS, 0); } if (priv->ip6_config) { - vpn6_parent_config = nm_ip6_config_new (ifindex); - nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_NO_DNS); - nm_ip6_config_set_gateway (vpn6_parent_config, NULL); + vpn6_parent_config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); + if (priv->ip_ifindex <= 0) + nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_NO_DNS, 0); } } /* Add any explicit route to the VPN gateway through the parent device */ - if (vpn4_parent_config && priv->ip4_external_gw) - add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw); - if (vpn6_parent_config && priv->ip6_external_gw) - add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw); + if ( vpn4_parent_config + && priv->ip4_external_gw) { + add_ip4_vpn_gateway_route (vpn4_parent_config, + parent_dev, + priv->ip4_external_gw, + nm_netns_get_platform (priv->netns)); + } + if ( vpn6_parent_config + && priv->ip6_external_gw) { + add_ip6_vpn_gateway_route (vpn6_parent_config, + parent_dev, + priv->ip6_external_gw, + nm_netns_get_platform (priv->netns)); + } nm_device_replace_vpn4_config (parent_dev, priv->last_device_ip4_config, vpn4_parent_config); g_clear_object (&priv->last_device_ip4_config); @@ -1093,25 +1132,32 @@ nm_vpn_connection_apply_config (NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + apply_parent_device_config (self); + if (priv->ip_ifindex > 0) { nm_platform_link_set_up (nm_netns_get_platform (priv->netns), priv->ip_ifindex, NULL); if (priv->ip4_config) { + nm_assert (priv->ip_ifindex == nm_ip4_config_get_ifindex (priv->ip4_config)); if (!nm_ip4_config_commit (priv->ip4_config, nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE, - nm_vpn_connection_get_ip4_route_metric (self))) + get_route_table (self, AF_INET, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN)) return FALSE; + nm_platform_ip4_dev_route_blacklist_set (nm_netns_get_platform (priv->netns), + priv->ip_ifindex, + priv->ip4_dev_route_blacklist); } if (priv->ip6_config) { + nm_assert (priv->ip_ifindex == nm_ip6_config_get_ifindex (priv->ip6_config)); if (!nm_ip6_config_commit (priv->ip6_config, nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE)) + get_route_table (self, AF_INET6, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + NULL)) return FALSE; } @@ -1119,11 +1165,6 @@ nm_vpn_connection_apply_config (NMVpnConnection *self) nm_platform_link_set_mtu (nm_netns_get_platform (priv->netns), priv->ip_ifindex, priv->mtu); } - apply_parent_device_config (self); - - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - _LOGI ("VPN connection: (IP Config Get) complete"); if (priv->vpn_state < STATE_PRE_UP) _set_vpn_state (self, STATE_PRE_UP, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); @@ -1387,19 +1428,47 @@ nm_vpn_connection_get_ip6_route_metric (NMVpnConnection *self) return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT; } +static guint32 +get_route_table (NMVpnConnection *self, + int addr_family, + gboolean fallback_main) +{ + NMConnection *connection; + NMSettingIPConfig *s_ip; + guint32 route_table = 0; + + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + connection = _get_applied_connection (self); + if (connection) { + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); + + if (s_ip) + route_table = nm_setting_ip_config_get_route_table (s_ip); + } + + return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); +} + static void nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMPlatformIP4Address address; - NMIP4Config *config; guint32 u32, route_metric; + NMSettingIPConfig *s_ip; + guint32 route_table; + NMIP4Config *config; GVariantIter *iter; const char *str; GVariant *v; gboolean b; - guint i, n; int ip_ifindex; + guint32 mss = 0; + gboolean never_default = FALSE; g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT)); @@ -1436,17 +1505,16 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) if (ip_ifindex <= 0) g_return_if_reached (); - config = nm_ip4_config_new (ip_ifindex); + config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + ip_ifindex); nm_ip4_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN); memset (&address, 0, sizeof (address)); address.plen = 24; /* Internal address of the VPN subnet's gateway */ - if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, "u", &u32)) { + if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, "u", &u32)) priv->ip4_internal_gw = u32; - nm_ip4_config_set_gateway (config, priv->ip4_internal_gw); - } if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, "u", &u32)) address.address = u32; @@ -1482,7 +1550,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) } if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_MSS, "u", &u32)) - nm_ip4_config_set_mss (config, u32); + mss = u32; if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, "&s", &str)) nm_ip4_config_add_domain (config, str); @@ -1493,14 +1561,17 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) g_variant_iter_free (iter); } + route_table = get_route_table (self, AF_INET, TRUE); route_metric = nm_vpn_connection_get_ip4_route_metric (self); if ( g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { if (priv->ip4_config) { - n = nm_ip4_config_get_num_routes (priv->ip4_config); - for (i = 0; i < n; i++) - nm_ip4_config_add_route (config, nm_ip4_config_get_route (priv->ip4_config, i)); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) + nm_ip4_config_add_route (config, route, NULL); } } else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) { while (g_variant_iter_next (iter, "@au", &v)) { @@ -1516,12 +1587,14 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) g_variant_get_child (v, 1, "u", &plen); g_variant_get_child (v, 2, "u", &route.gateway); /* 4th item is unused route metric */ + route.table_coerced = nm_platform_route_table_coerce (route_table); route.metric = route_metric; route.rt_source = NM_IP_CONFIG_SOURCE_VPN; if (plen > 32 || plen == 0) break; route.plen = plen; + route.network = nm_utils_ip4_address_clear_host_address (route.network, plen); /* Ignore host routes to the VPN gateway since NM adds one itself * below. Since NM knows more about the routing situation than @@ -1529,7 +1602,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) * whatever the server provides. */ if (!(priv->ip4_external_gw && route.network == priv->ip4_external_gw && route.plen == 32)) - nm_ip4_config_add_route (config, &route); + nm_ip4_config_add_route (config, &route, NULL); break; default: break; @@ -1540,13 +1613,36 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) } if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, "b", &b)) - nm_ip4_config_set_never_default (config, b); + never_default = b; /* Merge in user overrides from the NMConnection's IPv4 setting */ + s_ip = nm_connection_get_setting_ip4_config (_get_applied_connection (self)); nm_ip4_config_merge_setting (config, - nm_connection_get_setting_ip4_config (_get_applied_connection (self)), + s_ip, + route_table, route_metric); + if ( !never_default + && !nm_setting_ip_config_get_never_default (s_ip)) { + const NMPlatformIP4Route r = { + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = priv->ip4_internal_gw, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .mss = mss, + }; + + nm_ip4_config_add_route (config, &r, NULL); + } + + g_clear_pointer (&priv->ip4_dev_route_blacklist, g_ptr_array_unref); + + nm_ip4_config_add_dependent_routes (config, + route_table, + nm_vpn_connection_get_ip4_route_metric (self), + &priv->ip4_dev_route_blacklist); + if (priv->ip4_config) { nm_ip4_config_replace (priv->ip4_config, config, NULL); g_object_unref (config); @@ -1565,13 +1661,16 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMPlatformIP6Address address; guint32 u32, route_metric; + NMSettingIPConfig *s_ip; + guint32 route_table; NMIP6Config *config; GVariantIter *iter; const char *str; GVariant *v; gboolean b; - guint i, n; int ip_ifindex; + guint32 mss = 0; + gboolean never_default = FALSE; g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT)); @@ -1595,7 +1694,8 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) if (ip_ifindex <= 0) g_return_if_reached (); - config = nm_ip6_config_new (ip_ifindex); + config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + ip_ifindex); nm_ip6_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN); memset (&address, 0, sizeof (address)); @@ -1605,7 +1705,6 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) g_clear_pointer (&priv->ip6_internal_gw, g_free); if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, "@ay", &v)) { priv->ip6_internal_gw = ip6_addr_dup_from_variant (v); - nm_ip6_config_set_gateway (config, priv->ip6_internal_gw); g_variant_unref (v); } @@ -1644,7 +1743,7 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) } if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_MSS, "u", &u32)) - nm_ip6_config_set_mss (config, u32); + mss = u32; if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN, "&s", &str)) nm_ip6_config_add_domain (config, str); @@ -1655,14 +1754,17 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) g_variant_iter_free (iter); } + route_table = get_route_table (self, AF_INET6, TRUE); route_metric = nm_vpn_connection_get_ip6_route_metric (self); if ( g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { if (priv->ip6_config) { - n = nm_ip6_config_get_num_routes (priv->ip6_config); - for (i = 0; i < n; i++) - nm_ip6_config_add_route (config, nm_ip6_config_get_route (priv->ip6_config, i)); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) + nm_ip6_config_add_route (config, route, NULL); } } else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) { GVariant *dest, *next_hop; @@ -1681,6 +1783,7 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) route.plen = prefix; ip6_addr_from_variant (next_hop, &route.gateway); + route.table_coerced = nm_platform_route_table_coerce (route_table); route.metric = route_metric; route.rt_source = NM_IP_CONFIG_SOURCE_VPN; @@ -1690,7 +1793,7 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) * the server provides. */ if (!(priv->ip6_external_gw && IN6_ARE_ADDR_EQUAL (&route.network, priv->ip6_external_gw) && route.plen == 128)) - nm_ip6_config_add_route (config, &route); + nm_ip6_config_add_route (config, &route, NULL); next: g_variant_unref (dest); @@ -1700,13 +1803,33 @@ next: } if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, "b", &b)) - nm_ip6_config_set_never_default (config, b); + never_default = b; /* Merge in user overrides from the NMConnection's IPv6 setting */ + s_ip = nm_connection_get_setting_ip6_config (_get_applied_connection (self)); nm_ip6_config_merge_setting (config, - nm_connection_get_setting_ip6_config (_get_applied_connection (self)), + s_ip, + route_table, route_metric); + if ( !never_default + && !nm_setting_ip_config_get_never_default (s_ip)) { + const NMPlatformIP6Route r = { + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = *(priv->ip6_internal_gw ?: &in6addr_any), + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .mss = mss, + }; + + nm_ip6_config_add_route (config, &r, NULL); + } + + nm_ip6_config_add_dependent_routes (config, + route_table, + route_metric); + if (priv->ip6_config) { nm_ip6_config_replace (priv->ip6_config, config, NULL); g_object_unref (config); @@ -2636,6 +2759,8 @@ dispose (GObject *object) g_clear_pointer (&priv->connect_hash, g_variant_unref); + g_clear_pointer (&priv->ip4_dev_route_blacklist, g_ptr_array_unref); + nm_clear_g_source (&priv->connect_timeout); dispatcher_cleanup (self); diff --git a/src/vpn/nm-vpn-manager.c b/src/vpn/nm-vpn-manager.c index 8e708d12..d0639168 100644 --- a/src/vpn/nm-vpn-manager.c +++ b/src/vpn/nm-vpn-manager.c @@ -255,7 +255,7 @@ nm_vpn_manager_init (NMVpnManager *self) try_add_plugin (self, info->data); g_slist_free_full (infos, g_object_unref); - priv->active_services = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->active_services = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); } static void |