Added background scan start after ssid scan complete
[platform/core/connectivity/net-config.git] / src / wifi-ssid-scan.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <stdio.h>
21
22 #include "log.h"
23 #include "util.h"
24 #include "neterror.h"
25 #include "netdbus.h"
26 #include "netsupplicant.h"
27 #include "wifi-ssid-scan.h"
28 #include "wifi-background-scan.h"
29
30 #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
31 #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
32
33 #define WIFI_KEYMGMT_NONE                       (1 << 0)
34 #define WIFI_KEYMGMT_IEEE8021X          (1 << 1)
35 #define WIFI_KEYMGMT_WPA_NONE           (1 << 2)
36 #define WIFI_KEYMGMT_WPA_PSK            (1 << 3)
37 #define WIFI_KEYMGMT_WPA_PSK_256        (1 << 4)
38 #define WIFI_KEYMGMT_WPA_FT_PSK         (1 << 5)
39 #define WIFI_KEYMGMT_WPA_FT_EAP         (1 << 6)
40 #define WIFI_KEYMGMT_WPA_EAP            (1 << 7)
41 #define WIFI_KEYMGMT_WPA_EAP_256        (1 << 8)
42 #define WIFI_KEYMGMT_WPS                        (1 << 9)
43
44 #define WIFI_PAIRWISE_NONE                      (1 << 0)
45 #define WIFI_PAIRWISE_TKIP                      (1 << 1)
46 #define WIFI_PAIRWISE_CCMP                      (1 << 2)
47
48 typedef struct {
49         const char *str;
50         unsigned int val;
51 } strval_s;
52
53 static strval_s wifi_keymgmt[] = {
54         { "none",                       WIFI_KEYMGMT_NONE },
55         { "ieee8021x",          WIFI_KEYMGMT_IEEE8021X },
56         { "wpa-none",           WIFI_KEYMGMT_WPA_NONE },
57         { "wpa-psk",            WIFI_KEYMGMT_WPA_PSK },
58         { "wpa-psk-sha256",     WIFI_KEYMGMT_WPA_PSK_256 },
59         { "wpa-ft-psk",         WIFI_KEYMGMT_WPA_FT_PSK },
60         { "wpa-ft-eap",         WIFI_KEYMGMT_WPA_FT_EAP },
61         { "wpa-eap",            WIFI_KEYMGMT_WPA_EAP },
62         { "wpa-eap-sha256",     WIFI_KEYMGMT_WPA_EAP_256 },
63         { "wps",                        WIFI_KEYMGMT_WPS },
64         { }
65 };
66
67 static strval_s wifi_pairwise[] = {
68         { "none",                       WIFI_PAIRWISE_NONE },
69         { "tkip",                       WIFI_PAIRWISE_TKIP },
70         { "ccmp",                       WIFI_PAIRWISE_CCMP },
71         { }
72 };
73
74 typedef enum {
75         WIFI_SECURITY_UNKNOWN = 0x00,
76         WIFI_SECURITY_NONE,
77         WIFI_SECURITY_WEP,
78         WIFI_SECURITY_PSK,
79         WIFI_SECURITY_PSK2,
80         WIFI_SECURITY_IEEE8021X,
81 } wifi_security_e;
82
83 typedef struct {
84         unsigned char ssid[33];
85         unsigned char bssid[6];
86         wifi_security_e security;
87         unsigned int wpa_keymgmt;
88         unsigned int wpa_pairwise;
89         unsigned int rsn_keymgmt;
90         unsigned int rsn_pairwise;
91         gboolean rsn_selected;
92         gboolean privacy;
93         gboolean wps;
94 } bss_info_t;
95
96 static gboolean g_ssid_scan_state = FALSE;
97 static GSList *bss_info_list = NULL;
98 static guint ssid_scan_timer = 0;
99 static char *g_ssid = NULL;
100
101 static void __check_keymgmt(const char *str_keymgmt, unsigned int *key_info)
102 {
103         int i;
104
105         for (i = 0; wifi_keymgmt[i].str; i++) {
106                 if (g_strcmp0(str_keymgmt, wifi_keymgmt[i].str) == 0) {
107                         INFO("keymgmt : %s", str_keymgmt);
108                         *key_info |= wifi_keymgmt[i].val;
109                         break;
110                 }
111         }
112 }
113
114 static void __check_pairwise(const char *str_pairwise, unsigned int *pairwise_info)
115 {
116         int i;
117
118         for (i = 0; wifi_pairwise[i].str; i++) {
119                 if (g_strcmp0(str_pairwise, wifi_pairwise[i].str) == 0) {
120                         INFO("pairwise : %s", str_pairwise);
121                         *pairwise_info |= wifi_pairwise[i].val;
122                         break;
123                 }
124         }
125 }
126
127 static wifi_security_e __check_security(bss_info_t *bss_info)
128 {
129         gboolean ieee8021x = FALSE;
130         gboolean psk = FALSE;
131         gboolean ft_ieee8021x = FALSE;
132         gboolean ft_psk = FALSE;
133         unsigned int keymgmt = bss_info->rsn_keymgmt | bss_info->wpa_keymgmt;
134
135         if (keymgmt & (WIFI_KEYMGMT_WPA_EAP | WIFI_KEYMGMT_WPA_EAP_256))
136                 ieee8021x = TRUE;
137         else if (keymgmt & WIFI_KEYMGMT_WPA_FT_EAP)
138                 ft_ieee8021x = TRUE;
139
140         if (keymgmt & (WIFI_KEYMGMT_WPA_PSK | WIFI_KEYMGMT_WPA_PSK_256))
141                 psk = TRUE;
142         else if (keymgmt & WIFI_KEYMGMT_WPA_FT_PSK)
143                 ft_psk = TRUE;
144
145         if (ieee8021x || ft_ieee8021x)
146                 bss_info->security = WIFI_SECURITY_IEEE8021X;
147         else if (psk || ft_psk)
148                 bss_info->security = WIFI_SECURITY_PSK;
149         else if (bss_info->privacy)
150                 bss_info->security = WIFI_SECURITY_WEP;
151         else
152                 bss_info->security = WIFI_SECURITY_NONE;
153
154         if (bss_info->rsn_selected) {
155                 unsigned int pairwise = bss_info->rsn_pairwise | bss_info->wpa_pairwise;
156                 if ((pairwise & WIFI_PAIRWISE_CCMP) ||
157                         (pairwise & (WIFI_PAIRWISE_CCMP | WIFI_PAIRWISE_TKIP)))
158                         bss_info->security = WIFI_SECURITY_PSK2;
159         }
160
161         return bss_info->security;
162 }
163
164 static gboolean __ssid_scan_timeout(gpointer data)
165 {
166         wifi_ssid_scan_emit_scan_completed();
167
168         return FALSE;
169 }
170
171 static void _start_ssid_scan_timer(void)
172 {
173         INFO("Wi-Fi SSID scan started");
174         g_ssid_scan_state = TRUE;
175
176         netconfig_start_timer_seconds(5, __ssid_scan_timeout, NULL, &ssid_scan_timer);
177 }
178
179 static void _stop_ssid_scan_timer(void)
180 {
181         INFO("Wi-Fi SSID scan finished");
182         g_ssid_scan_state = FALSE;
183
184         netconfig_stop_timer(&ssid_scan_timer);
185 }
186
187 static void _parse_wpa_message(GVariant *param, bss_info_t *bss_info)
188 {
189         GVariantIter *iter1;
190         GVariant *var;
191         gchar *key;
192
193         g_variant_get(param, "a{sv}", &iter1);
194         while (g_variant_iter_loop(iter1, "{sv}", &key, &var)) {
195                 if (g_strcmp0(key, "KeyMgmt") == 0) {
196                         GVariantIter *iter2;
197                         g_variant_get(var, "as", &iter2);
198                         char *str;
199                         while (g_variant_iter_loop(iter2, "s", &str)) {
200                                 if (str == NULL)
201                                         break;
202                                 unsigned int key_info = 0;
203                                 __check_keymgmt(str, &key_info);
204                                 if (bss_info->rsn_selected)
205                                         bss_info->rsn_keymgmt = key_info;
206                                 else
207                                         bss_info->wpa_keymgmt = key_info;
208                         }
209                         g_variant_iter_free(iter2);
210                 } else if (g_strcmp0(key, "Pairwise") == 0) {
211                         GVariantIter *iter2;
212                         g_variant_get(var, "as", &iter2);
213                         char *str;
214                         while (g_variant_iter_loop(iter2, "s", &str)) {
215                                 if (str == NULL)
216                                         break;
217                                 unsigned int pairwise_info = 0;
218                                 __check_pairwise(str, &pairwise_info);
219                                 if (bss_info->rsn_selected)
220                                         bss_info->rsn_pairwise = pairwise_info;
221                                 else
222                                         bss_info->wpa_pairwise = pairwise_info;
223                         }
224                         g_variant_iter_free(iter2);
225                 }
226         }
227
228         g_variant_iter_free(iter1);
229
230         return;
231 }
232
233 static gboolean _request_ssid_scan(const char *object_path, const char *ssid)
234 {
235         /* TODO: Revise following code */
236
237         GDBusConnection *connection = NULL;
238         GVariant *reply = NULL;
239         GVariant *params = NULL;
240         GError *error = NULL;
241         GVariantBuilder *builder1 = NULL;
242         GVariantBuilder *builder2 = NULL;
243         GVariantBuilder *builder3 = NULL;
244         const gchar *key1 = "Type";
245         const gchar *val1 = "active";
246         const gchar *key2 = "SSIDs";
247         int i = 0;
248
249         connection = netdbus_get_connection();
250         if (connection == NULL) {
251                 DBG("Failed to get GDBusconnection");
252                 return FALSE;
253         }
254
255         builder1 = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
256         g_variant_builder_add(builder1, "{sv}", key1, g_variant_new_string(val1));
257
258         builder2 = g_variant_builder_new(G_VARIANT_TYPE("aay"));
259         builder3 = g_variant_builder_new(G_VARIANT_TYPE("ay"));
260
261         for (i = 0; i < strlen(ssid); i++)
262                 g_variant_builder_add(builder3, "y", ssid[i]);
263
264         g_variant_builder_add(builder2, "@ay", g_variant_builder_end(builder3));
265         g_variant_builder_add(builder1, "{sv}", key2, g_variant_builder_end(builder2));
266
267         params = g_variant_new("(@a{sv})", g_variant_builder_end(builder1));
268
269         g_variant_builder_unref(builder1);
270         g_variant_builder_unref(builder2);
271         g_variant_builder_unref(builder3);
272
273         reply = g_dbus_connection_call_sync(
274                         connection,
275                         SUPPLICANT_SERVICE,
276                         object_path,
277                         SUPPLICANT_INTERFACE ".Interface",
278                         "Scan",
279                         params,
280                         NULL,
281                         G_DBUS_CALL_FLAGS_NONE,
282                         NETCONFIG_DBUS_REPLY_TIMEOUT,
283                         netdbus_get_cancellable(),
284                         &error);
285
286         if (reply == NULL) {
287                 if (error != NULL) {
288                         ERR("Error!!! dbus_connection_send_with_reply_and_block() failed. "
289                                         "DBus error [%d: %s]", error->code, error->message);
290                         g_error_free(error);
291                 } else
292                         ERR("Error!!! Failed to get properties");
293
294                 return FALSE;
295         }
296
297         if (g_ssid != NULL)
298                 g_free(g_ssid);
299
300         g_ssid = g_strdup(ssid);
301
302         g_variant_unref(reply);
303
304         return TRUE;
305 }
306
307 static void _emit_ssid_scan_completed(void)
308 {
309         GVariantBuilder *builder = NULL;
310         GSList* list = NULL;
311         const char *prop_ssid = "ssid";
312         const char *prop_bssid = "bssid";
313         char bssid_buf[18] = {0,};
314         char *bssid_str = bssid_buf;
315         const char *prop_security = "security";
316         const char *prop_wps = "wps";
317
318         builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
319         for (list = bss_info_list; list != NULL; list = list->next) {
320                 bss_info_t *bss_info = (bss_info_t *)list->data;
321                 if (bss_info && g_strcmp0((char *)bss_info->ssid, g_ssid) == 0) {
322                         const gchar *ssid = (char *)bss_info->ssid;
323                         const gchar *bssid = (gchar *)&bss_info->bssid[0];
324                         wifi_security_e security = __check_security(bss_info);
325                         gboolean wps = bss_info->wps;
326                         DBG("BSS found; SSID:%s security:%d WPS:%d", ssid, security, wps);
327
328                         if (bssid) {
329                                 snprintf(bssid_str, sizeof(bssid_buf), MACSTR, MAC2STR(bssid));
330                                 DBG("BSSID: %s", bssid_str);
331                         }
332                         g_variant_builder_add(builder, "{sv}", prop_ssid, g_variant_new_string(ssid));
333                         g_variant_builder_add(builder, "{sv}", prop_bssid,
334                                                 g_variant_new_string(bssid));
335                         g_variant_builder_add(builder, "{sv}", prop_security, g_variant_new_int32(security));
336                         /* WPS */
337                         g_variant_builder_add(builder, "{sv}", prop_wps, g_variant_new_boolean(wps));
338                 }
339         }
340
341         wifi_emit_specific_scan_completed((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
342
343         if (builder)
344                 g_variant_builder_unref(builder);
345
346         if (bss_info_list != NULL) {
347                 g_slist_free_full(bss_info_list, g_free);
348                 bss_info_list = NULL;
349         }
350
351         if (g_ssid != NULL) {
352                 g_free(g_ssid);
353                 g_ssid = NULL;
354         }
355
356         INFO("SpecificScanCompleted");
357
358         return;
359 }
360
361 gboolean wifi_ssid_scan(const char *ssid)
362 {
363         const char *if_path;
364         static char *scan_ssid = NULL;
365
366         netconfig_wifi_bgscan_stop();
367
368         if (ssid != NULL) {
369                 if (scan_ssid != NULL)
370                         g_free(scan_ssid);
371                 scan_ssid = g_strdup(ssid);
372         }
373
374         if (scan_ssid == NULL)
375                 goto error;
376
377         if_path = netconfig_wifi_get_supplicant_interface();
378         if (if_path == NULL) {
379                 DBG("Fail to get wpa_supplicant DBus path");
380                 goto error;
381         }
382
383         if (netconfig_wifi_get_scanning() == TRUE) {
384                 DBG("Wi-Fi scan in progress, %s scan will be delayed", scan_ssid);
385                 return TRUE;
386         }
387
388         if (bss_info_list) {
389                 g_slist_free_full(bss_info_list, g_free);
390                 bss_info_list = NULL;
391         }
392
393         INFO("Start Wi-Fi scan with %s(%d)", scan_ssid, strlen(scan_ssid));
394         if (_request_ssid_scan(if_path, (const char *)scan_ssid) == TRUE) {
395                 _start_ssid_scan_timer();
396                 g_free(scan_ssid);
397                 scan_ssid = NULL;
398                 return TRUE;
399         }
400
401 error:
402         if (scan_ssid != NULL) {
403                 g_free(scan_ssid);
404                 scan_ssid = NULL;
405         }
406
407         netconfig_wifi_bgscan_start(FALSE);
408
409         return FALSE;
410 }
411
412 gboolean wifi_ssid_scan_get_state(void)
413 {
414         return g_ssid_scan_state;
415 }
416
417 void wifi_ssid_scan_emit_scan_completed(void)
418 {
419         if (g_ssid_scan_state != TRUE)
420                 return;
421
422         _stop_ssid_scan_timer();
423         _emit_ssid_scan_completed();
424         netconfig_wifi_bgscan_start(FALSE);
425 }
426
427 void wifi_ssid_scan_add_bss(GVariant *message)
428 {
429         GVariantIter *iter;
430         GVariant *value;
431         gchar *path = NULL;
432         gchar *key;
433         bss_info_t *bss_info;
434
435         if (g_ssid_scan_state != TRUE)
436                 return;
437
438         INFO("NEW BSS added");
439
440         if (message == NULL) {
441                 DBG("Message does not have parameters");
442                 return;
443         }
444
445         if (path != NULL)
446                 INFO("Object path of BSS added is %s", path);
447
448         bss_info = g_try_new0(bss_info_t, 1);
449         if (bss_info == NULL)
450                 return;
451
452         g_variant_get(message, "(oa{sv})", &path, &iter);
453         while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
454                 if (g_strcmp0(key, "SSID") == 0) {
455                         const guchar *ssid;
456                         gsize ssid_len;
457                         ssid = g_variant_get_fixed_array(value, &ssid_len, sizeof(guchar));
458                         if (ssid != NULL && ssid_len > 0 && ssid_len < 33)
459                                 memcpy(bss_info->ssid, ssid, ssid_len);
460                         else
461                                 memset(bss_info->ssid, 0, sizeof(bss_info->ssid));
462                 } else if (g_strcmp0(key, "Privacy") == 0) {
463                         gboolean privacy = FALSE;
464                         privacy = g_variant_get_boolean(value);
465                         bss_info->privacy = privacy;
466                 } else if (g_strcmp0(key, "RSN") == 0) {
467                         bss_info->rsn_selected = TRUE;
468                         _parse_wpa_message(value, bss_info);
469                 } else if (g_strcmp0(key, "WPA") == 0) {
470                         bss_info->rsn_selected = FALSE;
471                         _parse_wpa_message(value, bss_info);
472                 } else if (g_strcmp0(key, "IEs") == 0) {
473                         const guchar *ie;
474                         gsize ie_len;
475                         ie = g_variant_get_fixed_array(value, &ie_len, sizeof(guchar));
476                         DBG("The IE : %s", ie);
477                 } else if (g_strcmp0(key, "BSSID") == 0) {
478                         const guchar *bssid;
479                         gsize bssid_len;
480
481                         bssid = g_variant_get_fixed_array(value, &bssid_len, sizeof(guchar));
482                         if (bssid != NULL && bssid_len == 6)
483                                 memcpy(bss_info->bssid, bssid, bssid_len);
484                 }
485         }
486
487         g_variant_iter_free(iter);
488         if (path)
489                 g_free(path);
490
491         if (bss_info->ssid[0] == '\0') {
492                 g_free(bss_info);
493                 return;
494         }
495
496         if (bss_info->security == WIFI_SECURITY_UNKNOWN) {
497                 if (bss_info->privacy == TRUE)
498                         bss_info->security = WIFI_SECURITY_WEP;
499                 else
500                         bss_info->security = WIFI_SECURITY_NONE;
501         }
502
503         bss_info_list = g_slist_append(bss_info_list, bss_info);
504 }
505
506 gboolean handle_request_specific_scan(Wifi *wifi,
507                 GDBusMethodInvocation *context, const gchar *ssid)
508 {
509         gboolean result = FALSE;
510
511         g_return_val_if_fail(wifi != NULL, FALSE);
512         g_return_val_if_fail(ssid != NULL, FALSE);
513
514         result = wifi_ssid_scan((const char *)ssid);
515
516         if (result != TRUE)
517                 netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "FailSpecificScan");
518         else
519                 wifi_complete_request_specific_scan(wifi, context);
520
521         return result;
522 }