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