summaryrefslogtreecommitdiff
path: root/netspeed/src/backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'netspeed/src/backend.c')
-rw-r--r--netspeed/src/backend.c562
1 files changed, 497 insertions, 65 deletions
diff --git a/netspeed/src/backend.c b/netspeed/src/backend.c
index a3418f82..572928cc 100644
--- a/netspeed/src/backend.c
+++ b/netspeed/src/backend.c
@@ -27,14 +27,36 @@
#include <sys/sockio.h>
#endif
+#include <glib.h>
+#include <glib/gi18n.h>
#include <glibtop/netlist.h>
#include <glibtop/netload.h>
+#include "backend.h"
+
#ifdef HAVE_IW
- #include <iwlib.h>
+#include <iwlib.h>
#endif /* HAVE_IW */
-#include "backend.h"
+#ifdef HAVE_NL
+
+#include <errno.h>
+#include <linux/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/attr.h>
+#include <netlink/msg.h>
+
+#include "nl80211.h"
+#include "ieee80211.h"
+
+struct nl80211_state {
+ struct nl_sock *sock;
+ int nl80211_id;
+};
+
+#endif /* HAVE_NL */
gboolean
is_dummy_device(const char* device)
@@ -118,14 +140,12 @@ get_default_route(void)
return NULL;
}
-
void
free_devices_list(GList *list)
{
g_list_free_full (list, g_free);
}
-
/* Frees a DevInfo struct and all the stuff it contains
*/
void
@@ -135,17 +155,15 @@ free_device_info(DevInfo *devinfo)
g_free(devinfo->ip);
g_free(devinfo->netmask);
g_free(devinfo->ptpip);
- g_free(devinfo->hwaddr);
g_free(devinfo->ipv6);
g_free(devinfo->essid);
- g_free(devinfo->tx_rate);
- g_free(devinfo->rx_rate);
- g_free(devinfo->sum_rate);
+#ifdef HAVE_NL
+ g_free(devinfo->tx_bitrate);
+ g_free(devinfo->rx_bitrate);
+ g_free(devinfo->channel);
+#endif /* HAVE_NL */
}
-
-
-
static char*
format_ipv4(guint32 ip)
{
@@ -154,7 +172,6 @@ format_ipv4(guint32 ip)
return str;
}
-
static char*
format_ipv6(const guint8 ip[16])
{
@@ -163,7 +180,6 @@ format_ipv6(const guint8 ip[16])
return str;
}
-
/* TODO:
these stuff are not portable because of ioctl
*/
@@ -191,10 +207,11 @@ get_ptp_info(DevInfo *devinfo)
void
-get_device_info(const char *device, DevInfo *devinfo)
+get_device_info (const char *device,
+ DevInfo *devinfo)
{
glibtop_netload netload;
- guint8 *hw;
+ gboolean ptp = FALSE;
g_assert(device);
@@ -204,61 +221,67 @@ get_device_info(const char *device, DevInfo *devinfo)
devinfo->type = DEV_UNKNOWN;
glibtop_get_netload(&netload, device);
- devinfo->tx = netload.bytes_out;
- devinfo->rx = netload.bytes_in;
devinfo->up = (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP) ? TRUE : FALSE);
devinfo->running = (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_RUNNING) ? TRUE : FALSE);
- devinfo->ip = format_ipv4(netload.address);
- devinfo->netmask = format_ipv4(netload.subnet);
- devinfo->ipv6 = format_ipv6(netload.address6);
- devinfo->qual = 0;
- devinfo->essid = NULL;
-
- hw = netload.hwaddress;
- if (hw[6] || hw[7]) {
- devinfo->hwaddr = g_strdup_printf(
- "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
- hw[0], hw[1], hw[2], hw[3],
- hw[4], hw[5], hw[6], hw[7]);
- } else {
- devinfo->hwaddr = g_strdup_printf(
- "%02X:%02X:%02X:%02X:%02X:%02X",
- hw[0], hw[1], hw[2],
- hw[3], hw[4], hw[5]);
- }
- /* stolen from gnome-applets/multiload/linux-proc.c */
-
- if(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) {
- devinfo->type = DEV_LO;
- }
-
-#ifdef HAVE_IW
-
- else if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_WIRELESS)) {
- devinfo->type = DEV_WIRELESS;
- get_wireless_info (devinfo);
- }
-
+ if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) {
+ devinfo->type = DEV_LO;
+ }
+ else if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_WIRELESS)) {
+ devinfo->type = DEV_WIRELESS;
+ }
+ else if(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_POINTOPOINT)) {
+ if (g_str_has_prefix(device, "plip")) {
+ devinfo->type = DEV_PLIP;
+ }
+ else if (g_str_has_prefix(device, "sl")) {
+ devinfo->type = DEV_SLIP;
+ }
+ else {
+ devinfo->type = DEV_PPP;
+ }
+ ptp = TRUE;
+ }
+ else {
+ devinfo->type = DEV_ETHERNET;
+ }
+
+ switch (devinfo->type) {
+#if defined (HAVE_NL)
+ case DEV_WIRELESS:
+ get_wireless_info (devinfo);
+ break;
+#endif /* HAVE_NL */
+ case DEV_LO:
+ break;
+#if defined (HAVE_IW)
+ case DEV_WIRELESS:
+ get_wireless_info (devinfo);
#endif /* HAVE_IW */
-
- else if(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_POINTOPOINT)) {
- if (g_str_has_prefix(device, "plip")) {
- devinfo->type = DEV_PLIP;
- }
- else if (g_str_has_prefix(device, "sl")) {
- devinfo->type = DEV_SLIP;
- }
- else {
- devinfo->type = DEV_PPP;
- }
-
- get_ptp_info(devinfo);
- }
- else {
- devinfo->type = DEV_ETHERNET;
- }
+ default:
+ memcpy (devinfo->hwaddr, netload.hwaddress, 8);
+ break;
+ }
+
+ if (devinfo->running) {
+ devinfo->ip = format_ipv4(netload.address);
+ devinfo->netmask = format_ipv4(netload.subnet);
+ devinfo->ipv6 = format_ipv6(netload.address6);
+#if defined (HAVE_NL)
+ if (devinfo->type != DEV_WIRELESS) {
+ devinfo->tx = netload.bytes_out;
+ devinfo->rx = netload.bytes_in;
+ if (ptp)
+ get_ptp_info (devinfo);
+ }
+#else
+ devinfo->tx = netload.bytes_out;
+ devinfo->rx = netload.bytes_in;
+ if (ptp)
+ get_ptp_info (devinfo);
+#endif /* HAVE_NL */
+ }
}
gboolean
@@ -327,3 +350,412 @@ out:
close (fd);
}
#endif /* HAVE_IW */
+
+#ifdef HAVE_NL
+int iw_debug = 0;
+
+static int
+nl80211_init (struct nl80211_state *state)
+{
+ int err;
+
+ state->sock = nl_socket_alloc ();
+ if (!state->sock) {
+ g_warning ("Failed to allocate netlink socket");
+ return -ENOMEM;
+ }
+
+ if (genl_connect (state->sock)) {
+ g_warning ("Failed to connect to generic netlink");
+ err = -ENOLINK;
+ goto out_handle_destroy;
+ }
+
+ nl_socket_set_buffer_size (state->sock, 8192, 8192);
+
+ /* try to set NETLINK_EXT_ACK to 1, ignoring errors */
+ err = 1;
+ setsockopt (nl_socket_get_fd (state->sock), SOL_NETLINK,
+ NETLINK_EXT_ACK, &err, sizeof (err));
+
+ state->nl80211_id = genl_ctrl_resolve (state->sock, "nl80211");
+ if (state->nl80211_id < 0) {
+ g_warning ("nl80211 not found");
+ err = -ENOENT;
+ goto out_handle_destroy;
+ }
+
+ return 0;
+
+out_handle_destroy:
+ nl_socket_free (state->sock);
+ return err;
+}
+
+
+static void
+nl80211_cleanup (struct nl80211_state *state)
+{
+ nl_socket_free (state->sock);
+}
+
+static int
+scan_cb (struct nl_msg *msg,
+ void *arg)
+{
+ DevInfo *devinfo = (DevInfo*) arg;
+ struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_BSSID] = { },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+ [NL80211_BSS_INFORMATION_ELEMENTS] = { },
+ [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+ [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+ [NL80211_BSS_BEACON_IES] = { },
+ };
+
+ /* Parse and error check. */
+ nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_BSS]) {
+ g_warning ("bss info missing!");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested (bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) {
+ g_warning ("failed to parse nested attributes!");
+ return NL_SKIP;
+ }
+ if (!bss[NL80211_BSS_BSSID]) return NL_SKIP;
+ if (!bss[NL80211_BSS_STATUS]) return NL_SKIP;
+
+ if (nla_get_u32 (bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED) return NL_SKIP;
+ int len = MIN(ETH_ALEN, nla_len (bss[NL80211_BSS_BSSID]));
+ memcpy (devinfo->station_mac_addr, nla_data (bss[NL80211_BSS_BSSID]), len);
+
+ return NL_SKIP;
+}
+
+static void
+parse_bitrate (struct nlattr *bitrate_attr,
+ char *buf,
+ int buflen)
+{
+ int rate = 0;
+ char *pos = buf;
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ };
+
+ if (nla_parse_nested (rinfo, NL80211_RATE_INFO_MAX,
+ bitrate_attr, rate_policy)) {
+ g_warning ("failed to parse nested rate attributes!");
+ return;
+ }
+
+ if (rinfo[NL80211_RATE_INFO_BITRATE32])
+ rate = nla_get_u32 (rinfo[NL80211_RATE_INFO_BITRATE32]);
+ else if (rinfo[NL80211_RATE_INFO_BITRATE])
+ rate = nla_get_u16 (rinfo[NL80211_RATE_INFO_BITRATE]);
+ if (rate > 0)
+ pos += snprintf (pos, buflen - (pos - buf),
+ _("%d.%d MBit/s"), rate / 10, rate % 10);
+ else
+ pos += snprintf (pos, buflen - (pos - buf), _("(unknown)"));
+
+ if (rinfo[NL80211_RATE_INFO_MCS])
+ pos += snprintf (pos, buflen - (pos - buf),
+ _(" MCS %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_MCS]));
+ if (rinfo[NL80211_RATE_INFO_VHT_MCS])
+ pos += snprintf (pos, buflen - (pos - buf),
+ _(" VHT-MCS %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_VHT_MCS]));
+ if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
+ pos += snprintf (pos, buflen - (pos - buf), _(" 40MHz"));
+ if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
+ pos += snprintf (pos, buflen - (pos - buf), _(" 80MHz"));
+ if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
+ pos += snprintf (pos, buflen - (pos - buf), _(" 80P80MHz"));
+ if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
+ pos += snprintf (pos, buflen - (pos - buf), _(" 160MHz"));
+ if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+ pos += snprintf (pos, buflen - (pos - buf), _(" short GI)"));
+ if (rinfo[NL80211_RATE_INFO_VHT_NSS])
+ pos += snprintf (pos, buflen - (pos - buf),
+ _(" VHT-NSS %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_VHT_NSS]));
+ if (rinfo[NL80211_RATE_INFO_HE_MCS])
+ pos += snprintf (pos, buflen - (pos - buf),
+ _(" HE-MCS %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_HE_MCS]));
+ if (rinfo[NL80211_RATE_INFO_HE_NSS])
+ pos += snprintf(pos, buflen - (pos - buf),
+ _(" HE-NSS %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_HE_NSS]));
+ if (rinfo[NL80211_RATE_INFO_HE_GI])
+ pos += snprintf(pos, buflen - (pos - buf),
+ _(" HE-GI %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_HE_GI]));
+ if (rinfo[NL80211_RATE_INFO_HE_DCM])
+ snprintf (pos, buflen - (pos - buf),
+ _(" HE-DCM %d"), nla_get_u8 (rinfo[NL80211_RATE_INFO_HE_DCM]));
+}
+
+static int
+station_cb (struct nl_msg *msg,
+ void *arg)
+{
+ DevInfo *devinfo = (DevInfo*) arg;
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
+ [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+ [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
+ [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
+ [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+ };
+
+ nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
+ genlmsg_attrlen (gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_STA_INFO]) {
+ g_warning ("sta stats missing!");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested (sinfo, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO],
+ stats_policy)) {
+ g_warning ("failed to parse nested attributes!\n");
+ return NL_SKIP;
+ }
+
+ if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) {
+ guint32 rx_bytes = nla_get_u32 (sinfo[NL80211_STA_INFO_RX_BYTES]);
+ devinfo->rx = (guint64) rx_bytes;
+ g_debug ("RX: %" G_GUINT32_FORMAT " bytes (%" G_GUINT32_FORMAT " packets)",
+ rx_bytes,
+ nla_get_u32 (sinfo[NL80211_STA_INFO_RX_PACKETS]));
+ }
+ if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS]) {
+ guint32 tx_bytes = nla_get_u32 (sinfo[NL80211_STA_INFO_TX_BYTES]);
+ devinfo->tx = (guint64) tx_bytes;
+ g_debug ("TX: %" G_GUINT32_FORMAT " bytes (%" G_GUINT32_FORMAT " packets)",
+ tx_bytes,
+ nla_get_u32 (sinfo[NL80211_STA_INFO_TX_PACKETS]));
+ }
+ if (sinfo[NL80211_STA_INFO_SIGNAL]) {
+ int8_t dBm = (int8_t)nla_get_u8 (sinfo[NL80211_STA_INFO_SIGNAL]);
+ g_debug ("signal: %d dBm", dBm);
+ devinfo->rssi = dBm;
+ devinfo->qual = CLAMP (2 * ((int)dBm + 100), 1, 100);
+ }
+ if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
+ char buf[100];
+ parse_bitrate (sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof (buf));
+ g_debug ("rx bitrate: %s", buf);
+ devinfo->rx_bitrate = g_strdup (buf);
+ }
+ if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+ char buf[100];
+ parse_bitrate (sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof (buf));
+ g_debug ("tx bitrate: %s", buf);
+ devinfo->tx_bitrate = g_strdup (buf);
+ }
+ if (sinfo[NL80211_STA_INFO_CONNECTED_TIME]) {
+ devinfo->connected_time = nla_get_u32 (sinfo[NL80211_STA_INFO_CONNECTED_TIME]);
+ g_debug ("connected time: %" G_GUINT32_FORMAT " seconds", devinfo->connected_time);
+ }
+
+ return NL_SKIP;
+}
+
+static char *
+channel_width_name (enum nl80211_chan_width width)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return _("20 MHz (no HT)");
+ case NL80211_CHAN_WIDTH_20:
+ return _("20 MHz");
+ case NL80211_CHAN_WIDTH_40:
+ return _("40 MHz");
+ case NL80211_CHAN_WIDTH_80:
+ return _("80 MHz");
+ case NL80211_CHAN_WIDTH_80P80:
+ return _("80+80 MHz");
+ case NL80211_CHAN_WIDTH_160:
+ return _("160 MHz");
+ case NL80211_CHAN_WIDTH_5:
+ return _("5 MHz");
+ case NL80211_CHAN_WIDTH_10:
+ return _("10 MHz");
+ default:
+ return _("unknown");
+ }
+}
+
+static int
+ieee80211_frequency_to_channel (int freq)
+{
+ /* see 802.11-2007 17.3.8.3.2 and Annex J */
+ if (freq == 2484)
+ return 14;
+ else if (freq < 2484)
+ return (freq - 2407) / 5;
+ else if (freq >= 4910 && freq <= 4980)
+ return (freq - 4000) / 5;
+ else if (freq <= 45000) /* DMG band lower limit */
+ return (freq - 5000) / 5;
+ else if (freq >= 58320 && freq <= 64800)
+ return (freq - 56160) / 2160;
+ else
+ return 0;
+}
+
+static int
+iface_cb (struct nl_msg *msg,
+ void *arg)
+{
+ DevInfo *devinfo = (DevInfo*) arg;
+ struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+
+ nla_parse (tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb_msg[NL80211_ATTR_MAC]) {
+ int len = MIN(ETH_ALEN, nla_len (tb_msg[NL80211_ATTR_MAC]));
+ memcpy (devinfo->hwaddr, nla_data (tb_msg[NL80211_ATTR_MAC]), len);
+ }
+
+ if (tb_msg[NL80211_ATTR_SSID]) {
+ char buf[G_MAXUINT8];
+ int len = nla_len (tb_msg[NL80211_ATTR_SSID]);
+ memcpy (buf, nla_data (tb_msg[NL80211_ATTR_SSID]), len);
+ buf [len] = '\0';
+ devinfo->essid = g_strescape (buf, NULL);
+ g_debug ("ssid: %s", buf);
+ }
+
+ if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
+ uint32_t freq = nla_get_u32 (tb_msg[NL80211_ATTR_WIPHY_FREQ]);
+ char buf[100];
+ char *pos = buf;
+
+ pos += sprintf (pos, _("%d (%d MHz)"),
+ ieee80211_frequency_to_channel (freq), freq);
+
+ if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH])
+ sprintf (pos, _(", width: %s"),
+ channel_width_name (nla_get_u32 (tb_msg[NL80211_ATTR_CHANNEL_WIDTH])));
+
+ devinfo->channel = g_strdup (buf);
+ }
+
+ return NL_SKIP;
+}
+
+void
+get_wireless_info (DevInfo *devinfo)
+{
+ struct nl80211_state nlstate = {.sock = NULL, .nl80211_id = -1};
+ int err, ret;
+ struct nl_msg *msg;
+ signed long long devidx = 0;
+
+ err = nl80211_init (&nlstate);
+ if (err || (nlstate.nl80211_id < 0)) {
+ g_warning ("failed to init netlink");
+ return;
+ }
+
+ devidx = if_nametoindex (devinfo->name);
+
+ /* Get MAC, SSID & channel info from interface message */
+ msg = nlmsg_alloc ();
+ if (!msg) {
+ g_warning ("failed to allocate netlink message");
+ goto cleanup;
+ }
+ genlmsg_put (msg, 0, 0, nlstate.nl80211_id, 0, NLM_F_DUMP, NL80211_CMD_GET_INTERFACE, 0);
+ /* Add message attribute, which interface to use */
+ nla_put_u32 (msg, NL80211_ATTR_IFINDEX, devidx);
+ /* Add the callback */
+ nl_socket_modify_cb (nlstate.sock, NL_CB_VALID, NL_CB_CUSTOM, iface_cb, devinfo);
+ /* Send the message */
+ ret = nl_send_auto (nlstate.sock, msg);
+ g_debug ("NL80211_CMD_GET_INTERFACE sent %d bytes to the kernel", ret);
+ /* Retrieve the kernel's answer */
+ ret = nl_recvmsgs_default (nlstate.sock);
+ nlmsg_free (msg);
+ if (ret < 0) {
+ g_warning ("failed to recive netlink message");
+ }
+
+ if (!devinfo->running)
+ goto cleanup;
+
+ /* Get station MAC from scan message */
+ msg = nlmsg_alloc ();
+ if (!msg) {
+ g_warning ("failed to allocate netlink message");
+ goto cleanup;
+ }
+ genlmsg_put (msg, 0, 0, nlstate.nl80211_id, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
+ /* Add message attribute, which interface to use */
+ nla_put_u32 (msg, NL80211_ATTR_IFINDEX, devidx);
+ /* Add the callback */
+ nl_socket_modify_cb (nlstate.sock, NL_CB_VALID, NL_CB_CUSTOM, scan_cb, devinfo);
+ /* Send the message */
+ ret = nl_send_auto (nlstate.sock, msg);
+ g_debug ("NL80211_CMD_GET_SCAN sent %d bytes to the kernel", ret);
+ /* Retrieve the kernel's answer */
+ ret = nl_recvmsgs_default (nlstate.sock);
+ nlmsg_free (msg);
+ if (ret < 0) {
+ g_warning ("failed to recive netlink message");
+ goto cleanup;
+ }
+
+ if (!devinfo->running)
+ goto cleanup;
+
+ /* Get in/out bitrate/rate/total, siganl quality from station message */
+ msg = nlmsg_alloc ();
+ if (!msg) {
+ g_warning ("failed to allocate netlink message");
+ goto cleanup;
+ }
+ genlmsg_put (msg, 0, 0, nlstate.nl80211_id, 0, NLM_F_DUMP, NL80211_CMD_GET_STATION, 0);
+ /* Add message attribute, which interface to use */
+ nla_put (msg, NL80211_ATTR_MAC, ETH_ALEN, devinfo->station_mac_addr);
+ nla_put_u32 (msg, NL80211_ATTR_IFINDEX, devidx);
+ /* Add the callback */
+ nl_socket_modify_cb (nlstate.sock, NL_CB_VALID, NL_CB_CUSTOM, station_cb, devinfo);
+ /* Send the message */
+ ret = nl_send_auto (nlstate.sock, msg);
+ g_debug ("NL80211_CMD_GET_STATION sent %d bytes to the kernel", ret);
+ /* Retrieve the kernel's answer */
+ ret = nl_recvmsgs_default (nlstate.sock);
+ nlmsg_free (msg);
+ if (ret < 0) {
+ g_warning ("failed to recive netlink message");
+ }
+
+ cleanup:
+ nl80211_cleanup (&nlstate);
+}
+#endif /* HAVE_NL */