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