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