URI:
       wifi.c - slstatus - status monitor
  HTML git clone git://git.suckless.org/slstatus
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       wifi.c (9831B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <ifaddrs.h>
            3 #include <stdio.h>
            4 #include <string.h>
            5 #include <sys/ioctl.h>
            6 #include <sys/socket.h>
            7 #include <unistd.h>
            8 
            9 #include "../slstatus.h"
           10 #include "../util.h"
           11 
           12 #define RSSI_TO_PERC(rssi) \
           13                         rssi >= -50 ? 100 : \
           14                         (rssi <= -100 ? 0 : \
           15                         (2 * (rssi + 100)))
           16 
           17 #if defined(__linux__)
           18         #include <stdint.h>
           19         #include <net/if.h>
           20         #include <linux/netlink.h>
           21         #include <linux/genetlink.h>
           22         #include <linux/nl80211.h>
           23 
           24         static int nlsock = -1;
           25         static uint32_t seq = 1;
           26         static char resp[4096 + 1];
           27 
           28         static char *
           29         findattr(int attr, const char *p, const char *e, size_t *len)
           30         {
           31                 while (p < e) {
           32                         struct nlattr nla;
           33                         memcpy(&nla, p, sizeof(nla));
           34                         if (nla.nla_type == attr) {
           35                                 *len = nla.nla_len - NLA_HDRLEN;
           36                                 return (char *)(p + NLA_HDRLEN);
           37                         }
           38                         p += NLA_ALIGN(nla.nla_len);
           39                 }
           40                 return NULL;
           41         }
           42 
           43         static uint16_t
           44         nl80211fam(void)
           45         {
           46                 static const char family[] = "nl80211";
           47                 static uint16_t id;
           48                 ssize_t r;
           49                 size_t len;
           50                 char ctrl[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(sizeof(family))] = {0}, *p = ctrl;
           51 
           52                 if (id)
           53                         return id;
           54 
           55                 memcpy(p, &(struct nlmsghdr){
           56                         .nlmsg_len = sizeof(ctrl),
           57                         .nlmsg_type = GENL_ID_CTRL,
           58                         .nlmsg_flags = NLM_F_REQUEST,
           59                         .nlmsg_seq = seq++,
           60                         .nlmsg_pid = 0,
           61                 }, sizeof(struct nlmsghdr));
           62                 p += NLMSG_HDRLEN;
           63                 memcpy(p, &(struct genlmsghdr){
           64                         .cmd = CTRL_CMD_GETFAMILY,
           65                         .version = 1,
           66                 }, sizeof(struct genlmsghdr));
           67                 p += GENL_HDRLEN;
           68                 memcpy(p, &(struct nlattr){
           69                         .nla_len = NLA_HDRLEN+sizeof(family),
           70                         .nla_type = CTRL_ATTR_FAMILY_NAME,
           71                 }, sizeof(struct nlattr));
           72                 p += NLA_HDRLEN;
           73                 memcpy(p, family, sizeof(family));
           74 
           75                 if (nlsock < 0)
           76                         nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
           77                 if (nlsock < 0) {
           78                         warn("socket 'AF_NETLINK':");
           79                         return 0;
           80                 }
           81                 if (send(nlsock, ctrl, sizeof(ctrl), 0) != sizeof(ctrl)) {
           82                         warn("send 'AF_NETLINK':");
           83                         return 0;
           84                 }
           85                 r = recv(nlsock, resp, sizeof(resp), 0);
           86                 if (r < 0) {
           87                         warn("recv 'AF_NETLINK':");
           88                         return 0;
           89                 }
           90                 if ((size_t)r <= sizeof(ctrl))
           91                         return 0;
           92                 p = findattr(CTRL_ATTR_FAMILY_ID, resp + sizeof(ctrl), resp + r, &len);
           93                 if (p && len == 2)
           94                         memcpy(&id, p, 2);
           95 
           96                 return id;
           97         }
           98 
           99         static int
          100         ifindex(const char *interface)
          101         {
          102                 static struct ifreq ifr;
          103                 static int ifsock = -1;
          104 
          105                 if (ifsock < 0)
          106                         ifsock = socket(AF_UNIX, SOCK_DGRAM, 0);
          107                 if (ifsock < 0) {
          108                         warn("socket 'AF_UNIX':");
          109                         return -1;
          110                 }
          111                 if (strcmp(ifr.ifr_name, interface) != 0) {
          112                         if (snprintf(ifr.ifr_name, sizeof(ifr.ifr_name),
          113                                      "%s", interface) >= (int)sizeof(ifr.ifr_name)) {
          114                                 warn("interface name too long: '%s'", interface);
          115                                 return -1;
          116                         }
          117                 }
          118                 if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) {
          119                         warn("ioctl 'SIOCGIFINDEX':");
          120                         return -1;
          121                 }
          122                 return ifr.ifr_ifindex;
          123         }
          124 
          125         const char *
          126         wifi_essid(const char *interface)
          127         {
          128                 uint16_t fam = nl80211fam();
          129                 ssize_t r;
          130                 size_t len;
          131                 char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req;
          132                 int idx = ifindex(interface);
          133                 if (!fam) {
          134                         fprintf(stderr, "nl80211 family not found\n");
          135                         return NULL;
          136                 }
          137                 if (idx < 0) {
          138                         fprintf(stderr, "interface %s not found\n", interface);
          139                         return NULL;
          140                 }
          141 
          142                 memcpy(p, &(struct nlmsghdr){
          143                         .nlmsg_len = sizeof(req),
          144                         .nlmsg_type = fam,
          145                         .nlmsg_flags = NLM_F_REQUEST,
          146                         .nlmsg_seq = seq++,
          147                         .nlmsg_pid = 0,
          148                 }, sizeof(struct nlmsghdr));
          149                 p += NLMSG_HDRLEN;
          150                 memcpy(p, &(struct genlmsghdr){
          151                         .cmd = NL80211_CMD_GET_INTERFACE,
          152                         .version = 1,
          153                 }, sizeof(struct genlmsghdr));
          154                 p += GENL_HDRLEN;
          155                 memcpy(p, &(struct nlattr){
          156                         .nla_len = NLA_HDRLEN+4,
          157                         .nla_type = NL80211_ATTR_IFINDEX,
          158                 }, sizeof(struct nlattr));
          159                 p += NLA_HDRLEN;
          160                 memcpy(p, &(uint32_t){idx}, 4);
          161 
          162                 if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
          163                         warn("send 'AF_NETLINK':");
          164                         return NULL;
          165                 }
          166                 r = recv(nlsock, resp, sizeof(resp) - 1, 0);
          167                 if (r < 0) {
          168                         warn("recv 'AF_NETLINK':");
          169                         return NULL;
          170                 }
          171 
          172                 if ((size_t)r <= NLMSG_HDRLEN + GENL_HDRLEN)
          173                         return NULL;
          174                 p = findattr(NL80211_ATTR_SSID, resp + NLMSG_HDRLEN + GENL_HDRLEN, resp + r, &len);
          175                 if (p)
          176                         p[len] = 0;
          177 
          178                 return p;
          179         }
          180 
          181         const char *
          182         wifi_perc(const char *interface)
          183         {
          184                 static char strength[4];
          185                 struct nlmsghdr hdr;
          186                 uint16_t fam = nl80211fam();
          187                 ssize_t r;
          188                 size_t len;
          189                 char req[NLMSG_HDRLEN + GENL_HDRLEN + NLA_HDRLEN + NLA_ALIGN(4)] = {0}, *p = req, *e;
          190                 int idx = ifindex(interface);
          191 
          192                 if (idx < 0) {
          193                         fprintf(stderr, "interface %s not found\n", interface);
          194                         return NULL;
          195                 }
          196 
          197                 memcpy(p, &(struct nlmsghdr){
          198                         .nlmsg_len = sizeof(req),
          199                         .nlmsg_type = fam,
          200                         .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP,
          201                         .nlmsg_seq = seq++,
          202                         .nlmsg_pid = 0,
          203                 }, sizeof(struct nlmsghdr));
          204                 p += NLMSG_HDRLEN;
          205                 memcpy(p, &(struct genlmsghdr){
          206                         .cmd = NL80211_CMD_GET_STATION,
          207                         .version = 1,
          208                 }, sizeof(struct genlmsghdr));
          209                 p += GENL_HDRLEN;
          210                 memcpy(p, &(struct nlattr){
          211                         .nla_len = NLA_HDRLEN + 4,
          212                         .nla_type = NL80211_ATTR_IFINDEX,
          213                 }, sizeof(struct nlattr));
          214                 p += NLA_HDRLEN;
          215                 memcpy(p, &idx, 4);
          216 
          217                 if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
          218                         warn("send 'AF_NETLINK':");
          219                         return NULL;
          220                 }
          221 
          222                 *strength = 0;
          223                 while (1) {
          224                         r = recv(nlsock, resp, sizeof(resp), 0);
          225                         if (r < 0) {
          226                                 warn("recv 'AF_NETLINK':");
          227                                 return NULL;
          228                         }
          229                         if ((size_t)r < sizeof(hdr))
          230                                 return NULL;
          231 
          232                         for (p = resp; p != resp + r && (size_t)(resp + r-p) >= sizeof(hdr); p = e) {
          233                                 memcpy(&hdr, p, sizeof(hdr));
          234                                 e = resp + r - p < hdr.nlmsg_len ? resp + r : p + hdr.nlmsg_len;
          235 
          236                                 if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) {
          237                                         p += NLMSG_HDRLEN+GENL_HDRLEN;
          238                                         p = findattr(NL80211_ATTR_STA_INFO, p, e, &len);
          239                                         if (p)
          240                                                 p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len);
          241                                         if (p && len == 1)
          242                                                 snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p));
          243                                 }
          244                                 if (hdr.nlmsg_type == NLMSG_DONE)
          245                                         return *strength ? strength : NULL;
          246                         }
          247                 }
          248         }
          249 #elif defined(__OpenBSD__)
          250         #include <net/if.h>
          251         #include <net/if_media.h>
          252         #include <net80211/ieee80211.h>
          253         #include <sys/select.h> /* before <sys/ieee80211_ioctl.h> for NBBY */
          254         #include <net80211/ieee80211_ioctl.h>
          255         #include <stdlib.h>
          256         #include <sys/types.h>
          257 
          258         static int
          259         load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr)
          260         {
          261                 struct ieee80211_bssid bssid;
          262                 int sockfd;
          263                 uint8_t zero_bssid[IEEE80211_ADDR_LEN];
          264 
          265                 memset(&bssid, 0, sizeof(bssid));
          266                 memset(nr, 0, sizeof(struct ieee80211_nodereq));
          267                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          268                         warn("socket 'AF_INET':");
          269                         return 0;
          270                 }
          271                 strlcpy(bssid.i_name, interface, sizeof(bssid.i_name));
          272                 if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) {
          273                         warn("ioctl 'SIOCG80211BSSID':");
          274                         close(sockfd);
          275                         return 0;
          276                 }
          277                 memset(&zero_bssid, 0, sizeof(zero_bssid));
          278                 if (memcmp(bssid.i_bssid, zero_bssid,
          279                     IEEE80211_ADDR_LEN) == 0) {
          280                         close(sockfd);
          281                         return 0;
          282                 }
          283                 strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname));
          284                 memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr));
          285                 if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) {
          286                         warn("ioctl 'SIOCG80211NODE':");
          287                         close(sockfd);
          288                         return 0;
          289                 }
          290 
          291                 return close(sockfd), 1;
          292         }
          293 
          294         const char *
          295         wifi_perc(const char *interface)
          296         {
          297                 struct ieee80211_nodereq nr;
          298                 int q;
          299 
          300                 if (load_ieee80211_nodereq(interface, &nr)) {
          301                         if (nr.nr_max_rssi)
          302                                 q = IEEE80211_NODEREQ_RSSI(&nr);
          303                         else
          304                                 q = RSSI_TO_PERC(nr.nr_rssi);
          305 
          306                         return bprintf("%d", q);
          307                 }
          308 
          309                 return NULL;
          310         }
          311 
          312         const char *
          313         wifi_essid(const char *interface)
          314         {
          315                 struct ieee80211_nodereq nr;
          316 
          317                 if (load_ieee80211_nodereq(interface, &nr))
          318                         return bprintf("%s", nr.nr_nwid);
          319 
          320                 return NULL;
          321         }
          322 #elif defined(__FreeBSD__)
          323         #include <net/if.h>
          324         #include <net80211/ieee80211_ioctl.h>
          325 
          326         int
          327         load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len)
          328         {
          329                 char warn_buf[256];
          330                 struct ieee80211req ireq;
          331                 memset(&ireq, 0, sizeof(ireq));
          332                 ireq.i_type = type;
          333                 ireq.i_data = (caddr_t) data;
          334                 ireq.i_len = *len;
          335 
          336                 strlcpy(ireq.i_name, interface, sizeof(ireq.i_name));
          337                 if (ioctl(sock, SIOCG80211, &ireq) < 0) {
          338                         snprintf(warn_buf,  sizeof(warn_buf),
          339                                         "ioctl: 'SIOCG80211': %d", type);
          340                         warn(warn_buf);
          341                         return 0;
          342                 }
          343 
          344                 *len = ireq.i_len;
          345                 return 1;
          346         }
          347 
          348         const char *
          349         wifi_perc(const char *interface)
          350         {
          351                 union {
          352                         struct ieee80211req_sta_req sta;
          353                         uint8_t buf[24 * 1024];
          354                 } info;
          355                 uint8_t bssid[IEEE80211_ADDR_LEN];
          356                 int rssi_dbm;
          357                 int sockfd;
          358                 size_t len;
          359                 const char *fmt;
          360 
          361                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          362                         warn("socket 'AF_INET':");
          363                         return NULL;
          364                 }
          365 
          366                 /* Retreive MAC address of interface */
          367                 len = IEEE80211_ADDR_LEN;
          368                 fmt = NULL;
          369                 if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len))
          370                 {
          371                         /* Retrieve info on station with above BSSID */
          372                         memset(&info, 0, sizeof(info));
          373                         memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid));
          374 
          375                         len = sizeof(info);
          376                         if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) {
          377                                 rssi_dbm = info.sta.info[0].isi_noise +
          378                                                   info.sta.info[0].isi_rssi / 2;
          379 
          380                                 fmt = bprintf("%d", RSSI_TO_PERC(rssi_dbm));
          381                         }
          382                 }
          383 
          384                 close(sockfd);
          385                 return fmt;
          386         }
          387 
          388         const char *
          389         wifi_essid(const char *interface)
          390         {
          391                 char ssid[IEEE80211_NWID_LEN + 1];
          392                 size_t len;
          393                 int sockfd;
          394                 const char *fmt;
          395 
          396                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          397                         warn("socket 'AF_INET':");
          398                         return NULL;
          399                 }
          400 
          401                 fmt = NULL;
          402                 len = sizeof(ssid);
          403                 memset(&ssid, 0, len);
          404                 if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len)) {
          405                         if (len < sizeof(ssid))
          406                                 len += 1;
          407                         else
          408                                 len = sizeof(ssid);
          409 
          410                         ssid[len - 1] = '\0';
          411                         fmt = bprintf("%s", ssid);
          412                 }
          413 
          414                 close(sockfd);
          415                 return fmt;
          416         }
          417 #endif