d33ae4102b8e1fe42b660d3c3571b9a89d4fd0e7
[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-netlink-scan.h"
26 #include <netlink/genl/genl.h>
27 #include <netlink/genl/family.h>
28 #include <netlink/genl/ctrl.h>
29 #include <netlink/msg.h>
30 #include <netlink/attr.h>
31 #include <netlink/netlink.h>
32 #include <ctype.h>
33
34 static GSList *bss_info_list = NULL;
35 static guint scan_timer = 0;
36 static unsigned char samsung_oui[3] = {0x00, 0x16, 0x32};
37
38 static gboolean __netconfig_scan_timeout(gpointer data)
39 {
40         __netconfig_notify_netlink_scan_done();
41
42         return FALSE;
43 }
44
45 static void __netconfig_start_scan_timer(void)
46 {
47         if (scan_timer == 0) {
48                 netconfig_start_timer_seconds(5, __netconfig_scan_timeout, NULL, &scan_timer);
49                 INFO("Get scan data timer started: %d", scan_timer);
50         }
51 }
52
53 static void __netconfig_stop_scan_timer(void)
54 {
55         netconfig_stop_timer(&scan_timer);
56         INFO("Get scan data timer stopped: %d", scan_timer);
57 }
58
59 void __netconfig_notify_netlink_scan_done(void)
60 {
61         GVariantBuilder *builder = NULL;
62         GSList* list = NULL;
63         const char *prop_ssid = "ssid";
64         const char *prop_bssid = "bssid";
65         const char *prop_freq = "freq";
66         const char *prop_rssi = "rssi";
67         const char *prop_vsie = "vsie";
68
69         builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
70         for (list = bss_info_list; list != NULL; list = list->next) {
71                 struct bss_scan_info_t *bss_info = (struct bss_scan_info_t *)list->data;
72
73                 if (bss_info) {
74                         char *bssid = (char *)bss_info->bssid;
75                         char *ssid = (char *)bss_info->ssid;
76                         char *vsie = (char *)bss_info->vsie;
77                         int freq = (int)bss_info->freq;
78                         int signal = (int)bss_info->signal;
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                         g_variant_builder_add(builder, "{sv}", prop_vsie, g_variant_new_string(vsie));
85                 }
86         }
87
88         wifi_emit_netlink_scan_completed((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
89         g_variant_builder_unref(builder);
90
91         if (bss_info_list != NULL)
92                 g_slist_free_full(bss_info_list, g_free);
93
94         bss_info_list = NULL;
95         __netconfig_stop_scan_timer();
96         INFO("NetlinkScanCompleted");
97
98         return;
99 }
100
101 static int ack_handler(struct nl_msg *msg, void *user_data)
102 {
103         int *err = user_data;
104         *err = 0;
105         return NL_STOP;
106 }
107
108 static int finish_handler(struct nl_msg *msg, void *user_data)
109 {
110         int *ret = user_data;
111         *ret = 0;
112         return NL_SKIP;
113 }
114
115 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
116                          void *user_data)
117 {
118         int *ret = user_data;
119         *ret = err->error;
120         return NL_SKIP;
121 }
122
123 static int no_seq_check(struct nl_msg *msg, void *user_data)
124 {
125         DBG("");
126         return NL_OK;
127 }
128
129 static int __netconfig_family_handler(struct nl_msg *msg, void *user_data)
130 {
131         /** Callback for NL_CB_VALID in multicast group */
132         struct netconfig_netlink_scan_handler_args *grp = user_data;
133         struct nlattr *tb[CTRL_ATTR_MAX + 1];
134         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
135         struct nlattr *mc_grp;
136         int rem_mc_grp;
137
138         nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
139
140         if (!tb[CTRL_ATTR_MCAST_GROUPS])
141                 return NL_SKIP;
142
143         nla_for_each_nested(mc_grp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mc_grp) {
144                 struct nlattr *tb_mc_grp[CTRL_ATTR_MCAST_GRP_MAX + 1];
145
146                 nla_parse(tb_mc_grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mc_grp), nla_len(mc_grp), NULL);
147
148                 if (!tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID])
149                         continue;
150                 if (strncmp(nla_data(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME]), grp->group,
151                                         nla_len(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME])))
152                         continue;
153
154                 grp->id = nla_get_u32(tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID]);
155                 break;
156         }
157
158         return NL_SKIP;
159 }
160
161 static int __netconfig_get_multicast_id(struct nl_sock *socket, const char *family, const char *group)
162 {
163         struct nl_msg *msg = NULL;
164         struct nl_cb *cb = NULL;
165         int ret, ctrl_id;
166         struct netconfig_netlink_scan_handler_args grp = { .group = group, .id = -ENOENT, };
167
168         msg = nlmsg_alloc();
169         if (!msg)
170                 return -ENOMEM;
171
172         cb = nl_cb_alloc(NL_CB_DEFAULT);
173         if (!cb) {
174                 ret = -ENOMEM;
175                 goto cb_fail;
176         }
177
178         ctrl_id = genl_ctrl_resolve(socket, "nlctrl");
179
180         genlmsg_put(msg, 0, 0, ctrl_id, 0, 0, CTRL_CMD_GETFAMILY, 0);
181
182         ret = -ENOBUFS;
183         NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
184
185         ret = nl_send_auto_complete(socket, msg);
186         if (ret < 0)
187                 goto out;
188
189         ret = 1;
190
191         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
192         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
193         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_family_handler, &grp);
194
195         while (ret > 0)
196                 nl_recvmsgs(socket, cb);
197
198         if (ret == 0)
199                 ret = grp.id;
200
201 nla_put_failure:
202 out:
203         nl_cb_put(cb);
204 cb_fail:
205         nlmsg_free(msg);
206         return ret;
207 }
208
209 static void __netconfig_macaddress_str(char *bssid, unsigned char *user_data)
210 {
211         int i;
212
213         for (i = 0; i < 6; i++) {
214                 if (i == 0) {
215                         snprintf(bssid, 3, "%02x", user_data[i]);
216                         bssid += 2;
217                 } else {
218                         snprintf(bssid, 4, ":%02x", user_data[i]);
219                         bssid += 3;
220                 }
221         }
222 }
223
224 static void __netconfig_get_vsie(unsigned char *bss_element, int length, char **dst)
225 {
226         int i = 0;
227         uint8_t len = 0;
228         gboolean vsie_found = FALSE;
229
230         if (length < 3) {
231                 DBG("Vendor specific data not available");
232                 return;
233         }
234
235         /** Check for vendor specific information element */
236         for (i = 0; i < length; i++) {
237                 if (bss_element[i] == 221) {
238                         len = bss_element[i+1];
239                         vsie_found = TRUE;
240                         goto out;
241                 }
242         }
243 out:
244         if (vsie_found && memcmp(bss_element+i+2, samsung_oui, 3) == 0) {
245                 DBG("Vendor Specific IE found, len: %d", len);
246                 *dst = g_try_malloc0(2*(len+2) + 1);
247                 if (*dst == NULL) {
248                         DBG("Failed to allocate memory");
249                         return;
250                 }
251                 char *buf = (*dst);
252                 int j = 0;
253
254                 for (j = i; j <= (i + len + 1); j++) {
255                         snprintf(buf, 3, "%02x", bss_element[j]);
256                         buf += 2;
257                 }
258
259                 vsie_found = FALSE;
260         }
261 }
262
263 static void __netconfig_found_ap(unsigned char *bss_element, int length, char *str)
264 {
265         uint8_t len;
266         uint8_t *data;
267         int i;
268
269         while (length >= 2 && length >= bss_element[1]) {
270                 if (bss_element[0] == 0 && bss_element[1] <= 32) {
271                         len = bss_element[1];
272                         data = bss_element + 2;
273                         for (i = 0; i < len; i++) {
274                                 if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\')
275                                         snprintf(&str[i], 2, "%c", data[i]);
276                                 else if (data[i] == ' ' && (i != 0 && i != len -1))
277                                         snprintf(&str[i], 2, "%c", ' ');
278                                 else
279                                         snprintf(&str[i], 3, "%.2x", data[i]);
280                         }
281                         break;
282                 }
283                 length -= bss_element[1] + 2;
284                 bss_element += bss_element[1] + 2;
285         }
286 }
287
288 static int __netconfig_netlink_scan_cb(struct nl_msg *msg, void *user_data)
289 {
290         /** Called by the kernel with a dump of the successful scan's data. Called for each SSID. */
291         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
292         char bssid[NETCONFIG_BSSID_LEN+1];
293         char ssid[NETCONFIG_SSID_LEN+1] = {0, };
294         char *vsie = NULL;
295         struct nlattr *tb[NL80211_ATTR_MAX + 1];
296         struct nlattr *bss[NL80211_BSS_MAX + 1];
297         struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
298                 [NL80211_BSS_FREQUENCY] = {.type = NLA_U32},
299                 [NL80211_BSS_BSSID] = { },
300                 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
301                 [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32},
302         };
303
304         /** Parse nl message and check error. */
305         nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
306         if (!tb[NL80211_ATTR_BSS]) {
307                 DBG("BSS info not available");
308                 return NL_SKIP;
309         }
310         if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
311                 DBG("Failed to parse nested attributes");
312                 return NL_SKIP;
313         }
314         if ((!bss[NL80211_BSS_BSSID]) || (!bss[NL80211_BSS_INFORMATION_ELEMENTS]))
315                 return NL_SKIP;
316
317         /** Extract BSSID and AP info. */
318         __netconfig_macaddress_str(bssid, nla_data(bss[NL80211_BSS_BSSID]));
319         __netconfig_found_ap(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), ssid);
320         __netconfig_get_vsie(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), &vsie);
321
322         /** Create AP info list. */
323         if (ssid[0] != '\0') {
324                 struct bss_scan_info_t *bss_info;
325                 int signal;
326
327                 bss_info = g_try_new0(struct bss_scan_info_t, 1);
328                 if (bss_info == NULL)
329                         return NL_SKIP;
330
331                 g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
332                 g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
333                 if (vsie) {
334                         g_strlcpy(bss_info->vsie, vsie, strlen(vsie)+1);
335                         g_free(vsie);
336                 }
337                 bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
338
339                 if (bss[NL80211_BSS_SIGNAL_MBM]) {
340                         signal = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
341                         signal /= 100; /** mBm to dBm */
342                         bss_info->signal = signal;
343                 }
344                 DBG("%s %d %d %s [vsie: %s]", bss_info->bssid, bss_info->freq, bss_info->signal, bss_info->ssid, bss_info->vsie);
345
346                 if (bss_info->ssid[0] == '\0')
347                         g_free(bss_info);
348                 else
349                         bss_info_list = g_slist_append(bss_info_list, bss_info);
350
351                 if (scan_timer == 0) {
352                         DBG("Start scan timer");
353                         __netconfig_start_scan_timer();
354                 }
355         }
356
357         return NL_SKIP;
358 }
359
360 static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
361 {
362         /** Called by the kernel when the scan is done or has been aborted. */
363         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
364         struct netconfig_netlink_scan_results *results = user_data;
365
366         if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
367                 DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
368                 results->done = 1;
369                 results->aborted = 0;
370         } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
371                 DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
372                 results->done = 1;
373                 results->aborted = 1;
374         }
375
376         return NL_SKIP;
377 }
378
379 static int __netconfig_request_netlink_scan(struct nl_sock *socket, int if_index, int id, char *ssid)
380 {
381         struct netconfig_netlink_scan_results results = { .done = 0, .aborted = 0 };
382         struct nl_msg *msg = NULL;
383         struct nl_cb *cb = NULL;
384         struct nl_msg *ssids = NULL;
385         int err = 0;
386         int ret = 0;
387         int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
388         nl_socket_add_membership(socket, mcid);
389
390         msg = nlmsg_alloc();
391         if (!msg) {
392                 DBG("Failed to allocate msg");
393                 return -ENOMEM;
394         }
395         ssids = nlmsg_alloc();
396         if (!ssids) {
397                 DBG("Failed to allocate ssids");
398                 nlmsg_free(msg);
399                 return -ENOMEM;
400         }
401         cb = nl_cb_alloc(NL_CB_DEFAULT);
402         if (!cb) {
403                 DBG("Failed to allocate callbacks");
404                 nlmsg_free(msg);
405                 nlmsg_free(ssids);
406                 return -ENOMEM;
407         }
408
409         /** Set nl message and callback functions. */
410         genlmsg_put(msg, 0, 0, id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
411         nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
412         if (!ssid)
413                 nla_put(ssids, 1, 0, "");
414         else
415                 nla_put(ssids, 1, strlen(ssid), ssid);
416         nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
417         nlmsg_free(ssids);
418
419         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_reply, &results);
420         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
421         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
422         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
423         nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
424
425         /** Send NL80211_CMD_TRIGGER_SCAN to start the scan. */
426         err = 1;
427         ret = nl_send_auto_complete(socket, msg);
428         DBG("Sent %d bytes to the kernel", ret);
429         while (err > 0)
430                 ret = nl_recvmsgs(socket, cb);
431
432         if (ret < 0) {
433                 DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
434                 return ret;
435         }
436
437         while (!results.done)
438                 nl_recvmsgs(socket, cb);
439
440         if (results.aborted) {
441                 DBG("scan aborted");
442                 return 1;
443         }
444         DBG("Scan done");
445
446         /** Release memory */
447         nlmsg_free(msg);
448         nl_cb_put(cb);
449         nl_socket_drop_membership(socket, mcid);
450         return 0;
451 }
452
453 static int __netconfig_initialize_nl80211(netconfig_nl_global *global)
454 {
455         int err = 0;
456
457         global->if_index = __netconfig_get_interface_index(WIFI_IFNAME);
458         if (global->if_index < 0) {
459                 DBG("Failed to get interface index");
460                 return -1;
461         }
462
463         global->socket = nl_socket_alloc();
464         if (!global->socket) {
465                 DBG("Failed to allocate netlink socket.");
466                 return -ENOMEM;
467         }
468
469         if (genl_connect(global->socket)) {
470                 DBG("Failed to connect to generic netlink.");
471                 err = -ENOLINK;
472                 goto fail;
473         }
474
475         global->id = genl_ctrl_resolve(global->socket, "nl80211");
476         if (global->id < 0) {
477                 DBG("Failed to find the nl80211 driver");
478                 err = -ENOENT;
479                 goto fail;
480         }
481
482         return 0;
483
484 fail:
485         nl_socket_free(global->socket);
486         return err;
487 }
488
489 static int __netconfig_initialize_nl_msg(netconfig_nl_global *global)
490 {
491         if (global == NULL) {
492                 DBG("Invalid parameter.");
493                 return -EINVAL;
494         }
495
496         global->msg = nlmsg_alloc();
497         if (global->msg == NULL) {
498                 DBG("Failed to allocate netlink message");
499                 return -ENOMEM;
500         }
501
502         /* Set command into message */
503         genlmsg_put(global->msg, 0, 0, global->id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
504         nla_put_u32(global->msg, NL80211_ATTR_IFINDEX, global->if_index);
505         nl_socket_modify_cb(global->socket, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_cb, NULL);
506
507         return 0;
508 }
509
510 int handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context)
511 {
512         DBG("");
513         netconfig_nl_global global = {
514                 .id = -1,
515                 .if_index = -1,
516                 .socket = NULL,
517                 .msg = NULL,
518         };
519
520         /** Initialize netlink socket */
521         int ret = __netconfig_initialize_nl80211(&global);
522         if (ret < 0) {
523                 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
524                 wifi_complete_netlink_specific_scan(wifi, context);
525                 return ret;
526         }
527
528         /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
529         ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, NULL);
530         if (ret < 0) {
531                 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
532                 wifi_complete_netlink_scan(wifi, context);
533                 return ret;
534         }
535
536         ret = __netconfig_initialize_nl_msg(&global);
537         if (ret < 0) {
538                 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
539                 wifi_complete_netlink_specific_scan(wifi, context);
540                 return ret;
541         }
542
543         ret = nl_send_auto_complete(global.socket, global.msg);
544         DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
545
546         /** Receive the kernel message. */
547         ret = nl_recvmsgs_default(global.socket);
548         nlmsg_free(global.msg);
549         if (ret < 0) {
550                 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
551                 wifi_complete_netlink_scan(wifi, context);
552                 return ret;
553         }
554
555         wifi_complete_netlink_scan(wifi, context);
556         return 1;
557 }
558
559 int handle_netlink_specific_scan(Wifi *wifi, GDBusMethodInvocation *context, gchar *ssid)
560 {
561         DBG("ssid: %s", ssid);
562         netconfig_nl_global global = {
563                 .id = -1,
564                 .if_index = -1,
565                 .socket = NULL,
566                 .msg = NULL,
567         };
568
569         /** Initialize netlink socket */
570         int ret = __netconfig_initialize_nl80211(&global);
571         if (ret < 0) {
572                 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
573                 wifi_complete_netlink_specific_scan(wifi, context);
574                 return ret;
575         }
576
577         /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
578         ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, ssid);
579         if (ret != 0) {
580                 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
581                 wifi_complete_netlink_specific_scan(wifi, context);
582                 return ret;
583         }
584
585         ret = __netconfig_initialize_nl_msg(&global);
586         if (ret < 0) {
587                 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
588                 wifi_complete_netlink_specific_scan(wifi, context);
589                 return ret;
590         }
591
592         ret = nl_send_auto_complete(global.socket, global.msg);
593         DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
594
595         /** Receive the kernel message. */
596         ret = nl_recvmsgs_default(global.socket);
597         nlmsg_free(global.msg);
598         if (ret < 0) {
599                 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
600                 wifi_complete_netlink_specific_scan(wifi, context);
601                 return ret;
602         }
603
604         wifi_complete_netlink_specific_scan(wifi, context);
605         return 1;
606 }