wifi: switch to nl80211 interface - slstatus - status monitor | |
git clone git://git.suckless.org/slstatus | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 0b00c0319c7b4a941b40aa8709bb4c478f9a9b1f | |
parent f68f49273e70b3767b30c549dda04ddd4d25fc91 | |
Author: Joakim Sindholt <[email protected]> | |
Date: Tue, 30 Jul 2024 22:15:44 +0200 | |
wifi: switch to nl80211 interface | |
Diffstat: | |
M components/wifi.c | 264 ++++++++++++++++++++++++-----… | |
1 file changed, 206 insertions(+), 58 deletions(-) | |
--- | |
diff --git a/components/wifi.c b/components/wifi.c | |
@@ -15,86 +15,234 @@ | |
(2 * (rssi + 100))) | |
#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) < … | |
- 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… | |
+ | |
+ 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, &… | |
+ 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; | |
- | |
- 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) | |
+ uint16_t fam = nl80211fam(); | |
+ ssize_t r; | |
+ size_t len; | |
+ char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {… | |
+ int idx = ifindex(interface); | |
+ 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; | |
} | |
- close(sockfd); | |
+ if ((size_t)r <= NLMSG_HDRLEN+GENL_HDRLEN) | |
+ return NULL; | |
+ p = findattr(NL80211_ATTR_SSID, resp+NLMSG_HDRLEN+GENL_HDRLEN,… | |
+ if (p) | |
+ p[len] = 0; | |
+ return p; | |
+ } | |
- if (!strcmp(id, "")) | |
+ 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)] = {… | |
+ int idx = ifindex(interface); | |
+ 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) >= si… | |
+ memcpy(&hdr, p, sizeof(hdr)); | |
+ e = resp+r-p<hdr.nlmsg_len?resp+r:p+hdr.nlmsg_… | |
+ if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN… | |
+ p += NLMSG_HDRLEN+GENL_HDRLEN; | |
+ p = findattr(NL80211_ATTR_STA_INFO, p,… | |
+ if (p) | |
+ p = findattr(NL80211_STA_INFO_… | |
+ if (p && len == 1) | |
+ snprintf(strength, sizeof(stre… | |
+ } | |
+ if (hdr.nlmsg_type == NLMSG_DONE) | |
+ return *strength?strength:NULL; | |
+ } | |
+ } | |
} | |
#elif defined(__OpenBSD__) | |
#include <net/if.h> |