Add support for handling wifi authentication failure
[platform/upstream/connman.git] / plugins / wifi.c
index 8216816..0b5315f 100755 (executable)
 
 #if defined TIZEN_EXT
 #define WIFI_EAP_FAST_PAC_FILE         "/var/lib/wifi/wifi.pac"        /* path of Pac file for EAP-FAST */
+
+       /* Wi-Fi Signal Strength (for 2.4G (dB))
+        * Excellent :     ~ -63
+        * Good      : -64 ~ -74
+        * Weak      : -75 ~ -82
+        * Very weak : -83 ~ -88
+        * No signal : -89 ~
+        *
+        * Wi-Fi Signal Strength (for 5G (dB))
+        * Excellent :     ~ -67
+        * Good      : -68 ~ -76
+        * Weak      : -77 ~ -82
+        * Very weak : -83 ~ -88
+        * No signal : -89 ~
+        */
+#define RSSI_LEVEL_2_5G  -77
+#define RSSI_LEVEL_2_24G -75
+#define RSSI_LEVEL_3_5G  -68
+#define RSSI_LEVEL_3_24G -64
+#define WIFI_BSSID_STR_LEN 18
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#define ROAM_SCAN_INTERVAL 60 /* 60 seconds */
 #endif
 
 static struct connman_technology *wifi_technology = NULL;
@@ -2086,8 +2109,57 @@ static void service_state_changed(struct connman_service *service,
        }
 }
 
+static gboolean need_bss_transition(uint16_t freq, int snr, int strength)
+{
+       /*
+        * Since bssid->strength is a positive value,
+        * it need to be changed to its original value.
+        */
+       int signal = strength - 120;
+
+       /*
+        * If the currently connected AP matches the following conditions,
+        * scan for BSS transition is started.
+        *   - SNR is less than 20 or RSSI level is less than 3
+        */
+       if (snr < 20 && snr != 0)
+               return TRUE;
+       else if (freq > 4900 && signal <= RSSI_LEVEL_2_5G)
+               return TRUE;
+       else if (freq <= 4900 && signal <= RSSI_LEVEL_2_24G)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean check_bss_condition(uint16_t freq, int snr, uint16_t strength)
+{
+       /*
+        * Since bssid->strength is a positive value,
+        * it need to be changed to its original value.
+        */
+       int signal = strength - 120;
+
+       /*
+        * If the AP that matches the following conditions exists in the SCAN result,
+        * BSS transition is started.
+        *   - SNR is 25 or more and RSSI level is greater than 3
+        */
+       if (snr < 25 && snr != 0)
+               return FALSE;
+
+       if (freq > 4900 && signal > RSSI_LEVEL_3_5G)
+               return TRUE;
+       else if (freq <= 4900 && signal > RSSI_LEVEL_3_24G)
+               return TRUE;
+
+       return FALSE;
+}
+
 static void scan_callback_hidden(int result,
                        GSupplicantInterface *interface, void *user_data);
+
+static int network_disconnect(struct connman_network *network);
 #endif
 
 static void scan_callback(int result, GSupplicantInterface *interface,
@@ -2097,7 +2169,10 @@ static void scan_callback(int result, GSupplicantInterface *interface,
        struct wifi_data *wifi = connman_device_get_data(device);
        bool scanning;
 #if defined TIZEN_EXT
+       bool roaming_needed = false;
+       bool roaming_ap_found = false;
        GSList *list = NULL;
+       GSList *bssid_list = NULL;
        bool favorite_exists = false;
        struct connman_network *network = NULL;
        struct connman_service *service = NULL;
@@ -2157,7 +2232,8 @@ static void scan_callback(int result, GSupplicantInterface *interface,
                        if (service != NULL &&
                                (connman_service_get_favorite(service) == true) &&
                                (connman_service_get_autoconnect(service) == true)) {
-                               DBG("Favorite service exists [%s]", connman_network_get_string(network, "Name"));
+                               DBG("Favorite service exists [%s]",
+                                               connman_network_get_string(network, "Name"));
                                favorite_exists = true;
                                break;
                        }
@@ -2178,7 +2254,8 @@ static void scan_callback(int result, GSupplicantInterface *interface,
                        /* On error, let's recall scan_callback, which will cleanup */
                        return scan_callback(ret, interface, user_data);
                }
-       }
+       } else
+               wifi->allow_full_scan = false;
 #endif
 
        scanning = connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_WIFI);
@@ -2205,12 +2282,56 @@ static void scan_callback(int result, GSupplicantInterface *interface,
                connman_device_unref(device);
 
 #if defined TIZEN_EXT
-       if (wifi && wifi->scan_pending_network && result != -EIO) {
+       if (!wifi)
+               goto done;
+
+       if (wifi->scan_pending_network && result != -EIO) {
                network_connect(wifi->scan_pending_network);
                wifi->scan_pending_network = NULL;
                connman_network_set_connecting(wifi->network);
+       } else if (connman_setting_get_bool("WifiRoaming") && wifi->network) {
+               bssid_list = connman_network_get_bssid_list(wifi->network);
+
+               if (g_slist_length(bssid_list) <= 1)
+                       goto done;
+
+               if (!connman_network_get_connected(wifi->network))
+                       goto done;
+
+               if (connman_network_get_bool(wifi->network, "WiFi.Roaming"))
+                       goto done;
+
+               if (!need_bss_transition(
+                               connman_network_get_frequency(wifi->network),
+                               connman_network_get_snr(wifi->network),
+                               connman_network_get_strength(wifi->network)))
+                       goto done;
+
+               for (bssid_list; bssid_list; bssid_list = bssid_list->next) {
+                       struct g_connman_bssids *bssid = bssid_list->data;
+
+                       if (check_bss_condition(bssid->frequency,
+                                       bssid->score_snr, bssid->strength))
+                               roaming_ap_found = true;
+               }
+
+               if (roaming_ap_found) {
+                       char bssid_buff[WIFI_BSSID_STR_LEN] = {0,};
+                       char *bssid_str = bssid_buff;
+                       unsigned char *bssid;
+
+                       bssid = connman_network_get_bssid(wifi->network);
+                       snprintf(bssid_str, WIFI_BSSID_STR_LEN, MACSTR, MAC2STR(bssid));
+                       connman_network_set_string(wifi->network,
+                                       "WiFi.RoamingCurBSSID", bssid_str);
+
+                       network_disconnect(wifi->network);
+                       wifi->pending_network = wifi->network;
+                       connman_network_set_bool(wifi->network, "WiFi.Roaming", true);
+               }
        }
 
+done:
        if (is_wifi_notifier_registered != true &&
                        wifi_first_scan == true && found_with_first_scan == true) {
                wifi_first_scan = false;
@@ -3524,6 +3645,38 @@ static void connect_callback(int result, GSupplicantInterface *interface,
        return;
 
 found:
+       if (connman_network_get_bool(network, "WiFi.Roaming")) {
+               if (result < 0 ) {
+                       connman_network_set_bool(network, "WiFi.Roaming", false);
+                       connman_network_set_string(network,
+                                       "WiFi.RoamingCurBSSID", NULL);
+               } else {
+                       char bssid_buff[WIFI_BSSID_STR_LEN] = {0,};
+                       char *bssid_str = bssid_buff;
+                       unsigned char *bssid;
+                       const char *cur_bssid;
+
+                       bssid = g_supplicant_interface_get_add_network_bssid(interface);
+                       if (!bssid) {
+                               connman_network_set_bool(network, "WiFi.Roaming", false);
+                               connman_network_set_string(network,
+                                               "WiFi.RoamingCurBSSID", NULL);
+                       } else {
+                               snprintf(bssid_str,
+                                               WIFI_BSSID_STR_LEN,
+                                               MACSTR, MAC2STR(bssid));
+
+                               connman_network_set_string(network,
+                                               "WiFi.RoamingDstBSSID", bssid_str);
+
+                               cur_bssid = connman_network_get_string(network,
+                                               "WiFi.RoamingCurBSSID");
+
+                               __connman_technology_notify_roaming_state(ifname,
+                                               "started", cur_bssid, bssid_str);
+                       }
+               }
+       }
 #endif
        if (result == -ENOKEY) {
                connman_network_set_error(network,
@@ -3817,7 +3970,13 @@ found:
                return;
        }
 
+#if defined TIZEN_EXT
+       if (wifi->network &&
+                       (wifi->network != wifi->pending_network ||
+                       connman_network_get_bool(wifi->network, "WiFi.Roaming")))
+#else
        if (wifi->network && wifi->network != wifi->pending_network)
+#endif
                connman_network_set_connected(wifi->network, false);
        wifi->network = NULL;
 
@@ -3964,9 +4123,17 @@ static void set_connection_mode(struct connman_network *network,
 }
 
 static void signalpoll_callback(int result, int maxspeed, int strength,
-                               void *user_data)
-{
+                               int snr, void *user_data)
+{
+       char bssid_buff[WIFI_BSSID_STR_LEN] = {0,};
+       char *bssid_str = bssid_buff;
+       unsigned char *bssid;
+       struct timespec curr_time = {0};
+       __time_t roam_scan_time;
+       const char *interface = NULL;
+       struct connman_device *device;
        struct connman_network *network = user_data;
+       uint16_t freq = connman_network_get_frequency(network);
 
        if (result != 0) {
                DBG("Failed to get maxspeed from signalpoll !");
@@ -3978,12 +4145,36 @@ static void signalpoll_callback(int result, int maxspeed, int strength,
        if (strength > 100)
                strength = 100;
 
-       DBG("maxspeed = %d, strength = %d", maxspeed, strength);
+       DBG("freq = %u, maxspeed = %d, strength = %d, snr = %d", freq, maxspeed, strength, snr);
 
        connman_network_set_strength(network, (uint8_t)strength);
+       connman_network_set_snr(network, snr);
        connman_network_set_maxspeed(network, maxspeed);
        set_connection_mode(network, maxspeed);
 
+       clock_gettime(CLOCK_MONOTONIC, &curr_time);
+       roam_scan_time = connman_network_get_roam_scan_time(network);
+       if (curr_time.tv_sec <= roam_scan_time + ROAM_SCAN_INTERVAL)
+               goto done;
+
+       if (need_bss_transition(freq, snr, strength)) {
+               device = connman_network_get_device(network);
+               if (!device)
+                       goto done;
+
+               interface = connman_device_get_string(device, "Interface");
+               bssid = connman_network_get_bssid(network);
+               snprintf(bssid_str, WIFI_BSSID_STR_LEN, MACSTR, MAC2STR(bssid));
+               __connman_technology_notify_roaming_state(interface, "required", bssid_str, NULL);
+
+               if (connman_setting_get_bool("WifiRoamingScan") == false)
+                       goto done;
+
+               throw_wifi_scan(device, scan_callback);
+               connman_network_set_roam_scan_time(network, curr_time.tv_sec);
+       }
+
+done:
        connman_network_unref(network);
 }
 
@@ -4021,6 +4212,7 @@ static gboolean autosignalpoll_timeout(gpointer data)
 
                if (wifi->network)
                        connman_network_unref(wifi->network);
+
                return FALSE;
        }
 
@@ -4290,7 +4482,8 @@ static bool handle_wifi_assoc_retry(struct connman_network *network,
                return false;
        }
 
-       if (wifi->state != G_SUPPLICANT_STATE_ASSOCIATING &&
+       if (wifi->state != G_SUPPLICANT_STATE_AUTHENTICATING &&
+                       wifi->state != G_SUPPLICANT_STATE_ASSOCIATING &&
                        wifi->state != G_SUPPLICANT_STATE_ASSOCIATED) {
                wifi->assoc_retry_count = 0;
                return false;
@@ -4310,7 +4503,17 @@ static bool handle_wifi_assoc_retry(struct connman_network *network,
                 * however QA team recommends that the invalid-key error
                 * might be better to display for user experience.
                 */
-               connman_network_set_error(network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+               switch (wifi->state) {
+               case G_SUPPLICANT_STATE_AUTHENTICATING:
+                       connman_network_set_error(network, CONNMAN_NETWORK_ERROR_AUTHENTICATE_FAIL);
+                       break;
+               case G_SUPPLICANT_STATE_ASSOCIATED:
+                       connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+                       break;
+               default:
+                       connman_network_set_error(network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       break;
+               }
 
                return false;
        }
@@ -4371,6 +4574,13 @@ static void interface_state(GSupplicantInterface *interface)
 
        switch (state) {
        case G_SUPPLICANT_STATE_SCANNING:
+#if defined TIZEN_EXT
+               if (wifi->automaxspeed_timeout != 0) {
+                       g_source_remove(wifi->automaxspeed_timeout);
+                       wifi->automaxspeed_timeout = 0;
+                       DBG("Remove signalpoll timer!!");
+               }
+#endif
                if (wifi->connected)
                        connman_network_set_connected(network, false);
 
@@ -4908,6 +5118,59 @@ static void mesh_peer_removed(GSupplicantNetwork *supplicant_network)
 }
 #endif
 
+
+#if defined TIZEN_EXT
+static GSList *get_supported_security_list(unsigned int keymgmt,
+                                       bool owe_transition_mode,
+                                       GSupplicantNetwork *supplicant_network)
+{
+       GSList *sec_list = NULL;
+       dbus_bool_t privacy = g_supplicant_network_get_privacy(supplicant_network);
+       const char *enc_mode = g_supplicant_network_get_enc_mode(supplicant_network);
+
+       if (keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_EAP |
+                                       G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
+               sec_list = g_slist_prepend (sec_list, "ieee8021x");
+       else if (keymgmt & G_SUPPLICANT_KEYMGMT_WPA_FT_EAP)
+               sec_list = g_slist_prepend (sec_list, "ft_ieee8021x");
+
+       if (sec_list)
+               return sec_list;
+
+       if (keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_PSK |
+                                       G_SUPPLICANT_KEYMGMT_WPA_PSK_256)) {
+               if (!g_strcmp0(enc_mode, "aes"))
+                       sec_list = g_slist_prepend (sec_list, "rsn");
+               else if (!g_strcmp0(enc_mode, "tkip"))
+                       sec_list = g_slist_prepend (sec_list, "psk");
+               else if (!g_strcmp0(enc_mode, "mixed")) {
+                       sec_list = g_slist_prepend (sec_list, "psk");
+                       sec_list = g_slist_prepend (sec_list, "rsn");
+               }
+       } else if (keymgmt & G_SUPPLICANT_KEYMGMT_WPA_FT_PSK)
+               sec_list = g_slist_prepend (sec_list, "ft_psk");
+
+       if (keymgmt & G_SUPPLICANT_KEYMGMT_SAE)
+               sec_list = g_slist_prepend (sec_list, "sae");
+       if (keymgmt & G_SUPPLICANT_KEYMGMT_OWE || owe_transition_mode)
+               sec_list = g_slist_prepend (sec_list, "owe");
+       if (keymgmt & G_SUPPLICANT_KEYMGMT_DPP)
+               sec_list = g_slist_prepend (sec_list, "dpp");
+
+       if (sec_list)
+               return sec_list;
+
+       if (privacy)
+               sec_list = g_slist_prepend (sec_list, "wep");
+       else
+               sec_list = g_slist_prepend (sec_list, "none");
+
+       return sec_list;
+}
+#endif
+
 static void network_added(GSupplicantNetwork *supplicant_network)
 {
        struct connman_network *network;
@@ -4926,7 +5189,9 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        const unsigned char *transition_mode_ssid;
        const unsigned char *transition_mode_bssid;
        unsigned int transition_mode_ssid_len;
+       unsigned int keymgmt;
        GSList *vsie_list = NULL;
+       GSList *sec_list = NULL;
        const unsigned char *country_code;
        ieee80211_modes_e phy_mode;
 #endif
@@ -5030,6 +5295,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                        g_supplicant_network_get_frequency(supplicant_network));
 
 #if defined TIZEN_EXT
+       keymgmt = g_supplicant_network_get_keymgmt(supplicant_network);
        connman_network_set_bssid(network,
                        g_supplicant_network_get_bssid(supplicant_network));
        owe_transition_mode = (bool)g_supplicant_network_get_transition_mode(supplicant_network);
@@ -5041,14 +5307,18 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                transition_mode_bssid = g_supplicant_network_get_transition_mode_bssid(supplicant_network);
                connman_network_set_transition_mode_bssid(network, transition_mode_bssid);
        }
+
+       sec_list = get_supported_security_list(keymgmt,
+                       owe_transition_mode, supplicant_network);
+
+       connman_network_set_sec_list(network, sec_list);
        connman_network_set_maxrate(network,
                        g_supplicant_network_get_maxrate(supplicant_network));
        connman_network_set_enc_mode(network,
                        g_supplicant_network_get_enc_mode(supplicant_network));
        connman_network_set_rsn_mode(network,
                        g_supplicant_network_get_rsn_mode(supplicant_network));
-       connman_network_set_keymgmt(network,
-                       g_supplicant_network_get_keymgmt(supplicant_network));
+       connman_network_set_keymgmt(network, keymgmt);
        connman_network_set_bool(network, "WiFi.HS20AP",
                        g_supplicant_network_is_hs20AP(supplicant_network));
        connman_network_set_bssid_list(network,
@@ -5233,7 +5503,7 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
                                        calculate_strength(network));
                update_needed = true;
        }
-#if defined TIZEN_EXT
+#if defined TIZEN_EXT && defined TIZEN_EXT_INS
        else if (g_str_equal(property, "LastConnectedBSSID")) {
                const char *ident, *group;
                char *service_ident;
@@ -5256,7 +5526,10 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
                                        g_supplicant_network_get_last_connected_bssid(network));
 
                update_needed = true;
-       } else if (g_str_equal(property, "UpdateAssocReject")) {
+       }
+#endif /* defined TIZEN_EXT && defined TIZEN_EXT_INS */
+#if defined TIZEN_EXT
+       else if (g_str_equal(property, "UpdateAssocReject")) {
                connman_network_set_assoc_reject_table(connman_network,
                                        g_supplicant_network_get_assoc_reject_table(network));
                update_needed = true;
@@ -6129,7 +6402,7 @@ static int tech_set_regdom(struct connman_technology *technology, const char *al
        return g_supplicant_set_country(alpha2, regdom_callback, NULL);
 }
 
-#if defined TIZEN_EXT
+#if defined TIZEN_EXT && defined TIZEN_EXT_INS
 static void supp_ins_init(void)
 {
        const char *string;
@@ -6154,7 +6427,7 @@ static void supp_ins_init(void)
                connman_setting_get_int("INSSignalLevel3_24GHz")
        );
 }
-#endif
+#endif /* defined TIZEN_EXT && defined TIZEN_EXT_INS */
 
 static struct connman_technology_driver tech_driver = {
        .name           = "wifi",
@@ -6190,9 +6463,9 @@ static int wifi_init(void)
        failed_bssids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 #endif
 
-#if defined TIZEN_EXT
+#if defined TIZEN_EXT && defined TIZEN_EXT_INS
        supp_ins_init();
-#endif
+#endif /* defined TIZEN_EXT && defined TIZEN_EXT_INS */
        return 0;
 }