#include <err.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
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);
}