From 87b2d783b6c732ab9cf3ad8ef0cdaec8d971c74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Tue, 14 Jul 2015 09:16:35 +0200 Subject: [PATCH 1/4] core: accept 'ssids':aay option in RequestScan() dictionary parameter It allows specifying SSIDs that should be probe-scanned, which is useful for APs with hidden SSID, for example. --- introspection/nm-device-wifi.xml | 3 +- src/devices/wifi/nm-device-wifi.c | 66 +++++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/introspection/nm-device-wifi.xml b/introspection/nm-device-wifi.xml index 1cdcadfd09..27f3eb9066 100644 --- a/introspection/nm-device-wifi.xml +++ b/introspection/nm-device-wifi.xml @@ -33,7 +33,8 @@ - Options of scan (currently unused argument). + Options of scan. + Currently 'ssids' option with value of "aay" type is supported. diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index fb4b5fa40c..807f7b9cca 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -49,6 +49,7 @@ #include "nm-enum-types.h" #include "nm-wifi-enum-types.h" #include "nm-connection-provider.h" +#include "nm-core-internal.h" #include "nmdbus-device-wifi.h" @@ -160,7 +161,7 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); -static gboolean request_wireless_scan (gpointer user_data); +static void request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options); static void emit_ap_added_removed (NMDeviceWifi *self, guint signum, @@ -534,7 +535,7 @@ deactivate (NMDevice *device) /* Ensure we trigger a scan after deactivating a Hotspot */ if (old_mode == NM_802_11_MODE_AP) { cancel_pending_scan (self); - request_wireless_scan (self); + request_wireless_scan (self, NULL); } } @@ -1051,6 +1052,7 @@ request_scan_cb (NMDevice *device, gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); + gs_unref_variant GVariant *new_scan_options = user_data; if (error) { g_dbus_method_invocation_return_gerror (context, error); @@ -1066,7 +1068,7 @@ request_scan_cb (NMDevice *device, } cancel_pending_scan (self); - request_wireless_scan (self); + request_wireless_scan (self, new_scan_options); g_dbus_method_invocation_return_value (context, NULL); } @@ -1115,7 +1117,7 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, request_scan_cb, - NULL); + options ? g_variant_ref (options) : NULL); } static gboolean @@ -1276,23 +1278,58 @@ build_hidden_probe_list (NMDeviceWifi *self) return ssids; } -static gboolean -request_wireless_scan (gpointer user_data) +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, GVariant *scan_options) { - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); gboolean backoff = FALSE; GPtrArray *ssids = NULL; if (priv->requested_scan) { /* There's already a scan in progress */ - return FALSE; + return; } if (check_scanning_allowed (self)) { _LOGD (LOGD_WIFI_SCAN, "scanning requested"); - ssids = build_hidden_probe_list (self); + 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_SCAN, "ignoring invalid 'ssids' scan option"); + g_variant_unref (val); + } + } + if (!ssids) + ssids = build_hidden_probe_list (self); if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) { if (ssids) { @@ -1327,9 +1364,14 @@ request_wireless_scan (gpointer user_data) priv->pending_scan_id = 0; schedule_scan (self, backoff); - return FALSE; } +static gboolean +request_wireless_scan_periodic (gpointer user_data) +{ + request_wireless_scan (user_data, NULL); + return FALSE; +} /* * schedule_scan @@ -1357,7 +1399,7 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff) factor = 1; priv->pending_scan_id = g_timeout_add_seconds (next_scan, - request_wireless_scan, + request_wireless_scan_periodic, self); priv->scheduled_scan_time = now + priv->scan_interval; @@ -2771,7 +2813,7 @@ device_state_changed (NMDevice *device, /* Kick off a scan to get latest results */ priv->scan_interval = SCAN_INTERVAL_MIN; cancel_pending_scan (self); - request_wireless_scan (self); + request_wireless_scan (self, NULL); break; default: break; From 7691fe575377b5383d4cd6dd62e01ed4f6cafa93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Tue, 14 Jul 2015 09:46:55 +0200 Subject: [PATCH 2/4] libnm: add new functions allowing passing options to RequestScan() D-Bus call nm_device_wifi_request_scan_options() nm_device_wifi_request_scan_options_async() --- libnm/libnm.ver | 2 + libnm/nm-device-wifi.c | 154 ++++++++++++++++++++++++++++++++++------- libnm/nm-device-wifi.h | 12 +++- 3 files changed, 143 insertions(+), 25 deletions(-) diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 8d8f537e80..725b3e5e8a 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -851,6 +851,8 @@ global: nm_access_point_get_last_seen; nm_device_get_metered; nm_device_get_nm_plugin_missing; + nm_device_wifi_request_scan_options; + nm_device_wifi_request_scan_options_async; nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; diff --git a/libnm/nm-device-wifi.c b/libnm/nm-device-wifi.c index 644a89004f..383f43c60b 100644 --- a/libnm/nm-device-wifi.c +++ b/libnm/nm-device-wifi.c @@ -269,6 +269,52 @@ nm_device_wifi_get_access_point_by_path (NMDeviceWifi *device, return ap; } +static GVariant * +prepare_scan_options (GVariant *options) +{ + + GVariant *variant; + GVariantIter iter; + GVariantBuilder builder; + char *key; + GVariant *value; + + if (!options) + variant = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); + else { + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_iter_init (&iter, options); + while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) + { + // FIXME: verify options here? + g_variant_builder_add (&builder, "{sv}", key, value); + } + variant = g_variant_builder_end (&builder); + } + return variant; +} + +static gboolean +_device_wifi_request_scan (NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GError **error) +{ + gboolean ret; + GVariant *variant; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), FALSE); + + variant = prepare_scan_options (options); + + ret = nmdbus_device_wifi_call_request_scan_sync (NM_DEVICE_WIFI_GET_PRIVATE (device)->proxy, + variant, + cancellable, error); + if (error && *error) + g_dbus_error_strip_remote_error (*error); + return ret; +} + /** * nm_device_wifi_request_scan: * @device: a #NMDeviceWifi @@ -287,17 +333,36 @@ nm_device_wifi_request_scan (NMDeviceWifi *device, GCancellable *cancellable, GError **error) { - gboolean ret; + return _device_wifi_request_scan (device, NULL, cancellable, error); +} - g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), FALSE); - - ret = nmdbus_device_wifi_call_request_scan_sync (NM_DEVICE_WIFI_GET_PRIVATE (device)->proxy, - g_variant_new_array (G_VARIANT_TYPE ("{sv}"), - NULL, 0), - cancellable, error); - if (error && *error) - g_dbus_error_strip_remote_error (*error); - return ret; +/** + * nm_device_wifi_request_scan_options: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Request NM to scan for access points on @device. Note that the function + * returns immediately after requesting the scan, and it may take some time + * after that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid otions inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be + * set. + * + * Since: 1.2 + **/ +gboolean +nm_device_wifi_request_scan_options (NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GError **error) +{ + return _device_wifi_request_scan (device, options, cancellable, error); } static void @@ -324,19 +389,9 @@ request_scan_cb (GObject *source, g_slice_free (RequestScanInfo, info); } -/** - * nm_device_wifi_request_scan_async: - * @device: a #NMDeviceWifi - * @cancellable: a #GCancellable, or %NULL - * @callback: callback to be called when the scan has been requested - * @user_data: caller-specific data passed to @callback - * - * Request NM to scan for access points on @device. Note that @callback will be - * called immediately after requesting the scan, and it may take some time after - * that for the scan to complete. - **/ -void -nm_device_wifi_request_scan_async (NMDeviceWifi *device, +static void +_device_wifi_request_scan_async (NMDeviceWifi *device, + GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -344,6 +399,7 @@ nm_device_wifi_request_scan_async (NMDeviceWifi *device, NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); RequestScanInfo *info; GSimpleAsyncResult *simple; + GVariant *variant; g_return_if_fail (NM_IS_DEVICE_WIFI (device)); @@ -362,12 +418,62 @@ nm_device_wifi_request_scan_async (NMDeviceWifi *device, info->device = device; info->simple = simple; + variant = prepare_scan_options (options); + priv->scan_info = info; nmdbus_device_wifi_call_request_scan (NM_DEVICE_WIFI_GET_PRIVATE (device)->proxy, - g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0), + variant, cancellable, request_scan_cb, info); } +/** + * nm_device_wifi_request_scan_async: + * @device: a #NMDeviceWifi + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + **/ +void +nm_device_wifi_request_scan_async (NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _device_wifi_request_scan_async (device, NULL, cancellable, callback, user_data); +} + +/** + * nm_device_wifi_request_scan_options_async: + * @device: a #NMDeviceWifi + * @options: dictionary with options for RequestScan(), or %NULL + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the scan has been requested + * @user_data: caller-specific data passed to @callback + * + * Request NM to scan for access points on @device. Note that @callback will be + * called immediately after requesting the scan, and it may take some time after + * that for the scan to complete. + * This is the same as @nm_device_wifi_request_scan_async except it accepts @options + * for the scanning. The argument is the dictionary passed to RequestScan() + * D-Bus call. Valid otions inside the dictionary are: + * 'ssids' => array of SSIDs (saay) + * + * Since: 1.2 + **/ +void +nm_device_wifi_request_scan_options_async (NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + _device_wifi_request_scan_async (device, options, cancellable, callback, user_data); +} + /** * nm_device_wifi_request_scan_finish: * @device: a #NMDeviceWifi diff --git a/libnm/nm-device-wifi.h b/libnm/nm-device-wifi.h index 24339fd3d5..b5974ba2c2 100644 --- a/libnm/nm-device-wifi.h +++ b/libnm/nm-device-wifi.h @@ -77,11 +77,21 @@ const GPtrArray * nm_device_wifi_get_access_points (NMDeviceWifi * gboolean nm_device_wifi_request_scan (NMDeviceWifi *device, GCancellable *cancellable, GError **error); - +NM_AVAILABLE_IN_1_2 +gboolean nm_device_wifi_request_scan_options (NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GError **error); void nm_device_wifi_request_scan_async (NMDeviceWifi *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +NM_AVAILABLE_IN_1_2 +void nm_device_wifi_request_scan_options_async (NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); gboolean nm_device_wifi_request_scan_finish (NMDeviceWifi *device, GAsyncResult *result, GError **error); From e247567d879b314598efc2fe46f2bc8cd309250d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Tue, 14 Jul 2015 09:50:56 +0200 Subject: [PATCH 3/4] cli: add 'ssid' parameter for 'nmcli device wifi rescan' 'ssid' can repeat when more SSIDs should be scanned, e.g. $ nmcli dev wifi rescan ssid "hidden cafe" ssid AP12 ssid "my home Wi-Fi" Bash completion fixed by thaller@redhat.com --- clients/cli/devices.c | 62 ++++++++++++++++++++++++++++++------ clients/cli/nmcli-completion | 9 ++++-- man/nmcli.1.in | 10 ++++-- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index c10febdd95..c6afaa9078 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -265,7 +265,7 @@ usage (void) " wifi [list [ifname ] [bssid ]]\n\n" " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" " [bssid ] [name ] [private yes|no]\n\n" - " wifi rescan [[ifname] ]\n\n" + " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -356,12 +356,14 @@ usage_device_wifi (void) "only open, WEP and WPA-PSK networks are supported at the moment. It is also\n" "assumed that IP configuration is obtained via DHCP.\n" "\n" - "ARGUMENTS := rescan [[ifname] ]\n" + "ARGUMENTS := rescan [ifname ] [[ssid ] ...]\n" "\n" "Request that NetworkManager immediately re-scan for available access points.\n" "NetworkManager scans Wi-Fi networks periodically, but in some cases it might\n" - "be useful to start scanning manually. Note that this command does not show\n" - "the APs, use 'nmcli device wifi list' for that.\n\n")); + "be useful to start scanning manually. 'ssid' allows scanning for a specific\n" + "SSID, which is useful for APs with hidden SSIDs. More 'ssid' parameters can be\n" + "given. Note that this command does not show the APs,\n" + "use 'nmcli device wifi list' for that.\n\n")); } /* quit main loop */ @@ -2510,21 +2512,44 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) { NMDevice *device; const char *ifname = NULL; + GPtrArray *ssids; const GPtrArray *devices; int devices_idx; + GVariantBuilder builder, array_builder; + GVariant *options; + const char *ssid; + int i; nmc->should_wait = TRUE; + ssids = g_ptr_array_new (); + /* Get the parameters */ - if (argc > 0) { + while (argc > 0) { if (strcmp (*argv, "ifname") == 0) { + if (ifname) { + g_string_printf (nmc->return_text, _("Error: '%s' cannot repeat."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - } - ifname = *argv; + ifname = *argv; + } else if (strcmp (*argv, "ssid") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + g_ptr_array_add (ssids, *argv); + } else + g_printerr (_("Unknown parameter: %s\n"), *argv); + + argc--; + argv++; } /* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */ @@ -2541,12 +2566,31 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) goto error; } - nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (device), NULL, - request_rescan_cb, nmc); + if (ssids->len) { + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aay")); + + for (i = 0; i < ssids->len; i++) { + ssid = g_ptr_array_index (ssids, i); + g_variant_builder_add (&array_builder, "@ay", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, ssid, strlen (ssid), 1)); + } + + g_variant_builder_add (&builder, "{sv}", "ssids", g_variant_builder_end (&array_builder)); + options = g_variant_builder_end (&builder); + + nm_device_wifi_request_scan_options_async (NM_DEVICE_WIFI (device), options, + NULL, request_rescan_cb, nmc); + } else + nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (device), + NULL, request_rescan_cb, nmc); + + g_ptr_array_free (ssids, FALSE); return nmc->return_value; error: nmc->should_wait = FALSE; + g_ptr_array_free (ssids, FALSE); return nmc->return_value; } diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 4a55f3dbc6..190d0f3e32 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -579,7 +579,9 @@ _nmcli_compl_ARGS() # remove the options already seen. for i in ${!OPTIONS[*]}; do if [[ "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then - unset OPTIONS[$i] + if ! _nmcli_array_has_value OPTIONS_REPEATABLE "${OPTIONS[$i]}" ; then + unset OPTIONS[$i] + fi fi done for i in ${!OPTIONS_MANDATORY[*]}; do @@ -737,7 +739,7 @@ _nmcli() cur='' fi - local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP OPTIONS_SEP + local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP OPTIONS_SEP OPTIONS_REPEATABLE local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST local COMMAND_CONNECTION_ACTIVE="" @@ -1293,7 +1295,8 @@ _nmcli() ;; r|re|res|resc|resca|rescan) _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname) + OPTIONS_REPEATABLE=(ssid) + OPTIONS=(ifname ssid) _nmcli_compl_ARGS ;; esac diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 7ab3ef8cda..a2beba86aa 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -21,7 +21,7 @@ .\" .\" Copyright 2010 - 2015 Red Hat, Inc. .\" -.TH NMCLI "1" "19 February 2015" +.TH NMCLI "1" "12 August 2015" .SH NAME nmcli \- command\(hyline tool for controlling NetworkManager @@ -832,11 +832,15 @@ Available options are: Otherwise the connection is system\(hywide, which is the default. .RE .TP -.B wifi rescan [[ifname] ] +.B wifi rescan [ifname ] [[ssid ] ...] .br Request that \fINetworkManager\fP immediately re-scan for available access points. NetworkManager scans Wi\(hyFi networks periodically, but in some cases it can be -useful to start scanning manually (e.g. after resuming the computer). +useful to start scanning manually (e.g. after resuming the computer). By using +\fIssid\fP, it is possible to scan for a specific SSID, which is useful for APs +with hidden SSIDs. You can provide multiple \fIssid\fP parameters in order to +scan more SSIDs. +.br This command does not show the APs, use 'nmcli device wifi list' for that. .TP From 5955a66e0904403ff93e75deaaff173d95b6cac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 10 Jul 2015 13:16:21 +0200 Subject: [PATCH 4/4] cli: fix connecting to a hidden SSID with 'nmcli dev wifi connect' (bgo #752173) We have to set 802-11-wireless.hidden=true to be able to connect to hidden SSIDs. nmcli user indicates hidden SSID with 'hidden yes' parameter. https://bugzilla.gnome.org/show_bug.cgi?id=752173 --- clients/cli/devices.c | 79 ++++++++++++++++++++++++++++++------ clients/cli/nmcli-completion | 3 +- man/nmcli.1.in | 6 ++- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index c6afaa9078..81609d1c9d 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -264,7 +264,7 @@ usage (void) " delete ...\n\n" " wifi [list [ifname ] [bssid ]]\n\n" " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" - " [bssid ] [name ] [private yes|no]\n\n" + " [bssid ] [name ] [private yes|no] [hidden yes|no]\n\n" " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -345,7 +345,7 @@ usage_device_wifi (void) "used to list APs for a particular interface, or with a specific BSSID.\n" "\n" "ARGUMENTS := connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" - " [bssid ] [name ] [private yes|no]\n" + " [bssid ] [name ] [private yes|no] [hidden yes|no]\n" "\n" "Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n" "a new connection and then activates it on a device. This is a command-line\n" @@ -2230,9 +2230,9 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) { NMDevice *device = NULL; NMAccessPoint *ap = NULL; - NM80211ApFlags ap_flags; - NM80211ApSecurityFlags ap_wpa_flags; - NM80211ApSecurityFlags ap_rsn_flags; + NM80211ApFlags ap_flags = NM_802_11_AP_FLAGS_NONE; + NM80211ApSecurityFlags ap_wpa_flags = NM_802_11_AP_SEC_NONE; + NM80211ApSecurityFlags ap_rsn_flags = NM_802_11_AP_SEC_NONE; NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; @@ -2244,6 +2244,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) const char *password = NULL; const char *con_name = NULL; gboolean private = FALSE; + gboolean hidden = FALSE; gboolean wep_passphrase = FALSE; GByteArray *bssid1_arr = NULL; GByteArray *bssid2_arr = NULL; @@ -2343,6 +2344,19 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) g_clear_error (&err_tmp); goto error; } + } else if (strcmp (*argv, "hidden") == 0) { + GError *err_tmp = NULL; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (!nmc_string_to_bool (*argv, &hidden, &err_tmp)) { + g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&err_tmp); + goto error; + } } else { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2379,14 +2393,41 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) goto error; } + /* For hidden SSID first scan it so that NM learns about the AP */ + if (hidden) { + GVariantBuilder builder, array_builder; + GVariant *options; + GError *scan_err = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aay")); + g_variant_builder_add (&array_builder, "@ay", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, param_user, strlen (param_user), 1)); + g_variant_builder_add (&builder, "{sv}", "ssids", g_variant_builder_end (&array_builder)); + options = g_variant_builder_end (&builder); + + nm_device_wifi_request_scan_options (NM_DEVICE_WIFI (device), options, NULL, &scan_err); + if (scan_err) { + g_string_printf (nmc->return_text, _("Error: Failed to scan hidden SSID: %s."), + scan_err->message); + g_clear_error (&scan_err); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + } + /* Find an AP to connect to */ ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); if (!ap && !ifname) { - /* AP not found. ifname was not specified, so try finding the AP on another device. */ - while ((device = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { - ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); - if (ap) + NMDevice *dev; + + /* AP not found, ifname was not specified, so try finding the AP on another device. */ + while ((dev = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { + ap = find_ap_on_device (dev, bssid1_arr, bssid1_arr ? NULL : param_user); + if (ap) { + device = dev; break; + } } } @@ -2401,7 +2442,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) /* If there are some connection data from user, create a connection and * fill them into proper settings. */ - if (con_name || private || bssid2_arr || password) + if (con_name || private || bssid2_arr || password || hidden) connection = nm_simple_connection_new (); if (con_name || private) { @@ -2416,12 +2457,24 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (private) nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL); } - if (bssid2_arr) { + if (bssid2_arr || hidden) { s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - /* 'bssid' parameter is used to restrict the conenction only to the BSSID */ - g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL); + /* 'bssid' parameter is used to restrict the connection only to the BSSID */ + if (bssid2_arr) + g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL); + + /* 'hidden' parameter is used to indicate that SSID is not broadcasted */ + if (hidden) { + GBytes *ssid = g_bytes_new (param_user, strlen (param_user)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_HIDDEN, hidden, + NULL); + g_bytes_unref (ssid); + } } /* handle password */ diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 190d0f3e32..b58efde36d 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -495,6 +495,7 @@ _nmcli_compl_ARGS() stp| \ hairpin| \ save| \ + hidden| \ private) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "yes no" @@ -1289,7 +1290,7 @@ _nmcli() fi else _nmcli_array_delete_at words 0 3 - local OPTIONS=(password wep-key-type ifname bssid name private) + local OPTIONS=(password wep-key-type ifname bssid name private hidden) _nmcli_compl_ARGS fi ;; diff --git a/man/nmcli.1.in b/man/nmcli.1.in index a2beba86aa..57848c1f07 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -802,7 +802,8 @@ List available Wi\(hyFi access points. The \fIifname\fP and \fIbssid\fP options can be used to list APs for a particular interface or with a specific BSSID, respectively. .TP -.B wifi connect <(B)SSID> [password ] [wep\-key\-type key|phrase] [ifname ] [bssid ] [name ] [private yes|no] +.B wifi connect <(B)SSID> [password ] [wep\-key\-type key|phrase] [ifname ] [bssid ] [name ] +.B [private yes|no] [hidden yes|no] .br Connect to a Wi\(hyFi network specified by SSID or BSSID. The command creates a new connection and then activates it on a device. This is a command\(hyline counterpart @@ -830,6 +831,9 @@ Available options are: .IP \fIprivate\fP 13 \(en if set to \fByes\fP, the connection will only be visible to the user who created it. Otherwise the connection is system\(hywide, which is the default. +.IP \fIhidden\fP 13 +\(en set to \fByes\fP when connecting for the first time to an AP not broadcasting its SSID. +Otherwise the SSID would not be found and the connection attempt would fail. .RE .TP .B wifi rescan [ifname ] [[ssid ] ...]