From 9b0f202bdbf1d7e3e96575a651c524b2aca22854 Mon Sep 17 00:00:00 2001 From: Matheus Date: Thu, 10 Sep 2026 12:12:50 -0300 Subject: [PATCH] wlan: expose scan, scan wait and bss cache get to userspace --- kernel-src/include/kernel/wlan.h | 6 +++ kernel-src/io/net/netdev.c | 58 +++++++++++++++++++- kernel-src/io/net/wlan.c | 92 ++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 2 deletions(-) diff --git a/kernel-src/include/kernel/wlan.h b/kernel-src/include/kernel/wlan.h index dd0e6b98..d86e9262 100644 --- a/kernel-src/include/kernel/wlan.h +++ b/kernel-src/include/kernel/wlan.h @@ -2,8 +2,14 @@ #define _WLAN_H #include +#include int wlan_register(void *handle, const u80211_drv_device_metadata_t *metadata, const u80211_drv_device_ops_t *ops, u80211_drv_network_device_handle_t *network_device); void wlan_process_packet(u80211_drv_network_device_handle_t handle, void *packet, size_t packet_size); +int wlan_active_scan(netdev_t *netdev); +int wlan_wait_for_scan(netdev_t *netdev); + +int wlan_get_bss_cache(netdev_t *netdev, void *buffer, size_t size, size_t *records_written); + #endif diff --git a/kernel-src/io/net/netdev.c b/kernel-src/io/net/netdev.c index e8f1a766..0f3d95ad 100644 --- a/kernel-src/io/net/netdev.c +++ b/kernel-src/io/net/netdev.c @@ -6,6 +6,7 @@ #include #include #include +#include static int current_minor; static netdev_t *minors[64]; @@ -22,13 +23,66 @@ typedef struct { int mtu; } net_info_t; +typedef struct { + void *buffer; + size_t buffer_size; + size_t records_returned; // filled in by kernel +} wlan_bss_request_t; + +#define NETDEV_IOCTL_GET_INFO 0x1337631 +#define NETDEV_IOCTL_WLAN_SCAN 0x8021101 +#define NETDEV_IOCTL_WLAN_SCAN_WAIT 0x8021102 +#define NETDEV_IOCTL_WLAN_GET_BSS_CACHE 0x8021103 +#define NETDEV_IOCTL_WLAN_ASSOCIATE 0x8021104 +#define NETDEV_IOCTL_WLAN_ASSOCIATION_WAIT 0x8021105 +#define NETDEV_IOCTL_WLAN_DISASSOCIATE 0x8021106 +#define NETDEV_IOCTL_WLAN_SET_KEY 0x8021107 +#define NETDEV_IOCTL_WLAN_DEL_KEY 0x8021108 + +int handle_wlan_ioctl(netdev_t *netdev, unsigned long request, void *arg, int *result, cred_t *cred) { + // TODO: make every operation besides get bss cache be privileged + switch (request) { + case NETDEV_IOCTL_WLAN_SCAN: + return wlan_active_scan(netdev); + case NETDEV_IOCTL_WLAN_SCAN_WAIT: + return wlan_wait_for_scan(netdev); + case NETDEV_IOCTL_WLAN_GET_BSS_CACHE: { + wlan_bss_request_t bss_request; + int error = USERCOPY_POSSIBLY_FROM_USER(&bss_request, arg, sizeof(bss_request)); + if (error) + return error; + + if (IS_USER_ADDRESS(arg) && !IS_USER_ADDRESS(bss_request.buffer)) + return EFAULT; + + wlan_get_bss_cache(netdev, bss_request.buffer, bss_request.buffer_size, &bss_request.records_returned); + return USERCOPY_POSSIBLY_TO_USER(arg, &bss_request, sizeof(bss_request)); + } + case NETDEV_IOCTL_WLAN_ASSOCIATE: + + break; + case NETDEV_IOCTL_WLAN_DISASSOCIATE: + + break; + case NETDEV_IOCTL_WLAN_SET_KEY: + + break; + case NETDEV_IOCTL_WLAN_DEL_KEY: + + break; + + default: + return ENOTTY; + } +} + static int netdev_ioctl(int minor, unsigned long request, void *arg, int *result, cred_t *cred) { netdev_t *netdev = minors[minor]; if (netdev == NULL) return ENODEV; switch (request) { - case 0x1337631: { + case NETDEV_IOCTL_GET_INFO: { net_info_t *info = arg; sockaddr_t addr = {0}; abisockaddr_t abisockaddr; @@ -77,7 +131,7 @@ static int netdev_ioctl(int minor, unsigned long request, void *arg, int *result break; } default: - return ENOTTY; + return handle_wlan_ioctl(netdev, request, arg, result, cred); } return 0; diff --git a/kernel-src/io/net/wlan.c b/kernel-src/io/net/wlan.c index e104ab34..4fa1b71a 100644 --- a/kernel-src/io/net/wlan.c +++ b/kernel-src/io/net/wlan.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ typedef struct { int id; } wlan_device_t; +// TODO: make u80211 bss cache get more generic than having to pass the bss cache directly +// TODO: handle hotplug // TODO: in u80211_drv *_tx_buffer ops, pass device handle // TODO: change how netdesc_t is handled in kernel overall (?) // TODO: map u80211 errors to errnos @@ -244,3 +247,92 @@ void wlan_process_packet(u80211_drv_network_device_handle_t handle, void *packet u80211_process_packet(wlan->u80211_device, packet, packet_size); interrupt_loweripl(ipl); } + +int wlan_active_scan(netdev_t *netdev) { + wlan_device_t *wlan = (wlan_device_t *)netdev; + + return u80211_scan(wlan->u80211_device) == U80211_STATUS_SUCCESS ? 0 : EINVAL; +} + +int wlan_wait_for_scan(netdev_t *netdev) { + wlan_device_t *wlan = (wlan_device_t *)netdev; + + return u80211_wait_for_scan_completion(wlan->u80211_device) == U80211_STATUS_SUCCESS ? 0 : EINVAL; +} + +typedef struct { + uint16_t length; + uint16_t rsn_ie_length; + uint16_t variable_offset; + uint16_t interval; + uint16_t capabilities; + uint8_t bssid[6]; + uint8_t rate_bitmap[16]; + uint8_t channel; + uint8_t ssid_length; + // SSID + // RSN IE +} wlan_bss_info_t; + +// this may seem excessive (and, for your average residential air, it is). +// however, some places (like university campi or dense urban environments) +// can easily cross into the hundreds of APs. many different implementations +// have moved to use a 1000 limit to bss entries (see cfg80211 or the discussions on +// hostapd/wpa_supplicant to raise the limit) +#define MAX_APS 1000 + +int wlan_get_bss_cache(netdev_t *netdev, void *buffer, size_t size, size_t *records_written) { + wlan_device_t *wlan = (wlan_device_t *)netdev; + + u80211_ap_t **ap_buffer = alloc(sizeof(u80211_ap_t *) * MAX_APS); + if (ap_buffer == NULL) + return ENOMEM; + + size_t ap_count = u80211_bss_cache_get_aps(&wlan->u80211_device->bss_cache, ap_buffer, MAX_APS); + + int error = 0; + *records_written = 0; + for (size_t i = 0; i < ap_count; ++i) { + u80211_ap_t *ap = ap_buffer[i]; + wlan_bss_info_t bss_info; + bss_info.ssid_length = strlen(ap->ssid); + bss_info.length = sizeof(wlan_bss_info_t) + bss_info.ssid_length + ap->rsn_size; + + if (size < bss_info.length) + break; + + bss_info.variable_offset = sizeof(bss_info); + bss_info.rsn_ie_length = ap->rsn_size; + bss_info.interval = ap->interval; + bss_info.capabilities = ap->capabilities; + bss_info.channel = ap->channel; + memcpy(bss_info.bssid, &ap->mac_address, 6); + memcpy(bss_info.rate_bitmap, ap->rate_bitmap, sizeof(bss_info.rate_bitmap)); + + error = USERCOPY_POSSIBLY_TO_USER(buffer, &bss_info, sizeof(bss_info)); + if (error) + break; + + if (bss_info.ssid_length) { + error = USERCOPY_POSSIBLY_TO_USER((void *)((uintptr_t)buffer + sizeof(bss_info)), ap->ssid, bss_info.ssid_length); + if (error) + break; + } + + if (bss_info.rsn_ie_length) { + error = USERCOPY_POSSIBLY_TO_USER((void *)((uintptr_t)buffer + sizeof(bss_info) + bss_info.ssid_length), ap->rsn, bss_info.rsn_ie_length); + if (error) + break; + } + + size -= bss_info.length; + buffer = (void *)((uintptr_t)buffer + bss_info.length); + *records_written += 1; + } + + for (size_t i = 0; i < ap_count; ++i) + u80211_ap_release(ap_buffer[i]); + + free(ap_buffer); + return error; +}