From cd217db21cf166537e38ff971f518ac3f3905288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Mon, 5 Oct 2015 15:12:14 +0200 Subject: [PATCH 1/5] cli: move unique_connection_name() to common.c as nmc_unique_connection_name() --- clients/cli/common.c | 23 +++++++++++++++++++++++ clients/cli/common.h | 3 +++ clients/cli/connections.c | 30 +++--------------------------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index b988679ff9..8317764e3a 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -1011,6 +1011,29 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, } } +char * +nmc_unique_connection_name (const GPtrArray *connections, const char *try_name) +{ + NMConnection *connection; + const char *name; + char *new_name; + unsigned int num = 1; + int i = 0; + + new_name = g_strdup (try_name); + while (i < connections->len) { + connection = NM_CONNECTION (connections->pdata[i]); + + name = nm_connection_get_id (connection); + if (g_strcmp0 (new_name, name) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s-%d", try_name, num++); + i = 0; + } else + i++; + } + return new_name; +} /** * nmc_cleanup_readline: diff --git a/clients/cli/common.h b/clients/cli/common.h index bb3f44e851..c7acb653da 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -57,6 +57,9 @@ void nmc_secrets_requested (NMSecretAgentSimple *agent, GPtrArray *secrets, gpointer user_data); +char *nmc_unique_connection_name (const GPtrArray *connections, + const char *try_name); + void nmc_cleanup_readline (void); char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2); char *nmc_rl_gen_func_basic (const char *text, int state, const char **words); diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 94d0ff73c2..cb95694b07 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -5810,30 +5810,6 @@ cleanup_bridge_slave: return TRUE; } -static char * -unique_connection_name (const GPtrArray *connections, const char *try_name) -{ - NMConnection *connection; - const char *name; - char *new_name; - unsigned int num = 1; - int i = 0; - - new_name = g_strdup (try_name); - while (i < connections->len) { - connection = NM_CONNECTION (connections->pdata[i]); - - name = nm_connection_get_id (connection); - if (g_strcmp0 (new_name, name) == 0) { - g_free (new_name); - new_name = g_strdup_printf ("%s-%d", try_name, num++); - i = 0; - } else - i++; - } - return new_name; -} - typedef struct { NmCli *nmc; char *con_name; @@ -6180,7 +6156,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv) char *try_name = ifname ? g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); - default_name = unique_connection_name (nmc->connections, try_name); + default_name = nmc_unique_connection_name (nmc->connections, try_name); g_free (try_name); } @@ -9015,8 +8991,8 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) if (con_name) default_name = g_strdup (con_name); else - default_name = unique_connection_name (nmc->connections, - get_name_alias (connection_type, nmc_valid_connection_types)); + default_name = nmc_unique_connection_name (nmc->connections, + get_name_alias (connection_type, nmc_valid_connection_types)); g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, From d6427d719839ebcdd104c1ada0d4c8d972f0efff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 2 Oct 2015 22:46:53 +0200 Subject: [PATCH 2/5] cli: add 'nmcli device wifi hotspot' command Synopsis: nmcli device wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ] --- clients/cli/devices.c | 331 ++++++++++++++++++++++++++++++++++- clients/cli/nmcli-completion | 13 +- man/nmcli.1.in | 22 +++ 3 files changed, 360 insertions(+), 6 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 5dce08c220..1fde747dfa 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -267,6 +267,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] [hidden yes|no]\n\n" + " wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ]\n\n" " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -371,6 +372,18 @@ 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 := wifi hotspot [ifname ] [con-name ] [ssid ]\n" + " [band a|bg] [channel ]\n" + "\n" + "Create a Wi-Fi hotspot. Use 'connection down' or 'device disconnect'\n" + "to stop the hotspot.\n" + "Parameters of the hotspot can be influenced by the optional parameters:\n" + "ifname - Wi-Fi device to use\n" + "con-name - name of the created hotspot connection profile\n" + "ssid - SSID of the hotspot\n" + "band - Wi-Fi band to use\n" + "channel - Wi-Fi channel to use\n" + "\n" "ARGUMENTS := rescan [ifname ] [[ssid ] ...]\n" "\n" "Request that NetworkManager immediately re-scan for available access points.\n" @@ -1349,6 +1362,7 @@ connected_state_cb (NMDevice *device, NMActiveConnection *active) typedef struct { NmCli *nmc; NMDevice *device; + gboolean hotspot; } AddAndActivateInfo; static void @@ -1366,8 +1380,12 @@ add_and_activate_cb (GObject *client, active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error); if (error) { - g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), - error->message); + if (info->hotspot) + g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"), + error->message); + else + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), + error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); @@ -1375,7 +1393,10 @@ add_and_activate_cb (GObject *client, state = nm_active_connection_get_state (active); if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { - g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); + if (info->hotspot) + g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot")); + else + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; g_object_unref (active); quit (); @@ -1386,8 +1407,12 @@ add_and_activate_cb (GObject *client, if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"), - nm_active_connection_get_uuid (active), nm_device_get_iface (device)); + if (info->hotspot) + g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"), + nm_active_connection_get_uuid (active), nm_device_get_iface (device)); + else + g_print (_("Hotspot '%s' activated on device '%s'\n"), + nm_active_connection_get_id (active), nm_device_get_iface (device)); } g_object_unref (active); quit (); @@ -1566,6 +1591,7 @@ do_device_connect (NmCli *nmc, int argc, char **argv) info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; info->device = device; + info->hotspot = FALSE; nm_client_activate_connection_async (nmc->client, NULL, /* let NM find a connection automatically */ @@ -2659,6 +2685,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; info->device = device; + info->hotspot = FALSE; nm_client_add_and_activate_connection_async (nmc->client, connection, @@ -2679,6 +2706,298 @@ error: return nmc->return_value; } +static GBytes * +generate_ssid_for_hotspot (const char *ssid) +{ + GBytes *ssid_bytes; + char *hotspot_ssid = NULL; + + if (!ssid) { + hotspot_ssid = g_strdup_printf ("Hotspot-%s", g_get_host_name ()); + if (strlen (hotspot_ssid) > 32) + hotspot_ssid[32] = '\0'; + ssid = hotspot_ssid; + } + ssid_bytes = g_bytes_new (ssid, strlen (ssid)); + g_free (hotspot_ssid); + return ssid_bytes; +} + +#define WPA_PASSKEY_SIZE 8 +static void +generate_wpa_key (char *key, size_t len) +{ + guint i; + + g_return_if_fail (key); + g_return_if_fail (len > WPA_PASSKEY_SIZE); + + /* generate a 8-chars ASCII WPA key */ + for (i = 0; i < WPA_PASSKEY_SIZE; i++) { + int c; + c = g_random_int_range (33, 126); + /* too many non alphanumeric characters are hard to remember for humans */ + while (!g_ascii_isalnum (c)) + c = g_random_int_range (33, 126); + + key[i] = (gchar) c; + } + key[WPA_PASSKEY_SIZE] = '\0'; +} + +static void +generate_wep_key (char *key, size_t len) +{ + int i; + const char *hexdigits = "0123456789abcdef"; + + g_return_if_fail (key); + g_return_if_fail (len > 10); + + /* generate a 10-digit hex WEP key */ + for (i = 0; i < 10; i++) { + int digit; + digit = g_random_int_range (0, 16); + key[i] = hexdigits[digit]; + } + key[10] = '\0'; +} + +static void +set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, + const char *wifi_mode, + NMDeviceWifiCapabilities caps) +{ + char key[11]; + const char *key_mgmt; + + if (g_strcmp0 (wifi_mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { + if (caps & NM_WIFI_DEVICE_CAP_RSN) { + nm_setting_wireless_security_add_proto (s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp"); + nm_setting_wireless_security_add_group (s_wsec, "ccmp"); + generate_wpa_key (key, sizeof (key)); + key_mgmt = "wpa-psk"; + } else if (caps & NM_WIFI_DEVICE_CAP_WPA) { + nm_setting_wireless_security_add_proto (s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + generate_wpa_key (key, sizeof (key)); + key_mgmt = "wpa-psk"; + } else { + generate_wep_key (key, sizeof (key)); + key_mgmt = "none"; + } + } else { + generate_wep_key (key, sizeof (key)); + key_mgmt = "none"; + } + + if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, + NM_SETTING_WIRELESS_SECURITY_PSK, key, + NULL); + } else { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, key, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, + NULL); + } +} + +static NMCResultCode +do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) +{ + AddAndActivateInfo *info; + const char *ifname = NULL; + const char *con_name = NULL; + char *default_name = NULL; + const char *ssid = NULL; + const char *wifi_mode; + const char *band = NULL; + const char *channel = NULL; + unsigned long channel_int; + NMDevice *device = NULL; + int devices_idx; + const GPtrArray *devices; + NMDeviceWifiCapabilities caps; + NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig *s_ip4, *s_ip6; + GBytes *ssid_bytes; + + /* Set default timeout waiting for operation completion. */ + if (nmc->timeout == -1) + nmc->timeout = 60; + + while (argc > 0) { + if (strcmp (*argv, "ifname") == 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; + } + ifname = *argv; + } else if (strcmp (*argv, "con-name") == 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; + } + con_name = *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; + } + ssid = *argv; + if (strlen (ssid) > 32) { + g_string_printf (nmc->return_text, _("Error: ssid is too long.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "band") == 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; + } + band = *argv; + if (strcmp (band, "a") && strcmp (band, "bg")) { + g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."), + band); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "channel") == 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; + } + channel = *argv; + } else { + g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + argc--; + argv++; + } + + /* Verify band and channel parameters */ + if (!channel) { + if (g_strcmp0 (band, "bg") == 0) + channel = "1"; + if (g_strcmp0 (band, "a") == 0) + channel = "7"; + } + if (channel) { + if (!band) { + g_string_printf (nmc->return_text, _("Error: channel requires band too.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if ( !nmc_string_to_uint (channel, TRUE, 1, 5825, &channel_int) + || !nm_utils_wifi_is_channel_valid (channel_int, band)) { + g_string_printf (nmc->return_text, _("Error: channel '%s' not valid for band '%s'."), + channel, band); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + /* Find Wi-Fi device. When no ifname is provided, the first Wi-Fi is used. */ + devices = nm_client_get_devices (nmc->client); + devices_idx = 0; + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + + if (!device) { + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + else + g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* Check device supported mode */ + caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device)); + if (caps & NM_WIFI_DEVICE_CAP_AP) + wifi_mode = NM_SETTING_WIRELESS_MODE_AP; + else if (caps & NM_WIFI_DEVICE_CAP_ADHOC) + wifi_mode = NM_SETTING_WIRELESS_MODE_ADHOC; + else { + g_string_printf (nmc->return_text, _("Error: Device '%s' supports neither AP nor Ad-Hoc mode."), + nm_device_get_iface (device)); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* Create a connection with appropriate parameters */ + connection = nm_simple_connection_new (); + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + if (!con_name) + con_name = default_name = nmc_unique_connection_name (nm_client_get_connections (nmc->client), "Hotspot"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, con_name, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NULL); + g_free (default_name); + + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + ssid_bytes = generate_ssid_for_hotspot (ssid); + g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, wifi_mode, + NM_SETTING_WIRELESS_SSID, ssid_bytes, + NULL); + g_bytes_unref (ssid_bytes); + if (channel) + g_object_set (s_wifi, + NM_SETTING_WIRELESS_CHANNEL, (guint32) channel_int, + NM_SETTING_WIRELESS_BAND, band, + NULL); + + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* Activate the connection now */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + info = g_malloc0 (sizeof (AddAndActivateInfo)); + info->nmc = nmc; + info->device = device; + info->hotspot = TRUE; + + nm_client_add_and_activate_connection_async (nmc->client, + connection, + device, + NULL, + NULL, + add_and_activate_cb, + info); + +error: + return nmc->return_value; +} + static void request_rescan_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -2791,6 +3110,8 @@ do_device_wifi (NmCli *nmc, int argc, char **argv) nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); } else if (matches (*argv, "connect") == 0) { nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1); + } else if (matches (*argv, "hotspot") == 0) { + nmc->return_value = do_device_wifi_hotspot (nmc, argc-1, argv+1); } else if (matches (*argv, "rescan") == 0) { nmc->return_value = do_device_wifi_rescan (nmc, argc-1, argv+1); } else { diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 6d9b6ae1cb..fe589f660b 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -589,6 +589,12 @@ _nmcli_compl_ARGS() return 0 fi ;; + band) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "a bg" + return 0 + fi + ;; *) return 1 ;; @@ -1317,7 +1323,7 @@ _nmcli() ;; w|wi|wif|wifi) if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" list connect rescan + _nmcli_compl_COMMAND "${words[2]}" list connect hotspot rescan else case "${words[2]}" in l|li|lis|list) @@ -1338,6 +1344,11 @@ _nmcli() _nmcli_compl_ARGS fi ;; + h|ho|hot|hots|hotsp|hotspo|hotspot) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname con-name ssid band channel) + _nmcli_compl_ARGS + ;; r|re|res|resc|resca|rescan) _nmcli_array_delete_at words 0 2 OPTIONS_REPEATABLE=(ssid) diff --git a/man/nmcli.1.in b/man/nmcli.1.in index a54023d936..7183ca35a7 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -853,6 +853,28 @@ Otherwise the connection is system\(hywide, which is the default. Otherwise the SSID would not be found and the connection attempt would fail. .RE .TP +.B wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ] +.br +Create a Wi-Fi hotspot. The command creates a hotspot connection profile according to +Wi-Fi device capabilities and activates it on the device. The hotspot is secured with WPA +if device/driver supports that, otherwise WEP is used. +Use \fIconnection down\fP or \fIdevice disconnect\fP to stop the hotspot. +.br +.RS +.PP +Parameters of the hotspot can be influenced by the optional parameters: +.IP \fIifname\fP 10 +\(en what Wi-Fi device is used +.IP \fIcon-name\fP 10 +\(en name of the created hotspot connection profile +.IP \fIssid\fP 10 +\(en SSID of the hotspot +.IP \fIband\fP 10 +\(en Wi-Fi band to use +.IP \fIchannel\fP 10 +\(en Wi-Fi channel to use +.RE +.TP .B wifi rescan [ifname ] [[ssid ] ...] .br Request that \fINetworkManager\fP immediately re-scan for available access points. From 781d24f1ddfac109ba54cbf2120337cb30e56563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Tue, 13 Oct 2015 17:48:06 +0200 Subject: [PATCH 3/5] cli: add 'password' option for 'nmcli device wifi hotspot' It allows user provided password to be used to secure the hotspot. Otherwise, nmcli will generate a suitable password. --- clients/cli/devices.c | 65 ++++++++++++++++++++++++++++-------- clients/cli/nmcli-completion | 2 +- man/nmcli.1.in | 5 +++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 1fde747dfa..03eea15535 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -268,6 +268,7 @@ usage (void) " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" " [bssid ] [name ] [private yes|no] [hidden yes|no]\n\n" " wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ]\n\n" + " [password ]\n\n" " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -373,7 +374,7 @@ usage_device_wifi (void) "assumed that IP configuration is obtained via DHCP.\n" "\n" "ARGUMENTS := wifi hotspot [ifname ] [con-name ] [ssid ]\n" - " [band a|bg] [channel ]\n" + " [band a|bg] [channel ] [password ]\n" "\n" "Create a Wi-Fi hotspot. Use 'connection down' or 'device disconnect'\n" "to stop the hotspot.\n" @@ -383,6 +384,7 @@ usage_device_wifi (void) "ssid - SSID of the hotspot\n" "band - Wi-Fi band to use\n" "channel - Wi-Fi channel to use\n" + "password - password to use for the hotspot\n" "\n" "ARGUMENTS := rescan [ifname ] [[ssid ] ...]\n" "\n" @@ -2763,12 +2765,15 @@ generate_wep_key (char *key, size_t len) key[10] = '\0'; } -static void +static gboolean set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, const char *wifi_mode, - NMDeviceWifiCapabilities caps) + NMDeviceWifiCapabilities caps, + const char *password, + GError **error) { - char key[11]; + char generated_key[11]; + const char *key; const char *key_mgmt; if (g_strcmp0 (wifi_mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { @@ -2776,35 +2781,54 @@ set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, nm_setting_wireless_security_add_proto (s_wsec, "rsn"); nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp"); nm_setting_wireless_security_add_group (s_wsec, "ccmp"); - generate_wpa_key (key, sizeof (key)); key_mgmt = "wpa-psk"; } else if (caps & NM_WIFI_DEVICE_CAP_WPA) { nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); nm_setting_wireless_security_add_group (s_wsec, "tkip"); - generate_wpa_key (key, sizeof (key)); key_mgmt = "wpa-psk"; - } else { - generate_wep_key (key, sizeof (key)); + } else key_mgmt = "none"; - } - } else { - generate_wep_key (key, sizeof (key)); + } else key_mgmt = "none"; - } if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) { + /* use WPA */ + if (password) { + if (!nm_utils_wpa_psk_valid (password)) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' is not valid WPA PSK"), password); + return FALSE; + } + key = password; + } else { + generate_wpa_key (generated_key, sizeof (generated_key)); + key = generated_key; + } g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, NM_SETTING_WIRELESS_SECURITY_PSK, key, NULL); } else { + /* use WEP */ + if (password) { + if (!nm_utils_wep_key_valid (password, NM_WEP_KEY_TYPE_KEY)) { + g_set_error (error, NMCLI_ERROR, 0, + _("'%s' is not valid WEP key (it should be 5 or 13 ASCII chars)"), + password); + return FALSE; + } + key = password; + } else { + generate_wep_key (generated_key, sizeof (generated_key)); + key = generated_key; + } g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, key, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, NULL); } + return TRUE; } static NMCResultCode @@ -2819,6 +2843,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) const char *band = NULL; const char *channel = NULL; unsigned long channel_int; + const char *password = NULL; NMDevice *device = NULL; int devices_idx; const GPtrArray *devices; @@ -2829,6 +2854,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) NMSettingWirelessSecurity *s_wsec; NMSettingIPConfig *s_ip4, *s_ip6; GBytes *ssid_bytes; + GError *error = NULL; /* Set default timeout waiting for operation completion. */ if (nmc->timeout == -1) @@ -2881,6 +2907,13 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) goto error; } channel = *argv; + } else if (strcmp (*argv, "password") == 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; + } + password = *argv; } else { g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -2967,7 +3000,13 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps); + if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, &error)) { + g_object_unref (connection); + g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); + goto error; + } s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); nm_connection_add_setting (connection, NM_SETTING (s_ip4)); diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index fe589f660b..62951a1a88 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -1346,7 +1346,7 @@ _nmcli() ;; h|ho|hot|hots|hotsp|hotspo|hotspot) _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname con-name ssid band channel) + OPTIONS=(ifname con-name ssid band channel password) _nmcli_compl_ARGS ;; r|re|res|resc|resca|rescan) diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 7183ca35a7..e195b97bfc 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -854,6 +854,7 @@ Otherwise the SSID would not be found and the connection attempt would fail. .RE .TP .B wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ] +.B [password ] .br Create a Wi-Fi hotspot. The command creates a hotspot connection profile according to Wi-Fi device capabilities and activates it on the device. The hotspot is secured with WPA @@ -873,6 +874,10 @@ Parameters of the hotspot can be influenced by the optional parameters: \(en Wi-Fi band to use .IP \fIchannel\fP 10 \(en Wi-Fi channel to use +.IP \fIpassword\fP 10 +\(en password to use for the created hotspot. If not provided, +nmcli will generate a password. The password is either WPA +pre-shared key or WEP key. .RE .TP .B wifi rescan [ifname ] [[ssid ] ...] From 2f52a10be12a9b04d802bd6d5040ead71aa7434d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Wed, 14 Oct 2015 12:05:32 +0200 Subject: [PATCH 4/5] cli: add '--show-password' option for 'nmcli device wifi hotspot' It is useful to show nmcli-generated hotspot password (if a user does not provide his own password). Without the option the user would have to look into the generated profile in order to find out the password. --- clients/cli/devices.c | 13 +++++++++++-- clients/cli/nmcli-completion | 3 ++- man/nmcli.1.in | 17 ++++++++++------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 03eea15535..0c38419234 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -268,7 +268,7 @@ usage (void) " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" " [bssid ] [name ] [private yes|no] [hidden yes|no]\n\n" " wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ]\n\n" - " [password ]\n\n" + " [password ] [--show-password]\n\n" " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -375,6 +375,7 @@ usage_device_wifi (void) "\n" "ARGUMENTS := wifi hotspot [ifname ] [con-name ] [ssid ]\n" " [band a|bg] [channel ] [password ]\n" + " [--show-password]\n" "\n" "Create a Wi-Fi hotspot. Use 'connection down' or 'device disconnect'\n" "to stop the hotspot.\n" @@ -385,6 +386,7 @@ usage_device_wifi (void) "band - Wi-Fi band to use\n" "channel - Wi-Fi channel to use\n" "password - password to use for the hotspot\n" + "--show-password - tell nmcli to print password to stdout\n" "\n" "ARGUMENTS := rescan [ifname ] [[ssid ] ...]\n" "\n" @@ -2770,6 +2772,7 @@ set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, const char *wifi_mode, NMDeviceWifiCapabilities caps, const char *password, + gboolean show_password, GError **error) { char generated_key[11]; @@ -2828,6 +2831,9 @@ set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, NULL); } + if (show_password) + g_print (_("Hotspot password: %s\n"), key); + return TRUE; } @@ -2844,6 +2850,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) const char *channel = NULL; unsigned long channel_int; const char *password = NULL; + gboolean show_password = FALSE; NMDevice *device = NULL; int devices_idx; const GPtrArray *devices; @@ -2914,6 +2921,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) goto error; } password = *argv; + } else if (nmc_arg_is_option (*argv, "show-password")) { + show_password = TRUE; } else { g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -3000,7 +3009,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, &error)) { + if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, show_password, &error)) { g_object_unref (connection); g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 62951a1a88..02364c65cd 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -512,6 +512,7 @@ _nmcli_compl_ARGS() save| \ hidden| \ private) + show-password) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "yes no" return 0 @@ -1346,7 +1347,7 @@ _nmcli() ;; h|ho|hot|hots|hotsp|hotspo|hotspot) _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname con-name ssid band channel password) + OPTIONS=(ifname con-name ssid band channel password show-password) _nmcli_compl_ARGS ;; r|re|res|resc|resca|rescan) diff --git a/man/nmcli.1.in b/man/nmcli.1.in index e195b97bfc..61328f05a4 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -854,7 +854,7 @@ Otherwise the SSID would not be found and the connection attempt would fail. .RE .TP .B wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ] -.B [password ] +.B [password ] [--show-password] .br Create a Wi-Fi hotspot. The command creates a hotspot connection profile according to Wi-Fi device capabilities and activates it on the device. The hotspot is secured with WPA @@ -864,20 +864,23 @@ Use \fIconnection down\fP or \fIdevice disconnect\fP to stop the hotspot. .RS .PP Parameters of the hotspot can be influenced by the optional parameters: -.IP \fIifname\fP 10 +.IP \fIifname\fP 17 \(en what Wi-Fi device is used -.IP \fIcon-name\fP 10 +.IP \fIcon-name\fP 17 \(en name of the created hotspot connection profile -.IP \fIssid\fP 10 +.IP \fIssid\fP 17 \(en SSID of the hotspot -.IP \fIband\fP 10 +.IP \fIband\fP 17 \(en Wi-Fi band to use -.IP \fIchannel\fP 10 +.IP \fIchannel\fP 17 \(en Wi-Fi channel to use -.IP \fIpassword\fP 10 +.IP \fIpassword\fP 17 \(en password to use for the created hotspot. If not provided, nmcli will generate a password. The password is either WPA pre-shared key or WEP key. +.IP \fI--show-password\fP 17 +\(en tell nmcli to print the password to stdout. It is useful +when the user did not provide his own password. .RE .TP .B wifi rescan [ifname ] [[ssid ] ...] From fe5927ae488742d524b6ae496a0152f6b58c70f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Wed, 14 Oct 2015 12:22:49 +0200 Subject: [PATCH 5/5] nmcli: add a hotspot example to nmcli manual page --- man/nmcli.1.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 61328f05a4..0b4d90a5bf 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -1070,6 +1070,11 @@ using password "caffeine". This is mainly useful when connecting to "Cafe Hotspo the first time. Next time, it is better to use 'nmcli con up id "My cafe"' so that the existing connection profile can be used and no additional is created. +.IP "\fB\f(CWnmcli dev wifi hotspot -s con-name QuickHotspot\fP\fP" +.IP +creates a hotspot profile and connects it. Prints the hotspot password the user should use +to connect to the hotspot from other devices. + .IP "\fB\f(CWnmcli connection add type ethernet autoconnect no ifname eth0\fP\fP" .IP non-interactively adds an Ethernet connection tied to eth0 interface with automatic IP configuration (DHCP),