slstatus

status monitor
git clone git://git.suckless.org/slstatus
Log | Files | Refs | README | 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