[hackers] [slstatus][PATCH 2/2] wifi: switch to nl80211 interface

From: Joakim Sindholt <opensource_AT_zhasha.com>
Date: Tue, 30 Jul 2024 22:15:44 +0200

---
 components/wifi.c | 267 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 210 insertions(+), 57 deletions(-)
diff --git a/components/wifi.c b/components/wifi.c
index dffb0df..d639cef 100644
--- a/components/wifi.c
+++ b/components/wifi.c
_AT_@ -18,86 +18,239 @@ RSSI_TO_PERC(int rssi)
 }
 
 #if defined(__linux__)
-	#include <limits.h>
-	#include <linux/wireless.h>
+	#include <stdint.h>
+	#include <net/if.h>
+	#include <linux/netlink.h>
+	#include <linux/genetlink.h>
+	#include <linux/nl80211.h>
 
-	#define NET_OPERSTATE "/sys/class/net/%s/operstate"
+	static int nlsock = -1;
+	static uint32_t seq = 1;
+	static char resp[4096];
 
-	const char *
-	wifi_perc(const char *interface)
+	static char *
+	findattr(int attr, char *p, char *e, size_t *len)
 	{
-		int cur;
-		size_t i;
-		char *p, *datastart;
-		char path[PATH_MAX];
-		char status[5];
-		FILE *fp;
-
-		if (esnprintf(path, sizeof(path), NET_OPERSTATE, interface) < 0)
-			return NULL;
-		if (!(fp = fopen(path, "r"))) {
-			warn("fopen '%s':", path);
-			return NULL;
+		struct nlattr nla;
+		size_t alen;
+
+		while ((size_t)(e-p) >= sizeof(nla)) {
+			memcpy(&nla, p, sizeof(nla));
+			if (nla.nla_len < NLA_HDRLEN)
+				return NULL;
+			if (nla.nla_type == attr) {
+				p += NLA_HDRLEN;
+				*len = nla.nla_len-NLA_HDRLEN;
+				return (size_t)(e-p)>=*len?p:NULL;
+			}
+			alen = NLA_ALIGN(nla.nla_len);
+			if ((size_t)(e-p) < alen)
+				return NULL;
+			p += alen;
 		}
-		p = fgets(status, 5, fp);
-		fclose(fp);
-		if (!p || strcmp(status, "up\n") != 0)
-			return NULL;
+		return NULL;
+	}
 
-		if (!(fp = fopen("/proc/net/wireless", "r"))) {
-			warn("fopen '/proc/net/wireless':");
-			return NULL;
+	static uint16_t
+	nl80211fam(void)
+	{
+		static const char family[] = "nl80211";
+		static uint16_t id;
+		ssize_t r;
+		size_t len;
+		char ctrl[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(sizeof(family))] = {0}, *p = ctrl;
+
+		if (id)
+			return id;
+
+		memcpy(p, &(struct nlmsghdr){
+			.nlmsg_len = sizeof(ctrl),
+			.nlmsg_type = GENL_ID_CTRL,
+			.nlmsg_flags = NLM_F_REQUEST,
+			.nlmsg_seq = seq++,
+			.nlmsg_pid = 0,
+		}, sizeof(struct nlmsghdr));
+		p += NLMSG_HDRLEN;
+		memcpy(p, &(struct genlmsghdr){
+			.cmd = CTRL_CMD_GETFAMILY,
+			.version = 1,
+		}, sizeof(struct genlmsghdr));
+		p += GENL_HDRLEN;
+		memcpy(p, &(struct nlattr){
+			.nla_len = NLA_HDRLEN+sizeof(family),
+			.nla_type = CTRL_ATTR_FAMILY_NAME,
+		}, sizeof(struct nlattr));
+		p += NLA_HDRLEN;
+		memcpy(p, family, sizeof(family));
+
+		if (nlsock < 0)
+			nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+		if (nlsock < 0) {
+			warn("socket 'AF_NETLINK':");
+			return 0;
 		}
+		if (send(nlsock, ctrl, sizeof(ctrl), 0) != sizeof(ctrl)) {
+			warn("send 'AF_NETLINK':");
+			return 0;
+		}
+		r = recv(nlsock, resp, sizeof(resp), 0);
+		if (r < 0) {
+			warn("recv 'AF_NETLINK':");
+			return 0;
+		}
+		if ((size_t)r <= sizeof(ctrl))
+			return 0;
+		p = findattr(CTRL_ATTR_FAMILY_ID, resp+sizeof(ctrl), resp+r, &len);
+		if (p && len == 2)
+			memcpy(&id, p, 2);
+		return id;
+	}
 
-		for (i = 0; i < 3; i++)
-			if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
-				break;
-
-		fclose(fp);
-		if (i < 2 || !p)
-			return NULL;
-
-		if (!(datastart = strstr(buf, interface)))
-			return NULL;
-
-		datastart = (datastart+(strlen(interface)+1));
-		sscanf(datastart + 1, " %*d   %d  %*d  %*d\t\t  %*d\t   "
-		       "%*d\t\t%*d\t\t %*d\t  %*d\t\t %*d", &cur);
-
-		/* 70 is the max of /proc/net/wireless */
-		return bprintf("%d", (int)((float)cur / 70 * 100));
+	static int
+	ifindex(const char *interface)
+	{
+		static struct ifreq ifr;
+		static int ifsock = -1;
+
+		if (ifsock < 0)
+			ifsock = socket(AF_UNIX, SOCK_DGRAM, 0);
+		if (ifsock < 0) {
+			warn("socket 'AF_UNIX':");
+			return -1;
+		}
+		if (strcmp(ifr.ifr_name, interface) != 0) {
+			strcpy(ifr.ifr_name, interface);
+			if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) {
+				warn("ioctl 'SIOCGIFINDEX':");
+				return -1;
+			}
+		}
+		return ifr.ifr_ifindex;
 	}
 
 	const char *
 	wifi_essid(const char *interface)
 	{
-		static char id[IW_ESSID_MAX_SIZE+1];
-		int sockfd;
-		struct iwreq wreq;
+		uint16_t fam = nl80211fam();
+		ssize_t r;
+		size_t len;
+		char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req;
+		int idx = ifindex(interface);
 
-		memset(&wreq, 0, sizeof(struct iwreq));
-		wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
-		if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s",
-		              interface) < 0)
+		if (!fam) {
+			fprintf(stderr, "nl80211 family not found\n");
+			return NULL;
+		}
+		if (idx < 0) {
+			fprintf(stderr, "interface %s not found\n", interface);
 			return NULL;
+		}
 
-		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-			warn("socket 'AF_INET':");
+		memcpy(p, &(struct nlmsghdr){
+			.nlmsg_len = sizeof(req),
+			.nlmsg_type = fam,
+			.nlmsg_flags = NLM_F_REQUEST,
+			.nlmsg_seq = seq++,
+			.nlmsg_pid = 0,
+		}, sizeof(struct nlmsghdr));
+		p += NLMSG_HDRLEN;
+		memcpy(p, &(struct genlmsghdr){
+			.cmd = NL80211_CMD_GET_INTERFACE,
+			.version = 1,
+		}, sizeof(struct genlmsghdr));
+		p += GENL_HDRLEN;
+		memcpy(p, &(struct nlattr){
+			.nla_len = NLA_HDRLEN+4,
+			.nla_type = NL80211_ATTR_IFINDEX,
+		}, sizeof(struct nlattr));
+		p += NLA_HDRLEN;
+		memcpy(p, &(uint32_t){idx}, 4);
+
+		if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
+			warn("send 'AF_NETLINK':");
 			return NULL;
 		}
-		wreq.u.essid.pointer = id;
-		if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) {
-			warn("ioctl 'SIOCGIWESSID':");
-			close(sockfd);
+		r = recv(nlsock, resp, sizeof(resp), 0);
+		if (r < 0) {
+			warn("recv 'AF_NETLINK':");
 			return NULL;
 		}
+		if ((size_t)r <= NLMSG_HDRLEN+GENL_HDRLEN)
+			return NULL;
+		p = findattr(NL80211_ATTR_SSID, resp+NLMSG_HDRLEN+GENL_HDRLEN, resp+r, &len);
+		if (p)
+			p[len] = 0;
+		return p;
+	}
 
-		close(sockfd);
+	const char *
+	wifi_perc(const char *interface)
+	{
+		static char strength[4];
+		struct nlmsghdr hdr;
+		uint16_t fam = nl80211fam();
+		ssize_t r;
+		size_t len;
+		char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req, *e;
+		int idx = ifindex(interface);
 
-		if (!strcmp(id, ""))
+		if (!fam) {
+			fprintf(stderr, "nl80211 family not found\n");
 			return NULL;
+		}
+		if (idx < 0) {
+			fprintf(stderr, "interface %s not found\n", interface);
+			return NULL;
+		}
 
-		return id;
+		memcpy(p, &(struct nlmsghdr){
+			.nlmsg_len = sizeof(req),
+			.nlmsg_type = fam,
+			.nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP,
+			.nlmsg_seq = seq++,
+			.nlmsg_pid = 0,
+		}, sizeof(struct nlmsghdr));
+		p += NLMSG_HDRLEN;
+		memcpy(p, &(struct genlmsghdr){
+			.cmd = NL80211_CMD_GET_STATION,
+			.version = 1,
+		}, sizeof(struct genlmsghdr));
+		p += GENL_HDRLEN;
+		memcpy(p, &(struct nlattr){
+			.nla_len = NLA_HDRLEN+4,
+			.nla_type = NL80211_ATTR_IFINDEX,
+		}, sizeof(struct nlattr));
+		p += NLA_HDRLEN;
+		memcpy(p, &(uint32_t){idx}, 4);
+
+		if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
+			warn("send 'AF_NETLINK':");
+			return NULL;
+		}
+		*strength = 0;
+		while (1) {
+			r = recv(nlsock, resp, sizeof(resp), 0);
+			if (r < 0) {
+				warn("recv 'AF_NETLINK':");
+				return NULL;
+			}
+			if ((size_t)r < sizeof(hdr))
+				return NULL;
+			for (p = resp; p != resp+r && (size_t)(resp+r-p) >= sizeof(hdr); p = e) {
+				memcpy(&hdr, p, sizeof(hdr));
+				e = resp+r-p<hdr.nlmsg_len?resp+r:p+hdr.nlmsg_len;
+				if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) {
+					p += NLMSG_HDRLEN+GENL_HDRLEN;
+					p = findattr(NL80211_ATTR_STA_INFO, p, e, &len);
+					if (p)
+						p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len);
+					if (p && len == 1)
+						snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p));
+				}
+				if (hdr.nlmsg_type == NLMSG_DONE)
+					return *strength?strength:NULL;
+			}
+		}
 	}
 #elif defined(__OpenBSD__)
 	#include <net/if.h>
-- 
2.44.2
Received on Tue Jul 30 2024 - 22:15:44 CEST

This archive was generated by hypermail 2.3.0 : Tue Jul 30 2024 - 22:36:36 CEST