HMAC user space daemon

This commit is contained in:
Sven Zehl 2016-09-26 17:40:11 +02:00
parent d9d1d9cf3c
commit 9c1055cae6
4 changed files with 735 additions and 0 deletions

View File

@ -0,0 +1,6 @@
all: hybrid_tdma_csma_mac_x86
hybrid_tdma_csma_mac_x86:
g++ hybrid_tdma_csma_mac.c -I /usr/include/libnl3/ -DCONFIG_LIBNL30 -o hmac_userspace_daemon -levent -lnl-genl-3 -lnl-3 -lzmq -lstdc++ -lpthread
clean:
rm hmac_userspace_daemon

View File

@ -0,0 +1,491 @@
/*
* Userland part of the hybrid TDMA/CSMA MAC processor which is responsible for sending the configuration at
* the beginning of each time slot to the ATH9k WiFi driver using Netlink.
*
* build with: make all
*
* @authors S Zehl, A. Zubow
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <unistd.h>
#include <time.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/util.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <netlink/socket.h>
#include <stdlib.h>
#include "../backports-3.12.8-1/include/uapi/linux/nl80211.h"
#include "iw.h"
#include <pthread.h>
#include <unistd.h>
#include <cassert>
#include <string>
#include <iostream>
#include <zmq.hpp>
#include <string>
#include <sstream>
#include <vector>
// struct to be passed via netlink to ath9k driver
struct tid_sleep_tuple
{
char mac[6]; // destination MAC address
char mask; // TID mask
};
int mDebug = 0;
int isRunning = 1;
// Used by local controller for communication with mac processor
int LOCAL_MAC_PROCESSOR_CTRL_PORT = 1217;
// variables set at start-up
long slotDuration = 10000; // mus
int slotsPerFrame = 10; // e.g. 10
char * interface; // e.g. wifi0
char * configuration; // e.g. 1,mac_addr,tid_map;2,mac_addr,tid_map
// updated at runtime
std::string *schedule_per_slot = NULL;
struct nl80211_state state;
// internal state for keeping slotting time aligned
long oldtime_l = 0;
int event_is_persistent;
const int estimatePrecisionEveryNslots = 1000;
int times[estimatePrecisionEveryNslots];
int cnt = 0;
long gl_clock = 0; // global clock w/ step size of slotDuration
long slotCnt = 0; // total slot counter
long frameCnt = 0; // total frame counter
///////////////////////////////////////////////////////////////////////////////
/** helper functions for string manipulation */
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
/** helper functions for string manipulation */
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
/**
* Called at start-up or in case of reconfiguration.
*/
void updateSchedule(std::string &msg) {
// init
for (int i=0; i<slotsPerFrame; i++) {
schedule_per_slot[i] = "";
}
// assign configuration to each slot
std::vector<std::string> msg_parts = split(msg, '#');
for (int k=0; k<msg_parts.size(); k++) {
std::vector<std::string> entry = split(msg_parts[k], ',');
int slot_id = atoi(entry[0].c_str());
const char * mac_addr = entry[1].c_str();
int tid_mask = atoi(entry[2].c_str());
std::stringstream ss;
if (schedule_per_slot[slot_id].empty()) {
ss << mac_addr << "," << entry[2].c_str();
} else {
ss << schedule_per_slot[slot_id] << "#" << mac_addr << "," << entry[2].c_str();
}
schedule_per_slot[slot_id] = ss.str();
}
}
/** Worker thread responsible for receiving new configuration updates */
void *worker_routine (void *arg)
{
std::cout << "Worker routine started ... ready to receive new configuration messages via ZMQ socket." << std::endl;
zmq::context_t *context = (zmq::context_t *) arg;
zmq::socket_t socket (*context, ZMQ_REP);
std::stringstream ss;
ss << "tcp://*:" << LOCAL_MAC_PROCESSOR_CTRL_PORT;
socket.bind (ss.str().c_str());
//socket.bind ("ipc:///tmp/localmacprocessor");
while (true) {
zmq::message_t request;
// Wait for next request from client
socket.recv (&request);
std::string msg = std::string(static_cast<char*>(request.data()), request.size());
std::cout << "Received new configuration update: " << msg << std::endl;
if (msg.find("TERMINATE") == 0) {
// shutdown process
isRunning = 0;
} else {
// update slot schedule
updateSchedule(msg);
}
sleep (0.1);
// Send ACK to client
zmq::message_t reply(2);
memcpy ((void *) reply.data(), "OK", 2);
socket.send(reply);
if(isRunning == 0)
{
socket.close();
break;
}
}
return 0;
}
/** netlink error handling */
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
int *ret = (int*) arg;
*ret = err->error;
printf("->CFG80211 returns: error: No:%d, %s\n",err->error, strerror((-1)*err->error));
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = (int*) arg;
*ret = 0;
return NL_SKIP;
}
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = (int*) arg;
*ret = 0;
return NL_STOP;
}
static int send_nl_msg(std::string& schedule)
{
if (mDebug == 1) {
std::cout << "Send schedule via netlink to ath9k driver: " << schedule << std::endl;
}
std::vector<std::string> tuples = split(schedule, '#');
struct nl_cb *cb;
struct nl_cb *s_cb;
struct nl_msg *msg;
signed long long devidx = 0;
int err = 0;
devidx = if_nametoindex(interface);
msg = nlmsg_alloc();
if (!msg) {
fprintf(stderr, "failed to allocate netlink message\n");
return 2;
}
cb = nl_cb_alloc(NL_CB_DEFAULT);
s_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb || !s_cb) {
fprintf(stderr, "failed to allocate netlink callbacks\n");
err = 2;
goto out_free_msg;
}
// create NetLink message
genlmsg_put(msg, 0, 0, state.nl80211_id, 0, 0, NL80211_ATTR_TID_SLEEP_CTRL, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
// update tids_tuple with current slot schedule
uint8_t mac_u8[6];
struct tid_sleep_tuple tids_tuple[tuples.size()];
for (int k=0; k<tuples.size(); k++) {
std::vector<std::string> tuple = split(tuples[k], ',');
sscanf(tuple[0].c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac_u8[0], &mac_u8[1], &mac_u8[2], &mac_u8[3], &mac_u8[4], &mac_u8[5]);
int tid_mask = atoi(tuple[1].c_str());
for (int zz=0; zz<6; zz++) {
tids_tuple[k].mac[zz] = mac_u8[zz];
}
tids_tuple[k].mask = tid_mask;
}
NLA_PUT(msg, NL80211_ATTR_TID_SLEEP_CTRL_DATA, sizeof(struct tid_sleep_tuple)*tuples.size(), (const void *) &tids_tuple[0]);
nl_socket_set_cb(state.nl_sock, s_cb);
// send message
err = nl_send_auto_complete(state.nl_sock, msg);
if (err < 0)
goto out;
err = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
while (err > 0) {
nl_recvmsgs(state.nl_sock, cb);
}
out:
nl_cb_put(cb);
out_free_msg:
nlmsg_free(msg);
return err;
nla_put_failure:
fprintf(stderr, "building message failed\n");
return 2;
}
/** initialize netlink */
static int nl80211_init(struct nl80211_state *state)
{
int err;
printf("nl80211 init called v2\n");
state->nl_sock = nl_socket_alloc();
if (!state->nl_sock) {
fprintf(stderr, "Failed to allocate netlink socket.\n");
return -ENOMEM;
}
nl_socket_set_buffer_size(state->nl_sock, 8192, 8192);
if (genl_connect(state->nl_sock)) {
fprintf(stderr, "Failed to connect to generic netlink.\n");
err = -ENOLINK;
goto out_handle_destroy;
}
state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211");
if (state->nl80211_id < 0) {
fprintf(stderr, "nl80211 not found.\n");
err = -ENOENT;
goto out_handle_destroy;
}
return 0;
out_handle_destroy:
nl_socket_free(state->nl_sock);
return err;
}
/** timer callback called at the beginning of each new slot */
static void
timeout_cb(evutil_socket_t fd, short event, void *arg)
{
struct timeval newtime, difference;
struct event *timeout = (struct event *)arg;
long error = 0;
if (isRunning == 0) {
printf("Terminating ...\n");
return;
}
evutil_gettimeofday(&newtime, NULL);
long newtime_l = 1000000 * newtime.tv_sec + newtime.tv_usec;
if (gl_clock == 0) {
gl_clock = (newtime_l / slotDuration) * slotDuration;
slotCnt = 0;
frameCnt = 0;
} else {
gl_clock = gl_clock + (slotDuration % 1000000);
slotCnt++;
frameCnt = slotCnt / slotsPerFrame;
}
// current frame number
long relFrameNum = slotCnt % slotsPerFrame;
// estimate slot scheduling precision
if(cnt < estimatePrecisionEveryNslots) {
times[cnt] = newtime_l;
cnt++;
} else if(cnt == estimatePrecisionEveryNslots) {
double sum = 0;
for(int i = 1; i < estimatePrecisionEveryNslots; i++) {
sum += times[i] - times[i-1];
}
sum = sum / (estimatePrecisionEveryNslots-1);
printf("Average slot duration: %.2f\n", sum);
cnt = 0;
}
if (!event_is_persistent) {
struct timeval tv;
evutil_timerclear(&tv);
if (oldtime_l != 0) {
error = 2 * (newtime_l - gl_clock);
}
tv.tv_sec = slotDuration / 1000000;
tv.tv_usec = (slotDuration % 1000000) - error;
event_add(timeout, &tv);
oldtime_l = newtime_l;
send_nl_msg(schedule_per_slot[relFrameNum]);
}
}
/** main entry point */
int
main(int argc, char **argv)
{
struct event timeout;
struct timeval tv;
struct event_base *base;
int flags;
int err;
char * inter_ptr;
char * conf_ptr;
int opt = 0;
char *mac = NULL;
char *guard_frames = NULL;
char *best_effort = NULL;
char *voice = NULL;
char *pt;
interface = '\0';
configuration = '\0';
while ((opt = getopt(argc, argv, "d:i:f:n:c:")) != -1) {
switch(opt) {
case 'd':
mDebug = atoi(optarg);
printf("\nDebug = %d", mDebug);
break;
case 'i':
inter_ptr = optarg;
interface = (char *) malloc(strlen(inter_ptr));
strcpy(interface, inter_ptr);
printf("\nInterface = %s", interface);
break;
case 'f':
slotDuration = atoi(optarg);
printf("\nSlot Duration = %lu", slotDuration);
break;
case 'n':
slotsPerFrame = atoi(optarg);
printf("\nTotal number of slots in frame = %d", slotsPerFrame);
break;
case 'c':
conf_ptr = optarg;
configuration = (char *) malloc(strlen(conf_ptr));
strcpy(configuration, conf_ptr);
printf("\nConfig = %s", configuration);
break;
case '?':
printf("Usage: ./hybrid_tdma_csma_mac -i wifi0 -f 20000 -n 10\n");
break;
}
}
// init schedule
schedule_per_slot = new std::string[slotsPerFrame];
// update schedule
std::string msg(configuration);
updateSchedule(msg);
if (interface == NULL) {
printf("Error no interface supplied. Usage: ./hybrid_tdma_csma_mac -i wifi0 -f 20000 -n 10\n");
return -1;
}
printf("Using init schedule w/:\n");
for(int i=0; i<10; i++) {
printf("#%d: %s, ", i, schedule_per_slot[i].c_str());
}
// Prepare our context and sockets
zmq::context_t context (1);
try {
pthread_t worker;
pthread_create (&worker, NULL, worker_routine, (void *) &context);
} catch (const std::exception& ex) {
std::cerr << "Error setting up ZMQ: " << ex.what() << std::endl;
return -1;
} catch (...) {
std::cerr << "Other strange error!!! " << std::endl;
return -1;
}
err = nl80211_init(&state);
event_is_persistent = 0;
flags = 0;
/* Initalize the event library */
base = event_base_new();
/* Initalize one event */
event_assign(&timeout, base, -1, flags, timeout_cb, (void*) &timeout);
evutil_timerclear(&tv);
/* align schedule to seconds */
struct timeval curTime;
evutil_gettimeofday(&curTime, NULL);
int mus = 1000000 - curTime.tv_usec;
tv.tv_sec = 1;
tv.tv_usec = mus;
event_add(&timeout, &tv);
event_base_dispatch(base);
return (0);
}

View File

@ -0,0 +1,61 @@
#ifndef __IEEE80211
#define __IEEE80211
/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
#define IEEE80211_HT_CAP_SGI_40 0x0040
#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
#define IEEE80211_HT_MCS_MASK_LEN 10
/**
* struct ieee80211_mcs_info - MCS information
* @rx_mask: RX mask
* @rx_highest: highest supported RX rate. If set represents
* the highest supported RX data rate in units of 1 Mbps.
* If this field is 0 this value should not be used to
* consider the highest RX data rate supported.
* @tx_params: TX parameters
*/
struct ieee80211_mcs_info {
__u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN];
__u16 rx_highest;
__u8 tx_params;
__u8 reserved[3];
} __attribute__ ((packed));
/**
* struct ieee80211_ht_cap - HT capabilities
*
* This structure is the "HT capabilities element" as
* described in 802.11n D5.0 7.3.2.57
*/
struct ieee80211_ht_cap {
__u16 cap_info;
__u8 ampdu_params_info;
/* 16 bytes MCS information */
struct ieee80211_mcs_info mcs;
__u16 extended_ht_cap_info;
__u32 tx_BF_cap_info;
__u8 antenna_selection_info;
} __attribute__ ((packed));
struct ieee80211_vht_mcs_info {
__u16 rx_vht_mcs;
__u16 rx_highest;
__u16 tx_vht_mcs;
__u16 tx_highest;
} __attribute__ ((packed));
struct ieee80211_vht_cap {
__u32 cap_info;
struct ieee80211_vht_mcs_info mcs;
} __attribute__ ((packed));
#endif /* __IEEE80211 */

177
hmac_userspace_daemon/iw.h Normal file
View File

@ -0,0 +1,177 @@
#ifndef __IW_H
#define __IW_H
#include <stdbool.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <endian.h>
#include "ieee80211.h"
#define ETH_ALEN 6
/* libnl 1.x compatibility code */
#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
# define nl_sock nl_handle
#endif
struct nl80211_state {
struct nl_sock *nl_sock;
int nl80211_id;
};
enum command_identify_by {
CIB_NONE,
CIB_PHY,
CIB_NETDEV,
CIB_WDEV,
};
enum id_input {
II_NONE,
II_NETDEV,
II_PHY_NAME,
II_PHY_IDX,
II_WDEV,
};
struct cmd {
const char *name;
const char *args;
const char *help;
const enum nl80211_commands cmd;
int nl_msg_flags;
int hidden;
const enum command_identify_by idby;
/*
* The handler should return a negative error code,
* zero on success, 1 if the arguments were wrong
* and the usage message should and 2 otherwise.
*/
int (*handler)(struct nl80211_state *state,
struct nl_cb *cb,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id);
const struct cmd *(*selector)(int argc, char **argv);
const struct cmd *parent;
};
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
#define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))
#define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel)\
static struct cmd \
__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden\
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (_name), \
.args = (_args), \
.cmd = (_nlcmd), \
.nl_msg_flags = (_flags), \
.hidden = (_hidden), \
.idby = (_idby), \
.handler = (_handler), \
.help = (_help), \
.parent = _section, \
.selector = (_sel), \
}
#define __ACMD(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel, _alias)\
__COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel);\
static const struct cmd *_alias = &__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden
#define COMMAND(section, name, args, cmd, flags, idby, handler, help) \
__COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, NULL)
#define COMMAND_ALIAS(section, name, args, cmd, flags, idby, handler, help, selector, alias)\
__ACMD(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, selector, alias)
#define HIDDEN(section, name, args, cmd, flags, idby, handler) \
__COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 1, idby, handler, NULL, NULL)
#define TOPLEVEL(_name, _args, _nlcmd, _flags, _idby, _handler, _help) \
struct cmd \
__section ## _ ## _name \
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (#_name), \
.args = (_args), \
.cmd = (_nlcmd), \
.nl_msg_flags = (_flags), \
.idby = (_idby), \
.handler = (_handler), \
.help = (_help), \
}
#define SECTION(_name) \
struct cmd __section ## _ ## _name \
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (#_name), \
.hidden = 1, \
}
#define DECLARE_SECTION(_name) \
extern struct cmd __section ## _ ## _name;
extern const char iw_version[];
extern int iw_debug;
int handle_cmd(struct nl80211_state *state, enum id_input idby,
int argc, char **argv);
struct print_event_args {
struct timeval ts; /* internal */
bool have_ts; /* must be set false */
bool frame, time, reltime;
};
__u32 listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits);
int __prepare_listen_events(struct nl80211_state *state);
__u32 __do_listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits,
struct print_event_args *args);
int mac_addr_a2n(unsigned char *mac_addr, char *arg);
void mac_addr_n2a(char *mac_addr, unsigned char *arg);
int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len,
unsigned char **mask);
unsigned char *parse_hex(char *hex, size_t *outlen);
int parse_keys(struct nl_msg *msg, char **argv, int argc);
void print_ht_mcs(const __u8 *mcs);
void print_ampdu_length(__u8 exponent);
void print_ampdu_spacing(__u8 spacing);
void print_ht_capability(__u16 cap);
void print_vht_info(__u32 capa, const __u8 *mcs);
char *channel_width_name(enum nl80211_chan_width width);
const char *iftype_name(enum nl80211_iftype iftype);
const char *command_name(enum nl80211_commands cmd);
int ieee80211_channel_to_frequency(int chan, enum nl80211_band band);
int ieee80211_frequency_to_channel(int freq);
void print_ssid_escaped(const uint8_t len, const uint8_t *data);
int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group);
char *reg_initiator_to_string(__u8 initiator);
const char *get_reason_str(uint16_t reason);
const char *get_status_str(uint16_t status);
enum print_ie_type {
PRINT_SCAN,
PRINT_LINK,
};
#define BIT(x) (1ULL<<(x))
void print_ies(unsigned char *ie, int ielen, bool unknown,
enum print_ie_type ptype);
void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen);
DECLARE_SECTION(set);
DECLARE_SECTION(get);
#endif /* __IW_H */