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 (9672B)
       ---
            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];
           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                         strcpy(ifr.ifr_name, interface);
          113                         if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) {
          114                                 warn("ioctl 'SIOCGIFINDEX':");
          115                                 return -1;
          116                         }
          117                 }
          118                 return ifr.ifr_ifindex;
          119         }
          120 
          121         const char *
          122         wifi_essid(const char *interface)
          123         {
          124                 uint16_t fam = nl80211fam();
          125                 ssize_t r;
          126                 size_t len;
          127                 char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req;
          128                 int idx = ifindex(interface);
          129                 if (!fam) {
          130                         fprintf(stderr, "nl80211 family not found\n");
          131                         return NULL;
          132                 }
          133                 if (idx < 0) {
          134                         fprintf(stderr, "interface %s not found\n", interface);
          135                         return NULL;
          136                 }
          137 
          138                 memcpy(p, &(struct nlmsghdr){
          139                         .nlmsg_len = sizeof(req),
          140                         .nlmsg_type = fam,
          141                         .nlmsg_flags = NLM_F_REQUEST,
          142                         .nlmsg_seq = seq++,
          143                         .nlmsg_pid = 0,
          144                 }, sizeof(struct nlmsghdr));
          145                 p += NLMSG_HDRLEN;
          146                 memcpy(p, &(struct genlmsghdr){
          147                         .cmd = NL80211_CMD_GET_INTERFACE,
          148                         .version = 1,
          149                 }, sizeof(struct genlmsghdr));
          150                 p += GENL_HDRLEN;
          151                 memcpy(p, &(struct nlattr){
          152                         .nla_len = NLA_HDRLEN+4,
          153                         .nla_type = NL80211_ATTR_IFINDEX,
          154                 }, sizeof(struct nlattr));
          155                 p += NLA_HDRLEN;
          156                 memcpy(p, &(uint32_t){idx}, 4);
          157 
          158                 if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
          159                         warn("send 'AF_NETLINK':");
          160                         return NULL;
          161                 }
          162                 r = recv(nlsock, resp, sizeof(resp), 0);
          163                 if (r < 0) {
          164                         warn("recv 'AF_NETLINK':");
          165                         return NULL;
          166                 }
          167 
          168                 if ((size_t)r <= NLMSG_HDRLEN + GENL_HDRLEN)
          169                         return NULL;
          170                 p = findattr(NL80211_ATTR_SSID, resp + NLMSG_HDRLEN + GENL_HDRLEN, resp + r, &len);
          171                 if (p)
          172                         p[len] = 0;
          173 
          174                 return p;
          175         }
          176 
          177         const char *
          178         wifi_perc(const char *interface)
          179         {
          180                 static char strength[4];
          181                 struct nlmsghdr hdr;
          182                 uint16_t fam = nl80211fam();
          183                 ssize_t r;
          184                 size_t len;
          185                 char req[NLMSG_HDRLEN + GENL_HDRLEN + NLA_HDRLEN + NLA_ALIGN(4)] = {0}, *p = req, *e;
          186                 int idx = ifindex(interface);
          187 
          188                 if (idx < 0) {
          189                         fprintf(stderr, "interface %s not found\n", interface);
          190                         return NULL;
          191                 }
          192 
          193                 memcpy(p, &(struct nlmsghdr){
          194                         .nlmsg_len = sizeof(req),
          195                         .nlmsg_type = fam,
          196                         .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP,
          197                         .nlmsg_seq = seq++,
          198                         .nlmsg_pid = 0,
          199                 }, sizeof(struct nlmsghdr));
          200                 p += NLMSG_HDRLEN;
          201                 memcpy(p, &(struct genlmsghdr){
          202                         .cmd = NL80211_CMD_GET_STATION,
          203                         .version = 1,
          204                 }, sizeof(struct genlmsghdr));
          205                 p += GENL_HDRLEN;
          206                 memcpy(p, &(struct nlattr){
          207                         .nla_len = NLA_HDRLEN + 4,
          208                         .nla_type = NL80211_ATTR_IFINDEX,
          209                 }, sizeof(struct nlattr));
          210                 p += NLA_HDRLEN;
          211                 memcpy(p, &idx, 4);
          212 
          213                 if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
          214                         warn("send 'AF_NETLINK':");
          215                         return NULL;
          216                 }
          217 
          218                 *strength = 0;
          219                 while (1) {
          220                         r = recv(nlsock, resp, sizeof(resp), 0);
          221                         if (r < 0) {
          222                                 warn("recv 'AF_NETLINK':");
          223                                 return NULL;
          224                         }
          225                         if ((size_t)r < sizeof(hdr))
          226                                 return NULL;
          227 
          228                         for (p = resp; p != resp + r && (size_t)(resp + r-p) >= sizeof(hdr); p = e) {
          229                                 memcpy(&hdr, p, sizeof(hdr));
          230                                 e = resp + r - p < hdr.nlmsg_len ? resp + r : p + hdr.nlmsg_len;
          231 
          232                                 if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) {
          233                                         p += NLMSG_HDRLEN+GENL_HDRLEN;
          234                                         p = findattr(NL80211_ATTR_STA_INFO, p, e, &len);
          235                                         if (p)
          236                                                 p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len);
          237                                         if (p && len == 1)
          238                                                 snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p));
          239                                 }
          240                                 if (hdr.nlmsg_type == NLMSG_DONE)
          241                                         return *strength ? strength : NULL;
          242                         }
          243                 }
          244         }
          245 #elif defined(__OpenBSD__)
          246         #include <net/if.h>
          247         #include <net/if_media.h>
          248         #include <net80211/ieee80211.h>
          249         #include <sys/select.h> /* before <sys/ieee80211_ioctl.h> for NBBY */
          250         #include <net80211/ieee80211_ioctl.h>
          251         #include <stdlib.h>
          252         #include <sys/types.h>
          253 
          254         static int
          255         load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr)
          256         {
          257                 struct ieee80211_bssid bssid;
          258                 int sockfd;
          259                 uint8_t zero_bssid[IEEE80211_ADDR_LEN];
          260 
          261                 memset(&bssid, 0, sizeof(bssid));
          262                 memset(nr, 0, sizeof(struct ieee80211_nodereq));
          263                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          264                         warn("socket 'AF_INET':");
          265                         return 0;
          266                 }
          267                 strlcpy(bssid.i_name, interface, sizeof(bssid.i_name));
          268                 if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) {
          269                         warn("ioctl 'SIOCG80211BSSID':");
          270                         close(sockfd);
          271                         return 0;
          272                 }
          273                 memset(&zero_bssid, 0, sizeof(zero_bssid));
          274                 if (memcmp(bssid.i_bssid, zero_bssid,
          275                     IEEE80211_ADDR_LEN) == 0) {
          276                         close(sockfd);
          277                         return 0;
          278                 }
          279                 strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname));
          280                 memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr));
          281                 if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) {
          282                         warn("ioctl 'SIOCG80211NODE':");
          283                         close(sockfd);
          284                         return 0;
          285                 }
          286 
          287                 return close(sockfd), 1;
          288         }
          289 
          290         const char *
          291         wifi_perc(const char *interface)
          292         {
          293                 struct ieee80211_nodereq nr;
          294                 int q;
          295 
          296                 if (load_ieee80211_nodereq(interface, &nr)) {
          297                         if (nr.nr_max_rssi)
          298                                 q = IEEE80211_NODEREQ_RSSI(&nr);
          299                         else
          300                                 q = RSSI_TO_PERC(nr.nr_rssi);
          301 
          302                         return bprintf("%d", q);
          303                 }
          304 
          305                 return NULL;
          306         }
          307 
          308         const char *
          309         wifi_essid(const char *interface)
          310         {
          311                 struct ieee80211_nodereq nr;
          312 
          313                 if (load_ieee80211_nodereq(interface, &nr))
          314                         return bprintf("%s", nr.nr_nwid);
          315 
          316                 return NULL;
          317         }
          318 #elif defined(__FreeBSD__)
          319         #include <net/if.h>
          320         #include <net80211/ieee80211_ioctl.h>
          321 
          322         int
          323         load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len)
          324         {
          325                 char warn_buf[256];
          326                 struct ieee80211req ireq;
          327                 memset(&ireq, 0, sizeof(ireq));
          328                 ireq.i_type = type;
          329                 ireq.i_data = (caddr_t) data;
          330                 ireq.i_len = *len;
          331 
          332                 strlcpy(ireq.i_name, interface, sizeof(ireq.i_name));
          333                 if (ioctl(sock, SIOCG80211, &ireq) < 0) {
          334                         snprintf(warn_buf,  sizeof(warn_buf),
          335                                         "ioctl: 'SIOCG80211': %d", type);
          336                         warn(warn_buf);
          337                         return 0;
          338                 }
          339 
          340                 *len = ireq.i_len;
          341                 return 1;
          342         }
          343 
          344         const char *
          345         wifi_perc(const char *interface)
          346         {
          347                 union {
          348                         struct ieee80211req_sta_req sta;
          349                         uint8_t buf[24 * 1024];
          350                 } info;
          351                 uint8_t bssid[IEEE80211_ADDR_LEN];
          352                 int rssi_dbm;
          353                 int sockfd;
          354                 size_t len;
          355                 const char *fmt;
          356 
          357                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          358                         warn("socket 'AF_INET':");
          359                         return NULL;
          360                 }
          361 
          362                 /* Retreive MAC address of interface */
          363                 len = IEEE80211_ADDR_LEN;
          364                 fmt = NULL;
          365                 if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len))
          366                 {
          367                         /* Retrieve info on station with above BSSID */
          368                         memset(&info, 0, sizeof(info));
          369                         memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid));
          370 
          371                         len = sizeof(info);
          372                         if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) {
          373                                 rssi_dbm = info.sta.info[0].isi_noise +
          374                                                   info.sta.info[0].isi_rssi / 2;
          375 
          376                                 fmt = bprintf("%d", RSSI_TO_PERC(rssi_dbm));
          377                         }
          378                 }
          379 
          380                 close(sockfd);
          381                 return fmt;
          382         }
          383 
          384         const char *
          385         wifi_essid(const char *interface)
          386         {
          387                 char ssid[IEEE80211_NWID_LEN + 1];
          388                 size_t len;
          389                 int sockfd;
          390                 const char *fmt;
          391 
          392                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
          393                         warn("socket 'AF_INET':");
          394                         return NULL;
          395                 }
          396 
          397                 fmt = NULL;
          398                 len = sizeof(ssid);
          399                 memset(&ssid, 0, len);
          400                 if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len)) {
          401                         if (len < sizeof(ssid))
          402                                 len += 1;
          403                         else
          404                                 len = sizeof(ssid);
          405 
          406                         ssid[len - 1] = '\0';
          407                         fmt = bprintf("%s", ssid);
          408                 }
          409 
          410                 close(sockfd);
          411                 return fmt;
          412         }
          413 #endif