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