diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h index 457b89cd55..aa79ac2b3c 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h @@ -30,6 +30,7 @@ #ifndef _NET80211_IEEE80211_IOCTL_H_ #define _NET80211_IEEE80211_IOCTL_H_ +#ifndef IEEE80211_IOCTLS_ABBREVIATED /* * IEEE 802.11 ioctls. */ @@ -381,6 +382,7 @@ struct ieee80211req_chanlist { uint8_t ic_channels[32]; /* NB: can be variable length */ }; +#ifndef IEEE80211_IOCTLS_ABBREVIATED /* * Get the active channel list info. */ @@ -393,6 +395,7 @@ struct ieee80211req_chaninfo { (((_nchan)-1) * sizeof(struct ieee80211_channel))) #define IEEE80211_CHANINFO_SPACE(_ci) \ IEEE80211_CHANINFO_SIZE((_ci)->ic_nchans) +#endif /* * Retrieve the WPA/RSN information element for an associated station. @@ -418,6 +421,7 @@ struct ieee80211req_sta_stats { } is_u; struct ieee80211_nodestats is_stats; }; +#endif /* * 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_txpower; /* current tx power */ uint16_t isi_vlan; /* vlan tag */ +#ifndef IEEE80211_IOCTLS_ABBREVIATED /* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */ uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */ uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */ @@ -456,6 +461,7 @@ struct ieee80211req_sta_info { uint16_t isi_localid; uint8_t isi_peerstate; /* XXX frag state? */ +#endif /* variable length IE data */ }; @@ -514,6 +520,7 @@ struct ieee80211req_sta_txpow { (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \ IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#ifndef IEEE80211_IOCTLS_ABBREVIATED /* * Station mode roaming parameters. These are maintained * per band/mode and control the roaming algorithm. @@ -569,6 +576,7 @@ struct ieee80211_devcaps_req { (((_nchan)-1) * sizeof(struct ieee80211_channel))) #define IEEE80211_DEVCAPS_SPACE(_dc) \ IEEE80211_DEVCAPS_SIZE((_dc)->dc_chaninfo.ic_nchans) +#endif struct ieee80211_chanswitch_req { 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_nrates; 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_meshid_len; /* MESH ID length */ /* variable length SSID, followed by variable length MESH ID, diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp b/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp index 89a8f7a342..5919867a1c 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp @@ -3,6 +3,9 @@ * Distributed under the terms of the MIT License. */ +#include +#include + extern "C" { #include #include @@ -23,12 +26,16 @@ extern "C" { #include #include +#include +#include + +#undef _NET80211_IEEE80211_IOCTL_H_ +#define IEEE80211_IOCTLS_ABBREVIATED +#include "../../freebsd_wlan/net80211/ieee80211_ioctl.h" } #include -#include - #include #include @@ -98,7 +105,7 @@ start_wlan(device_t device) if_initname(ifp, device_get_name(device), i); - dprintf("%s: wlan started.\n", __func__); + TRACE("%s: wlan started.\n", __func__); return B_OK; } @@ -120,7 +127,7 @@ stop_wlan(device_t device) status_t wlan_close(void* cookie) { - dprintf("wlan_close(%p)\n", cookie); + TRACE("wlan_close(%p)\n", cookie); struct ifnet* ifp = (struct ifnet*)cookie; 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 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; -#if 0 - switch (op) { - case SIOCG80211: - case SIOCS80211: - { - // FreeBSD drivers assume that the request structure has already - // been copied into kernel space - struct ieee80211req request; - if (user_memcpy(&request, arg, sizeof(struct ieee80211req)) != B_OK) + struct ieee80211com* ic = (ieee80211com*)ifp; + + struct ieee80211req ireq; + if (user_memcpy(&ireq, arg, sizeof(struct ieee80211req)) != B_OK) + return B_BAD_ADDRESS; + + switch (ireq.i_type) { + case IEEE80211_IOC_SCAN_RESULTS: { + 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; - TRACE("wlan_control: %" B_PRIu32 ", %d\n", op, request.i_type); - status_t status = ifp->if_ioctl(ifp, op, (caddr_t)&request); + break; + } + + 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) return status; - if (op == SIOCG80211 && user_memcpy(arg, &request, - sizeof(struct ieee80211req)) != B_OK) - return B_BAD_ADDRESS; - return B_OK; - } - } -#endif + if (op == SIOCG80211) { + if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK) + return B_BAD_ADDRESS; + } - 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; } diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.c b/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.c index 5f4ae04d38..b433d14242 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.c +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.c @@ -129,6 +129,12 @@ ieee80211_node2req(struct ieee80211com *ic, const struct ieee80211_node *ni, nr->nr_rsnakms |= IEEE80211_WPA_AKM_SHA256_8021X; if (ni->ni_supported_rsnakms & IEEE80211_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 */ 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->na_nodes = i = 0; 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 >= i + sizeof(struct ieee80211_nodereq)) { ieee80211_node2req(ic, ni, &nrbuf); diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.h b/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.h index a2c69e00e4..d8e3ff87cb 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.h +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_ioctl.h @@ -339,6 +339,9 @@ struct ieee80211_nodereq { u_int nr_rsnprotos; u_int nr_rsnciphers; u_int nr_rsnakms; +#ifdef __FreeBSD_version + uint8_t nr_rsnie[257]; +#endif /* Node flags */ u_int8_t nr_flags; @@ -394,6 +397,9 @@ struct ieee80211_nodereq_all { int na_nodes; /* returned count */ size_t na_size; /* size of node buffer */ +#ifdef __FreeBSD_version + int na_startnode; +#endif struct ieee80211_nodereq *na_node; /* allocated node buffer */ /* Match nodes by flag */ diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_node.h b/src/libs/compat/openbsd_wlan/net80211/ieee80211_node.h index 2bc7b4afe7..bb3aaf2bc0 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_node.h +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_node.h @@ -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_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_ */ diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_var.h b/src/libs/compat/openbsd_wlan/net80211/ieee80211_var.h index 1288a6c389..cb79c423ce 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_var.h +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_var.h @@ -273,6 +273,9 @@ struct ieee80211com { size_t); struct timeout ic_bgscan_timeout; uint32_t ic_bgscan_fail; +#ifdef __HAIKU__ + bigtime_t ic_last_raise_inact; +#endif u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];