#include <string.h>
#include "connman.h"
+#include <connman/acd.h>
+#include "src/shared/arp.h"
/*
* How many times to send RS with the purpose of
*/
#define RS_REFRESH_TIMEOUT 3
-#if defined TIZEN_EXT
-#define WIFI_ENCYPTION_MODE_LEN_MAX 6
-#define WIFI_BSSID_LEN_MAX 6
-#endif
-
/*
* As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3)
* Router Solicitation messages, each separated by at least
*/
#define RTR_SOLICITATION_INTERVAL 4
+#define DHCP_RETRY_TIMEOUT 10
+
+#if defined TIZEN_EXT
+static unsigned char invalid_bssid[WIFI_BSSID_LEN_MAX] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+#endif
+
static GSList *network_list = NULL;
static GSList *driver_list = NULL;
int index;
int router_solicit_count;
int router_solicit_refresh_count;
+ struct acd_host *acd_host;
+ guint ipv4ll_timeout;
+ guint dhcp_timeout;
struct connman_network_driver *driver;
void *driver_data;
char *private_key_passphrase;
char *phase2_auth;
bool wps;
+ bool wps_advertizing;
bool use_wps;
char *pin_wps;
#if defined TIZEN_EXT
GSList *bssid_list;
ieee80211_modes_e phy_mode;
connection_mode_e connection_mode;
+ char *connector;
+ char *c_sign_key;
+ char *net_access_key;
+#endif
+#if defined TIZEN_EXT
+ unsigned char last_connected_bssid[WIFI_BSSID_LEN_MAX];
+ GHashTable *assoc_reject_table;
+ bool owe_transition_mode;
+ void *transition_mode_ssid;
+ int transition_mode_ssid_len;
+ unsigned char transition_mode_bssid[WIFI_BSSID_LEN_MAX];
#endif
} wifi;
type);
}
+void connman_network_append_acddbus(DBusMessageIter *dict,
+ struct connman_network *network)
+{
+ if (!network->acd_host)
+ return;
+
+ acd_host_append_dbus_property(network->acd_host, dict);
+}
+
+static int start_acd(struct connman_network *network);
+
+static void remove_ipv4ll_timeout(struct connman_network *network)
+{
+ if (network->ipv4ll_timeout > 0) {
+ g_source_remove(network->ipv4ll_timeout);
+ network->ipv4ll_timeout = 0;
+ }
+}
+
+static void acd_host_ipv4_available(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ int err;
+
+ if (!network)
+ return;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ err = __connman_ipconfig_address_add(ipconfig_ipv4);
+ if (err < 0)
+ goto err;
+
+#if defined TIZEN_EXT
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
+#else
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+#endif
+ if (err < 0)
+ goto err;
+
+ __connman_service_save(service);
+
+ return;
+
+err:
+ connman_network_set_error(__connman_service_get_network(service),
+ CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+}
+
+static int start_ipv4ll(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ struct in_addr addr;
+ char *address;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return -EINVAL;
+ }
+
+ /* Apply random IPv4 address. */
+ addr.s_addr = htonl(arp_random_ip());
+ address = inet_ntoa(addr);
+ if (!address) {
+ connman_error("Could not convert IPv4LL random address %u",
+ addr.s_addr);
+ return -EINVAL;
+ }
+ __connman_ipconfig_set_local(ipconfig_ipv4, address);
+
+ connman_info("Probing IPv4LL address %s", address);
+ return start_acd(network);
+}
+
+static gboolean start_ipv4ll_ontimeout(gpointer data)
+{
+ struct connman_network *network = data;
+
+ if (!network)
+ return FALSE;
+
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+
+ return FALSE;
+}
+
+static void acd_host_ipv4_lost(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ enum connman_ipconfig_type type;
+ enum connman_ipconfig_method method;
+
+ if (!network)
+ return;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ type = __connman_ipconfig_get_config_type(ipconfig_ipv4);
+ if (type != CONNMAN_IPCONFIG_TYPE_IPV4)
+ return;
+
+ __connman_ipconfig_address_remove(ipconfig_ipv4);
+
+ method = __connman_ipconfig_get_method(ipconfig_ipv4);
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+ /*
+ * We have one more chance for DHCP. If this fails
+ * acd_host_ipv4_conflict will be called.
+ */
+ network = __connman_service_get_network(service);
+ if (network)
+ __connman_network_enable_ipconfig(network, ipconfig_ipv4);
+ } else {
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+ }
+}
+
+static void acd_host_ipv4_conflict(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ enum connman_ipconfig_method method;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return;
+ }
+
+ method = __connman_ipconfig_get_method(ipconfig_ipv4);
+ connman_info("%s conflict counts=%u", __FUNCTION__,
+ acd_host_get_conflicts_count(acd));
+
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP &&
+ acd_host_get_conflicts_count(acd) < 2) {
+ connman_info("%s Sending DHCP decline", __FUNCTION__);
+ __connman_dhcp_decline(ipconfig_ipv4);
+
+ connman_network_set_connected_dhcp_later(network, DHCP_RETRY_TIMEOUT);
+ __connman_ipconfig_set_local(ipconfig_ipv4, NULL);
+ } else {
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+ __connman_ipconfig_set_method(ipconfig_ipv4,
+ CONNMAN_IPCONFIG_METHOD_AUTO);
+ __connman_dhcp_decline(ipconfig_ipv4);
+ }
+ /* Start IPv4LL ACD. */
+ start_ipv4ll(network);
+ }
+}
+
+static void acd_host_ipv4_maxconflict(struct acd_host *acd, gpointer user_data)
+{
+ struct connman_network *network = user_data;
+
+ remove_ipv4ll_timeout(network);
+ connman_info("Had maximum number of conflicts. Next IPv4LL address will be "
+ "tried in %d seconds", RATE_LIMIT_INTERVAL);
+ /* Wait, then start IPv4LL ACD. */
+ network->ipv4ll_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ RATE_LIMIT_INTERVAL,
+ start_ipv4ll_ontimeout,
+ network,
+ NULL);
+}
+
+static int start_acd(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ const char* address;
+ struct in_addr addr;
+
+ remove_ipv4ll_timeout(network);
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ if (!ipconfig_ipv4) {
+ connman_error("Service has no IPv4 configuration");
+ return -EINVAL;
+ }
+
+ if (!network->acd_host) {
+ int index;
+
+ index = __connman_ipconfig_get_index(ipconfig_ipv4);
+ network->acd_host = acd_host_new(index,
+ connman_service_get_dbuspath(service));
+ if (!network->acd_host) {
+ connman_error("Could not create ACD data structure");
+ return -EINVAL;
+ }
+
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_AVAILABLE,
+ acd_host_ipv4_available, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_LOST,
+ acd_host_ipv4_lost, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_CONFLICT,
+ acd_host_ipv4_conflict, network);
+ acd_host_register_event(network->acd_host,
+ ACD_HOST_EVENT_IPV4_MAXCONFLICT,
+ acd_host_ipv4_maxconflict, network);
+ }
+
+ address = __connman_ipconfig_get_local(ipconfig_ipv4);
+ if (!address)
+ return -EINVAL;
+
+ connman_info("Starting ACD for address %s", address);
+ if (inet_pton(AF_INET, address, &addr) != 1)
+ connman_error("Could not convert address %s", address);
+
+ acd_host_start(network->acd_host, htonl(addr.s_addr));
+
+ return 0;
+}
+
static void dhcp_success(struct connman_network *network)
{
struct connman_service *service;
if (!ipconfig_ipv4)
return;
+ if (connman_setting_get_bool("AddressConflictDetection")) {
+ err = start_acd(network);
+ if (!err)
+ return;
+
+ /* On error proceed without ACD. */
+ }
+
err = __connman_ipconfig_address_add(ipconfig_ipv4);
if (err < 0)
goto err;
if (!__connman_ipconfig_get_local(ipconfig))
__connman_service_read_ip4config(service);
+ if (connman_setting_get_bool("AddressConflictDetection")) {
+ err = start_acd(network);
+ if (!err)
+ return 0;
+
+ /* On error proceed without ACD. */
+ }
+
err = __connman_ipconfig_address_add(ipconfig);
if (err < 0)
goto err;
return err;
}
+static void remove_dhcp_timeout(struct connman_network *network)
+{
+ if (network->dhcp_timeout > 0) {
+ g_source_remove(network->dhcp_timeout);
+ network->dhcp_timeout = 0;
+ }
+}
+
static int set_connected_dhcp(struct connman_network *network)
{
struct connman_service *service;
int err;
DBG("network %p", network);
+ remove_dhcp_timeout(network);
service = connman_service_lookup_from_network(network);
ipconfig_ipv4 = __connman_service_get_ip4config(service);
return 0;
}
+static gboolean set_connected_dhcp_timout(gpointer data)
+{
+ struct connman_network *network = data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig;
+ enum connman_ipconfig_method method;
+
+ network->dhcp_timeout = 0;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return FALSE;
+
+ ipconfig = __connman_service_get_ip4config(service);
+ if (!ipconfig)
+ return FALSE;
+
+ /* Method is still DHCP? */
+ method = __connman_ipconfig_get_method(ipconfig);
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP)
+ set_connected_dhcp(network);
+
+ return FALSE;
+}
+
+void connman_network_set_connected_dhcp_later(struct connman_network *network,
+ uint32_t sec)
+{
+ remove_dhcp_timeout(network);
+
+ network->dhcp_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ sec,
+ set_connected_dhcp_timout,
+ network,
+ NULL);
+}
+
static int manual_ipv6_set(struct connman_network *network,
struct connman_ipconfig *ipconfig_ipv6)
{
network->connecting = false;
ipconfig_ipv6 = __connman_service_get_ip6config(service);
+#if defined TIZEN_EXT
+ if (!ipconfig_ipv6)
+ goto err;
+#endif
err = __connman_ipconfig_address_add(ipconfig_ipv6);
if (err < 0)
goto err;
network->router_solicit_refresh_count = 0;
connman_network_unref(network);
- return;
}
int __connman_network_refresh_rs_ipv6(struct connman_network *network,
__connman_service_notify_ipv4_configuration(service);
/* fall through */
case CONNMAN_IPCONFIG_METHOD_DHCP:
+ remove_dhcp_timeout(network);
__connman_dhcp_stop(ipconfig_ipv4);
break;
}
__connman_ipconfig_address_unset(ipconfig_ipv4);
__connman_ipconfig_address_unset(ipconfig_ipv6);
+
#if defined TIZEN_EXT
}
#endif
g_free(network->wifi.private_key_passphrase);
g_free(network->wifi.phase2_auth);
g_free(network->wifi.pin_wps);
+
#if defined TIZEN_EXT
g_slist_free_full(network->wifi.vsie_list, g_free);
g_slist_free_full(network->wifi.bssid_list, g_free);
g_free(network->node);
g_free(network->name);
g_free(network->identifier);
+ acd_host_free(network->acd_host);
network->device = NULL;
network->type = type;
network->identifier = ident;
+ network->acd_host = NULL;
+ network->ipv4ll_timeout = 0;
network_list = g_slist_prepend(network_list, network);
+ network->dhcp_timeout = 0;
+
DBG("network %p identifier %s type %s", network, identifier,
type2string(type));
return network;
}
#endif
+#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
+bool connman_network_check_validity(struct connman_network *network)
+{
+ return (NULL == g_slist_find(network_list, network)) ? false : true;
+}
+#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
+
/**
* connman_network_set_associating:
* @network: network structure
case CONNMAN_NETWORK_ERROR_BLOCKED:
set_blocked_error(network);
break;
-
}
__connman_network_disconnect(network);
if (!network->device)
return -ENODEV;
- network->connecting = true;
-
#if defined TIZEN_EXT
if (network->type != CONNMAN_NETWORK_TYPE_CELLULAR)
#endif
__connman_device_disconnect(network->device);
+
+ network->connecting = true;
+
#if defined TIZEN_EXT
DBG("ConnMan, Connect Request [%s]", network->name);
#endif
+
err = network->driver->connect(network);
if (err < 0) {
if (err == -EINPROGRESS) {
return err;
}
+#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
+ /*
+ * Note: If EAP on Ethernet is ON, then network will be connected
+ * after EAP Success event is recieved, from plugin/ethernet.c
+ */
+ struct connman_service *service = connman_service_lookup_from_network(network);
+ if (service && __connman_service_get_use_eapol(service)) {
+ connman_network_set_associating(network, true);
+ return 0;
+ }
+#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
+
set_connected(network);
return err;
DBG("network %p", network);
+ remove_ipv4ll_timeout(network);
+ if (network->acd_host)
+ acd_host_stop(network->acd_host);
+
if (!network->connected && !network->connecting &&
!network->associating)
return -ENOTCONN;
return -EUNATCH;
network->connecting = false;
+
#if defined TIZEN_EXT
DBG("ConnMan, Disconnect request");
+ struct connman_service *service = connman_service_lookup_from_network(network);
+ connman_service_set_disconnection_requested(service, true);
#endif
if (network->driver->disconnect)
err = network->driver->disconnect(network);
case CONNMAN_IPCONFIG_METHOD_OFF:
case CONNMAN_IPCONFIG_METHOD_FIXED:
return -EINVAL;
- case CONNMAN_IPCONFIG_METHOD_AUTO:
- release_dhcpv6(network);
- break;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
__connman_ipconfig_address_remove(ipconfig);
break;
+ case CONNMAN_IPCONFIG_METHOD_AUTO:
+ release_dhcpv6(network);
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+ break;
+ /* fall through */
case CONNMAN_IPCONFIG_METHOD_DHCP:
+ remove_dhcp_timeout(network);
__connman_dhcp_stop(ipconfig_ipv4);
break;
}
return (unsigned char *)network->wifi.bssid;
}
+int connman_network_set_transition_mode_bssid(struct connman_network *network,
+ const unsigned char *transition_mode_bssid)
+{
+ int i = 0;
+
+ if (transition_mode_bssid == NULL)
+ return -EINVAL;
+
+ for (;i < WIFI_BSSID_LEN_MAX;i++)
+ network->wifi.transition_mode_bssid[i] = transition_mode_bssid[i];
+
+ return 0;
+}
+
+unsigned char *connman_network_get_transition_mode_bssid(struct connman_network *network)
+{
+ return (unsigned char *)network->wifi.transition_mode_bssid;
+}
+
+bool connman_network_check_transition_mode(struct connman_network *network1, struct connman_network *network2)
+{
+
+ if (network1 == NULL || network2 == NULL)
+ return FALSE;
+
+ if (network1->wifi.owe_transition_mode == FALSE || network2->wifi.owe_transition_mode == FALSE)
+ return FALSE;
+
+ if ((memcmp(network1->wifi.bssid, network2->wifi.transition_mode_bssid, WIFI_BSSID_LEN_MAX) == 0)
+ && (memcmp(network1->wifi.transition_mode_bssid, network2->wifi.bssid, WIFI_BSSID_LEN_MAX) == 0))
+ return TRUE;
+ else
+ return FALSE;
+}
+
int connman_network_set_maxspeed(struct connman_network *network,
int maxspeed)
{
{
return network->wifi.bssid_list;
}
+
+int connman_network_set_last_connected_bssid(struct connman_network *network,
+ const unsigned char *bssid)
+{
+ if (!bssid)
+ return -EINVAL;
+
+ if (!memcmp(bssid, invalid_bssid, WIFI_BSSID_LEN_MAX))
+ return -EINVAL;
+
+ memcpy(network->wifi.last_connected_bssid, bssid, WIFI_BSSID_LEN_MAX);
+
+ return 0;
+}
+
+unsigned char *connman_network_get_last_connected_bssid(struct connman_network *network)
+{
+ return (unsigned char *)network->wifi.last_connected_bssid;
+}
+
+void connman_network_set_assoc_reject_table(struct connman_network *network,
+ GHashTable *assoc_reject_table)
+{
+ if (!network)
+ return;
+
+ if (!assoc_reject_table)
+ return;
+
+ network->wifi.assoc_reject_table = assoc_reject_table;
+}
+
+GHashTable *connman_network_get_assoc_reject_table(struct connman_network *network)
+{
+ if (!network)
+ return NULL;
+
+ return network->wifi.assoc_reject_table;
+}
#endif
int connman_network_set_nameservers(struct connman_network *network,
uint8_t strength)
{
network->strength = strength;
+#if defined TIZEN_EXT
+ __connman_service_notify_strength_changed(network);
+#endif
return 0;
}
} else if (g_str_equal(key, "WiFi.PinWPS")) {
g_free(network->wifi.pin_wps);
network->wifi.pin_wps = g_strdup(value);
+#if defined TIZEN_EXT
+ } else if (g_str_equal(key, "WiFi.Connector")) {
+ g_free(network->wifi.connector);
+ network->wifi.connector = g_strdup(value);
+ } else if (g_str_equal(key, "WiFi.CSignKey")) {
+ g_free(network->wifi.c_sign_key);
+ network->wifi.c_sign_key = g_strdup(value);
+ } else if (g_str_equal(key, "WiFi.NetAccessKey")) {
+ g_free(network->wifi.net_access_key);
+ network->wifi.net_access_key = g_strdup(value);
+#endif
} else {
return -EINVAL;
}
return network->wifi.phase2_auth;
else if (g_str_equal(key, "WiFi.PinWPS"))
return network->wifi.pin_wps;
+#if defined TIZEN_EXT
+ else if (g_str_equal(key, "WiFi.Connector"))
+ return network->wifi.connector;
+ else if (g_str_equal(key, "WiFi.CSignKey"))
+ return network->wifi.c_sign_key;
+ else if (g_str_equal(key, "WiFi.NetAccessKey"))
+ return network->wifi.net_access_key;
+#endif
return NULL;
}
network->roaming = value;
else if (g_strcmp0(key, "WiFi.WPS") == 0)
network->wifi.wps = value;
+ else if (g_strcmp0(key, "WiFi.WPSAdvertising") == 0)
+ network->wifi.wps_advertizing = value;
else if (g_strcmp0(key, "WiFi.UseWPS") == 0)
network->wifi.use_wps = value;
#if defined TIZEN_EXT
network->default_internet = value;
else if (g_strcmp0(key, "WiFi.HS20AP") == 0)
network->wifi.isHS20AP = value;
+ else if (g_strcmp0(key, "WiFi.TRANSITION_MODE") == 0)
+ network->wifi.owe_transition_mode = value;
#endif
return -EINVAL;
return network->roaming;
else if (g_str_equal(key, "WiFi.WPS"))
return network->wifi.wps;
+ else if (g_str_equal(key, "WiFi.WPSAdvertising"))
+ return network->wifi.wps_advertizing;
else if (g_str_equal(key, "WiFi.UseWPS"))
return network->wifi.use_wps;
#if defined TIZEN_EXT
return network->default_internet;
else if (g_str_equal(key, "WiFi.HS20AP"))
return network->wifi.isHS20AP;
+ else if (g_strcmp0(key, "WiFi.TRANSITION_MODE"))
+ return network->wifi.owe_transition_mode;
#endif
return false;
network->wifi.ssid_len = size;
} else
network->wifi.ssid_len = 0;
+#ifdef TIZEN_EXT
+ } else if (g_str_equal(key, "WiFi.TRANSITION_MODE_SSID")) {
+ g_free(network->wifi.transition_mode_ssid);
+ network->wifi.transition_mode_ssid = g_try_malloc(size);
+ if (network->wifi.transition_mode_ssid) {
+ memcpy(network->wifi.transition_mode_ssid, data, size);
+ network->wifi.transition_mode_ssid_len = size;
+ } else
+ network->wifi.transition_mode_ssid_len = 0;
+#endif
} else {
return -EINVAL;
}
if (size)
*size = network->wifi.ssid_len;
return network->wifi.ssid;
+#ifdef TIZEN_EXT
+ } else if (g_str_equal(key, "WiFi.TRANSITION_MODE_SSID")) {
+ if (size)
+ *size = network->wifi.transition_mode_ssid_len;
+ return network->wifi.transition_mode_ssid;
+#endif
}
-
return NULL;
}