aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2025-05-06 18:30:39 +0200
committerPaul Oliver <contact@pauloliver.dev>2025-05-06 21:29:05 +0200
commit965bc45937fdfe2a20bc284c3ccc590d01eee389 (patch)
treee471c8a5da2f09b069dd77f09515b32efbf52f3d
Initial
-rw-r--r--.gitignore2
-rw-r--r--README.md10
-rwxr-xr-xbuild.sh11
-rw-r--r--miniwifi.c134
-rw-r--r--wifi_scan.c897
-rw-r--r--wifi_scan.h119
6 files changed, 1173 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..12fbb72
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+miniwifi
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..466f7dd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# Miniwifi
+
+Tiny wireless access status monitoring app, outputs assigned IP and SSID
+attached to wlan0 each second. Output color changes depending on whether an
+internet connection is available.
+
+`wifi-scan` library was copied from
+[here](https://github.com/bmegli/wifi-scan/tree/master).
+
+Meant to be used with status bar apps like i3-blocks.
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..c14c447
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ "${1}" = "DEBUG" ] ; then
+ gcc -c -ggdb wifi_scan.c -o wifi_scan.o
+ gcc -c -ggdb -DDEBUG -W -Wextra miniwifi.c -o miniwifi.o
+else
+ gcc -c -O3 wifi_scan.c -o wifi_scan.o
+ gcc -c -O3 -W -Wextra miniwifi.c -o miniwifi.o
+fi
+
+gcc wifi_scan.o miniwifi.o -lmnl -o miniwifi
diff --git a/miniwifi.c b/miniwifi.c
new file mode 100644
index 0000000..b85c38a
--- /dev/null
+++ b/miniwifi.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "wifi_scan.h"
+
+#define COLOR_CONNECTED "#859900"
+#define COLOR_DISCONNECTED "#dc322f"
+#define ICON_CONNECTED "f05a9"
+#define ICON_DISCONNECTED "f16bc"
+
+int main() {
+ const char *ip_addr = NULL;
+ const char *ssid = NULL;
+ const char *color = NULL;
+ const char *icon = NULL;
+ const char *fmt = "<span color=\"%s\"><span font=\"Symbols Nerd Font\">&#x%s;</span> %s %s</span>\n";
+
+#ifdef DEBUG
+ for (int i = 0; true; ++i) {
+#else
+ for (;;) {
+#endif
+
+#ifdef DEBUG
+ printf("\nloop #%d:\n", ++i);
+#endif
+
+ color = COLOR_DISCONNECTED;
+ icon = ICON_DISCONNECTED;
+
+ // check if wlan0 has an IP address
+ struct ifreq ifr;
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, "wlan0", IFNAMSIZ - 1);
+
+ int station_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ int ioctl_res = ioctl(station_fd, SIOCGIFADDR, &ifr);
+
+#ifdef DEBUG
+ if (ioctl_res < 0) {
+ perror("ioctl()");
+ }
+#endif
+
+ close(station_fd);
+ ip_addr = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
+
+#ifdef DEBUG
+ printf("ip_addr set to: %s\n", ip_addr);
+#endif
+
+ if (ioctl_res != 0 || strcmp(ip_addr, "0.0.0.0") == 0) {
+ ssid = "-";
+ ip_addr = "-";
+
+#ifdef DEBUG
+ printf("ioctl() failed or ip set to 0:0:0:0, ssid set to: %s, ip_addr set to: %s\n", ssid, ip_addr);
+#endif
+ } else {
+ // get ssid in case we have an IP
+ struct station_info station;
+ wifi_scan_station(wifi_scan_init("wlan0"), &station);
+ ssid = station.ssid;
+
+#ifdef DEBUG
+ printf("ssid set to: %s\n", ssid);
+#endif
+
+ // lastly, check if we can ping google
+ struct sockaddr_in sock_in;
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_port = htons(443);
+ inet_pton(AF_INET, "8.8.8.8", &sock_in.sin_addr);
+
+ int remote_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ int connect_res = connect(remote_fd, (struct sockaddr *)&sock_in, sizeof(sock_in));
+
+#ifdef DEBUG
+ if (connect_res < 0) {
+ perror("connect()");
+ }
+#endif
+
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(remote_fd, &fdset);
+
+ struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+ int select_res = select(remote_fd + 1, NULL, &fdset, NULL, &tv);
+
+#ifdef DEBUG
+ if (select_res < 0) {
+ perror("select()");
+ }
+#endif
+
+ if (select_res == 1) {
+#ifdef DEBUG
+ printf("select() returned 1\n");
+#endif
+
+ int so_error;
+ socklen_t len = sizeof so_error;
+ getsockopt(remote_fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
+
+ // we have a good connection
+ if (so_error == 0) {
+#ifdef DEBUG
+ printf("so_error set to 0, we must have internet\n");
+#endif
+
+ color = COLOR_CONNECTED;
+ icon = ICON_CONNECTED;
+ }
+ }
+
+ close(remote_fd);
+ }
+
+ // print results
+#ifndef DEBUG
+ printf(fmt, color, icon, ssid, ip_addr);
+#endif
+ fflush(stdout);
+ sleep(1);
+ }
+
+ return 0;
+}
diff --git a/wifi_scan.c b/wifi_scan.c
new file mode 100644
index 0000000..6201474
--- /dev/null
+++ b/wifi_scan.c
@@ -0,0 +1,897 @@
+/*
+ * wifi-scan library implementation
+ *
+ * Copyright 2016-2018 (C) Bartosz Meglicki <meglickib@gmail.com>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+ /*
+ * Library Overview
+ *
+ * This library uses netlink nl80211 user-space interface to retrieve wireless device information from kernel-space.
+ * For netlink communication libmnl is used (minimalistic user-space netlink library).
+ *
+ * First concept you need to understand is that netlink uses sockets to communicate between user-space and kernel-space.
+ *
+ * There are 2 netlink communication channels (sockets/buffers)
+ * - for notifications (about triggers, ready scan results)
+ * - for commands (commanding triggers, retrieving scan results, station information)
+ *
+ * wifi_scan_init initializes 2 channels, gets nl80211 id using generic netlink (genetlink), gets id of
+ * multicast group scan and subscribes to this group notifcations using notifications channel.
+ *
+ * wifi_scan_station gets the last (not necessarilly fresh) scan results that are available from the device,
+ * checks which station we are associated with and retrieves information about this station (using commands channel)
+ *
+ * wifi_scan_all reads up any pending notifications, commands a trigger if necessary, waits for the device to gather
+ * results and finally reads scan results with get_scan function (those are fresh results)
+ *
+ * wifi_scan_close frees up resources of two channels and any other resoureces that library uses.
+ *
+ * prepare_nl_messsage/send_nl_message/receive_nl_message are helper functions to simplify common tasks when issuing commands
+ *
+ * validate function simplifies common tasks (validates each attribute against table specifying what is valid)
+ *
+ */
+
+#include "wifi_scan.h"
+
+#include <libmnl/libmnl.h> //netlink libmnl
+#include <linux/nl80211.h> //nl80211 netlink
+#include <linux/genetlink.h> //generic netlink
+
+#include <net/if.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <fcntl.h> //fntnl (set descriptor options)
+#include <errno.h> //errno
+
+// everything needed for sending/receiving with netlink
+struct netlink_channel
+{
+ struct mnl_socket *nl; //netlink socket
+ char *buf; //buffer for messages (in and out)
+ uint16_t nl80211_id; //generic netlink nl80211 id
+ uint32_t ifindex; //the wireless interface number (e.g. interface number for wlan0)
+ uint32_t sequence; //the sequence number of netlink message
+ void *context; //additional data to be stored/used when processing concrete message
+};
+
+// internal library data passed around by user
+struct wifi_scan
+{
+ struct netlink_channel notification_channel;
+ struct netlink_channel command_channel;
+};
+
+// DECLARATIONS AND TOP-DOWN LIBRARY OVERVIEW
+
+// INITIALIZATION
+
+// data needed from CTRL_CMD_GETFAMILY for nl80211, nl80211 id is stored in the channel rather then here
+struct context_CTRL_CMD_GETFAMILY
+{
+ uint32_t id_NL80211_MULTICAST_GROUP_SCAN; //the id of group scan which we need to subscribe to
+};
+
+// public interface - initialize the library for wireless interface (e.g. wlan0)
+struct wifi_scan *wifi_scan_init(const char *interface);
+
+// allocate memory, set initial values, etc.
+static void init_netlink_channel(struct netlink_channel *channel, const char *interface);
+// create netlink sockets for generic netlink
+static void init_netlink_socket(struct netlink_channel *channel);
+
+// execute command to get nl80211 family and process the results
+static int get_family_and_scan_ids(struct netlink_channel *channel);
+// this processes kernel reply for get family request, stores family id
+static int handle_CTRL_CMD_GETFAMILY(const struct nlmsghdr *nlh, void *data);
+// parses multicast groups to get scan multicast group id
+static void parse_CTRL_ATTR_MCAST_GROUPS(struct nlattr *nested, struct netlink_channel *channel);
+
+// subscribes channel to multicast group scan using scan group id
+static void subscribe_NL80211_MULTICAST_GROUP_SCAN(struct netlink_channel *channel, uint32_t scan_group_id);
+
+// CLEANUP
+
+// public interface - cleans up after library
+void wifi_scan_close(struct wifi_scan *wifi);
+// cleans up after single channel
+static void close_netlink_channel(struct netlink_channel *channel);
+
+// SCANNING
+
+// public interface - trigger scan if necessary, retrieve information about all known BSSes
+int wifi_scan_all(struct wifi_scan *wifi, struct bss_info *bss_infos, int bss_infos_length);
+
+// SCANNING - notification related
+
+// the data needed from notifications
+struct context_NL80211_MULTICAST_GROUP_SCAN
+{
+ int new_scan_results; //are new scan results waiting for us?
+ int scan_triggered; //was scan was already triggered by somebody else?
+};
+
+// read but do not block
+static void read_past_notifications(struct netlink_channel *notifications);
+// go non-blocking
+static void set_channel_non_blocking(struct netlink_channel *channel);
+// go back blocking
+static void set_channel_blocking(struct netlink_channel *channel);
+// this handles notifications
+static int handle_NL80211_MULTICAST_GROUP_SCAN(const struct nlmsghdr *nlh, void *data);
+// triggers scan if no results are waiting yet and if it was not already triggered
+static int trigger_scan_if_necessary(struct netlink_channel *commands, struct context_NL80211_MULTICAST_GROUP_SCAN *scanning);
+// triggers the scan
+static int trigger_scan(struct netlink_channel *channel);
+// wait for the notification that scan finished
+static void wait_for_new_scan_results(struct netlink_channel *notifications);
+
+// SCANNING - scan related
+
+// the data needed from new scan results
+struct context_NL80211_CMD_NEW_SCAN_RESULTS
+{
+ struct bss_info *bss_infos;
+ int bss_infos_length;
+ int scanned;
+};
+
+// get scan results cached by the driver
+static int get_scan(struct netlink_channel *channel);
+// process the new scan results
+static int handle_NL80211_CMD_NEW_SCAN_RESULTS(const struct nlmsghdr *nlh, void *data);
+// get the information about bss (nested attribute)
+static void parse_NL80211_ATTR_BSS(struct nlattr *nested, struct netlink_channel *channel);
+// get the information from IE (non-netlink binary data here!)
+static void parse_NL80211_BSS_INFORMATION_ELEMENTS(struct nlattr *attr, char SSID_OUT[33]);
+// get BSSID (mac address)
+static void parse_NL80211_BSS_BSSID(struct nlattr *attr, uint8_t bssid_out[BSSID_LENGTH]);
+
+// STATION
+
+// data needed from command new station
+struct context_NL80211_CMD_NEW_STATION
+{
+ struct station_info *station;
+};
+
+// public interface - get information about station we are associated with
+int wifi_scan_station(struct wifi_scan *wifi,struct station_info *station);
+// get information about station with BSSID
+static int get_station(struct netlink_channel *channel, uint8_t bssid[BSSID_LENGTH]);
+// process command new station
+static int handle_NL80211_CMD_NEW_STATION(const struct nlmsghdr *nlh, void *data);
+// process station info (nested attribute)
+static void parse_NL80211_ATTR_STA_INFO(struct nlattr *nested, struct netlink_channel *channel);
+
+// NETLINK HELPERS
+
+// NETLINK HELPERS - message construction/sending/receiving
+
+// create the message with specified parameters for the channel
+// fill the message with additional attributes as needed with:
+// mnl_attr_put_[|u8|u16|u32|u64|str|strz] and mnl_attr_nest_[start|end]
+static struct nlmsghdr *prepare_nl_message(uint32_t type, uint16_t flags, uint8_t genl_cmd, struct netlink_channel *channel);
+// send the above message
+static void send_nl_message(struct nlmsghdr *nlh, struct netlink_channel *channel);
+// receive the results and process them using callback function
+static int receive_nl_message(struct netlink_channel *channel, mnl_cb_t callback);
+
+// NETLINK HELPERS - validation
+
+// formal requirements for attribute
+struct attribute_validation
+{
+ int attr; // attribute constant from nl80211.h
+ enum mnl_attr_data_type type; // MNL_TYPE_[UNSPEC|U8|U16|U32|U64|STRING|FLAG|MSECS|NESTED|NESTED_COMPAT|NUL_STRING|BINARY]
+ size_t len; // length in bytes, can be ommitted for attibutes of known size (e.g. U16), can be 0 if unspeciffied
+};
+
+// all information needed to validate attributes
+struct validation_data
+{
+ struct nlattr **attribute_table; //validated attributes are returned here
+ int attribute_length; //at most that many, distinct constants from nl80211.h go here
+ const struct attribute_validation *validation; //vavildate against that table
+ int validation_length;
+};
+
+// data of type struct validation_data*, validate attr against data, this is called for each attribute
+static int validate(const struct nlattr *attr, void *data);
+
+// GENNERAL PURPOSE
+
+// if anything goes wrong...
+static void die(const char *s);
+// as above but scream errno
+static void die_errno(const char *s);
+
+// #####################################################################
+// IMPLEMENTATION
+
+// validate only what we are going to use, note that
+// this lists all the attributes used by the library
+
+const struct attribute_validation NL80211_VALIDATION[]={
+ {CTRL_ATTR_FAMILY_ID, MNL_TYPE_U16},
+ {CTRL_ATTR_MCAST_GROUPS, MNL_TYPE_NESTED} };
+
+const struct attribute_validation NL80211_MCAST_GROUPS_VALIDATION[]={
+ {CTRL_ATTR_MCAST_GRP_ID, MNL_TYPE_U32},
+ {CTRL_ATTR_MCAST_GRP_NAME, MNL_TYPE_STRING} };
+
+const struct attribute_validation NL80211_BSS_VALIDATION[]={
+ {NL80211_BSS_BSSID, MNL_TYPE_BINARY, 6},
+ {NL80211_BSS_FREQUENCY, MNL_TYPE_U32},
+ {NL80211_BSS_INFORMATION_ELEMENTS, MNL_TYPE_BINARY},
+ {NL80211_BSS_STATUS, MNL_TYPE_U32},
+ {NL80211_BSS_SIGNAL_MBM, MNL_TYPE_U32},
+ {NL80211_BSS_SEEN_MS_AGO, MNL_TYPE_U32} };
+
+const struct attribute_validation NL80211_NEW_SCAN_RESULTS_VALIDATION[]={
+ {NL80211_ATTR_IFINDEX, MNL_TYPE_U32},
+ {NL80211_ATTR_SCAN_SSIDS, MNL_TYPE_NESTED},
+ {NL80211_ATTR_BSS, MNL_TYPE_NESTED} };
+
+const struct attribute_validation NL80211_CMD_NEW_STATION_VALIDATION[]={
+ {NL80211_ATTR_STA_INFO, MNL_TYPE_NESTED},
+};
+
+const struct attribute_validation NL80211_STA_INFO_VALIDATION[]={
+ {NL80211_STA_INFO_SIGNAL, MNL_TYPE_U8},
+ {NL80211_STA_INFO_SIGNAL_AVG, MNL_TYPE_U8},
+ {NL80211_STA_INFO_RX_PACKETS, MNL_TYPE_U32},
+ {NL80211_STA_INFO_TX_PACKETS, MNL_TYPE_U32}
+};
+
+const int NL80211_VALIDATION_LENGTH=sizeof(NL80211_VALIDATION)/sizeof(struct attribute_validation);
+const int NL80211_MCAST_GROUPS_VALIDATION_LENGTH=sizeof(NL80211_MCAST_GROUPS_VALIDATION)/sizeof(struct attribute_validation);
+const int NL80211_BSS_VALIDATION_LENGTH=sizeof(NL80211_BSS_VALIDATION)/sizeof(struct attribute_validation);
+const int NL80211_NEW_SCAN_RESULTS_VALIDATION_LENGTH=sizeof(NL80211_NEW_SCAN_RESULTS_VALIDATION)/sizeof(struct attribute_validation);
+const int NL80211_CMD_NEW_STATION_VALIDATION_LENGTH=sizeof(NL80211_CMD_NEW_STATION_VALIDATION)/sizeof(struct attribute_validation);
+const int NL80211_STA_INFO_VALIDATION_LENGTH=sizeof(NL80211_STA_INFO_VALIDATION)/sizeof(struct attribute_validation);
+
+// INITIALIZATION
+
+// public interface - pass wireless interface like wlan0
+struct wifi_scan *wifi_scan_init(const char *interface)
+{
+ struct wifi_scan *wifi=malloc(sizeof(struct wifi_scan));
+ if(wifi==NULL)
+ die("Insufficient memory - malloc(sizeof(struct wifi_data)");
+
+ init_netlink_channel(&wifi->notification_channel, interface);
+
+ struct context_CTRL_CMD_GETFAMILY family_context ={0};
+ wifi->notification_channel.context=&family_context;
+
+ if(get_family_and_scan_ids(&wifi->notification_channel) == -1)
+ die_errno("GetFamilyAndScanId failed");
+
+ if(family_context.id_NL80211_MULTICAST_GROUP_SCAN == 0)
+ die("No scan multicast group in generic netlink nl80211\n");
+
+ init_netlink_channel(&wifi->command_channel, interface);
+ wifi->command_channel.nl80211_id = wifi->notification_channel.nl80211_id;
+
+ subscribe_NL80211_MULTICAST_GROUP_SCAN(&wifi->notification_channel, family_context.id_NL80211_MULTICAST_GROUP_SCAN);
+
+ return wifi;
+}
+
+// prerequisities:
+// - proper interface, e.g. wlan0, wlan1
+static void init_netlink_channel(struct netlink_channel *channel, const char *interface)
+{
+ channel->sequence=1;
+ channel->buf=(char*) malloc(MNL_SOCKET_BUFFER_SIZE);
+
+ if(channel->buf == NULL)
+ die("Insufficent memory for netlink socket buffer");
+
+ channel->ifindex=if_nametoindex(interface);
+
+ if(channel->ifindex==0)
+ die_errno("Incorrect network interface");
+
+ channel->context=NULL;
+
+ init_netlink_socket(channel);
+}
+
+static void init_netlink_socket(struct netlink_channel *channel)
+{
+ channel->nl = mnl_socket_open(NETLINK_GENERIC);
+
+ if (channel->nl == NULL)
+ die_errno("mnl_socket_open");
+
+ if (mnl_socket_bind(channel->nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ die_errno("mnl_socket_bind");
+}
+
+// prerequisities:
+// - channel initialized with init_netlink_channel
+// - channel context of type context_CTRL_CMD_GETFAMILY
+static int get_family_and_scan_ids(struct netlink_channel *channel)
+{
+ struct nlmsghdr *nlh=prepare_nl_message(GENL_ID_CTRL, NLM_F_REQUEST | NLM_F_ACK, CTRL_CMD_GETFAMILY, channel);
+ mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, NL80211_GENL_NAME);
+
+ send_nl_message(nlh, channel);
+
+ return receive_nl_message(channel, handle_CTRL_CMD_GETFAMILY);
+}
+
+// prerequisities:
+// - netlink_channel passed as data
+// - data->context of type struct context_CTRL_CMD_GETFAMILY
+static int handle_CTRL_CMD_GETFAMILY(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTRL_ATTR_MAX+1] = {};
+ struct genlmsghdr *genl = (struct genlmsghdr *)mnl_nlmsg_get_payload(nlh);
+ struct netlink_channel *channel = (struct netlink_channel*)data;
+ struct validation_data vd={tb, CTRL_ATTR_MAX, NL80211_VALIDATION, NL80211_VALIDATION_LENGTH};
+
+ mnl_attr_parse(nlh, sizeof(*genl), validate, &vd);
+
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ die("No family id attribute");
+
+ channel->nl80211_id=mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+
+ if (tb[CTRL_ATTR_MCAST_GROUPS])
+ parse_CTRL_ATTR_MCAST_GROUPS(tb[CTRL_ATTR_MCAST_GROUPS], channel);
+
+ return MNL_CB_OK;
+}
+
+// prerequisities:
+// - data->context of type struct context_CTRL_CMD_GETFAMILY
+static void parse_CTRL_ATTR_MCAST_GROUPS(struct nlattr *nested, struct netlink_channel *channel)
+{
+ struct nlattr *pos;
+
+ mnl_attr_for_each_nested(pos, nested)
+ {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1] = {};
+ struct validation_data vd={tb, CTRL_ATTR_MCAST_GRP_MAX, NL80211_MCAST_GROUPS_VALIDATION, NL80211_MCAST_GROUPS_VALIDATION_LENGTH};
+
+ mnl_attr_parse_nested(pos, validate, &vd);
+
+ if ( tb[CTRL_ATTR_MCAST_GRP_NAME])
+ {
+ const char *name=mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+
+ if( strcmp(name, "scan") == 0 )
+ {
+ if (tb[CTRL_ATTR_MCAST_GRP_ID])
+ {
+ struct context_CTRL_CMD_GETFAMILY *context=channel->context;
+ context->id_NL80211_MULTICAST_GROUP_SCAN= mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
+ }
+ else
+ die("Missing id attribute for scan multicast group");
+ }
+ }
+ }
+}
+
+// prerequisities:
+// - channel initialized with init_netlink_channel
+static void subscribe_NL80211_MULTICAST_GROUP_SCAN(struct netlink_channel *channel, uint32_t scan_group_id)
+{
+ if (mnl_socket_setsockopt(channel->nl, NETLINK_ADD_MEMBERSHIP, &scan_group_id, sizeof(int)) < 0)
+ die_errno("mnl_socket_set_sockopt");
+}
+
+// CLEANUP
+
+// prerequisities:
+// - wifi initialized with wifi_scan_init
+void wifi_scan_close(struct wifi_scan *wifi)
+{
+ close_netlink_channel(&wifi->notification_channel);
+ close_netlink_channel(&wifi->command_channel);
+ free(wifi);
+}
+
+// prerequisities:
+// - channel initalized with init_netlink-channel
+static void close_netlink_channel(struct netlink_channel *channel)
+{
+ free(channel->buf);
+ mnl_socket_close(channel->nl);
+}
+
+
+// SCANNING
+
+// handle also trigger abort
+// public interface
+//
+// prerequisities:
+// - wifi initialized with wifi_scan_init
+// - bss_info table of sized bss_info_length passed
+int wifi_scan_all(struct wifi_scan *wifi, struct bss_info *bss_infos, int bss_infos_length)
+{
+ struct netlink_channel *notifications=&wifi->notification_channel;
+ struct context_NL80211_MULTICAST_GROUP_SCAN scanning={0,0};
+ notifications->context=&scanning;
+
+ struct netlink_channel *commands=&wifi->command_channel;
+ struct context_NL80211_CMD_NEW_SCAN_RESULTS scan_results = {bss_infos, bss_infos_length, 0};
+ commands->context=&scan_results;
+
+ //somebody else might have triggered scanning or even the results can be already waiting
+ read_past_notifications(notifications);
+
+ //if no results yet or scan not triggered then trigger it.
+ //the device can be busy - we have to take it into account
+ if( trigger_scan_if_necessary(commands, &scanning) == -1)
+ return -1; //most likely with errno set to EBUSY
+
+ //now just wait for trigger/new_scan_results
+ wait_for_new_scan_results(notifications);
+
+ //finally read the scan
+ get_scan(commands);
+
+ return scan_results.scanned;
+}
+
+// SCANNING - notification related
+
+// prerequisities
+// - subscribed to scan group with subscribe_NL80211_MULTICAST_GROUP_SCAN
+// - context_NL80211_MULTICAST_GROUP_SCAN set for notifications
+static void read_past_notifications(struct netlink_channel *notifications)
+{
+ set_channel_non_blocking(notifications);
+ int ret, run_ret;
+
+ while( (ret = mnl_socket_recvfrom(notifications->nl, notifications->buf, MNL_SOCKET_BUFFER_SIZE) ) >= 0)
+ {
+ //the line below fills context about past scans/triggers
+ run_ret = mnl_cb_run(notifications->buf, ret, 0, 0, handle_NL80211_MULTICAST_GROUP_SCAN, notifications);
+ if(run_ret <= 0)
+ die_errno("ReadPastNotificationsNonBlocking mnl_cb_run failed");
+ }
+
+ if(ret == -1)
+ if( !(errno == EINPROGRESS || errno == EWOULDBLOCK) )
+ die_errno("ReadPastNotificationsNonBlocking mnl_socket_recv failed");
+ //no more notifications waiting
+ set_channel_blocking(notifications);
+}
+
+// prerequisities
+// - channel initialized with init_netlink_channel
+static void set_channel_non_blocking(struct netlink_channel *channel)
+{
+ int fd = mnl_socket_get_fd(channel->nl);
+ int flags = fcntl(fd, F_GETFL, 0);
+ if(flags == -1)
+ die_errno("SetChannelNonBlocking F_GETFL");
+ if( fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ die_errno("SetChannelNonBlocking F_SETFL");
+}
+
+// prerequisities
+// - channel initialized with init_netlink_channel
+static void set_channel_blocking(struct netlink_channel *channel)
+{
+ int fd = mnl_socket_get_fd(channel->nl);
+ int flags = fcntl(fd, F_GETFL, 0);
+ if(flags == -1)
+ die_errno("SetChannelNonBlocking F_GETFL");
+ if( fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1)
+ die_errno("SetChannelNonBlocking F_SETFL");
+}
+
+// prerequisities:
+// - subscribed to scan group with subscribe_NL80211_MULTICAST_GROUP_SCAN
+// - netlink_channel passed as data
+// - data->context of type struct context_NL80211_MULTICAST_GROUP_SCAN
+static int handle_NL80211_MULTICAST_GROUP_SCAN(const struct nlmsghdr *nlh, void *data)
+{
+ struct netlink_channel *channel=data;
+ struct context_NL80211_MULTICAST_GROUP_SCAN *context = channel->context;
+
+ struct genlmsghdr *genl = (struct genlmsghdr *)mnl_nlmsg_get_payload(nlh);
+
+// printf("Got message type %d seq %d pid %d genl cmd %d \n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ if(genl->cmd == NL80211_CMD_TRIGGER_SCAN)
+ {
+ context->scan_triggered=1;
+// printf("TRIGGER type %u seq %u pid %u genl cmd %u\n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ return MNL_CB_OK; //do nothing for now
+ }
+ else if(genl->cmd == NL80211_CMD_NEW_SCAN_RESULTS)
+ {
+// printf("NEW SCAN RESULTS type %u seq %u pid %u genl cmd %u\n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ if(nlh->nlmsg_pid==0 && nlh->nlmsg_seq==0)
+ context->new_scan_results = 1;
+ return MNL_CB_OK; //do nothing for now
+ }
+ else
+ {
+ fprintf(stderr, "Ignoring generic netlink command type %u seq %u pid %u genl cmd %u\n",nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ return MNL_CB_OK;
+ }
+}
+
+
+// prerequisities:
+// - commands initialized with init_netlink_channel
+// - scanning updated with read_past_notifications
+static int trigger_scan_if_necessary(struct netlink_channel *commands, struct context_NL80211_MULTICAST_GROUP_SCAN *scanning)
+{
+ if(!scanning->new_scan_results && !scanning->scan_triggered)
+ if(trigger_scan(commands) == -1)
+ return -1; //most likely errno set to EBUSY which means hardware is doing something else, try again later
+ return 0;
+}
+
+// prerequisities:
+// - channel initialized with init_netlink_channel
+static int trigger_scan(struct netlink_channel *channel)
+{
+ struct nlmsghdr *nlh=prepare_nl_message(channel->nl80211_id, NLM_F_REQUEST | NLM_F_ACK, NL80211_CMD_TRIGGER_SCAN, channel);
+ mnl_attr_put_u32(nlh, NL80211_ATTR_IFINDEX, channel->ifindex);
+ send_nl_message(nlh, channel);
+ return receive_nl_message(channel, handle_NL80211_CMD_NEW_SCAN_RESULTS);
+}
+
+// prerequisities
+// - channel initalized with init_netlink_channel
+// - subscribed to scan group with subscribe_NL80211_MULTICAST_GROUP_SCAN
+// - context_NL80211_MULTICAST_GROUP_SCAN set for notifications
+static void wait_for_new_scan_results(struct netlink_channel *notifications)
+{
+ struct context_NL80211_MULTICAST_GROUP_SCAN *scanning=notifications->context;
+ int ret;
+
+ while(!scanning->new_scan_results)
+ {
+ if ( (ret = mnl_socket_recvfrom(notifications->nl, notifications->buf, MNL_SOCKET_BUFFER_SIZE)) <=0 )
+ die_errno("Waiting for new scan results failed - mnl_socket_recvfrom");
+
+ if ( (ret=mnl_cb_run(notifications->buf, ret, 0, 0, handle_NL80211_MULTICAST_GROUP_SCAN, notifications)) <=0 )
+ die_errno("Processing notificatoins failed - mnl_cb_run");
+ }
+}
+
+// SCANNING - scan related
+
+// prerequisities:
+// - channel initalized with init_netlink_channel
+// - channel context of type context_NL80211_CMD_NEW_SCAN_RESULTS
+static int get_scan(struct netlink_channel *channel)
+{
+ struct nlmsghdr *nlh=prepare_nl_message(channel->nl80211_id, NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK, NL80211_CMD_GET_SCAN, channel);
+ mnl_attr_put_u32(nlh, NL80211_ATTR_IFINDEX, channel->ifindex);
+
+ send_nl_message(nlh, channel);
+ return receive_nl_message(channel, handle_NL80211_CMD_NEW_SCAN_RESULTS);
+}
+
+// prerequisities:
+// - netlink_channel passed as data
+// - data->context of type context_NL80211_CMD_NEW_SCAN_RESULTS
+static int handle_NL80211_CMD_NEW_SCAN_RESULTS(const struct nlmsghdr *nlh, void *data)
+{
+ struct netlink_channel *channel=data;
+ struct nlattr *tb[NL80211_ATTR_MAX+1] = {};
+ struct validation_data vd={tb, NL80211_ATTR_MAX, NL80211_NEW_SCAN_RESULTS_VALIDATION, NL80211_NEW_SCAN_RESULTS_VALIDATION_LENGTH};
+ struct genlmsghdr *genl = (struct genlmsghdr *)mnl_nlmsg_get_payload(nlh);
+
+// printf("NSR type %u seq %u pid %u genl cmd %u\n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+
+ if(genl->cmd != NL80211_CMD_NEW_SCAN_RESULTS)
+ {
+ fprintf(stderr, "Ignoring generic netlink command %u seq %u pid %u genl cmd %u\n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ return MNL_CB_OK;
+ }
+
+ //seq 0 - notification from kernel, then pid should also 0, if it is result of our scan we have sequence and our pid
+// int new_scan_results= nlh->nlmsg_seq != 0 && nlh->nlmsg_pid != 0;
+
+ mnl_attr_parse(nlh, sizeof(*genl), validate, &vd);
+
+ if(tb[NL80211_ATTR_IFINDEX])
+ {
+ // uint32_t ifindex=mnl_attr_get_u32(tb[NL80211_ATTR_IFINDEX]);
+// printf("ifindex %u\n", ifindex);
+ }
+ if (!tb[NL80211_ATTR_BSS])
+ return MNL_CB_OK;
+
+ parse_NL80211_ATTR_BSS(tb[NL80211_ATTR_BSS], channel);
+
+ return MNL_CB_OK;
+}
+
+// prerequisities:
+// - channel context of type context_NL80211_CMD_NEW_SCAN_RESULTS
+static void parse_NL80211_ATTR_BSS(struct nlattr *nested, struct netlink_channel *channel)
+{
+ struct nlattr *tb[NL80211_BSS_MAX+1] = {};
+ struct validation_data vd={tb, NL80211_BSS_MAX, NL80211_BSS_VALIDATION, NL80211_BSS_VALIDATION_LENGTH};
+ struct context_NL80211_CMD_NEW_SCAN_RESULTS *scan_results = channel->context;
+ struct bss_info *bss = scan_results->bss_infos + scan_results->scanned;
+
+ mnl_attr_parse_nested(nested, validate, &vd);
+
+ enum nl80211_bss_status status=BSS_NONE;
+
+ if(tb[NL80211_BSS_STATUS])
+ status=mnl_attr_get_u32(tb[NL80211_BSS_STATUS]);
+
+ //if we have found associated station store first as last and associated as first
+ if(status==NL80211_BSS_STATUS_ASSOCIATED || status==NL80211_BSS_STATUS_ASSOCIATED || status==NL80211_BSS_STATUS_IBSS_JOINED)
+ {
+ if(scan_results->scanned>0 && scan_results->scanned < scan_results->bss_infos_length)
+ memcpy(bss, scan_results->bss_infos, sizeof(struct bss_info));
+ bss=scan_results->bss_infos;
+ }
+
+ //check bounds, make exception if we have found associated station and replace previous data
+ if(scan_results->bss_infos_length == 0 || ( scan_results->scanned >= scan_results->bss_infos_length && bss != scan_results->bss_infos ) )
+ {
+ ++scan_results->scanned;
+ return;
+ }
+
+ if ( tb[NL80211_BSS_BSSID])
+ parse_NL80211_BSS_BSSID(tb[NL80211_BSS_BSSID], bss->bssid);
+
+ if ( tb[NL80211_BSS_FREQUENCY])
+ bss->frequency = mnl_attr_get_u32(tb[NL80211_BSS_FREQUENCY]);
+
+ if ( tb[NL80211_BSS_INFORMATION_ELEMENTS])
+ parse_NL80211_BSS_INFORMATION_ELEMENTS(tb[NL80211_BSS_INFORMATION_ELEMENTS], bss->ssid);
+
+ if ( tb[NL80211_BSS_SIGNAL_MBM])
+ bss->signal_mbm=mnl_attr_get_u32(tb[NL80211_BSS_SIGNAL_MBM]);
+
+ if ( tb[NL80211_BSS_SEEN_MS_AGO])
+ bss->seen_ms_ago = mnl_attr_get_u32(tb[NL80211_BSS_SEEN_MS_AGO]);
+
+ bss->status=status;
+
+ ++scan_results->scanned;
+}
+
+//This is guesswork! Read up on that!!! I don't think it's netlink in this attribute, some lower beacon layer
+static void parse_NL80211_BSS_INFORMATION_ELEMENTS(struct nlattr *attr, char SSID_OUT[SSID_MAX_LENGTH_WITH_NULL])
+{
+ const char *payload=mnl_attr_get_payload(attr);
+ int len=mnl_attr_get_payload_len(attr);
+ if(len==0 || payload[0]!=0 || payload[1] >= SSID_MAX_LENGTH_WITH_NULL || payload[1] > len-2)
+ {
+ fprintf(stderr, "SSID len 0 or payload not starting from 0 or payload length > 32 or payload length > length-2!\n");
+ SSID_OUT[0]='\0';
+ return;
+ }
+ int ssid_len=payload[1];
+ strncpy(SSID_OUT, payload+2, ssid_len);
+ SSID_OUT[ssid_len]='\0';
+}
+
+static void parse_NL80211_BSS_BSSID(struct nlattr *attr, uint8_t bssid_out[BSSID_LENGTH])
+{
+ const char *payload=mnl_attr_get_payload(attr);
+ int len=mnl_attr_get_payload_len(attr);
+
+ if(len != BSSID_LENGTH)
+ {
+ fprintf(stderr, "BSSID length != %d, ignoring", BSSID_LENGTH);
+ memset(bssid_out, 0, BSSID_LENGTH);
+ return;
+ }
+
+ memcpy(bssid_out, payload, BSSID_LENGTH);
+}
+
+// STATION
+
+// public interface
+//
+// prerequisities:
+// - wifi initialized with wifi_scan_init
+int wifi_scan_station(struct wifi_scan *wifi,struct station_info *station)
+{
+ struct netlink_channel *commands=&wifi->command_channel;
+ struct bss_info bss;
+
+ struct context_NL80211_CMD_NEW_SCAN_RESULTS scan_results = {&bss, 1, 0};
+ commands->context=&scan_results;
+ get_scan(commands);
+
+ if(scan_results.scanned==0)
+ return 0;
+
+ struct context_NL80211_CMD_NEW_STATION station_results = {station};
+ commands->context=&station_results;
+ get_station(commands, bss.bssid);
+
+ memcpy(station->bssid, bss.bssid, BSSID_LENGTH);
+ memcpy(station->ssid, bss.ssid, SSID_MAX_LENGTH_WITH_NULL);
+ station->status=bss.status;
+
+ return 1;
+}
+
+// prerequisites:
+// - channel initalized with init_netlink_channel
+// - context_NL80211_CMD_NEW_STATION set for channel
+static int get_station(struct netlink_channel *channel, uint8_t bssid[BSSID_LENGTH])
+{
+ struct nlmsghdr *nlh=prepare_nl_message(channel->nl80211_id, NLM_F_REQUEST | NLM_F_ACK, NL80211_CMD_GET_STATION, channel);
+ mnl_attr_put_u32(nlh, NL80211_ATTR_IFINDEX, channel->ifindex);
+ mnl_attr_put(nlh, NL80211_ATTR_MAC, BSSID_LENGTH, bssid);
+ send_nl_message(nlh, channel);
+ return receive_nl_message(channel, handle_NL80211_CMD_NEW_STATION);
+}
+
+// prerequisities:
+// - netlink_channel passed as data
+// - data->context of type context_NL80211_CMD_NEW_STATION
+static int handle_NL80211_CMD_NEW_STATION(const struct nlmsghdr *nlh, void *data)
+{
+ struct netlink_channel *channel=data;
+ struct nlattr *tb[NL80211_ATTR_MAX+1] = {};
+ struct validation_data vd={tb, NL80211_ATTR_MAX, NL80211_CMD_NEW_STATION_VALIDATION, NL80211_CMD_NEW_STATION_VALIDATION_LENGTH};
+ struct genlmsghdr *genl = (struct genlmsghdr *)mnl_nlmsg_get_payload(nlh);
+
+ if(genl->cmd != NL80211_CMD_NEW_STATION)
+ {
+ fprintf(stderr, "Ignoring generic netlink command %u seq %u pid %u genl cmd %u\n", nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_pid, genl->cmd);
+ return MNL_CB_OK;
+ }
+
+ mnl_attr_parse(nlh, sizeof(*genl), validate, &vd);
+
+ if(!tb[NL80211_ATTR_STA_INFO]) //or error, no statoin
+ return MNL_CB_OK;
+
+ parse_NL80211_ATTR_STA_INFO(tb[NL80211_ATTR_STA_INFO], channel);
+
+ return MNL_CB_OK;
+}
+
+// prerequisities:
+// - channel context of type context_NL80211_CMD_NEW_STATION
+static void parse_NL80211_ATTR_STA_INFO(struct nlattr *nested, struct netlink_channel *channel)
+{
+ struct nlattr *tb[NL80211_STA_INFO_MAX+1] = {};
+ struct validation_data vd={tb, NL80211_STA_INFO_MAX, NL80211_STA_INFO_VALIDATION, NL80211_STA_INFO_VALIDATION_LENGTH};
+ struct context_NL80211_CMD_NEW_STATION *station_results = channel->context;
+ struct station_info *station= station_results->station;
+
+ mnl_attr_parse_nested(nested, validate, &vd);
+
+ if ( tb[NL80211_STA_INFO_SIGNAL])
+ station->signal_dbm=(int8_t)mnl_attr_get_u8(tb[NL80211_STA_INFO_SIGNAL]);
+ if ( tb[NL80211_STA_INFO_SIGNAL_AVG])
+ station->signal_avg_dbm=(int8_t)mnl_attr_get_u8(tb[NL80211_STA_INFO_SIGNAL_AVG]);
+ if (tb[NL80211_STA_INFO_RX_PACKETS])
+ station->rx_packets=mnl_attr_get_u32(tb[NL80211_STA_INFO_RX_PACKETS]);
+ if (tb[NL80211_STA_INFO_TX_PACKETS])
+ station->tx_packets=mnl_attr_get_u32(tb[NL80211_STA_INFO_TX_PACKETS]);
+}
+
+
+// NETLINK HELPERS
+
+// NETLINK HELPERS - message construction/sending/receiving
+
+// prerequisities:
+// - channel initialized with init_netlink_channel
+static struct nlmsghdr *prepare_nl_message(uint32_t type, uint16_t flags, uint8_t genl_cmd, struct netlink_channel *channel)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+
+ nlh = mnl_nlmsg_put_header(channel->buf);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = flags;
+ nlh->nlmsg_seq = channel->sequence;
+
+ genl = (struct genlmsghdr*)mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = genl_cmd;
+ genl->version = 1;
+ return nlh;
+}
+
+// prerequisities:
+// - prepare_nl_message called first
+// - mnl_attr_put_xxx used if additional attributes needed
+static void send_nl_message(struct nlmsghdr *nlh, struct netlink_channel *channel)
+{
+ if (mnl_socket_sendto(channel->nl, nlh, nlh->nlmsg_len) < 0)
+ die_errno("mnl_socket_sendto");
+}
+
+// prerequisities:
+// - send_nl_message called first
+// - prerequisities for callback matched
+static int receive_nl_message(struct netlink_channel *channel, mnl_cb_t callback)
+{
+ int ret;
+ unsigned int portid = mnl_socket_get_portid(channel->nl);
+
+ ret = mnl_socket_recvfrom(channel->nl, channel->buf, MNL_SOCKET_BUFFER_SIZE);
+
+ while (ret > 0)
+ {
+ ret = mnl_cb_run(channel->buf, ret, channel->sequence, portid, callback, channel);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(channel->nl, channel->buf, MNL_SOCKET_BUFFER_SIZE);
+ }
+
+ ++channel->sequence;
+
+ return ret;
+}
+
+// NETLINK HELPERS - validation
+
+// prerequisities:
+// - data of type validation_data
+static int validate(const struct nlattr *attr, void *data)
+{
+ struct validation_data *vd=data;
+ const struct nlattr **tb = (const struct nlattr**) vd->attribute_table;
+ int type = mnl_attr_get_type(attr) ,i;
+
+// printf("%d\n", type);
+
+ if (mnl_attr_type_valid(attr, vd->attribute_length) < 0)
+ return MNL_CB_OK;
+
+ for(i=0; i < vd->validation_length;++i)
+ if(type == vd->validation[i].attr)
+ {
+ int len=vd->validation[i].len;
+ if(len==0 && mnl_attr_validate(attr, vd->validation[i].type) < 0)
+ {
+ perror("mnl_attr_validate error");
+ return MNL_CB_ERROR;
+ }
+ if(len != 0 && mnl_attr_validate2(attr, vd->validation[i].type, len) < 0)
+ {
+ perror("mnl_attr_validate error");
+ return MNL_CB_ERROR;
+ }
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+// GENNERAL PURPOSE
+
+static void die(const char *s)
+{
+ fprintf(stderr, "%s", s);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static void die_errno(const char *s)
+{
+ perror(s);
+ exit(1);
+}
diff --git a/wifi_scan.h b/wifi_scan.h
new file mode 100644
index 0000000..95a5550
--- /dev/null
+++ b/wifi_scan.h
@@ -0,0 +1,119 @@
+/*
+ * wifi-scan library header
+ *
+ * Copyright 2016-2018 (C) Bartosz Meglicki <meglickib@gmail.com>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+// some constants - mac address length, mac adress string length, max length of wireless network id with null character
+enum wifi_constants {BSSID_LENGTH=6, BSSID_STRING_LENGTH=18, SSID_MAX_LENGTH_WITH_NULL=33};
+// anything >=0 should mean that your are associated with the station
+enum bss_status{BSS_NONE=-1, BSS_AUTHENTHICATED=0, BSS_ASSOCIATED=1, BSS_IBSS_JOINED=2};
+
+// internal data used by the functions
+struct wifi_scan;
+
+// a single wireless network can have multiple BSSes working as network under one SSID
+struct bss_info
+{
+ uint8_t bssid[BSSID_LENGTH]; //this is hardware mac address of your AP
+ uint32_t frequency; // this is AP frequency in mHz
+ char ssid[SSID_MAX_LENGTH_WITH_NULL]; //this is the name of your AP as you see it when connecting
+ enum bss_status status; //anything >=0 means that your are connected to this station/network
+ int32_t signal_mbm; //signal strength in mBm, divide it by 100 to get signal in dBm
+ int32_t seen_ms_ago; //when the above information was collected
+};
+
+// like above
+struct station_info
+{
+ uint8_t bssid[BSSID_LENGTH]; //this is hardware mac address of your AP
+ char ssid[SSID_MAX_LENGTH_WITH_NULL]; //this is the name of your AP as you see it when connecting
+ enum bss_status status; //anything >=0 means that your are connected to this station/network
+ int8_t signal_dbm; //signal strength in dBm from last received PPDU
+ int8_t signal_avg_dbm; //signal strength average in dBm
+ uint32_t rx_packets; //the number of received packets
+ uint32_t tx_packets; //the number of transmitted packets
+};
+
+/* Initializes the library
+ *
+ * If this functions fails the library will die with error message explaining why
+ *
+ * parameters:
+ * interface - wireless interface, e.g. wlan0, wlan1
+ *
+ * returns:
+ * struct wifi_scan * - pass it to all the functions in the library
+ *
+ */
+struct wifi_scan *wifi_scan_init(const char *interface);
+
+/* Frees the resources used by library
+ *
+ * parameters:
+ * wifi - library data initialized with wifi_scan_init
+ *
+ * preconditions:
+ * wifi initialized with wifi_scan_init
+ */
+void wifi_scan_close(struct wifi_scan *wifi);
+
+/* Retrieve information about station you are associated to
+ *
+ * Retrieves information only about single station.
+ * This function can be called repeateadly fast.
+ *
+ * parameters:
+ * wifi - library data initialized with wifi_scan_init
+ * station - to be filled with information
+ *
+ * returns:
+ * -1 on error (errno is set), 0 if not associated to any station, 1 if data was retrieved
+ *
+ * preconditions:
+ * wifi initialized with wifi_scan_init
+ *
+ */
+int wifi_scan_station(struct wifi_scan *wifi, struct station_info *station);
+
+/* Make a passive scan of all networks around.
+ *
+ * This function triggers passive scan if necessery, waits for completion and returns the data.
+ * If some other scan was triggered in the meanwhile the library will collect it's results.
+ * Triggering a scan requires permissions, for testing you may use sudo.
+ *
+ * Scanning may take some time (it can be order of second).
+ * While scanning the link may be unusable for other programs!
+ *
+ * parameters:
+ * wifi - library data initialized with wifi_scan_init
+ * bss_infos - array of bss_info of size bss_infos_length
+ * bss_infos_length - the length of passed array
+ *
+ * returns:
+ * -1 on error (errno is set) or the number of found BSSes, the number may be greater then bss_infos_length
+ *
+ * Some devices may fail with -1 and errno=EBUSY if triggering scan when another scan is in progress. You may wait and retry in that case
+ *
+ * preconditions:
+ * wifi initialized with wifi_scan_init
+ *
+ */
+int wifi_scan_all(struct wifi_scan *wifi, struct bss_info *bss_infos, int bss_infos_length);
+
+#ifdef __cplusplus
+}
+#endif