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-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>
34 static GSList *bss_info_list = NULL;
35 static guint scan_timer = 0;
36 static unsigned char samsung_oui[3] = {0x00, 0x16, 0x32};
38 static gboolean __netconfig_scan_timeout(gpointer data)
40 __netconfig_notify_netlink_scan_done();
45 static void __netconfig_start_scan_timer(void)
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);
53 static void __netconfig_stop_scan_timer(void)
55 netconfig_stop_timer(&scan_timer);
56 INFO("Get scan data timer stopped: %d", scan_timer);
59 void __netconfig_notify_netlink_scan_done(void)
61 GVariantBuilder *builder = 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";
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;
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;
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));
88 wifi_emit_netlink_scan_completed((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
89 g_variant_builder_unref(builder);
91 if (bss_info_list != NULL)
92 g_slist_free_full(bss_info_list, g_free);
95 __netconfig_stop_scan_timer();
96 INFO("NetlinkScanCompleted");
101 static int ack_handler(struct nl_msg *msg, void *user_data)
103 int *err = user_data;
108 static int finish_handler(struct nl_msg *msg, void *user_data)
110 int *ret = user_data;
115 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
118 int *ret = user_data;
123 static int no_seq_check(struct nl_msg *msg, void *user_data)
129 static int __netconfig_family_handler(struct nl_msg *msg, void *user_data)
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;
138 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
140 if (!tb[CTRL_ATTR_MCAST_GROUPS])
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];
146 nla_parse(tb_mc_grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mc_grp), nla_len(mc_grp), NULL);
148 if (!tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID])
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])))
154 grp->id = nla_get_u32(tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID]);
161 static int __netconfig_get_multicast_id(struct nl_sock *socket, const char *family, const char *group)
163 struct nl_msg *msg = NULL;
164 struct nl_cb *cb = NULL;
166 struct netconfig_netlink_scan_handler_args grp = { .group = group, .id = -ENOENT, };
172 cb = nl_cb_alloc(NL_CB_DEFAULT);
178 ctrl_id = genl_ctrl_resolve(socket, "nlctrl");
180 genlmsg_put(msg, 0, 0, ctrl_id, 0, 0, CTRL_CMD_GETFAMILY, 0);
183 NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
185 ret = nl_send_auto_complete(socket, msg);
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);
196 nl_recvmsgs(socket, cb);
209 static void __netconfig_macaddress_str(char *bssid, unsigned char *user_data)
213 for (i = 0; i < 6; i++) {
215 snprintf(bssid, 3, "%02x", user_data[i]);
218 snprintf(bssid, 4, ":%02x", user_data[i]);
224 static void __netconfig_get_vsie(unsigned char *bss_element, int length, char **dst)
228 gboolean vsie_found = FALSE;
231 DBG("Vendor specific data not available");
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];
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);
248 DBG("Failed to allocate memory");
254 for (j = i; j <= (i + len + 1); j++) {
255 snprintf(buf, 3, "%02x", bss_element[j]);
263 static void __netconfig_found_ap(unsigned char *bss_element, int length, char *str)
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", ' ');
279 snprintf(&str[i], 3, "%.2x", data[i]);
283 length -= bss_element[1] + 2;
284 bss_element += bss_element[1] + 2;
288 static int __netconfig_netlink_scan_cb(struct nl_msg *msg, void *user_data)
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, };
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},
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");
310 if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
311 DBG("Failed to parse nested attributes");
314 if ((!bss[NL80211_BSS_BSSID]) || (!bss[NL80211_BSS_INFORMATION_ELEMENTS]))
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);
322 /** Create AP info list. */
323 if (ssid[0] != '\0') {
324 struct bss_scan_info_t *bss_info;
327 bss_info = g_try_new0(struct bss_scan_info_t, 1);
328 if (bss_info == NULL)
331 g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
332 g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
334 g_strlcpy(bss_info->vsie, vsie, strlen(vsie)+1);
337 bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
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;
344 DBG("%s %d %d %s [vsie: %s]", bss_info->bssid, bss_info->freq, bss_info->signal, bss_info->ssid, bss_info->vsie);
346 if (bss_info->ssid[0] == '\0')
349 bss_info_list = g_slist_append(bss_info_list, bss_info);
351 if (scan_timer == 0) {
352 DBG("Start scan timer");
353 __netconfig_start_scan_timer();
360 static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
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;
366 if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
367 DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
369 results->aborted = 0;
370 } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
371 DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
373 results->aborted = 1;
379 static int __netconfig_request_netlink_scan(struct nl_sock *socket, int if_index, int id, char *ssid)
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;
387 int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
388 nl_socket_add_membership(socket, mcid);
392 DBG("Failed to allocate msg");
395 ssids = nlmsg_alloc();
397 DBG("Failed to allocate ssids");
401 cb = nl_cb_alloc(NL_CB_DEFAULT);
403 DBG("Failed to allocate callbacks");
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);
413 nla_put(ssids, 1, 0, "");
415 nla_put(ssids, 1, strlen(ssid), ssid);
416 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
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);
425 /** Send NL80211_CMD_TRIGGER_SCAN to start the scan. */
427 ret = nl_send_auto_complete(socket, msg);
428 DBG("Sent %d bytes to the kernel", ret);
430 ret = nl_recvmsgs(socket, cb);
433 DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
437 while (!results.done)
438 nl_recvmsgs(socket, cb);
440 if (results.aborted) {
446 /** Release memory */
449 nl_socket_drop_membership(socket, mcid);
453 static int __netconfig_initialize_nl80211(netconfig_nl_global *global)
457 global->if_index = __netconfig_get_interface_index(WIFI_IFNAME);
458 if (global->if_index < 0) {
459 DBG("Failed to get interface index");
463 global->socket = nl_socket_alloc();
464 if (!global->socket) {
465 DBG("Failed to allocate netlink socket.");
469 if (genl_connect(global->socket)) {
470 DBG("Failed to connect to generic netlink.");
475 global->id = genl_ctrl_resolve(global->socket, "nl80211");
476 if (global->id < 0) {
477 DBG("Failed to find the nl80211 driver");
485 nl_socket_free(global->socket);
489 static int __netconfig_initialize_nl_msg(netconfig_nl_global *global)
491 if (global == NULL) {
492 DBG("Invalid parameter.");
496 global->msg = nlmsg_alloc();
497 if (global->msg == NULL) {
498 DBG("Failed to allocate netlink message");
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);
510 int handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context)
513 netconfig_nl_global global = {
520 /** Initialize netlink socket */
521 int ret = __netconfig_initialize_nl80211(&global);
523 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
524 wifi_complete_netlink_specific_scan(wifi, context);
528 /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
529 ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, NULL);
531 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
532 wifi_complete_netlink_scan(wifi, context);
536 ret = __netconfig_initialize_nl_msg(&global);
538 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
539 wifi_complete_netlink_specific_scan(wifi, context);
543 ret = nl_send_auto_complete(global.socket, global.msg);
544 DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
546 /** Receive the kernel message. */
547 ret = nl_recvmsgs_default(global.socket);
548 nlmsg_free(global.msg);
550 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
551 wifi_complete_netlink_scan(wifi, context);
555 wifi_complete_netlink_scan(wifi, context);
559 int handle_netlink_specific_scan(Wifi *wifi, GDBusMethodInvocation *context, gchar *ssid)
561 DBG("ssid: %s", ssid);
562 netconfig_nl_global global = {
569 /** Initialize netlink socket */
570 int ret = __netconfig_initialize_nl80211(&global);
572 DBG("__netconfig_initialize_nl80211() failed, error %d", ret);
573 wifi_complete_netlink_specific_scan(wifi, context);
577 /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
578 ret = __netconfig_request_netlink_scan(global.socket, global.if_index, global.id, ssid);
580 DBG("__netconfig_request_netlink_scan() failed, error %d", ret);
581 wifi_complete_netlink_specific_scan(wifi, context);
585 ret = __netconfig_initialize_nl_msg(&global);
587 DBG("__netconfig_initialize_nl_msg() failed, error %d", ret);
588 wifi_complete_netlink_specific_scan(wifi, context);
592 ret = nl_send_auto_complete(global.socket, global.msg);
593 DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
595 /** Receive the kernel message. */
596 ret = nl_recvmsgs_default(global.socket);
597 nlmsg_free(global.msg);
599 DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
600 wifi_complete_netlink_specific_scan(wifi, context);
604 wifi_complete_netlink_specific_scan(wifi, context);