hak5-wifi-coconut/libwifiuserspace/userspace/userspace.c

836 lines
23 KiB
C

/*
* GPL-2.0-or-later
*
* Userspace port (C) 2019 Hak5 Inc
*
*/
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#else
#include <windows.h>
#define usleep(x) Sleep((x) < 1000 ? 1 : (x) / 1000)
#define sleep(x) Sleep(x * 1000)
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#include "userspace.h"
#include "rt2800usb/rt2800usb.h"
#if defined(_WIN32) && !defined(__MINGW32__)
/*
* Kluge a windows time into a user time
*/
int gettimeofday(struct timeval* tp, struct timezone* tzp) {
static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
time = ((uint64_t)file_time.dwLowDateTime);
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long)((time - EPOCH) / 10000000L);
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
return 0;
}
#define timercmp(a, b, CMP) \
(((a)->tv_sec == (b)->tv_sec) ? \
((a)->tv_usec CMP (b)->tv_usec) : \
((a)->tv_sec CMP (b)->tv_sec))
#endif
#ifdef _WIN32
#define timeradd(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
if ((result)->tv_usec >= 1000000) \
{ \
++(result)->tv_sec; \
(result)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
void *userspace_wifi_service_thread(void *ctx) {
struct userspace_wifi_context *context = (struct userspace_wifi_context *) ctx;
int r;
while (context->service_thread_enabled) {
r = libusb_handle_events_completed(context->libusb_context, NULL);
if (r != 0) {
userspace_wifi_error(context, NULL, r, "Failed to handle USB events: %s",
libusb_error_name(r));
}
}
return NULL;
}
void *userspace_wifi_cmd_service_thread(void *ctx) {
struct userspace_wifi_context *context = (struct userspace_wifi_context *) ctx;
struct userspace_wifi_command *command;
while (context->cmd_thread_enabled) {
pthread_mutex_lock(&context->cmd_wakeup_mutex);
pthread_cond_wait(&context->cmd_wakeup_cond, &context->cmd_wakeup_mutex);
while (1) {
pthread_mutex_lock(&context->cmd_mutex);
command = context->cmd_queue;
if (command != NULL) {
context->cmd_queue = command->next;
if (context->cmd_queue == NULL)
context->cmd_queue_last = NULL;
/* We must unlock BEFORE calling the callback */
pthread_mutex_unlock(&context->cmd_mutex);
(command->callback)(context, command->device, command->param);
free(command);
continue;
}
pthread_mutex_unlock(&context->cmd_mutex);
break;
}
pthread_mutex_unlock(&context->cmd_wakeup_mutex);
}
return NULL;
}
#ifdef __APPLE__
/* Find the last instance of the / in the path and slice everything
* after it off by chopping with a \0. Modifies the passed path. */
void chop_after_last_slash(char *path) {
char *last = path;
char *pos = NULL;
while ((pos = strstr(last + 1, "/")) != NULL) {
last = pos;
}
last[0] = 0;
}
#endif
/*
* Standard firmware loader which looks in the specified firmware dir (if any), various
* system directories, and the libwifiuserspace share directory
*/
int userspace_load_firmware_file(const struct userspace_wifi_context *context, const char *file_name,
const char **file_hints, size_t hints_len,
uint8_t **firmware_blob, size_t *blob_len) {
FILE *fwfile = NULL;
int fwfd = -1;
struct stat statbuf;
char fw_path[2048];
size_t i;
#ifdef __APPLE__
char exe_path[2048];
uint32_t exe_path_sz = 2048;
#endif
/* Start by looking at the context dir, if any */
if (context->firmware_directory != NULL) {
snprintf(fw_path, 2048, "%s/%s", context->firmware_directory, file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
/* Look at all the hints */
for (i = 0; i < hints_len; i++) {
snprintf(fw_path, 2048, "%s/%s/%s", context->firmware_directory, file_hints[i], file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
}
}
/* Look at the current directory */
snprintf(fw_path, 2048, "%s", file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
/* Look at all the hints */
for (i = 0; i < hints_len; i++) {
snprintf(fw_path, 2048, "%s/%s", file_hints[i], file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
}
/* Look at the share firmware dir */
snprintf(fw_path, 2048, "%s/%s", FIRMWAREDIR, file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
/* Look at all the hints */
for (i = 0; i < hints_len; i++) {
snprintf(fw_path, 2048, "%s/%s/%s", FIRMWAREDIR, file_hints[i], file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
}
/* Look at the stock linux firmware dir */
snprintf(fw_path, 2048, "%s/%s", "/lib/firmware/", file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
/* Look at all the hints */
for (i = 0; i < hints_len; i++) {
snprintf(fw_path, 2048, "%s/%s/%s", "/lib/firmware/", file_hints[i], file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
}
/* Look in our path in case we're on OSX and the firmware is in our framework */
#ifdef __APPLE__
if (_NSGetExecutablePath(exe_path, &exe_path_sz) == 0) {
chop_after_last_slash(exe_path);
snprintf(fw_path, 2048, "%s/%s/%s", exe_path, "../Resources/firmware/", file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
/* Look at all the hints */
for (i = 0; i < hints_len; i++) {
snprintf(fw_path, 2048, "%s/%s/%s/%s", exe_path, "../Resources/firmware/", file_hints[i], file_name);
if ((fwfile = fopen(fw_path, "rb")) != NULL)
goto got_file;
}
}
#endif
goto no_file;
got_file:
fwfd = fileno(fwfile);
if (fstat(fwfd, &statbuf) != 0) {
fclose(fwfile);
userspace_wifi_error(context, NULL, -ENOENT, "Could not find firmware file '%s'",
file_name);
return -ENOENT;
}
#ifndef _WIN32
if (!S_ISREG(statbuf.st_mode)) {
fclose(fwfile);
userspace_wifi_error(context, NULL, -ENOENT, "Firmware file '%s' not a normal file",
file_name);
return -ENOENT;
}
#endif
(*firmware_blob) = (uint8_t *) malloc(statbuf.st_size);
if ((*firmware_blob) == NULL) {
fclose(fwfile);
goto no_mem;
}
*blob_len = statbuf.st_size;
fread((*firmware_blob), *blob_len, 1, fwfile);
fclose(fwfile);
return 0;
no_file:
userspace_wifi_error(context, NULL, -ENOENT, "Could not find firmware file '%s' in "
"any of the standard locations. Make sure you've installed the required "
"firmware files.", file_name);
return -ENOENT;
no_mem:
userspace_wifi_error(context, NULL, -ENOENT, "Could not allocate memory to load "
"firmware file '%s'", file_name);
return -ENOMEM;
}
int userspace_wifi_init(struct userspace_wifi_context **context) {
/* int status; */
*context = (struct userspace_wifi_context *) malloc(sizeof(struct userspace_wifi_context));
if (*context == NULL)
return -1;
memset(*context, 0, sizeof(struct userspace_wifi_context));
(*context)->devs = NULL;
(*context)->devs_cnt = 0;
(*context)->service_device_count = 0;
(*context)->load_firmware_file = &userspace_load_firmware_file;
/*
status = libusb_init(&(*context)->libusb_context);
if (status < 0)
return status;
*/
pthread_mutex_init(&(*context)->libusb_mutex, NULL);
(*context)->cmd_thread_enabled = true;
pthread_mutex_init(&(*context)->cmd_mutex, NULL);
pthread_mutex_init(&(*context)->cmd_wakeup_mutex, NULL);
pthread_cond_init(&(*context)->cmd_wakeup_cond, NULL);
pthread_create(&(*context)->cmd_thread, NULL, userspace_wifi_cmd_service_thread, (*context));
pthread_mutex_init(&(*context)->led_ts_mutex, NULL);
pthread_mutex_init(&(*context)->led_cond_mutex, NULL);
pthread_cond_init(&(*context)->led_cond, NULL);
(*context)->led_devs = NULL;
(*context)->led_thread_enable = false;
return 0;
}
void userspace_wifi_free(struct userspace_wifi_context *context) {
if (context != NULL) {
context->service_thread_enabled = false;
if (context->devs != NULL) {
libusb_free_device_list(context->devs, 1);
}
if (context->libusb_context != NULL)
libusb_exit(context->libusb_context);
if (context->service_thread_active) {
pthread_join(context->async_service_thread, NULL);
}
free(context);
}
}
int userspace_wifi_probe(struct userspace_wifi_context *context,
struct userspace_wifi_probe_dev **devices) {
struct userspace_wifi_probe_dev *probe_dev;
int i, ret;
struct libusb_device_descriptor desc;
struct libusb_device_handle *handle;
int probed_count = 0;
if (context->devs != NULL)
libusb_free_device_list(context->devs, 1);
if (context->libusb_context != NULL)
libusb_exit(context->libusb_context);
libusb_init(&context->libusb_context);
context->devs = NULL;
*devices = NULL;
context->devs_cnt = libusb_get_device_list(context->libusb_context, &(context->devs));
if (context->devs_cnt <= 0)
return context->devs_cnt;
/*
* Scan via each of the userspace drivers
*/
for (i = 0; context->devs[i]; ++i) {
ret = libusb_get_device_descriptor(context->devs[i], &desc);
if (ret != 0)
continue;
ret = libusb_open(context->devs[i], &handle);
if (ret != 0)
continue;
/*
* For each supported driver, probe until we get a match.
* For each match, add it to our count and our linked list
*/
if (rt2800usb_probe_device(&desc, &probe_dev) > 0) {
probe_dev->context = context;
probe_dev->dev = context->devs[i];
/*
* Collect as much info about the device as we can, we need it to
* identify grouped devices for some platforms
*/
probe_dev->usb_bus = libusb_get_bus_number(context->devs[i]);
probe_dev->usb_port = libusb_get_port_number(context->devs[i]);
probe_dev->usb_device = libusb_get_device_address(context->devs[i]);
ret = libusb_get_port_numbers(context->devs[i], probe_dev->usb_bus_path, 8);
if (ret > 0) {
probe_dev->usb_bus_path_len = ret;
}
if (desc.iSerialNumber) {
libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
probe_dev->usb_serial, 64);
}
probe_dev->next = *devices;
*devices = probe_dev;
probe_dev = NULL;
probed_count++;
}
libusb_close(handle);
}
return probed_count;
}
void userspace_wifi_free_probe(struct userspace_wifi_probe_dev *devices) {
struct userspace_wifi_probe_dev *next;
while (devices != NULL) {
next = devices->next;
if (devices->driver_name)
free(devices->driver_name);
if (devices->device_type)
free(devices->device_type);
free(devices);
devices = next;
}
}
void userspace_wifi_for_each_probe(struct userspace_wifi_context *context,
struct userspace_wifi_probe_dev *devices,
int (*cb)(struct userspace_wifi_context *, struct userspace_wifi_probe_dev *)) {
struct userspace_wifi_probe_dev *next = devices;
int r;
while (next != NULL) {
r = (*cb)(context, next);
if (r)
return;
next = next->next;
}
}
void userspace_wifi_handle_usbio(struct userspace_wifi_context *context) {
context->service_thread_enabled = true;
int r;
while (context->service_thread_enabled) {
r = libusb_handle_events_completed(context->libusb_context, NULL);
if (r < 0) {
userspace_wifi_error(context, NULL, r, "Handling USB IO queue failed: %s",
libusb_error_name(r));
break;
}
}
}
int userspace_wifi_device_open(struct userspace_wifi_context *context,
struct userspace_wifi_probe_dev *probedevice,
struct userspace_wifi_dev **device) {
int r;
if (!context->service_thread_enabled) {
context->service_thread_enabled = true;
context->service_thread_active = true;
pthread_create(&context->async_service_thread, NULL, userspace_wifi_service_thread, context);
}
r = (*(probedevice->open_device))(probedevice, device);
if (r < 0) {
userspace_wifi_error(context, *device, r, "Opening device failed");
return r;
}
#if 0
if (!context->service_thread_active) {
context->service_thread_enabled = true;
context->service_thread_active = true;
context->service_device_count++;
pthread_create(&(context->async_service_thread), NULL, userspace_wifi_service_thread, context);
}
#endif
return r;
}
/*
* Queue work to the end of the worker queue
*/
void userspace_wifi_queue_work(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void (*callback)(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param), void *param) {
struct userspace_wifi_command *cmd =
(struct userspace_wifi_command *) malloc(sizeof(struct userspace_wifi_command));
cmd->next = NULL;
cmd->device = device;
cmd->callback = callback;
cmd->param = param;
pthread_mutex_lock(&context->cmd_mutex);
cmd->id = context->work_id++;
if (context->cmd_queue == NULL) {
context->cmd_queue = cmd;
context->cmd_queue_last = cmd;
} else {
context->cmd_queue_last->next = cmd;
context->cmd_queue_last = cmd;
}
/* Signal the worker thread */
pthread_cond_signal(&context->cmd_wakeup_cond);
pthread_mutex_unlock(&context->cmd_mutex);
}
void _userspace_wifi_start_capture_cb(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param) {
/*
* TODO handle error and backpropagate
*/
device->start_capture(device);
}
int userspace_wifi_device_start_capture(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device) {
userspace_wifi_queue_work(context, device, &_userspace_wifi_start_capture_cb, NULL);
return 0;
}
void _userspace_wifi_stop_capture_cb(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param) {
device->start_capture(device);
}
int userspace_wifi_device_stop_capture(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device) {
userspace_wifi_queue_work(context, device, &_userspace_wifi_stop_capture_cb, NULL);
return 0;
}
struct _userspace_wifi_channel_param {
int channel;
enum nl80211_chan_width width;
};
void _userspace_wifi_channel_cb(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param) {
struct _userspace_wifi_channel_param *ch = (struct _userspace_wifi_channel_param *) param;
int r;
/*
* TODO handle error and backpropagate
*/
r = device->set_channel(device, ch->channel, ch->width);
if (r < 0)
userspace_wifi_error(context, device, r, "Setting channel %d:%d failed: %d / %s",
ch->channel, ch->width, r, strerror(r));
free(ch);
}
int userspace_wifi_device_set_channel(struct userspace_wifi_context *context,
struct userspace_wifi_dev *dev,
int channel, enum nl80211_chan_width width) {
struct _userspace_wifi_channel_param *p =
(struct _userspace_wifi_channel_param *) malloc(sizeof(struct _userspace_wifi_channel_param));
if (p == NULL)
return -ENOMEM;
p->channel = channel;
p->width = width;
userspace_wifi_queue_work(context, dev, &_userspace_wifi_channel_cb, p);
return 0;
}
void _userspace_wifi_led_on(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param) {
device->set_led(device, true);
}
void _userspace_wifi_led_off(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
void *param) {
device->set_led(device, false);
}
int userspace_wifi_device_set_led(struct userspace_wifi_context *context,
struct userspace_wifi_dev *dev, bool enable) {
if (enable)
userspace_wifi_queue_work(context, dev, &_userspace_wifi_led_on, NULL);
else
userspace_wifi_queue_work(context, dev, &_userspace_wifi_led_off, NULL);
return 0;
}
void *_userspace_wifi_led_thread(void *ctx) {
struct userspace_wifi_context *context = (struct userspace_wifi_context *) ctx;
struct userspace_wifi_dev_led *led;
struct timeval now;
struct timeval diff_ts;
struct timeval sleep_ts;
while (context->led_thread_enable) {
sleep_ts.tv_sec = 0;
sleep_ts.tv_usec = 0;
diff_ts.tv_sec = 0;
diff_ts.tv_usec = 0;
pthread_mutex_lock(&context->led_ts_mutex);
gettimeofday(&now, NULL);
led = context->led_devs;
while (led) {
if (led->trigger_ts.tv_sec == 0) {
led = led->next;
continue;
}
/*
* Did we miss the timer?
*/
if (timercmp(&led->trigger_ts, &now, <=)) {
/*
* Zero out the trigger
*/
led->trigger_ts.tv_sec = 0;
userspace_wifi_device_set_led(context, led->dev, led->restore_state);
led = led->next;
continue;
}
/*
* Otherwise we're still waiting; Figure out when to wake up. We want to find
* the most likely next timer.
*/
timersub(&led->trigger_ts, &now, &diff_ts);
if (timercmp(&sleep_ts, &diff_ts, <)) {
sleep_ts.tv_sec = diff_ts.tv_sec;
sleep_ts.tv_usec = diff_ts.tv_usec;
if (sleep_ts.tv_sec > 0) {
printf("something weird in sleep for device %d: %lu secs\n", led->dev->dev_id, sleep_ts.tv_sec);
}
}
led = led->next;
}
pthread_mutex_unlock(&context->led_ts_mutex);
/*
* Sleep the shortest amount of time and try all the timers again without
* waiting for a cond signal
*/
if (sleep_ts.tv_sec != 0 || sleep_ts.tv_usec != 0) {
sleep(sleep_ts.tv_sec);
usleep(sleep_ts.tv_usec);
continue;
}
/*
* Otherwise we've handled all extant timers, sleep the thread until we get a
* conditional kick
*/
pthread_mutex_lock(&context->led_cond_mutex);
pthread_cond_wait(&context->led_cond, &context->led_cond_mutex);
pthread_mutex_unlock(&context->led_cond_mutex);
}
return NULL;
}
void _userspace_wifi_start_led_thread(struct userspace_wifi_context *context) {
context->led_thread_enable = true;
pthread_create(&context->led_thread, NULL, _userspace_wifi_led_thread, context);
pthread_cond_signal(&context->led_cond);
}
void _userspace_wifi_kill_led_thread(struct userspace_wifi_context *context) {
context->led_thread_enable = false;
pthread_cond_signal(&context->led_cond);
pthread_join(context->led_thread, NULL);
}
int userspace_wifi_device_enable_led_control(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device) {
struct userspace_wifi_dev_led *led =
(struct userspace_wifi_dev_led *) malloc(sizeof(struct userspace_wifi_dev_led));
if (led == NULL)
return -ENOMEM;
memset(led, 0, sizeof(struct userspace_wifi_dev_led));
device->led_control = led;
led->dev = device;
led->next = NULL;
pthread_mutex_lock(&context->led_ts_mutex);
led->next = context->led_devs;
context->led_devs = led;
if (!context->led_thread_enable)
_userspace_wifi_start_led_thread(context);
pthread_mutex_unlock(&context->led_ts_mutex);
return 0;
}
void userspace_wifi_device_disable_led_control(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device) {
struct userspace_wifi_dev_led *led = NULL, *removal = NULL;
pthread_mutex_lock(&context->led_ts_mutex);
led = context->led_devs;
if (led == device->led_control) {
context->led_devs = led->next;
removal = led;
} else {
while (led) {
if (led->next == device->led_control) {
led->next = device->led_control->next;
removal = led;
break;
}
led = led->next;
}
}
if (!removal) {
pthread_mutex_unlock(&context->led_ts_mutex);
return;
}
free(removal);
device->led_control = NULL;
if (context->led_devs == NULL)
_userspace_wifi_kill_led_thread(context);
pthread_mutex_unlock(&context->led_ts_mutex);
}
int userspace_wifi_device_blink_led(struct userspace_wifi_context *context,
struct userspace_wifi_dev *device,
unsigned int duration_us, bool restore_state,
bool extend) {
struct timeval add_ts = {
.tv_sec = 0,
.tv_usec = duration_us
};
struct timeval now;
if (device->led_control == NULL)
return -ENODEV;
pthread_mutex_lock(&context->led_ts_mutex);
/*
* Only toggle the LED if we're not in a timer
*/
if (device->led_control->trigger_ts.tv_sec == 0) {
userspace_wifi_device_set_led(context, device, !restore_state);
}
/*
* If we're extending the timer or there is no timer
*/
if (extend || device->led_control->trigger_ts.tv_sec == 0) {
gettimeofday(&now, NULL);
timeradd(&now, &add_ts, &device->led_control->trigger_ts);
device->led_control->restore_state = restore_state;
/*
* Wake up the thread if it isn't already in a processing state
*/
pthread_cond_signal(&context->led_cond);
}
pthread_mutex_unlock(&context->led_ts_mutex);
return 0;
}