| 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> |