Fix coverity issuses.
[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                         return NL_SKIP;
498                 }
499         }
500
501         /** Create AP info list. */
502         if (ssid[0] != '\0') {
503                 struct bss_scan_info_t *bss_info;
504                 int signal;
505
506                 bss_info = g_try_new0(struct bss_scan_info_t, 1);
507                 if (bss_info == NULL) {
508                         g_free(vsie);
509                         return NL_SKIP;
510                 }
511
512                 g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
513                 g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
514                 if (vsie) {
515                         g_strlcpy(bss_info->vsie, vsie, strlen(vsie)+1);
516                         g_free(vsie);
517                         vsie = NULL;
518                 }
519                 bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
520
521                 if (bss[NL80211_BSS_SIGNAL_MBM]) {
522                         signal = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
523                         signal /= 100; /** mBm to dBm */
524                         bss_info->signal = signal;
525                 }
526
527                 bss_info->security_type = sec_type;
528                 bss_info->encryption_type = enc_type;
529                 DBG("%s %d %d %s %d %d [vsie: %s]", bss_info->bssid, bss_info->freq,
530                                 bss_info->signal, bss_info->ssid, bss_info->security_type,
531                                 bss_info->encryption_type, bss_info->vsie);
532
533                 if (bss_info->ssid[0] == '\0')
534                         g_free(bss_info);
535                 else
536                         bss_info_list = g_slist_append(bss_info_list, bss_info);
537
538         }
539
540         g_free(vsie);
541         return NL_SKIP;
542 }
543
544 static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
545 {
546         /** Called by the kernel when the scan is done or has been aborted. */
547         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
548         struct netconfig_netlink_scan_results *results = user_data;
549
550         if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
551                 DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
552                 results->done = 1;
553                 results->aborted = 0;
554         } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
555                 DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
556                 results->done = 1;
557                 results->aborted = 1;
558         }
559
560         return NL_SKIP;
561 }
562
563 static int __netconfig_request_netlink_scan(struct nl_sock *socket,
564                 int if_index, int id, GVariant *params)
565 {
566         struct netconfig_netlink_scan_results results = { .done = 0, .aborted = 0 };
567         struct nl_msg *msg = NULL;
568         struct nl_cb *cb = NULL;
569         struct nl_msg *ssids = NULL;
570         int err = 0;
571         int ret = 0;
572         unsigned char ies[NETCONFIG_MAX_VSIE_LEN+1] = {0x00, };
573         int ies_len = 0;
574         GVariantIter *iter;
575         GVariant *value;
576         gchar *key;
577         gboolean ssid_found = FALSE;
578         int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
579
580         ret = nl_socket_add_membership(socket, mcid);
581         if (ret < 0) {
582                 DBG("Failed to add membership, error: (%s)", nl_geterror(-ret));
583                 return ret;
584         }
585
586         msg = nlmsg_alloc();
587         if (!msg) {
588                 DBG("Failed to allocate msg");
589                 nl_socket_drop_membership(socket, mcid);
590                 return -ENOMEM;
591         }
592         ssids = nlmsg_alloc();
593         if (!ssids) {
594                 DBG("Failed to allocate ssids");
595                 nlmsg_free(msg);
596                 nl_socket_drop_membership(socket, mcid);
597                 return -ENOMEM;
598         }
599         cb = nl_cb_alloc(NL_CB_DEFAULT);
600         if (!cb) {
601                 DBG("Failed to allocate callbacks");
602                 nlmsg_free(msg);
603                 nlmsg_free(ssids);
604                 nl_socket_drop_membership(socket, mcid);
605                 return -ENOMEM;
606         }
607
608         /** Set nl message and callback functions. */
609         genlmsg_put(msg, 0, 0, id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
610         ret = nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
611         if (ret < 0) {
612                 DBG("Failed to add integer attribute to netlink message, error: (%s)", nl_geterror(-ret));
613                 goto out;
614         }
615
616         g_variant_get(params, "a{sv}", &iter);
617         while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
618                 if (g_strcmp0(key, "SSID") == 0) {
619                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
620                                 char *ssid = g_strdup(g_variant_get_string(value, NULL));
621                                 ssid_found = TRUE;
622                                 DBG("ssid [%s]", ssid);
623
624                                 ret = nla_put(ssids, 1, strlen(ssid), ssid);
625                                 g_free(ssid);
626                                 if (ret < 0) {
627                                         DBG("Failed to add ssid to netlink message, error: (%s)", nl_geterror(-ret));
628                                         g_variant_iter_free(iter);
629                                         goto out;
630                                 }
631                         }
632                 } else if (g_strcmp0(key, "VSIE") == 0) {
633                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
634                                 char *vsie = g_strdup(g_variant_get_string(value, NULL));
635                                 int vsie_len = strlen(vsie);
636                                 DBG("vsie: %s vsie_len: %d", vsie, vsie_len);
637
638                                 ies_len = (vsie_len % 2) ? ((vsie_len / 2) + 1) : (vsie_len / 2);
639                                 __netconfig_hex_str_to_bin(vsie, ies, ies_len);
640                                 g_free(vsie);
641                         }
642                 }
643         }
644         g_variant_iter_free(iter);
645
646         if (!ssid_found) {
647                 ret = nla_put(ssids, 1, 0, "");
648                 if (ret < 0) {
649                         DBG("nla_put error: (%s)", nl_geterror(-ret));
650                         goto out;
651                 }
652         }
653         nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
654
655         if (ies[0] == NETCONFIG_VENDOR_SPECIFIC_ID && ies[1] >= 4) {
656                 DBG("ies_len: %d ies: %02x %02x %02x %02x %02x %02x %02x", ies_len,
657                                 ies[0], ies[1], ies[2], ies[3], ies[4], ies[5], ies[6]);
658                 ret = nla_put(msg, NL80211_ATTR_IE, ies_len, ies);
659                 if (ret < 0) {
660                         DBG("Failed to add vsie data to netlink message, error: (%s)", nl_geterror(-ret));
661                         goto out;
662                 }
663         }
664
665         err = 1;
666         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_reply, &results);
667         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
668         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
669         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
670         nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
671
672         /** Send NL80211_CMD_TRIGGER_SCAN to start the scan. */
673         ret = nl_send_auto_complete(socket, msg);
674         if (ret < 0) {
675                 DBG("nl_send_auto_complete() error: (%s)", nl_geterror(-ret));
676                 goto out;
677         }
678
679         DBG("Sent %d bytes to the kernel", ret);
680         ssid_found = FALSE;
681
682         while (err > 0)
683                 ret = nl_recvmsgs(socket, cb);
684
685         if (ret < 0) {
686                 DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
687                 goto out;
688         }
689
690         while (!results.done)
691                 nl_recvmsgs(socket, cb);
692
693         if (results.aborted) {
694                 DBG("scan aborted");
695                 return 1;
696         }
697
698 out:
699         /** Release memory */
700         nlmsg_free(ssids);
701         nlmsg_free(msg);
702         nl_cb_put(cb);
703         nl_socket_drop_membership(socket, mcid);
704
705         if (ret < 0)
706                 return ret;
707
708         DBG("Scan done");
709         return 0;
710 }
711
712 static int __netconfig_initialize_nl80211(netconfig_nl_global *global)
713 {
714         int err = 0;
715
716         global->if_index = __netconfig_get_interface_index(WIFI_IFNAME);
717         if (global->if_index < 0) {
718                 DBG("Failed to get interface index");
719                 return -1;
720         }
721
722         global->socket = nl_socket_alloc();
723         if (!global->socket) {
724                 DBG("Failed to allocate netlink socket.");
725                 return -ENOMEM;
726         }
727
728         if (genl_connect(global->socket)) {
729                 DBG("Failed to connect to generic netlink.");
730                 err = -ENOLINK;
731                 goto fail;
732         }
733
734         global->id = genl_ctrl_resolve(global->socket, "nl80211");
735         if (global->id < 0) {
736                 DBG("Failed to find the nl80211 driver");
737                 err = -ENOENT;
738                 goto fail;
739         }
740
741         return 0;
742
743 fail:
744         nl_socket_free(global->socket);
745         return err;
746 }
747
748 static int __netconfig_initialize_nl_msg(netconfig_nl_global *global)
749 {
750         int rv;
751
752         if (global == NULL) {
753                 DBG("Invalid parameter.");
754                 return -EINVAL;
755         }
756
757         global->msg = nlmsg_alloc();
758         if (global->msg == NULL) {
759                 DBG("Failed to allocate netlink message");
760                 return -ENOMEM;
761         }
762
763         /* Set command into message */
764         genlmsg_put(global->msg, 0, 0, global->id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
765         rv = nla_put_u32(global->msg, NL80211_ATTR_IFINDEX, global->if_index);
766         if (rv < 0) {
767                 DBG("Failed to add integer attribute to netlink message.");
768                 nlmsg_free(global->msg);
769                 return rv;
770         }
771         nl_socket_modify_cb(global->socket, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_cb, NULL);
772
773         return 0;
774 }
775
776 int handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context, GVariant *params)
777 {
778         DBG("");
779         netconfig_nl_global global = {
780                 .id = -1,
781                 .if_index = -1,
782                 .socket = NULL,
783                 .msg = NULL,
784         };
785
786         /** Initialize netlink socket */
787         int ret = __netconfig_initialize_nl80211(&global);
788         if (ret < 0) {
789                 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
790                 wifi_complete_netlink_scan(wifi, context);
791                 return TRUE;
792         }
793
794         /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
795         ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, params);
796         if (ret < 0) {
797                 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
798                 wifi_complete_netlink_scan(wifi, context);
799                 return TRUE;
800         }
801
802         ret = __netconfig_initialize_nl_msg(&global);
803         if (ret < 0) {
804                 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
805                 wifi_complete_netlink_scan(wifi, context);
806                 return TRUE;
807         }
808
809         ret = nl_send_auto_complete(global.socket, global.msg);
810         DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
811
812         /** Receive the kernel message. */
813         ret = nl_recvmsgs_default(global.socket);
814         nlmsg_free(global.msg);
815         if (ret < 0) {
816                 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
817                 wifi_complete_netlink_scan(wifi, context);
818                 return TRUE;
819         }
820
821         wifi_complete_netlink_scan(wifi, context);
822         __netconfig_notify_netlink_scan_done();
823
824         return TRUE;
825 }