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