device/ap: Moved AP dbus interface into ap.c

Now, a user can setup an AP as follows:

- Set device "Mode" to ap (ap interface will appear on bus)
- call "Start()" on AP interface

Issuing "Stop()" on the AP interface will stop and cleanup
the internal AP structures, but the AP interface will remain
up. To shutdown completely the device Mode must be switched
back to station. If the AP interface is running, the Mode can
directly be switched to station without calling Stop; this
has the same effect and will take down the AP interface.
This commit is contained in:
James Prestwood 2018-06-29 16:29:17 -07:00 committed by Denis Kenzior
parent 9976007dd8
commit fd79297553
5 changed files with 227 additions and 149 deletions

180
src/ap.c
View File

@ -43,12 +43,12 @@
#include "src/eapol.h"
#include "src/handshake.h"
#include "src/ap.h"
#include "src/dbus.h"
struct ap_state {
struct device *device;
char *ssid;
char *psk;
ap_event_cb_t event_cb;
int channel;
unsigned int ciphers;
uint32_t beacon_interval;
@ -60,6 +60,9 @@ struct ap_state {
uint16_t last_aid;
struct l_queue *sta_states;
struct l_dbus_message *pending;
bool started : 1;
};
struct sta_state {
@ -80,8 +83,6 @@ struct sta_state {
static struct l_genl_family *nl80211 = NULL;
static struct l_queue *ap_list = NULL;
static void ap_sta_free(void *data)
{
struct sta_state *sta = data;
@ -106,11 +107,14 @@ static void ap_frame_watch_remove(void *data, void *user_data)
netdev_frame_watch_remove(netdev, L_PTR_TO_UINT(data));
}
static void ap_free(void *data)
static void ap_reset(struct ap_state *ap)
{
struct ap_state *ap = data;
struct netdev *netdev = device_get_netdev(ap->device);
if (ap->pending)
dbus_pending_reply(&ap->pending,
dbus_error_aborted(ap->pending));
l_free(ap->ssid);
memset(ap->psk, 0, strlen(ap->psk));
l_free(ap->psk);
@ -128,6 +132,15 @@ static void ap_free(void *data)
if (ap->rates)
l_uintset_free(ap->rates);
ap->started = false;
}
static void ap_free(void *data)
{
struct ap_state *ap = data;
ap_reset(ap);
l_free(ap);
}
@ -1136,15 +1149,6 @@ static void ap_deauth_cb(struct netdev *netdev, const struct mmpdu_header *hdr,
ap_sta_free(sta);
}
static void ap_stopped(struct ap_state *ap)
{
ap->event_cb(ap->device, AP_EVENT_STOPPED);
ap_free(ap);
l_queue_remove(ap_list, ap);
}
static void ap_netdev_notify(struct netdev *netdev,
enum netdev_watch_event event, void *user_data)
{
@ -1152,7 +1156,7 @@ static void ap_netdev_notify(struct netdev *netdev,
switch (event) {
case NETDEV_WATCH_EVENT_DOWN:
ap_stopped(ap);
ap_reset(ap);
break;
default:
break;
@ -1165,22 +1169,23 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
ap->start_stop_cmd_id = 0;
if (!ap->pending)
return;
if (l_genl_msg_get_error(msg) < 0) {
l_error("START_AP failed: %i", l_genl_msg_get_error(msg));
ap_stopped(ap);
} else {
l_info("START_AP ok");
dbus_pending_reply(&ap->pending,
dbus_error_invalid_args(ap->pending));
ap_reset(ap);
ap->event_cb(ap->device, AP_EVENT_STARTED);
return;
}
}
static bool ap_match_device(const void *a, const void *b)
{
const struct ap_state *ap = a;
dbus_pending_reply(&ap->pending,
l_dbus_message_new_method_return(ap->pending));
return ap->device == b;
ap->started = true;
}
static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
@ -1250,24 +1255,17 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
return cmd;
}
int ap_start(struct device *device, const char *ssid, const char *psk,
ap_event_cb_t event_cb)
static int ap_start(struct ap_state *ap, const char *ssid, const char *psk,
struct l_dbus_message *message)
{
struct netdev *netdev = device_get_netdev(device);
struct wiphy *wiphy = device_get_wiphy(device);
struct ap_state *ap;
struct netdev *netdev = device_get_netdev(ap->device);
struct wiphy *wiphy = device_get_wiphy(ap->device);
struct l_genl_msg *cmd;
const struct l_queue_entry *entry;
uint32_t id;
if (l_queue_find(ap_list, ap_match_device, device))
return -EEXIST;
ap = l_new(struct ap_state, 1);
ap->device = device;
ap->ssid = l_strdup(ssid);
ap->psk = l_strdup(psk);
ap->event_cb = event_cb;
/* TODO: Start a Get Survey to decide the channel */
ap->channel = 6;
/* TODO: Add all ciphers supported by wiphy */
@ -1333,15 +1331,12 @@ int ap_start(struct device *device, const char *ssid, const char *psk,
ap->netdev_watch_id = netdev_watch_add(netdev, ap_netdev_notify, ap);
if (!ap_list)
ap_list = l_queue_new();
l_queue_push_tail(ap_list, ap);
ap->pending = l_dbus_message_ref(message);
return 0;
error:
ap_free(ap);
ap_reset(ap);
return -EIO;
}
@ -1352,12 +1347,21 @@ static void ap_stop_cb(struct l_genl_msg *msg, void *user_data)
ap->start_stop_cmd_id = 0;
if (l_genl_msg_get_error(msg) < 0)
l_error("STOP_AP failed: %i", l_genl_msg_get_error(msg));
else
l_info("STOP_AP ok");
if (!ap->pending)
goto end;
ap_stopped(ap);
if (l_genl_msg_get_error(msg) < 0) {
l_error("STOP_AP failed: %i", l_genl_msg_get_error(msg));
dbus_pending_reply(&ap->pending,
dbus_error_failed(ap->pending));
goto end;
}
dbus_pending_reply(&ap->pending,
l_dbus_message_new_method_return(ap->pending));
end:
ap_reset(ap);
}
static struct l_genl_msg *ap_build_cmd_stop_ap(struct ap_state *ap)
@ -1371,13 +1375,9 @@ static struct l_genl_msg *ap_build_cmd_stop_ap(struct ap_state *ap)
return cmd;
}
int ap_stop(struct device *device)
static int ap_stop(struct ap_state *ap, struct l_dbus_message *message)
{
struct l_genl_msg *cmd;
struct ap_state *ap = l_queue_find(ap_list, ap_match_device, device);
if (!ap)
return -ENODEV;
cmd = ap_build_cmd_stop_ap(ap);
if (!cmd)
@ -1393,13 +1393,70 @@ int ap_stop(struct device *device)
return -EIO;
}
ap->pending = l_dbus_message_ref(message);
return 0;
}
void ap_init(struct l_genl_family *in)
static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
struct l_dbus_message *message, void *user_data)
{
struct ap_state *ap = user_data;
const char *ssid, *wpa2_psk;
if (ap->pending)
return dbus_error_busy(message);
if (ap->started)
return dbus_error_already_exists(message);
if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk))
return dbus_error_invalid_args(message);
if (ap_start(ap, ssid, wpa2_psk, message) < 0)
return dbus_error_invalid_args(message);
return NULL;
}
static struct l_dbus_message *ap_dbus_stop(struct l_dbus *dbus,
struct l_dbus_message *message, void *user_data)
{
struct ap_state *ap = user_data;
if (ap->pending)
return dbus_error_busy(message);
/* already stopped, no-op */
if (!ap->started)
return l_dbus_message_new_method_return(message);
if (ap_stop(ap, message) < 0)
return dbus_error_failed(message);
return NULL;
}
static void ap_setup_interface(struct l_dbus_interface *interface)
{
l_dbus_interface_method(interface, "Start", 0, ap_dbus_start, "",
"ss", "ssid", "wpa2_psk");
l_dbus_interface_method(interface, "Stop", 0, ap_dbus_stop, "", "");
}
static void ap_destroy_interface(void *user_data)
{
struct ap_state *ap = user_data;
ap_free(ap);
}
bool ap_init(struct l_genl_family *in)
{
nl80211 = in;
return l_dbus_register_interface(dbus_get_bus(), IWD_AP_INTERFACE,
ap_setup_interface, ap_destroy_interface, false);
/*
* TODO: Check wiphy supports AP mode, supported channels,
* check wiphy's NL80211_ATTR_TX_FRAME_TYPES.
@ -1408,5 +1465,24 @@ void ap_init(struct l_genl_family *in)
void ap_exit(void)
{
l_queue_destroy(ap_list, ap_free);
l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
}
bool ap_add_interface(struct device *device)
{
struct ap_state *ap;
/* just allocate/set device, Start method will complete setup */
ap = l_new(struct ap_state, 1);
ap->device = device;
/* setup ap dbus interface */
return l_dbus_object_add_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE, ap);
}
bool ap_remove_interface(struct device *device)
{
return l_dbus_object_remove_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE);
}

View File

@ -29,9 +29,8 @@ struct device;
typedef void (*ap_event_cb_t)(struct device *device, enum ap_event event_type);
int ap_start(struct device *device, const char *ssid, const char *psk,
ap_event_cb_t event_cb);
int ap_stop(struct device *device);
bool ap_add_interface(struct device *device);
bool ap_remove_interface(struct device *device);
void ap_init(struct l_genl_family *in);
bool ap_init(struct l_genl_family *in);
void ap_exit(void);

View File

@ -94,6 +94,8 @@ struct device {
bool seen_hidden_networks : 1;
uint32_t ap_roam_watch;
enum device_mode mode;
};
struct signal_agent {
@ -169,8 +171,6 @@ static const char *device_state_to_string(enum device_state state)
return "disconnecting";
case DEVICE_STATE_ROAMING:
return "roaming";
case DEVICE_STATE_AP:
return "accesspoint";
}
return "invalid";
@ -407,7 +407,7 @@ static bool new_scan_results(uint32_t wiphy_id, uint32_t ifindex, int err,
if (err)
return false;
if (device->state == DEVICE_STATE_AP)
if (device->mode == DEVICE_MODE_AP)
return false;
device_set_scan_results(device, bss_list);
@ -464,6 +464,11 @@ enum device_state device_get_state(struct device *device)
return device->state;
}
enum device_mode device_get_mode(struct device *device)
{
return device->mode;
}
static void periodic_scan_trigger(int err, void *user_data)
{
struct device *device = user_data;
@ -537,9 +542,6 @@ static void device_enter_state(struct device *device, enum device_state state)
break;
case DEVICE_STATE_ROAMING:
break;
case DEVICE_STATE_AP:
periodic_scan_stop(device);
break;
}
disconnected = device->state <= DEVICE_STATE_AUTOCONNECT;
@ -1770,7 +1772,7 @@ static struct l_dbus_message *device_scan(struct l_dbus *dbus,
return dbus_error_busy(message);
if (device->state == DEVICE_STATE_OFF ||
device->state == DEVICE_STATE_AP)
device->mode != DEVICE_MODE_STATION)
return dbus_error_failed(message);
device->scan_pending = l_dbus_message_ref(message);
@ -2043,73 +2045,17 @@ static struct l_dbus_message *device_signal_agent_unregister(
return l_dbus_message_new_method_return(message);
}
static void device_ap_event(struct device *device, enum ap_event event_type)
static struct l_dbus_message *device_set_mode_ap(struct device *device,
struct l_dbus_message *message)
{
struct l_dbus_message *reply;
switch (event_type) {
case AP_EVENT_STARTED:
if (!device->start_ap_pending)
break;
reply = l_dbus_message_new_method_return(
device->start_ap_pending);
dbus_pending_reply(&device->start_ap_pending, reply);
break;
case AP_EVENT_STOPPED:
if (device->state == DEVICE_STATE_AP) {
netdev_set_iftype(device->netdev,
NETDEV_IFTYPE_STATION);
device_enter_state(device, DEVICE_STATE_DISCONNECTED);
}
if (device->start_ap_pending) {
reply = dbus_error_failed(device->start_ap_pending);
dbus_pending_reply(&device->start_ap_pending, reply);
} else if (device->stop_ap_pending) {
reply = l_dbus_message_new_method_return(
device->stop_ap_pending);
dbus_pending_reply(&device->stop_ap_pending, reply);
}
break;
}
}
static struct l_dbus_message *device_start_ap(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
struct device *device = user_data;
const char *ssid, *wpa2_psk;
if (device->state == DEVICE_STATE_AP)
if (device->mode == DEVICE_MODE_AP)
return dbus_error_already_exists(message);
if (device->state != DEVICE_STATE_DISCONNECTED &&
device->state != DEVICE_STATE_AUTOCONNECT)
return dbus_error_busy(message);
if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk))
return dbus_error_invalid_args(message);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP);
if (ap_start(device, ssid, wpa2_psk, device_ap_event) < 0) {
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
return dbus_error_failed(message);
}
device_enter_state(device, DEVICE_STATE_AP);
device->start_ap_pending = l_dbus_message_ref(message);
periodic_scan_stop(device);
/* Drop all state we can related to client mode */
@ -2129,29 +2075,26 @@ static struct l_dbus_message *device_start_ap(struct l_dbus *dbus,
l_queue_destroy(device->networks_sorted, NULL);
device->networks_sorted = l_queue_new();
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP);
device->mode = DEVICE_MODE_AP;
ap_add_interface(device);
return NULL;
}
static struct l_dbus_message *device_stop_ap(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
static struct l_dbus_message *device_set_mode_sta(struct device *device,
struct l_dbus_message *message)
{
struct device *device = user_data;
if (device->state != DEVICE_STATE_AP)
if (device->mode != DEVICE_MODE_AP)
return dbus_error_not_found(message);
if (device->stop_ap_pending)
return dbus_error_busy(message);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
if (ap_stop(device) < 0)
return dbus_error_failed(message);
device->mode = DEVICE_MODE_STATION;
if (device->start_ap_pending)
dbus_pending_reply(&device->start_ap_pending,
dbus_error_aborted(device->start_ap_pending));
device->stop_ap_pending = l_dbus_message_ref(message);
ap_remove_interface(device);
return NULL;
}
@ -2339,6 +2282,13 @@ static bool device_property_get_state(struct l_dbus *dbus,
struct device *device = user_data;
const char *statestr = "unknown";
/* special case for AP mode */
if (device->mode == DEVICE_MODE_AP) {
l_dbus_message_builder_append_basic(builder, 's',
"accesspoint");
return true;
}
switch (device->state) {
case DEVICE_STATE_CONNECTED:
statestr = "connected";
@ -2357,9 +2307,6 @@ static bool device_property_get_state(struct l_dbus *dbus,
case DEVICE_STATE_ROAMING:
statestr = "roaming";
break;
case DEVICE_STATE_AP:
statestr = "accesspoint";
break;
}
l_dbus_message_builder_append_basic(builder, 's', statestr);
@ -2380,6 +2327,58 @@ static bool device_property_get_adapter(struct l_dbus *dbus,
return true;
}
static bool device_property_get_mode(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct device *device = user_data;
const char *modestr = "unknown";
switch (device->mode) {
case DEVICE_MODE_STATION:
modestr = "station";
break;
case DEVICE_MODE_AP:
modestr = "ap";
break;
}
l_dbus_message_builder_append_basic(builder, 's', modestr);
return true;
}
static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_iter *new_value,
l_dbus_property_complete_cb_t complete,
void *user_data)
{
struct device *device = user_data;
struct l_dbus_message *reply;
const char* mode;
if (!l_dbus_message_iter_get_variant(new_value, "s", &mode))
return dbus_error_invalid_args(message);
if (!strcmp(mode, "station")) {
reply = device_set_mode_sta(device, message);
if (reply)
return reply;
} else if (!strcmp(mode, "ap")) {
reply = device_set_mode_ap(device, message);
if (reply)
return reply;
} else {
return dbus_error_invalid_args(message);
}
complete(dbus, message, NULL);
return NULL;
}
static void setup_device_interface(struct l_dbus_interface *interface)
{
l_dbus_interface_method(interface, "Scan", 0,
@ -2395,11 +2394,6 @@ static void setup_device_interface(struct l_dbus_interface *interface)
l_dbus_interface_method(interface, "UnregisterSignalLevelAgent", 0,
device_signal_agent_unregister,
"", "o", "path");
l_dbus_interface_method(interface, "StartAccessPoint", 0,
device_start_ap, "", "ss", "ssid", "wpa2_psk");
l_dbus_interface_method(interface, "StopAccessPoint", 0,
device_stop_ap, "", "");
l_dbus_interface_property(interface, "Name", 0, "s",
device_property_get_name, NULL);
l_dbus_interface_property(interface, "Address", 0, "s",
@ -2419,6 +2413,9 @@ static void setup_device_interface(struct l_dbus_interface *interface)
device_property_get_state, NULL);
l_dbus_interface_property(interface, "Adapter", 0, "o",
device_property_get_adapter, NULL);
l_dbus_interface_property(interface, "Mode", 0, "s",
device_property_get_mode,
device_property_set_mode);
}
static void device_netdev_notify(struct netdev *netdev,

View File

@ -40,8 +40,12 @@ enum device_state {
DEVICE_STATE_CONNECTING, /* Connecting */
DEVICE_STATE_CONNECTED,
DEVICE_STATE_DISCONNECTING,
DEVICE_STATE_ROAMING,
DEVICE_STATE_AP,
DEVICE_STATE_ROAMING
};
enum device_mode {
DEVICE_MODE_STATION,
DEVICE_MODE_AP
};
typedef void (*device_watch_func_t)(struct device *device,
@ -62,6 +66,7 @@ struct netdev *device_get_netdev(struct device *device);
uint32_t device_get_ifindex(struct device *device);
const uint8_t *device_get_address(struct device *device);
enum device_state device_get_state(struct device *device);
enum device_mode device_get_mode(struct device *device);
uint32_t device_add_state_watch(struct device *device,
device_state_watch_func_t func,

View File

@ -476,6 +476,8 @@ static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target)
{
l_debug("%p", wsc);
if (device_get_mode(wsc->device) != DEVICE_MODE_STATION)
goto error;
/*
* For now we assign the target pointer directly, since we should not
* be triggering any more scans while disconnecting / connecting
@ -501,7 +503,6 @@ static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target)
case DEVICE_STATE_AUTOCONNECT:
case DEVICE_STATE_OFF:
case DEVICE_STATE_ROAMING:
case DEVICE_STATE_AP:
l_warn("wsc_check_can_connect: invalid device state");
break;
}