From af1f183b3d1a38548be96cf41e835847e89b953b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 15 Jul 2014 17:16:56 -0500 Subject: [PATCH 01/11] wwan: split out WWAN IP4Config from device config We want to set the WWAN config last, to ensure that the configuration we use overwrites anything that pppd might have set, becuase it touches some stuff itself. That means we have to keep the WWAN config separate, since dev_ip4_config is used for DHCP and IPv4LL, which we always set first to ensure they these don't overwrite external, administrator added config (eg, priv->ext_ip4_config). This also synchronizes the IPv4 config path with the upcoming IPv6 config path. --- src/devices/nm-device-private.h | 2 ++ src/devices/nm-device.c | 29 +++++++++++++++++++++++++++++ src/devices/wwan/nm-device-modem.c | 6 ++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index c1f1c799bf..3bb17db162 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -98,4 +98,6 @@ void nm_device_set_carrier (NMDevice *device, gboolean carrier); void nm_device_emit_recheck_auto_activate (NMDevice *device); void nm_device_queue_recheck_assume (NMDevice *device); +void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ee6c3862ea..0a17300978 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -237,6 +237,7 @@ typedef struct { IpState ip4_state; NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ + NMIP4Config * wwan_ip4_config; /* WWAN configuration */ /* DHCPv4 tracking */ NMDHCPClient * dhcp4_client; @@ -2662,6 +2663,12 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->ext_ip4_config) nm_ip4_config_merge (composite, priv->ext_ip4_config); + /* Merge WWAN config *last* to ensure modem-given settings overwrite + * any external stuff set by pppd or other scripts. + */ + if (priv->wwan_ip4_config) + nm_ip4_config_merge (composite, priv->wwan_ip4_config); + /* Merge user overrides into the composite config */ connection = nm_device_get_connection (self); if (connection) { @@ -5204,6 +5211,25 @@ nm_device_set_vpn4_config (NMDevice *device, NMIP4Config *config) } } +void +nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + if (priv->wwan_ip4_config == config) + return; + + g_clear_object (&priv->wwan_ip4_config); + if (config) + priv->wwan_ip4_config = g_object_ref (config); + + /* NULL to use existing configs */ + if (!ip4_config_merge_and_apply (device, NULL, TRUE, NULL)) { + nm_log_warn (LOGD_IP4, "(%s): failed to set WWAN IPv4 configuration", + nm_device_get_ip_iface (device)); + } +} + static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, @@ -5864,6 +5890,8 @@ update_ip_config (NMDevice *self, gboolean initial) nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); if (priv->vpn4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config); + if (priv->wwan_ip4_config) + nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); ip4_config_merge_and_apply (self, NULL, FALSE, NULL); } @@ -6511,6 +6539,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) nm_device_set_ip6_config (self, NULL, TRUE, &ignored); g_clear_object (&priv->dev_ip4_config); g_clear_object (&priv->ext_ip4_config); + g_clear_object (&priv->wwan_ip4_config); g_clear_object (&priv->vpn4_config); g_clear_object (&priv->ip4_config); g_clear_object (&priv->ac_ip6_config); diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 98ff610229..a4d92998dd 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -158,8 +158,10 @@ modem_ip4_config_result (NMModem *self, error && error->message ? error->message : "(unknown)"); nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else - nm_device_activate_schedule_ip4_config_result (device, config); + } else { + nm_device_set_wwan_ip4_config (device, config); + nm_device_activate_schedule_ip4_config_result (device, NULL); + } } static void From 75fa46bd19dd63f78bf0990e69219e416e6354ea Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Jun 2014 09:59:38 -0500 Subject: [PATCH 02/11] ppp: add IPv6 support Add support for IPv6 to the pppd plugin and return the interface identifiers to NetworkManager. Use those to construct the IPv6LL addresses for the PPP interface and the peer. --- introspection/nm-ppp-manager.xml | 5 + src/ppp-manager/Makefile.am | 4 +- src/ppp-manager/nm-ppp-manager.c | 158 +++++++++++++++++++++++++------ src/ppp-manager/nm-ppp-manager.h | 6 ++ src/ppp-manager/nm-pppd-plugin.c | 74 ++++++++++++++- src/ppp-manager/nm-pppd-plugin.h | 19 ++-- 6 files changed, 228 insertions(+), 38 deletions(-) diff --git a/introspection/nm-ppp-manager.xml b/introspection/nm-ppp-manager.xml index 9e2dfdb1c3..2867daf979 100644 --- a/introspection/nm-ppp-manager.xml +++ b/introspection/nm-ppp-manager.xml @@ -14,6 +14,11 @@ + + + + + diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am index 2d740f14a3..ba8c2d7be5 100644 --- a/src/ppp-manager/Makefile.am +++ b/src/ppp-manager/Makefile.am @@ -20,8 +20,8 @@ nm_pppd_plugin_la_SOURCES = \ nm_pppd_plugin_la_LDFLAGS = -module -avoid-version nm_pppd_plugin_la_LIBADD = \ - $(top_builddir)/libnm-util/libnm-util.la \ $(DBUS_LIBS) \ - $(GLIB_LIBS) + $(GLIB_LIBS) \ + -ldl endif diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c index 7a837fbce4..9e7669d85c 100644 --- a/src/ppp-manager/nm-ppp-manager.c +++ b/src/ppp-manager/nm-ppp-manager.c @@ -67,6 +67,10 @@ static gboolean impl_ppp_manager_set_ip4_config (NMPPPManager *manager, GHashTable *config, GError **err); +static gboolean impl_ppp_manager_set_ip6_config (NMPPPManager *manager, + GHashTable *config, + GError **err); + #include "nm-ppp-manager-glue.h" static void _ppp_cleanup (NMPPPManager *manager); @@ -101,6 +105,7 @@ G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, G_TYPE_OBJECT) enum { STATE_CHANGED, IP4_CONFIG, + IP6_CONFIG, STATS, LAST_SIGNAL @@ -132,6 +137,7 @@ nm_ppp_manager_error_quark (void) static void nm_ppp_manager_init (NMPPPManager *manager) { + NM_PPP_MANAGER_GET_PRIVATE (manager)->monitor_fd = -1; } static void @@ -245,6 +251,14 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) G_TYPE_STRING, G_TYPE_OBJECT); + signals[IP6_CONFIG] = + g_signal_new ("ip6-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, ip6_config), + NULL, NULL, NULL, + G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT); + signals[STATS] = g_signal_new ("stats", G_OBJECT_CLASS_TYPE (object_class), @@ -300,8 +314,12 @@ monitor_stats (NMPPPManager *manager) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + /* already monitoring */ + if (priv->monitor_fd >= 0) + return; + priv->monitor_fd = socket (AF_INET, SOCK_DGRAM, 0); - if (priv->monitor_fd > 0) { + if (priv->monitor_fd >= 0) { g_warn_if_fail (priv->monitor_id == 0); if (priv->monitor_id) g_source_remove (priv->monitor_id); @@ -498,20 +516,52 @@ static gboolean impl_ppp_manager_set_state (NMPPPManager *manager, return TRUE; } +static gboolean +set_ip_config_common (NMPPPManager *self, + GHashTable *hash, + const char *iface_prop, + guint32 *out_mtu) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); + NMConnection *connection; + NMSettingPPP *s_ppp; + GValue *val; + + val = g_hash_table_lookup (hash, iface_prop); + if (!val || !G_VALUE_HOLDS_STRING (val)) { + nm_log_err (LOGD_PPP, "no interface received!"); + return FALSE; + } + if (priv->ip_iface == NULL) + priv->ip_iface = g_value_dup_string (val); + + /* Got successful IP config; obviously the secrets worked */ + connection = nm_act_request_get_connection (priv->act_req); + g_assert (connection); + g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL); + + /* Get any custom MTU */ + s_ppp = nm_connection_get_setting_ppp (connection); + if (s_ppp && out_mtu) + *out_mtu = nm_setting_ppp_get_mtu (s_ppp); + + monitor_stats (self); + return TRUE; +} + static gboolean impl_ppp_manager_set_ip4_config (NMPPPManager *manager, GHashTable *config_hash, GError **err) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - NMConnection *connection; - NMSettingPPP *s_ppp; NMIP4Config *config; NMPlatformIP4Address address; GValue *val; int i; + guint32 mtu = 0; - nm_log_info (LOGD_PPP, "PPP manager(IP Config Get) reply received."); + nm_log_info (LOGD_PPP, "PPP manager (IPv4 Config Get) reply received."); remove_timeout_handler (manager); @@ -557,35 +607,89 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, nm_ip4_config_add_wins (config, g_array_index (wins, guint, i)); } - val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_INTERFACE); - if (!val || !G_VALUE_HOLDS_STRING (val)) { - nm_log_err (LOGD_PPP, "no interface received!"); + if (!set_ip_config_common (manager, config_hash, NM_PPP_IP4_CONFIG_INTERFACE, &mtu)) goto out; - } - priv->ip_iface = g_value_dup_string (val); - /* Got successful IP4 config; obviously the secrets worked */ - connection = nm_act_request_get_connection (priv->act_req); - g_assert (connection); - g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL); - - /* Merge in custom MTU */ - s_ppp = nm_connection_get_setting_ppp (connection); - if (s_ppp) { - guint32 mtu = nm_setting_ppp_get_mtu (s_ppp); - - if (mtu) - nm_ip4_config_set_mtu (config, mtu); - } + if (mtu) + nm_ip4_config_set_mtu (config, mtu); /* Push the IP4 config up to the device */ g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config); - monitor_stats (manager); - - out: +out: g_object_unref (config); + return TRUE; +} +/* Converts the named Interface Identifier item to an IPv6 LL address and + * returns the IID. + */ +static gboolean +iid_value_to_ll6_addr (GHashTable *hash, + const char *prop, + struct in6_addr *out_addr, + NMUtilsIPv6IfaceId *out_iid) +{ + GValue *val; + guint64 iid; + + val = g_hash_table_lookup (hash, prop); + if (!val || !G_VALUE_HOLDS (val, G_TYPE_UINT64)) { + nm_log_dbg (LOGD_PPP, "pppd plugin property '%s' missing or not a uint64", prop); + return FALSE; + } + + iid = g_value_get_uint64 (val); + g_return_val_if_fail (iid != 0, FALSE); + + /* Construct an IPv6 LL address from the interface identifier. See + * http://tools.ietf.org/html/rfc4291#section-2.5.1 (IPv6) and + * http://tools.ietf.org/html/rfc5072#section-4.1 (IPv6 over PPP). + */ + memset (out_addr->s6_addr, 0, sizeof (out_addr->s6_addr)); + out_addr->s6_addr16[0] = htons (0xfe80); + memcpy (out_addr->s6_addr + 8, &iid, sizeof (iid)); + if (out_iid) + nm_utils_ipv6_interface_identfier_get_from_addr (out_iid, out_addr); + return TRUE; +} + +static gboolean +impl_ppp_manager_set_ip6_config (NMPPPManager *manager, + GHashTable *hash, + GError **err) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMIP6Config *config; + NMPlatformIP6Address addr; + struct in6_addr a; + NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; + + nm_log_info (LOGD_PPP, "PPP manager (IPv6 Config Get) reply received."); + + remove_timeout_handler (manager); + + config = nm_ip6_config_new (); + + memset (&addr, 0, sizeof (addr)); + addr.plen = 64; + + if (iid_value_to_ll6_addr (hash, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) { + nm_ip6_config_set_gateway (config, &a); + addr.peer_address = a; + } + + if (iid_value_to_ll6_addr (hash, NM_PPP_IP6_CONFIG_OUR_IID, &addr.address, &iid)) { + nm_ip6_config_add_address (config, &addr); + + if (set_ip_config_common (manager, hash, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) { + /* Push the IPv6 config and interface identifier up to the device */ + g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config); + } + } else + nm_log_err (LOGD_PPP, "invalid IPv6 address received!"); + + g_object_unref (config); return TRUE; } @@ -1127,11 +1231,11 @@ _ppp_cleanup (NMPPPManager *manager) priv->monitor_id = 0; } - if (priv->monitor_fd) { + if (priv->monitor_fd >= 0) { /* Get the stats one last time */ monitor_cb (manager); close (priv->monitor_fd); - priv->monitor_fd = 0; + priv->monitor_fd = -1; } if (priv->ppp_timeout_handler) { diff --git a/src/ppp-manager/nm-ppp-manager.h b/src/ppp-manager/nm-ppp-manager.h index 1b4bc1886d..7b0125b413 100644 --- a/src/ppp-manager/nm-ppp-manager.h +++ b/src/ppp-manager/nm-ppp-manager.h @@ -29,7 +29,9 @@ #include "nm-activation-request.h" #include "nm-connection.h" #include "nm-ip4-config.h" +#include "nm-ip6-config.h" #include "nm-pppd-plugin.h" +#include "NetworkManagerUtils.h" #define NM_TYPE_PPP_MANAGER (nm_ppp_manager_get_type ()) #define NM_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_MANAGER, NMPPPManager)) @@ -50,6 +52,10 @@ typedef struct { /* Signals */ void (*state_changed) (NMPPPManager *manager, NMPPPStatus status); void (*ip4_config) (NMPPPManager *manager, const char *iface, NMIP4Config *config); + void (*ip6_config) (NMPPPManager *manager, + const char *iface, + const NMUtilsIPv6IfaceId *iid, + NMIP6Config *config); void (*stats) (NMPPPManager *manager, guint32 in_bytes, guint32 out_bytes); } NMPPPManagerClass; diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c index e6ee86f7a4..0cea4e1d10 100644 --- a/src/ppp-manager/nm-pppd-plugin.c +++ b/src/ppp-manager/nm-pppd-plugin.c @@ -19,6 +19,7 @@ * Copyright (C) 2008 Red Hat, Inc. */ +#include #include #include #include @@ -26,10 +27,15 @@ #include #include #include +#include #include #include #include +#define INET6 +#include +#include + #include "NetworkManager.h" #include "nm-pppd-plugin.h" #include "nm-ppp-status.h" @@ -128,7 +134,6 @@ str_to_gvalue (const char *str) val = g_slice_new0 (GValue); g_value_init (val, G_TYPE_STRING); g_value_set_string (val, str); - return val; } @@ -140,7 +145,6 @@ uint_to_gvalue (guint32 i) val = g_slice_new0 (GValue); g_value_init (val, G_TYPE_UINT); g_value_set_uint (val, i); - return val; } @@ -230,7 +234,7 @@ nm_ip_up (void *data, int arg) g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_WINS, val); } - g_message ("nm-ppp-plugin: (%s): sending Ip4Config to NetworkManager...", __func__); + g_message ("nm-ppp-plugin: (%s): sending IPv4 config to NetworkManager...", __func__); dbus_g_proxy_call_no_reply (proxy, "SetIp4Config", DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID, @@ -239,6 +243,48 @@ nm_ip_up (void *data, int arg) g_hash_table_destroy (hash); } +static GValue * +eui64_to_gvalue (eui64_t eui) +{ + GValue *val; + guint64 iid; + + G_STATIC_ASSERT (sizeof (iid) == sizeof (eui)); + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_UINT64); + memcpy (&iid, &eui, sizeof (eui)); + g_value_set_uint64 (val, iid); + return val; +} + +static void +nm_ip6_up (void *data, int arg) +{ + ipv6cp_options *ho = &ipv6cp_hisoptions[0]; + ipv6cp_options *go = &ipv6cp_gotoptions[0]; + GHashTable *hash; + + g_return_if_fail (DBUS_IS_G_PROXY (proxy)); + + g_message ("nm-ppp-plugin: (%s): ip6-up event", __func__); + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy); + g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_INTERFACE, str_to_gvalue (ifname)); + g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_OUR_IID, eui64_to_gvalue (go->ourid)); + g_hash_table_insert (hash, NM_PPP_IP6_CONFIG_PEER_IID, eui64_to_gvalue (ho->hisid)); + + /* DNS is done via DHCPv6 or router advertisements */ + + g_message ("nm-ppp-plugin: (%s): sending IPv6 config to NetworkManager...", __func__); + + dbus_g_proxy_call_no_reply (proxy, "SetIp6Config", + DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID, + G_TYPE_INVALID); + + g_hash_table_destroy (hash); +} + static int get_chap_check (void) { @@ -319,6 +365,27 @@ nm_exit_notify (void *data, int arg) proxy = NULL; } +static void +add_ip6_notifier (void) +{ + static struct notifier **notifier = NULL; + static gsize load_once = 0; + + if (g_once_init_enter (&load_once)) { + void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); + + if (handle) { + notifier = dlsym (handle, "ipv6_up_notifier"); + dlclose (handle); + } + g_once_init_leave (&load_once, 1); + } + if (notifier) + add_notifier (notifier, nm_ip6_up, NULL); + else + g_message ("nm-ppp-plugin: no IPV6CP notifier support; IPv6 not available"); +} + int plugin_init (void) { @@ -356,6 +423,7 @@ plugin_init (void) add_notifier (&phasechange, nm_phasechange, NULL); add_notifier (&ip_up_notifier, nm_ip_up, NULL); add_notifier (&exitnotify, nm_exit_notify, proxy); + add_ip6_notifier (); return 0; } diff --git a/src/ppp-manager/nm-pppd-plugin.h b/src/ppp-manager/nm-pppd-plugin.h index 95a2a18291..2c3073e4ee 100644 --- a/src/ppp-manager/nm-pppd-plugin.h +++ b/src/ppp-manager/nm-pppd-plugin.h @@ -16,14 +16,21 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2014 Red Hat, Inc. */ #define NM_DBUS_INTERFACE_PPP "org.freedesktop.NetworkManager.PPP" #define NM_PPP_IP4_CONFIG_INTERFACE "interface" -#define NM_PPP_IP4_CONFIG_ADDRESS "address" -#define NM_PPP_IP4_CONFIG_PREFIX "prefix" -#define NM_PPP_IP4_CONFIG_GATEWAY "gateway" -#define NM_PPP_IP4_CONFIG_DNS "dns" -#define NM_PPP_IP4_CONFIG_WINS "wins" +#define NM_PPP_IP4_CONFIG_ADDRESS "address" +#define NM_PPP_IP4_CONFIG_PREFIX "prefix" +#define NM_PPP_IP4_CONFIG_GATEWAY "gateway" +#define NM_PPP_IP4_CONFIG_DNS "dns" +#define NM_PPP_IP4_CONFIG_WINS "wins" + +#define NM_PPP_IP6_CONFIG_INTERFACE "interface" +#define NM_PPP_IP6_CONFIG_OUR_IID "our-iid" +#define NM_PPP_IP6_CONFIG_PEER_IID "peer-iid" + +#define DBUS_TYPE_EUI64 (dbus_g_type_get_collection ("GByteArray", G_TYPE_UINT8)) + From f3557d326cdab575cde84d93743b3c9744925bb2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 15 Oct 2013 20:50:48 -0500 Subject: [PATCH 03/11] wwan: read supported IP types from ModemManager Not all modems support IPv6, and to prevent some common failure cases, make sure we don't try to use IPv6 when the modem doesn't support it. --- src/devices/wwan/nm-modem-broadband.c | 16 ++++++++++++++++ src/devices/wwan/nm-modem.c | 22 ++++++++++++++++++++++ src/devices/wwan/nm-modem.h | 23 +++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 6f0f6bd2ba..07ee7de423 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -807,6 +807,21 @@ modem_state_changed (MMModem *modem, /*****************************************************************************/ +static NMModemIPType +mm_ip_family_to_nm (MMBearerIpFamily family) +{ + NMModemIPType nm_type = NM_MODEM_IP_TYPE_UNKNOWN; + + if (family & MM_BEARER_IP_FAMILY_IPV4) + nm_type |= NM_MODEM_IP_TYPE_IPV4; + if (family & MM_BEARER_IP_FAMILY_IPV6) + nm_type |= NM_MODEM_IP_TYPE_IPV6; + if (family & MM_BEARER_IP_FAMILY_IPV4V6) + nm_type |= MM_BEARER_IP_FAMILY_IPV4V6; + + return nm_type; +} + NMModem * nm_modem_broadband_new (GObject *object, GError **error) { @@ -831,6 +846,7 @@ nm_modem_broadband_new (GObject *object, GError **error) NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface), NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */ + NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)), NM_MODEM_STATE, mm_state_to_nm (mm_modem_get_state (modem_iface)), NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), NM_MODEM_BROADBAND_MODEM, modem_object, diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 2df9f2c23e..d8ccfd42c7 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -47,6 +47,7 @@ enum { PROP_STATE, PROP_DEVICE_ID, PROP_SIM_ID, + PROP_IP_TYPES, LAST_PROP }; @@ -63,6 +64,7 @@ typedef struct { NMModemState prev_state; /* revert to this state if enable/disable fails */ char *device_id; char *sim_id; + NMModemIPType ip_types; NMPPPManager *ppp_manager; @@ -218,6 +220,12 @@ nm_modem_emit_removed (NMModem *self) g_signal_emit (self, signals[REMOVED], 0); } +NMModemIPType +nm_modem_get_supported_ip_types (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->ip_types; +} + /*****************************************************************************/ /* IP method PPP */ @@ -861,6 +869,9 @@ get_property (GObject *object, guint prop_id, case PROP_SIM_ID: g_value_set_string (value, priv->sim_id); break; + case PROP_IP_TYPES: + g_value_set_uint (value, priv->ip_types); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -909,6 +920,9 @@ set_property (GObject *object, guint prop_id, g_free (priv->sim_id); priv->sim_id = g_value_dup_string (value); break; + case PROP_IP_TYPES: + priv->ip_types = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1036,6 +1050,14 @@ nm_modem_class_init (NMModemClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_IP_TYPES, + g_param_spec_uint (NM_MODEM_IP_TYPES, + "IP Types", + "Supported IP types", + 0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + /* Signals */ signals[PPP_STATS] = diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index c992cf7214..b2999e2ff0 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -47,6 +47,7 @@ G_BEGIN_DECLS #define NM_MODEM_STATE "state" #define NM_MODEM_DEVICE_ID "device-id" #define NM_MODEM_SIM_ID "sim-id" +#define NM_MODEM_IP_TYPES "ip-types" /* Supported IP types */ /* Signals */ #define NM_MODEM_PPP_STATS "ppp-stats" @@ -62,12 +63,32 @@ G_BEGIN_DECLS #define MM_MODEM_IP_METHOD_STATIC 1 #define MM_MODEM_IP_METHOD_DHCP 2 +/** + * NMModemIPType: + * @NM_MODEM_IP_TYPE_UNKNOWN: unknown or no IP support + * @NM_MODEM_IP_TYPE_IPV4: IPv4-only bearers are supported + * @NM_MODEM_IP_TYPE_IPV6: IPv6-only bearers are supported + * @NM_MODEM_IP_TYPE_IPV4V6: dual-stack IPv4 + IPv6 bearers are supported + * + * Indicates what IP protocols the modem supports for an IP bearer. Any + * combination of flags is possible. For example, (%NM_MODEM_IP_TYPE_IPV4 | + * %NM_MODEM_IP_TYPE_IPV6) indicates that the modem supports IPv4 and IPv6 + * but not simultaneously on the same bearer. + */ +typedef enum { + NM_MODEM_IP_TYPE_UNKNOWN = 0x0, + NM_MODEM_IP_TYPE_IPV4 = 0x1, + NM_MODEM_IP_TYPE_IPV6 = 0x2, + NM_MODEM_IP_TYPE_IPV4V6 = 0x4 +} NMModemIPType; + typedef enum { NM_MODEM_ERROR_CONNECTION_NOT_GSM, /*< nick=ConnectionNotGsm >*/ NM_MODEM_ERROR_CONNECTION_NOT_CDMA, /*< nick=ConnectionNotCdma >*/ NM_MODEM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ NM_MODEM_ERROR_INITIALIZATION_FAILED, /*< nick=InitializationFailed >*/ + NM_MODEM_ERROR_IP_CONFIG_INVALID, /*< nick=IpConfigInvalid >*/ } NMModemError; typedef enum { /*< underscore_name=nm_modem_state >*/ @@ -209,6 +230,8 @@ void nm_modem_set_state (NMModem *self, void nm_modem_set_prev_state (NMModem *self, const char *reason); const char * nm_modem_state_to_string (NMModemState state); +NMModemIPType nm_modem_get_supported_ip_types (NMModem *self); + /* For the modem-manager only */ void nm_modem_emit_removed (NMModem *self); From dd6abd407acd461e1dd853edade83082dbb0098b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 19 Feb 2014 12:00:35 -0600 Subject: [PATCH 04/11] wwan: clean up and split IP method values Add an 'unknown' value; make the type an enum, and rename it since this enum is private to NetworkManager and abstracts differences in old and new MM. We also need separate methods for IPv4 and IPv6 since they may not use the same mechanism. For example, IPv4 may use DHCP but IPv6 may require static configuration, based on what the modem firmware wants. --- src/devices/wwan/nm-modem-broadband.c | 10 ++-- src/devices/wwan/nm-modem.c | 70 ++++++++++++++++----------- src/devices/wwan/nm-modem.h | 12 +++-- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 07ee7de423..56bb1c34ef 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -163,7 +163,7 @@ connect_ready (MMModemSimple *simple_iface, NMModemBroadband *self) { GError *error = NULL; - guint ip_method; + NMModemIPMethod ip_method; g_clear_object (&self->priv->connect_properties); @@ -200,13 +200,13 @@ connect_ready (MMModemSimple *simple_iface, switch (mm_bearer_ip_config_get_method (self->priv->ipv4_config)) { case MM_BEARER_IP_METHOD_PPP: - ip_method = MM_MODEM_IP_METHOD_PPP; + ip_method = NM_MODEM_IP_METHOD_PPP; break; case MM_BEARER_IP_METHOD_STATIC: - ip_method = MM_MODEM_IP_METHOD_STATIC; + ip_method = NM_MODEM_IP_METHOD_STATIC; break; case MM_BEARER_IP_METHOD_DHCP: - ip_method = MM_MODEM_IP_METHOD_DHCP; + ip_method = NM_MODEM_IP_METHOD_AUTO; break; default: error = g_error_new (NM_MODEM_ERROR, @@ -224,7 +224,7 @@ connect_ready (MMModemSimple *simple_iface, /* IPv4 for now only */ g_object_set (self, NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer), - NM_MODEM_IP_METHOD, ip_method, + NM_MODEM_IP4_METHOD, ip_method, NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer), NULL); diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index d8ccfd42c7..62d1b2b0ae 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -42,7 +42,8 @@ enum { PROP_PATH, PROP_UID, PROP_DRIVER, - PROP_IP_METHOD, + PROP_IP4_METHOD, + PROP_IP6_METHOD, PROP_IP_TIMEOUT, PROP_STATE, PROP_DEVICE_ID, @@ -59,7 +60,8 @@ typedef struct { char *control_port; char *data_port; char *ppp_iface; - guint32 ip_method; + NMModemIPMethod ip4_method; + NMModemIPMethod ip6_method; NMModemState state; NMModemState prev_state; /* revert to this state if enable/disable fails */ char *device_id; @@ -401,18 +403,18 @@ nm_modem_stage3_ip4_config_start (NMModem *self, g_assert (req); priv = NM_MODEM_GET_PRIVATE (self); - switch (priv->ip_method) { - case MM_MODEM_IP_METHOD_PPP: + switch (priv->ip4_method) { + case NM_MODEM_IP_METHOD_PPP: ret = ppp_stage3_ip4_config_start (self, req, reason); break; - case MM_MODEM_IP_METHOD_STATIC: + case NM_MODEM_IP_METHOD_STATIC: ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason); break; - case MM_MODEM_IP_METHOD_DHCP: + case NM_MODEM_IP_METHOD_AUTO: ret = device_class->act_stage3_ip4_config_start (device, NULL, reason); break; default: - nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip4_method); ret = NM_ACT_STAGE_RETURN_FAILURE; break; } @@ -431,8 +433,8 @@ nm_modem_ip4_pre_commit (NMModem *modem, * 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->ip_method == MM_MODEM_IP_METHOD_STATIC - || priv->ip_method == MM_MODEM_IP_METHOD_DHCP) { + 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); g_assert (address); @@ -641,22 +643,19 @@ deactivate (NMModem *self, NMDevice *device) priv->ppp_manager = NULL; } - switch (priv->ip_method) { - case MM_MODEM_IP_METHOD_PPP: - break; - case MM_MODEM_IP_METHOD_STATIC: - case MM_MODEM_IP_METHOD_DHCP: + if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC || + priv->ip4_method == NM_MODEM_IP_METHOD_AUTO || + priv->ip6_method == NM_MODEM_IP_METHOD_STATIC || + priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { ifindex = nm_device_get_ip_ifindex (device); if (ifindex > 0) { nm_platform_route_flush (ifindex); nm_platform_address_flush (ifindex); nm_platform_link_set_down (ifindex); } - break; - default: - nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); - break; } + priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; + priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; g_free (priv->ppp_iface); priv->ppp_iface = NULL; @@ -854,8 +853,11 @@ get_property (GObject *object, guint prop_id, case PROP_UID: g_value_set_string (value, priv->uid); break; - case PROP_IP_METHOD: - g_value_set_uint (value, priv->ip_method); + case PROP_IP4_METHOD: + g_value_set_uint (value, priv->ip4_method); + break; + case PROP_IP6_METHOD: + g_value_set_uint (value, priv->ip6_method); break; case PROP_IP_TIMEOUT: g_value_set_uint (value, priv->mm_ip_timeout); @@ -903,8 +905,11 @@ set_property (GObject *object, guint prop_id, /* Construct only */ priv->uid = g_value_dup_string (value); break; - case PROP_IP_METHOD: - priv->ip_method = g_value_get_uint (value); + case PROP_IP4_METHOD: + priv->ip4_method = g_value_get_uint (value); + break; + case PROP_IP6_METHOD: + priv->ip6_method = g_value_get_uint (value); break; case PROP_IP_TIMEOUT: priv->mm_ip_timeout = g_value_get_uint (value); @@ -1013,12 +1018,21 @@ nm_modem_class_init (NMModemClass *klass) G_PARAM_STATIC_STRINGS)); g_object_class_install_property - (object_class, PROP_IP_METHOD, - g_param_spec_uint (NM_MODEM_IP_METHOD, "", "", - MM_MODEM_IP_METHOD_PPP, - MM_MODEM_IP_METHOD_DHCP, - MM_MODEM_IP_METHOD_PPP, - G_PARAM_READWRITE | + (object_class, PROP_IP4_METHOD, + g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "", + NM_MODEM_IP_METHOD_UNKNOWN, + NM_MODEM_IP_METHOD_AUTO, + NM_MODEM_IP_METHOD_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_IP6_METHOD, + g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "", + NM_MODEM_IP_METHOD_UNKNOWN, + NM_MODEM_IP_METHOD_AUTO, + NM_MODEM_IP_METHOD_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index b2999e2ff0..7fc215c0fa 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -42,7 +42,8 @@ G_BEGIN_DECLS #define NM_MODEM_DRIVER "driver" #define NM_MODEM_CONTROL_PORT "control-port" #define NM_MODEM_DATA_PORT "data-port" -#define NM_MODEM_IP_METHOD "ip-method" +#define NM_MODEM_IP4_METHOD "ip4-method" +#define NM_MODEM_IP6_METHOD "ip6-method" #define NM_MODEM_IP_TIMEOUT "ip-timeout" #define NM_MODEM_STATE "state" #define NM_MODEM_DEVICE_ID "device-id" @@ -59,9 +60,12 @@ G_BEGIN_DECLS #define NM_MODEM_REMOVED "removed" #define NM_MODEM_STATE_CHANGED "state-changed" -#define MM_MODEM_IP_METHOD_PPP 0 -#define MM_MODEM_IP_METHOD_STATIC 1 -#define MM_MODEM_IP_METHOD_DHCP 2 +typedef enum { + NM_MODEM_IP_METHOD_UNKNOWN = 0, + NM_MODEM_IP_METHOD_PPP, + NM_MODEM_IP_METHOD_STATIC, + NM_MODEM_IP_METHOD_AUTO, /* DHCP and/or SLAAC */ +} NMModemIPMethod; /** * NMModemIPType: From 8c05e26c425f8bbe2429614dbc0ee34c70301110 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 15 Oct 2013 21:03:42 -0500 Subject: [PATCH 05/11] wwan: add infrastructure for IPv6 config results --- src/devices/bluetooth/nm-device-bt.c | 3 +- src/devices/nm-device-private.h | 3 + src/devices/nm-device.c | 35 +++- src/devices/wwan/nm-device-modem.c | 122 ++++++++++++- src/devices/wwan/nm-modem.c | 257 +++++++++++++++++++++++++-- src/devices/wwan/nm-modem.h | 24 ++- src/devices/wwan/wwan-exports.ver | 1 + 7 files changed, 418 insertions(+), 27 deletions(-) diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 9d59344933..17f77bc610 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -882,8 +882,7 @@ act_stage3_ip6_config_start (NMDevice *device, if (priv->bt_type == NM_BT_CAPABILITY_DUN) { ret = nm_modem_stage3_ip6_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem, - device, - NM_DEVICE_CLASS (nm_device_bt_parent_class), + nm_device_get_act_request (device), reason); } else ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, reason); diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 3bb17db162..ec245f4356 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -99,5 +99,8 @@ void nm_device_emit_recheck_auto_activate (NMDevice *device); void nm_device_queue_recheck_assume (NMDevice *device); void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); +void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); + +gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 0a17300978..8078a16bdb 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -264,6 +264,7 @@ typedef struct { NMIP6Config * ip6_config; IpState ip6_state; NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ + NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ NMRDisc * rdisc; @@ -446,7 +447,7 @@ reason_to_string (NMDeviceStateReason reason) /***********************************************************/ -static inline gboolean +gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); @@ -3096,6 +3097,12 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->ext_ip6_config) nm_ip6_config_merge (composite, priv->ext_ip6_config); + /* Merge WWAN config *last* to ensure modem-given settings overwrite + * any external stuff set by pppd or other scripts. + */ + if (priv->wwan_ip6_config) + nm_ip6_config_merge (composite, priv->wwan_ip6_config); + /* Merge user overrides into the composite config */ connection = nm_device_get_connection (self); if (connection) { @@ -4630,11 +4637,11 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf NMDevicePrivate *priv; g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (NM_IS_IP4_CONFIG (config)); priv = NM_DEVICE_GET_PRIVATE (self); g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); + if (config) + priv->dev_ip4_config = g_object_ref (config); activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET); @@ -5334,6 +5341,25 @@ nm_device_set_vpn6_config (NMDevice *device, NMIP6Config *config) } } +void +nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + if (priv->wwan_ip6_config == config) + return; + + g_clear_object (&priv->wwan_ip6_config); + if (config) + priv->wwan_ip6_config = g_object_ref (config); + + /* NULL to use existing configs */ + if (!ip6_config_merge_and_apply (device, TRUE, NULL)) { + nm_log_warn (LOGD_IP6, "(%s): failed to set WWAN IPv6 configuration", + nm_device_get_ip_iface (device)); + } +} + NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *self) { @@ -5909,6 +5935,8 @@ update_ip_config (NMDevice *self, gboolean initial) 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); if (priv->vpn6_config) nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config); @@ -6545,6 +6573,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) g_clear_object (&priv->ac_ip6_config); g_clear_object (&priv->ext_ip6_config); g_clear_object (&priv->vpn6_config); + g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); clear_act_request (self); diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index a4d92998dd..5b4b56ab35 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -20,6 +20,7 @@ #include "config.h" +#include #include #include "nm-device-modem.h" @@ -30,6 +31,7 @@ #include "nm-dbus-manager.h" #include "nm-settings-connection.h" #include "nm-modem-broadband.h" +#include "NetworkManagerUtils.h" G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) @@ -153,7 +155,7 @@ modem_ip4_config_result (NMModem *self, g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE); if (error) { - nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s", + nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); @@ -164,14 +166,89 @@ modem_ip4_config_result (NMModem *self, } } +static void +modem_ip6_config_result (NMModem *self, + NMIP6Config *config, + gboolean do_slaac, + GError *error, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + NMIP6Config *ignored = NULL; + gboolean got_config = !!config; + + g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE); + + if (error) { + nm_log_warn (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + + /* Re-enable IPv6 on the interface */ + nm_device_ipv6_sysctl_set (device, "disable_ipv6", "0"); + + if (config) + nm_device_set_wwan_ip6_config (device, config); + + if (do_slaac == FALSE) { + if (got_config) + nm_device_activate_schedule_ip6_config_result (device); + else { + nm_log_warn (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } + return; + } + + /* Start SLAAC now that we have a link-local address from the modem */ + ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &reason); + g_assert (ignored == NULL); + switch (ret) { + case NM_ACT_STAGE_RETURN_FAILURE: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + break; + case NM_ACT_STAGE_RETURN_STOP: + /* all done */ + nm_device_activate_schedule_ip6_config_result (device); + break; + case NM_ACT_STAGE_RETURN_POSTPONE: + /* let SLAAC run */ + break; + default: + /* Should never get here since we've assured that the IPv6 method + * will either be "auto" or "ignored" when starting IPv6 configuration. + */ + g_assert_not_reached (); + } +} + static void data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); + const char *old = nm_device_get_ip_iface (self); + const char *new = nm_modem_get_data_port (modem); + gboolean changed = FALSE; + + if (new && g_strcmp0 (new, old)) + changed = TRUE; /* We set the IP iface in the device as soon as we know it, so that we * properly ifup it if needed */ - nm_device_set_ip_iface (self, nm_modem_get_data_port (modem)); + nm_device_set_ip_iface (self, new); + + /* Disable IPv6 immediately on the interface since NM handles IPv6 + * internally, and leaving it enabled could allow the kernel's IPv6 + * RA handling code to run before NM is ready. + */ + if (changed) + nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); } static void @@ -395,12 +472,43 @@ act_stage3_ip6_config_start (NMDevice *device, NMIP6Config **out_config, NMDeviceStateReason *reason) { + NMConnection *connection; + const char *method; + + connection = nm_device_get_connection (device); + g_assert (connection); + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + + /* Only Ignore and Auto methods make sense for WWAN */ + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + return NM_ACT_STAGE_RETURN_STOP; + + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) { + nm_log_warn (LOGD_IP6, "(%s): unhandled WWAN IPv6 method '%s'; will fail", + nm_device_get_iface (device), method); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; + } + return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, - device, - NM_DEVICE_CLASS (nm_device_modem_parent_class), + nm_device_get_act_request (device), reason); } +static gboolean +get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + gboolean success; + + g_return_val_if_fail (priv->modem, FALSE); + success = nm_modem_get_iid (priv->modem, out_iid); + if (!success) + success = NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_ip_iface_identifier (device, out_iid); + return success; +} + /*****************************************************************************/ static gboolean @@ -488,8 +596,10 @@ nm_device_modem_new (NMModem *modem) /* If the data port is known, set it as the IP interface immediately */ data_port = nm_modem_get_data_port (modem); - if (data_port) + if (data_port) { nm_device_set_ip_iface (device, data_port); + nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); + } return device; } @@ -511,6 +621,7 @@ set_modem (NMDeviceModem *self, NMModem *modem) g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self); + g_signal_connect (modem, NM_MODEM_IP6_CONFIG_RESULT, G_CALLBACK (modem_ip6_config_result), self); g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self); g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self); g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self); @@ -605,6 +716,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) device_class->set_enabled = set_enabled; device_class->owns_iface = owns_iface; device_class->is_available = is_available; + device_class->get_ip_iface_identifier = get_ip_iface_identifier; device_class->state_changed = device_state_changed; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 62d1b2b0ae..588933b7bc 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -62,6 +62,7 @@ typedef struct { char *ppp_iface; NMModemIPMethod ip4_method; NMModemIPMethod ip6_method; + NMUtilsIPv6IfaceId iid; NMModemState state; NMModemState prev_state; /* revert to this state if enable/disable fails */ char *device_id; @@ -86,6 +87,7 @@ enum { PPP_FAILED, PREPARE_RESULT, IP4_CONFIG_RESULT, + IP6_CONFIG_RESULT, AUTH_REQUESTED, AUTH_RESULT, REMOVED, @@ -228,6 +230,97 @@ nm_modem_get_supported_ip_types (NMModem *self) return NM_MODEM_GET_PRIVATE (self)->ip_types; } +/** + * nm_modem_get_connection_ip_type: + * @self: the #NMModem + * @connection: the #NMConnection to determine IP type to use + * + * Given a modem and a connection, determine which NMModemIpType to use + * when connecting. + * + * Returns: a single %NMModemIpType value + */ +NMModemIPType +nm_modem_get_connection_ip_type (NMModem *self, + NMConnection *connection, + GError **error) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + const char *method; + gboolean ip4 = TRUE, ip6 = TRUE; + gboolean ip4_may_fail = TRUE, ip6_may_fail = TRUE; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4) { + method = nm_setting_ip4_config_get_method (s_ip4); + if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + ip4 = FALSE; + ip4_may_fail = nm_setting_ip4_config_get_may_fail (s_ip4); + } + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6) { + method = nm_setting_ip6_config_get_method (s_ip6); + if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + ip6 = FALSE; + ip6_may_fail = nm_setting_ip6_config_get_may_fail (s_ip6); + } + + if (ip4 && !ip6) { + if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV4)) { + g_set_error_literal (error, + NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Connection requested IPv4 but IPv4 is " + "unsuported by the modem."); + return NM_MODEM_IP_TYPE_UNKNOWN; + } + return NM_MODEM_IP_TYPE_IPV4; + } + + if (ip6 && !ip4) { + if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV6)) { + g_set_error_literal (error, + NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Connection requested IPv6 but IPv6 is " + "unsuported by the modem."); + return NM_MODEM_IP_TYPE_UNKNOWN; + } + return NM_MODEM_IP_TYPE_IPV6; + } + + if (ip4 && ip6) { + /* Modem supports dual-stack */ + if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6) + return NM_MODEM_IP_TYPE_IPV4V6; + + /* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack; + * if one method is marked "may-fail" then use the other. + */ + if (ip6_may_fail) + return NM_MODEM_IP_TYPE_IPV4; + else if (ip4_may_fail) + return NM_MODEM_IP_TYPE_IPV6; + + g_set_error_literal (error, + NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Connection requested both IPv4 and IPv6 " + "but dual-stack addressing is unsupported " + "by the modem."); + return NM_MODEM_IP_TYPE_UNKNOWN; + } + + g_set_error_literal (error, + NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Connection specified no IP configuration!"); + return NM_MODEM_IP_TYPE_UNKNOWN; +} + /*****************************************************************************/ /* IP method PPP */ @@ -246,6 +339,18 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_ } } +static void +set_data_port (NMModem *self, const char *new_data_port) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (g_strcmp0 (priv->data_port, new_data_port) != 0) { + g_free (priv->data_port); + priv->data_port = g_strdup (new_data_port); + g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT); + } +} + static void ppp_ip4_config (NMPPPManager *ppp_manager, const char *iface, @@ -253,7 +358,6 @@ ppp_ip4_config (NMPPPManager *ppp_manager, gpointer user_data) { NMModem *self = NM_MODEM (user_data); - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); guint32 i, num; guint32 bad_dns1 = htonl (0x0A0B0C0D); guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */ @@ -262,9 +366,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, gboolean dns_workaround = FALSE; /* Notify about the new data port to use */ - g_free (priv->ppp_iface); - priv->ppp_iface = g_strdup (iface); - g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT); + set_data_port (self, iface); /* Work around a PPP bug (#1732) which causes many mobile broadband * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers. @@ -305,6 +407,23 @@ ppp_ip4_config (NMPPPManager *ppp_manager, g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, config, NULL); } +static void +ppp_ip6_config (NMPPPManager *ppp_manager, + const char *iface, + const NMUtilsIPv6IfaceId *iid, + NMIP6Config *config, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + + /* Notify about the new data port to use */ + set_data_port (self, iface); + + NM_MODEM_GET_PRIVATE (self)->iid = *iid; + + nm_modem_emit_ip6_config_result (self, config, NULL); +} + static void ppp_stats (NMPPPManager *ppp_manager, guint32 in_bytes, @@ -323,20 +442,27 @@ ppp_stats (NMPPPManager *ppp_manager, } static NMActStageReturn -ppp_stage3_ip4_config_start (NMModem *self, - NMActRequest *req, - NMDeviceStateReason *reason) +ppp_stage3_ip_config_start (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); const char *ppp_name = NULL; GError *error = NULL; NMActStageReturn ret; - guint ip_timeout = 20; + guint ip_timeout = 30; g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + /* If we're already running PPP don't restart it; for example, if both + * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the + * same pppd for both v4 and v6. + */ + if (priv->ppp_manager) + return NM_ACT_STAGE_RETURN_POSTPONE; + if (NM_MODEM_GET_CLASS (self)->get_user_pass) { NMConnection *connection = nm_act_request_get_connection (req); @@ -346,7 +472,7 @@ ppp_stage3_ip4_config_start (NMModem *self, } /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, - * use the default one (20s) */ + * use the default one (30s) */ if (priv->mm_ip_timeout > 0) { nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds", priv->mm_ip_timeout); @@ -361,6 +487,9 @@ ppp_stage3_ip4_config_start (NMModem *self, g_signal_connect (priv->ppp_manager, "ip4-config", G_CALLBACK (ppp_ip4_config), self); + g_signal_connect (priv->ppp_manager, "ip6-config", + G_CALLBACK (ppp_ip6_config), + self); g_signal_connect (priv->ppp_manager, "stats", G_CALLBACK (ppp_stats), self); @@ -405,7 +534,7 @@ nm_modem_stage3_ip4_config_start (NMModem *self, priv = NM_MODEM_GET_PRIVATE (self); switch (priv->ip4_method) { case NM_MODEM_IP_METHOD_PPP: - ret = ppp_stage3_ip4_config_start (self, req, reason); + ret = ppp_stage3_ip_config_start (self, req, reason); break; case NM_MODEM_IP_METHOD_STATIC: ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason); @@ -445,15 +574,82 @@ nm_modem_ip4_pre_commit (NMModem *modem, /*****************************************************************************/ +void +nm_modem_emit_ip6_config_result (NMModem *self, + NMIP6Config *config, + GError *error) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + guint i, num; + gboolean do_slaac = TRUE; + + if (error) { + g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, NULL, FALSE, error); + return; + } + + if (config) { + /* 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); + + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + if (!priv->iid.id) + priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1]; + } else + do_slaac = FALSE; + } + } + g_assert (config || do_slaac); + + g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, config, do_slaac, NULL); +} + +static NMActStageReturn +stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *reason) +{ + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; +} + NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *self, - NMDevice *device, - NMDeviceClass *device_class, + NMActRequest *req, NMDeviceStateReason *reason) { - /* FIXME: We don't support IPv6 on modems quite yet... */ - nm_device_activate_schedule_ip6_config_timeout (device); - return NM_ACT_STAGE_RETURN_POSTPONE; + NMModemPrivate *priv; + NMActStageReturn ret; + + g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + priv = NM_MODEM_GET_PRIVATE (self); + switch (priv->ip6_method) { + case NM_MODEM_IP_METHOD_PPP: + ret = ppp_stage3_ip_config_start (self, req, reason); + break; + case NM_MODEM_IP_METHOD_STATIC: + case NM_MODEM_IP_METHOD_AUTO: + /* Both static and DHCP/Auto retrieve a base IP config from the modem + * which in the static case is the full config, and the DHCP/Auto case + * is just the IPv6LL address to use for SLAAC. + */ + ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason); + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip6_method); + ret = NM_ACT_STAGE_RETURN_FAILURE; + break; + } + + return ret; } /*****************************************************************************/ @@ -779,6 +975,15 @@ nm_modem_owns_port (NMModem *self, const char *iface) return FALSE; } +gboolean +nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid) +{ + g_return_val_if_fail (NM_IS_MODEM (self), FALSE); + + *out_iid = NM_MODEM_GET_PRIVATE (self)->iid; + return TRUE; +} + /*****************************************************************************/ void @@ -978,6 +1183,7 @@ nm_modem_class_init (NMModemClass *klass) object_class->finalize = finalize; klass->act_stage1_prepare = act_stage1_prepare; + klass->stage3_ip6_config_request = stage3_ip6_config_request; klass->deactivate = deactivate; /* Properties */ @@ -1099,6 +1305,27 @@ nm_modem_class_init (NMModemClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER); + /** + * NMModem::ip6-config-result: + * @modem: the #NMModem on which the signal is emitted + * @config: the #NMIP6Config to apply to the modem's data port + * @do_slaac: %TRUE if IPv6 SLAAC should be started + * @error: a #GError if any error occurred during IP configuration + * + * This signal is emitted when IPv6 configuration has completed or failed. + * If @error is set the configuration failed. If @config is set, then + * the details should be applied to the data port before any further + * configuration (like SLAAC) is done. @do_slaac indicates whether SLAAC + * should be started after applying @config to the data port. + */ + signals[IP6_CONFIG_RESULT] = + g_signal_new (NM_MODEM_IP6_CONFIG_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ip6_config_result), + NULL, NULL, NULL, + G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_BOOLEAN, G_TYPE_POINTER); + signals[PREPARE_RESULT] = g_signal_new (NM_MODEM_PREPARE_RESULT, G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index 7fc215c0fa..776d3d7f39 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -55,6 +55,7 @@ G_BEGIN_DECLS #define NM_MODEM_PPP_FAILED "ppp-failed" #define NM_MODEM_PREPARE_RESULT "prepare-result" #define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result" +#define NM_MODEM_IP6_CONFIG_RESULT "ip6-config-result" #define NM_MODEM_AUTH_REQUESTED "auth-requested" #define NM_MODEM_AUTH_RESULT "auth-result" #define NM_MODEM_REMOVED "removed" @@ -147,6 +148,12 @@ typedef struct { NMActRequest *req, NMDeviceStateReason *reason); + /* Request the IP6 config; when the config returns the modem + * subclass should emit the ip6_config_result signal. + */ + NMActStageReturn (*stage3_ip6_config_request) (NMModem *self, + NMDeviceStateReason *reason); + void (*set_mm_enabled) (NMModem *self, gboolean enabled); void (*disconnect) (NMModem *self, gboolean warn); @@ -161,6 +168,10 @@ typedef struct { void (*prepare_result) (NMModem *self, gboolean success, NMDeviceStateReason reason); void (*ip4_config_result) (NMModem *self, NMIP4Config *config, GError *error); + void (*ip6_config_result) (NMModem *self, + NMIP6Config *config, + gboolean do_slaac, + GError *error); void (*auth_requested) (NMModem *self); void (*auth_result) (NMModem *self, GError *error); @@ -179,6 +190,7 @@ const char *nm_modem_get_uid (NMModem *modem); const char *nm_modem_get_control_port (NMModem *modem); const char *nm_modem_get_data_port (NMModem *modem); const char *nm_modem_get_driver (NMModem *modem); +gboolean nm_modem_get_iid (NMModem *modem, NMUtilsIPv6IfaceId *out_iid); gboolean nm_modem_owns_port (NMModem *modem, const char *iface); @@ -207,8 +219,7 @@ NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem, NMDeviceStateReason *reason); NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem, - NMDevice *device, - NMDeviceClass *device_class, + NMActRequest *req, NMDeviceStateReason *reason); void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config); @@ -239,6 +250,15 @@ NMModemIPType nm_modem_get_supported_ip_types (NMModem *self); /* For the modem-manager only */ void nm_modem_emit_removed (NMModem *self); +NMModemIPType nm_modem_get_connection_ip_type (NMModem *self, + NMConnection *connection, + GError **error); + +/* For subclasses */ +void nm_modem_emit_ip6_config_result (NMModem *self, + NMIP6Config *config, + GError *error); + G_END_DECLS #endif /* NM_MODEM_H */ diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver index dc505d1768..46f2e8dc07 100644 --- a/src/devices/wwan/wwan-exports.ver +++ b/src/devices/wwan/wwan-exports.ver @@ -11,6 +11,7 @@ global: nm_modem_get_control_port; nm_modem_get_data_port; nm_modem_get_driver; + nm_modem_get_iid; nm_modem_get_path; nm_modem_get_secrets; nm_modem_get_state; From 0caea7db2cdffc6e1d279084869574faf8649001 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 16 Oct 2013 09:30:43 -0500 Subject: [PATCH 06/11] wwan: add IPv6 support for ModemManager1 (bgo #682623) https://bugzilla.gnome.org/show_bug.cgi?id=682623 --- src/devices/wwan/nm-modem-broadband.c | 210 ++++++++++++++++++++++---- 1 file changed, 178 insertions(+), 32 deletions(-) diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 56bb1c34ef..e019c0428c 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -157,13 +157,29 @@ ask_for_pin (NMModemBroadband *self) NM_SETTING_GSM_PIN); } +static NMModemIPMethod +get_bearer_ip_method (MMBearerIpConfig *config) +{ + MMBearerIpMethod mm_method; + + mm_method = mm_bearer_ip_config_get_method (config); + if (mm_method == MM_BEARER_IP_METHOD_PPP) + return NM_MODEM_IP_METHOD_PPP; + else if (mm_method == MM_BEARER_IP_METHOD_STATIC) + return NM_MODEM_IP_METHOD_STATIC; + else if (mm_method == MM_BEARER_IP_METHOD_DHCP) + return NM_MODEM_IP_METHOD_AUTO; + return NM_MODEM_IP_METHOD_UNKNOWN; +} + static void connect_ready (MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *self) { GError *error = NULL; - NMModemIPMethod ip_method; + NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; + NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; g_clear_object (&self->priv->connect_properties); @@ -196,35 +212,31 @@ connect_ready (MMModemSimple *simple_iface, /* Grab IP configurations */ self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer); - self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer); + if (self->priv->ipv4_config) + ip4_method = get_bearer_ip_method (self->priv->ipv4_config); - switch (mm_bearer_ip_config_get_method (self->priv->ipv4_config)) { - case MM_BEARER_IP_METHOD_PPP: - ip_method = NM_MODEM_IP_METHOD_PPP; - break; - case MM_BEARER_IP_METHOD_STATIC: - ip_method = NM_MODEM_IP_METHOD_STATIC; - break; - case MM_BEARER_IP_METHOD_DHCP: - ip_method = NM_MODEM_IP_METHOD_AUTO; - break; - default: - error = g_error_new (NM_MODEM_ERROR, - NM_MODEM_ERROR_CONNECTION_INVALID, - "invalid IP config"); - nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s", - nm_modem_get_uid (NM_MODEM (self)), - error->message); - g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer); + if (self->priv->ipv6_config) + ip6_method = get_bearer_ip_method (self->priv->ipv6_config); + + if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN && + ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) { + nm_log_warn (LOGD_MB, "(%s) failed to connect modem: invalid bearer IP configuration", + nm_modem_get_uid (NM_MODEM (self))); + + error = g_error_new_literal (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "invalid bearer IP configuration"); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, error); g_error_free (error); g_object_unref (self); return; } - /* IPv4 for now only */ g_object_set (self, NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer), - NM_MODEM_IP4_METHOD, ip_method, + NM_MODEM_IP4_METHOD, ip4_method, + NM_MODEM_IP6_METHOD, ip6_method, NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer), NULL); @@ -250,12 +262,15 @@ create_cdma_connect_properties (NMConnection *connection) } static MMSimpleConnectProperties * -create_gsm_connect_properties (NMConnection *connection) +create_gsm_connect_properties (NMModem *modem, + NMConnection *connection, + GError **error) { NMSettingGsm *setting; NMSettingPPP *s_ppp; MMSimpleConnectProperties *properties; const gchar *str; + NMModemIPType ip_type; setting = nm_connection_get_setting_gsm (connection); properties = mm_simple_connect_properties_new (); @@ -310,6 +325,22 @@ create_gsm_connect_properties (NMConnection *connection) mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth); } + /* Determine IP types to use when connecting */ + ip_type = nm_modem_get_connection_ip_type (modem, connection, error); + if (ip_type == NM_MODEM_IP_TYPE_UNKNOWN) { + g_object_unref (properties); + return NULL; + } + + if (ip_type == NM_MODEM_IP_TYPE_IPV4) + mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4); + else if (ip_type == NM_MODEM_IP_TYPE_IPV6) + mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV6); + else if (ip_type == NM_MODEM_IP_TYPE_IPV4V6) + mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4V6); + else + g_assert_not_reached (); + return properties; } @@ -320,17 +351,29 @@ act_stage1_prepare (NMModem *_self, { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); MMModemCapability caps; + GError *error = NULL; g_clear_object (&self->priv->connect_properties); caps = mm_modem_get_current_capabilities (self->priv->modem_iface); if (MODEM_CAPS_3GPP (caps)) - self->priv->connect_properties = create_gsm_connect_properties (connection); + self->priv->connect_properties = create_gsm_connect_properties (_self, connection, &error); else if (MODEM_CAPS_3GPP2 (caps)) self->priv->connect_properties = create_cdma_connect_properties (connection); else { nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem", nm_modem_get_uid (NM_MODEM (self))); + *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + if (error) { + nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s", + nm_modem_get_uid (NM_MODEM (self)), + nm_connection_get_id (connection), + error->message); + g_clear_error (&error); + *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; return NM_ACT_STAGE_RETURN_FAILURE; } @@ -591,11 +634,10 @@ set_mm_enabled (NMModem *_self, } /*****************************************************************************/ -/* IP method static */ +/* IPv4 method static */ static gboolean -ip_string_to_network_address (const gchar *str, - guint32 *out) +ip4_string_to_num (const gchar *str, guint32 *out) { guint32 addr = 0; gboolean success = FALSE; @@ -610,7 +652,7 @@ ip_string_to_network_address (const gchar *str, } static gboolean -static_stage3_done (NMModemBroadband *self) +static_stage3_ip4_done (NMModemBroadband *self) { GError *error = NULL; NMIP4Config *config = NULL; @@ -629,7 +671,7 @@ static_stage3_done (NMModemBroadband *self) /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address (self->priv->ipv4_config); - if (!ip_string_to_network_address (address_string, &address_network)) { + if (!ip4_string_to_num (address_string, &address_network)) { error = g_error_new (NM_MODEM_ERROR, NM_MODEM_ERROR_CONNECTION_INVALID, "(%s) retrieving IP4 configuration failed: invalid address given '%s'", @@ -640,7 +682,7 @@ static_stage3_done (NMModemBroadband *self) /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway (self->priv->ipv4_config); - ip_string_to_network_address (gw_string, &gw); + ip4_string_to_num (gw_string, &gw); config = nm_ip4_config_new (); @@ -660,7 +702,7 @@ static_stage3_done (NMModemBroadband *self) /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config); for (i = 0; dns[i]; i++) { - if ( ip_string_to_network_address (dns[i], &address_network) + if ( ip4_string_to_num (dns[i], &address_network) && address_network > 0) { nm_ip4_config_add_nameserver (config, address_network); nm_log_info (LOGD_MB, " DNS %s", dns[i]); @@ -682,7 +724,110 @@ static_stage3_ip4_config_start (NMModem *_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_done, self); + g_idle_add ((GSourceFunc) static_stage3_ip4_done, self); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ +/* IPv6 method static */ + +static gboolean +stage3_ip6_done (NMModemBroadband *self) +{ + GError *error = NULL; + NMIP6Config *config = NULL; + const gchar *address_string; + NMPlatformIP6Address address; + NMModemIPMethod ip_method; + const gchar **dns; + guint i; + + g_assert (self->priv->ipv6_config); + + memset (&address, 0, sizeof (address)); + + ip_method = get_bearer_ip_method (self->priv->ipv6_config); + + address_string = mm_bearer_ip_config_get_address (self->priv->ipv6_config); + if (!address_string) { + /* DHCP/SLAAC is allowed to skip addresses; other methods require it */ + if (ip_method != NM_MODEM_IP_METHOD_AUTO) { + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "(%s) retrieving IPv6 configuration failed: no address given", + nm_modem_get_uid (NM_MODEM (self))); + } + goto out; + } + + /* Fail if invalid IP address retrieved */ + if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) { + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", + nm_modem_get_uid (NM_MODEM (self)), + address_string); + goto out; + } + + nm_log_info (LOGD_MB, "(%s): IPv6 base configuration:", + nm_modem_get_uid (NM_MODEM (self))); + + config = nm_ip6_config_new (); + + address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv6_config); + nm_ip6_config_add_address (config, &address); + + nm_log_info (LOGD_MB, " address %s/%d", address_string, address.plen); + + 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))) { + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'", + nm_modem_get_uid (NM_MODEM (self)), + address_string); + goto out; + } + nm_log_info (LOGD_MB, " gateway %s", address_string); + nm_ip6_config_set_gateway (config, &address.address); + } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { + /* Gateway required for the 'static' method */ + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "(%s) retrieving IPv6 configuration failed: missing gateway", + nm_modem_get_uid (NM_MODEM (self))); + goto out; + } + + /* DNS servers */ + dns = mm_bearer_ip_config_get_dns (self->priv->ipv6_config); + for (i = 0; dns[i]; i++) { + struct in6_addr addr; + + if (inet_pton (AF_INET6, dns[i], &addr)) { + nm_ip6_config_add_nameserver (config, &addr); + nm_log_info (LOGD_MB, " DNS %s", dns[i]); + } + } + +out: + nm_modem_emit_ip6_config_result (NM_MODEM (self), config, error); + g_clear_object (&config); + g_clear_error (&error); + return FALSE; +} + +static NMActStageReturn +stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_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); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -984,6 +1129,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass) modem_class->get_capabilities = get_capabilities; modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; + modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->disconnect = disconnect; modem_class->deactivate = deactivate; modem_class->set_mm_enabled = set_mm_enabled; From d29ab97c39a627b52a13f458416b1dd515ffde9e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 26 Mar 2014 08:29:29 -0500 Subject: [PATCH 07/11] wwan: move IP method handling into the modem object Do it in one place (NMModem) so that NMDeviceBt takes advantage of the same logic that NMDeviceModem used to use. --- src/devices/wwan/nm-device-modem.c | 18 ------------- src/devices/wwan/nm-modem.c | 43 +++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 5b4b56ab35..cc08d176d7 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -472,24 +472,6 @@ act_stage3_ip6_config_start (NMDevice *device, NMIP6Config **out_config, NMDeviceStateReason *reason) { - NMConnection *connection; - const char *method; - - connection = nm_device_get_connection (device); - g_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - - /* Only Ignore and Auto methods make sense for WWAN */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) - return NM_ACT_STAGE_RETURN_STOP; - - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) { - nm_log_warn (LOGD_IP6, "(%s): unhandled WWAN IPv6 method '%s'; will fail", - nm_device_get_iface (device), method); - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - return NM_ACT_STAGE_RETURN_FAILURE; - } - return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, nm_device_get_act_request (device), reason); diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 588933b7bc..eab98219d5 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -521,6 +521,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self, { NMModemPrivate *priv; NMActRequest *req; + NMConnection *connection; + const char *method; NMActStageReturn ret; g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); @@ -530,6 +532,21 @@ nm_modem_stage3_ip4_config_start (NMModem *self, req = nm_device_get_act_request (device); g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + + /* Only Disabled and Auto methods make sense for WWAN */ + if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + return NM_ACT_STAGE_RETURN_STOP; + + 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); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; + } priv = NM_MODEM_GET_PRIVATE (self); switch (priv->ip4_method) { @@ -543,8 +560,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self, ret = device_class->act_stage3_ip4_config_start (device, NULL, reason); break; default: - nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip4_method); - ret = NM_ACT_STAGE_RETURN_FAILURE; + nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self)); + ret = NM_ACT_STAGE_RETURN_STOP; break; } @@ -623,6 +640,8 @@ nm_modem_stage3_ip6_config_start (NMModem *self, { NMModemPrivate *priv; NMActStageReturn ret; + NMConnection *connection; + const char *method; g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); @@ -630,6 +649,22 @@ nm_modem_stage3_ip6_config_start (NMModem *self, g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + connection = nm_act_request_get_connection (req); + g_assert (connection); + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + + /* Only Ignore and Auto methods make sense for WWAN */ + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + return NM_ACT_STAGE_RETURN_STOP; + + 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); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; + } + priv = NM_MODEM_GET_PRIVATE (self); switch (priv->ip6_method) { case NM_MODEM_IP_METHOD_PPP: @@ -644,8 +679,8 @@ nm_modem_stage3_ip6_config_start (NMModem *self, ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason); break; default: - nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip6_method); - ret = NM_ACT_STAGE_RETURN_FAILURE; + nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self)); + ret = NM_ACT_STAGE_RETURN_STOP; break; } From b5817dffa096f261bfb01312d8503276d6464191 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Jun 2014 13:27:14 -0500 Subject: [PATCH 08/11] core: remove child devices when a parent's ip_iface becomes known Child devices shouldn't be exposed as real NMDevices (yet) since the configuration and life cycle is controlled by the parent. We already do this if the ip_iface is known when the child device is added, but PPP and other transient interfaces often show up just before we know the parent's ip_iface. --- src/nm-manager.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/nm-manager.c b/src/nm-manager.c index 68b1846dc2..e0bb4dc9fb 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1719,6 +1719,30 @@ recheck_assume_connection (NMDevice *device, gpointer user_data) } } +static void +device_ip_iface_changed (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + const char *ip_iface = nm_device_get_ip_iface (device); + GSList *iter; + + /* Remove NMDevice objects that are actually child devices of others, + * when the other device finally knows its IP interface name. For example, + * remove the PPP interface that's a child of a WWAN device, since it's + * not really a standalone NMDevice. + */ + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { + NMDevice *candidate = NM_DEVICE (iter->data); + + if ( candidate != device + && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0) { + remove_device (self, candidate, FALSE); + break; + } + } +} + /** * add_device: * @self: the #NMManager @@ -1774,6 +1798,10 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) G_CALLBACK (device_removed_cb), self); + g_signal_connect (device, "notify::" NM_DEVICE_IP_IFACE, + G_CALLBACK (device_ip_iface_changed), + self); + if (priv->startup) { g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION, G_CALLBACK (device_has_pending_action_changed), From 20081816e1389aef48d65f574813faaac17f1930 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Jun 2014 15:28:52 -0500 Subject: [PATCH 09/11] wwan: don't disconnect if nothing to disconnect Avoid this error: NetworkManager[25181]: (cdc-wdm1): Failed to connect 'T-Mobile Internet': Connection requested IPv4 but IPv4 is unsuported by the modem. NetworkManager[25181]: (cdc-wdm1): device state change: prepare -> failed (reason 'modem-init-failed') [40 120 28] ** (NetworkManager:25181): CRITICAL **: mm_modem_simple_disconnect: assertion 'MM_IS_MODEM_SIMPLE (self)' failed self->priv->simple_iface is only valid if stage1/prepare actually completes, so don't try to access it if stage1/prepare failed. --- src/devices/wwan/nm-modem-broadband.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index e019c0428c..4ea477d4ac 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -866,11 +866,15 @@ simple_disconnect_ready (MMModemSimple *modem_iface, } static void -disconnect (NMModem *self, +disconnect (NMModem *modem, gboolean warn) { + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); SimpleDisconnectContext *ctx; + if (!self->priv->simple_iface) + return; + ctx = g_slice_new (SimpleDisconnectContext); ctx->self = g_object_ref (self); From 784d2631700528bde2ae85bca1b551c185d9c270 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2014 14:03:42 -0500 Subject: [PATCH 10/11] dhcp: DHCPv6 OtherConf failures should not be fatal OtherConf implies the address has already been delivered via RA, and possibly DNS too, meaning our IP configuration is already good enough. If nothing on the network bothers to reply to our DHCPv6 Information Requests, let's just run with the config we already have instead of tearing down the whole device. --- src/devices/nm-device.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 8078a16bdb..a38506f949 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3155,10 +3155,17 @@ dhcp6_fail (NMDevice *device, gboolean timeout) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); dhcp6_cleanup (device, TRUE, FALSE); - if (timeout || (priv->ip6_state == IP_CONF)) - nm_device_activate_schedule_ip6_config_timeout (device); - else if (priv->ip6_state == IP_FAIL) - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + + if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) { + if (timeout || (priv->ip6_state == IP_CONF)) + nm_device_activate_schedule_ip6_config_timeout (device); + else if (priv->ip6_state == IP_FAIL) + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } else { + /* not a hard failure; just live with the RA info */ + if (priv->ip6_state == IP_CONF) + nm_device_activate_schedule_ip6_config_result (device); + } } static void From 2e0ba9d865dd386ba719f7aaaf72c101c4eb7bfa Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2014 14:07:33 -0500 Subject: [PATCH 11/11] wwan: use a shorter DHCP timeout With WWAN, the DHCP is all done in modem firmware, and never started until we know we have a successful packet connection to the network. Which means the modem firmware already knows the IP details and is ready to provide them. Furthermore, since the DHCP is done on what is essentially a reliable, wired point-to-point link, we don't have to waste time with retransmits for dropped packets either. --- src/devices/wwan/nm-device-modem.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index cc08d176d7..fd1b7f2868 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -591,6 +591,18 @@ 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); +} + static void set_modem (NMDeviceModem *self, NMModem *modem) { @@ -684,6 +696,7 @@ 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->check_connection_compatible = check_connection_compatible; device_class->check_connection_available = check_connection_available;