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