netdev: Support sending Neighbor Report requests

This commit is contained in:
Andrew Zaborowski 2016-12-23 05:38:01 -05:00 committed by Denis Kenzior
parent 8646ab5bdd
commit dafa43fa54
2 changed files with 102 additions and 1 deletions

View File

@ -58,10 +58,12 @@ struct netdev {
struct device *device;
struct wiphy *wiphy;
unsigned int ifi_flags;
uint32_t frequency;
netdev_event_func_t event_filter;
netdev_connect_cb_t connect_cb;
netdev_disconnect_cb_t disconnect_cb;
netdev_neighbor_report_cb_t neighbor_report_cb;
void *user_data;
struct eapol_sm *sm;
struct handshake_state *handshake;
@ -72,6 +74,7 @@ struct netdev {
uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id;
enum netdev_result result;
struct l_timeout *neighbor_report_timeout;
struct l_queue *watches;
uint32_t next_watch_id;
@ -286,6 +289,7 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->connected = false;
netdev->connect_cb = NULL;
netdev->event_filter = NULL;
netdev->neighbor_report_cb = NULL;
netdev->user_data = NULL;
netdev->result = NETDEV_RESULT_OK;
@ -359,6 +363,9 @@ static void netdev_free(void *data)
netdev->user_data = NULL;
}
if (netdev->neighbor_report_cb)
l_timeout_remove(netdev->neighbor_report_timeout);
device_remove(netdev->device);
l_queue_destroy(netdev->watches, l_free);
@ -1371,12 +1378,12 @@ static int netdev_connect_common(struct netdev *netdev,
netdev->connected = true;
netdev->handshake = hs;
netdev->sm = sm;
netdev->frequency = bss->frequency;
handshake_state_set_authenticator_address(hs, bss->addr);
handshake_state_set_supplicant_address(hs, netdev->addr);
return 0;
}
int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
@ -1489,6 +1496,94 @@ int netdev_disconnect(struct netdev *netdev,
return 0;
}
static uint32_t netdev_send_action_frame(struct netdev *netdev,
const uint8_t *to,
const uint8_t *body, size_t body_len,
l_genl_msg_func_t callback)
{
struct l_genl_msg *msg;
const uint16_t frame_type = 0x00d0;
uint8_t action_frame[24 + body_len];
uint32_t id;
memset(action_frame, 0, 24);
l_put_le16(frame_type, action_frame + 0);
memcpy(action_frame + 4, to, 6);
memcpy(action_frame + 10, netdev->addr, 6);
memcpy(action_frame + 16, netdev->handshake->aa, 6);
memcpy(action_frame + 24, body, body_len);
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + body_len);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4,
&netdev->frequency);
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME, sizeof(action_frame),
action_frame);
id = l_genl_family_send(nl80211, msg, callback, netdev, NULL);
if (!id)
l_genl_msg_unref(msg);
return id;
}
static void netdev_neighbor_report_req_cb(struct l_genl_msg *msg,
void *user_data)
{
struct netdev *netdev = user_data;
if (!netdev->neighbor_report_cb)
return;
if (l_genl_msg_get_error(msg) < 0) {
netdev->neighbor_report_cb(netdev, NULL, 0, netdev->user_data);
netdev->neighbor_report_cb = NULL;
l_timeout_remove(netdev->neighbor_report_timeout);
}
}
static void netdev_neighbor_report_timeout(struct l_timeout *timeout,
void *user_data)
{
struct netdev *netdev = user_data;
netdev->neighbor_report_cb(netdev, NULL, 0, netdev->user_data);
netdev->neighbor_report_cb = NULL;
}
int netdev_neighbor_report_req(struct netdev *netdev,
netdev_neighbor_report_cb_t cb)
{
const uint8_t action_frame[] = {
0x05, /* Category: Radio Measurement */
0x04, /* Radio Measurement Action: Neighbor Report Request */
0x01, /* Dialog Token: a non-zero value (unused) */
};
if (netdev->neighbor_report_cb || !netdev->connected)
return -EBUSY;
if (!netdev_send_action_frame(netdev, netdev->handshake->aa,
action_frame, sizeof(action_frame),
netdev_neighbor_report_req_cb))
return -EIO;
netdev->neighbor_report_cb = cb;
/* Set a 3-second timeout */
netdev->neighbor_report_timeout =
l_timeout_create(3, netdev_neighbor_report_timeout,
netdev, NULL);
return 0;
}
static void netdev_action_frame_event(struct netdev *netdev,
const uint8_t *data, size_t len)
{

View File

@ -70,6 +70,9 @@ typedef void (*netdev_destroy_func_t)(void *user_data);
typedef void (*netdev_eapol_event_func_t)(unsigned int event,
const void *event_data,
void *user_data);
typedef void (*netdev_neighbor_report_cb_t)(struct netdev *netdev,
const uint8_t *reports,
size_t reports_len, void *user_data);
const uint8_t *netdev_get_address(struct netdev *netdev);
uint32_t netdev_get_ifindex(struct netdev *netdev);
@ -94,6 +97,9 @@ int netdev_set_powered(struct netdev *netdev, bool powered,
netdev_set_powered_cb_t cb, void *user_data,
netdev_destroy_func_t destroy);
int netdev_neighbor_report_req(struct netdev *netdev,
netdev_neighbor_report_cb_t cb);
struct netdev *netdev_find(int ifindex);
uint32_t netdev_watch_add(struct netdev *netdev, netdev_watch_func_t func,