File: [local] / src / usr.sbin / hostapd / apme.c (download)
Revision 1.17, Fri May 10 01:29:31 2019 UTC (5 years, 1 month ago) by guenther
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, HEAD Changes since 1.16: +1 -2 lines
Delete superfluous #includes of <ifaddrs.h>, <net/if_dl.h>, and <net/if_enc.h>
ok krw@, jsg@
|
/* $OpenBSD: apme.c,v 1.17 2019/05/10 01:29:31 guenther Exp $ */
/*
* Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h> /* roundup isclr */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
#include <net/if_llc.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <net80211/ieee80211_radiotap.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "hostapd.h"
#include "iapp.h"
void hostapd_apme_frame(struct hostapd_apme *, u_int8_t *, u_int);
void hostapd_apme_hopper(int, short, void *);
int
hostapd_apme_add(struct hostapd_config *cfg, const char *name)
{
struct hostapd_apme *apme;
if (hostapd_apme_lookup(cfg, name) != NULL)
return (EEXIST);
if ((apme = (struct hostapd_apme *)
calloc(1, sizeof(struct hostapd_apme))) == NULL)
return (ENOMEM);
if (strlcpy(apme->a_iface, name, sizeof(apme->a_iface)) >=
sizeof(apme->a_iface)) {
free(apme);
return (EINVAL);
}
apme->a_cfg = cfg;
apme->a_chanavail = NULL;
TAILQ_INSERT_TAIL(&cfg->c_apmes, apme, a_entries);
hostapd_log(HOSTAPD_LOG_DEBUG,
"%s: Host AP interface added", apme->a_iface);
return (0);
}
int
hostapd_apme_deauth(struct hostapd_apme *apme)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
struct hostapd_iapp *iapp = &cfg->c_iapp;
u_int8_t buf[sizeof(struct ieee80211_frame) + sizeof(u_int16_t)];
struct ieee80211_frame *wh;
bzero(&buf, sizeof(buf));
wh = (struct ieee80211_frame *)&buf[0];
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
IEEE80211_FC0_SUBTYPE_DEAUTH;
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
memset(&wh->i_addr1, 0xff, IEEE80211_ADDR_LEN);
bcopy(apme->a_bssid, wh->i_addr2, IEEE80211_ADDR_LEN);
bcopy(apme->a_bssid, wh->i_addr3, IEEE80211_ADDR_LEN);
*(u_int16_t *)(wh + 1) = htole16(IEEE80211_REASON_AUTH_EXPIRE);
if (write(apme->a_raw, buf, sizeof(buf)) == -1) {
hostapd_log(HOSTAPD_LOG_VERBOSE,
"%s/%s: failed to deauthenticate all stations: %s",
iapp->i_iface, apme->a_iface,
strerror(errno));
return (EIO);
}
hostapd_log(HOSTAPD_LOG_VERBOSE,
"%s/%s: deauthenticated all stations",
apme->a_iface, iapp->i_iface);
return (0);
}
struct hostapd_apme *
hostapd_apme_lookup(struct hostapd_config *cfg, const char *name)
{
struct hostapd_apme *apme;
TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
if (strcmp(name, apme->a_iface) == 0)
return (apme);
}
return (NULL);
}
struct hostapd_apme *
hostapd_apme_addhopper(struct hostapd_config *cfg, const char *name)
{
struct hostapd_apme *apme;
if ((apme = hostapd_apme_lookup(cfg, name)) == NULL)
return (NULL);
if (apme->a_chanavail != NULL)
return (NULL);
apme->a_curchan = IEEE80211_CHAN_MAX;
apme->a_maxchan = roundup(IEEE80211_CHAN_MAX, NBBY);
if ((apme->a_chanavail = (u_int8_t *)
calloc(apme->a_maxchan, sizeof(u_int8_t))) == NULL)
return (NULL);
memset(apme->a_chanavail, 0xff,
apme->a_maxchan * sizeof(u_int8_t));
(void)strlcpy(apme->a_chanreq.i_name, apme->a_iface, IFNAMSIZ);
return (apme);
}
void
hostapd_apme_sethopper(struct hostapd_apme *apme, int now)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
struct timeval tv;
bzero(&tv, sizeof(tv));
if (!now)
bcopy(&cfg->c_apme_hopdelay, &tv, sizeof(tv));
if (!evtimer_initialized(&apme->a_chanev))
evtimer_set(&apme->a_chanev, hostapd_apme_hopper, apme);
if (evtimer_add(&apme->a_chanev, &tv) == -1)
hostapd_fatal("failed to add hopper event");
}
void
hostapd_apme_hopper(int fd, short sig, void *arg)
{
struct hostapd_apme *apme = (struct hostapd_apme *)arg;
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
int ret;
if (apme->a_curchan >= IEEE80211_CHAN_MAX)
apme->a_curchan = 0;
do {
if (apme->a_curchan >= IEEE80211_CHAN_MAX)
return;
apme->a_curchan %= IEEE80211_CHAN_MAX;
apme->a_curchan++;
} while (isclr(apme->a_chanavail, apme->a_curchan));
apme->a_chanreq.i_channel = apme->a_curchan;
if ((ret = ioctl(cfg->c_apme_ctl, SIOCS80211CHANNEL,
&apme->a_chanreq)) != 0) {
hostapd_apme_sethopper(apme, 1);
return;
}
hostapd_log(HOSTAPD_LOG_DEBUG,
"[priv]: %s setting to channel %d",
apme->a_iface, apme->a_curchan);
hostapd_apme_sethopper(apme, 0);
}
void
hostapd_apme_term(struct hostapd_apme *apme)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
/* Remove the channel hopper, if active */
if (apme->a_chanavail != NULL) {
(void)event_del(&apme->a_chanev);
free(apme->a_chanavail);
apme->a_chanavail = NULL;
}
/* Kick a specified Host AP interface */
(void)event_del(&apme->a_ev);
if (close(apme->a_raw))
hostapd_fatal("failed to close: %s\n",
strerror(errno));
TAILQ_REMOVE(&cfg->c_apmes, apme, a_entries);
/* Remove all dynamic roaming addresses */
if (cfg->c_flags & HOSTAPD_CFG_F_PRIV)
hostapd_roaming_term(apme);
hostapd_log(HOSTAPD_LOG_DEBUG,
"%s: Host AP interface removed", apme->a_iface);
free(apme);
}
void
hostapd_apme_input(int fd, short sig, void *arg)
{
struct hostapd_apme *apme = (struct hostapd_apme *)arg;
u_int8_t buf[IAPP_MAXSIZE], *bp, *ep;
struct bpf_hdr *bph;
ssize_t len;
/* Ignore invalid signals */
if (sig != EV_READ)
return;
bzero(&buf, sizeof(buf));
if ((len = read(fd, buf, sizeof(buf))) <
(ssize_t)sizeof(struct ieee80211_frame))
return;
/*
* Loop through each frame.
*/
bp = (u_int8_t *)&buf;
ep = bp + len;
while (bp < ep) {
register u_int caplen, hdrlen;
bph = (struct bpf_hdr *)bp;
caplen = bph->bh_caplen;
hdrlen = bph->bh_hdrlen;
/* Process frame */
hostapd_apme_frame(apme, bp + hdrlen, caplen);
bp += BPF_WORDALIGN(caplen + hdrlen);
}
}
int
hostapd_apme_output(struct hostapd_apme *apme,
struct hostapd_ieee80211_frame *frame)
{
struct iovec iov[2];
int iovcnt;
struct ieee80211_frame wh;
bzero(&wh, sizeof(wh));
switch (frame->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
bcopy(frame->i_from, wh.i_addr2, IEEE80211_ADDR_LEN);
bcopy(frame->i_to, wh.i_addr1, IEEE80211_ADDR_LEN);
bcopy(frame->i_bssid, wh.i_addr3, IEEE80211_ADDR_LEN);
break;
case IEEE80211_FC1_DIR_TODS:
bcopy(frame->i_from, wh.i_addr2, IEEE80211_ADDR_LEN);
bcopy(frame->i_to, wh.i_addr3, IEEE80211_ADDR_LEN);
bcopy(frame->i_bssid, wh.i_addr1, IEEE80211_ADDR_LEN);
break;
case IEEE80211_FC1_DIR_FROMDS:
bcopy(frame->i_from, wh.i_addr3, IEEE80211_ADDR_LEN);
bcopy(frame->i_to, wh.i_addr1, IEEE80211_ADDR_LEN);
bcopy(frame->i_bssid, wh.i_addr2, IEEE80211_ADDR_LEN);
break;
default:
case IEEE80211_FC1_DIR_DSTODS:
return (EINVAL);
}
wh.i_fc[0] = IEEE80211_FC0_VERSION_0 | frame->i_fc[0];
wh.i_fc[1] = frame->i_fc[1];
bcopy(frame->i_dur, wh.i_dur, sizeof(wh.i_dur));
bcopy(frame->i_seq, wh.i_seq, sizeof(wh.i_seq));
iovcnt = 1;
iov[0].iov_base = &wh;
iov[0].iov_len = sizeof(struct ieee80211_frame);
if (frame->i_data != NULL && frame->i_data_len > 0) {
iovcnt = 2;
iov[1].iov_base = frame->i_data;
iov[1].iov_len = frame->i_data_len;
}
if (writev(apme->a_raw, iov, iovcnt) == -1)
return (errno);
return (0);
}
int
hostapd_apme_offset(struct hostapd_apme *apme,
u_int8_t *buf, const u_int len)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
struct ieee80211_radiotap_header *rh;
u_int rh_len;
if (cfg->c_apme_dlt == DLT_IEEE802_11)
return (0);
else if (cfg->c_apme_dlt != DLT_IEEE802_11_RADIO)
return (-1);
if (len < sizeof(struct ieee80211_radiotap_header))
return (-1);
rh = (struct ieee80211_radiotap_header*)buf;
rh_len = letoh16(rh->it_len);
if (rh->it_version != 0)
return (-1);
if (len <= rh_len)
return (-1);
return ((int)rh_len);
}
void
hostapd_apme_frame(struct hostapd_apme *apme, u_int8_t *buf, u_int len)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
struct hostapd_iapp *iapp = &cfg->c_iapp;
struct hostapd_apme *other_apme;
struct hostapd_node node;
struct ieee80211_frame *wh;
int offset;
if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
return;
wh = (struct ieee80211_frame *)(buf + offset);
/* Ignore short frames or fragments */
if (len < sizeof(struct ieee80211_frame))
return;
/* Handle received frames */
if ((hostapd_handle_input(apme, buf, len) ==
(HOSTAPD_FRAME_F_RET_SKIP >> HOSTAPD_FRAME_F_RET_S)) ||
cfg->c_flags & HOSTAPD_CFG_F_IAPP_PASSIVE)
return;
/*
* Only accept local association response frames, ...
*/
if (!((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
IEEE80211_FC1_DIR_NODS &&
(wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
IEEE80211_FC0_TYPE_MGT &&
(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
IEEE80211_FC0_SUBTYPE_ASSOC_RESP))
return;
/*
* ...sent by the Host AP (addr2) to our BSSID (addr3)
*/
if (bcmp(wh->i_addr2, apme->a_bssid, IEEE80211_ADDR_LEN) != 0 ||
bcmp(wh->i_addr3, apme->a_bssid, IEEE80211_ADDR_LEN) != 0)
return;
cfg->c_stats.cn_rx_apme++;
/*
* Double-check if the station got associated to our Host AP
*/
bcopy(wh->i_addr1, node.ni_macaddr, IEEE80211_ADDR_LEN);
if (hostapd_priv_apme_getnode(apme, &node) != 0) {
hostapd_log(HOSTAPD_LOG_DEBUG,
"%s: invalid association from %s on the Host AP",
apme->a_iface, etheraddr_string(wh->i_addr1));
return;
}
cfg->c_stats.cn_tx_apme++;
/*
* Delete node on other attached Host APs
*/
TAILQ_FOREACH(other_apme, &cfg->c_apmes, a_entries) {
if (apme == other_apme)
continue;
if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
(void)hostapd_roaming_del(other_apme, &node);
if (hostapd_apme_delnode(other_apme, &node) == 0)
cfg->c_stats.cn_tx_apme++;
}
if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
(void)hostapd_roaming_add(apme, &node);
(void)hostapd_iapp_add_notify(apme, &node);
}
void
hostapd_apme_init(struct hostapd_apme *apme)
{
struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
u_int i, dlt;
struct ifreq ifr;
apme->a_raw = hostapd_bpf_open(O_RDWR);
apme->a_rawlen = IAPP_MAXSIZE;
if (ioctl(apme->a_raw, BIOCSBLEN, &apme->a_rawlen) == -1)
hostapd_fatal("failed to set BPF buffer len \"%s\": %s\n",
apme->a_iface, strerror(errno));
i = 1;
if (ioctl(apme->a_raw, BIOCIMMEDIATE, &i) == -1)
hostapd_fatal("failed to set BPF immediate mode on \"%s\": "
"%s\n", apme->a_iface, strerror(errno));
bzero(&ifr, sizeof(struct ifreq));
(void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
/* This may fail, ignore it */
(void)ioctl(apme->a_raw, BIOCPROMISC, NULL);
/* Associate the wireless network interface to the BPF descriptor */
if (ioctl(apme->a_raw, BIOCSETIF, &ifr) == -1)
hostapd_fatal("failed to set BPF interface \"%s\": %s\n",
apme->a_iface, strerror(errno));
dlt = cfg->c_apme_dlt;
if (ioctl(apme->a_raw, BIOCSDLT, &dlt) == -1)
hostapd_fatal("failed to set BPF link type on \"%s\": %s\n",
apme->a_iface, strerror(errno));
/* Lock the BPF descriptor, no further configuration */
if (ioctl(apme->a_raw, BIOCLOCK, NULL) == -1)
hostapd_fatal("failed to lock BPF interface on \"%s\": %s\n",
apme->a_iface, strerror(errno));
}
int
hostapd_apme_addnode(struct hostapd_apme *apme, struct hostapd_node *node)
{
return (hostapd_priv_apme_setnode(apme, node, 1));
}
int
hostapd_apme_delnode(struct hostapd_apme *apme, struct hostapd_node *node)
{
return (hostapd_priv_apme_setnode(apme, node, 0));
}