openbsd_wlan (and FreeBSD ieee80211_ioctl.h): Initial ioctl compatibility.

* Introduce IEEE80211_IOCTLS_ABBREVIATED to FreeBSD's ieee80211_ioctl.h,
   so that the FreeBSD header can be included along with the OpenBSD
   net80211 stack without triggering lots of errors, so we can use
   these structs in openbsd_wlan ieee80211_haiku.cpp.

 * Implement basic ieee80211req handling machinery. We aren't going
   to handle anything but SIOCG80211 and SIOCS80211 in this method,
   so we can simplify it a bit at the same time.

 * Implement IEEE80211_IOC_SCAN_RESULTS to return scan results from
   the OpenBSD WiFi stack in FreeBSD ioctl format.

   This change adds two minor "features" (noted with #ifdefs) to the
   OpenBSD net80211 stack: one to specify what node index to return
   when returning known nodes (scan results), as this way we do not
   have to allocate a huge buffer to store the scan results in before
   sending them back to userland, but can instead get and convert
   a single one at a time.

   (We store the converted values in kernel mode all at once, though,
    so this is not as efficient as it could be. More improvement possible.)

 * Implement IEEE80211_IOC_BSSID and IEEE80211_IOC_SSID while we are at it.
   This actually may make joining open networks possible, but I didn't
   really test it. (They are used with SIOCG80211 to see what network
   the device is currently connecting/connected to.)
This commit is contained in:
Augustin Cavalier 2022-06-08 23:03:31 -04:00
parent fba0315889
commit bcb089f6c3
6 changed files with 214 additions and 23 deletions

View File

@ -30,6 +30,7 @@
#ifndef _NET80211_IEEE80211_IOCTL_H_ #ifndef _NET80211_IEEE80211_IOCTL_H_
#define _NET80211_IEEE80211_IOCTL_H_ #define _NET80211_IEEE80211_IOCTL_H_
#ifndef IEEE80211_IOCTLS_ABBREVIATED
/* /*
* IEEE 802.11 ioctls. * IEEE 802.11 ioctls.
*/ */
@ -381,6 +382,7 @@ struct ieee80211req_chanlist {
uint8_t ic_channels[32]; /* NB: can be variable length */ uint8_t ic_channels[32]; /* NB: can be variable length */
}; };
#ifndef IEEE80211_IOCTLS_ABBREVIATED
/* /*
* Get the active channel list info. * Get the active channel list info.
*/ */
@ -393,6 +395,7 @@ struct ieee80211req_chaninfo {
(((_nchan)-1) * sizeof(struct ieee80211_channel))) (((_nchan)-1) * sizeof(struct ieee80211_channel)))
#define IEEE80211_CHANINFO_SPACE(_ci) \ #define IEEE80211_CHANINFO_SPACE(_ci) \
IEEE80211_CHANINFO_SIZE((_ci)->ic_nchans) IEEE80211_CHANINFO_SIZE((_ci)->ic_nchans)
#endif
/* /*
* Retrieve the WPA/RSN information element for an associated station. * Retrieve the WPA/RSN information element for an associated station.
@ -418,6 +421,7 @@ struct ieee80211req_sta_stats {
} is_u; } is_u;
struct ieee80211_nodestats is_stats; struct ieee80211_nodestats is_stats;
}; };
#endif
/* /*
* Station information block; the mac address is used * Station information block; the mac address is used
@ -443,6 +447,7 @@ struct ieee80211req_sta_info {
uint16_t isi_associd; /* assoc response */ uint16_t isi_associd; /* assoc response */
uint16_t isi_txpower; /* current tx power */ uint16_t isi_txpower; /* current tx power */
uint16_t isi_vlan; /* vlan tag */ uint16_t isi_vlan; /* vlan tag */
#ifndef IEEE80211_IOCTLS_ABBREVIATED
/* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */ /* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */
uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */ uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */ uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
@ -456,6 +461,7 @@ struct ieee80211req_sta_info {
uint16_t isi_localid; uint16_t isi_localid;
uint8_t isi_peerstate; uint8_t isi_peerstate;
/* XXX frag state? */ /* XXX frag state? */
#endif
/* variable length IE data */ /* variable length IE data */
}; };
@ -514,6 +520,7 @@ struct ieee80211req_sta_txpow {
(IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \
IEEE80211_FC0_SUBTYPE_PROBE_RESP) IEEE80211_FC0_SUBTYPE_PROBE_RESP)
#ifndef IEEE80211_IOCTLS_ABBREVIATED
/* /*
* Station mode roaming parameters. These are maintained * Station mode roaming parameters. These are maintained
* per band/mode and control the roaming algorithm. * per band/mode and control the roaming algorithm.
@ -569,6 +576,7 @@ struct ieee80211_devcaps_req {
(((_nchan)-1) * sizeof(struct ieee80211_channel))) (((_nchan)-1) * sizeof(struct ieee80211_channel)))
#define IEEE80211_DEVCAPS_SPACE(_dc) \ #define IEEE80211_DEVCAPS_SPACE(_dc) \
IEEE80211_DEVCAPS_SIZE((_dc)->dc_chaninfo.ic_nchans) IEEE80211_DEVCAPS_SIZE((_dc)->dc_chaninfo.ic_nchans)
#endif
struct ieee80211_chanswitch_req { struct ieee80211_chanswitch_req {
struct ieee80211_channel csa_chan; /* new channel */ struct ieee80211_channel csa_chan; /* new channel */
@ -843,6 +851,9 @@ struct ieee80211req_scan_result {
uint8_t isr_bssid[IEEE80211_ADDR_LEN]; uint8_t isr_bssid[IEEE80211_ADDR_LEN];
uint8_t isr_nrates; uint8_t isr_nrates;
uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
#if defined(__HAIKU__) && defined(_KERNEL_MODE)
STATIC_ASSERT(IEEE80211_RATE_MAXSIZE == 15);
#endif
uint8_t isr_ssid_len; /* SSID length */ uint8_t isr_ssid_len; /* SSID length */
uint8_t isr_meshid_len; /* MESH ID length */ uint8_t isr_meshid_len; /* MESH ID length */
/* variable length SSID, followed by variable length MESH ID, /* variable length SSID, followed by variable length MESH ID,

View File

@ -3,6 +3,9 @@
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#include <AutoDeleter.h>
#include <util/KMessage.h>
extern "C" { extern "C" {
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -23,12 +26,16 @@ extern "C" {
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
#include <net80211/ieee80211_var.h> #include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_ioctl.h>
#undef _NET80211_IEEE80211_IOCTL_H_
#define IEEE80211_IOCTLS_ABBREVIATED
#include "../../freebsd_wlan/net80211/ieee80211_ioctl.h"
} }
#include <shared.h> #include <shared.h>
#include <util/KMessage.h>
#include <ether_driver.h> #include <ether_driver.h>
#include <net_notifications.h> #include <net_notifications.h>
@ -98,7 +105,7 @@ start_wlan(device_t device)
if_initname(ifp, device_get_name(device), i); if_initname(ifp, device_get_name(device), i);
dprintf("%s: wlan started.\n", __func__); TRACE("%s: wlan started.\n", __func__);
return B_OK; return B_OK;
} }
@ -120,7 +127,7 @@ stop_wlan(device_t device)
status_t status_t
wlan_close(void* cookie) wlan_close(void* cookie)
{ {
dprintf("wlan_close(%p)\n", cookie); TRACE("wlan_close(%p)\n", cookie);
struct ifnet* ifp = (struct ifnet*)cookie; struct ifnet* ifp = (struct ifnet*)cookie;
ifp->if_flags &= ~IFF_UP; ifp->if_flags &= ~IFF_UP;
@ -130,35 +137,182 @@ wlan_close(void* cookie)
} }
static uint8_t
fbsd_capinfo_from_obsd(uint16_t obsd_capinfo)
{
// FreeBSD only exposes the first 8 bits of the capinfo,
// and these are identical in OpenBSD. Makes things easy.
return uint8_t(obsd_capinfo);
}
status_t status_t
wlan_control(void* cookie, uint32 op, void* arg, size_t length) wlan_control(void* cookie, uint32 op, void* arg, size_t length)
{ {
if (op != SIOCG80211 && op != SIOCS80211)
return B_BAD_VALUE;
struct ifnet* ifp = (struct ifnet*)cookie; struct ifnet* ifp = (struct ifnet*)cookie;
#if 0 struct ieee80211com* ic = (ieee80211com*)ifp;
switch (op) {
case SIOCG80211: struct ieee80211req ireq;
case SIOCS80211: if (user_memcpy(&ireq, arg, sizeof(struct ieee80211req)) != B_OK)
{ return B_BAD_ADDRESS;
// FreeBSD drivers assume that the request structure has already
// been copied into kernel space switch (ireq.i_type) {
struct ieee80211req request; case IEEE80211_IOC_SCAN_RESULTS: {
if (user_memcpy(&request, arg, sizeof(struct ieee80211req)) != B_OK) if (op != SIOCG80211)
return B_BAD_VALUE;
const int32 count = ireq.i_len / sizeof(struct ieee80211req_scan_result);
// ieee80211req_scan_result is variable-length, this will get us the max count
void* req_scan_result = calloc(1, ireq.i_len);
MemoryDeleter resultDeleter(req_scan_result);
if (!resultDeleter.IsSet())
return B_NO_MEMORY;
struct ieee80211_nodereq nodereq;
struct ieee80211_nodereq_all nodereq_all = {};
uint8* out = (uint8*)req_scan_result;
uint16 remaining = ireq.i_len;
for (int i = 0; ; i++) {
nodereq_all.na_node = &nodereq;
nodereq_all.na_size = sizeof(struct ieee80211_nodereq);
nodereq_all.na_startnode = i;
IFF_LOCKGIANT(ifp);
status_t status = ifp->if_ioctl(ifp, SIOCG80211ALLNODES, (caddr_t)&nodereq_all);
IFF_UNLOCKGIANT(ifp);
if (status != B_OK)
return status;
if (nodereq_all.na_nodes == 0)
break;
int32 required = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len;
uint16_t ieLen = 0;
if (nodereq.nr_rsnie[1] != 0) {
ieLen = 2 + nodereq.nr_rsnie[1];
required += ieLen;
}
if (remaining < (sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len))
break;
struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)out;
sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ieLen;
sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags);
sr->isr_flags = 0; /* not actually used by userland */
sr->isr_noise = 0; /* unknown */
sr->isr_rssi = nodereq.nr_rssi;
sr->isr_intval = nodereq.nr_intval;
sr->isr_capinfo = fbsd_capinfo_from_obsd(nodereq.nr_capinfo);
sr->isr_erp = nodereq.nr_erp;
memcpy(sr->isr_bssid, nodereq.nr_bssid, IEEE80211_ADDR_LEN);
sr->isr_nrates = nodereq.nr_nrates;
memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE);
sr->isr_ssid_len = nodereq.nr_nwid_len;
sr->isr_meshid_len = 0;
memcpy(out + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len);
memcpy(out + sr->isr_ie_off + sr->isr_ssid_len,
nodereq.nr_rsnie, ieLen);
sr->isr_len = roundup(sr->isr_ie_off + sr->isr_ssid_len + sr->isr_ie_len, 4);
out += sr->isr_len;
remaining -= sr->isr_len;
}
ireq.i_len = (out - (uint8*)req_scan_result);
IFF_LOCKGIANT(ifp);
const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */;
if (ic->ic_opmode == IEEE80211_M_STA
&& system_time() >= (ic->ic_last_raise_inact + RAISE_INACT_INTERVAL)) {
// net80211 only raises and checks inactivity during active scans, or background
// scans performed in S_RUN, so we need to do it here so that stale nodes are
// evicted for S_SCAN, too. (This means "inact" will be raised a bit more often
// and nodes evicted faster during other modes, but that's acceptable.)
ieee80211_iterate_nodes(ic, ieee80211_node_raise_inact, NULL);
ieee80211_clean_inactive_nodes(ic, IEEE80211_INACT_SCAN);
ic->ic_last_raise_inact = system_time();
}
IFF_UNLOCKGIANT(ifp);
if (user_memcpy(ireq.i_data, req_scan_result, ireq.i_len) != B_OK)
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
TRACE("wlan_control: %" B_PRIu32 ", %d\n", op, request.i_type); break;
status_t status = ifp->if_ioctl(ifp, op, (caddr_t)&request); }
case IEEE80211_IOC_BSSID: {
if (ireq.i_len != IEEE80211_ADDR_LEN)
return EINVAL;
struct ieee80211_bssid bssid;
if (op == SIOCS80211) {
if (user_memcpy(bssid.i_bssid, ireq.i_data, ireq.i_len) != B_OK)
return B_BAD_ADDRESS;
}
IFF_LOCKGIANT(ifp);
status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
SIOCG80211BSSID : SIOCS80211BSSID, (caddr_t)&bssid);
IFF_UNLOCKGIANT(ifp);
if (status != B_OK) if (status != B_OK)
return status; return status;
if (op == SIOCG80211 && user_memcpy(arg, &request, if (op == SIOCG80211) {
sizeof(struct ieee80211req)) != B_OK) if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK)
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
return B_OK; }
}
}
#endif
return B_BAD_VALUE; break;
}
case IEEE80211_IOC_SSID: {
struct ifreq ifr;
struct ieee80211_nwid nwid;
ifr.ifr_data = (uint8_t*)&nwid;
if (op == SIOCS80211) {
nwid.i_len = ireq.i_len;
if (user_memcpy(nwid.i_nwid, ireq.i_data, ireq.i_len) != B_OK)
return B_BAD_ADDRESS;
}
IFF_LOCKGIANT(ifp);
status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
SIOCG80211NWID : SIOCS80211NWID, (caddr_t)&ifr);
ieee80211_state state = ic->ic_state;
IFF_UNLOCKGIANT(ifp);
if (status != B_OK)
return status;
if (op == SIOCG80211) {
if (state == IEEE80211_S_INIT || state == IEEE80211_S_SCAN) {
// The returned NWID will be the one we want to join, not connected to.
ireq.i_len = 0;
} else {
if (ireq.i_len < nwid.i_len)
return E2BIG;
ireq.i_len = nwid.i_len;
if (user_memcpy(ireq.i_data, nwid.i_nwid, ireq.i_len) != B_OK)
return B_BAD_ADDRESS;
}
}
break;
}
default:
TRACE("openbsd wlan_control: %" B_PRIu32 ", %d (not supported)\n", op, ireq.i_type);
return EOPNOTSUPP;
}
if (op == SIOCG80211 && user_memcpy(arg, &ireq,
sizeof(struct ieee80211req)) != B_OK)
return B_BAD_ADDRESS;
return B_OK;
} }

View File

@ -129,6 +129,12 @@ ieee80211_node2req(struct ieee80211com *ic, const struct ieee80211_node *ni,
nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_8021X; nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_8021X;
if (ni->ni_supported_rsnakms & IEEE80211_AKM_SHA256_PSK) if (ni->ni_supported_rsnakms & IEEE80211_AKM_SHA256_PSK)
nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_PSK; nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_PSK;
#ifdef __FreeBSD_version
if (ni->ni_rsnie != NULL)
memcpy(nr->nr_rsnie, ni->ni_rsnie, 2 + ni->ni_rsnie[1]);
else
nr->nr_rsnie[1] = 0;
#endif
/* Node flags */ /* Node flags */
nr->nr_flags = 0; nr->nr_flags = 0;
@ -934,6 +940,12 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
na = (struct ieee80211_nodereq_all *)data; na = (struct ieee80211_nodereq_all *)data;
na->na_nodes = i = 0; na->na_nodes = i = 0;
ni = RBT_MIN(ieee80211_tree, &ic->ic_tree); ni = RBT_MIN(ieee80211_tree, &ic->ic_tree);
#ifdef __FreeBSD_version
while (ni && na->na_startnode) {
ni = RBT_NEXT(ieee80211_tree, ni);
na->na_startnode--;
}
#endif
while (ni && na->na_size >= while (ni && na->na_size >=
i + sizeof(struct ieee80211_nodereq)) { i + sizeof(struct ieee80211_nodereq)) {
ieee80211_node2req(ic, ni, &nrbuf); ieee80211_node2req(ic, ni, &nrbuf);

View File

@ -339,6 +339,9 @@ struct ieee80211_nodereq {
u_int nr_rsnprotos; u_int nr_rsnprotos;
u_int nr_rsnciphers; u_int nr_rsnciphers;
u_int nr_rsnakms; u_int nr_rsnakms;
#ifdef __FreeBSD_version
uint8_t nr_rsnie[257];
#endif
/* Node flags */ /* Node flags */
u_int8_t nr_flags; u_int8_t nr_flags;
@ -394,6 +397,9 @@ struct ieee80211_nodereq_all {
int na_nodes; /* returned count */ int na_nodes; /* returned count */
size_t na_size; /* size of node buffer */ size_t na_size; /* size of node buffer */
#ifdef __FreeBSD_version
int na_startnode;
#endif
struct ieee80211_nodereq *na_node; /* allocated node buffer */ struct ieee80211_nodereq *na_node; /* allocated node buffer */
/* Match nodes by flag */ /* Match nodes by flag */

View File

@ -672,4 +672,9 @@ int ieee80211_ess_cmp(const struct ieee80211_ess_rbt *,
RBT_PROTOTYPE(ieee80211_tree, ieee80211_node, ni_node, ieee80211_node_cmp); RBT_PROTOTYPE(ieee80211_tree, ieee80211_node, ni_node, ieee80211_node_cmp);
RBT_PROTOTYPE(ieee80211_ess_tree, ieee80211_ess_rbt, ess_rbt, ieee80211_ess_cmp); RBT_PROTOTYPE(ieee80211_ess_tree, ieee80211_ess_rbt, ess_rbt, ieee80211_ess_cmp);
#ifdef __FreeBSD_version
void ieee80211_node_raise_inact(void *arg, struct ieee80211_node *ni);
void ieee80211_clean_inactive_nodes(struct ieee80211com *ic, int inact_max);
#endif
#endif /* _NET80211_IEEE80211_NODE_H_ */ #endif /* _NET80211_IEEE80211_NODE_H_ */

View File

@ -273,6 +273,9 @@ struct ieee80211com {
size_t); size_t);
struct timeout ic_bgscan_timeout; struct timeout ic_bgscan_timeout;
uint32_t ic_bgscan_fail; uint32_t ic_bgscan_fail;
#ifdef __HAIKU__
bigtime_t ic_last_raise_inact;
#endif
u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];