diff --git a/tools/tools/net80211/mlme_assoc/Makefile b/tools/tools/net80211/mlme_assoc/Makefile new file mode 100644 index 000000000000..580fb045ac52 --- /dev/null +++ b/tools/tools/net80211/mlme_assoc/Makefile @@ -0,0 +1,7 @@ +PROG= mlme_assoc +BINDIR= /usr/bin +MAN= + +SRCS= mlme_assoc.c + +.include diff --git a/tools/tools/net80211/mlme_assoc/README b/tools/tools/net80211/mlme_assoc/README new file mode 100644 index 000000000000..fc5e754a58d6 --- /dev/null +++ b/tools/tools/net80211/mlme_assoc/README @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2023 The FreeBSD Foundation +# +# This documentation was written by Björn Zeeb under sponsorship from +# the FreeBSD Foundation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +This is a simple program to drive net80211::ieee80211_sta_join1() calls from +user space. + +The program optionally accepts an interface name (e.g., wlan42), or an +interface name, an SSID and a BSSID. + +In the former case of no SSID/BSSID passed it will query the scan results and +then try to join each entry from the scan with a short delay. + +In the lastter case giving the SSID/BSSID one can trigger the "canreassoc" case +in ieee80211_sta_join1() or not depending on whether one passes the currently +associated SSID/BSSID or not. + +The tool is useful to trigger net80211::newstate() changes while other +newstate() changes are pending or being executed. + +I was specifically developed to show a problem with the LinuxKPI 802.11 compat +code. The reason is that ieee80211_sta_join1() also calls in (*iv_update_bss)() +swapping nodes before initiating the state changes and in LinuxKPI state is on +the sta and not the vif causing all kinds of troubles, especially if we lose +a state transition before the taskq is run or if the iv_bss node gets swapped +before a task is executed. diff --git a/tools/tools/net80211/mlme_assoc/mlme_assoc.c b/tools/tools/net80211/mlme_assoc/mlme_assoc.c new file mode 100644 index 000000000000..c26aaa03fe87 --- /dev/null +++ b/tools/tools/net80211/mlme_assoc/mlme_assoc.c @@ -0,0 +1,200 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * First get scan results in a hurry. + * Pick a random BSSID and try to assoc. + * Hopefully this is enough to trigger the newstate race along with the + * (*iv_update_bss)() logic. + * + * Alternatively pass IF SSID BSSID in and just try that. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +static int +if_up(int sd, const char *ifnam) +{ + struct ifreq ifr; + int error; + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); + + error = ioctl(sd, SIOCGIFFLAGS, &ifr); + if (error == -1) { + warn("SIOCGIFFLAGS"); + return (error); + } + + if (ifr.ifr_flags & IFF_UP) + return (0); + + ifr.ifr_flags |= IFF_UP; + + error = ioctl(sd, SIOCSIFFLAGS, &ifr); + if (error == -1) { + warn("SIOCSIFFLAGS"); + return (error); + } + + return (0); +} + +static int +try_mlme_assoc(int sd, const char *ifnam, uint8_t *ssid, uint8_t ssid_len, uint8_t *bssid) +{ + struct ieee80211req ireq; + struct ieee80211req_mlme mlme; + int error; + + memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + if (ssid != NULL) + memcpy(mlme.im_ssid, ssid, ssid_len); + mlme.im_ssid_len = ssid_len; + if (bssid != NULL) + memcpy(mlme.im_macaddr, bssid, IEEE80211_ADDR_LEN); + + memset(&ireq, 0, sizeof(ireq)); + strlcpy(ireq.i_name, ifnam, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_MLME; + ireq.i_val = 0; + ireq.i_data = (void *)&mlme; + ireq.i_len = sizeof(mlme); + + error = ioctl(sd, SIOCS80211, &ireq); + if (error == -1) { + warn("SIOCS80211, %#x", ireq.i_type); + return (error); + } + + return (0); +} + +static int +mlme_assoc_scan_results(int sd, const char *ifnam) +{ + struct ieee80211req ireq; + struct ieee80211req_scan_result *sr; + uint8_t buf[32 * 1024], *p; + ssize_t len; + int error; + + memset(&ireq, 0, sizeof(ireq)); + strlcpy(ireq.i_name, ifnam, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; + ireq.i_data = (void *)buf; + ireq.i_len = sizeof(buf); + + error = ioctl(sd, SIOCG80211, &ireq); + if (error == -1 || ireq.i_len < 0) { + warn("SIOCG80211, %#x", ireq.i_type); + return (error); + } + + p = buf; + len = ireq.i_len; + while (len > (ssize_t)sizeof(*sr)) { + sr = (struct ieee80211req_scan_result *)(void *)p; + p += sr->isr_len; + len -= sr->isr_len; + + error = try_mlme_assoc(sd, ifnam, (void *)(sr + 1), sr->isr_ssid_len, + sr->isr_bssid); + if (error != 0) { + warnx("try_mlme_assoc"); + return (error); + } + + usleep(100000); + } + + return (0); +} + +int +main(int argc, char *argv[]) +{ + const char *ifnam; + uint8_t *ssid, *bssid; + struct ether_addr ea; + int error, sd; + + ifnam = "wlan0"; + ssid = NULL; + bssid = NULL; + + if (argc == 4) { + ifnam = argv[1]; + ssid = (uint8_t *)argv[2]; + bssid = (uint8_t *)ether_aton_r(argv[3], &ea); + if (bssid == NULL) + warnx("ether_aton_r, ignoring BSSID"); + } else if (argc == 2) { + ifnam = argv[1]; + } + + sd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (sd == -1) + errx(EX_UNAVAILABLE, "socket"); + + error = if_up(sd, ifnam); + if (error != 0) + errx(EX_UNAVAILABLE, "if_up"); + + if (argc == 4) { + error = try_mlme_assoc(sd, ifnam, ssid, strlen((const char *)ssid), bssid); + if (error != 0) + errx(EX_UNAVAILABLE, "try_mlme_assoc"); + + } else { + error = mlme_assoc_scan_results(sd, ifnam); + if (error != 0) + errx(EX_UNAVAILABLE, "mlme_assoc_scan_results"); + } + + close(sd); + + return (0); +}