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