--- 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.2Received 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