Merge "Revise wifi statistics routine" into tizen
[platform/core/connectivity/net-config.git] / src / wifi-netlink-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 <glib.h>
21
22 #include "netsupplicant.h"
23 #include "log.h"
24 #include "util.h"
25 #include "wifi-config.h"
26 #include "wifi-netlink-scan.h"
27 #include <netlink/genl/genl.h>
28 #include <netlink/genl/family.h>
29 #include <netlink/genl/ctrl.h>
30 #include <netlink/msg.h>
31 #include <netlink/attr.h>
32 #include <netlink/netlink.h>
33 #include <ctype.h>
34
35 static GSList *bss_info_list = NULL;
36
37 static gint __netconfig_compare_bss_by_rssi(gconstpointer a, gconstpointer b)
38 {
39         struct bss_scan_info_t *entry_a = (struct bss_scan_info_t *) a;
40         struct bss_scan_info_t *entry_b = (struct bss_scan_info_t *) b;
41
42         if (entry_a->signal > entry_b->signal)
43                 return -1;
44
45         if (entry_a->signal < entry_b->signal)
46                 return 1;
47
48         return 0;
49 }
50
51 void __netconfig_notify_netlink_scan_done(const char *interface_name)
52 {
53         GVariantBuilder *builder = NULL;
54         GVariantBuilder *builder1 = NULL;
55         GVariantBuilder *builder2 = NULL;
56         GSList* list = NULL;
57         const char *prop_ssid = "ssid";
58         const char *prop_bssid = "bssid";
59         const char *prop_freq = "freq";
60         const char *prop_rssi = "rssi";
61         const char *prop_vsie_list = "vsie_list";
62         const char *prop_sec = "security";
63         const char *prop_enc = "encryption";
64
65         bss_info_list = g_slist_sort(bss_info_list, __netconfig_compare_bss_by_rssi);
66
67         builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
68         for (list = bss_info_list; list != NULL; list = list->next) {
69                 struct bss_scan_info_t *bss_info = (struct bss_scan_info_t *)list->data;
70
71                 if (bss_info) {
72                         char *bssid = (char *)bss_info->bssid;
73                         char *ssid = (char *)bss_info->ssid;
74                         GSList *vsie_list = bss_info->vsie_list;
75                         int freq = (int)bss_info->freq;
76                         int signal = (int)bss_info->signal;
77                         int sec_type = (int)bss_info->security_type;
78                         int enc_type = (int)bss_info->encryption_type;
79
80                         g_variant_builder_add(builder, "{sv}", prop_ssid, g_variant_new_string(ssid));
81                         g_variant_builder_add(builder, "{sv}", prop_bssid, g_variant_new_string(bssid));
82                         g_variant_builder_add(builder, "{sv}", prop_freq, g_variant_new_int32(freq));
83                         g_variant_builder_add(builder, "{sv}", prop_rssi, g_variant_new_int32(signal));
84                         builder1 = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
85                         GSList *list;
86                         unsigned char *net_vsie;
87                         unsigned int net_vsie_len;
88                         int count;
89                         for (list = vsie_list; list; list = list->next) {
90                                 builder2 = g_variant_builder_new(G_VARIANT_TYPE("ay"));
91                                 net_vsie = (unsigned char *)list->data;
92                                 net_vsie_len = net_vsie[1] + 2;
93
94                                 for (count = 0; count < net_vsie_len; count++) {
95                                         g_variant_builder_add(builder2, "y", net_vsie[count]);
96                                 }
97
98                                 g_variant_builder_add(builder1, "{sv}", "Vsie", g_variant_builder_end(builder2));
99                                 g_variant_builder_unref(builder2);
100                         }
101                         g_variant_builder_add(builder, "{sv}", prop_vsie_list, g_variant_builder_end(builder1));
102                         g_variant_builder_unref(builder1);
103
104                         if (vsie_list != NULL)
105                                 g_slist_free_full(vsie_list, g_free);
106
107                         vsie_list = NULL;
108
109                         g_variant_builder_add(builder, "{sv}", prop_sec, g_variant_new_int32(sec_type));
110                         g_variant_builder_add(builder, "{sv}", prop_enc, g_variant_new_int32(enc_type));
111                 }
112         }
113
114         wifi_emit_netlink_scan_completed((Wifi *)get_wifi_object(),
115                 interface_name, g_variant_builder_end(builder));
116         g_variant_builder_unref(builder);
117
118         if (bss_info_list != NULL)
119                 g_slist_free_full(bss_info_list, g_free);
120
121         bss_info_list = NULL;
122         INFO("NetlinkScanCompleted");
123
124         return;
125 }
126
127 static int ack_handler(struct nl_msg *msg, void *user_data)
128 {
129         int *err = user_data;
130         *err = 0;
131         DBG("");
132         return NL_STOP;
133 }
134
135 static int finish_handler(struct nl_msg *msg, void *user_data)
136 {
137         int *ret = user_data;
138         *ret = 0;
139         return NL_SKIP;
140 }
141
142 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
143                          void *user_data)
144 {
145         int *ret = user_data;
146         *ret = err->error;
147         return NL_SKIP;
148 }
149
150 static int no_seq_check(struct nl_msg *msg, void *user_data)
151 {
152         DBG("");
153         return NL_OK;
154 }
155
156 static int __netconfig_family_handler(struct nl_msg *msg, void *user_data)
157 {
158         /** Callback for NL_CB_VALID in multicast group */
159         struct netconfig_netlink_scan_handler_args *grp = user_data;
160         struct nlattr *tb[CTRL_ATTR_MAX + 1];
161         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
162         struct nlattr *mc_grp;
163         int rem_mc_grp;
164
165         nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
166
167         if (!tb[CTRL_ATTR_MCAST_GROUPS])
168                 return NL_SKIP;
169
170         nla_for_each_nested(mc_grp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mc_grp) {
171                 struct nlattr *tb_mc_grp[CTRL_ATTR_MCAST_GRP_MAX + 1];
172
173                 nla_parse(tb_mc_grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mc_grp), nla_len(mc_grp), NULL);
174
175                 if (!tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID])
176                         continue;
177                 if (strncmp(nla_data(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME]), grp->group,
178                                         nla_len(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME])))
179                         continue;
180
181                 grp->id = nla_get_u32(tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID]);
182                 break;
183         }
184
185         return NL_SKIP;
186 }
187
188 static int __netconfig_get_multicast_id(struct nl_sock *socket, const char *family, const char *group)
189 {
190         struct nl_msg *msg = NULL;
191         struct nl_cb *cb = NULL;
192         int ret, ctrl_id;
193         struct netconfig_netlink_scan_handler_args grp = { .group = group, .id = -ENOENT, };
194
195         msg = nlmsg_alloc();
196         if (!msg)
197                 return -ENOMEM;
198
199         cb = nl_cb_alloc(NL_CB_DEFAULT);
200         if (!cb) {
201                 ret = -ENOMEM;
202                 goto cb_fail;
203         }
204
205         ctrl_id = genl_ctrl_resolve(socket, "nlctrl");
206
207         genlmsg_put(msg, 0, 0, ctrl_id, 0, 0, CTRL_CMD_GETFAMILY, 0);
208
209         ret = -ENOBUFS;
210         NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
211
212         ret = nl_send_auto_complete(socket, msg);
213         if (ret < 0)
214                 goto out;
215
216         ret = 1;
217
218         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
219         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
220         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_family_handler, &grp);
221
222         while (ret > 0)
223                 nl_recvmsgs(socket, cb);
224
225         if (ret == 0)
226                 ret = grp.id;
227
228 nla_put_failure:
229 out:
230         nl_cb_put(cb);
231 cb_fail:
232         nlmsg_free(msg);
233         return ret;
234 }
235
236 static void __netconfig_macaddress_str(char *bssid, unsigned char *user_data)
237 {
238         int i;
239
240         for (i = 0; i < 6; i++) {
241                 if (i == 0) {
242                         snprintf(bssid, 3, "%02x", user_data[i]);
243                         bssid += 2;
244                 } else {
245                         snprintf(bssid, 4, ":%02x", user_data[i]);
246                         bssid += 3;
247                 }
248         }
249 }
250
251 typedef enum {
252         WIFI_SECURITY_TYPE_NONE = 0,
253         WIFI_SECURITY_TYPE_WEP = 1,
254         WIFI_SECURITY_TYPE_WPA_PSK = 2,
255         WIFI_SECURITY_TYPE_WPA2_PSK = 3,
256         WIFI_SECURITY_TYPE_EAP = 4,
257 } wifi_security_type_e;
258
259 typedef enum {
260         WIFI_ENCRYPTION_TYPE_NONE = 0,
261         WIFI_ENCRYPTION_TYPE_WEP = 1,
262         WIFI_ENCRYPTION_TYPE_TKIP = 2,
263         WIFI_ENCRYPTION_TYPE_AES = 3,
264         WIFI_ENCRYPTION_TYPE_TKIP_AES_MIXED = 4,
265 } wifi_encryption_type_e;
266
267 static unsigned char ms_oui[3]      = { 0x00, 0x50, 0xf2 };
268 static unsigned char ieee80211_oui[3]   = { 0x00, 0x0f, 0xac };
269
270 static void __netconfig_get_security(unsigned char *bss_element, int length, wifi_security_type_e *sec_type, wifi_encryption_type_e *enc_type)
271 {
272         int i;
273         unsigned char *data;
274         uint8_t *t_data;
275         int len;
276         __u16 count;
277
278         *sec_type = WIFI_SECURITY_TYPE_NONE;
279         *enc_type = WIFI_ENCRYPTION_TYPE_NONE;
280
281         while (length >= 2 && length >= bss_element[1]) {
282                 if (bss_element[0] == 221 && (bss_element[1] >= 4 && memcmp(bss_element + 2, ms_oui, 3) == 0)) {
283                         data = bss_element + 2 + 4 + 2 + 4;
284                         len = bss_element[1] - 4 - 2 - 4;
285
286                         if (len < 2) {
287                                 length -= bss_element[1] + 2;
288                                 bss_element += bss_element[1] + 2;
289                                 continue;
290                         }
291
292                         count = data[0] | (data[1] << 8);
293
294                         if (2 + (count * 4) > len) {
295                                 length -= bss_element[1] + 2;
296                                 bss_element += bss_element[1] + 2;
297                                 continue;
298                         }
299
300                         for (i = 0; i < count; i++) {
301                                 t_data = (data + 2 + (i * 4));
302
303                                 if ((memcmp(t_data, ms_oui, 3) == 0) || (memcmp(t_data, ieee80211_oui, 3) == 0)) {
304                                         if (t_data[3] == 1 || t_data[3] == 5) { // 1 : WEP-40, 5 : WEP-104
305                                                 *sec_type = WIFI_SECURITY_TYPE_WEP;
306                                                 *enc_type = WIFI_ENCRYPTION_TYPE_WEP;
307                                                 return;
308                                         } else if (t_data[3] == 2) { // 2 : TKIP
309                                                 if (*sec_type != WIFI_SECURITY_TYPE_WPA2_PSK)
310                                                         *sec_type = WIFI_SECURITY_TYPE_WPA_PSK;
311                                                 if (*enc_type == WIFI_ENCRYPTION_TYPE_AES)
312                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP_AES_MIXED;
313                                                 else
314                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP;
315                                         } else if (t_data[3] == 4) { // 3 : CCMP
316                                                 if (*sec_type != WIFI_SECURITY_TYPE_WPA2_PSK)
317                                                         *sec_type = WIFI_SECURITY_TYPE_WPA_PSK;
318                                                 if (*enc_type == WIFI_ENCRYPTION_TYPE_TKIP)
319                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP_AES_MIXED;
320                                                 else
321                                                         *enc_type = WIFI_ENCRYPTION_TYPE_AES;
322                                         }
323                                 } //if
324                         } //for
325
326                         data += 2 + (count * 4);
327                         len -= 2 + (count * 4);
328
329                         if (len < 2) {
330                                 length -= bss_element[1] + 2;
331                                 bss_element += bss_element[1] + 2;
332                                 continue;
333                         }
334
335                         count = data[0] | (data[1] << 8);
336
337                         if (2 + (count * 4) > len) {
338                                 length -= bss_element[1] + 2;
339                                 bss_element += bss_element[1] + 2;
340                                 continue;
341                         }
342
343                         for (i = 0; i < count; i++) {
344                                 t_data = (data + 2 + (i * 4));
345
346                                 if ((memcmp(t_data, ms_oui, 3) == 0) || (memcmp(t_data, ieee80211_oui, 3) == 0)) {
347                                         if (t_data[3] == 1 || t_data[3] == 3 || t_data[3] == 5) { // 1 : IEEE 802.1X, 3 : FT/IEEE 802.1X, 5 : IEEE 802.1X/SHA-256
348                                                 *sec_type = WIFI_SECURITY_TYPE_EAP;
349                                         } else if (t_data[3] == 2 || t_data[3] == 4 || t_data[3] == 6) {  // 2 : PSK, 4 : FT/PSK, 6 : PSK/SHA-256
350                                                 if (*sec_type != WIFI_SECURITY_TYPE_WPA2_PSK)
351                                                         *sec_type = WIFI_SECURITY_TYPE_WPA_PSK;
352                                         }
353                                 }
354                         }
355                 } else if (bss_element[0] == 48 && (bss_element[1] >= 2 && bss_element[1] <= 255)) {
356                         data = bss_element + 2 + 2 + 4;
357                         len = bss_element[1] - 2 - 4;
358
359                         if (len < 2) {
360                                 length -= bss_element[1] + 2;
361                                 bss_element += bss_element[1] + 2;
362                                 continue;
363                         }
364
365                         count = data[0] | (data[1] << 8);
366                         if (2 + (count + 4) > len) {
367                                 length -= bss_element[1] + 2;
368                                 bss_element += bss_element[1] + 2;
369                                 continue;
370                         }
371
372                         for (i = 0; i < count; i++) {
373                                 t_data = (data + 2 + (i * 4));
374
375                                 if ((memcmp(t_data, ms_oui, 3) == 0) || (memcmp(t_data, ieee80211_oui, 3) == 0)) {
376                                         if (t_data[3] == 1 || t_data[3] == 5) { // 1 : WEP-40, 5 : WEP-104
377                                                 *sec_type = WIFI_SECURITY_TYPE_WEP;
378                                                 *enc_type = WIFI_ENCRYPTION_TYPE_WEP;
379                                                 return;
380                                         } else if (t_data[3] == 2) { // 2 : TKIP
381                                                 *sec_type = WIFI_SECURITY_TYPE_WPA2_PSK;
382                                                 if (*enc_type == WIFI_ENCRYPTION_TYPE_AES)
383                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP_AES_MIXED;
384                                                 else
385                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP;
386                                         } else if (t_data[3] == 4) { // 3 : CCMP
387                                                 *sec_type = WIFI_SECURITY_TYPE_WPA2_PSK;
388                                                 if (*enc_type == WIFI_ENCRYPTION_TYPE_TKIP)
389                                                         *enc_type = WIFI_ENCRYPTION_TYPE_TKIP_AES_MIXED;
390                                                 else
391                                                         *enc_type = WIFI_ENCRYPTION_TYPE_AES;
392                                         }
393                                 }
394                         }
395
396                         data += 2 + (count * 4);
397                         len -= 2 + (count * 4);
398
399                         if (len < 2) {
400                                 length -= bss_element[1] + 2;
401                                 bss_element += bss_element[1] + 2;
402                                 continue;
403                         }
404
405                         count = data[0] | (data[1] << 8);
406                         if (2 + (count * 4) > len) {
407                                 length -= bss_element[1] + 2;
408                                 bss_element += bss_element[1] + 2;
409                                 continue;
410                         }
411
412                         for (i = 0; i < count; i++) {
413                                 t_data = (data + 2 + (i * 4));
414
415                                 if ((memcmp(t_data, ms_oui, 3) == 0) || (memcmp(t_data, ieee80211_oui, 3) == 0)) {
416                                         if (t_data[3] == 1 || t_data[3] == 3 || t_data[3] == 5) { // 1 : IEEE 802.1X, 3 : FT/IEEE 802.1X, 5 : IEEE 802.1X/SHA-256
417                                                 *sec_type = WIFI_SECURITY_TYPE_EAP;
418                                         } else if (t_data[3] == 2 || t_data[3] == 4 || t_data[3] == 6) {  // 2 : PSK, 4 : FT/PSK, 6 : PSK/SHA-256
419                                                 *sec_type = WIFI_SECURITY_TYPE_WPA2_PSK;
420                                         }
421                                 }
422                         } //for
423                 } //else if
424
425                 length -= bss_element[1] + 2;
426                 bss_element += bss_element[1] + 2;
427         } //while
428
429         return;
430 }
431
432 static void __netconfig_get_vsie(unsigned char *bss_element, int length, GSList **dst)
433 {
434         unsigned char *vsie;
435         int vsie_len = 0;
436
437         if (length < 3) {
438                 DBG("Vendor specific data not available");
439                 return;
440         }
441
442         while (length >= 2 && length >= bss_element[1]) {
443                 if (bss_element[0] == NETCONFIG_VENDOR_SPECIFIC_ID) {
444                         vsie_len = bss_element[1]+2;
445                         vsie = (unsigned char *)g_try_malloc0(vsie_len);
446
447                         if (vsie) {
448                                 memcpy(vsie, bss_element, vsie_len);
449                                 *dst = g_slist_append(*dst, vsie);
450                         } else
451                                 DBG("Failed to allocate memory");
452                 }
453
454                 length -= bss_element[1] + 2;
455                 bss_element += bss_element[1] + 2;
456         }
457 }
458
459 static void __netconfig_found_ap(unsigned char *bss_element, int length, char *str)
460 {
461         uint8_t len;
462         uint8_t *data;
463         int i;
464
465         while (length >= 2 && length >= bss_element[1]) {
466                 if (bss_element[0] == 0 && bss_element[1] <= 32) {
467                         len = bss_element[1];
468                         data = bss_element + 2;
469                         for (i = 0; i < len; i++) {
470                                 if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\')
471                                         snprintf(&str[i], 2, "%c", data[i]);
472                                 else if (data[i] == ' ' && (i != 0 && i != len -1))
473                                         snprintf(&str[i], 2, "%c", ' ');
474                                 else
475                                         snprintf(&str[i], 2, "%c", data[i]);
476                         }
477                         break;
478                 }
479                 length -= bss_element[1] + 2;
480                 bss_element += bss_element[1] + 2;
481         }
482 }
483
484 static int __netconfig_netlink_scan_cb(struct nl_msg *msg, void *user_data)
485 {
486         /** Called by the kernel with a dump of the successful scan's data. Called for each SSID. */
487         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
488         char bssid[NETCONFIG_BSSID_LEN+1];
489         char ssid[NETCONFIG_SSID_LEN+1] = {0, };
490         wifi_security_type_e sec_type = WIFI_SECURITY_TYPE_NONE;
491         wifi_encryption_type_e enc_type = WIFI_ENCRYPTION_TYPE_NONE;
492         int wep_check = 0;
493         GSList *vsie = NULL;
494         struct nlattr *tb[NL80211_ATTR_MAX + 1];
495         struct nlattr *bss[NL80211_BSS_MAX + 1];
496         struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
497                 [NL80211_BSS_FREQUENCY] = {.type = NLA_U32},
498                 [NL80211_BSS_BSSID] = { },
499                 [NL80211_BSS_CAPABILITY] = {.type = NLA_U16},
500                 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
501                 [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32},
502         };
503
504         /** Parse nl message and check error. */
505         nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
506         if (!tb[NL80211_ATTR_BSS]) {
507                 DBG("BSS info not available");
508                 return NL_SKIP;
509         }
510         if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
511                 DBG("Failed to parse nested attributes");
512                 return NL_SKIP;
513         }
514         if ((!bss[NL80211_BSS_BSSID]) || (!bss[NL80211_BSS_INFORMATION_ELEMENTS]))
515                 return NL_SKIP;
516
517         /** Extract BSSID and AP info. */
518         __netconfig_macaddress_str(bssid, nla_data(bss[NL80211_BSS_BSSID]));
519         __netconfig_found_ap(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), ssid);
520         __netconfig_get_vsie(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), &vsie);
521
522         if (bss[NL80211_BSS_CAPABILITY]) {
523                 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
524                 if (capa & WLAN_CAPABILITY_PRIVACY) {
525                         wep_check = 1;
526                 }
527         }
528         __netconfig_get_security(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
529                         nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), &sec_type, &enc_type);
530
531         if (wep_check && sec_type == WIFI_SECURITY_TYPE_NONE) {
532                 sec_type = WIFI_SECURITY_TYPE_WEP;
533                 enc_type = WIFI_ENCRYPTION_TYPE_WEP;
534         }
535
536         if (sec_type == WIFI_SECURITY_TYPE_EAP) {
537                 g_slist_free_full(vsie, g_free);
538                 return NL_SKIP;
539         }
540         for (GSList *list = bss_info_list; list != NULL; list = list->next) {
541                 struct bss_scan_info_t *bss_info = (struct bss_scan_info_t *)list->data;
542                 if ((g_strcmp0(bss_info->ssid, ssid) == 0) && (bss_info->security_type == sec_type)
543                                 && (bss_info->encryption_type == enc_type)) {
544                         g_slist_free_full(vsie, g_free);
545                         return NL_SKIP;
546                 }
547         }
548
549         /** Create AP info list. */
550         if (ssid[0] != '\0') {
551                 struct bss_scan_info_t *bss_info;
552                 int signal;
553
554                 bss_info = g_try_new0(struct bss_scan_info_t, 1);
555                 if (bss_info == NULL) {
556                         g_slist_free_full(vsie, g_free);
557                         return NL_SKIP;
558                 }
559
560                 g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
561                 g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
562                 bss_info->vsie_list = vsie;
563                 bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
564
565                 if (bss[NL80211_BSS_SIGNAL_MBM]) {
566                         signal = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
567                         signal /= 100; /** mBm to dBm */
568                         bss_info->signal = signal;
569                 }
570
571                 bss_info->security_type = sec_type;
572                 bss_info->encryption_type = enc_type;
573                 DBG("%s %d %d %s %d %d ", bss_info->bssid, bss_info->freq,
574                                 bss_info->signal, bss_info->ssid, bss_info->security_type,
575                                 bss_info->encryption_type);
576
577                 if (bss_info->ssid[0] == '\0')
578                         g_free(bss_info);
579                 else
580                         bss_info_list = g_slist_append(bss_info_list, bss_info);
581
582         } else
583                 g_slist_free_full(vsie, g_free);
584
585         return NL_SKIP;
586 }
587
588 static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
589 {
590         /** Called by the kernel when the scan is done or has been aborted. */
591         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
592         struct netconfig_netlink_scan_results *results = user_data;
593
594         if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
595                 DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
596                 results->done = 1;
597                 results->aborted = 0;
598         } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
599                 DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
600                 results->done = 1;
601                 results->aborted = 1;
602         }
603
604         return NL_SKIP;
605 }
606
607 static int __netconfig_trigger_netlink_scan(struct nl_sock *socket,
608                 int if_index, int id, GSList *ssid_list, char *vsie)
609 {
610         struct netconfig_netlink_scan_results results = { .done = 0, .aborted = 0 };
611         struct nl_msg *msg = NULL;
612         struct nl_cb *cb = NULL;
613         struct nl_msg *ssids = NULL;
614         int err = 0;
615         int ret = 0;
616         unsigned char ies[NETCONFIG_MAX_VSIE_LEN+1] = {0x00, };
617         int ies_len = 0;
618         int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
619
620         DBG("");
621         ret = nl_socket_add_membership(socket, mcid);
622         if (ret < 0) {
623                 DBG("Failed to add membership, error: (%s)", nl_geterror(-ret));
624                 return ret;
625         }
626
627         msg = nlmsg_alloc();
628         if (!msg) {
629                 DBG("Failed to allocate msg");
630                 nl_socket_drop_membership(socket, mcid);
631                 return -ENOMEM;
632         }
633         ssids = nlmsg_alloc();
634         if (!ssids) {
635                 DBG("Failed to allocate ssids");
636                 nlmsg_free(msg);
637                 nl_socket_drop_membership(socket, mcid);
638                 return -ENOMEM;
639         }
640         cb = nl_cb_alloc(NL_CB_DEFAULT);
641         if (!cb) {
642                 DBG("Failed to allocate callbacks");
643                 nlmsg_free(msg);
644                 nlmsg_free(ssids);
645                 nl_socket_drop_membership(socket, mcid);
646                 return -ENOMEM;
647         }
648
649         /** Set nl message and callback functions. */
650         genlmsg_put(msg, 0, 0, id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
651         ret = nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
652         if (ret < 0) {
653                 DBG("Failed to add integer attribute to netlink message, error: (%s)", nl_geterror(-ret));
654                 goto out;
655         }
656
657         for (GSList *i = ssid_list; i; i = i->next) {
658                 char *ssid = (char *)i->data;
659                 ret = nla_put(ssids, 1, strlen(ssid), ssid);
660                 if (ret < 0) {
661                         DBG("Failed to add ssid to netlink message, error: (%s)", nl_geterror(-ret));
662                         goto out;
663                 }
664         }
665
666         if (g_slist_length(ssid_list) == 0) {
667                 DBG("Scan with no ssids");
668                 ret = nla_put(ssids, 1, 0, "");
669                 if (ret < 0) {
670                         DBG("nla_put error: (%s)", nl_geterror(-ret));
671                         goto out;
672                 }
673         }
674         nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
675
676         if (vsie) {
677                 int vsie_len = strlen(vsie);
678                 DBG("vsie: %s vsie_len: %d", vsie, vsie_len);
679                 ies_len = (vsie_len % 2) ? ((vsie_len / 2) + 1) : (vsie_len / 2);
680                 __netconfig_hex_str_to_bin(vsie, ies, ies_len);
681         }
682
683         if (ies[0] == NETCONFIG_VENDOR_SPECIFIC_ID && ies[1] >= 4) {
684                 DBG("ies_len: %d ies: %02x %02x %02x %02x %02x %02x %02x", ies_len,
685                                 ies[0], ies[1], ies[2], ies[3], ies[4], ies[5], ies[6]);
686                 ret = nla_put(msg, NL80211_ATTR_IE, ies_len, ies);
687                 if (ret < 0) {
688                         DBG("Failed to add vsie data to netlink message, error: (%s)", nl_geterror(-ret));
689                         goto out;
690                 }
691         }
692
693         err = 1;
694         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_reply, &results);
695         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
696         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
697         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
698         nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
699         ret = nl_socket_set_nonblocking(socket);
700         DBG("Set the non-blocking mode [%d]", ret);
701
702         /** Send NL80211_CMD_TRIGGER_SCAN to start the scan. */
703         ret = nl_send_auto_complete(socket, msg);
704
705         if (ret < 0) {
706                 DBG("nl_send_auto_complete() error: (%s)", nl_geterror(-ret));
707                 goto out;
708         }
709
710         DBG("Sent %d bytes to the kernel", ret);
711
712         while (err > 0) {
713                 ret = nl_recvmsgs(socket, cb);
714                 if (ret < 0) {
715                         DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
716                         goto out;
717                 }
718         }
719
720         while (!results.done) {
721                 ret = nl_recvmsgs(socket, cb);
722 #if !defined TIZEN_WEARABLE
723                 if (ret == -NLE_AGAIN && err >= 0)
724                         continue;
725 #endif
726                 if (ret < 0) {
727                         DBG("scan failed");
728                         goto out;
729                 }
730                 if (err < 0) {
731                         DBG("scan failed err: %d", err);
732                         ret = -1;
733                         goto out;
734                 }
735         }
736
737         if (results.aborted) {
738                 DBG("scan aborted");
739                 ret = -1;
740                 goto out;
741         }
742
743 out:
744         /** Release memory */
745         nlmsg_free(ssids);
746         nlmsg_free(msg);
747         nl_cb_put(cb);
748         nl_socket_drop_membership(socket, mcid);
749
750         if (ret < 0 || ret == 1)
751                 return ret;
752
753         DBG("Scan done");
754         return 0;
755 }
756
757 static int __netconfig_netlink_scan(netconfig_nl_global *global, GSList *ssid_list, char *vsie)
758 {
759         DBG("");
760         int ret = __netconfig_trigger_netlink_scan(global->socket, global->if_index, global->id, ssid_list, vsie);
761         if (ret == -NLE_AGAIN) {
762                 DBG("Try Again");
763                 ret = __netconfig_trigger_netlink_scan(global->socket, global->if_index, global->id, ssid_list, vsie);
764         }
765         return ret;
766 }
767
768 static int __netconfig_initialize_nl80211(netconfig_nl_global *global,
769                 const char *interface_name)
770 {
771         int err = 0;
772
773         global->if_index = __netconfig_get_interface_index(interface_name);
774         if (global->if_index < 0) {
775                 DBG("Failed to get interface index");
776                 return -1;
777         }
778
779         global->socket = nl_socket_alloc();
780         if (!global->socket) {
781                 DBG("Failed to allocate netlink socket.");
782                 return -ENOMEM;
783         }
784
785         if (genl_connect(global->socket)) {
786                 DBG("Failed to connect to generic netlink.");
787                 err = -ENOLINK;
788                 goto fail;
789         }
790
791         global->id = genl_ctrl_resolve(global->socket, "nl80211");
792         if (global->id < 0) {
793                 DBG("Failed to find the nl80211 driver");
794                 err = -ENOENT;
795                 goto fail;
796         }
797
798         return 0;
799
800 fail:
801         nl_socket_free(global->socket);
802         return err;
803 }
804
805 static void __netconfig_deinitialize_nl80211(netconfig_nl_global *global)
806 {
807         if (global == NULL)
808                 return;
809
810         if (global->socket) {
811                 nl_socket_free(global->socket);
812                 global->socket = NULL;
813         }
814 }
815
816 static int __netconfig_initialize_nl_msg(netconfig_nl_global *global)
817 {
818         int rv;
819
820         if (global == NULL) {
821                 DBG("Invalid parameter.");
822                 return -EINVAL;
823         }
824
825         global->msg = nlmsg_alloc();
826         if (global->msg == NULL) {
827                 DBG("Failed to allocate netlink message");
828                 return -ENOMEM;
829         }
830
831         /* Set command into message */
832         genlmsg_put(global->msg, 0, 0, global->id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
833         rv = nla_put_u32(global->msg, NL80211_ATTR_IFINDEX, global->if_index);
834         if (rv < 0) {
835                 DBG("Failed to add integer attribute to netlink message.");
836                 nlmsg_free(global->msg);
837                 return rv;
838         }
839         nl_socket_modify_cb(global->socket, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_cb, NULL);
840
841         return 0;
842 }
843
844 static void __netconfig_get_netlink_scan_parameters(GVariant *params, GSList *ssid_list, char **vsie)
845 {
846         GVariantIter *iter;
847         GVariant *value;
848         gchar *key;
849         g_variant_get(params, "a{sv}", &iter);
850         while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
851                 if (g_strcmp0(key, "SSID") == 0) {
852                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
853                                 char *ssid = g_strdup(g_variant_get_string(value, NULL));
854                                 DBG("ssid [%s]", ssid);
855                                 ssid_list = g_slist_append(ssid_list, ssid);
856                         }
857                 } else if (g_strcmp0(key, "VSIE") == 0) {
858                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
859                                 *vsie = g_strdup(g_variant_get_string(value, NULL));
860                 }
861         }
862         g_variant_iter_free(iter);
863 }
864
865 static int __netconfig_get_scan_results(netconfig_nl_global *global)
866 {
867         int ret = __netconfig_initialize_nl_msg(global);
868         if (ret < 0) {
869                 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
870                 return ret;
871         }
872
873         ret = nl_send_auto_complete(global->socket, global->msg);
874         if (ret < 0) {
875                 DBG("nl_send_auto_complete() failed, error %d", ret);
876                 return ret;
877         }
878         DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
879
880         /** Receive the kernel message. */
881         ret = nl_recvmsgs_default(global->socket);
882         nlmsg_free(global->msg);
883         if (ret < 0) {
884                 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
885                 return ret;
886         }
887         return 0;
888 }
889
890 int handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context,
891                 const gchar *ifname, GVariant *params)
892 {
893         DBG("");
894         netconfig_nl_global global = {
895                 .id = -1,
896                 .if_index = -1,
897                 .socket = NULL,
898                 .msg = NULL,
899         };
900
901         /** Initialize netlink socket */
902         int ret = __netconfig_initialize_nl80211(&global, ifname);
903         if (ret < 0) {
904                 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
905                 netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "AccessDenied");
906                 return TRUE;
907         }
908
909         GSList *ssid_list = NULL;
910         char *vsie = NULL;
911         __netconfig_get_netlink_scan_parameters(params, ssid_list, &vsie);
912         /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
913         ret = __netconfig_netlink_scan(&global, ssid_list, vsie);
914         g_slist_free_full(ssid_list, g_free);
915         g_free(vsie);
916
917         if (ret < 0) {
918                 DBG("__netconfig_netlink_scan() failed, error %d", ret);
919                 netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "AccessDenied");
920                 __netconfig_deinitialize_nl80211(&global);
921                 return TRUE;
922         }
923
924         ret = __netconfig_get_scan_results(&global);
925         if (ret < 0) {
926                 netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "AccessDenied");
927                 __netconfig_deinitialize_nl80211(&global);
928                 return TRUE;
929         }
930
931         wifi_complete_netlink_scan(wifi, context);
932         __netconfig_notify_netlink_scan_done(ifname);
933         __netconfig_deinitialize_nl80211(&global);
934
935         return TRUE;
936 }