diff options
author | Nishant Chaprana <n.chaprana@samsung.com> | 2019-07-04 17:41:09 +0530 |
---|---|---|
committer | Nishant Chaprana <n.chaprana@samsung.com> | 2019-07-04 17:41:17 +0530 |
commit | 6b2381a2adabea7d8309ff158ef675ff88184305 (patch) | |
tree | 2c9b2bb6d8b214acc18b8e784e6841f468a5a040 /src/network.c | |
parent | 9362752a471a5c892d679548fbf2828d5fc5684b (diff) | |
download | connman-6b2381a2adabea7d8309ff158ef675ff88184305.tar.gz connman-6b2381a2adabea7d8309ff158ef675ff88184305.tar.bz2 connman-6b2381a2adabea7d8309ff158ef675ff88184305.zip |
Imported Upstream version 1.37upstream/1.37
Change-Id: Ib5957e7ee3a9315ee86a331189bc3e9e71751ee8
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
Diffstat (limited to 'src/network.c')
-rw-r--r-- | src/network.c | 365 |
1 files changed, 357 insertions, 8 deletions
diff --git a/src/network.c b/src/network.c index ed56210f..56fe24ff 100644 --- a/src/network.c +++ b/src/network.c @@ -27,6 +27,8 @@ #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 @@ -41,6 +43,15 @@ */ #define RS_REFRESH_TIMEOUT 3 +/* + * As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3) + * Router Solicitation messages, each separated by at least + * RTR_SOLICITATION_INTERVAL(4) seconds to obtain RA for IPv6 auto-configuration. + */ +#define RTR_SOLICITATION_INTERVAL 4 + +#define DHCP_RETRY_TIMEOUT 10 + static GSList *network_list = NULL; static GSList *driver_list = NULL; @@ -60,6 +71,9 @@ struct connman_network { 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; @@ -90,6 +104,7 @@ struct connman_network { char *private_key_passphrase; char *phase2_auth; bool wps; + bool wps_advertizing; bool use_wps; char *pin_wps; } wifi; @@ -146,6 +161,260 @@ static void set_configuration(struct connman_network *network, 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; + + err = __connman_ipconfig_gateway_add(ipconfig_ipv4); + 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; @@ -163,6 +432,14 @@ static void dhcp_success(struct connman_network *network) 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; @@ -229,6 +506,14 @@ static int set_connected_manual(struct connman_network *network) 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; @@ -241,6 +526,14 @@ 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; @@ -248,6 +541,7 @@ static int set_connected_dhcp(struct connman_network *network) int err; DBG("network %p", network); + remove_dhcp_timeout(network); service = connman_service_lookup_from_network(network); ipconfig_ipv4 = __connman_service_get_ip4config(service); @@ -263,6 +557,44 @@ static int set_connected_dhcp(struct connman_network *network) 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) { @@ -427,7 +759,7 @@ static void check_dhcpv6(struct nd_router_advert *reply, DBG("re-send router solicitation %d", network->router_solicit_count); network->router_solicit_count--; - __connman_inet_ipv6_send_rs(network->index, 1, + __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL, check_dhcpv6, network); return; } @@ -512,7 +844,6 @@ static void receive_refresh_rs_reply(struct nd_router_advert *reply, network->router_solicit_refresh_count = 0; connman_network_unref(network); - return; } int __connman_network_refresh_rs_ipv6(struct connman_network *network, @@ -577,7 +908,8 @@ static void autoconf_ipv6_set(struct connman_network *network) /* Try to get stateless DHCPv6 information, RFC 3736 */ network->router_solicit_count = 3; - __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network); + __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL, + check_dhcpv6, network); } static void set_connected(struct connman_network *network) @@ -663,6 +995,7 @@ static void set_disconnected(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; } @@ -911,6 +1244,7 @@ static void network_destruct(struct connman_network *network) g_free(network->node); g_free(network->name); g_free(network->identifier); + acd_host_free(network->acd_host); network->device = NULL; @@ -946,9 +1280,13 @@ struct connman_network *connman_network_create(const char *identifier, 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; @@ -1459,10 +1797,10 @@ int __connman_network_connect(struct connman_network *network) if (!network->device) return -ENODEV; - network->connecting = true; - __connman_device_disconnect(network->device); + network->connecting = true; + err = network->driver->connect(network); if (err < 0) { if (err == -EINPROGRESS) @@ -1490,6 +1828,10 @@ int __connman_network_disconnect(struct connman_network *network) 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; @@ -1529,13 +1871,16 @@ int __connman_network_clear_ipconfig(struct connman_network *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; } @@ -1919,6 +2264,8 @@ int connman_network_set_bool(struct connman_network *network, 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; @@ -1939,6 +2286,8 @@ bool connman_network_get_bool(struct connman_network *network, 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; |