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