blob: 18a5cda6d4ea508d1e6484871821f358259cf739 [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2007-2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <net/ethernet.h>
#include <glib.h>
#include <connman/blob.h>
#include "connman.h"
#define _DBG_WIFI(fmt, arg...) DBG(DBG_WIFI, fmt, ## arg)
static inline gboolean ispsk(const char *security)
{
return (g_strcmp0(security, "wpa") == 0 ||
g_strcmp0(security, "rsn") == 0 ||
g_strcmp0(security, "psk") == 0);
}
/*
* Construct a ``group name'' when creating a wifi service (see
* __connman_get_wifi_service_int). This string incorporates the
* SSID, security, and operating mode of all devices that should
* be grouped together. Note that we promote all PSK security
* settings to "psk" so services created before the device shows
* up in a scan result can be located when their associated
* network is created.
*/
char *connman_wifi_build_group_name(const unsigned char *ssid,
unsigned int ssid_len,
const char *mode,
const char *security)
{
GString *str;
unsigned int i;
str = g_string_sized_new((ssid_len * 2) + 24);
if (str == NULL)
return NULL;
if (ssid_len > 0 && ssid[0] != '\0') {
for (i = 0; i < ssid_len; i++)
g_string_append_printf(str, "%02x", ssid[i]);
}
/* NB: use "psk" for all wpa/rsn/psk services */
g_string_append_printf(str, "_%s_%s", mode,
ispsk(security) ? "psk" : security);
return g_string_free(str, FALSE);
}
/*
* Table of well-known SSID's that we want to disambiguate when
* constructing service identifiers. Normally all devices with
* the same SSID, security setup, and operating mode will be
* merged to the same service. A service advertising one of
* these SSID's will have a unique group name constructed that
* ensures like-devices will not be grouped together.
*
* TODO(sleffler) externalize this table and make it's use optional
*/
static struct {
char *name;
char *value;
} special_ssid[] = {
{ "<hidden>", "hidden" },
{ "default", "linksys" },
{ "wireless" },
{ "linksys" },
{ "netgear" },
{ "dlink" },
{ "2wire" },
{ "compaq" },
{ "tsunami" },
{ "comcomcom", "3com" },
{ "3Com", "3com" },
{ "Symbol", "symbol" },
{ "Motorola", "motorola" },
{ "Wireless" , "wireless" },
{ "WLAN", "wlan" },
{ }
};
/*
* Like connman_wifi_build_group_name but used in the wifi plugins
* when creating network objects from scan results. The group name
* must match the strings constructed by connman_wifi_build_group_name
* so the associated services are matched up. This routine differs
* in that it handles hidden SSID's and does uniquification of
* devices that beacon well-known SSID's that we don't want to merge
* into a group.
*/
char *connman_wifi_build_group(const char *addr, const char *name,
const unsigned char *ssid, unsigned int ssid_len,
const char *mode, const char *security)
{
GString *str;
unsigned int i;
if (addr == NULL)
return NULL;
str = g_string_sized_new((ssid_len * 2) + 24);
if (str == NULL)
return NULL;
if (ssid == NULL) {
g_string_append_printf(str, "hidden_%s", addr);
goto done;
}
for (i = 0; special_ssid[i].name; i++) {
if (g_strcmp0(special_ssid[i].name, name) == 0) {
if (special_ssid[i].value == NULL)
g_string_append_printf(str, "%s_%.*s",
name, 2*ETH_ALEN, addr);
else
g_string_append_printf(str, "%s_%.*s",
special_ssid[i].value, 2*ETH_ALEN, addr);
goto done;
}
}
if (ssid_len > 0 && ssid[0] != '\0') {
for (i = 0; i < ssid_len; i++)
g_string_append_printf(str, "%02x", ssid[i]);
} else
g_string_append_printf(str, "hidden_%.*s", 2*ETH_ALEN, addr);
done:
/* NB: use "psk" for all wpa/rsn/psk services */
g_string_append_printf(str, "_%s_%s", mode,
ispsk(security) ? "psk" : security);
return g_string_free(str, FALSE);
}
/*
* NB: this knows a bit too much about how services are written to the profile.
*/
static void append_hidden_ssids(GKeyFile *keyfile, GSList **hidden_ssids)
{
gchar **keys;
keys = g_key_file_get_groups(keyfile, NULL);
if (keys == NULL) {
connman_warn("%s: unable to retrieve groups", __func__);
return;
}
for (; *keys != NULL; keys++) {
gchar *key = *keys;
gchar *hex_ssid;
struct blob *ssid;
int ret;
if (g_ascii_strncasecmp(key, "wifi_", 5) != 0)
continue;
if (!g_key_file_get_boolean(keyfile, key, "WiFi.HiddenSSID",
NULL))
continue;
hex_ssid = g_key_file_get_string(keyfile, key, "SSID", NULL);
if (hex_ssid == NULL)
continue;
_DBG_WIFI("service %s", key);
ret = blob_new_from_hex(&ssid, hex_ssid);
g_free(hex_ssid);
if (ret < 0) {
connman_warn("%s: skip hex ssid %s for key %s",
__func__, hex_ssid, key);
continue;
}
_DBG_WIFI("ssid %.*s", (int) ssid->len, ssid->data);
*hidden_ssids = g_slist_prepend(*hidden_ssids, ssid);
}
}
/**
* connman_wifi_append_hidden_ssids:
* @ssids: pointer to a list of hidden SSIDs
*
* Append the list of SSIDs whose services whose WiFi.HiddenSSID property is
* TRUE. These SSIDs should be directly probed whenever performing a WiFi
* network scan.
*/
void connman_wifi_append_hidden_ssids(GSList **ssids)
{
__connman_profile_append_hidden_ssids(ssids, append_hidden_ssids);
}