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/devices | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/devices')
60 files changed, 7607 insertions, 3007 deletions
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), |