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