mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
net: port over u80211
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
#ifndef U80211_AP_H
|
||||
#define U80211_AP_H
|
||||
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/rbtree.h>
|
||||
|
||||
typedef struct {
|
||||
int refcount;
|
||||
u80211_mac_address_t mac_address;
|
||||
uint16_t interval;
|
||||
uint16_t capabilities;
|
||||
uint8_t channel;
|
||||
uint8_t rate_bitmap[16];
|
||||
char ssid[33];
|
||||
uint8_t *rsn;
|
||||
size_t rsn_size;
|
||||
u80211_rbtree_t cache_node;
|
||||
} u80211_ap_t;
|
||||
|
||||
u80211_ap_t *u80211_ap_allocate(const u80211_beacon_data_t *beacon_data);
|
||||
void u80211_ap_inactive(u80211_ap_t *ap);
|
||||
|
||||
static inline void u80211_ap_hold(u80211_ap_t *ap) {
|
||||
__atomic_add_fetch(&ap->refcount, 1, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline void u80211_ap_release(u80211_ap_t *ap) {
|
||||
if (__atomic_sub_fetch(&ap->refcount, 1, __ATOMIC_RELAXED) == 0)
|
||||
u80211_ap_inactive(ap);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef U80211_ASSOCIATION_H
|
||||
#define U80211_ASSOCIATION_H
|
||||
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/u80211.h>
|
||||
|
||||
void u80211_association_process_authentication(u80211_device_t *device, u80211_auth_data_t *auth_data); // called from interrupt context
|
||||
void u80211_association_process_response(u80211_device_t *device, u80211_association_response_data_t *association_data); // called from interrupt context
|
||||
void u80211_association_process_deauthentication(u80211_device_t *device, u80211_deauthentication_data_t *deauthentication_data); // called from interrupt context
|
||||
void u80211_association_process_disassociation(u80211_device_t *device, u80211_disassociation_data_t *disassociation_data); // called from interrupt context
|
||||
bool u80211_association_is_duplicate(u80211_device_t *device, const u80211_header_description_t *header, int expected_state); // called from interrupt context
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef U80211_BSS_CACHE_H
|
||||
#define U80211_BSS_CACHE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <u80211/ap.h>
|
||||
|
||||
typedef struct {
|
||||
void *rwlock;
|
||||
u80211_rbtree_t *root;
|
||||
size_t entry_count;
|
||||
} bss_cache_t;
|
||||
|
||||
int u80211_bss_cache_init(bss_cache_t *cache);
|
||||
void u80211_bss_cache_deinit(bss_cache_t *cache);
|
||||
void u80211_bss_cache_purge(bss_cache_t *cache);
|
||||
void u80211_bss_cache_insert(bss_cache_t *cache, u80211_ap_t *ap);
|
||||
void u80211_bss_cache_remove(bss_cache_t *cache, u80211_mac_address_t *mac);
|
||||
u80211_ap_t *u80211_bss_cache_find(bss_cache_t *cache, u80211_mac_address_t *mac);
|
||||
// each AP pointer written to buffer has an acquired reference that the caller must release.
|
||||
size_t u80211_bss_cache_get_aps(bss_cache_t *cache, u80211_ap_t **buffer, size_t capacity);
|
||||
size_t u80211_bss_cache_get_count(bss_cache_t *cache);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef U80211_KERNEL_INTERFACE_H
|
||||
#define U80211_KERNEL_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <u80211/u80211.h>
|
||||
|
||||
// allocates 'size' bytes of memory
|
||||
// return NULL on allocation failure
|
||||
void *u80211_kernel_allocate(size_t size);
|
||||
|
||||
// frees the kernel memory referenced by 'ptr'
|
||||
// 'ptr' will never be NULL
|
||||
void u80211_kernel_free(void *ptr);
|
||||
|
||||
// allocates an opaque mutex object
|
||||
// return NULL on allocation failure
|
||||
void *u80211_kernel_allocate_mutex(void);
|
||||
|
||||
// frees the mutex object associated with the 'mutex' pointer
|
||||
// 'mutex' will never be NULL
|
||||
void u80211_kernel_free_mutex(void *mutex);
|
||||
|
||||
// basic mutex operations
|
||||
void u80211_kernel_acquire_mutex(void *mutex);
|
||||
void u80211_kernel_release_mutex(void *mutex);
|
||||
|
||||
// allocates an opaque semaphore object
|
||||
// 'initial_count' contains the initial value of the semaphore
|
||||
void *u80211_kernel_allocate_semaphore(unsigned int initial_count);
|
||||
// frees the semaphore object referenced by 'semaphore'
|
||||
// 'semaphore' will never be NULL
|
||||
void u80211_kernel_free_semaphore(void *semaphore);
|
||||
|
||||
// basic semphore operations
|
||||
void u80211_kernel_wait_semaphore(void *semaphore);
|
||||
void u80211_kernel_signal_semaphore(void *semaphore);
|
||||
|
||||
// allocates an opaque spinlock object
|
||||
// return NULL on allocation failure
|
||||
void *u80211_kernel_allocate_spinlock(void);
|
||||
|
||||
// frees the spinlock object referenced by 'spinlock'
|
||||
// 'spinlock' will never be NULL
|
||||
void u80211_kernel_free_spinlock(void *spinlock);
|
||||
|
||||
// basic spinlock operations
|
||||
// in case the kernel implements interrupt priorities, these functions
|
||||
// must raise/lower it from the network interrupt priority.
|
||||
// otherwise, disable interrupts.
|
||||
void u80211_kernel_acquire_spinlock(void *spinlock);
|
||||
void u80211_kernel_release_spinlock(void *spinlock);
|
||||
|
||||
// allocates an opaque rwlock object
|
||||
// return NULL on allocation failure
|
||||
void *u80211_kernel_allocate_rwlock(void);
|
||||
|
||||
// frees the rwlock object referenced by 'rwlock'
|
||||
// 'spinlock' will never be NULL
|
||||
void u80211_kernel_free_rwlock(void *rwlock);
|
||||
|
||||
// basic rwlock operations
|
||||
// an acquire will always be paired with a release of the same type
|
||||
void u80211_kernel_acquire_rwlock_exclusive(void *rwlock);
|
||||
void u80211_kernel_acquire_rwlock_shared(void *rwlock);
|
||||
void u80211_kernel_release_rwlock_exclusive(void *rwlock);
|
||||
void u80211_kernel_release_rwlock_shared(void *rwlock);
|
||||
|
||||
typedef void (*u80211_kernel_work_fn_t)(void *context);
|
||||
|
||||
// allocates an opaque work object
|
||||
// return NULL on allocation failure
|
||||
void *u80211_kernel_allocate_work(void);
|
||||
|
||||
// enqueue threaded work. this might be called from an interrupt context
|
||||
// 'ms' == 0 enqueues the work immediatelly. otherwise,
|
||||
// work is enqueued 'ms' milliseconds in the future
|
||||
// if work is already pending, the new request must be ignored
|
||||
void u80211_kernel_enqueue_work(void *work, u80211_kernel_work_fn_t function, void *context, size_t ms);
|
||||
|
||||
// frees the work object referenced by 'work'
|
||||
// 'work' will never be NULL
|
||||
// may be called by the work's callback
|
||||
void u80211_kernel_free_work(void *work);
|
||||
|
||||
// kernel packet receive callback
|
||||
// buffer has an ethernet header
|
||||
void u80211_kernel_receive_callback(u80211_device_t *device, void *buffer, size_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef U80211_KEY_H
|
||||
#define U80211_KEY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <u80211/packet.h>
|
||||
|
||||
typedef enum {
|
||||
U80211_CIPHER_CCMP,
|
||||
U80211_CIPHER_TKIP,
|
||||
U80211_CIPHER_WEP40,
|
||||
U80211_CIPHER_WEP104,
|
||||
} u80211_cipher_t;
|
||||
|
||||
typedef enum {
|
||||
U80211_KEY_PAIRWISE = 1 << 0,
|
||||
U80211_KEY_GROUP = 1 << 1,
|
||||
U80211_KEY_RX = 1 << 2,
|
||||
U80211_KEY_TX = 1 << 3,
|
||||
} u80211_key_flags_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_cipher_t cipher;
|
||||
uint8_t index;
|
||||
u80211_mac_address_t peer;
|
||||
const uint8_t *key;
|
||||
size_t key_len;
|
||||
const uint8_t *rx_seq;
|
||||
size_t rx_seq_len;
|
||||
uint32_t flags;
|
||||
} u80211_key_t;
|
||||
|
||||
typedef struct {
|
||||
const u80211_header_description_t *header;
|
||||
uint8_t key_index;
|
||||
const u80211_mac_address_t *destination;
|
||||
const u80211_mac_address_t *source;
|
||||
uint8_t priority;
|
||||
const void *data;
|
||||
size_t data_size;
|
||||
uint8_t sequence[6];
|
||||
uint8_t mic[8];
|
||||
} u80211_tkip_data_t;
|
||||
|
||||
int u80211_set_key(u80211_device_t *device, const u80211_key_t *key);
|
||||
int u80211_del_key(u80211_device_t *device, uint8_t index, const u80211_mac_address_t *peer, uint32_t flags);
|
||||
int u80211_select_key(u80211_device_t *device, const u80211_header_description_t *header);
|
||||
int u80211_select_cipher(u80211_device_t *device, const u80211_header_description_t *header);
|
||||
int u80211_select_cipher_by_index(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index);
|
||||
bool u80211_key_update_rx_sequence(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index, u80211_cipher_t cipher, const uint8_t *sequence, size_t sequence_size);
|
||||
bool u80211_key_validate_tkip_rx(u80211_device_t *device, const u80211_tkip_data_t *data);
|
||||
bool u80211_key_prepare_tkip_tx(u80211_device_t *device, u80211_tkip_data_t *data);
|
||||
bool u80211_key_next_tx_sequence(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index, u80211_cipher_t cipher, uint8_t *sequence, size_t sequence_size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef U80211_LIST_H
|
||||
#define U80211_LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct u80211_list_node {
|
||||
struct u80211_list_node *prev;
|
||||
struct u80211_list_node *next;
|
||||
} u80211_list_node_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_list_node_t *head;
|
||||
u80211_list_node_t *tail;
|
||||
} u80211_list_t;
|
||||
|
||||
#define U80211_LIST_INIT_VALUE ((u80211_list_t) {.head = NULL, .tail = NULL})
|
||||
|
||||
static inline void u80211_list_init(u80211_list_t *list) {
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
}
|
||||
|
||||
static inline void u80211_list_push_front(u80211_list_t *list, u80211_list_node_t *node) {
|
||||
node->prev = NULL;
|
||||
node->next = list->head;
|
||||
if (list->head != NULL) {
|
||||
list->head->prev = node;
|
||||
} else {
|
||||
list->tail = node;
|
||||
}
|
||||
list->head = node;
|
||||
}
|
||||
|
||||
static inline void u80211_list_push_back(u80211_list_t *list, u80211_list_node_t *node) {
|
||||
node->next = NULL;
|
||||
node->prev = list->tail;
|
||||
if (list->tail != NULL) {
|
||||
list->tail->next = node;
|
||||
} else {
|
||||
list->head = node;
|
||||
}
|
||||
list->tail = node;
|
||||
}
|
||||
|
||||
static inline void u80211_list_remove(u80211_list_t *list, u80211_list_node_t *node) {
|
||||
if (node->prev != NULL) {
|
||||
node->prev->next = node->next;
|
||||
} else {
|
||||
list->head = node->next;
|
||||
}
|
||||
if (node->next != NULL) {
|
||||
node->next->prev = node->prev;
|
||||
} else {
|
||||
list->tail = node->prev;
|
||||
}
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
static inline u80211_list_node_t *u80211_list_pop_front(u80211_list_t *list) {
|
||||
if (list->head == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
u80211_list_node_t *node = list->head;
|
||||
u80211_list_remove(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline u80211_list_node_t *u80211_list_pop_back(u80211_list_t *list) {
|
||||
if (list->tail == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
u80211_list_node_t *node = list->tail;
|
||||
u80211_list_remove(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
#define u80211_list_for_each(LIST, ITER) \
|
||||
for (u80211_list_node_t *ITER = (LIST)->head; ITER != NULL; ITER = ITER->next)
|
||||
|
||||
#define u80211_list_for_each_safe(LIST, ITER) \
|
||||
for (u80211_list_node_t *ITER = (LIST)->head, *_next = (ITER ? ITER->next : NULL); ITER != NULL; ITER = _next, _next = (ITER ? ITER->next : NULL))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef U80211_MICHAEL_H
|
||||
#define U80211_MICHAEL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void u80211_michael_mic(const uint8_t key[8], const uint8_t da[6], const uint8_t sa[6], uint8_t priority, const void *data, size_t len, uint8_t out[8]);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef U80211_PACKET_H
|
||||
#define U80211_PACKET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct u80211_device u80211_device_t;
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
size_t size;
|
||||
size_t current_offset;
|
||||
} u80211_tx_buffer_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
int key;
|
||||
int cipher;
|
||||
} u80211_transmit_options_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bytes[6];
|
||||
} u80211_mac_address_t;
|
||||
|
||||
static inline bool u80211_mac_address_equal(const u80211_mac_address_t *a, const u80211_mac_address_t *b) {
|
||||
for (size_t i = 0; i < sizeof(a->bytes); ++i) {
|
||||
if (a->bytes[i] != b->bytes[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define U80211_HEADER_FRAME_CONTROL_GET_VERSION(x) ((x) & 0x3)
|
||||
#define U80211_HEADER_FRAME_CONTROL_GET_TYPE(x) (((x) & 0xc) >> 2)
|
||||
#define U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT 0
|
||||
#define U80211_HEADER_FRAME_CONTROL_TYPE_CONTROL 1
|
||||
#define U80211_HEADER_FRAME_CONTROL_TYPE_DATA 2
|
||||
#define U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(x) (((x) & 0xf0) >> 4)
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_ASSOCIATION_REQUEST 0
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_ASSOCIATION_RESPONSE 1
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_PROBE_REQUEST 4
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_PROBE_RESPONSE 5
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_DISASSOCIATION 10
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_AUTHENTICATION 11
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_DEAUTHENTICATION 12
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA 0
|
||||
#define U80211_HEADER_FRAME_CONTROL_SUBTYPE_NULL_DATA 4
|
||||
#define U80211_HEADER_FRAME_CONTROL_TO_DS 0x100
|
||||
#define U80211_HEADER_FRAME_CONTROL_FROM_DS 0x200
|
||||
#define U80211_HEADER_FRAME_CONTROL_MORE_FRAGMENTS 0x400
|
||||
#define U80211_HEADER_FRAME_CONTROL_RETRY 0x800
|
||||
#define U80211_HEADER_FRAME_CONTROL_POWER_MANAGEMENT 0x1000
|
||||
#define U80211_HEADER_FRAME_CONTROL_MORE_DATA 0x2000
|
||||
#define U80211_HEADER_FRAME_CONTROL_PROTECTED_FRAME 0x4000
|
||||
#define U80211_HEADER_FRAME_CONTROL_ORDER 0x8000
|
||||
|
||||
typedef struct {
|
||||
uint16_t frame_control;
|
||||
uint16_t duration_id;
|
||||
u80211_mac_address_t addresses[4];
|
||||
uint16_t sequence_control;
|
||||
} u80211_header_description_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_mac_address_t mac_address;
|
||||
uint16_t interval;
|
||||
uint16_t capabilities;
|
||||
uint8_t channel;
|
||||
uint8_t rate_bitmap[16];
|
||||
char ssid[33];
|
||||
const uint8_t *rsn;
|
||||
size_t rsn_size;
|
||||
} u80211_beacon_data_t;
|
||||
|
||||
#define U80211_AUTH_ALGORITHM_OPEN 0
|
||||
typedef struct {
|
||||
u80211_mac_address_t address;
|
||||
uint16_t auth_algorithm;
|
||||
uint16_t auth_transaction;
|
||||
uint16_t status;
|
||||
} u80211_auth_data_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_mac_address_t address;
|
||||
uint16_t capabilities;
|
||||
uint16_t status;
|
||||
uint16_t association_id;
|
||||
uint8_t rate_bitmap[16];
|
||||
} u80211_association_response_data_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_mac_address_t address;
|
||||
uint16_t reason;
|
||||
} u80211_deauthentication_data_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_mac_address_t address;
|
||||
uint16_t reason;
|
||||
} u80211_disassociation_data_t;
|
||||
|
||||
int u80211_deserialize_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size);
|
||||
int u80211_serialize_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor);
|
||||
void u80211_process_management_packet(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size); // called from an interrupt context
|
||||
void u80211_process_data_packet(u80211_device_t *device, u80211_header_description_t *header, void *data, size_t data_size); // called from an interrupt context
|
||||
|
||||
int u80211_send_probe_request(u80211_device_t *device);
|
||||
int u80211_send_authentication(u80211_device_t *device, u80211_auth_data_t *auth_data);
|
||||
int u80211_send_deauthentication(u80211_device_t *device, u80211_deauthentication_data_t *deauthentication_data);
|
||||
int u80211_send_association_request(u80211_device_t *device, const void *information_elements, size_t information_elements_size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef U80211_RBTREE_H
|
||||
#define U80211_RBTREE_H
|
||||
|
||||
typedef struct u80211_rbtree_t {
|
||||
struct u80211_rbtree_t *left, *right, *parent;
|
||||
} u80211_rbtree_t;
|
||||
|
||||
// return -1 when a < b
|
||||
// return 0 when a = b
|
||||
// return 1 when a > b
|
||||
typedef int (*u80211_rbtree_compare_fn_t)(u80211_rbtree_t *a, u80211_rbtree_t *b);
|
||||
typedef int (*u80211_rbtree_value_compare_fn_t)(void *a, u80211_rbtree_t *b);
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_lookup(u80211_rbtree_t *rbtree, void *key, u80211_rbtree_value_compare_fn_t compare_fn);
|
||||
void u80211_rbtree_insert(u80211_rbtree_t **rbtreep, u80211_rbtree_t *node, u80211_rbtree_compare_fn_t compare_fn);
|
||||
void u80211_rbtree_remove(u80211_rbtree_t **rbtreep, u80211_rbtree_t *node);
|
||||
void u80211_rbtree_check(u80211_rbtree_t *rbtree);
|
||||
u80211_rbtree_t *u80211_rbtree_first(u80211_rbtree_t *rbtree);
|
||||
u80211_rbtree_t *u80211_rbtree_last(u80211_rbtree_t *rbtree);
|
||||
u80211_rbtree_t *u80211_rbtree_successor(u80211_rbtree_t *node);
|
||||
u80211_rbtree_t *u80211_rbtree_predecessor(u80211_rbtree_t *node);
|
||||
u80211_rbtree_t *u80211_rbtree_find_first_larger_equal(u80211_rbtree_t *rbtree, void *key, u80211_rbtree_value_compare_fn_t compare_fn);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef U80211_REGULATORY_DATABASE_H
|
||||
#define U80211_REGULATORY_DATABASE_H
|
||||
|
||||
// TODO: this will have safe defaults for transmission on all countries.
|
||||
// eventually a proper "database" should be implemented to map a 2-character
|
||||
// country code into channel rules.
|
||||
|
||||
#define U80211_CHANNEL_RULES_DISABLED 1 // no receive/transmission.
|
||||
#define U80211_CHANNEL_RULES_PASSIVE 2 // only transmit when a passive scan returns an AP in that channel.
|
||||
typedef struct {
|
||||
int flags;
|
||||
int max_dbm;
|
||||
} u80211_channel_rules_t;
|
||||
|
||||
u80211_channel_rules_t u80211_get_channel_rules(int channel);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef U80211_RINGBUFFER_H
|
||||
#define U80211_RINGBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
uintmax_t write;
|
||||
uintmax_t read;
|
||||
void *data;
|
||||
} u80211_ringbuffer_t;
|
||||
|
||||
int u80211_ringbuffer_init(u80211_ringbuffer_t *ringbuffer, size_t size);
|
||||
void u80211_ringbuffer_destroy(u80211_ringbuffer_t *ringbuffer);
|
||||
|
||||
size_t u80211_ringbuffer_read(u80211_ringbuffer_t *ringbuffer, void *buffer, size_t count);
|
||||
size_t u80211_ringbuffer_write(u80211_ringbuffer_t *ringbuffer, const void *buffer, size_t count);
|
||||
size_t u80211_ringbuffer_truncate(u80211_ringbuffer_t *ringbuffer, size_t count);
|
||||
size_t u80211_ringbuffer_peek(const u80211_ringbuffer_t *ringbuffer, void *buffer, uintmax_t offset, size_t count);
|
||||
size_t u80211_ringbuffer_remove(u80211_ringbuffer_t *ringbuffer, size_t count);
|
||||
|
||||
#define U80211_RINGBUFFER_DATA_COUNT(x) ((x)->write - (x)->read)
|
||||
#define U80211_RINGBUFFER_SIZE(x) ((x)->size)
|
||||
#define U80211_RINGBUFFER_FREE_SPACE(x) (U80211_RINGBUFFER_SIZE(x) - U80211_RINGBUFFER_DATA_COUNT(x))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef U80211_SCAN_H
|
||||
#define U80211_SCAN_H
|
||||
|
||||
#include <u80211/packet.h>
|
||||
|
||||
void u80211_scan_process_response(u80211_device_t *device, u80211_beacon_data_t *beacon_data);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef U80211_STATUS_H
|
||||
#define U80211_STATUS_H
|
||||
|
||||
#define U80211_STATUS_SUCCESS 0
|
||||
#define U80211_STATUS_ENOMEM 1
|
||||
#define U80211_STATUS_UNSUPPORTED 2
|
||||
#define U80211_STATUS_NOT_ENOUGH_SPACE 3
|
||||
#define U80211_STATUS_BUSY 4
|
||||
#define U80211_STATUS_NOT_PERMITTED 5
|
||||
#define U80211_STATUS_RETRY 6
|
||||
#define U80211_STATUS_REJECTED 7
|
||||
#define U80211_STATUS_TIMED_OUT 8
|
||||
#define U80211_STATUS_NOT_ASSOCIATED 9
|
||||
#define U80211_STATUS_UNKNOWN_ERROR 0xffff
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef U80211_STRING_H
|
||||
#define U80211_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void *u80211_memcpy(void *destination, const void *source, size_t size);
|
||||
int u80211_memcmp(const void *a, const void *b, size_t size);
|
||||
void *u80211_memset(void *destination, int value, size_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef U80211_U80211_H
|
||||
#define U80211_U80211_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <u80211/bss_cache.h>
|
||||
#include <u80211/list.h>
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/ap.h>
|
||||
#include <u80211/key.h>
|
||||
#include <u80211/status.h>
|
||||
|
||||
static inline void *u80211_descriptor_allocate_space(u80211_tx_buffer_descriptor_t *descriptor, size_t size) {
|
||||
if (size > descriptor->current_offset)
|
||||
return NULL;
|
||||
|
||||
descriptor->current_offset -= size;
|
||||
return (uint8_t *)descriptor->data + descriptor->current_offset;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int (*allocate_tx_buffer)(u80211_device_t *device, size_t size, u80211_tx_buffer_descriptor_t *buffer_descriptor);
|
||||
int (*free_tx_buffer)(u80211_device_t *device, u80211_tx_buffer_descriptor_t *buffer_descriptor);
|
||||
int (*transmit)(u80211_device_t *device, u80211_tx_buffer_descriptor_t *buffer_descriptor, const u80211_transmit_options_t *options);
|
||||
int (*set_channel)(u80211_device_t *device, int channel);
|
||||
int (*set_key)(u80211_device_t *device, const u80211_key_t *key);
|
||||
int (*del_key)(u80211_device_t *device, uint8_t index, const u80211_mac_address_t *peer, uint32_t flags);
|
||||
} u80211_device_ops_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_mac_address_t mac_address;
|
||||
uint8_t rate_bitmap[16];
|
||||
} u80211_device_metadata_t;
|
||||
|
||||
#define U80211_DEVICE_STATE_DOWN 0
|
||||
#define U80211_DEVICE_STATE_SCANNING 1
|
||||
#define U80211_DEVICE_STATE_AUTHENTICATING 2
|
||||
#define U80211_DEVICE_STATE_ASSOCIATING 3
|
||||
#define U80211_DEVICE_STATE_ASSOCIATED 4
|
||||
#define U80211_DEVICE_STATE_DEAUTHENTICATING 5
|
||||
struct u80211_device {
|
||||
u80211_device_metadata_t metadata;
|
||||
void *driver_data;
|
||||
const u80211_device_ops_t *ops;
|
||||
|
||||
int state;
|
||||
size_t packet_count;
|
||||
uint16_t tx_sequence_control;
|
||||
uint16_t received_sequence_control;
|
||||
bool received_sequence_control_valid;
|
||||
void *key_spinlock;
|
||||
u80211_list_t keys;
|
||||
|
||||
void *scan_spinlock;
|
||||
void *scan_context;
|
||||
u80211_list_t scan_waiters;
|
||||
|
||||
bss_cache_t bss_cache;
|
||||
|
||||
void *association_spinlock;
|
||||
void *association_context;
|
||||
void *association_cleanup_work;
|
||||
u80211_list_t association_waiters;
|
||||
unsigned int association_generation;
|
||||
int association_result;
|
||||
u80211_ap_t *ap;
|
||||
u80211_ap_t *disconnected_ap;
|
||||
};
|
||||
|
||||
static inline int u80211_get_device_state(u80211_device_t *device) {
|
||||
return __atomic_load_n(&device->state, __ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline bool u80211_set_device_state(u80211_device_t *device, int old_state, int new_state) {
|
||||
return __atomic_compare_exchange_n(&device->state, &old_state, new_state, false, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
int u80211_scan(u80211_device_t *device);
|
||||
int u80211_wait_for_scan_completion(u80211_device_t *device);
|
||||
|
||||
void u80211_process_packet(u80211_device_t *device, void *packet, size_t packet_size);
|
||||
|
||||
// these are mostly diagnostic and should not be relied upon
|
||||
size_t u80211_get_packet_count(u80211_device_t *device);
|
||||
void u80211_reset_packet_count(u80211_device_t *device);
|
||||
|
||||
int u80211_register_device(const u80211_device_metadata_t *metadata, const u80211_device_ops_t *ops, void *driver_data, u80211_device_t **device_out);
|
||||
void u80211_unregister_device(u80211_device_t *device);
|
||||
|
||||
int u80211_associate(u80211_device_t *device, u80211_ap_t *ap, const void *information_elements, size_t information_elements_size);
|
||||
int u80211_wait_for_association_completion(u80211_device_t *device);
|
||||
int u80211_disassociate(u80211_device_t *device);
|
||||
|
||||
// allocates an ethernet-sized buffer
|
||||
int u80211_allocate_tx_buffer(u80211_device_t *device, u80211_tx_buffer_descriptor_t *descriptor);
|
||||
// expects an ethernet header. descriptor **must** be a buffer returned by u80211_allocate_tx_buffer.
|
||||
// consumes the descriptor regardless of the returned status.
|
||||
int u80211_transmit_buffer(u80211_device_t *device, u80211_tx_buffer_descriptor_t *descriptor);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef U80211_UTIL_H
|
||||
#define U80211_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211/string.h>
|
||||
|
||||
static inline uint16_t u80211_host_to_le16(uint16_t value) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return __builtin_bswap16(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t u80211_host_to_le32(uint32_t value) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return __builtin_bswap32(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t u80211_host_to_le64(uint64_t value) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return __builtin_bswap64(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint16_t u80211_le_to_host16(uint16_t value) {
|
||||
return u80211_host_to_le16(value);
|
||||
}
|
||||
|
||||
static inline uint32_t u80211_le_to_host32(uint32_t value) {
|
||||
return u80211_host_to_le32(value);
|
||||
}
|
||||
|
||||
static inline uint64_t u80211_le_to_host64(uint64_t value) {
|
||||
return u80211_host_to_le64(value);
|
||||
}
|
||||
|
||||
#define host_to_le(value) _Generic((value), \
|
||||
uint16_t: u80211_host_to_le16, \
|
||||
uint32_t: u80211_host_to_le32, \
|
||||
uint64_t: u80211_host_to_le64 \
|
||||
)(value)
|
||||
|
||||
#define le_to_host(value) _Generic((value), \
|
||||
uint16_t: u80211_le_to_host16, \
|
||||
uint32_t: u80211_le_to_host32, \
|
||||
uint64_t: u80211_le_to_host64 \
|
||||
)(value)
|
||||
|
||||
static inline uint16_t deserialize_le16(const void *source) {
|
||||
uint16_t value;
|
||||
u80211_memcpy(&value, source, sizeof(value));
|
||||
return le_to_host(value);
|
||||
}
|
||||
|
||||
static inline uint32_t deserialize_le32(const void *source) {
|
||||
uint32_t value;
|
||||
u80211_memcpy(&value, source, sizeof(value));
|
||||
return le_to_host(value);
|
||||
}
|
||||
|
||||
static inline void serialize_le16(uint8_t *destination, uint16_t value) {
|
||||
uint16_t little_endian_value = host_to_le(value);
|
||||
u80211_memcpy(destination, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
static inline void serialize_le32(uint8_t *destination, uint32_t value) {
|
||||
uint32_t little_endian_value = host_to_le(value);
|
||||
u80211_memcpy(destination, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *)((uintptr_t)ptr - offsetof(type, member)))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,344 @@
|
||||
#include <u80211/kernel_interface.h>
|
||||
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/eth.h>
|
||||
#include <kernel/event.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/interrupt.h>
|
||||
#include <kernel/page.h>
|
||||
#include <kernel/scheduler.h>
|
||||
#include <kernel/slab.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/timekeeper.h>
|
||||
#include <mutex.h>
|
||||
#include <pushlock.h>
|
||||
#include <semaphore.h>
|
||||
#include <spinlock.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static scache_t *pushlock_cache;
|
||||
static scache_t *semaphore_cache;
|
||||
static scache_t *spinlock_cache;
|
||||
static scache_t *work_cache;
|
||||
|
||||
void *u80211_kernel_allocate(size_t size) {
|
||||
return alloc(size);
|
||||
}
|
||||
|
||||
void u80211_kernel_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *u80211_kernel_allocate_mutex(void) {
|
||||
mutex_t *mutex = slab_allocate(pushlock_cache);
|
||||
if (mutex)
|
||||
MUTEX_INIT(mutex);
|
||||
|
||||
return mutex;
|
||||
}
|
||||
|
||||
void u80211_kernel_free_mutex(void *mutex) {
|
||||
slab_free(pushlock_cache, mutex);
|
||||
}
|
||||
|
||||
void u80211_kernel_acquire_mutex(void *mutex) {
|
||||
MUTEX_ACQUIRE(mutex);
|
||||
}
|
||||
|
||||
void u80211_kernel_release_mutex(void *mutex) {
|
||||
MUTEX_RELEASE(mutex);
|
||||
}
|
||||
|
||||
void *u80211_kernel_allocate_semaphore(unsigned int initial_count) {
|
||||
semaphore_t *semaphore = slab_allocate(semaphore_cache);
|
||||
if (semaphore)
|
||||
SEMAPHORE_INIT(semaphore, initial_count);
|
||||
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
void u80211_kernel_free_semaphore(void *semaphore) {
|
||||
slab_free(semaphore_cache, semaphore);
|
||||
}
|
||||
|
||||
void u80211_kernel_wait_semaphore(void *semaphore) {
|
||||
semaphore_wait(semaphore, false);
|
||||
}
|
||||
|
||||
void u80211_kernel_signal_semaphore(void *semaphore) {
|
||||
semaphore_signal(semaphore);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
spinlock_t lock;
|
||||
long old_ipl;
|
||||
} u80211_spinlock_t;
|
||||
|
||||
|
||||
void *u80211_kernel_allocate_spinlock(void) {
|
||||
u80211_spinlock_t *spinlock = slab_allocate(spinlock_cache);
|
||||
if (spinlock)
|
||||
SPINLOCK_INIT(spinlock->lock);
|
||||
|
||||
return spinlock;
|
||||
}
|
||||
|
||||
void u80211_kernel_free_spinlock(void *spinlock) {
|
||||
slab_free(spinlock_cache, spinlock);
|
||||
}
|
||||
|
||||
void u80211_kernel_acquire_spinlock(void *opaque_spinlock) {
|
||||
u80211_spinlock_t *spinlock = opaque_spinlock;
|
||||
spinlock->old_ipl = spinlock_acquire_raise_ipl(&spinlock->lock, IPL_NET);
|
||||
}
|
||||
|
||||
void u80211_kernel_release_spinlock(void *opaque_spinlock) {
|
||||
u80211_spinlock_t *spinlock = opaque_spinlock;
|
||||
spinlock_release_lower_ipl(&spinlock->lock, spinlock->old_ipl);
|
||||
}
|
||||
|
||||
void *u80211_kernel_allocate_rwlock(void) {
|
||||
pushlock_t *rwlock = slab_allocate(pushlock_cache);
|
||||
if (rwlock)
|
||||
*rwlock = 0;
|
||||
|
||||
return rwlock;
|
||||
}
|
||||
|
||||
void u80211_kernel_free_rwlock(void *rwlock) {
|
||||
slab_free(pushlock_cache, rwlock);
|
||||
}
|
||||
|
||||
void u80211_kernel_acquire_rwlock_exclusive(void *rwlock) {
|
||||
pushlock_acquire_exclusive(rwlock);
|
||||
}
|
||||
|
||||
void u80211_kernel_acquire_rwlock_shared(void *rwlock) {
|
||||
pushlock_acquire_shared(rwlock);
|
||||
}
|
||||
|
||||
void u80211_kernel_release_rwlock_exclusive(void *rwlock) {
|
||||
pushlock_release_exclusive(rwlock);
|
||||
}
|
||||
|
||||
void u80211_kernel_release_rwlock_shared(void *rwlock) {
|
||||
pushlock_release_shared(rwlock);
|
||||
}
|
||||
|
||||
typedef struct u80211_work_t {
|
||||
struct u80211_work_t *next;
|
||||
timespec_t deadline;
|
||||
u80211_kernel_work_fn_t function;
|
||||
void *context;
|
||||
eventheader_t finished_event;
|
||||
bool pending;
|
||||
bool running;
|
||||
bool freeing;
|
||||
bool destroy_on_return;
|
||||
} u80211_work_t;
|
||||
|
||||
static spinlock_t work_queue_lock;
|
||||
static eventheader_t work_queue_event;
|
||||
static u80211_work_t *work_queue;
|
||||
static u80211_work_t *current_work;
|
||||
static thread_t *worker_thread;
|
||||
|
||||
static long acquire_work_queue_lock(void) {
|
||||
return spinlock_acquire_raise_ipl(&work_queue_lock, IPL_NET);
|
||||
}
|
||||
|
||||
static void release_work_queue_lock(long old_ipl) {
|
||||
spinlock_release_lower_ipl(&work_queue_lock, old_ipl);
|
||||
}
|
||||
|
||||
static timespec_t deadline_after_ms(size_t ms) {
|
||||
timespec_t delay = {
|
||||
.s = ms / 1000,
|
||||
.ns = (ms % 1000) * 1000000,
|
||||
};
|
||||
|
||||
return timespec_add(timekeeper_timefromboot(), delay);
|
||||
}
|
||||
|
||||
static void insert_work(u80211_work_t *work) {
|
||||
// insert in the ordered list
|
||||
u80211_work_t **entry = &work_queue;
|
||||
while (*entry && !timespec_bigger((*entry)->deadline, work->deadline))
|
||||
entry = &(*entry)->next;
|
||||
|
||||
work->next = *entry;
|
||||
*entry = work;
|
||||
work->pending = true;
|
||||
}
|
||||
|
||||
static bool remove_work(u80211_work_t *work) {
|
||||
// find previous in the ordered list
|
||||
u80211_work_t **entry = &work_queue;
|
||||
while (*entry && *entry != work)
|
||||
entry = &(*entry)->next;
|
||||
|
||||
if (*entry == NULL)
|
||||
return false;
|
||||
|
||||
*entry = work->next;
|
||||
work->next = NULL;
|
||||
work->pending = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static time_t deadline_wait_us(timespec_t deadline, timespec_t now) {
|
||||
return (deadline.s - now.s) * 1000000 + (deadline.ns - now.ns) / 1000;
|
||||
}
|
||||
|
||||
static void wake_work_thread(void) {
|
||||
EVENT_SIGNAL(&work_queue_event);
|
||||
}
|
||||
|
||||
static void work_thread(void) {
|
||||
for (;;) {
|
||||
eventlistener_t listener;
|
||||
EVENT_INITLISTENER(&listener);
|
||||
EVENT_ATTACH(&listener, &work_queue_event);
|
||||
|
||||
long old_ipl = acquire_work_queue_lock();
|
||||
u80211_work_t *work = work_queue;
|
||||
if (work == NULL) {
|
||||
release_work_queue_lock(old_ipl);
|
||||
EVENT_WAIT(&listener, 0);
|
||||
EVENT_DETACHALL(&listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
timespec_t now = timekeeper_timefromboot();
|
||||
if (timespec_bigger(work->deadline, now)) {
|
||||
time_t wait_us = deadline_wait_us(work->deadline, now);
|
||||
release_work_queue_lock(old_ipl);
|
||||
EVENT_WAIT(&listener, wait_us);
|
||||
EVENT_DETACHALL(&listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
work_queue = work->next;
|
||||
|
||||
work->next = NULL;
|
||||
work->pending = false;
|
||||
work->running = true;
|
||||
|
||||
current_work = work;
|
||||
|
||||
u80211_kernel_work_fn_t function = work->function;
|
||||
void *context = work->context;
|
||||
|
||||
release_work_queue_lock(old_ipl);
|
||||
EVENT_DETACHALL(&listener);
|
||||
|
||||
function(context);
|
||||
|
||||
old_ipl = acquire_work_queue_lock();
|
||||
current_work = NULL;
|
||||
work->running = false;
|
||||
bool destroy = work->freeing && work->destroy_on_return;
|
||||
bool notify = work->freeing && !work->destroy_on_return;
|
||||
release_work_queue_lock(old_ipl);
|
||||
|
||||
if (notify)
|
||||
EVENT_SIGNAL(&work->finished_event);
|
||||
else if (destroy)
|
||||
slab_free(work_cache, work);
|
||||
}
|
||||
}
|
||||
|
||||
void *u80211_kernel_allocate_work(void) {
|
||||
u80211_work_t *work = slab_allocate(work_cache);
|
||||
if (work) {
|
||||
memset(work, 0, sizeof(*work));
|
||||
EVENT_INITHEADER(&work->finished_event);
|
||||
}
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
void u80211_kernel_enqueue_work(void *opaque_work, u80211_kernel_work_fn_t function, void *context, size_t ms) {
|
||||
u80211_work_t *work = opaque_work;
|
||||
timespec_t deadline = deadline_after_ms(ms);
|
||||
|
||||
long old_ipl = acquire_work_queue_lock();
|
||||
if (!work->pending && !work->freeing) {
|
||||
work->deadline = deadline;
|
||||
work->function = function;
|
||||
work->context = context;
|
||||
|
||||
insert_work(work);
|
||||
release_work_queue_lock(old_ipl);
|
||||
|
||||
wake_work_thread();
|
||||
return;
|
||||
}
|
||||
|
||||
release_work_queue_lock(old_ipl);
|
||||
}
|
||||
|
||||
void u80211_kernel_free_work(void *opaque_work) {
|
||||
u80211_work_t *work = opaque_work;
|
||||
bool removed;
|
||||
eventlistener_t listener;
|
||||
EVENT_INITLISTENER(&listener);
|
||||
EVENT_ATTACH(&listener, &work->finished_event);
|
||||
|
||||
long old_ipl = acquire_work_queue_lock();
|
||||
removed = remove_work(work);
|
||||
if (!work->running) {
|
||||
release_work_queue_lock(old_ipl);
|
||||
EVENT_DETACHALL(&listener);
|
||||
|
||||
if (removed)
|
||||
wake_work_thread();
|
||||
|
||||
slab_free(work_cache, work);
|
||||
return;
|
||||
}
|
||||
|
||||
work->freeing = true;
|
||||
if (current_thread() == worker_thread && current_work == work) {
|
||||
work->destroy_on_return = true;
|
||||
release_work_queue_lock(old_ipl);
|
||||
EVENT_DETACHALL(&listener);
|
||||
|
||||
if (removed)
|
||||
wake_work_thread();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
release_work_queue_lock(old_ipl);
|
||||
|
||||
if (removed)
|
||||
wake_work_thread();
|
||||
|
||||
EVENT_WAIT(&listener, 0);
|
||||
EVENT_DETACHALL(&listener);
|
||||
slab_free(work_cache, work);
|
||||
}
|
||||
|
||||
static void u80211_kernel_interface_init(void) {
|
||||
pushlock_cache = slab_newcache(sizeof(pushlock_t), 0, NULL, NULL);
|
||||
semaphore_cache = slab_newcache(sizeof(semaphore_t), 0, NULL, NULL);
|
||||
spinlock_cache = slab_newcache(sizeof(u80211_spinlock_t), 0, NULL, NULL);
|
||||
work_cache = slab_newcache(sizeof(u80211_work_t), 0, NULL, NULL);
|
||||
__assert(pushlock_cache && semaphore_cache && spinlock_cache && work_cache);
|
||||
|
||||
SPINLOCK_INIT(work_queue_lock);
|
||||
EVENT_INITHEADER(&work_queue_event);
|
||||
|
||||
worker_thread = sched_newthread(work_thread, PAGE_SIZE * 4, 0, NULL, NULL);
|
||||
__assert(worker_thread);
|
||||
sched_queue(worker_thread);
|
||||
}
|
||||
|
||||
INIT_ROUTINE_DEFINE(u80211, INIT_ROUTINE_FLAGS_NONE, u80211_kernel_interface_init, scheduler);
|
||||
|
||||
void u80211_kernel_receive_callback(u80211_device_t *device, void *buffer, size_t size) {
|
||||
(void)size;
|
||||
eth_process(device->driver_data, buffer);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <u80211/ap.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/string.h>
|
||||
|
||||
u80211_ap_t *u80211_ap_allocate(const u80211_beacon_data_t *beacon_data) {
|
||||
u80211_ap_t *ap = u80211_kernel_allocate(sizeof(*ap));
|
||||
if (ap == NULL)
|
||||
return NULL;
|
||||
|
||||
ap->rsn_size = beacon_data->rsn_size;
|
||||
if (ap->rsn_size) {
|
||||
ap->rsn = u80211_kernel_allocate(ap->rsn_size);
|
||||
if (ap->rsn == NULL) {
|
||||
u80211_kernel_free(ap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u80211_memcpy(ap->rsn, beacon_data->rsn, ap->rsn_size);
|
||||
} else {
|
||||
ap->rsn = NULL;
|
||||
}
|
||||
|
||||
__atomic_store_n(&ap->refcount, 1, __ATOMIC_RELAXED);
|
||||
ap->mac_address = beacon_data->mac_address;
|
||||
ap->interval = beacon_data->interval;
|
||||
ap->capabilities = beacon_data->capabilities;
|
||||
ap->channel = beacon_data->channel;
|
||||
u80211_memcpy(ap->rate_bitmap, beacon_data->rate_bitmap, sizeof(ap->rate_bitmap));
|
||||
u80211_memcpy(ap->ssid, beacon_data->ssid, sizeof(ap->ssid));
|
||||
return ap;
|
||||
}
|
||||
|
||||
void u80211_ap_inactive(u80211_ap_t *ap) {
|
||||
__atomic_thread_fence(__ATOMIC_ACQ_REL);
|
||||
|
||||
if (ap->rsn != NULL)
|
||||
u80211_kernel_free(ap->rsn);
|
||||
|
||||
u80211_kernel_free(ap);
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
#include <u80211/association.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
#define AUTH_TIMEOUT 5000
|
||||
#define ASSOC_TIMEOUT 5000
|
||||
#define DEAUTHENTICATION_REASON_LEAVING 3
|
||||
// if more space is needed, this can be changed
|
||||
#define ASSOCIATION_INFORMATION_ELEMENTS_MAX_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
void *completion_work;
|
||||
void *auth_timeout_work;
|
||||
void *assoc_timeout_work;
|
||||
unsigned int generation;
|
||||
unsigned int timeouts_in_flight;
|
||||
u80211_device_t *device;
|
||||
u80211_ap_t *ap; // this keeps a reference, the reference in the device is only incremented only when fully associated
|
||||
void *information_elements;
|
||||
size_t information_elements_size;
|
||||
} u80211_association_context_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_list_node_t node;
|
||||
void *semaphore;
|
||||
int result;
|
||||
} u80211_association_waiter_t;
|
||||
|
||||
static void wake_association_waiters(u80211_device_t *device, int result) {
|
||||
u80211_list_node_t *node;
|
||||
while ((node = u80211_list_pop_front(&device->association_waiters)) != NULL) {
|
||||
u80211_association_waiter_t *waiter = container_of(node, u80211_association_waiter_t, node);
|
||||
waiter->result = result;
|
||||
u80211_kernel_signal_semaphore(waiter->semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_association_context(u80211_association_context_t *association_context) {
|
||||
u80211_kernel_free_work(association_context->completion_work);
|
||||
u80211_kernel_free_work(association_context->auth_timeout_work);
|
||||
u80211_kernel_free_work(association_context->assoc_timeout_work);
|
||||
if (association_context->information_elements != NULL)
|
||||
u80211_kernel_free(association_context->information_elements);
|
||||
u80211_kernel_free(association_context);
|
||||
}
|
||||
|
||||
bool u80211_association_is_duplicate(u80211_device_t *device, const u80211_header_description_t *header, int expected_state) {
|
||||
bool duplicate = false;
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
bool active_stream = u80211_get_device_state(device) == expected_state && device->ap != NULL &&
|
||||
u80211_mac_address_equal(&device->ap->mac_address, &header->addresses[1]);
|
||||
if (active_stream) {
|
||||
duplicate = (header->frame_control & U80211_HEADER_FRAME_CONTROL_RETRY) &&
|
||||
device->received_sequence_control_valid && device->received_sequence_control == header->sequence_control;
|
||||
if (!duplicate) {
|
||||
device->received_sequence_control = header->sequence_control;
|
||||
device->received_sequence_control_valid = true;
|
||||
}
|
||||
}
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
static void release_disconnected_ap(void *ctx) {
|
||||
u80211_device_t *device = ctx;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
u80211_ap_t *ap = device->disconnected_ap;
|
||||
device->disconnected_ap = NULL;
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
if (ap != NULL)
|
||||
u80211_ap_release(ap);
|
||||
}
|
||||
|
||||
static void association_process_teardown(u80211_device_t *device, const u80211_mac_address_t *address, bool deauthentication) {
|
||||
bool cleanup_needed = false;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
|
||||
if (!device->ap || !u80211_mac_address_equal(&device->ap->mac_address, address))
|
||||
goto leave;
|
||||
|
||||
int state = u80211_get_device_state(device);
|
||||
bool attempt_in_progress = state == U80211_DEVICE_STATE_AUTHENTICATING || state == U80211_DEVICE_STATE_ASSOCIATING;
|
||||
if (state != U80211_DEVICE_STATE_ASSOCIATED && (!deauthentication || !attempt_in_progress))
|
||||
goto leave;
|
||||
|
||||
if (!u80211_set_device_state(device, state, U80211_DEVICE_STATE_DOWN))
|
||||
goto leave;
|
||||
|
||||
if (attempt_in_progress) {
|
||||
device->association_context = NULL;
|
||||
device->association_result = U80211_STATUS_REJECTED;
|
||||
wake_association_waiters(device, device->association_result);
|
||||
} else {
|
||||
device->disconnected_ap = device->ap;
|
||||
cleanup_needed = true;
|
||||
}
|
||||
|
||||
device->ap = NULL;
|
||||
++device->association_generation;
|
||||
|
||||
leave:
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
if (cleanup_needed)
|
||||
u80211_kernel_enqueue_work(device->association_cleanup_work, release_disconnected_ap, device, 0);
|
||||
}
|
||||
|
||||
void u80211_association_process_deauthentication(u80211_device_t *device, u80211_deauthentication_data_t *deauthentication_data) {
|
||||
association_process_teardown(device, &deauthentication_data->address, true);
|
||||
}
|
||||
|
||||
void u80211_association_process_disassociation(u80211_device_t *device, u80211_disassociation_data_t *disassociation_data) {
|
||||
association_process_teardown(device, &disassociation_data->address, false);
|
||||
}
|
||||
|
||||
void u80211_association_process_response(u80211_device_t *device, u80211_association_response_data_t *association_data) {
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
|
||||
u80211_association_context_t *association_context = device->association_context;
|
||||
if (!association_context || !device->ap)
|
||||
goto leave;
|
||||
|
||||
if (!u80211_mac_address_equal(&association_context->ap->mac_address, &association_data->address))
|
||||
goto leave;
|
||||
|
||||
int new_state = association_data->status ? U80211_DEVICE_STATE_DOWN : U80211_DEVICE_STATE_ASSOCIATED;
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_ASSOCIATING, new_state))
|
||||
goto leave;
|
||||
|
||||
if (association_data->status)
|
||||
device->ap = NULL;
|
||||
else
|
||||
u80211_ap_hold(device->ap);
|
||||
|
||||
device->association_context = NULL;
|
||||
++device->association_generation;
|
||||
device->association_result = association_data->status ? U80211_STATUS_REJECTED : U80211_STATUS_SUCCESS;
|
||||
wake_association_waiters(device, device->association_result);
|
||||
|
||||
leave:
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
}
|
||||
|
||||
static void association_timeout(void *ctx, int expected_state) {
|
||||
u80211_association_context_t *association_context = ctx;
|
||||
u80211_device_t *device = association_context->device;
|
||||
bool free = true;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
|
||||
// not associating/different association generation
|
||||
if (!device->ap || device->association_generation != association_context->generation)
|
||||
goto leave;
|
||||
|
||||
if (u80211_set_device_state(device, expected_state, U80211_DEVICE_STATE_DOWN)) {
|
||||
device->ap = NULL;
|
||||
device->association_context = NULL;
|
||||
++device->association_generation;
|
||||
device->association_result = U80211_STATUS_TIMED_OUT;
|
||||
wake_association_waiters(device, device->association_result);
|
||||
} else {
|
||||
// already progressed
|
||||
free = false;
|
||||
}
|
||||
|
||||
leave:
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
if (__atomic_sub_fetch(&association_context->timeouts_in_flight, 1, __ATOMIC_ACQ_REL) == 0 && free) {
|
||||
u80211_ap_release(association_context->ap);
|
||||
destroy_association_context(association_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void assoc_timeout(void *ctx) {
|
||||
association_timeout(ctx, U80211_DEVICE_STATE_ASSOCIATING);
|
||||
}
|
||||
|
||||
static void auth_completion_work(void *ctx) {
|
||||
u80211_association_context_t *association_context = ctx;
|
||||
u80211_device_t *device = association_context->device;
|
||||
|
||||
u80211_send_association_request(device, association_context->information_elements, association_context->information_elements_size);
|
||||
__atomic_add_fetch(&association_context->timeouts_in_flight, 1, __ATOMIC_RELAXED);
|
||||
u80211_kernel_enqueue_work(association_context->assoc_timeout_work, assoc_timeout, association_context, ASSOC_TIMEOUT);
|
||||
}
|
||||
|
||||
void u80211_association_process_authentication(u80211_device_t *device, u80211_auth_data_t *auth_data) {
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
|
||||
// not associating/different AP
|
||||
if (!device->ap || !u80211_mac_address_equal(&device->ap->mac_address, &auth_data->address))
|
||||
goto leave;
|
||||
|
||||
// not the same algo/different transaction stage than expected
|
||||
if (auth_data->auth_algorithm != U80211_AUTH_ALGORITHM_OPEN || auth_data->auth_transaction != 2)
|
||||
goto leave;
|
||||
|
||||
if (auth_data->status) {
|
||||
// failure
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_AUTHENTICATING, U80211_DEVICE_STATE_DOWN))
|
||||
goto leave;
|
||||
|
||||
device->ap = NULL;
|
||||
device->association_context = NULL;
|
||||
++device->association_generation;
|
||||
device->association_result = U80211_STATUS_REJECTED;
|
||||
wake_association_waiters(device, device->association_result);
|
||||
} else {
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_AUTHENTICATING, U80211_DEVICE_STATE_ASSOCIATING))
|
||||
goto leave;
|
||||
|
||||
u80211_association_context_t *ctx = device->association_context;
|
||||
u80211_kernel_enqueue_work(ctx->completion_work, auth_completion_work, ctx, 0);
|
||||
}
|
||||
|
||||
leave:
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
}
|
||||
|
||||
static void auth_timeout(void *ctx) {
|
||||
association_timeout(ctx, U80211_DEVICE_STATE_AUTHENTICATING);
|
||||
}
|
||||
|
||||
int u80211_associate(u80211_device_t *device, u80211_ap_t *ap, const void *information_elements, size_t information_elements_size) {
|
||||
if (information_elements_size > ASSOCIATION_INFORMATION_ELEMENTS_MAX_SIZE)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
release_disconnected_ap(device);
|
||||
|
||||
u80211_association_context_t *association_context = u80211_kernel_allocate(sizeof(u80211_association_context_t));
|
||||
if (association_context == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
association_context->completion_work = u80211_kernel_allocate_work();
|
||||
if (association_context->completion_work == NULL) {
|
||||
u80211_kernel_free(association_context);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
|
||||
association_context->auth_timeout_work = u80211_kernel_allocate_work();
|
||||
if (association_context->auth_timeout_work == NULL) {
|
||||
u80211_kernel_free_work(association_context->completion_work);
|
||||
u80211_kernel_free(association_context);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
|
||||
association_context->assoc_timeout_work = u80211_kernel_allocate_work();
|
||||
if (association_context->assoc_timeout_work == NULL) {
|
||||
u80211_kernel_free_work(association_context->auth_timeout_work);
|
||||
u80211_kernel_free_work(association_context->completion_work);
|
||||
u80211_kernel_free(association_context);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
|
||||
association_context->information_elements = NULL;
|
||||
association_context->information_elements_size = information_elements_size;
|
||||
if (information_elements_size != 0) {
|
||||
association_context->information_elements = u80211_kernel_allocate(information_elements_size);
|
||||
if (association_context->information_elements == NULL) {
|
||||
destroy_association_context(association_context);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
|
||||
u80211_memcpy(association_context->information_elements, information_elements, information_elements_size);
|
||||
}
|
||||
|
||||
association_context->device = device;
|
||||
association_context->ap = ap;
|
||||
__atomic_store_n(&association_context->timeouts_in_flight, 1, __ATOMIC_RELAXED);
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_DOWN, U80211_DEVICE_STATE_AUTHENTICATING)) {
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
destroy_association_context(association_context);
|
||||
return U80211_STATUS_BUSY;
|
||||
}
|
||||
|
||||
association_context->generation = device->association_generation;
|
||||
device->association_context = association_context;
|
||||
device->association_result = U80211_STATUS_UNKNOWN_ERROR;
|
||||
device->ap = ap;
|
||||
device->received_sequence_control = 0;
|
||||
device->received_sequence_control_valid = false;
|
||||
u80211_ap_hold(ap);
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
u80211_auth_data_t auth_data = {
|
||||
.address = ap->mac_address,
|
||||
.auth_algorithm = U80211_AUTH_ALGORITHM_OPEN,
|
||||
.auth_transaction = 1,
|
||||
.status = 0,
|
||||
};
|
||||
device->ops->set_channel(device, ap->channel);
|
||||
u80211_send_authentication(device, &auth_data);
|
||||
|
||||
u80211_kernel_enqueue_work(association_context->auth_timeout_work, auth_timeout, association_context, AUTH_TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u80211_deauthenticate(u80211_device_t *device, uint16_t reason) {
|
||||
u80211_deauthentication_data_t deauthentication_data = {
|
||||
.reason = reason,
|
||||
};
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
|
||||
if (device->ap == NULL || u80211_get_device_state(device) != U80211_DEVICE_STATE_ASSOCIATED) {
|
||||
// TODO: Support cancelling an authentication or association attempt.
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
return U80211_STATUS_NOT_ASSOCIATED;
|
||||
}
|
||||
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_ASSOCIATED, U80211_DEVICE_STATE_DEAUTHENTICATING)) {
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
return U80211_STATUS_NOT_ASSOCIATED;
|
||||
}
|
||||
|
||||
deauthentication_data.address = device->ap->mac_address;
|
||||
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
int status = u80211_send_deauthentication(device, &deauthentication_data);
|
||||
u80211_ap_t *disconnected_ap = NULL;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
if (device->ap != NULL && u80211_get_device_state(device) == U80211_DEVICE_STATE_DEAUTHENTICATING) {
|
||||
disconnected_ap = device->ap;
|
||||
device->ap = NULL;
|
||||
++device->association_generation;
|
||||
u80211_set_device_state(device, U80211_DEVICE_STATE_DEAUTHENTICATING, U80211_DEVICE_STATE_DOWN);
|
||||
}
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
if (disconnected_ap != NULL)
|
||||
u80211_ap_release(disconnected_ap);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int u80211_disassociate(u80211_device_t *device) {
|
||||
return u80211_deauthenticate(device, DEAUTHENTICATION_REASON_LEAVING);
|
||||
}
|
||||
|
||||
int u80211_wait_for_association_completion(u80211_device_t *device) {
|
||||
void *semaphore = u80211_kernel_allocate_semaphore(0);
|
||||
if (semaphore == NULL)
|
||||
return U80211_STATUS_RETRY;
|
||||
|
||||
u80211_association_waiter_t waiter = {
|
||||
.semaphore = semaphore,
|
||||
};
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
int state = u80211_get_device_state(device);
|
||||
if (state != U80211_DEVICE_STATE_AUTHENTICATING && state != U80211_DEVICE_STATE_ASSOCIATING) {
|
||||
int result = device->association_result;
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
u80211_kernel_free_semaphore(semaphore);
|
||||
return result;
|
||||
}
|
||||
|
||||
u80211_list_push_back(&device->association_waiters, &waiter.node);
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
u80211_kernel_wait_semaphore(semaphore);
|
||||
u80211_kernel_free_semaphore(semaphore);
|
||||
return waiter.result;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <u80211/bss_cache.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/rbtree.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
static int mac_compare(u80211_mac_address_t *a, u80211_mac_address_t *b) {
|
||||
for (size_t i = 0; i < sizeof(a->bytes); i++) {
|
||||
if (a->bytes[i] < b->bytes[i])
|
||||
return -1;
|
||||
if (a->bytes[i] > b->bytes[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rbtree_mac_compare(u80211_rbtree_t *a, u80211_rbtree_t *b) {
|
||||
u80211_ap_t *ap_a = container_of(a, u80211_ap_t, cache_node);
|
||||
u80211_ap_t *ap_b = container_of(b, u80211_ap_t, cache_node);
|
||||
|
||||
return mac_compare(&ap_a->mac_address, &ap_b->mac_address);
|
||||
}
|
||||
|
||||
static int rbtree_value_compare(void *a, u80211_rbtree_t *b) {
|
||||
u80211_ap_t *ap_b = container_of(b, u80211_ap_t, cache_node);
|
||||
|
||||
return mac_compare(a, &ap_b->mac_address);
|
||||
}
|
||||
|
||||
int u80211_bss_cache_init(bss_cache_t *cache) {
|
||||
cache->root = NULL;
|
||||
cache->entry_count = 0;
|
||||
cache->rwlock = u80211_kernel_allocate_rwlock();
|
||||
if (unlikely(cache->rwlock == NULL))
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void u80211_bss_cache_deinit(bss_cache_t *cache) {
|
||||
u80211_kernel_acquire_rwlock_exclusive(cache->rwlock);
|
||||
|
||||
u80211_rbtree_t *iterator = cache->root == NULL ? NULL : u80211_rbtree_first(cache->root);
|
||||
while (iterator != NULL) {
|
||||
u80211_rbtree_t *next = u80211_rbtree_successor(iterator);
|
||||
u80211_ap_t *ap = container_of(iterator, u80211_ap_t, cache_node);
|
||||
|
||||
u80211_rbtree_remove(&cache->root, iterator);
|
||||
--cache->entry_count;
|
||||
u80211_ap_release(ap);
|
||||
iterator = next;
|
||||
}
|
||||
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
u80211_kernel_free_rwlock(cache->rwlock);
|
||||
cache->rwlock = NULL;
|
||||
cache->root = NULL;
|
||||
}
|
||||
|
||||
void u80211_bss_cache_purge(bss_cache_t *cache) {
|
||||
u80211_kernel_acquire_rwlock_exclusive(cache->rwlock);
|
||||
|
||||
u80211_rbtree_t *iterator = cache->root == NULL ? NULL : u80211_rbtree_first(cache->root);
|
||||
while (iterator != NULL) {
|
||||
u80211_rbtree_t *next = u80211_rbtree_successor(iterator);
|
||||
|
||||
u80211_ap_t *ap = container_of(iterator, u80211_ap_t, cache_node);
|
||||
if (__atomic_load_n(&ap->refcount, __ATOMIC_RELAXED) == 1) {
|
||||
u80211_rbtree_remove(&cache->root, iterator);
|
||||
--cache->entry_count;
|
||||
u80211_ap_release(ap);
|
||||
}
|
||||
|
||||
iterator = next;
|
||||
}
|
||||
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
}
|
||||
|
||||
void u80211_bss_cache_insert(bss_cache_t *cache, u80211_ap_t *ap) {
|
||||
u80211_kernel_acquire_rwlock_exclusive(cache->rwlock);
|
||||
|
||||
u80211_rbtree_t *node = u80211_rbtree_lookup(cache->root, &ap->mac_address, rbtree_value_compare);
|
||||
if (node != NULL) {
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
return;
|
||||
}
|
||||
|
||||
u80211_rbtree_insert(&cache->root, &ap->cache_node, rbtree_mac_compare);
|
||||
u80211_ap_hold(ap);
|
||||
++cache->entry_count;
|
||||
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
}
|
||||
|
||||
void u80211_bss_cache_remove(bss_cache_t *cache, u80211_mac_address_t *mac) {
|
||||
u80211_kernel_acquire_rwlock_exclusive(cache->rwlock);
|
||||
|
||||
u80211_rbtree_t *node = u80211_rbtree_lookup(cache->root, mac, rbtree_value_compare);
|
||||
if (node == NULL) {
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
return;
|
||||
}
|
||||
|
||||
u80211_rbtree_remove(&cache->root, node);
|
||||
--cache->entry_count;
|
||||
|
||||
u80211_ap_t *ap = container_of(node, u80211_ap_t, cache_node);
|
||||
u80211_ap_release(ap);
|
||||
|
||||
u80211_kernel_release_rwlock_exclusive(cache->rwlock);
|
||||
}
|
||||
|
||||
u80211_ap_t *u80211_bss_cache_find(bss_cache_t *cache, u80211_mac_address_t *mac) {
|
||||
u80211_kernel_acquire_rwlock_shared(cache->rwlock);
|
||||
|
||||
u80211_rbtree_t *node = u80211_rbtree_lookup(cache->root, mac, rbtree_value_compare);
|
||||
if (node == NULL) {
|
||||
u80211_kernel_release_rwlock_shared(cache->rwlock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u80211_ap_t *ap = container_of(node, u80211_ap_t, cache_node);
|
||||
u80211_ap_hold(ap);
|
||||
|
||||
u80211_kernel_release_rwlock_shared(cache->rwlock);
|
||||
|
||||
return ap;
|
||||
}
|
||||
|
||||
size_t u80211_bss_cache_get_aps(bss_cache_t *cache, u80211_ap_t **buffer, size_t capacity) {
|
||||
u80211_kernel_acquire_rwlock_shared(cache->rwlock);
|
||||
|
||||
size_t count = 0;
|
||||
u80211_rbtree_t *iterator = cache->root == NULL ? NULL : u80211_rbtree_first(cache->root);
|
||||
while (iterator != NULL && count < capacity) {
|
||||
u80211_ap_t *ap = container_of(iterator, u80211_ap_t, cache_node);
|
||||
u80211_ap_hold(ap);
|
||||
buffer[count++] = ap;
|
||||
iterator = u80211_rbtree_successor(iterator);
|
||||
}
|
||||
|
||||
u80211_kernel_release_rwlock_shared(cache->rwlock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t u80211_bss_cache_get_count(bss_cache_t *cache) {
|
||||
u80211_kernel_acquire_rwlock_shared(cache->rwlock);
|
||||
size_t count = cache->entry_count;
|
||||
u80211_kernel_release_rwlock_shared(cache->rwlock);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/u80211.h>
|
||||
|
||||
int u80211_key_state_init(u80211_device_t *device);
|
||||
void u80211_key_state_deinit(u80211_device_t *device);
|
||||
|
||||
int u80211_register_device(const u80211_device_metadata_t *metadata, const u80211_device_ops_t *ops, void *driver_data, u80211_device_t **device_out) {
|
||||
u80211_device_t *device = u80211_kernel_allocate(sizeof(*device));
|
||||
if (device == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
device->metadata = *metadata;
|
||||
device->packet_count = 0;
|
||||
device->tx_sequence_control = 0;
|
||||
device->received_sequence_control = 0;
|
||||
device->received_sequence_control_valid = false;
|
||||
device->state = U80211_DEVICE_STATE_DOWN;
|
||||
int status = u80211_key_state_init(device);
|
||||
if (status != U80211_STATUS_SUCCESS) {
|
||||
u80211_kernel_free(device);
|
||||
return status;
|
||||
}
|
||||
|
||||
device->scan_spinlock = u80211_kernel_allocate_spinlock();
|
||||
if (device->scan_spinlock == NULL) {
|
||||
u80211_key_state_deinit(device);
|
||||
u80211_kernel_free(device);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
device->scan_context = NULL;
|
||||
u80211_list_init(&device->scan_waiters);
|
||||
|
||||
device->association_spinlock = u80211_kernel_allocate_spinlock();
|
||||
if (device->association_spinlock == NULL) {
|
||||
u80211_kernel_free_spinlock(device->scan_spinlock);
|
||||
u80211_key_state_deinit(device);
|
||||
u80211_kernel_free(device);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
device->association_cleanup_work = u80211_kernel_allocate_work();
|
||||
if (device->association_cleanup_work == NULL) {
|
||||
u80211_kernel_free_spinlock(device->association_spinlock);
|
||||
u80211_kernel_free_spinlock(device->scan_spinlock);
|
||||
u80211_key_state_deinit(device);
|
||||
u80211_kernel_free(device);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
device->association_context = NULL;
|
||||
u80211_list_init(&device->association_waiters);
|
||||
device->association_generation = 0;
|
||||
device->association_result = U80211_STATUS_UNKNOWN_ERROR;
|
||||
device->ap = NULL;
|
||||
device->disconnected_ap = NULL;
|
||||
|
||||
device->ops = ops;
|
||||
device->driver_data = driver_data;
|
||||
|
||||
status = u80211_bss_cache_init(&device->bss_cache);
|
||||
if (status != U80211_STATUS_SUCCESS) {
|
||||
u80211_kernel_free_work(device->association_cleanup_work);
|
||||
u80211_kernel_free_spinlock(device->association_spinlock);
|
||||
u80211_kernel_free_spinlock(device->scan_spinlock);
|
||||
u80211_key_state_deinit(device);
|
||||
u80211_kernel_free(device);
|
||||
return status;
|
||||
}
|
||||
|
||||
*device_out = device;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void u80211_unregister_device(u80211_device_t *device) {
|
||||
u80211_key_state_deinit(device);
|
||||
u80211_kernel_free_work(device->association_cleanup_work);
|
||||
if (device->disconnected_ap != NULL)
|
||||
u80211_ap_release(device->disconnected_ap);
|
||||
u80211_bss_cache_deinit(&device->bss_cache);
|
||||
u80211_kernel_free_spinlock(device->association_spinlock);
|
||||
u80211_kernel_free_spinlock(device->scan_spinlock);
|
||||
u80211_kernel_free(device);
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/michael.h>
|
||||
#include <u80211/u80211.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
#define SEQUENCE_SIZE 6
|
||||
#define TKIP_KEY_SIZE 32
|
||||
#define TKIP_TX_MIC_KEY_OFFSET 16
|
||||
#define TKIP_RX_MIC_KEY_OFFSET 24
|
||||
#define MIC_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
u80211_list_node_t node;
|
||||
u80211_cipher_t cipher;
|
||||
uint8_t index;
|
||||
u80211_mac_address_t peer;
|
||||
uint32_t flags;
|
||||
uint8_t rx_sequence[SEQUENCE_SIZE];
|
||||
uint8_t tx_sequence[SEQUENCE_SIZE];
|
||||
size_t key_len;
|
||||
uint8_t key[];
|
||||
} u80211_key_metadata_t;
|
||||
|
||||
static void destroy_key_metadata(u80211_key_metadata_t *metadata) {
|
||||
// TODO: proper memset for zeroing crypto data
|
||||
u80211_memset(metadata, 0, sizeof(*metadata) + metadata->key_len);
|
||||
u80211_kernel_free(metadata);
|
||||
}
|
||||
|
||||
static bool key_identity_equal(const u80211_key_metadata_t *metadata, uint8_t index, const u80211_mac_address_t *peer, uint32_t flags) {
|
||||
return metadata->index == index && metadata->flags == flags && u80211_mac_address_equal(&metadata->peer, peer);
|
||||
}
|
||||
|
||||
int u80211_key_state_init(u80211_device_t *device) {
|
||||
u80211_list_init(&device->keys);
|
||||
device->key_spinlock = u80211_kernel_allocate_spinlock();
|
||||
if (device->key_spinlock == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void u80211_key_state_deinit(u80211_device_t *device) {
|
||||
u80211_list_node_t *node;
|
||||
while ((node = u80211_list_pop_front(&device->keys)) != NULL)
|
||||
destroy_key_metadata(container_of(node, u80211_key_metadata_t, node));
|
||||
|
||||
u80211_kernel_free_spinlock(device->key_spinlock);
|
||||
device->key_spinlock = NULL;
|
||||
}
|
||||
|
||||
int u80211_set_key(u80211_device_t *device, const u80211_key_t *key) {
|
||||
if (device->ops->set_key == NULL)
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
|
||||
if ((key->key == NULL && key->key_len != 0) || key->key_len > SIZE_MAX - sizeof(u80211_key_metadata_t))
|
||||
return U80211_STATUS_NOT_PERMITTED;
|
||||
|
||||
u80211_key_metadata_t *metadata = u80211_kernel_allocate(sizeof(*metadata) + key->key_len);
|
||||
if (metadata == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
metadata->index = key->index;
|
||||
metadata->cipher = key->cipher;
|
||||
metadata->peer = key->peer;
|
||||
metadata->flags = key->flags;
|
||||
metadata->key_len = key->key_len;
|
||||
|
||||
u80211_memset(metadata->rx_sequence, 0, sizeof(metadata->rx_sequence));
|
||||
u80211_memset(metadata->tx_sequence, 0, sizeof(metadata->tx_sequence));
|
||||
|
||||
if (key->rx_seq != NULL && key->rx_seq_len == sizeof(metadata->rx_sequence))
|
||||
u80211_memcpy(metadata->rx_sequence, key->rx_seq, sizeof(metadata->rx_sequence));
|
||||
|
||||
if (key->key_len != 0)
|
||||
u80211_memcpy(metadata->key, key->key, key->key_len);
|
||||
|
||||
int status = device->ops->set_key(device, key);
|
||||
if (status != U80211_STATUS_SUCCESS) {
|
||||
destroy_key_metadata(metadata);
|
||||
return status;
|
||||
}
|
||||
|
||||
u80211_key_metadata_t *old_metadata = NULL;
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
u80211_list_for_each(&device->keys, node) {
|
||||
u80211_key_metadata_t *candidate = container_of(node, u80211_key_metadata_t, node);
|
||||
if (key_identity_equal(candidate, key->index, &key->peer, key->flags)) {
|
||||
old_metadata = candidate;
|
||||
u80211_list_remove(&device->keys, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
u80211_list_push_front(&device->keys, &metadata->node);
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
|
||||
if (old_metadata != NULL)
|
||||
destroy_key_metadata(old_metadata);
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_del_key(u80211_device_t *device, uint8_t index, const u80211_mac_address_t *peer, uint32_t flags) {
|
||||
if (device->ops->del_key == NULL)
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
|
||||
int status = device->ops->del_key(device, index, peer, flags);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_key_metadata_t *removed_metadata = NULL;
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
u80211_list_for_each(&device->keys, node) {
|
||||
u80211_key_metadata_t *candidate = container_of(node, u80211_key_metadata_t, node);
|
||||
if (key_identity_equal(candidate, index, peer, flags)) {
|
||||
removed_metadata = candidate;
|
||||
u80211_list_remove(&device->keys, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
|
||||
if (removed_metadata != NULL)
|
||||
destroy_key_metadata(removed_metadata);
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static bool key_matches_header(const u80211_key_metadata_t *metadata, u80211_device_t *device, const u80211_header_description_t *header) {
|
||||
bool tx = u80211_mac_address_equal(&header->addresses[1], &device->metadata.mac_address);
|
||||
bool group = header->addresses[0].bytes[0] & 1;
|
||||
uint32_t direction_flag = tx ? U80211_KEY_TX : U80211_KEY_RX;
|
||||
uint32_t type_flag = group ? U80211_KEY_GROUP : U80211_KEY_PAIRWISE;
|
||||
const u80211_mac_address_t *peer = tx ? &header->addresses[0] : &header->addresses[1];
|
||||
|
||||
return (metadata->flags & direction_flag) && (metadata->flags & type_flag) && (group || u80211_mac_address_equal(&metadata->peer, peer));
|
||||
}
|
||||
|
||||
// expect key spinlock to be locked
|
||||
static u80211_key_metadata_t *select_key(u80211_device_t *device, const u80211_header_description_t *header) {
|
||||
u80211_list_for_each(&device->keys, node) {
|
||||
u80211_key_metadata_t *metadata = container_of(node, u80211_key_metadata_t, node);
|
||||
if (key_matches_header(metadata, device, header))
|
||||
return metadata;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// expect key spinlock to be locked
|
||||
static u80211_key_metadata_t *select_key_by_index(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index) {
|
||||
u80211_list_for_each(&device->keys, node) {
|
||||
u80211_key_metadata_t *metadata = container_of(node, u80211_key_metadata_t, node);
|
||||
if (metadata->index == index && key_matches_header(metadata, device, header))
|
||||
return metadata;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int u80211_select_key(u80211_device_t *device, const u80211_header_description_t *header) {
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
u80211_key_metadata_t *metadata = select_key(device, header);
|
||||
int index = metadata == NULL ? -1 : metadata->index;
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int u80211_select_cipher(u80211_device_t *device, const u80211_header_description_t *header) {
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
|
||||
u80211_key_metadata_t *metadata = select_key(device, header);
|
||||
int cipher = -1;
|
||||
if (metadata)
|
||||
cipher = metadata->cipher;
|
||||
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
int u80211_select_cipher_by_index(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index) {
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
|
||||
u80211_key_metadata_t *metadata = select_key_by_index(device, header, index);
|
||||
int cipher = -1;
|
||||
if (metadata)
|
||||
cipher = metadata->cipher;
|
||||
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
static bool sequence_is_newer(const uint8_t *sequence, const uint8_t *previous, size_t size) {
|
||||
for (size_t i = size; i != 0; --i) {
|
||||
if (sequence[i - 1] != previous[i - 1])
|
||||
return sequence[i - 1] > previous[i - 1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mic_equal(const uint8_t a[MIC_SIZE], const uint8_t b[MIC_SIZE]) {
|
||||
uint8_t difference = 0;
|
||||
for (size_t i = 0; i < MIC_SIZE; ++i)
|
||||
difference |= a[i] ^ b[i];
|
||||
return difference == 0;
|
||||
}
|
||||
|
||||
bool u80211_key_validate_tkip_rx(u80211_device_t *device, const u80211_tkip_data_t *data) {
|
||||
bool valid = false;
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
|
||||
u80211_key_metadata_t *metadata = select_key_by_index(device, data->header, data->key_index);
|
||||
if (metadata == NULL || metadata->cipher != U80211_CIPHER_TKIP || metadata->key_len != TKIP_KEY_SIZE)
|
||||
goto unlock;
|
||||
if (!sequence_is_newer(data->sequence, metadata->rx_sequence, SEQUENCE_SIZE))
|
||||
goto unlock;
|
||||
|
||||
uint8_t calculated_mic[MIC_SIZE];
|
||||
u80211_michael_mic(metadata->key + TKIP_RX_MIC_KEY_OFFSET, data->destination->bytes, data->source->bytes,
|
||||
data->priority, data->data, data->data_size, calculated_mic);
|
||||
if (!mic_equal(calculated_mic, data->mic))
|
||||
goto unlock;
|
||||
|
||||
u80211_memcpy(metadata->rx_sequence, data->sequence, SEQUENCE_SIZE);
|
||||
valid = true;
|
||||
|
||||
unlock:
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool u80211_key_update_rx_sequence(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index, u80211_cipher_t cipher, const uint8_t *sequence, size_t sequence_size) {
|
||||
if (sequence_size != SEQUENCE_SIZE)
|
||||
return false;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
u80211_key_metadata_t *metadata = select_key_by_index(device, header, index);
|
||||
bool valid = metadata != NULL && metadata->cipher == cipher && sequence_is_newer(sequence, metadata->rx_sequence, sequence_size);
|
||||
if (valid)
|
||||
u80211_memcpy(metadata->rx_sequence, sequence, sequence_size);
|
||||
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return valid;
|
||||
}
|
||||
|
||||
static bool increment_sequence(uint8_t *sequence, size_t size) {
|
||||
// find where to increment
|
||||
size_t carry_end = 0;
|
||||
while (carry_end < size && sequence[carry_end] == UINT8_MAX)
|
||||
carry_end++;
|
||||
|
||||
// can't increase sequence
|
||||
if (carry_end == size)
|
||||
return false;
|
||||
|
||||
u80211_memset(sequence, 0, carry_end);
|
||||
sequence[carry_end]++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool u80211_key_prepare_tkip_tx(u80211_device_t *device, u80211_tkip_data_t *data) {
|
||||
bool valid = false;
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
|
||||
u80211_key_metadata_t *metadata = select_key_by_index(device, data->header, data->key_index);
|
||||
if (metadata == NULL || metadata->cipher != U80211_CIPHER_TKIP || metadata->key_len != TKIP_KEY_SIZE)
|
||||
goto unlock;
|
||||
|
||||
uint8_t sequence[SEQUENCE_SIZE];
|
||||
u80211_memcpy(sequence, metadata->tx_sequence, sizeof(sequence));
|
||||
if (!increment_sequence(sequence, sizeof(sequence)))
|
||||
goto unlock;
|
||||
|
||||
uint8_t mic[MIC_SIZE];
|
||||
u80211_michael_mic(metadata->key + TKIP_TX_MIC_KEY_OFFSET, data->destination->bytes, data->source->bytes,
|
||||
data->priority, data->data, data->data_size, mic);
|
||||
|
||||
u80211_memcpy(metadata->tx_sequence, sequence, sizeof(sequence));
|
||||
u80211_memcpy(data->sequence, sequence, sizeof(sequence));
|
||||
u80211_memcpy(data->mic, mic, sizeof(mic));
|
||||
valid = true;
|
||||
|
||||
unlock:
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool u80211_key_next_tx_sequence(u80211_device_t *device, const u80211_header_description_t *header, uint8_t index, u80211_cipher_t cipher, uint8_t *sequence, size_t sequence_size) {
|
||||
if (sequence_size != SEQUENCE_SIZE)
|
||||
return false;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->key_spinlock);
|
||||
u80211_key_metadata_t *metadata = select_key(device, header);
|
||||
bool valid = false;
|
||||
if (metadata == NULL || metadata->index != index || metadata->cipher != cipher)
|
||||
goto unlock;
|
||||
if (!increment_sequence(metadata->tx_sequence, sequence_size))
|
||||
goto unlock;
|
||||
|
||||
u80211_memcpy(sequence, metadata->tx_sequence, sequence_size);
|
||||
valid = true;
|
||||
|
||||
unlock:
|
||||
u80211_kernel_release_spinlock(device->key_spinlock);
|
||||
return valid;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#include <u80211/michael.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t left;
|
||||
uint32_t right;
|
||||
uint8_t pending[4];
|
||||
size_t pending_size;
|
||||
} michael_context_t;
|
||||
|
||||
static uint32_t rotate_left(uint32_t value, unsigned int amount) {
|
||||
return (value << amount) | (value >> (32 - amount));
|
||||
}
|
||||
|
||||
static uint32_t rotate_right(uint32_t value, unsigned int amount) {
|
||||
return (value >> amount) | (value << (32 - amount));
|
||||
}
|
||||
|
||||
static uint32_t exchange_bytes(uint32_t value) {
|
||||
return ((value & 0x00ff00ff) << 8) | ((value & 0xff00ff00) >> 8);
|
||||
}
|
||||
|
||||
static void michael_block(michael_context_t *context, uint32_t word) {
|
||||
context->left ^= word;
|
||||
context->right ^= rotate_left(context->left, 17);
|
||||
context->left += context->right;
|
||||
context->right ^= exchange_bytes(context->left);
|
||||
context->left += context->right;
|
||||
context->right ^= rotate_left(context->left, 3);
|
||||
context->left += context->right;
|
||||
context->right ^= rotate_right(context->left, 2);
|
||||
context->left += context->right;
|
||||
}
|
||||
|
||||
static void michael_update(michael_context_t *context, const void *data, size_t size) {
|
||||
const uint8_t *bytes = data;
|
||||
|
||||
if (context->pending_size != 0) {
|
||||
while (size != 0 && context->pending_size < sizeof(context->pending)) {
|
||||
context->pending[context->pending_size++] = *bytes++;
|
||||
size--;
|
||||
}
|
||||
|
||||
if (context->pending_size == sizeof(context->pending)) {
|
||||
michael_block(context, deserialize_le32(context->pending));
|
||||
context->pending_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (size >= sizeof(uint32_t)) {
|
||||
michael_block(context, deserialize_le32(bytes));
|
||||
bytes += sizeof(uint32_t);
|
||||
size -= sizeof(uint32_t);
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
u80211_memcpy(context->pending, bytes, size);
|
||||
context->pending_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
void u80211_michael_mic(const uint8_t key[8], const uint8_t da[6], const uint8_t sa[6], uint8_t priority, const void *data, size_t len, uint8_t out[8]) {
|
||||
uint8_t pseudo_header[16];
|
||||
u80211_memcpy(pseudo_header, da, 6);
|
||||
u80211_memcpy(pseudo_header + 6, sa, 6);
|
||||
pseudo_header[12] = priority;
|
||||
u80211_memset(pseudo_header + 13, 0, 3);
|
||||
|
||||
michael_context_t context = {
|
||||
.left = deserialize_le32(key),
|
||||
.right = deserialize_le32(key + 4),
|
||||
};
|
||||
michael_update(&context, pseudo_header, sizeof(pseudo_header));
|
||||
michael_update(&context, data, len);
|
||||
|
||||
const uint8_t padding_marker = 0x5a;
|
||||
michael_update(&context, &padding_marker, sizeof(padding_marker));
|
||||
const uint8_t zeroes[4] = { 0 };
|
||||
if (context.pending_size != 0)
|
||||
michael_update(&context, zeroes, sizeof(context.pending) - context.pending_size);
|
||||
michael_update(&context, zeroes, sizeof(zeroes));
|
||||
|
||||
serialize_le32(out, context.left);
|
||||
serialize_le32(out + 4, context.right);
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
#include <u80211/association.h>
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/u80211.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
#define CCMP_HEADER_SIZE 8
|
||||
#define CCMP_MIC_SIZE 8
|
||||
#define CCMP_EXT_IV 0x20
|
||||
#define TKIP_HEADER_SIZE 8
|
||||
#define TKIP_MIC_SIZE 8
|
||||
#define TKIP_ICV_SIZE 4
|
||||
#define TKIP_EXT_IV 0x20
|
||||
#define SEQUENCE_SIZE 6
|
||||
|
||||
static bool deserialize_cipher_key_index(const void *data, size_t data_size, uint8_t *index) {
|
||||
// all currently supported 802.11 cipher headers store the key id in bits 6-7 of byte 3
|
||||
if (data_size < 4)
|
||||
return false;
|
||||
|
||||
*index = ((const uint8_t *)data)[3] >> 6;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool data_packet_for_device(u80211_device_t *device, const u80211_header_description_t *header) {
|
||||
return u80211_mac_address_equal(&header->addresses[0], &device->metadata.mac_address) || (header->addresses[0].bytes[0] & 1);
|
||||
}
|
||||
|
||||
static void data_packet_addresses(const u80211_header_description_t *header, const u80211_mac_address_t **destination, const u80211_mac_address_t **source) {
|
||||
bool to_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS;
|
||||
bool from_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS;
|
||||
|
||||
if (to_ds) {
|
||||
*destination = &header->addresses[2];
|
||||
*source = from_ds ? &header->addresses[3] : &header->addresses[1];
|
||||
} else {
|
||||
*destination = &header->addresses[0];
|
||||
*source = from_ds ? &header->addresses[2] : &header->addresses[1];
|
||||
}
|
||||
}
|
||||
|
||||
static bool process_ccmp_rx(u80211_device_t *device, const u80211_header_description_t *header, uint8_t key_index, void **data_start, size_t *data_size) {
|
||||
if (*data_size < CCMP_HEADER_SIZE + CCMP_MIC_SIZE)
|
||||
return false;
|
||||
|
||||
const uint8_t *ccmp_header = *data_start;
|
||||
if (!(ccmp_header[3] & CCMP_EXT_IV))
|
||||
return false;
|
||||
|
||||
const uint8_t sequence[SEQUENCE_SIZE] = {
|
||||
ccmp_header[0], ccmp_header[1], ccmp_header[4],
|
||||
ccmp_header[5], ccmp_header[6], ccmp_header[7],
|
||||
};
|
||||
|
||||
if (!u80211_key_update_rx_sequence(device, header, key_index, U80211_CIPHER_CCMP, sequence, sizeof(sequence)))
|
||||
return false;
|
||||
|
||||
*data_start = (uint8_t *)*data_start + CCMP_HEADER_SIZE;
|
||||
*data_size -= CCMP_HEADER_SIZE + CCMP_MIC_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool process_tkip_rx(u80211_device_t *device, const u80211_header_description_t *header, uint8_t key_index, void **data_start, size_t *data_size) {
|
||||
// TODO: support qos data frames and use their tid as the michael priority
|
||||
if (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control) != U80211_HEADER_FRAME_CONTROL_TYPE_DATA)
|
||||
return false;
|
||||
|
||||
if (U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control) != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA)
|
||||
return false;
|
||||
|
||||
if (*data_size < TKIP_HEADER_SIZE + TKIP_MIC_SIZE + TKIP_ICV_SIZE)
|
||||
return false;
|
||||
|
||||
const uint8_t *tkip_header = *data_start;
|
||||
if (!(tkip_header[3] & TKIP_EXT_IV))
|
||||
return false;
|
||||
if (tkip_header[1] != ((tkip_header[0] | 0x20) & 0x7f))
|
||||
return false;
|
||||
|
||||
const uint8_t *payload = tkip_header + TKIP_HEADER_SIZE;
|
||||
size_t payload_size = *data_size - TKIP_HEADER_SIZE - TKIP_MIC_SIZE - TKIP_ICV_SIZE;
|
||||
const uint8_t *mic = payload + payload_size;
|
||||
|
||||
u80211_tkip_data_t tkip_data = {
|
||||
.header = header,
|
||||
.key_index = key_index,
|
||||
.priority = 0,
|
||||
.data = payload,
|
||||
.data_size = payload_size,
|
||||
.sequence = {
|
||||
tkip_header[2], tkip_header[0], tkip_header[4],
|
||||
tkip_header[5], tkip_header[6], tkip_header[7],
|
||||
},
|
||||
};
|
||||
u80211_memcpy(tkip_data.mic, mic, sizeof(tkip_data.mic));
|
||||
data_packet_addresses(header, &tkip_data.destination, &tkip_data.source);
|
||||
|
||||
if (!u80211_key_validate_tkip_rx(device, &tkip_data))
|
||||
return false;
|
||||
|
||||
*data_start = (void *)payload;
|
||||
*data_size = payload_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void u80211_process_packet(u80211_device_t *device, void *packet, size_t packet_size) {
|
||||
__atomic_add_fetch(&device->packet_count, 1, __ATOMIC_RELAXED);
|
||||
|
||||
void *data_start;
|
||||
size_t data_size;
|
||||
u80211_header_description_t header;
|
||||
if (u80211_deserialize_header(packet, packet_size, &header, &data_start, &data_size) != U80211_STATUS_SUCCESS)
|
||||
return;
|
||||
|
||||
// encrypted packets contain an additional header that we need to handle
|
||||
// TODO: this might be handled by future hardware, so this needs to be a flag in the device metadata
|
||||
if (header.frame_control & U80211_HEADER_FRAME_CONTROL_PROTECTED_FRAME) {
|
||||
uint8_t key_index;
|
||||
if (!deserialize_cipher_key_index(data_start, data_size, &key_index))
|
||||
return;
|
||||
|
||||
switch (u80211_select_cipher_by_index(device, &header, key_index)) {
|
||||
case U80211_CIPHER_CCMP:
|
||||
if (!process_ccmp_rx(device, &header, key_index, &data_start, &data_size))
|
||||
return;
|
||||
break;
|
||||
case U80211_CIPHER_TKIP:
|
||||
if (!process_tkip_rx(device, &header, key_index, &data_start, &data_size))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header.frame_control)) {
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT:
|
||||
u80211_process_management_packet(device, &header, data_start, data_size);
|
||||
break;
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_DATA:
|
||||
if (data_packet_for_device(device, &header) &&
|
||||
u80211_association_is_duplicate(device, &header, U80211_DEVICE_STATE_ASSOCIATED))
|
||||
return;
|
||||
u80211_process_data_packet(device, &header, data_start, data_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t u80211_get_packet_count(u80211_device_t *device) {
|
||||
return __atomic_load_n(&device->packet_count, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
void u80211_reset_packet_count(u80211_device_t *device) {
|
||||
__atomic_store_n(&device->packet_count, 0, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static int deserialize_management_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) {
|
||||
if (source_size < 14)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
u80211_memcpy(&header->addresses[1], source, 6);
|
||||
u80211_memcpy(&header->addresses[2], (const void *)((uintptr_t)source + 6), 6);
|
||||
header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12));
|
||||
|
||||
*data_start = (void *)((uintptr_t)source + 14);
|
||||
*data_size = source_size - 14;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int deserialize_control_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) {
|
||||
(void)source;
|
||||
(void)source_size;
|
||||
(void)header;
|
||||
(void)data_start;
|
||||
(void)data_size;
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static int deserialize_data_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) {
|
||||
int subtype = U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control);
|
||||
|
||||
if (subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA && subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_NULL_DATA)
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
|
||||
size_t header_size = (header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS) && (header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS) ? 20 : 14;
|
||||
if (source_size < header_size)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
u80211_memcpy(&header->addresses[1], source, 6);
|
||||
u80211_memcpy(&header->addresses[2], (const void *)((uintptr_t)source + 6), 6);
|
||||
|
||||
if (header_size == 20) {
|
||||
header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12));
|
||||
u80211_memcpy(&header->addresses[3], (const void *)((uintptr_t)source + 14), 6);
|
||||
} else {
|
||||
header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12));
|
||||
}
|
||||
|
||||
*data_start = (void *)((uintptr_t)source + header_size);
|
||||
*data_size = source_size - header_size;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_deserialize_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) {
|
||||
if (source_size < 10)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
header->frame_control = deserialize_le16(source);
|
||||
header->duration_id = deserialize_le16((const void *)((uintptr_t)source + 2));
|
||||
u80211_memcpy(&header->addresses[0], (const void *)((uintptr_t)source + 4), 6);
|
||||
|
||||
void *next_part = (void *)((uintptr_t)source + 10);
|
||||
size_t next_part_size = source_size - 10;
|
||||
|
||||
switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control)) {
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT:
|
||||
return deserialize_management_header(next_part, next_part_size, header, data_start, data_size);
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_CONTROL:
|
||||
return deserialize_control_header(next_part, next_part_size, header, data_start, data_size);
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_DATA:
|
||||
return deserialize_data_header(next_part, next_part_size, header, data_start, data_size);
|
||||
default:
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void serialize_common_header(u80211_header_description_t *header, uint8_t *destination) {
|
||||
serialize_le16(destination, header->frame_control);
|
||||
serialize_le16(destination + 2, header->duration_id);
|
||||
u80211_memcpy(destination + 4, &header->addresses[0], 6);
|
||||
}
|
||||
|
||||
static int serialize_management_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
uint8_t *destination = u80211_descriptor_allocate_space(descriptor, 24);
|
||||
if (destination == NULL)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
serialize_common_header(header, destination);
|
||||
u80211_memcpy(destination + 10, &header->addresses[1], 6);
|
||||
u80211_memcpy(destination + 16, &header->addresses[2], 6);
|
||||
serialize_le16(destination + 22, header->sequence_control);
|
||||
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int serialize_control_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
(void)header;
|
||||
(void)descriptor;
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static int serialize_data_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
int subtype = U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control);
|
||||
|
||||
if (subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA && subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_NULL_DATA)
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
|
||||
size_t header_size = (header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS) && (header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS) ? 30 : 24;
|
||||
|
||||
uint8_t *destination = u80211_descriptor_allocate_space(descriptor, header_size);
|
||||
if (destination == NULL)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
serialize_common_header(header, destination);
|
||||
u80211_memcpy(destination + 10, &header->addresses[1], 6);
|
||||
u80211_memcpy(destination + 16, &header->addresses[2], 6);
|
||||
|
||||
if (header_size == 30) {
|
||||
serialize_le16(destination + 22, header->sequence_control);
|
||||
u80211_memcpy(destination + 24, &header->addresses[3], 6);
|
||||
} else {
|
||||
serialize_le16(destination + 22, header->sequence_control);
|
||||
}
|
||||
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_serialize_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control)) {
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT:
|
||||
return serialize_management_header(header, descriptor);
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_CONTROL:
|
||||
return serialize_control_header(header, descriptor);
|
||||
case U80211_HEADER_FRAME_CONTROL_TYPE_DATA:
|
||||
return serialize_data_header(header, descriptor);
|
||||
default:
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/status.h>
|
||||
|
||||
#define ETHERNET_HEADER_SIZE 14
|
||||
#define ETHERNET_MAX_FRAME_SIZE 1514
|
||||
#define LLCSNAP_SIZE 8
|
||||
#define TX_BUFFER_HEADROOM 64
|
||||
#define CCMP_HEADER_SIZE 8
|
||||
#define CCMP_EXT_IV 0x20
|
||||
#define TKIP_HEADER_SIZE 8
|
||||
#define TKIP_MIC_SIZE 8
|
||||
#define TKIP_EXT_IV 0x20
|
||||
#define TX_BUFFER_TAILROOM TKIP_MIC_SIZE
|
||||
#define SEQUENCE_SIZE 6
|
||||
|
||||
// this is the expected LLC/SNAP header for our use-case. it is then followed by a big-endian 16-bit ethertype.
|
||||
const uint8_t byte_header[6] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
|
||||
|
||||
void u80211_process_data_packet(u80211_device_t *device, u80211_header_description_t *header, void *data, size_t data_size) {
|
||||
if (data_size < LLCSNAP_SIZE)
|
||||
return;
|
||||
|
||||
if (u80211_memcmp(data, byte_header, sizeof(byte_header)) != 0)
|
||||
return;
|
||||
|
||||
uint16_t ethertype;
|
||||
u80211_memcpy(ðertype, (void *)((uintptr_t)data + 6), sizeof(ethertype));
|
||||
|
||||
// create an ethernet header before the payload
|
||||
// doing this in-place is safe due to the LLC/SNAP header + 802.11 header being much
|
||||
// larger than the ethernet header.
|
||||
data = (void *)((uintptr_t)data + LLCSNAP_SIZE - ETHERNET_HEADER_SIZE);
|
||||
data_size = data_size + ETHERNET_HEADER_SIZE - LLCSNAP_SIZE;
|
||||
|
||||
u80211_mac_address_t *destination;
|
||||
u80211_mac_address_t *source;
|
||||
bool to_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS;
|
||||
bool from_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS;
|
||||
|
||||
if (to_ds) {
|
||||
destination = &header->addresses[2];
|
||||
source = from_ds ? &header->addresses[3] : &header->addresses[1];
|
||||
} else {
|
||||
destination = &header->addresses[0];
|
||||
source = from_ds ? &header->addresses[2] : &header->addresses[1];
|
||||
}
|
||||
|
||||
u80211_memcpy(data, destination, sizeof(*destination));
|
||||
u80211_memcpy((void *)((uintptr_t)data + 6), source, sizeof(*source));
|
||||
u80211_memcpy((void *)((uintptr_t)data + 12), ðertype, sizeof(ethertype));
|
||||
|
||||
u80211_kernel_receive_callback(device, data, data_size);
|
||||
}
|
||||
|
||||
static int prepare_ccmp_tx(u80211_device_t *device, u80211_header_description_t *header, uint8_t key, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
uint8_t *ccmp_header = u80211_descriptor_allocate_space(descriptor, CCMP_HEADER_SIZE);
|
||||
if (ccmp_header == NULL)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
uint8_t sequence[SEQUENCE_SIZE];
|
||||
if (!u80211_key_next_tx_sequence(device, header, key, U80211_CIPHER_CCMP, sequence, sizeof(sequence)))
|
||||
return U80211_STATUS_NOT_PERMITTED;
|
||||
|
||||
// TODO: other hardware might provide more complete cipher handling, including the CCMP header
|
||||
ccmp_header[0] = sequence[0];
|
||||
ccmp_header[1] = sequence[1];
|
||||
ccmp_header[2] = 0;
|
||||
ccmp_header[3] = CCMP_EXT_IV | key << 6;
|
||||
ccmp_header[4] = sequence[2];
|
||||
ccmp_header[5] = sequence[3];
|
||||
ccmp_header[6] = sequence[4];
|
||||
ccmp_header[7] = sequence[5];
|
||||
header->frame_control |= U80211_HEADER_FRAME_CONTROL_PROTECTED_FRAME;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int prepare_tkip_tx(u80211_device_t *device, u80211_header_description_t *header, uint8_t key, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
u80211_tkip_data_t tkip_data = {
|
||||
.header = header,
|
||||
.key_index = key,
|
||||
.destination = &header->addresses[2],
|
||||
.source = &header->addresses[1],
|
||||
.priority = 0,
|
||||
.data = (uint8_t *)descriptor->data + descriptor->current_offset,
|
||||
.data_size = descriptor->size - descriptor->current_offset,
|
||||
};
|
||||
|
||||
uint8_t *tkip_header = u80211_descriptor_allocate_space(descriptor, TKIP_HEADER_SIZE);
|
||||
if (tkip_header == NULL)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
if (!u80211_key_prepare_tkip_tx(device, &tkip_data))
|
||||
return U80211_STATUS_NOT_PERMITTED;
|
||||
|
||||
// TODO: other hardware might not expect something exactly like this. this code adds both
|
||||
// the header and the trailing mic.
|
||||
tkip_header[0] = tkip_data.sequence[1];
|
||||
tkip_header[1] = (tkip_data.sequence[1] | 0x20) & 0x7f;
|
||||
tkip_header[2] = tkip_data.sequence[0];
|
||||
tkip_header[3] = TKIP_EXT_IV | key << 6;
|
||||
tkip_header[4] = tkip_data.sequence[2];
|
||||
tkip_header[5] = tkip_data.sequence[3];
|
||||
tkip_header[6] = tkip_data.sequence[4];
|
||||
tkip_header[7] = tkip_data.sequence[5];
|
||||
u80211_memcpy((uint8_t *)descriptor->data + descriptor->size, tkip_data.mic, TKIP_MIC_SIZE);
|
||||
descriptor->size += TKIP_MIC_SIZE;
|
||||
header->frame_control |= U80211_HEADER_FRAME_CONTROL_PROTECTED_FRAME;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_allocate_tx_buffer(u80211_device_t *device, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
int status = device->ops->allocate_tx_buffer(device, ETHERNET_MAX_FRAME_SIZE + TX_BUFFER_HEADROOM + TX_BUFFER_TAILROOM, descriptor);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// keep MIC tailroom outside the logical buffer so other ciphers do not transmit it
|
||||
descriptor->data = (void *)((uintptr_t)descriptor->data + TX_BUFFER_HEADROOM);
|
||||
descriptor->size -= TX_BUFFER_HEADROOM + TX_BUFFER_TAILROOM;
|
||||
descriptor->current_offset -= TX_BUFFER_HEADROOM + TX_BUFFER_TAILROOM;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_transmit_buffer(u80211_device_t *device, u80211_tx_buffer_descriptor_t *descriptor) {
|
||||
descriptor->data = (uint8_t *)descriptor->data - TX_BUFFER_HEADROOM;
|
||||
descriptor->size += TX_BUFFER_HEADROOM;
|
||||
descriptor->current_offset += TX_BUFFER_HEADROOM;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->association_spinlock);
|
||||
u80211_ap_t *ap = device->ap;
|
||||
if (u80211_get_device_state(device) != U80211_DEVICE_STATE_ASSOCIATED || ap == NULL) {
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_NOT_ASSOCIATED;
|
||||
}
|
||||
u80211_ap_hold(ap);
|
||||
u80211_kernel_release_spinlock(device->association_spinlock);
|
||||
|
||||
size_t ethernet_frame_size = descriptor->size - descriptor->current_offset;
|
||||
if (ethernet_frame_size < ETHERNET_HEADER_SIZE) {
|
||||
u80211_ap_release(ap);
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
}
|
||||
|
||||
uint8_t *ethernet_header = (uint8_t *)descriptor->data + descriptor->current_offset;
|
||||
u80211_mac_address_t destination;
|
||||
u80211_mac_address_t source;
|
||||
uint16_t ethertype;
|
||||
u80211_memcpy(&destination, ethernet_header, sizeof(destination));
|
||||
u80211_memcpy(&source, ethernet_header + 6, sizeof(source));
|
||||
u80211_memcpy(ðertype, ethernet_header + 12, sizeof(ethertype));
|
||||
|
||||
descriptor->current_offset += ETHERNET_HEADER_SIZE;
|
||||
uint8_t *llc_snap = u80211_descriptor_allocate_space(descriptor, LLCSNAP_SIZE);
|
||||
if (llc_snap == NULL) {
|
||||
u80211_ap_release(ap);
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
}
|
||||
u80211_memcpy(llc_snap, byte_header, sizeof(byte_header));
|
||||
u80211_memcpy(llc_snap + sizeof(byte_header), ðertype, sizeof(ethertype));
|
||||
|
||||
u80211_header_description_t header = {
|
||||
.frame_control = (U80211_HEADER_FRAME_CONTROL_TYPE_DATA << 2) | U80211_HEADER_FRAME_CONTROL_TO_DS,
|
||||
.sequence_control = __atomic_fetch_add(&device->tx_sequence_control, 0x10, __ATOMIC_RELAXED),
|
||||
.addresses = {
|
||||
ap->mac_address,
|
||||
source,
|
||||
destination,
|
||||
},
|
||||
};
|
||||
u80211_ap_release(ap);
|
||||
|
||||
int key = u80211_select_key(device, &header);
|
||||
int cipher = -1;
|
||||
if (key >= 0) {
|
||||
// supported cipher headers have two bits for key selection
|
||||
if (key > 3) {
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_NOT_PERMITTED;
|
||||
}
|
||||
|
||||
cipher = u80211_select_cipher_by_index(device, &header, key);
|
||||
int cipher_status;
|
||||
switch (cipher) {
|
||||
case U80211_CIPHER_CCMP:
|
||||
cipher_status = prepare_ccmp_tx(device, &header, key, descriptor);
|
||||
break;
|
||||
case U80211_CIPHER_TKIP:
|
||||
cipher_status = prepare_tkip_tx(device, &header, key, descriptor);
|
||||
break;
|
||||
default:
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_UNSUPPORTED;
|
||||
}
|
||||
if (cipher_status != U80211_STATUS_SUCCESS) {
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return cipher_status;
|
||||
}
|
||||
}
|
||||
|
||||
int status = u80211_serialize_header(&header, descriptor);
|
||||
if (status != U80211_STATUS_SUCCESS) {
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return status;
|
||||
}
|
||||
|
||||
const u80211_transmit_options_t options = { .key = key, .cipher = cipher };
|
||||
return device->ops->transmit(device, descriptor, &options);
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/scan.h>
|
||||
#include <u80211/association.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/u80211.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
#define IE_ID_SSID 0
|
||||
#define IE_ID_RATES 1
|
||||
#define IE_ID_CHANNEL 3
|
||||
#define IE_ID_RSN 48
|
||||
#define IE_ID_RATES_EXT 50
|
||||
|
||||
#define MANAGEMENT_HEADER_SIZE 24
|
||||
#define AUTH_DATA_SIZE 6
|
||||
#define REASON_CODE_SIZE 2
|
||||
#define ASSOCIATION_REQUEST_FIXED_SIZE 4
|
||||
#define ASSOCIATION_RESPONSE_FIXED_SIZE 6
|
||||
#define CAPABILITY_ESS 0x0001
|
||||
#define CAPABILITY_SHORT_PREAMBLE 0x0020
|
||||
#define CAPABILITY_SHORT_SLOT_TIME 0x0400
|
||||
// TODO: Only hardcode these capabilities on 2.4 GHz.
|
||||
#define ASSOCIATION_CAPABILITIES (CAPABILITY_ESS | CAPABILITY_SHORT_PREAMBLE | CAPABILITY_SHORT_SLOT_TIME)
|
||||
// TODO: Revisit the listen interval when power saving is implemented.
|
||||
#define ASSOCIATION_LISTEN_INTERVAL 10
|
||||
#define MAX_SSID_SIZE 32
|
||||
#define MAX_RATE_VALUE 125
|
||||
|
||||
// TODO check if basic rates are supported by the device
|
||||
static bool handle_rates(uint8_t rate_bitmap[16], const uint8_t *rates, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint8_t rate = rates[i] & 0x7f;
|
||||
|
||||
// these two have special meanings, so ignore them
|
||||
if (rate == 126 || rate == 127)
|
||||
continue;
|
||||
|
||||
rate_bitmap[rate / 8] |= 1 << (rate % 8);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool management_packet_for_device(u80211_device_t *device, u80211_header_description_t *header) {
|
||||
return u80211_mac_address_equal(&header->addresses[1], &header->addresses[2]) && u80211_mac_address_equal(&header->addresses[0], &device->metadata.mac_address);
|
||||
}
|
||||
|
||||
static bool management_packet_for_device_or_broadcast(u80211_device_t *device, u80211_header_description_t *header) {
|
||||
const u80211_mac_address_t broadcast_address = { .bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
||||
|
||||
return u80211_mac_address_equal(&header->addresses[1], &header->addresses[2]) &&
|
||||
(u80211_mac_address_equal(&header->addresses[0], &device->metadata.mac_address) ||
|
||||
u80211_mac_address_equal(&header->addresses[0], &broadcast_address));
|
||||
}
|
||||
|
||||
static bool process_information_elements(uint8_t rate_bitmap[16], char *ssid, uint8_t *channel, const uint8_t **rsn, size_t *rsn_size, const void *data, size_t data_size) {
|
||||
const uint8_t *bytes = data;
|
||||
size_t offset = 0;
|
||||
while (offset < data_size) {
|
||||
if (data_size - offset < 2)
|
||||
return false;
|
||||
|
||||
uint8_t id = bytes[offset];
|
||||
uint8_t size = bytes[offset + 1];
|
||||
offset += 2;
|
||||
|
||||
if (data_size - offset < size)
|
||||
return false;
|
||||
|
||||
switch (id) {
|
||||
case IE_ID_SSID:
|
||||
if (size > MAX_SSID_SIZE)
|
||||
return false;
|
||||
|
||||
if (ssid != NULL) {
|
||||
u80211_memcpy(ssid, bytes + offset, size);
|
||||
ssid[size] = '\0';
|
||||
}
|
||||
break;
|
||||
case IE_ID_CHANNEL:
|
||||
if (size != 1)
|
||||
return false;
|
||||
|
||||
if (channel != NULL)
|
||||
*channel = bytes[offset];
|
||||
break;
|
||||
case IE_ID_RATES:
|
||||
if (size > 8)
|
||||
return false;
|
||||
/* fall through */
|
||||
case IE_ID_RATES_EXT:
|
||||
if (!handle_rates(rate_bitmap, bytes + offset, size))
|
||||
return false;
|
||||
break;
|
||||
case IE_ID_RSN:
|
||||
if (rsn != NULL && rsn_size != NULL) {
|
||||
*rsn = bytes + offset;
|
||||
*rsn_size = size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t count_rates(const uint8_t rate_bitmap[16]) {
|
||||
size_t rate_count = 0;
|
||||
for (size_t rate = 0; rate <= MAX_RATE_VALUE; ++rate) {
|
||||
if (rate_bitmap[rate / 8] & (1 << (rate % 8)))
|
||||
++rate_count;
|
||||
}
|
||||
|
||||
return rate_count;
|
||||
}
|
||||
|
||||
static size_t rate_information_elements_size(size_t rate_count) {
|
||||
size_t size = 2 + min(rate_count, 8);
|
||||
|
||||
if (rate_count > 8)
|
||||
size += 2 + rate_count - 8;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t serialize_rate_information_elements(uint8_t *destination, const uint8_t rate_bitmap[16], size_t rate_count) {
|
||||
size_t supported_rate_count = min(rate_count, 8);
|
||||
size_t extended_rate_count = rate_count - supported_rate_count;
|
||||
size_t offset = 0;
|
||||
|
||||
destination[offset++] = IE_ID_RATES;
|
||||
destination[offset++] = supported_rate_count;
|
||||
|
||||
size_t rate_index = 0;
|
||||
for (size_t rate = 0; rate <= MAX_RATE_VALUE; ++rate) {
|
||||
if (!(rate_bitmap[rate / 8] & (1 << (rate % 8))))
|
||||
continue;
|
||||
|
||||
if (rate_index == supported_rate_count && extended_rate_count != 0) {
|
||||
destination[offset++] = IE_ID_RATES_EXT;
|
||||
destination[offset++] = extended_rate_count;
|
||||
}
|
||||
|
||||
destination[offset++] = rate;
|
||||
++rate_index;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int prepare_management_packet(u80211_device_t *device, const u80211_mac_address_t *address, int subtype, size_t data_size, u80211_tx_buffer_descriptor_t *descriptor, uint8_t **data) {
|
||||
int status = device->ops->allocate_tx_buffer(device, MANAGEMENT_HEADER_SIZE + data_size, descriptor);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
*data = u80211_descriptor_allocate_space(descriptor, data_size);
|
||||
if (*data == NULL) {
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
}
|
||||
|
||||
u80211_header_description_t header = {
|
||||
.frame_control = subtype << 4,
|
||||
.sequence_control = __atomic_fetch_add(&device->tx_sequence_control, 0x10, __ATOMIC_RELAXED),
|
||||
.addresses = {
|
||||
*address,
|
||||
device->metadata.mac_address,
|
||||
*address,
|
||||
},
|
||||
};
|
||||
|
||||
status = u80211_serialize_header(&header, descriptor);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
device->ops->free_tx_buffer(device, descriptor);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void process_probe_response(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
if (data_size < 12)
|
||||
return;
|
||||
|
||||
if (!management_packet_for_device(device, header))
|
||||
return;
|
||||
|
||||
u80211_beacon_data_t beacon_data;
|
||||
u80211_memset(&beacon_data, 0, sizeof(beacon_data));
|
||||
beacon_data.interval = deserialize_le16((const void *)((uintptr_t)data + 8));
|
||||
beacon_data.capabilities = deserialize_le16((const void *)((uintptr_t)data + 10));
|
||||
beacon_data.mac_address = header->addresses[2];
|
||||
|
||||
if (!process_information_elements(beacon_data.rate_bitmap, beacon_data.ssid, &beacon_data.channel, &beacon_data.rsn, &beacon_data.rsn_size, (const void *)((uintptr_t)data + 12), data_size - 12))
|
||||
return;
|
||||
|
||||
u80211_scan_process_response(device, &beacon_data);
|
||||
}
|
||||
|
||||
static void process_auth_packet(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
if (data_size < 6)
|
||||
return;
|
||||
|
||||
if (!management_packet_for_device(device, header))
|
||||
return;
|
||||
|
||||
u80211_auth_data_t auth_data;
|
||||
auth_data.address = header->addresses[2];
|
||||
auth_data.auth_algorithm = deserialize_le16(data);
|
||||
auth_data.auth_transaction = deserialize_le16((const void *)((uintptr_t)data + 2));
|
||||
auth_data.status = deserialize_le16((const void *)((uintptr_t)data + 4));
|
||||
|
||||
if (u80211_association_is_duplicate(device, header, U80211_DEVICE_STATE_AUTHENTICATING))
|
||||
return;
|
||||
|
||||
u80211_association_process_authentication(device, &auth_data);
|
||||
}
|
||||
|
||||
static void process_association_response(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
if (data_size < ASSOCIATION_RESPONSE_FIXED_SIZE)
|
||||
return;
|
||||
|
||||
if (!management_packet_for_device(device, header))
|
||||
return;
|
||||
|
||||
u80211_association_response_data_t association_data;
|
||||
u80211_memset(&association_data, 0, sizeof(association_data));
|
||||
association_data.address = header->addresses[2];
|
||||
association_data.capabilities = deserialize_le16(data);
|
||||
association_data.status = deserialize_le16((const void *)((uintptr_t)data + 2));
|
||||
association_data.association_id = deserialize_le16((const void *)((uintptr_t)data + 4));
|
||||
|
||||
if (!process_information_elements(association_data.rate_bitmap, NULL, NULL, NULL, NULL, (const void *)((uintptr_t)data + ASSOCIATION_RESPONSE_FIXED_SIZE), data_size - ASSOCIATION_RESPONSE_FIXED_SIZE))
|
||||
return;
|
||||
|
||||
if (u80211_association_is_duplicate(device, header, U80211_DEVICE_STATE_ASSOCIATING))
|
||||
return;
|
||||
|
||||
u80211_association_process_response(device, &association_data);
|
||||
}
|
||||
|
||||
static void process_deauthentication_packet(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
if (data_size < REASON_CODE_SIZE)
|
||||
return;
|
||||
|
||||
if (!management_packet_for_device_or_broadcast(device, header))
|
||||
return;
|
||||
|
||||
u80211_deauthentication_data_t deauthentication_data = {
|
||||
.address = header->addresses[2],
|
||||
.reason = deserialize_le16(data),
|
||||
};
|
||||
u80211_association_process_deauthentication(device, &deauthentication_data);
|
||||
}
|
||||
|
||||
static void process_disassociation_packet(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
if (data_size < REASON_CODE_SIZE)
|
||||
return;
|
||||
|
||||
if (!management_packet_for_device_or_broadcast(device, header))
|
||||
return;
|
||||
|
||||
u80211_disassociation_data_t disassociation_data = {
|
||||
.address = header->addresses[2],
|
||||
.reason = deserialize_le16(data),
|
||||
};
|
||||
u80211_association_process_disassociation(device, &disassociation_data);
|
||||
}
|
||||
|
||||
void u80211_process_management_packet(u80211_device_t *device, u80211_header_description_t *header, const void *data, size_t data_size) {
|
||||
int subtype = U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control);
|
||||
|
||||
switch (subtype) {
|
||||
case U80211_HEADER_FRAME_CONTROL_SUBTYPE_ASSOCIATION_RESPONSE:
|
||||
process_association_response(device, header, data, data_size);
|
||||
break;
|
||||
case U80211_HEADER_FRAME_CONTROL_SUBTYPE_PROBE_RESPONSE:
|
||||
process_probe_response(device, header, data, data_size);
|
||||
break;
|
||||
case U80211_HEADER_FRAME_CONTROL_SUBTYPE_AUTHENTICATION:
|
||||
process_auth_packet(device, header, data, data_size);
|
||||
break;
|
||||
case U80211_HEADER_FRAME_CONTROL_SUBTYPE_DEAUTHENTICATION:
|
||||
process_deauthentication_packet(device, header, data, data_size);
|
||||
break;
|
||||
case U80211_HEADER_FRAME_CONTROL_SUBTYPE_DISASSOCIATION:
|
||||
process_disassociation_packet(device, header, data, data_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int u80211_send_authentication(u80211_device_t *device, u80211_auth_data_t *auth_data) {
|
||||
u80211_tx_buffer_descriptor_t descriptor;
|
||||
uint8_t *data;
|
||||
int status = prepare_management_packet(device, &auth_data->address, U80211_HEADER_FRAME_CONTROL_SUBTYPE_AUTHENTICATION, AUTH_DATA_SIZE, &descriptor, &data);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
serialize_le16(data, auth_data->auth_algorithm);
|
||||
serialize_le16(data + 2, auth_data->auth_transaction);
|
||||
serialize_le16(data + 4, auth_data->status);
|
||||
|
||||
const u80211_transmit_options_t options = { .key = -1, .cipher = -1 };
|
||||
return device->ops->transmit(device, &descriptor, &options);
|
||||
}
|
||||
|
||||
int u80211_send_deauthentication(u80211_device_t *device, u80211_deauthentication_data_t *deauthentication_data) {
|
||||
u80211_tx_buffer_descriptor_t descriptor;
|
||||
uint8_t *data;
|
||||
int status = prepare_management_packet(device, &deauthentication_data->address, U80211_HEADER_FRAME_CONTROL_SUBTYPE_DEAUTHENTICATION, REASON_CODE_SIZE, &descriptor, &data);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
serialize_le16(data, deauthentication_data->reason);
|
||||
|
||||
const u80211_transmit_options_t options = { .key = -1, .cipher = -1 };
|
||||
return device->ops->transmit(device, &descriptor, &options);
|
||||
}
|
||||
|
||||
int u80211_send_probe_request(u80211_device_t *device) {
|
||||
size_t rate_count = count_rates(device->metadata.rate_bitmap);
|
||||
size_t probe_request_data_size = 2 + rate_information_elements_size(rate_count);
|
||||
const u80211_mac_address_t broadcast_address = { .bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
||||
|
||||
u80211_tx_buffer_descriptor_t descriptor;
|
||||
uint8_t *probe_request_data;
|
||||
int status = prepare_management_packet(device, &broadcast_address, U80211_HEADER_FRAME_CONTROL_SUBTYPE_PROBE_REQUEST, probe_request_data_size, &descriptor, &probe_request_data);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
probe_request_data[0] = IE_ID_SSID;
|
||||
probe_request_data[1] = 0;
|
||||
serialize_rate_information_elements(probe_request_data + 2, device->metadata.rate_bitmap, rate_count);
|
||||
|
||||
const u80211_transmit_options_t options = { .key = -1, .cipher = -1 };
|
||||
return device->ops->transmit(device, &descriptor, &options);
|
||||
}
|
||||
|
||||
int u80211_send_association_request(u80211_device_t *device, const void *information_elements, size_t information_elements_size) {
|
||||
u80211_ap_t *ap = device->ap;
|
||||
size_t ssid_size = 0;
|
||||
while (ssid_size < MAX_SSID_SIZE && ap->ssid[ssid_size] != '\0')
|
||||
++ssid_size;
|
||||
|
||||
size_t rate_count = count_rates(device->metadata.rate_bitmap);
|
||||
size_t generated_data_size = ASSOCIATION_REQUEST_FIXED_SIZE + 2 + ssid_size + rate_information_elements_size(rate_count);
|
||||
size_t association_request_data_size = generated_data_size + information_elements_size;
|
||||
|
||||
u80211_tx_buffer_descriptor_t descriptor;
|
||||
uint8_t *data;
|
||||
int status = prepare_management_packet(device, &ap->mac_address, U80211_HEADER_FRAME_CONTROL_SUBTYPE_ASSOCIATION_REQUEST, association_request_data_size, &descriptor, &data);
|
||||
if (status != U80211_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
serialize_le16(data, ASSOCIATION_CAPABILITIES);
|
||||
serialize_le16(data + 2, ASSOCIATION_LISTEN_INTERVAL);
|
||||
data[4] = IE_ID_SSID;
|
||||
data[5] = ssid_size;
|
||||
u80211_memcpy(data + 6, ap->ssid, ssid_size);
|
||||
size_t rate_information_size = serialize_rate_information_elements(data + 6 + ssid_size, device->metadata.rate_bitmap, rate_count);
|
||||
if (information_elements_size != 0)
|
||||
u80211_memcpy(data + 6 + ssid_size + rate_information_size, information_elements, information_elements_size);
|
||||
|
||||
const u80211_transmit_options_t options = { .key = -1, .cipher = -1 };
|
||||
return device->ops->transmit(device, &descriptor, &options);
|
||||
}
|
||||
@@ -0,0 +1,555 @@
|
||||
#include <u80211/rbtree.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define RBTREE_COLOUR_BLACK 0
|
||||
#define RBTREE_COLOUR_RED 1
|
||||
|
||||
// helper macros as the least significant bit of the parent pointer is used for the colour
|
||||
#define RBTREE_NODE_GET_PARENT(x) ((u80211_rbtree_t *)((uintptr_t)(x)->parent & ~1lu))
|
||||
#define RBTREE_NODE_GET_COLOUR(x) ((x) == NULL ? RBTREE_COLOUR_BLACK : ((uintptr_t)(x)->parent & 1))
|
||||
#define RBTREE_NODE_SET_COLOUR(x, c) ((x)->parent = (u80211_rbtree_t *)((uintptr_t)RBTREE_NODE_GET_PARENT(x) | (c)))
|
||||
#define RBTREE_NODE_SET_PARENT(x, p) ((x)->parent = (u80211_rbtree_t *)((uintptr_t)(p) | RBTREE_NODE_GET_COLOUR(x)))
|
||||
#define RBTREE_CONSTRUCT_PARENT(x, c) ((u80211_rbtree_t *)((uintptr_t)(x) | (c)))
|
||||
|
||||
static void rotate_right(u80211_rbtree_t **rootp, u80211_rbtree_t *node) {
|
||||
/* G G
|
||||
// | |
|
||||
// P -> N
|
||||
// / \ / \
|
||||
// N 3 1 P
|
||||
// / \ / \
|
||||
// 1 2 2 3
|
||||
*/
|
||||
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
u80211_rbtree_t *grand_parent = RBTREE_NODE_GET_PARENT(parent);
|
||||
|
||||
// N
|
||||
if (grand_parent == NULL)
|
||||
*rootp = node;
|
||||
else if (grand_parent->left == parent)
|
||||
grand_parent->left = node;
|
||||
else
|
||||
grand_parent->right = node;
|
||||
|
||||
RBTREE_NODE_SET_PARENT(node, grand_parent);
|
||||
|
||||
// P
|
||||
u80211_rbtree_t *node_right_subtree = node->right;
|
||||
|
||||
node->right = parent;
|
||||
RBTREE_NODE_SET_PARENT(parent, node);
|
||||
|
||||
// 2
|
||||
parent->left = node_right_subtree;
|
||||
if (node_right_subtree)
|
||||
RBTREE_NODE_SET_PARENT(node_right_subtree, parent);
|
||||
}
|
||||
|
||||
static void rotate_left(u80211_rbtree_t **rootp, u80211_rbtree_t *node) {
|
||||
/* G G
|
||||
// | |
|
||||
// P N
|
||||
// / \ -> / \
|
||||
// 1 N P 3
|
||||
// / \ / \
|
||||
// 2 3 1 2
|
||||
*/
|
||||
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
u80211_rbtree_t *grand_parent = RBTREE_NODE_GET_PARENT(parent);
|
||||
|
||||
// N
|
||||
if (grand_parent == NULL)
|
||||
*rootp = node;
|
||||
else if (grand_parent->left == parent)
|
||||
grand_parent->left = node;
|
||||
else
|
||||
grand_parent->right = node;
|
||||
|
||||
RBTREE_NODE_SET_PARENT(node, grand_parent);
|
||||
|
||||
// P
|
||||
u80211_rbtree_t *node_left_subtree = node->left;
|
||||
|
||||
node->left = parent;
|
||||
RBTREE_NODE_SET_PARENT(parent, node);
|
||||
|
||||
// 2
|
||||
parent->right = node_left_subtree;
|
||||
if (node_left_subtree)
|
||||
RBTREE_NODE_SET_PARENT(node_left_subtree, parent);
|
||||
}
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_find_first_larger_equal(u80211_rbtree_t *rbtree, void *key, u80211_rbtree_value_compare_fn_t compare_fn) {
|
||||
u80211_rbtree_t *found = NULL;
|
||||
for (;;) {
|
||||
if (rbtree == NULL)
|
||||
return found;
|
||||
|
||||
int comparison = compare_fn(key, rbtree);
|
||||
|
||||
if (comparison == 0)
|
||||
return rbtree;
|
||||
|
||||
if (comparison == -1)
|
||||
found = rbtree;
|
||||
|
||||
rbtree = comparison == 1 ? rbtree->right : rbtree->left;
|
||||
}
|
||||
}
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_lookup(u80211_rbtree_t *rbtree, void *key, u80211_rbtree_value_compare_fn_t compare_fn) {
|
||||
for (;;) {
|
||||
if (rbtree == NULL)
|
||||
return NULL;
|
||||
|
||||
int comparison = compare_fn(key, rbtree);
|
||||
|
||||
if (comparison == 0)
|
||||
return rbtree;
|
||||
|
||||
rbtree = comparison > 0 ? rbtree->right : rbtree->left;
|
||||
}
|
||||
}
|
||||
|
||||
void u80211_rbtree_insert(u80211_rbtree_t **rbtreep, u80211_rbtree_t *node, u80211_rbtree_compare_fn_t compare_fn) {
|
||||
u80211_rbtree_t *rbtree = *rbtreep;
|
||||
if (rbtree == NULL) {
|
||||
*rbtreep = node;
|
||||
node->right = node->left = node->parent = NULL; // NULL implicitly sets to black
|
||||
return;
|
||||
}
|
||||
|
||||
// find last node
|
||||
int comparison;
|
||||
u80211_rbtree_t **next;
|
||||
for (;;) {
|
||||
comparison = compare_fn(node, rbtree);
|
||||
next = comparison > 0 ? &rbtree->right : &rbtree->left;
|
||||
if (*next == NULL)
|
||||
break;
|
||||
|
||||
rbtree = *next;
|
||||
}
|
||||
|
||||
// set tree pointers
|
||||
*next = node;
|
||||
node->right = NULL;
|
||||
node->left = NULL;
|
||||
node->parent = RBTREE_CONSTRUCT_PARENT(rbtree, RBTREE_COLOUR_RED); // all inserted nodes are red
|
||||
|
||||
// parent is red, iterate up the tree to fix any property 3 violations we find
|
||||
// node now means the node whose parent we are checking for a property 3 violation
|
||||
u80211_rbtree_t *parent, *grand_parent, *uncle;
|
||||
for (;;) {
|
||||
parent = RBTREE_NODE_GET_PARENT(node);
|
||||
// check for property 3 violation (red parent and red child)
|
||||
if (RBTREE_NODE_GET_COLOUR(parent) == RBTREE_COLOUR_BLACK)
|
||||
return; // no violation, stop here
|
||||
|
||||
grand_parent = RBTREE_NODE_GET_PARENT(parent);
|
||||
uncle = grand_parent->left == parent ? grand_parent->right : grand_parent->left;
|
||||
|
||||
// if the uncle is black, we can leave the loop as there are no more checks needed in this case
|
||||
if (RBTREE_NODE_GET_COLOUR(uncle) == RBTREE_COLOUR_BLACK)
|
||||
break;
|
||||
|
||||
// uncle is red, recolour the tree such as:
|
||||
// parent = black, grandparent = red, uncle = black
|
||||
// and go up for another violation check, as changing the grandparent to red might have
|
||||
// violated it again
|
||||
RBTREE_NODE_SET_COLOUR(parent, RBTREE_COLOUR_BLACK);
|
||||
RBTREE_NODE_SET_COLOUR(uncle, RBTREE_COLOUR_BLACK);
|
||||
// SPECIAL CASE: if grandparent is root, do nothing. we always want the root to be black.
|
||||
if (RBTREE_NODE_GET_PARENT(grand_parent))
|
||||
RBTREE_NODE_SET_COLOUR(grand_parent, RBTREE_COLOUR_RED);
|
||||
node = grand_parent;
|
||||
}
|
||||
|
||||
// black uncle, we have to do a rotation in order to be able to recolour it
|
||||
|
||||
/* if grandparent -> parent and parent -> child are in opposite directions,
|
||||
// we will have to do an extra rotation to straighten it out first
|
||||
// Left rotation: Right rotation:
|
||||
// G G G G
|
||||
// / \ / \ / \ / \
|
||||
// P U -> N U U P -> U N
|
||||
// \ / / \
|
||||
// N P N P
|
||||
*/
|
||||
|
||||
if (grand_parent->left == parent && parent->right == node) {
|
||||
rotate_left(rbtreep, node);
|
||||
node = parent;
|
||||
parent = grand_parent->left;
|
||||
} else if (grand_parent->right == parent && parent->left == node) {
|
||||
rotate_right(rbtreep, node);
|
||||
node = parent;
|
||||
parent = grand_parent->right;
|
||||
}
|
||||
|
||||
/* now, we have to do a rotation so that we can recolour the nodes without
|
||||
// breaking any properties of the tree
|
||||
// G P G P
|
||||
// / \ / \ / \ / \
|
||||
// P U -> N G OR U P -> G N
|
||||
// / \ \ /
|
||||
// N U N U
|
||||
*/
|
||||
|
||||
if (grand_parent->left == parent)
|
||||
rotate_right(rbtreep, parent);
|
||||
else
|
||||
rotate_left(rbtreep, parent);
|
||||
|
||||
/* finally, recolour the nodes:
|
||||
// R B R B
|
||||
// / \ / \ / \ / \
|
||||
// R B -> R R OR B R -> R R
|
||||
// \ \ / /
|
||||
// B B B B
|
||||
*/
|
||||
|
||||
// same operation on both cases
|
||||
RBTREE_NODE_SET_COLOUR(parent, RBTREE_COLOUR_BLACK);
|
||||
RBTREE_NODE_SET_COLOUR(grand_parent, RBTREE_COLOUR_RED);
|
||||
// no need to change node or uncle colour
|
||||
}
|
||||
|
||||
void u80211_rbtree_check(u80211_rbtree_t *rbtree) {
|
||||
if (rbtree == NULL)
|
||||
return;
|
||||
|
||||
// check that the root is black (always will be in this implementation)
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(rbtree);
|
||||
|
||||
// perform a preorder transversal and ensure the properties are followed
|
||||
u80211_rbtree_t *node = rbtree;
|
||||
int leaf_black_count = 0;
|
||||
int current_black_count = 0;
|
||||
int count_blacks = 1;
|
||||
int last_direction = 0; // 1 = left, 2 = right
|
||||
int going_up = 0;
|
||||
for (;;) {
|
||||
int node_colour = RBTREE_NODE_GET_COLOUR(node);
|
||||
if (node_colour == RBTREE_COLOUR_RED) {
|
||||
} else if (!going_up) {
|
||||
++current_black_count;
|
||||
leaf_black_count += count_blacks;
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
// this is a NULL node, do nescessary checks and go up
|
||||
count_blacks = 0;
|
||||
|
||||
--current_black_count;
|
||||
node = parent;
|
||||
parent = RBTREE_NODE_GET_PARENT(node);
|
||||
going_up = 1;
|
||||
} else if (going_up && last_direction == 1) {
|
||||
// we have gone up from the left node
|
||||
// go to the right
|
||||
last_direction = 2;
|
||||
parent = node;
|
||||
node = node->right;
|
||||
going_up = 0;
|
||||
} else if (going_up && last_direction == 2) {
|
||||
// we have gone up from the right
|
||||
// go up again
|
||||
if (parent == NULL)
|
||||
break; // end of tree
|
||||
|
||||
if (node_colour == RBTREE_COLOUR_BLACK)
|
||||
--current_black_count;
|
||||
last_direction = parent->left == node ? 1 : 2;
|
||||
node = parent;
|
||||
parent = RBTREE_NODE_GET_PARENT(parent);
|
||||
} else {
|
||||
// we have gone down, go to the left
|
||||
last_direction = 1;
|
||||
parent = node;
|
||||
node = node->left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_first(u80211_rbtree_t *rbtree) {
|
||||
while (rbtree->left)
|
||||
rbtree = rbtree->left;
|
||||
|
||||
return rbtree;
|
||||
}
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_last(u80211_rbtree_t *rbtree) {
|
||||
while (rbtree->right)
|
||||
rbtree = rbtree->right;
|
||||
|
||||
return rbtree;
|
||||
}
|
||||
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_successor(u80211_rbtree_t *node) {
|
||||
u80211_rbtree_t *iterator = node->right;
|
||||
if (iterator == NULL) {
|
||||
// no right, go up until we leave a left node
|
||||
iterator = node;
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
while (parent) {
|
||||
if (parent->left == iterator)
|
||||
return parent;
|
||||
|
||||
iterator = parent;
|
||||
parent = RBTREE_NODE_GET_PARENT(iterator);
|
||||
}
|
||||
|
||||
// end of tree
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// we have gone right, go all the way down left
|
||||
while (iterator->left != NULL)
|
||||
iterator = iterator->left;
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
u80211_rbtree_t *u80211_rbtree_predecessor(u80211_rbtree_t *node) {
|
||||
u80211_rbtree_t *iterator = node->left;
|
||||
if (iterator == NULL) {
|
||||
// no left, go up until we leave a right node
|
||||
iterator = node;
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
while (parent) {
|
||||
if (parent->right == iterator)
|
||||
return parent;
|
||||
|
||||
iterator = parent;
|
||||
parent = RBTREE_NODE_GET_PARENT(iterator);
|
||||
}
|
||||
|
||||
// end of tree
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// we have gone right, go all the way down left
|
||||
while (iterator->right != NULL)
|
||||
iterator = iterator->right;
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
static void u80211_rbtree_remove_leaf(u80211_rbtree_t **rbtreep, u80211_rbtree_t *node) {
|
||||
u80211_rbtree_t *og_node = node;
|
||||
if (*rbtreep == node) {
|
||||
// if root, set root to null
|
||||
*rbtreep = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (RBTREE_NODE_GET_COLOUR(node) == RBTREE_COLOUR_RED) {
|
||||
// if red there is no rebalancing needed, just remove it
|
||||
goto do_remove;
|
||||
}
|
||||
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
|
||||
// black leaf node
|
||||
u80211_rbtree_t *sibling;
|
||||
for (;;) {
|
||||
if (parent == NULL)
|
||||
goto do_remove;
|
||||
|
||||
sibling = parent->left == node ? parent->right : parent->left;
|
||||
|
||||
// the only case where we have to loop upwards is when everyone is black
|
||||
if (RBTREE_NODE_GET_COLOUR(parent) == RBTREE_COLOUR_RED ||
|
||||
RBTREE_NODE_GET_COLOUR(sibling) == RBTREE_COLOUR_RED ||
|
||||
RBTREE_NODE_GET_COLOUR(sibling->left) == RBTREE_COLOUR_RED ||
|
||||
RBTREE_NODE_GET_COLOUR(sibling->right) == RBTREE_COLOUR_RED)
|
||||
break;
|
||||
|
||||
// colour the sibling red and move upwards
|
||||
RBTREE_NODE_SET_COLOUR(sibling, RBTREE_COLOUR_RED);
|
||||
|
||||
node = parent;
|
||||
parent = RBTREE_NODE_GET_PARENT(node);
|
||||
}
|
||||
|
||||
// red sibling, do a rotation so that the sibling is now the grandparent
|
||||
// and switch the colours of the sibling and parent
|
||||
// P S | R B
|
||||
// / \ / | / /
|
||||
// N S -> P | B -> R
|
||||
// / | / /
|
||||
// N | B B
|
||||
if (RBTREE_NODE_GET_COLOUR(sibling) == RBTREE_COLOUR_RED) {
|
||||
// set parent to red and sibling to black
|
||||
RBTREE_NODE_SET_COLOUR(parent, RBTREE_COLOUR_RED);
|
||||
RBTREE_NODE_SET_COLOUR(sibling, RBTREE_COLOUR_BLACK);
|
||||
|
||||
// rotate
|
||||
if (parent->left == sibling) {
|
||||
rotate_right(rbtreep, sibling);
|
||||
sibling = parent->left;
|
||||
} else {
|
||||
rotate_left(rbtreep, sibling);
|
||||
sibling = parent->right;
|
||||
}
|
||||
}
|
||||
|
||||
/* if all of the sibling nodes are black and parent is red, we can
|
||||
// recolour the tree so that the imbalance in black paths is fixed
|
||||
// Example:
|
||||
// R B
|
||||
// / \ / \
|
||||
// B B -> B R
|
||||
// / \ / \
|
||||
// B B B B
|
||||
*/
|
||||
if (RBTREE_NODE_GET_COLOUR(parent) == RBTREE_COLOUR_RED &&
|
||||
RBTREE_NODE_GET_COLOUR(sibling) == RBTREE_COLOUR_BLACK &&
|
||||
RBTREE_NODE_GET_COLOUR(sibling->left) == RBTREE_COLOUR_BLACK &&
|
||||
RBTREE_NODE_GET_COLOUR(sibling->right) == RBTREE_COLOUR_BLACK
|
||||
) {
|
||||
RBTREE_NODE_SET_COLOUR(parent, RBTREE_COLOUR_BLACK);
|
||||
RBTREE_NODE_SET_COLOUR(sibling, RBTREE_COLOUR_RED);
|
||||
goto do_remove;
|
||||
}
|
||||
|
||||
u80211_rbtree_t *close_nephew, *far_nephew;
|
||||
if (parent->right == sibling) {
|
||||
close_nephew = sibling->left;
|
||||
far_nephew = sibling->right;
|
||||
} else {
|
||||
close_nephew = sibling->right;
|
||||
far_nephew = sibling->left;
|
||||
}
|
||||
|
||||
/* if the nearest nephew is red and the farthest is black, rotate it into the new sibling
|
||||
// and exchange the colour between it and the old sibling
|
||||
// example:
|
||||
//
|
||||
// P P | X X
|
||||
// / \ / \ | / \ / \
|
||||
// N S N C | B R B B
|
||||
// / \ -> \ | \ -> \
|
||||
// C F S | B R
|
||||
// \ | \ \
|
||||
// F | B B
|
||||
*/
|
||||
if (RBTREE_NODE_GET_COLOUR(close_nephew) == RBTREE_COLOUR_RED && RBTREE_NODE_GET_COLOUR(far_nephew) == RBTREE_COLOUR_BLACK) {
|
||||
// do rotation
|
||||
if (close_nephew == sibling->left)
|
||||
rotate_right(rbtreep, close_nephew);
|
||||
else
|
||||
rotate_left(rbtreep, close_nephew);
|
||||
|
||||
// update for next step
|
||||
far_nephew = sibling;
|
||||
sibling = close_nephew;
|
||||
|
||||
// recolour
|
||||
RBTREE_NODE_SET_COLOUR(far_nephew, RBTREE_COLOUR_RED);
|
||||
RBTREE_NODE_SET_COLOUR(sibling, RBTREE_COLOUR_BLACK);
|
||||
}
|
||||
|
||||
/* in this case, the closest nephew is black and the distant nephew is red.
|
||||
// there is no need to check for this, as the result of the last operation, if it happened, guarantees it.
|
||||
// we need to rotate such that the sibling becomes the new grandparent and recolour such that:
|
||||
// sibling -> parent's colour
|
||||
// parent -> black
|
||||
// farthest nephew -> black
|
||||
// example:
|
||||
// P S | B X
|
||||
// / \ / \ | / \ / \
|
||||
// N S -> P F | X R -> B B
|
||||
// / \ / \ | / \ / \
|
||||
// C F N C | B B B B
|
||||
*/
|
||||
|
||||
if (parent->right == sibling)
|
||||
rotate_left(rbtreep, sibling);
|
||||
else
|
||||
rotate_right(rbtreep, sibling);
|
||||
|
||||
RBTREE_NODE_SET_COLOUR(sibling, RBTREE_NODE_GET_COLOUR(parent));
|
||||
RBTREE_NODE_SET_COLOUR(parent, RBTREE_COLOUR_BLACK);
|
||||
RBTREE_NODE_SET_COLOUR(far_nephew, RBTREE_COLOUR_BLACK);
|
||||
|
||||
do_remove:
|
||||
// do the actual removal of the node
|
||||
parent = RBTREE_NODE_GET_PARENT(og_node);
|
||||
if (parent->left == og_node)
|
||||
parent->left = NULL;
|
||||
else
|
||||
parent->right = NULL;
|
||||
}
|
||||
|
||||
void u80211_rbtree_remove(u80211_rbtree_t **rbtreep, u80211_rbtree_t *node) {
|
||||
if (node->left == NULL && node->right == NULL) {
|
||||
// leaf node?
|
||||
u80211_rbtree_remove_leaf(rbtreep, node);
|
||||
return;
|
||||
} else if (!(node->left && node->right)) {
|
||||
// has one child
|
||||
// replace it with the child and colour the child black
|
||||
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
u80211_rbtree_t *child = node->left ? node->left : node->right;
|
||||
if (parent == NULL)
|
||||
*rbtreep = child;
|
||||
else if (parent->left == node)
|
||||
parent->left = child;
|
||||
else
|
||||
parent->right = child;
|
||||
|
||||
child->parent = RBTREE_CONSTRUCT_PARENT(parent, RBTREE_COLOUR_BLACK);
|
||||
return;
|
||||
}
|
||||
|
||||
// two children
|
||||
// swap itself with its successor and delete it again
|
||||
// doing this guarantees that it will only have, at most, one child when deleted again
|
||||
u80211_rbtree_t *succ = u80211_rbtree_successor(node);
|
||||
u80211_rbtree_t *succ_parent = RBTREE_NODE_GET_PARENT(succ);
|
||||
u80211_rbtree_t *parent = RBTREE_NODE_GET_PARENT(node);
|
||||
|
||||
// set the successor in place
|
||||
if (parent == NULL) {
|
||||
// root
|
||||
*rbtreep = succ;
|
||||
} else if (parent->left == node) {
|
||||
parent->left = succ;
|
||||
} else {
|
||||
parent->right = succ;
|
||||
}
|
||||
|
||||
int succ_colour = RBTREE_NODE_GET_COLOUR(succ);
|
||||
succ->parent = node->parent; // copy both the parent and colour
|
||||
succ->left = node->left;
|
||||
u80211_rbtree_t *old_succ_right = succ->right;
|
||||
succ->right = node->right;
|
||||
|
||||
RBTREE_NODE_SET_PARENT(succ->left, succ);
|
||||
if (succ_parent != node)
|
||||
RBTREE_NODE_SET_PARENT(succ->right, succ);
|
||||
|
||||
// set the node in place
|
||||
if (succ_parent == node) {
|
||||
succ->right = node;
|
||||
node->parent = RBTREE_CONSTRUCT_PARENT(succ, succ_colour);
|
||||
} else {
|
||||
succ_parent->left = node;
|
||||
node->parent = RBTREE_CONSTRUCT_PARENT(succ_parent, succ_colour);
|
||||
}
|
||||
|
||||
node->left = NULL; // sucessor has no left
|
||||
node->right = old_succ_right;
|
||||
if (node->right)
|
||||
RBTREE_NODE_SET_PARENT(node->right, node);
|
||||
|
||||
// remove the node. this will not recurse again as the node will have only one child
|
||||
u80211_rbtree_remove(rbtreep, node);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <u80211/regulatory_database.h>
|
||||
#include <u80211/status.h>
|
||||
|
||||
#define CHANNEL_COUNT 14
|
||||
static u80211_channel_rules_t channels[CHANNEL_COUNT] = {
|
||||
{0, 20}, // ch1
|
||||
{0, 20}, // ch2
|
||||
{0, 20}, // ch3
|
||||
{0, 20}, // ch4
|
||||
{0, 20}, // ch5
|
||||
{0, 20}, // ch6
|
||||
{0, 20}, // ch7
|
||||
{0, 20}, // ch8
|
||||
{0, 20}, // ch9
|
||||
{0, 20}, // ch10
|
||||
{0, 20}, // ch11
|
||||
{U80211_CHANNEL_RULES_DISABLED, 0}, // ch12
|
||||
{U80211_CHANNEL_RULES_DISABLED, 0}, // ch13
|
||||
{U80211_CHANNEL_RULES_DISABLED, 0}, // ch14
|
||||
};
|
||||
|
||||
u80211_channel_rules_t u80211_get_channel_rules(int channel) {
|
||||
if (channel > CHANNEL_COUNT) {
|
||||
const u80211_channel_rules_t disabled_channel = {U80211_CHANNEL_RULES_DISABLED, 0};
|
||||
return disabled_channel;
|
||||
}
|
||||
|
||||
return channels[channel - 1];
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/ringbuffer.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/util.h>
|
||||
|
||||
int u80211_ringbuffer_init(u80211_ringbuffer_t *ringbuffer, size_t size) {
|
||||
ringbuffer->size = 0;
|
||||
ringbuffer->write = 0;
|
||||
ringbuffer->read = 0;
|
||||
ringbuffer->data = NULL;
|
||||
|
||||
if (size == 0)
|
||||
return U80211_STATUS_NOT_ENOUGH_SPACE;
|
||||
|
||||
ringbuffer->data = u80211_kernel_allocate(size);
|
||||
if (ringbuffer->data == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
ringbuffer->size = size;
|
||||
ringbuffer->write = 0;
|
||||
ringbuffer->read = 0;
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void u80211_ringbuffer_destroy(u80211_ringbuffer_t *ringbuffer) {
|
||||
if (ringbuffer->data != NULL)
|
||||
u80211_kernel_free(ringbuffer->data);
|
||||
ringbuffer->size = 0;
|
||||
ringbuffer->write = 0;
|
||||
ringbuffer->read = 0;
|
||||
ringbuffer->data = NULL;
|
||||
}
|
||||
|
||||
size_t u80211_ringbuffer_truncate(u80211_ringbuffer_t *ringbuffer, size_t count) {
|
||||
size_t true_count = min(U80211_RINGBUFFER_DATA_COUNT(ringbuffer), count);
|
||||
ringbuffer->read += true_count;
|
||||
return true_count;
|
||||
}
|
||||
|
||||
size_t u80211_ringbuffer_read(u80211_ringbuffer_t *ringbuffer, void *buffer, size_t count) {
|
||||
count = min(count, U80211_RINGBUFFER_DATA_COUNT(ringbuffer));
|
||||
size_t first_pass_offset = ringbuffer->read % ringbuffer->size;
|
||||
size_t first_pass_remaining = ringbuffer->size - first_pass_offset;
|
||||
size_t first_pass_count = min(count, first_pass_remaining);
|
||||
|
||||
u80211_memcpy(buffer, (const void *)((uintptr_t)ringbuffer->data + first_pass_offset), first_pass_count);
|
||||
if (first_pass_count != count)
|
||||
u80211_memcpy((void *)((uintptr_t)buffer + first_pass_count), ringbuffer->data, count - first_pass_count);
|
||||
|
||||
ringbuffer->read += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t u80211_ringbuffer_peek(const u80211_ringbuffer_t *ringbuffer, void *buffer, uintmax_t offset, size_t count) {
|
||||
size_t data_count = U80211_RINGBUFFER_DATA_COUNT(ringbuffer);
|
||||
if (offset >= data_count)
|
||||
return 0;
|
||||
|
||||
uintmax_t read = ringbuffer->read + offset;
|
||||
count = min(count, data_count - offset);
|
||||
size_t first_pass_offset = read % ringbuffer->size;
|
||||
size_t first_pass_remaining = ringbuffer->size - first_pass_offset;
|
||||
size_t first_pass_count = min(count, first_pass_remaining);
|
||||
|
||||
u80211_memcpy(buffer, (const void *)((uintptr_t)ringbuffer->data + first_pass_offset), first_pass_count);
|
||||
if (first_pass_count != count)
|
||||
u80211_memcpy((void *)((uintptr_t)buffer + first_pass_count), ringbuffer->data, count - first_pass_count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t u80211_ringbuffer_write(u80211_ringbuffer_t *ringbuffer, const void *buffer, size_t count) {
|
||||
count = min(count, U80211_RINGBUFFER_FREE_SPACE(ringbuffer));
|
||||
size_t first_pass_offset = ringbuffer->write % ringbuffer->size;
|
||||
size_t first_pass_remaining = ringbuffer->size - first_pass_offset;
|
||||
size_t first_pass_count = min(count, first_pass_remaining);
|
||||
|
||||
u80211_memcpy((void *)((uintptr_t)ringbuffer->data + first_pass_offset), buffer, first_pass_count);
|
||||
if (first_pass_count != count)
|
||||
u80211_memcpy(ringbuffer->data, (const void *)((uintptr_t)buffer + first_pass_count), count - first_pass_count);
|
||||
|
||||
ringbuffer->write += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t u80211_ringbuffer_remove(u80211_ringbuffer_t *ringbuffer, size_t count) {
|
||||
size_t true_count = min(U80211_RINGBUFFER_DATA_COUNT(ringbuffer), count);
|
||||
ringbuffer->write -= true_count;
|
||||
return true_count;
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
#include <u80211/u80211.h>
|
||||
#include <u80211/packet.h>
|
||||
#include <u80211/kernel_interface.h>
|
||||
#include <u80211/bss_cache.h>
|
||||
#include <u80211/regulatory_database.h>
|
||||
#include <u80211/ringbuffer.h>
|
||||
#include <u80211/scan.h>
|
||||
#include <u80211/status.h>
|
||||
#include <u80211/string.h>
|
||||
#include <u80211/util.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define WAIT_MS 75
|
||||
#define BUFFER_COUNT 64
|
||||
#define MAX_RSN_SIZE UINT8_MAX
|
||||
#define BUFFER_SIZE (BUFFER_COUNT * (sizeof(u80211_beacon_data_t) + MAX_RSN_SIZE))
|
||||
|
||||
typedef struct {
|
||||
u80211_ringbuffer_t buffer;
|
||||
uint8_t rsn[MAX_RSN_SIZE]; // use this as a small scratch space
|
||||
u80211_device_t *device;
|
||||
bool receiving;
|
||||
int current_channel;
|
||||
void *work;
|
||||
} u80211_scan_state_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_list_node_t node;
|
||||
void *semaphore;
|
||||
} u80211_scan_waiter_t;
|
||||
|
||||
static void wake_scan_waiters(u80211_device_t *device) {
|
||||
u80211_list_node_t *node;
|
||||
while ((node = u80211_list_pop_front(&device->scan_waiters)) != NULL) {
|
||||
u80211_scan_waiter_t *waiter = container_of(node, u80211_scan_waiter_t, node);
|
||||
u80211_kernel_signal_semaphore(waiter->semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_scan_state(u80211_scan_state_t *scan_state) {
|
||||
u80211_kernel_free_work(scan_state->work);
|
||||
u80211_ringbuffer_destroy(&scan_state->buffer);
|
||||
u80211_kernel_free(scan_state);
|
||||
}
|
||||
|
||||
static void complete_scan(u80211_scan_state_t *scan_state) {
|
||||
u80211_device_t *device = scan_state->device;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
|
||||
u80211_set_device_state(device, U80211_DEVICE_STATE_SCANNING, U80211_DEVICE_STATE_DOWN);
|
||||
wake_scan_waiters(device);
|
||||
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
destroy_scan_state(scan_state);
|
||||
}
|
||||
|
||||
void u80211_scan_process_response(u80211_device_t *device, u80211_beacon_data_t *beacon_data) {
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
u80211_scan_state_t *scan_state = device->scan_context;
|
||||
|
||||
if (scan_state == NULL || !scan_state->receiving || beacon_data->rsn_size > MAX_RSN_SIZE) {
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t record_size = sizeof(*beacon_data) + beacon_data->rsn_size;
|
||||
if (U80211_RINGBUFFER_FREE_SPACE(&scan_state->buffer) < record_size) {
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (beacon_data->channel == 0)
|
||||
beacon_data->channel = scan_state->current_channel;
|
||||
|
||||
u80211_ringbuffer_write(&scan_state->buffer, beacon_data, sizeof(*beacon_data));
|
||||
u80211_ringbuffer_write(&scan_state->buffer, beacon_data->rsn, beacon_data->rsn_size);
|
||||
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
}
|
||||
|
||||
static bool valid_bssid(const u80211_mac_address_t *mac_address) {
|
||||
bool all_zero = true;
|
||||
bool all_broadcast = true;
|
||||
for (size_t i = 0; i < sizeof(mac_address->bytes); ++i) {
|
||||
if (mac_address->bytes[i] != 0)
|
||||
all_zero = false;
|
||||
if (mac_address->bytes[i] != 0xff)
|
||||
all_broadcast = false;
|
||||
}
|
||||
|
||||
return !all_zero && !all_broadcast && !(mac_address->bytes[0] & 1);
|
||||
}
|
||||
|
||||
// TODO: this obviously won't work for anything other than 2.4ghz wifi
|
||||
static int next_channel(int current) {
|
||||
for (int channel = current + 1; channel <= 14; ++channel) {
|
||||
if (u80211_get_channel_rules(channel).flags & (U80211_CHANNEL_RULES_DISABLED | U80211_CHANNEL_RULES_PASSIVE))
|
||||
continue;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void scan_work(void *ctx) {
|
||||
u80211_scan_state_t *scan_state = ctx;
|
||||
u80211_device_t *device = scan_state->device;
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
scan_state->receiving = false;
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
while (U80211_RINGBUFFER_DATA_COUNT(&scan_state->buffer) >= sizeof(u80211_beacon_data_t)) {
|
||||
u80211_beacon_data_t beacon_data;
|
||||
if (u80211_ringbuffer_read(&scan_state->buffer, &beacon_data, sizeof(beacon_data)) != sizeof(beacon_data))
|
||||
break;
|
||||
|
||||
if (u80211_ringbuffer_read(&scan_state->buffer, scan_state->rsn, beacon_data.rsn_size) != beacon_data.rsn_size)
|
||||
break;
|
||||
beacon_data.rsn = beacon_data.rsn_size == 0 ? NULL : scan_state->rsn;
|
||||
|
||||
if (beacon_data.channel != scan_state->current_channel || !valid_bssid(&beacon_data.mac_address))
|
||||
continue;
|
||||
|
||||
u80211_ap_t *ap = u80211_ap_allocate(&beacon_data);
|
||||
if (ap == NULL)
|
||||
continue;
|
||||
|
||||
u80211_bss_cache_insert(&device->bss_cache, ap);
|
||||
u80211_ap_release(ap);
|
||||
}
|
||||
|
||||
int current_channel = next_channel(scan_state->current_channel);
|
||||
if (current_channel < 0) {
|
||||
complete_scan(scan_state);
|
||||
return;
|
||||
}
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
scan_state->current_channel = current_channel;
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
device->ops->set_channel(device, scan_state->current_channel);
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
scan_state->receiving = true;
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
u80211_send_probe_request(device);
|
||||
|
||||
u80211_kernel_enqueue_work(scan_state->work, scan_work, scan_state, WAIT_MS);
|
||||
}
|
||||
|
||||
int u80211_scan(u80211_device_t *device) {
|
||||
u80211_scan_state_t *scan_state = u80211_kernel_allocate(sizeof(u80211_scan_state_t));
|
||||
if (scan_state == NULL)
|
||||
return U80211_STATUS_ENOMEM;
|
||||
|
||||
int status = u80211_ringbuffer_init(&scan_state->buffer, BUFFER_SIZE);
|
||||
if (status != U80211_STATUS_SUCCESS) {
|
||||
u80211_kernel_free(scan_state);
|
||||
return status;
|
||||
}
|
||||
|
||||
scan_state->work = u80211_kernel_allocate_work();
|
||||
if (scan_state->work == NULL) {
|
||||
u80211_ringbuffer_destroy(&scan_state->buffer);
|
||||
u80211_kernel_free(scan_state);
|
||||
return U80211_STATUS_ENOMEM;
|
||||
}
|
||||
|
||||
scan_state->receiving = false;
|
||||
scan_state->device = device;
|
||||
scan_state->current_channel = next_channel(0);
|
||||
if (scan_state->current_channel < 0) {
|
||||
destroy_scan_state(scan_state);
|
||||
return U80211_STATUS_NOT_PERMITTED;
|
||||
}
|
||||
|
||||
if (!u80211_set_device_state(device, U80211_DEVICE_STATE_DOWN, U80211_DEVICE_STATE_SCANNING)) {
|
||||
destroy_scan_state(scan_state);
|
||||
return U80211_STATUS_BUSY;
|
||||
}
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
device->scan_context = scan_state;
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
u80211_bss_cache_purge(&device->bss_cache);
|
||||
|
||||
device->ops->set_channel(device, scan_state->current_channel);
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
scan_state->receiving = true;
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
u80211_send_probe_request(device);
|
||||
|
||||
u80211_kernel_enqueue_work(scan_state->work, scan_work, scan_state, WAIT_MS);
|
||||
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_wait_for_scan_completion(u80211_device_t *device) {
|
||||
void *semaphore = u80211_kernel_allocate_semaphore(0);
|
||||
if (semaphore == NULL)
|
||||
return U80211_STATUS_RETRY;
|
||||
|
||||
u80211_scan_waiter_t waiter = {
|
||||
.semaphore = semaphore,
|
||||
};
|
||||
|
||||
u80211_kernel_acquire_spinlock(device->scan_spinlock);
|
||||
if (u80211_get_device_state(device) != U80211_DEVICE_STATE_SCANNING) {
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
u80211_kernel_free_semaphore(semaphore);
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
u80211_list_push_back(&device->scan_waiters, &waiter.node);
|
||||
u80211_kernel_release_spinlock(device->scan_spinlock);
|
||||
|
||||
u80211_kernel_wait_semaphore(semaphore);
|
||||
u80211_kernel_free_semaphore(semaphore);
|
||||
return U80211_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <u80211/string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void *u80211_memcpy(void *destination, const void *source, size_t size) {
|
||||
uint8_t *destination_bytes = destination;
|
||||
const uint8_t *source_bytes = source;
|
||||
|
||||
for (size_t i = 0; i < size; i++)
|
||||
destination_bytes[i] = source_bytes[i];
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
int u80211_memcmp(const void *a, const void *b, size_t size) {
|
||||
const uint8_t *a_bytes = a;
|
||||
const uint8_t *b_bytes = b;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (a_bytes[i] != b_bytes[i])
|
||||
return a_bytes[i] < b_bytes[i] ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *u80211_memset(void *destination, int value, size_t size) {
|
||||
uint8_t *destination_bytes = destination;
|
||||
|
||||
for (size_t i = 0; i < size; i++)
|
||||
destination_bytes[i] = (uint8_t)value;
|
||||
|
||||
return destination;
|
||||
}
|
||||
Reference in New Issue
Block a user