wifi.c - 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(… | |
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_GE… | |
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), r… | |
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… | |
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", inte… | |
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 + GE… | |
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_A… | |
186 int idx = ifindex(interface); | |
187 | |
188 if (idx < 0) { | |
189 fprintf(stderr, "interface %s not found\n", inte… | |
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 + … | |
229 memcpy(&hdr, p, sizeof(hdr)); | |
230 e = resp + r - p < hdr.nlmsg_len ? resp … | |
231 | |
232 if (!*strength && hdr.nlmsg_len > NLMSG_… | |
233 p += NLMSG_HDRLEN+GENL_HDRLEN; | |
234 p = findattr(NL80211_ATTR_STA_IN… | |
235 if (p) | |
236 p = findattr(NL80211_STA… | |
237 if (p && len == 1) | |
238 snprintf(strength, sizeo… | |
239 } | |
240 if (hdr.nlmsg_type == NLMSG_DONE) | |
241 return *strength ? strength : NU… | |
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 NB… | |
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_n… | |
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_mac… | |
281 if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rs… | |
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, i… | |
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, IEEE802… | |
366 { | |
367 /* Retrieve info on station with above BSSID */ | |
368 memset(&info, 0, sizeof(info)); | |
369 memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssi… | |
370 | |
371 len = sizeof(info); | |
372 if (load_ieee80211req(sockfd, interface, &info, … | |
373 rssi_dbm = info.sta.info[0].isi_noise + | |
374 info.sta.info[0].isi_r… | |
375 | |
376 fmt = bprintf("%d", RSSI_TO_PERC(rssi_db… | |
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, IEEE8021… | |
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 |