Browse Source

Inital commit

pull/1/head
Milan Stute 9 months ago
commit
540cb8b50a
56 changed files with 6460 additions and 0 deletions
  1. +7
    -0
      .gitignore
  2. +6
    -0
      .gitmodules
  3. +19
    -0
      CMakeLists.txt
  4. +105
    -0
      README.md
  5. +51
    -0
      daemon/CMakeLists.txt
  6. +401
    -0
      daemon/core.c
  7. +59
    -0
      daemon/core.h
  8. +14
    -0
      daemon/corewlan.h
  9. +64
    -0
      daemon/corewlan.m
  10. +323
    -0
      daemon/io.c
  11. +37
    -0
      daemon/io.h
  12. +455
    -0
      daemon/netutils.c
  13. +47
    -0
      daemon/netutils.h
  14. +188
    -0
      daemon/owl.c
  15. +1
    -0
      googletest
  16. +1
    -0
      radiotap
  17. BIN
      resources/overview.png
  18. +40
    -0
      src/CMakeLists.txt
  19. +101
    -0
      src/channel.c
  20. +53
    -0
      src/channel.h
  21. +158
    -0
      src/circular_buffer.c
  22. +55
    -0
      src/circular_buffer.h
  23. +57
    -0
      src/crc32.c
  24. +8
    -0
      src/crc32.h
  25. +114
    -0
      src/election.c
  26. +34
    -0
      src/election.h
  27. +65
    -0
      src/frame.c
  28. +209
    -0
      src/frame.h
  29. +297
    -0
      src/hashmap.c
  30. +102
    -0
      src/hashmap.h
  31. +180
    -0
      src/ieee80211.h
  32. +137
    -0
      src/log.c
  33. +50
    -0
      src/log.h
  34. +192
    -0
      src/peers.c
  35. +84
    -0
      src/peers.h
  36. +495
    -0
      src/rx.c
  37. +63
    -0
      src/rx.h
  38. +57
    -0
      src/schedule.c
  39. +51
    -0
      src/schedule.h
  40. +116
    -0
      src/siphash24.c
  41. +15
    -0
      src/siphash24.h
  42. +88
    -0
      src/state.c
  43. +88
    -0
      src/state.h
  44. +53
    -0
      src/sync.c
  45. +33
    -0
      src/sync.h
  46. +383
    -0
      src/tx.c
  47. +56
    -0
      src/tx.h
  48. +37
    -0
      src/version.c
  49. +22
    -0
      src/version.h
  50. +203
    -0
      src/wire.c
  51. +192
    -0
      src/wire.h
  52. +15
    -0
      tests/CMakeLists.txt
  53. +99
    -0
      tests/test_awdl_election.cpp
  54. +104
    -0
      tests/test_awdl_peers.cpp
  55. +62
    -0
      tests/test_awdl_sync.cpp
  56. +514
    -0
      tests/test_wire.cpp

+ 7
- 0
.gitignore View File

@@ -0,0 +1,7 @@
# IDEA
.idea/
cmake-build-debug/
cmake-build-release/

# Build files
build/

+ 6
- 0
.gitmodules View File

@@ -0,0 +1,6 @@
[submodule "googletest"]
path = googletest
url = https://github.com/google/googletest.git
[submodule "radiotap"]
path = radiotap
url = https://github.com/radiotap/radiotap-library.git

+ 19
- 0
CMakeLists.txt View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.6)

project(owl)

if (CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options("-Wall" "-Wextra")
endif ()

add_library(radiotap radiotap/radiotap.c)
target_compile_definitions(radiotap PRIVATE -D_BSD_SOURCE -DRADIOTAP_SUPPORT_OVERRIDES)

add_subdirectory(src)

add_subdirectory(daemon)

option(INSTALL_GTEST "" OFF)
add_subdirectory(googletest)

add_subdirectory(tests)

+ 105
- 0
README.md View File

@@ -0,0 +1,105 @@
# Open Wireless Link

Open Wireless Link (OWL) is an open implementation of the Apple Wireless Direct Link (AWDL) ad hoc protocol for Linux and macOS written in C and part of the [Open Wireless Link project](https://owlink.org).

OWL runs in userspace and makes use of Linux’s Netlink API for Wi-Fi specific operations such as channel switching and to integrate itself in the Linux networking stack by providing a virtual network interface such that existing IPv6-capable programs can use AWDL without modification.


## Disclaimer

OWL is experimental software and is the result of reverse engineering efforts by the [Open Wireless Link](https://owlink.org) project.
Therefore, it does not support all features of AWDL or might be incompatible with future AWDL versions.
OWL is not affiliated with or endorsed by Apple Inc. Use this code at your own risk.


## Requirements

To use OWL, you will need a Wi-Fi card supporting active monitor mode with frame injection. We recommend the Atheros AR9280 chip (IEEE 802.11n) which we used to develop and test this code. (Configurations that do not support active monitor mode, i.e., ACK received frames, might suffer from throughput degradation because the sender will re-transmit each frame up to 7 times as per the IEEE 802.11 standard.)


## Build

The project is build using CMake. To build the project, simply clone this repositry in `<OWLDIR>` and run
```sh
cd <OWLDIR>
git submodule update --init
mkdir build
cd build
cmake ..
make
sudo make install
```

OWL requires `libpcap` for frame injection and reception, `libev` for event handling, and `libnl` (Linux only) for interactions with the system's networking stack which have to be installed on the target system.
On Debian Linux, install all dependencies via
```sh
sudo apt install libpcap-dev libev-dev libnl-3-dev libnl-genl-3-dev libnl-route-3-dev
```


## Use

Simply run

```sh
owl -i <WLAN_IFACE>
```

You may increase the log level with `-v` and `-vv` and daemonize the program with `-D`. For other options, have a look at `daemon/owl.c`.

When started, OWL creates a virtual network interface `awdl0` with a link-local IPv6 address. Discovered AWDL peers are automatically added (and removed) to (and from) the system's neighbor table. Run `ip n` to see a list of all current neighbors.


## Architecture

The following figure shows the core structure of OWL.

![Overview](resources/overview.png)


## Code Structure

We provide a coarse structure of the most important components and files to facilitate navigating the code base.

* `daemon/` Contains the active components that interact with the system.
* `core.{c,h}` Schedules all relevant functions on the event loop.
* `io.{c,h}` Platform-specific functions to send and receive frames.
* `netutils.{c,h}` Platform-specific functions to interact with the system's networking stack.
* `owl.c` Contains `main()` and sets up the `core` based on user arguments.
* `googletest/` The runtime for running the tests.
* `radiotap/` Library for parsing Radiotap headers.
* `src/` Contains platform-independent AWDL code.
* `channel.{c,h}` Utilities for managing the channel sequence.
* `election.{c,h}` Code for running the election process.
* `frame.{c,h}` The corresponding header file contains the definitions of all TLVs.
* `peers.{c,h}` Manages the peer table.
* `rx.{c,h}` Functions for handling a received data and action frames including parsing TLVs.
* `schedule.{c,h}` Functions to determine *when* and *which* frames should be sent.
* `state.{c,h}` Consolidates the AWDL state.
* `sync.{c,h}` Synchronization: mangaging (extended) availability windows.
* `tx.{c,h}` Crafting valid data and action frames ready for transmission.
* `version.{c,h}` Parse and create AWDL version numbers.
* `wire.{c,h}` Mini-library for safely reading and writing primitives types from and to a frame buffer.
* `tests/` The actual test cases for this repository.
* `README.md` This file.


## Current Limitations/TODOs

* OWL uses static election metric and counter values, so it either takes part as a slave (low values) or wins the election (high values). See `AWDL_ELECTION_METRIC_INIT` and `AWDL_ELECTION_COUNTER_INIT` and in `include/election.h`.
* The channel sequence does not adjust itself automatically to current network load and/or other triggers. This would require a better understanding of Apple's implementation. Currently, the channel sequence is fixed when initalizing. See `awdl_chanseq_init_static()` in `src/state.{c,h}`.
* OWL does not allow a concurrent connection to an AP. This means, that when started, the Wi-Fi interface exclusively uses AWDL. To work around this, OWL could create a new monitor interface (instead of making the Wi-Fi interface one) and adjust its channel sequence to include the channel of the AP network.


## Our Papers

* Milan Stute, Sashank Narain, Alex Mariotto, Alexander Heinrich, David Kreitschmann, Guevara Noubir, and Matthias Hollick. **A Billion Open Interfaces for Eve and Mallory: MitM, DoS, and Tracking Attacks on iOS and macOS Through Apple Wireless Direct Link.** *28th USENIX Security Symposium (USENIX Security ’19)*, August 14–16, 2019, Santa Clara, CA, USA. [Link](https://www.usenix.org/conference/usenixsecurity19/presentation/stute)

* Milan Stute, David Kreitschmann, and Matthias Hollick. **One Billion Apples’ Secret Sauce: Recipe for the Apple Wireless Direct Link Ad hoc Protocol.** *The 24th Annual International Conference on Mobile Computing and Networking (MobiCom '18)*, October 29–November 2, 2018, New Delhi, India. ACM. [doi:10.1145/3241539.3241566](https://doi.org/10.1145/3241539.3241566)

* Milan Stute, David Kreitschmann, and Matthias Hollick. **Demo: Linux Goes Apple Picking: Cross-Platform Ad hoc Communication with Apple Wireless Direct Link.** *The 24th Annual International Conference on Mobile Computing and Networking (MobiCom '18)*, October 29–November 2, 2018, New Delhi, India. ACM. [doi:10.1145/3241539.3267716](https://doi.org/10.1145/3241539.3267716)


## Contact

* **Milan Stute** ([email](mailto:mstute@seemoo.tu-darmstadt.de), [web](https://seemoo.de/mstute))

+ 51
- 0
daemon/CMakeLists.txt View File

@@ -0,0 +1,51 @@
set(CMAKE_CXX_STANDARD 11)

include(FindPackageHandleStandardArgs)

set(SOURCES PRIVATE
io.c
io.h
core.c
core.h
netutils.c
netutils.h)

if (APPLE)
list(APPEND SOURCES corewlan.m corewlan.h)
endif ()

add_executable(owl "")

target_sources(owl ${SOURCES} owl.c)

find_library(libpcap_LIBRARY NAMES pcap)
find_path(libpcap_INCLUDE pcap.h)
find_package_handle_standard_args(libpcap DEFAULT_MSG libpcap_LIBRARY libpcap_INCLUDE)
if (NOT libpcap_FOUND)
message(FATAL_ERROR "libpcap not found")
endif ()

find_library(libev_LIBRARY NAMES ev)
find_path(libev_INCLUDE ev.h PATHS /usr/local/include)
find_package_handle_standard_args(libev DEFAULT_MSG libev_LIBRARY libev_INCLUDE)
if (NOT libev_FOUND)
message(FATAL_ERROR "libev not found")
endif ()

if (APPLE)
find_library(FOUNDATION Foundation)
find_library(COREWLAN CoreWLAN)
find_library(SYSTEMCONFIGURATION SystemConfiguration)
endif ()

target_include_directories(owl PRIVATE ${CMAKE_SOURCE_DIR}/src ${libev_INCLUDE})

target_link_libraries(owl awdl ${libpcap_LIBRARY} ${libev_LIBRARY})
if (APPLE)
target_link_libraries(owl ${FOUNDATION} ${COREWLAN} ${SYSTEMCONFIGURATION})
else ()
target_include_directories(owl PRIVATE /usr/include/libnl3)
target_link_libraries(owl nl-3 nl-genl-3 nl-route-3)
endif ()

install(TARGETS owl DESTINATION bin)

+ 401
- 0
daemon/core.c View File

@@ -0,0 +1,401 @@
#include "core.h"
#include "netutils.h"

#include "log.h"
#include "wire.h"
#include "rx.h"
#include "tx.h"
#include "schedule.h"

#include <signal.h>

#ifdef __APPLE__
# define SIGSTATS SIGINFO
#else
# define SIGSTATS SIGUSR1
#endif

#define ETHER_LENGTH 14
#define ETHER_DST_OFFSET 0
#define ETHER_SRC_OFFSET 6
#define ETHER_ETHERTYPE_OFFSET 12

#define POLL_NEW_UNICAST 0x1
#define POLL_NEW_MULTICAST 0x2

static void dump_frame(const char *dump_file, const struct pcap_pkthdr *hdr, const uint8_t *buf) {
if (dump_file) {
/* Make sure file exists because 'pcap_dump_open_append' does NOT create file for you */
fclose(fopen(dump_file, "a+"));
pcap_t *p = pcap_open_dead(DLT_IEEE802_11_RADIO, 65535);
pcap_dumper_t *dumper = pcap_dump_open_append(p, dump_file);
pcap_dump((u_char *) dumper, hdr, buf);
pcap_close(p);
pcap_dump_close(dumper);
}
}

static void ev_timer_rearm(struct ev_loop *loop, ev_timer *timer, double in) {
ev_timer_stop(loop, timer);
ev_timer_set(timer, in, 0.);
ev_timer_start(loop, timer);
}

void wlan_device_ready(struct ev_loop *loop, ev_io *handle, int revents) {
struct daemon_state *state = handle->data;
int cnt = pcap_dispatch(state->io.wlan_handle, 1, &awdl_receive_frame, handle->data);
if (cnt > 0)
ev_feed_event(loop, handle, revents);
}

static int poll_host_device(struct daemon_state *state) {
struct buf *buf = NULL;
while (!state->next && !circular_buf_full(state->tx_queue_multicast)) {
buf = buf_new_owned(ETHER_MAX_LEN);
int len = buf_len(buf);
if (host_recv(&state->io, (uint8_t *) buf_data(buf), &len) < 0) {
goto wire_error;
} else {
bool is_multicast;
struct ether_addr dst;
buf_take(buf, buf_len(buf) - len);
READ_ETHER_ADDR(buf, ETHER_DST_OFFSET, &dst);
is_multicast = dst.ether_addr_octet[0] & 0x01;
if (is_multicast) {
circular_buf_put(state->tx_queue_multicast, buf);
return POLL_NEW_MULTICAST;
} else { /* unicast */
state->next = buf;
return POLL_NEW_UNICAST;
}
}
}
wire_error:
if (buf)
buf_free(buf);
return 0;
}

void host_device_ready(struct ev_loop *loop, ev_io *handle, int revents) {
(void) loop;
(void) revents; /* should always be EV_READ */
struct daemon_state *state = handle->data;

int poll_result = poll_host_device(state); /* fill TX queues */
if (poll_result & POLL_NEW_UNICAST)
awdl_send_unicast(loop, &state->ev_state.tx_timer, 0);
if (poll_result & POLL_NEW_MULTICAST)
awdl_send_multicast(loop, &state->ev_state.tx_mcast_timer, 0);
}

void awdl_receive_frame(uint8_t *user, const struct pcap_pkthdr *hdr, const uint8_t *buf) {
#define MAX_NUM_AMPDU 16 /* TODO lookup this constant from the standard */
struct daemon_state *state = (void *) user;
int result;
const struct buf *frame = buf_new_const(buf, hdr->caplen);
struct buf *data_arr[MAX_NUM_AMPDU];
struct buf **data = &data_arr[0];
result = awdl_rx(frame, &data, &state->awdl_state);
if (result == RX_OK) {
for (struct buf **data_start = &data_arr[0]; data_start < data; data_start++) {
host_send(&state->io, buf_data(*data_start), buf_len(*data_start));
buf_free(*data_start);
}
} else if (result < RX_OK) {
log_warn("unhandled frame (%d)", result);
dump_frame(state->dump, hdr, buf);
state->awdl_state.stats.rx_unknown++;
}
buf_free(frame);
}

int awdl_send_data(const struct buf *buf, const struct io_state *io_state,
struct awdl_state *awdl_state, struct ieee80211_state *ieee80211_state) {
uint8_t awdl_data[65535];
int awdl_data_len;
uint16_t ethertype;
struct ether_addr src, dst;
uint64_t now;
uint16_t period, slot, tu;

READ_BE16(buf, ETHER_ETHERTYPE_OFFSET, &ethertype);
READ_ETHER_ADDR(buf, ETHER_DST_OFFSET, &dst);
READ_ETHER_ADDR(buf, ETHER_SRC_OFFSET, &src);

buf_strip(buf, ETHER_LENGTH);
awdl_data_len = awdl_init_full_data_frame(awdl_data, &src, &dst,
buf_data(buf), buf_len(buf),
awdl_state, ieee80211_state);
now = clock_time_us();
period = awdl_sync_current_eaw(now, &awdl_state->sync) / AWDL_CHANSEQ_LENGTH;
slot = awdl_sync_current_eaw(now, &awdl_state->sync) % AWDL_CHANSEQ_LENGTH;
tu = awdl_sync_next_aw_tu(now, &awdl_state->sync);
log_trace("Send data (len %d) to %s (%u.%u.%u)", awdl_data_len,
ether_ntoa(&dst), period, slot, tu);
awdl_state->stats.tx_data++;
if (wlan_send(io_state, awdl_data, awdl_data_len) < 0)
return TX_FAIL;
return TX_OK;

wire_error:
return TX_FAIL;
}

void awdl_send_action(struct daemon_state *state, enum awdl_action_type type) {
uint8_t buf[65535];
int len;

len = awdl_init_full_action_frame(buf, &state->awdl_state, &state->ieee80211_state, type);
if (len < 0)
return;
log_trace("send %s", awdl_frame_as_str(type));
wlan_send(&state->io, buf, len);

state->awdl_state.stats.tx_action++;
}

void awdl_send_psf(struct ev_loop *loop, ev_timer *handle, int revents) {
(void) loop;
(void) revents;
awdl_send_action(handle->data, AWDL_ACTION_PSF);
}

void awdl_send_mif(struct ev_loop *loop, ev_timer *timer, int revents) {
(void) revents;
struct daemon_state *state = timer->data;
struct awdl_state *awdl_state = &state->awdl_state;
uint64_t now, next_aw, eaw_len;

now = clock_time_us();
next_aw = awdl_sync_next_aw_us(now, &awdl_state->sync);
eaw_len = awdl_state->sync.presence_mode * awdl_state->sync.aw_period;

/* Schedule MIF in middle of sequence (if non-zero) */
if (awdl_chan_num(awdl_state->channel.current, awdl_state->channel.enc) > 0)
awdl_send_action(state, AWDL_ACTION_MIF);

/* schedule next in the middle of EAW */
ev_timer_rearm(loop, timer, usec_to_sec(next_aw + ieee80211_tu_to_usec(eaw_len / 2)));
}

void awdl_send_unicast(struct ev_loop *loop, ev_timer *timer, int revents) {
(void) revents;
struct daemon_state *state = timer->data;
struct awdl_state *awdl_state = &state->awdl_state;
uint64_t now = clock_time_us();

if (state->next) { /* we have something to send */
struct awdl_peer *peer;
struct ether_addr dst;
read_ether_addr(state->next, ETHER_DST_OFFSET, &dst);
if (awdl_peer_get(awdl_state->peers.peers, &dst, &peer) < 0) {
log_debug("Drop frame to non-peer %s", ether_ntoa(&dst));
buf_free(state->next);
state->next = NULL;
} else {
double in = awdl_can_send_unicast_in(awdl_state, peer, now, AWDL_UNICAST_GUARD_TU);
if (in == 0) { /* send now */
awdl_send_data(state->next, &state->io, &state->awdl_state, &state->ieee80211_state);
buf_free(state->next);
state->next = NULL;
state->awdl_state.stats.tx_data_unicast++;
} else { /* try later */
if (in < 0) /* we are at the end of slot but within guard */
in = usec_to_sec(-in + ieee80211_tu_to_usec(AWDL_UNICAST_GUARD_TU));
ev_timer_rearm(loop, timer, in);
}
}
}

if (!state->next)
poll_host_device(state);
/* rearm if more unicast frames available */
if (state->next) {
ev_timer_rearm(loop, timer, 0.);
}
}

void awdl_send_multicast(struct ev_loop *loop, ev_timer *timer, int revents) {
(void) revents;
struct daemon_state *state = timer->data;
struct awdl_state *awdl_state = &state->awdl_state;
uint64_t now = clock_time_us();

if (!circular_buf_empty(state->tx_queue_multicast)) { /* we have something to send */
double in = awdl_can_send_in(awdl_state, now, AWDL_MULTICAST_GUARD_TU);
if (awdl_is_multicast_eaw(awdl_state, now) && (in == 0)) { /* we can send now */
void *next;
circular_buf_get(state->tx_queue_multicast, &next, 0);
awdl_send_data((struct buf *) next, &state->io, &state->awdl_state, &state->ieee80211_state);
buf_free(next);
state->awdl_state.stats.tx_data_multicast++;
} else { /* try later */
if (in < 0) /* we are at the end of slot but within guard */
in = usec_to_sec(-in + ieee80211_tu_to_usec(AWDL_MULTICAST_GUARD_TU));
ev_timer_rearm(loop, timer, in);
}
}
if (circular_buf_empty(state->tx_queue_multicast))
/* poll for more frames to keep queue full */
poll_host_device(state);

/* rearm if more multicast frames available */
if (!circular_buf_empty(state->tx_queue_multicast)) {
ev_timer_rearm(loop, timer, 0.);
}
}

void awdl_switch_channel(struct ev_loop *loop, ev_timer *timer, int revents) {
(void) revents;
uint64_t now, next_aw;
int slot;
struct awdl_chan chan_new;
int chan_num_new, chan_num_old;
struct daemon_state *state = timer->data;
struct awdl_state *awdl_state = &state->awdl_state;

chan_num_old = awdl_chan_num(awdl_state->channel.current, awdl_state->channel.enc);

now = clock_time_us();
slot = awdl_sync_current_eaw(now, &awdl_state->sync) % AWDL_CHANSEQ_LENGTH;
chan_new = awdl_state->channel.sequence[slot];
chan_num_new = awdl_chan_num(awdl_state->channel.sequence[slot], awdl_state->channel.enc);

if (chan_num_new && (chan_num_new != chan_num_old)) {
log_debug("switch channel to %d (slot %d)", chan_num_new, slot);
set_channel(state->io.wlan_ifindex, chan_num_new);
awdl_state->channel.current = chan_new;
}

next_aw = awdl_sync_next_aw_us(now, &awdl_state->sync);

ev_timer_rearm(loop, timer, usec_to_sec(next_aw));
}

static void awdl_neighbor_add(struct awdl_peer *p, void *_io_state) {
struct io_state *io_state = _io_state;
neighbor_add_rfc4291(io_state->host_ifindex, &p->addr);
}

static void awdl_neighbor_remove(struct awdl_peer *p, void *_io_state) {
struct io_state *io_state = _io_state;
neighbor_remove_rfc4291(io_state->host_ifindex, &p->addr);
}

void awdl_clean_peers(struct ev_loop *loop, ev_timer *timer, int revents) {
(void) loop;
(void) revents; /* should always be EV_TIMER */
uint64_t cutoff_time;
struct daemon_state *state;

state = (struct daemon_state *) timer->data;
cutoff_time = clock_time_us() - state->awdl_state.peers.timeout;

awdl_peers_remove(state->awdl_state.peers.peers, cutoff_time,
state->awdl_state.peer_remove_cb, state->awdl_state.peer_remove_cb_data);

/* TODO for now run election immediately after clean up; might consider seperate timer for this */
awdl_election_run(&state->awdl_state.election, &state->awdl_state.peers);

ev_timer_again(loop, timer);
}

void awdl_print_stats(struct ev_loop *loop, ev_signal *handle, int revents) {
(void) loop;
(void) revents; /* should always be EV_TIMER */
struct awdl_stats *stats = &((struct daemon_state *) handle->data)->awdl_state.stats;

log_info("STATISTICS");
log_info(" TX action %llu, data %llu, unicast %llu, multicast %llu",
stats->tx_action, stats->tx_data, stats->tx_data_unicast, stats->tx_data_multicast);
log_info(" RX action %llu, data %llu, unknown %llu",
stats->rx_action, stats->rx_data, stats->rx_unknown);
}

int awdl_init(struct daemon_state *state, const char *wlan, const char *host, struct awdl_chan chan, const char *dump) {
int err;
char hostname[HOST_NAME_LENGTH_MAX + 1];

err = netutils_init();
if (err < 0)
return err;

err = io_state_init(&state->io, wlan, host, &AWDL_BSSID);
if (err < 0)
return err;

err = get_hostname(hostname, sizeof(hostname));
if (err < 0)
return err;

awdl_init_state(&state->awdl_state, hostname, &state->io.if_ether_addr, chan, clock_time_us());
state->awdl_state.peer_cb = awdl_neighbor_add;
state->awdl_state.peer_cb_data = (void *) &state->io;
state->awdl_state.peer_remove_cb = awdl_neighbor_remove;
state->awdl_state.peer_remove_cb_data = (void *) &state->io;
ieee80211_init_state(&state->ieee80211_state);

state->next = NULL;
state->tx_queue_multicast = circular_buf_init(16);
state->dump = dump;

return 0;
}

void awdl_free(struct daemon_state *state) {
circular_buf_free(state->tx_queue_multicast);
io_state_free(&state->io);
netutils_cleanup();
}

void awdl_schedule(struct ev_loop *loop, struct daemon_state *state) {

state->ev_state.loop = loop;

/* Timer for channel switching */
state->ev_state.chan_timer.data = (void *) state;
ev_timer_init(&state->ev_state.chan_timer, awdl_switch_channel, 0, 0);
ev_timer_start(loop, &state->ev_state.chan_timer);

/* Timer for peer table cleanup */
state->ev_state.peer_timer.data = (void *) state;
ev_timer_init(&state->ev_state.peer_timer, awdl_clean_peers, 0, usec_to_sec(state->awdl_state.peers.clean_interval));
ev_timer_again(loop, &state->ev_state.peer_timer);

/* Trigger frame reception from WLAN device */
state->ev_state.read_wlan.data = (void *) state;
ev_io_init(&state->ev_state.read_wlan, wlan_device_ready, state->io.wlan_fd, EV_READ);
ev_io_start(loop, &state->ev_state.read_wlan);

/* Trigger frame reception from host device */
state->ev_state.read_host.data = (void *) state;
ev_io_init(&state->ev_state.read_host, host_device_ready, state->io.host_fd, EV_READ);
ev_io_start(loop, &state->ev_state.read_host);

/* Timer for PSFs */
state->ev_state.psf_timer.data = (void *) state;
ev_timer_init(&state->ev_state.psf_timer, awdl_send_psf,
usec_to_sec(ieee80211_tu_to_usec(state->awdl_state.psf_interval)),
usec_to_sec(ieee80211_tu_to_usec(state->awdl_state.psf_interval)));
ev_timer_start(loop, &state->ev_state.psf_timer);

/* Timer for MIFs */
state->ev_state.mif_timer.data = (void *) state;
ev_timer_init(&state->ev_state.mif_timer, awdl_send_mif, 0, 0);
ev_timer_start(loop, &state->ev_state.mif_timer);

/* Timer for unicast packets */
state->ev_state.tx_timer.data = (void *) state;
ev_timer_init(&state->ev_state.tx_timer, awdl_send_unicast, 0, 0);
ev_timer_start(loop, &state->ev_state.tx_timer);

/* Timer for multicast packets */
state->ev_state.tx_mcast_timer.data = (void *) state;
ev_timer_init(&state->ev_state.tx_mcast_timer, awdl_send_multicast, 0, 0);
ev_timer_start(loop, &state->ev_state.tx_mcast_timer);

/* Register signal to print statistics */
state->ev_state.stats.data = (void *) state;
ev_signal_init(&state->ev_state.stats, awdl_print_stats, SIGSTATS);
ev_signal_start(loop, &state->ev_state.stats);
}

+ 59
- 0
daemon/core.h View File

@@ -0,0 +1,59 @@
#ifndef OWL_CORE_H
#define OWL_CORE_H

#include <stdbool.h>
#include <ev.h>

#include "state.h"
#include "circular_buffer.h"
#include "io.h"

struct ev_state {
struct ev_loop *loop;
ev_timer mif_timer, psf_timer, tx_timer, tx_mcast_timer, chan_timer, peer_timer;
ev_io read_wlan, read_host;
ev_signal stats;
};

struct daemon_state {
struct io_state io;
struct awdl_state awdl_state;
struct ieee80211_state ieee80211_state;
struct ev_state ev_state;
struct buf *next;
cbuf_handle_t tx_queue_multicast;
const char *dump;
};

int awdl_init(struct daemon_state *state, const char *wlan, const char *host, struct awdl_chan chan, const char *dump);

void awdl_free(struct daemon_state *state);

void awdl_schedule(struct ev_loop *loop, struct daemon_state *state);

void wlan_device_ready(struct ev_loop *loop, ev_io *handle, int revents);

void host_device_ready(struct ev_loop *loop, ev_io *handle, int revents);

void awdl_receive_frame(uint8_t *user, const struct pcap_pkthdr *hdr, const uint8_t *buf);

void awdl_send_action(struct daemon_state *state, enum awdl_action_type type);

void awdl_send_psf(struct ev_loop *loop, ev_timer *handle, int revents);

void awdl_send_mif(struct ev_loop *loop, ev_timer *handle, int revents);

void awdl_send_unicast(struct ev_loop *loop, ev_timer *timer, int revents);

void awdl_send_multicast(struct ev_loop *loop, ev_timer *timer, int revents);

int awdl_send_data(const struct buf *buf, const struct io_state *io_state,
struct awdl_state *awdl_state, struct ieee80211_state *ieee80211_state);

void awdl_switch_channel(struct ev_loop *loop, ev_timer *handle, int revents);

void awdl_clean_peers(struct ev_loop *loop, ev_timer *timer, int revents);

void awdl_print_stats(struct ev_loop *loop, ev_signal *handle, int revents);

#endif /* OWL_CORE_H */

+ 14
- 0
daemon/corewlan.h View File

@@ -0,0 +1,14 @@
#ifndef APPLE_COREWLAN_H
#define APPLE_COREWLAN_H

int corewlan_init();

void corewlan_free();

int corewlan_disassociate(int ifindex);

int corewlan_set_channel(int ifindex, int channel);

int corewlan_get_hostname(char *name, size_t len);

#endif /* APPLE_COREWLAN_H */

+ 64
- 0
daemon/corewlan.m View File

@@ -0,0 +1,64 @@
#import <Foundation/Foundation.h>
#import <CoreWLAN/CWInterface.h>
#import <CoreWLAN/CWChannel.h>
#import <CoreWLAN/CWWiFiClient.h>
#import <net/if.h>

static struct wlan_state {
CWWiFiClient *client;
CWInterface *iface;
NSSet<CWChannel *> *supportedChannels;
} state;

int corewlan_init() {
state.iface = NULL;
state.supportedChannels = NULL;
return 0;
}

void corewlan_free() {
}

int corewlan_disassociate(int ifindex) {
if (!state.client) {
state.client = [CWWiFiClient sharedWiFiClient];
}
if (!state.iface) {
char iface_cstr[IFNAMSIZ];
NSString *iface_str =[NSString stringWithUTF8String:if_indextoname(ifindex, iface_cstr)];
state.iface = [state.client interfaceWithName:iface_str];
}
[state.iface disassociate];
return 0;
}

int corewlan_set_channel(int ifindex, int channel) {
if (!state.client) {
state.client = [CWWiFiClient sharedWiFiClient];
}
if (!state.iface) {
char iface_cstr[IFNAMSIZ];
NSString *iface_str = [NSString stringWithUTF8String:if_indextoname(ifindex, iface_cstr)];
state.iface = [state.client interfaceWithName:iface_str];
}
if (!state.supportedChannels)
state.supportedChannels = [state.iface supportedWLANChannels];
CWChannel* currentCandidate = NULL;
for (CWChannel* chan in state.supportedChannels) {
if ([chan channelNumber] != channel)
continue;
if (!currentCandidate || [chan channelWidth] > [currentCandidate channelWidth])
currentCandidate = chan;
}
if (!currentCandidate)
return -1;
NSError* err;
[state.iface setWLANChannel:currentCandidate error:&err];
return !err;
}

int corewlan_get_hostname(char *name, size_t len) {
NSString *computerName = [(NSString *) SCDynamicStoreCopyComputerName(NULL, NULL) autorelease];
strlcpy(name, [computerName UTF8String], len);
return 0;
}

+ 323
- 0
daemon/io.c View File

@@ -0,0 +1,323 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#ifndef __APPLE__
#include <linux/if_tun.h>
#else
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#endif /* __APPLE__ */
#include "io.h"
#include "netutils.h"

#include <log.h>
#include <wire.h>

static int open_nonblocking_device(const char *dev, pcap_t **pcap_handle, const struct ether_addr *bssid_filter) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
struct bpf_program filter;
int fd;
char filter_str[128];

handle = pcap_create(dev, errbuf);
if (handle == NULL) {
log_error("pcap: unable to open device %s (%s)", dev, errbuf);
return -1;
}

pcap_set_snaplen(handle, 65535);
pcap_set_promisc(handle, 1);
#ifdef __APPLE__
/* On Linux, we activate monitor mode via nl80211 */
pcap_set_rfmon(handle, 1);
#endif /* __APPLE__ */

pcap_set_timeout(handle, 1);

if (pcap_activate(handle) < 0) {
log_error("pcap: unable to activate device %s (%s)", dev, pcap_geterr(handle));
pcap_close(handle);
return -1;
}

if (pcap_setnonblock(handle, 1, errbuf)) {
log_error("pcap: cannot set to non-blocking mode (%s)", errbuf);
pcap_close(handle);
return -1;
}

/* FIXME direction does not seem to have an effect (we get our own frames every time we poll) */
if (pcap_setdirection(handle, PCAP_D_IN)) {
log_warn("pcap: unable to monitor only incoming traffic on device %s (%s)", dev, pcap_geterr(handle));
}

if (pcap_datalink(handle) != DLT_IEEE802_11_RADIO) {
log_error("pcap: device %s does not support radiotap headers", dev);
pcap_close(handle);
return -1;
}

snprintf(filter_str, sizeof(filter_str), "wlan addr3 %s", ether_ntoa(bssid_filter));
if (pcap_compile(handle, &filter, filter_str, 1, PCAP_NETMASK_UNKNOWN) == -1) {
log_error("pcap: could not create filter (%s)", pcap_geterr(handle));
return -1;
}

if (pcap_setfilter(handle, &filter) == -1) {
log_error("pcap: could not set filter (%s)", pcap_geterr(handle));
return -1;
}

if ((fd = pcap_get_selectable_fd(handle)) == -1) {
log_error("pcap: unable to get fd");
return -1;
}

*pcap_handle = handle;

return fd;
}


static int open_tun(char *dev, const struct ether_addr *self) {
#ifndef __APPLE__
static int one = 1;
struct ifreq ifr;
int fd, err, s;

if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
log_error("tun: unable to open tun device %d", fd);
return fd;
}

memset(&ifr, 0, sizeof(ifr));

/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
* IFF_NO_PI - Do not provide packet information
*/
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (*dev)
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);

/* Set non-blocking mode */
if ((err = ioctl(fd, FIONBIO, &one)) < 0) {
close(fd);
return err;
}

// Create a socket for ioctl
s = socket(AF_INET6, SOCK_DGRAM, 0);

// Set HW address
ifr.ifr_hwaddr.sa_family = 1; /* ARPHRD_ETHER */
memcpy(ifr.ifr_hwaddr.sa_data, self, 6);
if ((err = ioctl(s, SIOCSIFHWADDR, &ifr)) < 0) {
log_error("tun: unable to set HW address");
close(fd);
return err;
}

// Get current flags and set them
ioctl(s, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if ((err = ioctl(s, SIOCSIFFLAGS, &ifr)) < 0) {
log_error("tun: unable to set up");
close(fd);
return err;
}

/* Set reduced MTU */
ifr.ifr_mtu = 1450; /* TODO arbitary limit to fit all headers */
if ((err = ioctl(s, SIOCSIFMTU, (void *) &ifr)) < 0) {
log_error("tun: unable to set MTU");
close(fd);
return err;
}

close(s);

return fd;
#else
for (int i = 0; i < 16; ++i) {
char tuntap[IFNAMSIZ];
sprintf(tuntap, "/dev/tap%d", i);

int fd = open(tuntap, O_RDWR);
if (fd > 0) {
struct ifreq ifr;
struct in6_aliasreq ifr6;
int err, s;

if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
log_error("fcntl error on %s", tuntap);
return -errno;
}

sprintf(dev, "tap%d", i);

// Create a socket for ioctl
s = socket(AF_INET6, SOCK_DGRAM, 0);

// Set HW address
strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
ifr.ifr_addr.sa_len = sizeof(struct ether_addr);
ifr.ifr_addr.sa_family = AF_LINK;
memcpy(ifr.ifr_addr.sa_data, self, sizeof(struct ether_addr));
if ((err = ioctl(s, SIOCSIFLLADDR, (caddr_t) &ifr)) < 0) {
log_error("tun: unable to set HW address %s", ether_ntoa(self));
close(fd);
return err;
}

/* Set reduced MTU */
ifr.ifr_mtu = 1450; /* TODO arbitary limit to fit all headers */
if ((err = ioctl(s, SIOCSIFMTU, (caddr_t) &ifr)) < 0) {
log_error("tun: unable to set MTU");
close(fd);
return err;
}

/* Set IPv6 address */
memset(&ifr6, 0, sizeof(ifr6));
strlcpy(ifr6.ifra_name, dev, sizeof(ifr6.ifra_name));

ifr6.ifra_addr.sin6_len = sizeof(ifr6.ifra_addr);
ifr6.ifra_addr.sin6_family = AF_INET6;
rfc4291_addr(self, &ifr6.ifra_addr.sin6_addr);

ifr6.ifra_prefixmask.sin6_len = sizeof(ifr6.ifra_prefixmask);
ifr6.ifra_prefixmask.sin6_family = AF_INET6;
memset((void *) &ifr6.ifra_prefixmask.sin6_addr, 0x00, sizeof(ifr6.ifra_prefixmask));
for (int i = 0; i < 8; i++) /* prefix length: 64 */
ifr6.ifra_prefixmask.sin6_addr.s6_addr[i] = 0xff;

if (ioctl(s, SIOCAIFADDR_IN6, (caddr_t) &ifr6) < 0) {
log_error("tun: unable to set IPv6 address, %s", strerror(errno));
close(fd);
return -errno;
}

close(s);

return fd;
}
}
log_error("tun: cannot open available device");
return -1;
#endif /* __APPLE__ */
}

int io_state_init(struct io_state *state, const char *wlan, const char *host, const struct ether_addr *bssid_filter) {
int err;

/* setup WLAN interface */
strcpy(state->wlan_ifname, wlan);

state->wlan_ifindex = if_nametoindex(state->wlan_ifname);
if (!state->wlan_ifindex) {
log_error("No such interface exists %s", state->wlan_ifname);
return -ENOENT;
}
/* TODO we might instead open a new monitor interface instead */
err = link_down(state->wlan_ifindex);
if (err < 0) {
log_error("Could set link down: %", state->wlan_ifname);
return err;
}
if (!state->wlan_no_monitor_mode) /* if device is already in monitor mode */
err = set_monitor_mode(state->wlan_ifindex);
if (err < 0) {
log_error("Could not put device in monitor mode: %s", state->wlan_ifname);
return err;
}
err = link_up(state->wlan_ifindex);
if (err < 0) {
log_error("Could set link up: %", state->wlan_ifname);
return err;
}
state->wlan_fd = open_nonblocking_device(state->wlan_ifname, &state->wlan_handle, bssid_filter);
if ((err = state->wlan_fd) < 0) {
log_error("Could not open device: %s", state->wlan_ifname);
return err;
}

err = link_ether_addr_get(state->wlan_ifname, &state->if_ether_addr);
if (err < 0) {
log_error("Could not get LLC address from %s", state->wlan_ifname);
return err;
}

/* setup host interface */
if (strlen(host) > 0) {
strcpy(state->host_ifname, host);
/* Host interface needs to have same ether_addr, to make active (!) monitor mode work */
state->host_fd = open_tun(state->host_ifname, &state->if_ether_addr);
if ((err = state->host_fd) < 0) {
log_error("Could not open device: %s", state->host_ifname);
return err;
}
state->host_ifindex = if_nametoindex(state->host_ifname);
if (!state->host_ifindex) {
log_error("No such interface exists %s", state->host_ifname);
return -ENOENT;
}
} else {
log_debug("No host device given, start without host device");
}

return 0;
}

void io_state_free(struct io_state *state) {
close(state->host_fd);
pcap_close(state->wlan_handle);
}

int wlan_send(const struct io_state *state, const uint8_t *buf, int len) {
int err;
if (!state || !state->wlan_handle)
return -EINVAL;
err = pcap_inject(state->wlan_handle, buf, len);
if (err < 0) {
log_error("unable to inject packet (%s)", pcap_geterr(state->wlan_handle));
return err;
}
return 0;
}

int host_send(const struct io_state *state, const uint8_t *buf, int len) {
if (!state || !state->host_fd)
return -EINVAL;
if (write(state->host_fd, buf, len) < 0) {
return -errno;
}
return 0;
}

int host_recv(const struct io_state *state, uint8_t *buf, int *len) {
long nread;
if (!state || !state->host_fd)
return -EINVAL;
nread = read(state->host_fd, buf, *len);
if (nread < 0) {
if (errno != EWOULDBLOCK)
log_error("tun: error reading from device");
return -errno;
}
*len = nread;
return 0;
}

+ 37
- 0
daemon/io.h View File

@@ -0,0 +1,37 @@
#ifndef OWL_IO_H
#define OWL_IO_H

#include <stdint.h>
#include <pcap/pcap.h>
#include <net/if.h>

#ifdef __APPLE__
#include <net/ethernet.h>
#else
#include <netinet/ether.h>
#endif

struct io_state {
pcap_t *wlan_handle;
char wlan_ifname[IFNAMSIZ]; /* name of WLAN iface */
int wlan_ifindex; /* index of WLAN iface */
char host_ifname[IFNAMSIZ]; /* name of host iface */
int host_ifindex; /* index of host iface */
struct ether_addr if_ether_addr; /* MAC address of WLAN and host iface */
int wlan_fd;
int host_fd;
char *dumpfile;
char wlan_no_monitor_mode;
};

int io_state_init(struct io_state *state, const char *wlan, const char *host, const struct ether_addr *bssid_filter);

void io_state_free(struct io_state *state);

int wlan_send(const struct io_state *state, const uint8_t *buf, int len);

int host_send(const struct io_state *state, const uint8_t *buf, int len);

int host_recv(const struct io_state *state, uint8_t *buf, int *len);

#endif /* OWL_IO_H */

+ 455
- 0
daemon/netutils.c View File

@@ -0,0 +1,455 @@
#include "netutils.h"

#include <errno.h>
#include <ieee80211.h>
#include <log.h>
#include <string.h>

#ifndef __APPLE__

#include <unistd.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/route/link.h>
#include <netlink/route/neighbour.h>
#include <netlink/errno.h>

#include <linux/nl80211.h>

struct nlroute_state {
struct nl_sock *socket;
};

static struct nlroute_state nlroute_state;

static int nlroute_init(struct nlroute_state *state) {
state->socket = nl_socket_alloc();
if (!state->socket) {
log_error("Failed to allocate netlink socket.");
return -ENOMEM;
}

if (nl_connect(state->socket, NETLINK_ROUTE)) {
log_error("Failed to connect to generic netlink.");
nl_socket_free(state->socket);
return -ENOLINK;
}

return 0;
}

static void nlroute_free(struct nlroute_state *state) {
nl_socket_free(state->socket);
}

struct nl80211_state {
struct nl_sock *socket;
int nl80211_id;
};

static struct nl80211_state nl80211_state;

static int nl80211_init(struct nl80211_state *state) {
state->socket = nl_socket_alloc();
if (!state->socket) {
log_error("Failed to allocate netlink socket.");
return -ENOMEM;
}

nl_socket_set_buffer_size(state->socket, 8192, 8192);

if (genl_connect(state->socket)) {
log_error("Failed to connect to generic netlink.");
nl_socket_free(state->socket);
return -ENOLINK;
}

state->nl80211_id = genl_ctrl_resolve(state->socket, "nl80211");
if (state->nl80211_id < 0) {
log_error("nl80211 not found.");
nl_socket_free(state->socket);
return -ENOENT;
}

return 0;
}

static void nl80211_free(struct nl80211_state *state) {
nl_socket_free(state->socket);
}

int netutils_init() {
int err;
err = nlroute_init(&nlroute_state);
if (err < 0)
return err;
err = nl80211_init(&nl80211_state);
if (err < 0)
return err;
return 0;
}

void netutils_cleanup() {
nlroute_free(&nlroute_state);
nl80211_free(&nl80211_state);
}

int neighbor_add(int ifindex, const struct ether_addr *eth, const struct in6_addr *in6) {
int err;
struct rtnl_neigh *neigh;
struct nl_addr *nl_ipaddr, *nl_llcaddr;

neigh = rtnl_neigh_alloc();
if (!neigh) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}

rtnl_neigh_set_ifindex(neigh, ifindex);

nl_ipaddr = nl_addr_build(AF_INET6, (void *) in6, sizeof(struct in6_addr));
if (!nl_ipaddr) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}
rtnl_neigh_set_dst(neigh, nl_ipaddr);

nl_llcaddr = nl_addr_build(AF_LLC, (void *) eth, sizeof(struct ether_addr));
if (!nl_llcaddr) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}
rtnl_neigh_set_lladdr(neigh, nl_llcaddr);

rtnl_neigh_set_state(neigh, NUD_PERMANENT);

err = rtnl_neigh_add(nlroute_state.socket, neigh, NLM_F_CREATE);
if (err < 0) {
log_error("Could not add neighbor: %s", nl_geterror(err));
}

out:
if (neigh)
rtnl_neigh_put(neigh);
if (nl_ipaddr)
nl_addr_put(nl_ipaddr);
if (nl_llcaddr)
nl_addr_put(nl_llcaddr);

return err;
}

int neighbor_remove(int ifindex, const struct in6_addr *in6) {
int err;
struct rtnl_neigh *neigh;
struct nl_addr *nl_ipaddr;

neigh = rtnl_neigh_alloc();
if (!neigh) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}

rtnl_neigh_set_ifindex(neigh, ifindex);

nl_ipaddr = nl_addr_build(AF_INET6, (void *) in6, sizeof(struct in6_addr));
if (!nl_ipaddr) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}
rtnl_neigh_set_dst(neigh, nl_ipaddr);

err = rtnl_neigh_delete(nlroute_state.socket, neigh, 0);
if (err < 0) {
log_error("Could not delete neighbor: %s", nl_geterror(err));
}

out:
if (neigh)
rtnl_neigh_put(neigh);
if (nl_ipaddr)
nl_addr_put(nl_ipaddr);

return err;
}

int set_monitor_mode(int ifindex) {
int err = 0;
struct nl_msg *m = NULL;
struct nl_msg *flags = NULL;

m = nlmsg_alloc();
if (!m) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}

genlmsg_put(m, 0, 0, nl80211_state.nl80211_id, 0, 0, NL80211_CMD_SET_INTERFACE, 0);

NLA_PUT_U32(m, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(m, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);

flags = nlmsg_alloc();
if (!flags) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}
NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_ACTIVE);
nla_put_nested(m, NL80211_ATTR_MNTR_FLAGS, flags);
nlmsg_free(flags);
flags = 0;

err = nl_send_auto(nl80211_state.socket, m);
if (err < 0) {
log_error("Error while sending via netlink: %s", nl_geterror(err));
goto out;
}

err = nl_recvmsgs_default(nl80211_state.socket);
if (err < 0) {
log_error("Error while receiving via netlink: %s", nl_geterror(err));
goto out;
}

goto out;

nla_put_failure:
log_error("building message failed");
err = -ENOBUFS;
out:
if (m)
nlmsg_free(m);
if (flags)
nlmsg_free(flags);
return err;
}

int set_channel(int ifindex, int channel /* TODO int ctrl_freq , int bw, int cntr_freq */) {
int err;
struct nl_msg *m;
int freq;

freq = ieee80211_channel_to_frequency(channel);
if (!freq) {
log_error("Invalid channel number %d", channel);
err = -EINVAL;
goto out;
}

m = nlmsg_alloc();
if (!m) {
log_error("Could not allocate netlink message");
err = -ENOMEM;
goto out;
}

if (genlmsg_put(m, 0, 0, nl80211_state.nl80211_id, 0, 0, NL80211_CMD_SET_CHANNEL, 0) == NULL) {
err = -ENOBUFS;
goto out;
}

NLA_PUT_U32(m, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(m, NL80211_ATTR_WIPHY_FREQ, freq);
NLA_PUT_U32(m, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS);

err = nl_send_auto(nl80211_state.socket, m);
if (err < 0) {
log_error("error while sending via netlink");
goto out;
}

err = nl_recvmsgs_default(nl80211_state.socket);
goto out;

nla_put_failure:
log_error("building message failed");
err = -ENOBUFS;
out:
if (m)
nlmsg_free(m);
return err;
}

static int link_updown(int ifindex, int up) {
int err;
struct rtnl_link *old, *req;

err = rtnl_link_get_kernel(nlroute_state.socket, ifindex, NULL, &old);
if (err < 0) {
log_error("Could not get link: %s", nl_geterror(err));
return err;
}

req = rtnl_link_alloc();

if (up)
rtnl_link_set_flags(req, rtnl_link_str2flags("up"));
else
rtnl_link_set_flags(req, rtnl_link_str2flags("down"));

rtnl_link_change(nlroute_state.socket, old, req, 0);

rtnl_link_put(old);

return 0;
}

int link_up(int ifindex) {
return link_updown(ifindex, 1);
}

int link_down(int ifindex) {
return link_updown(ifindex, 0);
}

int link_ether_addr_get(const char *ifname, struct ether_addr *addr) {
int err;
struct rtnl_link *link;
struct nl_addr *nladdr;

err = rtnl_link_get_kernel(nlroute_state.socket, 0, ifname, &link);
if (err < 0) {
log_error("Could not get link: %s", nl_geterror(err));
return err;
}

nladdr = rtnl_link_get_addr(link);

*addr = *(struct ether_addr *) nl_addr_get_binary_addr(nladdr);

return 0;
}

int get_hostname(char *name, size_t len) {
if (gethostname(name, len) < 0)
return -errno;
return 0;
}

#else /* __APPLE__ */

#include <unistd.h>
#include <fcntl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if_dl.h>

#include "corewlan.h"

int netutils_init() {
return corewlan_init();
}

void netutils_cleanup() {
corewlan_free();
}

int set_monitor_mode(int ifindex) {
corewlan_disassociate(ifindex);
/* TODO implement here instead of using libpcap */
return 0;
}

int set_channel(int ifindex, int channel) {
return corewlan_set_channel(ifindex, channel);
}

int link_up(int ifindex) {
(void) ifindex;
return 0; /* TODO implement */
}

int link_down(int ifindex) {
(void) ifindex;
return 0; /* TODO implement */
}

int link_ether_addr_get(const char *ifname, struct ether_addr *addr) {
struct ifaddrs *ifap, *ifa;
if (getifaddrs(&ifap) < 0)
return -1;
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr && (ifa->ifa_addr->sa_family == AF_LINK) && !strcmp(ifname, ifa->ifa_name)) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr;
memcpy(addr, LLADDR(sdl), sdl->sdl_alen);
return 0;
}
}
return -1;
}

int get_hostname(char *name, size_t len) {
return corewlan_get_hostname(name, len);
}

/* TODO would be nicer to use PF_ROUTE socket instead of calling ndp */
int neighbor_add(int ifindex, const struct ether_addr *eth, const struct in6_addr *in6) {
if (fork() <= 0) {
char eth_str[18];
char in6_str[INET6_ADDRSTRLEN];
char iface[IFNAMSIZ];
char in6scope_str[INET6_ADDRSTRLEN + 1 + IFNAMSIZ];
strlcpy(eth_str, ether_ntoa(eth), sizeof(eth_str));;
sprintf(in6scope_str, "%s%%%s", inet_ntop(AF_INET6, (void *) in6, in6_str, sizeof(in6_str)),
if_indextoname(ifindex, iface));
int null = open("/dev/null", O_WRONLY);
dup2(null, 1 /* stdout */);
dup2(null, 2 /* stderr */);
execlp("/usr/sbin/ndp", "/usr/sbin/ndp", "-sn", in6scope_str, eth_str, (char *) NULL);
close(null);
}
return 0;
}

int neighbor_remove(int ifindex, const struct in6_addr *in6) {
if (fork() <= 0) {
char in6_str[INET6_ADDRSTRLEN];
char iface[IFNAMSIZ];
char in6scope_str[INET6_ADDRSTRLEN + 1 + IFNAMSIZ];
sprintf(in6scope_str, "%s%%%s", inet_ntop(AF_INET6, (void *) in6, in6_str, sizeof(in6_str)),
if_indextoname(ifindex, iface));
int null = open("/dev/null", O_WRONLY);
dup2(null, 1 /* stdout */);
dup2(null, 2 /* stderr */);
execlp("/usr/sbin/ndp", "/usr/sbin/ndp", "-dn", in6scope_str, (char *) NULL);
close(null);
}
return 0;
}

#endif /* __APPLE__ */

int neighbor_add_rfc4291(int ifindex, const struct ether_addr *addr) {
struct in6_addr in6;
rfc4291_addr(addr, &in6);
return neighbor_add(ifindex, addr, &in6);
}

int neighbor_remove_rfc4291(int ifindex, const struct ether_addr *addr) {
struct in6_addr in6;
rfc4291_addr(addr, &in6);
return neighbor_remove(ifindex, &in6);
}

void rfc4291_addr(const struct ether_addr *eth, struct in6_addr *in6) {
memset(in6, 0, sizeof(struct in6_addr));
in6->s6_addr[0] = 0xfe;
in6->s6_addr[1] = 0x80;
in6->s6_addr[8] = eth->ether_addr_octet[0] ^ 0x02;
in6->s6_addr[9] = eth->ether_addr_octet[1];
in6->s6_addr[10] = eth->ether_addr_octet[2];
in6->s6_addr[11] = 0xff;
in6->s6_addr[12] = 0xfe;
in6->s6_addr[13] = eth->ether_addr_octet[3];
in6->s6_addr[14] = eth->ether_addr_octet[4];
in6->s6_addr[15] = eth->ether_addr_octet[5];
}

+ 47
- 0
daemon/netutils.h View File

@@ -0,0 +1,47 @@
#ifndef OWL_NETUTILS_H_
#define OWL_NETUTILS_H_

#include <netinet/in.h>
#ifdef __APPLE__
#include <net/ethernet.h>
#else
#include <netinet/ether.h>
#endif

/**
* Needs to be run once before netutils can be used (platform-dependent).
*
* For example, on Linux, sets up nl80211 socket and state.
*
* @return 0 on success, a negative value on failure
*/
int netutils_init();

/**
* Clean up methods if netutils are no longer needed (platform-dependent)
*/
void netutils_cleanup();

int set_monitor_mode(int ifindex);

int set_channel(int ifindex, int channel);

int link_up(int ifindex);

int link_down(int ifindex);

int link_ether_addr_get(const char *ifname, struct ether_addr *addr);

int get_hostname(char *name, size_t len);

int neighbor_add(int ifindex, const struct ether_addr *, const struct in6_addr *);

int neighbor_remove(int ifindex, const struct in6_addr *);

int neighbor_add_rfc4291(int ifindex, const struct ether_addr *);

int neighbor_remove_rfc4291(int ifindex, const struct ether_addr *);

void rfc4291_addr(const struct ether_addr *eth, struct in6_addr *in6);

#endif /* OWL_NETUTILS_H_ */

+ 188
- 0
daemon/owl.c View File

@@ -0,0 +1,188 @@
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <ev.h>

#include "log.h"
#include "core.h"

#define DEFAULT_AWDL_DEVICE "awdl0"
#define FAILED_DUMP "failed.pcap"

static void daemonize() {
pid_t pid;
long x;

/* Fork off the parent process */
pid = fork();

/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);

/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);

/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);

/* Catch, ignore and handle signals */
/* TODO: Implement a working signal handler */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);

/* Fork off for the second time*/
pid = fork();

/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);

/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);

/* Set new file permissions */
umask(0);

/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir("/");

/* Close all open file descriptors */
for (x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
close(x);
}

/* Open the log file */
openlog("owl", LOG_PID, LOG_DAEMON);
}

int main(int argc, char *argv[]) {
int c;
int daemon = 0;
int dump = 0;
int chan_num = 6;
struct awdl_chan chan;
int log_level = LOG_INFO;
int filter_rssi = 1;
int no_monitor_mode = 0;

char wlan[IFNAMSIZ];
char host[IFNAMSIZ] = DEFAULT_AWDL_DEVICE;

struct ev_loop *loop;

struct daemon_state state;

while ((c = getopt(argc, argv, "Dc:dvi:h:a:t:fN")) != -1) {
switch (c) {
case 'D':
daemon = 1;
break;
case 'c':
chan_num = atoi(optarg);
break;
case 'd':
dump = 1;
break;
case 'f':
filter_rssi = 0;
break;
case 'v':
if (log_level < LOG_TRACE)
log_level++; /* increase log level with every occurence of option */
break;
case 'i':
strcpy(wlan, optarg);
break;
case 'h':
strcpy(host, optarg);
break;
case 'N':
no_monitor_mode = 1;
break;
case '?':
if (optopt == 'i')
fprintf(stderr, "Option -%c needs to specify a wireless interface.\n", optopt);
return EXIT_FAILURE;
default:
abort();
}
}

log_set_level(log_level);

printf(" .oOXWMMMMWXOx:\n"
" .oOOOx:'''''''''''':OOOx:\n"
" oXOo' ........ ':OXx.\n"
" .oOOO''''''''''OOOo.\n"
" oXOo' 'oOO:\n"
" :oOOOOXXXXOOOOo:.\n"
" oXO:' ':OXo\n"
" .:xOXXXXXXOx:.\n"
" .xXMMMMMMMMMMMMMMMMXx.\n"
" 'XWWWWWWMMMMMMMMMMMMMMMMMMMMMMWWWWWWX'\n"
" oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWo\n"
" OMMMMMMWWMMMMMMMMMMMMMMWWWMMMMMO\n"
" OMMWx' 'xWMMMMWx' 'oXMMO\n"
" :MW: oMMx 'WM:\n"
" XM' .xOOo. :o .xOOo. WX\n"
" WX :MMMMMX :MMMMMX xW\n"
" XW 'WMMMMX .xx. 'WMMMWX XX\n"
" 'Wx 'xWMx' OMMO 'xWMx' xM'\n"
" 'XX: 'XX' :XX'\n"
" 'xXOx:..................:xXWx'\n"
" 'xXMMMMMMMMMMMMMMMMMMWO'\n"
"\n"
" Open Wireless Link\n"
"\n"
" https://owlink.org\n"
"\n");

if (daemon)
daemonize();

switch (chan_num) {
case 6:
chan = CHAN_OPCLASS_6;
break;
case 44:
chan = CHAN_OPCLASS_44;
break;
case 149:
chan = CHAN_OPCLASS_149;
break;
default:
log_error("Unsupported channel %d (use 6, 44, or 149)", chan_num);
return EXIT_FAILURE;
}

state.io.wlan_no_monitor_mode = no_monitor_mode;

if (awdl_init(&state, wlan, host, chan, dump ? FAILED_DUMP : 0) < 0) {
log_error("could not initialize core");
return EXIT_FAILURE;
}
state.awdl_state.filter_rssi = filter_rssi;

if (state.io.wlan_ifindex)
log_info("WLAN device: %s (addr %s)", state.io.wlan_ifname, ether_ntoa(&state.io.if_ether_addr));
if (state.io.host_ifindex)
log_info("Host device: %s", state.io.host_ifname);

loop = EV_DEFAULT;

awdl_schedule(loop, &state);

ev_run(loop, 0);

awdl_free(&state);

return EXIT_SUCCESS;
}

+ 1
- 0
googletest

@@ -0,0 +1 @@
Subproject commit 2fe3bd994b3189899d93f1d5a881e725e046fdc2

+ 1
- 0
radiotap

@@ -0,0 +1 @@
Subproject commit 1ca6b4f0d7225971ebcadf575c8f9e00ee55e840

BIN
resources/overview.png View File

Before After
Width: 499  |  Height: 729  |  Size: 85KB

+ 40
- 0
src/CMakeLists.txt View File

@@ -0,0 +1,40 @@
add_library(awdl "")

target_sources(awdl PRIVATE
log.c
log.h
state.c
state.h
election.c
election.h
sync.c
sync.h
channel.c
channel.h
schedule.c
schedule.h
tx.c
tx.h
rx.c
rx.h
frame.c
frame.h
crc32.c
crc32.h
wire.c
wire.h
peers.c
peers.h
version.c
version.h
hashmap.c
hashmap.h
siphash24.c
siphash24.h
circular_buffer.c
circular_buffer.h
)

target_include_directories(awdl PRIVATE ${CMAKE_SOURCE_DIR}/radiotap)

target_link_libraries(awdl radiotap)

+ 101
- 0
src/channel.c View File

@@ -0,0 +1,101 @@
#include "channel.h"
#include "ieee80211.h"

void awdl_chanseq_init(struct awdl_chan *seq) {
for (int i = 0; i < AWDL_CHANSEQ_LENGTH; i++, seq++) {
if (i < 8)
*seq = CHAN_OPCLASS_149;
else
*seq = CHAN_OPCLASS_6;
}
}

void awdl_chanseq_init_idle(struct awdl_chan *seq) {
for (int i = 0; i < AWDL_CHANSEQ_LENGTH; i++, seq++) {
switch (i) {
case 8:
*seq = CHAN_OPCLASS_6;
break;
case 0:
case 9:
case 10:
*seq = CHAN_OPCLASS_149;
break;
default:
*seq = CHAN_NULL;
break;
}
}
}

void awdl_chanseq_init_static(struct awdl_chan *seq, const struct awdl_chan *chan) {
for (int i = 0; i < AWDL_CHANSEQ_LENGTH; i++, seq++) {
*seq = *chan;
}
}

uint8_t awdl_chan_num(struct awdl_chan chan, enum awdl_chan_encoding enc) {
switch (enc) {
case AWDL_CHAN_ENC_SIMPLE:
return chan.simple.chan_num;
case AWDL_CHAN_ENC_LEGACY:
return chan.legacy.chan_num;
case AWDL_CHAN_ENC_OPCLASS:
return chan.opclass.chan_num;
default:
return 0; /* unknown encoding */
}
}

int awdl_chan_encoding_size(enum awdl_chan_encoding enc) {
switch (enc) {
case AWDL_CHAN_ENC_SIMPLE:
return 1;
case AWDL_CHAN_ENC_LEGACY:
case AWDL_CHAN_ENC_OPCLASS:
return 2;
default:
return -1; /* unknown encoding */
}
}

/* adapted from iw/util.c */
int ieee80211_channel_to_frequency(int chan) {
/* see 802.11 17.3.8.3.2 and Annex J
* there are overlapping channel numbers in 5GHz and 2GHz bands */
if (chan <= 0)
return 0; /* not supported */

/* 2 GHz band */
if (chan == 14)
return 2484;
else if (chan < 14)
return 2407 + chan * 5;

/* 5 GHz band */
if (chan < 32)
return 0; /* not supported */

if (chan >= 182 && chan <= 196)
return 4000 + chan * 5;
else
return 5000 + chan * 5;

}

/* from iw/util.c */
int ieee80211_frequency_to_channel(int freq) {
/* see 802.11-2007 17.3.8.3.2 and Annex J */
if (freq == 2484)
return 14;
else if (freq < 2484)
return (freq - 2407) / 5;
else if (freq >= 4910 && freq <= 4980)
return (freq - 4000) / 5;
else if (freq <= 45000) /* DMG band lower limit */
return (freq - 5000) / 5;
else if (freq >= 58320 && freq <= 64800)
return (freq - 56160) / 2160;
else
return 0;
}

+ 53
- 0
src/channel.h View File

@@ -0,0 +1,53 @@
#ifndef AWDL_CHANNEL_H_
#define AWDL_CHANNEL_H_

#include <stdint.h>

#define AWDL_CHANSEQ_LENGTH 16

enum awdl_chan_encoding {
AWDL_CHAN_ENC_SIMPLE = 0,
AWDL_CHAN_ENC_LEGACY = 1,
AWDL_CHAN_ENC_OPCLASS = 3,
};

struct awdl_chan {
union {
uint8_t val[2];
struct {
uint8_t chan_num;
} simple;
struct {
uint8_t flags;
uint8_t chan_num;
} legacy;
struct {
uint8_t chan_num;
uint8_t opclass;
} opclass;
};
};

#define CHAN_NULL (struct awdl_chan) { { { 0, 0x00 } } }
#define CHAN_OPCLASS_6 (struct awdl_chan) { { { 6, 0x51 } } }
#define CHAN_OPCLASS_44 (struct awdl_chan) { { { 44, 0x80 } } }
#define CHAN_OPCLASS_149 (struct awdl_chan) { { { 149, 0x80 } } }

uint8_t awdl_chan_num(struct awdl_chan chan, enum awdl_chan_encoding);

int awdl_chan_encoding_size(enum awdl_chan_encoding);

struct awdl_channel_state {
enum awdl_chan_encoding enc;
struct awdl_chan sequence[AWDL_CHANSEQ_LENGTH];
struct awdl_chan master;
struct awdl_chan current;
};

void awdl_chanseq_init(struct awdl_chan *seq);

void awdl_chanseq_init_idle(struct awdl_chan *seq);

void awdl_chanseq_init_static(struct awdl_chan *seq, const struct awdl_chan *chan);

#endif /* AWDL_CHANNEL_H_ */

+ 158
- 0
src/circular_buffer.c View File

@@ -0,0 +1,158 @@
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <assert.h>

#include "circular_buffer.h"

// The definition of our circular buffer structure is hidden from the user
struct circular_buf {
void **buffer;
size_t head;
size_t tail;
size_t max; //of the buffer
int full;
};

static void advance_pointer(cbuf_handle_t cbuf)
{
assert(cbuf);

if(cbuf->full)
{
cbuf->tail = (cbuf->tail + 1) % cbuf->max;
}

cbuf->head = (cbuf->head + 1) % cbuf->max;

// We mark full because we will advance tail on the next time around
cbuf->full = (cbuf->head == cbuf->tail);
}

static void retreat_pointer(cbuf_handle_t cbuf)
{
assert(cbuf);

cbuf->full = 0;
cbuf->tail = (cbuf->tail + 1) % cbuf->max;
}

cbuf_handle_t circular_buf_init(size_t size)
{
assert(size);

cbuf_handle_t cbuf = malloc(sizeof(struct circular_buf));
assert(cbuf);

cbuf->buffer = malloc(size * sizeof(void*));
cbuf->max = size;
circular_buf_reset(cbuf);

assert(circular_buf_empty(cbuf));

return cbuf;
}

void circular_buf_free(cbuf_handle_t cbuf)
{
assert(cbuf);
assert(cbuf->buffer);
free(cbuf->buffer);
free(cbuf);
}

void circular_buf_reset(cbuf_handle_t cbuf)
{
assert(cbuf);

cbuf->head = 0;
cbuf->tail = 0;
cbuf->full = 0;
}

size_t circular_buf_size(cbuf_handle_t cbuf)
{
assert(cbuf);

size_t size = cbuf->max;

if(!cbuf->full)
{
if(cbuf->head >= cbuf->tail)
{
size = (cbuf->head - cbuf->tail);
}
else
{
size = (cbuf->max + cbuf->head - cbuf->tail);
}

}

return size;
}

size_t circular_buf_capacity(cbuf_handle_t cbuf)
{
assert(cbuf);

return cbuf->max;
}

void circular_buf_put(cbuf_handle_t cbuf, void *data)
{
assert(cbuf && cbuf->buffer);

cbuf->buffer[cbuf->head] = data;

advance_pointer(cbuf);
}

int circular_buf_put2(cbuf_handle_t cbuf, void *data)
{
int r = -1;

assert(cbuf && cbuf->buffer);

if(!circular_buf_full(cbuf))
{
cbuf->buffer[cbuf->head] = data;
advance_pointer(cbuf);
r = 0;
}

return r;
}

int circular_buf_get(cbuf_handle_t cbuf, void **data, int peek)
{
assert(cbuf && cbuf->buffer);

int r = -1;

if(!circular_buf_empty(cbuf))
{
if (data)