1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 The FreeBSD Foundation 5 * 6 * This software was developed by Björn Zeeb under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * First get scan results in a hurry. 33 * Pick a random BSSID and try to assoc. 34 * Hopefully this is enough to trigger the newstate race along with the 35 * (*iv_update_bss)() logic. 36 * 37 * Alternatively pass IF SSID BSSID in and just try that. 38 */ 39 40 #include <err.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <sysexits.h> 44 #include <unistd.h> 45 46 #include <sys/socket.h> 47 #include <sys/ioctl.h> 48 49 #include <net/if.h> 50 #include <net/ethernet.h> 51 52 #include <net80211/ieee80211.h> 53 #include <net80211/ieee80211_ioctl.h> 54 55 static int 56 if_up(int sd, const char *ifnam) 57 { 58 struct ifreq ifr; 59 int error; 60 61 memset(&ifr, 0, sizeof(ifr)); 62 strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 63 64 error = ioctl(sd, SIOCGIFFLAGS, &ifr); 65 if (error == -1) { 66 warn("SIOCGIFFLAGS"); 67 return (error); 68 } 69 70 if (ifr.ifr_flags & IFF_UP) 71 return (0); 72 73 ifr.ifr_flags |= IFF_UP; 74 75 error = ioctl(sd, SIOCSIFFLAGS, &ifr); 76 if (error == -1) { 77 warn("SIOCSIFFLAGS"); 78 return (error); 79 } 80 81 return (0); 82 } 83 84 static int 85 try_mlme_assoc(int sd, const char *ifnam, uint8_t *ssid, uint8_t ssid_len, uint8_t *bssid) 86 { 87 struct ieee80211req ireq; 88 struct ieee80211req_mlme mlme; 89 int error; 90 91 memset(&mlme, 0, sizeof(mlme)); 92 mlme.im_op = IEEE80211_MLME_ASSOC; 93 if (ssid != NULL) 94 memcpy(mlme.im_ssid, ssid, ssid_len); 95 mlme.im_ssid_len = ssid_len; 96 if (bssid != NULL) 97 memcpy(mlme.im_macaddr, bssid, IEEE80211_ADDR_LEN); 98 99 memset(&ireq, 0, sizeof(ireq)); 100 strlcpy(ireq.i_name, ifnam, sizeof(ireq.i_name)); 101 ireq.i_type = IEEE80211_IOC_MLME; 102 ireq.i_val = 0; 103 ireq.i_data = (void *)&mlme; 104 ireq.i_len = sizeof(mlme); 105 106 error = ioctl(sd, SIOCS80211, &ireq); 107 if (error == -1) { 108 warn("SIOCS80211, %#x", ireq.i_type); 109 return (error); 110 } 111 112 return (0); 113 } 114 115 static int 116 mlme_assoc_scan_results(int sd, const char *ifnam) 117 { 118 struct ieee80211req ireq; 119 struct ieee80211req_scan_result *sr; 120 uint8_t buf[32 * 1024], *p; 121 ssize_t len; 122 int error; 123 124 memset(&ireq, 0, sizeof(ireq)); 125 strlcpy(ireq.i_name, ifnam, sizeof(ireq.i_name)); 126 ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; 127 ireq.i_data = (void *)buf; 128 ireq.i_len = sizeof(buf); 129 130 error = ioctl(sd, SIOCG80211, &ireq); 131 if (error == -1 || ireq.i_len < 0) { 132 warn("SIOCG80211, %#x", ireq.i_type); 133 return (error); 134 } 135 136 p = buf; 137 len = ireq.i_len; 138 while (len > (ssize_t)sizeof(*sr)) { 139 sr = (struct ieee80211req_scan_result *)(void *)p; 140 p += sr->isr_len; 141 len -= sr->isr_len; 142 143 error = try_mlme_assoc(sd, ifnam, (void *)(sr + 1), sr->isr_ssid_len, 144 sr->isr_bssid); 145 if (error != 0) { 146 warnx("try_mlme_assoc"); 147 return (error); 148 } 149 150 usleep(100000); 151 } 152 153 return (0); 154 } 155 156 int 157 main(int argc, char *argv[]) 158 { 159 const char *ifnam; 160 uint8_t *ssid, *bssid; 161 struct ether_addr ea; 162 int error, sd; 163 164 ifnam = "wlan0"; 165 ssid = NULL; 166 bssid = NULL; 167 168 if (argc == 4) { 169 ifnam = argv[1]; 170 ssid = (uint8_t *)argv[2]; 171 bssid = (uint8_t *)ether_aton_r(argv[3], &ea); 172 if (bssid == NULL) 173 warnx("ether_aton_r, ignoring BSSID"); 174 } else if (argc == 2) { 175 ifnam = argv[1]; 176 } 177 178 sd = socket(AF_LOCAL, SOCK_DGRAM, 0); 179 if (sd == -1) 180 errx(EX_UNAVAILABLE, "socket"); 181 182 error = if_up(sd, ifnam); 183 if (error != 0) 184 errx(EX_UNAVAILABLE, "if_up"); 185 186 if (argc == 4) { 187 error = try_mlme_assoc(sd, ifnam, ssid, strlen((const char *)ssid), bssid); 188 if (error != 0) 189 errx(EX_UNAVAILABLE, "try_mlme_assoc"); 190 191 } else { 192 error = mlme_assoc_scan_results(sd, ifnam); 193 if (error != 0) 194 errx(EX_UNAVAILABLE, "mlme_assoc_scan_results"); 195 } 196 197 close(sd); 198 199 return (0); 200 } 201