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