slstatus

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