[net-config] Added Support of Netlink scan. 80/160580/3
authorNiraj Kumar Goit <niraj.g@samsung.com>
Fri, 17 Nov 2017 03:43:44 +0000 (09:13 +0530)
committerNiraj Kumar Goit <niraj.g@samsung.com>
Mon, 27 Nov 2017 10:15:18 +0000 (15:45 +0530)
'NetlinkScan' Dbus method is added to directly trigger
driver scan when wifi is in deactivated state.

Change-Id: Ic5ec451de8f07458c3473e949bd68b999d408df0
Signed-off-by: Niraj Kumar Goit <niraj.g@samsung.com>
CMakeLists.txt
include/util.h
include/wifi-netlink-scan.h [new file with mode: 0755]
interfaces/netconfig-iface-wifi.xml
packaging/net-config.spec
resources/etc/dbus-1/system.d/net-config.conf
src/utils/util.c
src/wifi-netlink-scan.c [new file with mode: 0755]
src/wifi.c

index 1069cdd..8ac8f89 100755 (executable)
@@ -15,6 +15,7 @@ SET(SRCS
        src/wifi-eap.c
        src/wifi-wps.c
        src/wifi-bssid-scan.c
+       src/wifi-netlink-scan.c
        src/wifi-agent.c
        src/wifi-power.c
        src/wifi-state.c
@@ -81,6 +82,7 @@ PKG_CHECK_MODULES(pkgs REQUIRED
        gio-unix-2.0
        capi-system-info
        libtzplatform-config
+       libnl-2.0
        ${P2P_REQUIRED_PKGS}
        ${WEARABLE_REQUIRED_PKGS}
        )
index 4115fc4..299d8d3 100755 (executable)
@@ -51,6 +51,7 @@ gboolean netconfig_is_wifi_tethering_on(void);
 
 gboolean netconfig_interface_up(const char *ifname);
 gboolean netconfig_interface_down(const char *ifname);
+int __netconfig_get_interface_index(const char *interface_name);
 
 int netconfig_execute_cmd(const char *cmd);
 int netconfig_execute_file(const char *file_path, char *const args[], char *const env[]);
diff --git a/include/wifi-netlink-scan.h b/include/wifi-netlink-scan.h
new file mode 100755 (executable)
index 0000000..e0c52c3
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Network Configuration Module
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __NETCONFIG_WIFI_NETLINK_SCAN_H__
+#define __NETCONFIG_WIFI_NETLINK_SCAN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "wifi.h"
+
+#define NETCONFIG_SSID_LEN                     32
+#define NETCONFIG_BSSID_LEN                    17
+#define NETCONFIG_MAX_VSIE_LEN                 255
+
+#define NL80211_CMD_SCAN_ABORTED               35
+#define NL80211_CMD_GET_SCAN                   32
+#define NL80211_CMD_TRIGGER_SCAN               33
+#define NL80211_CMD_NEW_SCAN_RESULTS           34
+#define NL80211_BSS_BSSID                      1
+#define NL80211_BSS_FREQUENCY                  2
+#define NL80211_BSS_INFORMATION_ELEMENTS       6
+#define NL80211_BSS_SIGNAL_MBM                 7
+#define NL80211_BSS_MAX                                15
+#define NL80211_ATTR_BSS                       47
+#define NL80211_ATTR_IFINDEX                   3
+#define NL80211_ATTR_SCAN_SSIDS                        45
+#define NL80211_ATTR_MAX                       221
+
+struct bss_scan_info_t{
+       char bssid[NETCONFIG_BSSID_LEN+1];
+       char ssid[NETCONFIG_SSID_LEN+1];
+       char vsie[NETCONFIG_MAX_VSIE_LEN+1];
+       int ssid_len;
+       int freq;
+       int signal;
+};
+
+struct netconfig_netlink_scan_results {
+    int done;
+    int aborted;
+};
+
+struct netconfig_netlink_scan_handler_args {
+    const char *group;
+    int id;
+};
+
+void __netconfig_notify_netlink_scan_done(void);
+gboolean handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NETCONFIG_WIFI_NETLINK_SCAN_H__ */
index 54c665a..1065910 100755 (executable)
@@ -45,6 +45,8 @@
                </method>
                <method name="RequestBssidScan">
                </method>
+               <method name="NetlinkScan">
+               </method>
                <method name="RequestWpsCancel">
                </method>
                <method name="RequestWpsConnect">
                <signal name="BssidScanCompleted">
                        <arg type="a{sv}" name="Fields" direction="out"/>
                </signal>
+               <signal name="NetlinkScanCompleted">
+                       <arg type="a{sv}" name="Fields" direction="out"/>
+               </signal>
                <signal name="IpConflictEvent">
                        <arg type="a{sv}" name="Fields" direction="out"/>
                </signal>
index 7805980..15ef805 100755 (executable)
@@ -14,6 +14,7 @@ BuildRequires:        pkgconfig(capi-vpnsvc)
 BuildRequires: cmake
 BuildRequires: pkgconfig(sqlite3)
 BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires:  pkgconfig(libnl-2.0)
 Requires:              vconf
 Requires:              connman
 Requires:              systemd
index 66997da..dcef481 100755 (executable)
@@ -75,6 +75,7 @@
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="PowerOffCompleted" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="SpecificScanCompleted" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="BssidScanCompleted" privilege="http://tizen.org/privilege/network.get" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="NetlinkScanCompleted" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="IPConflictEvent" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="TdlsDisconnect" privilege="http://tizen.org/privilege/network.set" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="TdlsConnectedPeer" privilege="http://tizen.org/privilege/network.get" />
index b5d96a6..762fe24 100755 (executable)
@@ -557,6 +557,7 @@ int __netconfig_get_interface_index(const char *interface_name)
        close(sock);
 
        if (result < 0) {
+               strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER);
                DBG("Failed to get ifr index: %s", error_buf);
                return -1;
        }
diff --git a/src/wifi-netlink-scan.c b/src/wifi-netlink-scan.c
new file mode 100755 (executable)
index 0000000..7c3f9b2
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Network Configuration Module
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <glib.h>
+
+#include "netsupplicant.h"
+#include "log.h"
+#include "util.h"
+#include "wifi-netlink-scan.h"
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <netlink/netlink.h>
+#include <ctype.h>
+
+static GSList *bss_info_list = NULL;
+static guint scan_timer = 0;
+static unsigned char samsung_oui[3] = {0x00, 0x16, 0x32};
+
+static gboolean __netconfig_scan_timeout(gpointer data)
+{
+       __netconfig_notify_netlink_scan_done();
+
+       return FALSE;
+}
+
+static void __netconfig_start_scan_timer(void)
+{
+       if (scan_timer == 0) {
+               netconfig_start_timer_seconds(5, __netconfig_scan_timeout, NULL, &scan_timer);
+               INFO("Get scan data timer started: %d", scan_timer);
+       }
+}
+
+static void __netconfig_stop_scan_timer(void)
+{
+       netconfig_stop_timer(&scan_timer);
+       INFO("Get scan data timer stopped: %d", scan_timer);
+}
+
+void __netconfig_notify_netlink_scan_done(void)
+{
+       GVariantBuilder *builder = NULL;
+       GSList* list = NULL;
+       const char *prop_ssid = "ssid";
+       const char *prop_bssid = "bssid";
+       const char *prop_freq = "freq";
+       const char *prop_rssi = "rssi";
+       const char *prop_vsie = "vsie";
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       for (list = bss_info_list; list != NULL; list = list->next) {
+               struct bss_scan_info_t *bss_info = (struct bss_scan_info_t *)list->data;
+
+               if (bss_info) {
+                       char *bssid = (char *)bss_info->bssid;
+                       char *ssid = (char *)bss_info->ssid;
+                       char *vsie = (char *)bss_info->vsie;
+                       int freq = (int)bss_info->freq;
+                       int signal = (int)bss_info->signal;
+
+                       g_variant_builder_add(builder, "{sv}", prop_ssid, g_variant_new_string(ssid));
+                       g_variant_builder_add(builder, "{sv}", prop_bssid, g_variant_new_string(bssid));
+                       g_variant_builder_add(builder, "{sv}", prop_freq, g_variant_new_int32(freq));
+                       g_variant_builder_add(builder, "{sv}", prop_rssi, g_variant_new_int32(signal));
+                       g_variant_builder_add(builder, "{sv}", prop_vsie, g_variant_new_string(vsie));
+               }
+       }
+
+       wifi_emit_netlink_scan_completed((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
+       g_variant_builder_unref(builder);
+
+       if (bss_info_list != NULL)
+               g_slist_free_full(bss_info_list, g_free);
+
+       bss_info_list = NULL;
+       __netconfig_stop_scan_timer();
+       INFO("NetlinkScanCompleted");
+
+       return;
+}
+
+static int ack_handler(struct nl_msg *msg, void *user_data)
+{
+       int *err = user_data;
+       *err = 0;
+       return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *user_data)
+{
+       int *ret = user_data;
+       *ret = 0;
+       return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+                        void *user_data)
+{
+       int *ret = user_data;
+       *ret = err->error;
+       return NL_SKIP;
+}
+
+static int no_seq_check(struct nl_msg *msg, void *user_data)
+{
+       DBG("");
+       return NL_OK;
+}
+
+static int __netconfig_family_handler(struct nl_msg *msg, void *user_data)
+{
+       /** Callback for NL_CB_VALID in multicast group */
+       struct netconfig_netlink_scan_handler_args *grp = user_data;
+       struct nlattr *tb[CTRL_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *mc_grp;
+       int rem_mc_grp;
+
+       nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb[CTRL_ATTR_MCAST_GROUPS])
+               return NL_SKIP;
+
+       nla_for_each_nested(mc_grp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mc_grp) {
+               struct nlattr *tb_mc_grp[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+               nla_parse(tb_mc_grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mc_grp), nla_len(mc_grp), NULL);
+
+               if (!tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID])
+                       continue;
+               if (strncmp(nla_data(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME]), grp->group,
+                                       nla_len(tb_mc_grp[CTRL_ATTR_MCAST_GRP_NAME])))
+                       continue;
+
+               grp->id = nla_get_u32(tb_mc_grp[CTRL_ATTR_MCAST_GRP_ID]);
+               break;
+       }
+
+       return NL_SKIP;
+}
+
+static int __netconfig_get_multicast_id(struct nl_sock *socket, const char *family, const char *group)
+{
+       struct nl_msg *msg = NULL;
+       struct nl_cb *cb = NULL;
+       int ret, ctrl_id;
+       struct netconfig_netlink_scan_handler_args grp = { .group = group, .id = -ENOENT, };
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb) {
+               ret = -ENOMEM;
+               goto cb_fail;
+       }
+
+       ctrl_id = genl_ctrl_resolve(socket, "nlctrl");
+
+       genlmsg_put(msg, 0, 0, ctrl_id, 0, 0, CTRL_CMD_GETFAMILY, 0);
+
+       ret = -ENOBUFS;
+       NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+       ret = nl_send_auto_complete(socket, msg);
+       if (ret < 0)
+               goto out;
+
+       ret = 1;
+
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_family_handler, &grp);
+
+       while (ret > 0)
+               nl_recvmsgs(socket, cb);
+
+       if (ret == 0)
+               ret = grp.id;
+
+nla_put_failure:
+out:
+       nl_cb_put(cb);
+cb_fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+static void __netconfig_macaddress_str(char *bssid, unsigned char *user_data)
+{
+       int i;
+
+       for (i = 0; i < 6; i++) {
+               if (i == 0) {
+                       snprintf(bssid, 3, "%02x", user_data[i]);
+                       bssid += 2;
+               } else {
+                       snprintf(bssid, 4, ":%02x", user_data[i]);
+                       bssid += 3;
+               }
+       }
+}
+
+static void __netconfig_get_vsie(unsigned char *bss_element, int length, char **dst)
+{
+       int i = 0;
+       uint8_t len = 0;
+       gboolean vsie_found = FALSE;
+
+       if (length < 3) {
+               DBG("Vendor specific data not available");
+               return;
+       }
+
+       /** Check for vendor specific information element */
+       for (i = 0; i < length; i++) {
+               if (bss_element[i] == 221) {
+                       len = bss_element[i+1];
+                       vsie_found = TRUE;
+                       goto out;
+               }
+       }
+out:
+       if (vsie_found && memcmp(bss_element+i+2, samsung_oui, 3) == 0) {
+               DBG("Vendor Specific IE found, len: %d", len);
+               *dst = g_try_malloc0(2*(len+2) + 1);
+               if (*dst == NULL) {
+                       DBG("Failed to allocate memory");
+                       return;
+               }
+               char *buf = (*dst);
+               int j = 0;
+
+               for (j = i; j <= (i + len + 1); j++) {
+                       snprintf(buf, 3, "%02x", bss_element[j]);
+                       buf += 2;
+               }
+
+               vsie_found = FALSE;
+       }
+}
+
+static void __netconfig_found_ap(unsigned char *bss_element, int length, char *str)
+{
+       uint8_t len;
+       uint8_t *data;
+       int i;
+
+       while (length >= 2 && length >= bss_element[1]) {
+               if (bss_element[0] == 0 && bss_element[1] <= 32) {
+                       len = bss_element[1];
+                       data = bss_element + 2;
+                       for (i = 0; i < len; i++) {
+                               if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\')
+                                       snprintf(&str[i], 2, "%c", data[i]);
+                               else if (data[i] == ' ' && (i != 0 && i != len -1))
+                                       snprintf(&str[i], 2, "%c", ' ');
+                               else
+                                       snprintf(&str[i], 3, "%.2x", data[i]);
+                       }
+                       break;
+               }
+               length -= bss_element[1] + 2;
+               bss_element += bss_element[1] + 2;
+       }
+}
+
+static int __netconfig_netlink_scan_cb(struct nl_msg *msg, void *user_data)
+{
+       /** Called by the kernel with a dump of the successful scan's data. Called for each SSID. */
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       char bssid[NETCONFIG_BSSID_LEN+1];
+       char ssid[NETCONFIG_SSID_LEN+1] = {0, };
+       char *vsie = NULL;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct nlattr *bss[NL80211_BSS_MAX + 1];
+       struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+               [NL80211_BSS_FREQUENCY] = {.type = NLA_U32},
+               [NL80211_BSS_BSSID] = { },
+               [NL80211_BSS_INFORMATION_ELEMENTS] = { },
+               [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32},
+       };
+
+       /** Parse nl message and check error. */
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_BSS]) {
+               DBG("BSS info not available");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
+               DBG("Failed to parse nested attributes");
+               return NL_SKIP;
+       }
+       if ((!bss[NL80211_BSS_BSSID]) || (!bss[NL80211_BSS_INFORMATION_ELEMENTS]))
+               return NL_SKIP;
+
+       /** Extract BSSID and AP info. */
+       __netconfig_macaddress_str(bssid, nla_data(bss[NL80211_BSS_BSSID]));
+       __netconfig_found_ap(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), ssid);
+       __netconfig_get_vsie(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), &vsie);
+
+       /** Create AP info list. */
+       if (ssid[0] != '\0') {
+               struct bss_scan_info_t *bss_info;
+               int signal;
+
+               bss_info = g_try_new0(struct bss_scan_info_t, 1);
+               if (bss_info == NULL)
+                       return NL_SKIP;
+
+               g_strlcpy(bss_info->bssid, bssid, strlen(bssid)+1);
+               g_strlcpy(bss_info->ssid, ssid, strlen(ssid)+1);
+               if (vsie) {
+                       g_strlcpy(bss_info->vsie, vsie, strlen(vsie)+1);
+                       g_free(vsie);
+               }
+               bss_info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+
+               if (bss[NL80211_BSS_SIGNAL_MBM]) {
+                       signal = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+                       signal /= 100; /** mBm to dBm */
+                       bss_info->signal = signal;
+               }
+               DBG("%s %d %d %s [vsie: %s]", bss_info->bssid, bss_info->freq, bss_info->signal, bss_info->ssid, bss_info->vsie);
+
+               if (bss_info->ssid[0] == '\0')
+                       g_free(bss_info);
+               else
+                       bss_info_list = g_slist_append(bss_info_list, bss_info);
+
+               if (scan_timer == 0) {
+                       DBG("Start scan timer");
+                       __netconfig_start_scan_timer();
+               }
+       }
+
+       return NL_SKIP;
+}
+
+static int __netconfig_netlink_scan_reply(struct nl_msg *msg, void *user_data)
+{
+       /** Called by the kernel when the scan is done or has been aborted. */
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct netconfig_netlink_scan_results *results = user_data;
+
+       if (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS) {
+               DBG("Received NL80211_CMD_NEW_SCAN_RESULTS in reply");
+               results->done = 1;
+               results->aborted = 0;
+       } else if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) {
+               DBG("Received NL80211_CMD_SCAN_ABORTED in reply");
+               results->done = 1;
+               results->aborted = 1;
+       }
+
+       return NL_SKIP;
+}
+
+static int __netconfig_request_netlink_scan(struct nl_sock *socket, int if_index, int id)
+{
+       struct netconfig_netlink_scan_results results = { .done = 0, .aborted = 0 };
+       struct nl_msg *msg = NULL;
+       struct nl_cb *cb = NULL;
+       struct nl_msg *ssids = NULL;
+       int err = 0;
+       int ret = 0;
+       int mcid = __netconfig_get_multicast_id(socket, "nl80211", "scan");
+       nl_socket_add_membership(socket, mcid);
+
+       msg = nlmsg_alloc();
+       if (!msg) {
+               DBG("Failed to allocate msg");
+               return -ENOMEM;
+       }
+       ssids = nlmsg_alloc();
+       if (!ssids) {
+               DBG("Failed to allocate ssids");
+               nlmsg_free(msg);
+               return -ENOMEM;
+       }
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb) {
+               DBG("Failed to allocate callbacks");
+               nlmsg_free(msg);
+               nlmsg_free(ssids);
+               return -ENOMEM;
+       }
+
+       /** Set nl message and callback functions. */
+       genlmsg_put(msg, 0, 0, id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
+       nla_put(ssids, 1, 0, "");
+       nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+       nlmsg_free(ssids);
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_reply, &results);
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+       nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+
+       /** Send NL80211_CMD_TRIGGER_SCAN to start the scan. */
+       err = 1;
+       ret = nl_send_auto_complete(socket, msg);
+       DBG("Sent %d bytes to the kernel", ret);
+       while (err > 0)
+               ret = nl_recvmsgs(socket, cb);
+
+       if (ret < 0) {
+               DBG("nl_recvmsgs() ret: %d (%s)", ret, nl_geterror(-ret));
+               return ret;
+       }
+
+       while (!results.done)
+               nl_recvmsgs(socket, cb);
+
+       if (results.aborted) {
+               DBG("scan aborted");
+               return 1;
+       }
+       DBG("Scan done");
+
+       /** Release memory */
+       nlmsg_free(msg);
+       nl_cb_put(cb);
+       nl_socket_drop_membership(socket, mcid);
+       return 0;
+}
+
+gboolean handle_netlink_scan(Wifi *wifi, GDBusMethodInvocation *context)
+{
+       DBG("");
+       int if_index = __netconfig_get_interface_index(WIFI_IFNAME);
+
+       /** Open socket to kernel. */
+       struct nl_sock *socket = nl_socket_alloc();
+       genl_connect(socket);
+       int id = genl_ctrl_resolve(socket, "nl80211");
+
+       /** Request NL80211_CMD_TRIGGER_SCAN to the kernel. */
+       int err = __netconfig_request_netlink_scan(socket, if_index, id);
+       if (err != 0) {
+               DBG("__netconfig_request_netlink_scan() failed, error %d", err);
+               wifi_complete_netlink_scan(wifi, context);
+               return err;
+       }
+
+       /** Get info of all available APs. */
+       struct nl_msg *msg = nlmsg_alloc();
+       genlmsg_put(msg, 0, 0, id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
+       nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, __netconfig_netlink_scan_cb, NULL);
+
+       int ret = nl_send_auto_complete(socket, msg);
+       DBG("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
+
+       /** Receive the kernel message. */
+       ret = nl_recvmsgs_default(socket);
+       nlmsg_free(msg);
+       if (ret < 0) {
+               DBG("nl_recvmsgs_default() failed. ret: %d (error: %s)", ret, nl_geterror(-ret));
+               wifi_complete_netlink_scan(wifi, context);
+               return ret;
+       }
+
+       wifi_complete_netlink_scan(wifi, context);
+       return TRUE;
+}
index 3ec8d94..a50f8bd 100755 (executable)
@@ -31,6 +31,7 @@
 #include "wifi-eap.h"
 #include "wifi-wps.h"
 #include "wifi-bssid-scan.h"
+#include "wifi-netlink-scan.h"
 #include "wifi-power.h"
 #include "wifi-state.h"
 #include "wifi-agent.h"
@@ -166,6 +167,8 @@ void wifi_object_create_and_init(void)
                        G_CALLBACK(handle_request_bssid_scan), NULL);
        g_signal_connect(wifi_object, "handle-get-bssid-list",
                        G_CALLBACK(handle_get_bssid_list), NULL);
+       g_signal_connect(wifi_object, "handle-netlink-scan",
+                       G_CALLBACK(handle_netlink_scan), NULL);
 
        /* WPS Connect */
        g_signal_connect(wifi_object, "handle-request-wps-connect",