2 * Network Configuration Module
4 * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "netsupplicant.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>
35 static GSList *bss_info_list = NULL;
36 static guint scan_timer = 0;
37 static unsigned char samsung_oui[3] = {0x00, 0x16, 0x32};
39 static gboolean __netconfig_scan_timeout(gpointer data)
41 __netconfig_notify_netlink_scan_done();
46 static void __netconfig_start_scan_timer(void)
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);
54 static void __netconfig_stop_scan_timer(void)
56 netconfig_stop_timer(&scan_timer);
57 INFO("Get scan data timer stopped: %d", scan_timer);
60 void __netconfig_notify_netlink_scan_done(void)
62 GVariantBuilder *builder = 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";
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;
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;
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));
89 wifi_emit_netlink_scan_completed((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
90 g_variant_builder_unref(builder);
92 if (bss_info_list != NULL)
93 g_slist_free_full(bss_info_list, g_free);
96 __netconfig_stop_scan_timer();
97 INFO("NetlinkScanCompleted");
102 static int ack_handler(struct nl_msg *msg, void *user_data)
104 int *err = user_data;
109 static int finish_handler(struct nl_msg *msg, void *user_data)
111 int *ret = user_data;
116 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
119 int *ret = user_data;
124 static int no_seq_check(struct nl_msg *msg, void *user_data)
130 static int __netconfig_family_handler(struct nl_msg *msg, void *user_data)
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;
139 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
141 if (!tb[CTRL_ATTR_MCAST_GROUPS])
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];
147 nla_parse(tb_mc_grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mc_grp), nla_len(mc_grp), NULL);
149 if (!tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID])
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])))
155 grp->id = nla_get_u32(tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID]);
162 static int __netconfig_get_multicast_id(struct nl_sock *socket, const char *family, const char *group)
164 struct nl_msg *msg = NULL;
165 struct nl_cb *cb = NULL;
167 struct netconfig_netlink_scan_handler_args grp = { .group = group, .id = -ENOENT, };
173 cb = nl_cb_alloc(NL_CB_DEFAULT);
179 ctrl_id = genl_ctrl_resolve(socket, "nlctrl");
181 genlmsg_put(msg, 0, 0, ctrl_id, 0, 0, CTRL_CMD_GETFAMILY, 0);
184 NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
186 ret = nl_send_auto_complete(socket, msg);
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);
197 nl_recvmsgs(socket, cb);
210 static void __netconfig_macaddress_str(char *bssid, unsigned char *user_data)
214 for (i = 0; i < 6; i++) {
216 snprintf(bssid, 3, "%02x", user_data[i]);
219 snprintf(bssid, 4, ":%02x", user_data[i]);
225 static void __netconfig_get_vsie(unsigned char *bss_element, int length, char **dst)
229 gboolean vsie_found = FALSE;
232 DBG("Vendor specific data not available");
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];
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);
249 DBG("Failed to allocate memory");
255 for (j = i; j <= (i + len + 1); j++) {
256 snprintf(buf, 3, "%02x", bss_element[j]);
264 static void __netconfig_found_ap(unsigned char *bss_element, int length, char *str)
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", ' ');
280 snprintf(&str[i], 3, "%.2x", data[i]);
284 length -= bss_element[1] + 2;
285 bss_element += bss_element[1] + 2;
289 static int __netconfig_netlink_scan_cb(struct nl_msg *msg, void *user_data)
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, };
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},
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");
311 if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
312 DBG("Failed to parse nested attributes");
315 if ((!bss[NL80211_BSS_BSSID]) || (!bss[NL80211_BSS_INFORMATION_ELEMENTS]))
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);
323 /** Create AP info list. */
324 if (ssid[0] != '\0') {
325 struct bss_scan_info_t *bss_info;
328 bss_info = g_try_new0(struct bss_scan_info_t, 1);
329 if (bss_info == NULL)
332 g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
333 g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
335 g_strlcpy(bss_info->vsie, vsie, strlen(vsie)+1);
338 bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
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;
345 DBG("%s %d %d %s [vsie: %s]", bss_info->bssid, bss_info->freq, bss_info->signal, bss_info->ssid, bss_info->vsie);
347 if (bss_info->ssid[0] == '\0')
350 bss_info_list = g_slist_append(bss_info_list, bss_info);
352 if (scan_timer == 0) {
353 DBG("Start scan timer");
354 __netconfig_start_scan_timer();
361 static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
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;
367 if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
368 DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
370 results->aborted = 0;
371 } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
372 DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
374 results->aborted = 1;
380 static int __netconfig_request_netlink_scan(struct nl_sock *socket,
381 int if_index, int id, GVariant *params)
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;
389 unsigned char ies[NETCONFIG_MAX_VSIE_LEN+1] = {0x00, };
394 gboolean ssid_found = FALSE;
395 int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
396 nl_socket_add_membership(socket, mcid);
400 DBG("Failed to allocate msg");
403 ssids = nlmsg_alloc();
405 DBG("Failed to allocate ssids");
409 cb = nl_cb_alloc(NL_CB_DEFAULT);
411 DBG("Failed to allocate callbacks");
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);
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));
427 DBG("ssid [%s]", ssid);
429 nla_put(ssids, 1, strlen(ssid), ssid);
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);
438 ies_len = (vsie_len % 2) ? ((vsie_len / 2) + 1) : (vsie_len / 2);
439 __netconfig_hex_str_to_bin(vsie, ies, ies_len);
444 g_variant_iter_free(iter);
447 nla_put(ssids, 1, 0, "");
448 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
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);
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);
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);
470 ret = nl_recvmsgs(socket, cb);
473 DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
477 while (!results.done)
478 nl_recvmsgs(socket, cb);
480 if (results.aborted) {
486 /** Release memory */
489 nl_socket_drop_membership(socket, mcid);
493 static int __netconfig_initialize_nl80211(netconfig_nl_global *global)
497 global->if_index = __netconfig_get_interface_index(WIFI_IFNAME);
498 if (global->if_index < 0) {
499 DBG("Failed to get interface index");
503 global->socket = nl_socket_alloc();
504 if (!global->socket) {
505 DBG("Failed to allocate netlink socket.");
509 if (genl_connect(global->socket)) {
510 DBG("Failed to connect to generic netlink.");
515 global->id = genl_ctrl_resolve(global->socket, "nl80211");
516 if (global->id < 0) {
517 DBG("Failed to find the nl80211 driver");
525 nl_socket_free(global->socket);
529 static int __netconfig_initialize_nl_msg(netconfig_nl_global *global)
531 if (global == NULL) {
532 DBG("Invalid parameter.");
536 global->msg = nlmsg_alloc();
537 if (global->msg == NULL) {
538 DBG("Failed to allocate netlink message");
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);
550 int handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context, GVariant *params)
553 netconfig_nl_global global = {
560 /** Initialize netlink socket */
561 int ret = __netconfig_initialize_nl80211(&global);
563 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
564 wifi_complete_netlink_scan(wifi, context);
568 /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
569 ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, params);
571 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
572 wifi_complete_netlink_scan(wifi, context);
576 ret = __netconfig_initialize_nl_msg(&global);
578 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
579 wifi_complete_netlink_scan(wifi, context);
583 ret = nl_send_auto_complete(global.socket, global.msg);
584 DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
586 /** Receive the kernel message. */
587 ret = nl_recvmsgs_default(global.socket);
588 nlmsg_free(global.msg);
590 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
591 wifi_complete_netlink_scan(wifi, context);
595 wifi_complete_netlink_scan(wifi, context);