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/eapol.h"
#include "src/handshake.h" #include "src/handshake.h"
#include "src/ap.h" #include "src/ap.h"
#include "src/dbus.h"
struct ap_state { struct ap_state {
struct device *device; struct device *device;
char *ssid; char *ssid;
char *psk; char *psk;
ap_event_cb_t event_cb;
int channel; int channel;
unsigned int ciphers; unsigned int ciphers;
uint32_t beacon_interval; uint32_t beacon_interval;
@ -60,6 +60,9 @@ struct ap_state {
uint16_t last_aid; uint16_t last_aid;
struct l_queue *sta_states; struct l_queue *sta_states;
struct l_dbus_message *pending;
bool started : 1;
}; };
struct sta_state { struct sta_state {
@ -80,8 +83,6 @@ struct sta_state {
static struct l_genl_family *nl80211 = NULL; static struct l_genl_family *nl80211 = NULL;
static struct l_queue *ap_list = NULL;
static void ap_sta_free(void *data) static void ap_sta_free(void *data)
{ {
struct sta_state *sta = 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)); 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); 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); l_free(ap->ssid);
memset(ap->psk, 0, strlen(ap->psk)); memset(ap->psk, 0, strlen(ap->psk));
l_free(ap->psk); l_free(ap->psk);
@ -128,6 +132,15 @@ static void ap_free(void *data)
if (ap->rates) if (ap->rates)
l_uintset_free(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); l_free(ap);
} }
@ -1136,15 +1149,6 @@ static void ap_deauth_cb(struct netdev *netdev, const struct mmpdu_header *hdr,
ap_sta_free(sta); 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, static void ap_netdev_notify(struct netdev *netdev,
enum netdev_watch_event event, void *user_data) enum netdev_watch_event event, void *user_data)
{ {
@ -1152,7 +1156,7 @@ static void ap_netdev_notify(struct netdev *netdev,
switch (event) { switch (event) {
case NETDEV_WATCH_EVENT_DOWN: case NETDEV_WATCH_EVENT_DOWN:
ap_stopped(ap); ap_reset(ap);
break; break;
default: default:
break; break;
@ -1165,22 +1169,23 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
ap->start_stop_cmd_id = 0; ap->start_stop_cmd_id = 0;
if (!ap->pending)
return;
if (l_genl_msg_get_error(msg) < 0) { if (l_genl_msg_get_error(msg) < 0) {
l_error("START_AP failed: %i", l_genl_msg_get_error(msg)); l_error("START_AP failed: %i", l_genl_msg_get_error(msg));
ap_stopped(ap); dbus_pending_reply(&ap->pending,
} else { dbus_error_invalid_args(ap->pending));
l_info("START_AP ok"); ap_reset(ap);
ap->event_cb(ap->device, AP_EVENT_STARTED); return;
} }
}
static bool ap_match_device(const void *a, const void *b) dbus_pending_reply(&ap->pending,
{ l_dbus_message_new_method_return(ap->pending));
const struct ap_state *ap = a;
return ap->device == b; ap->started = true;
} }
static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap) 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; return cmd;
} }
int ap_start(struct device *device, const char *ssid, const char *psk, static int ap_start(struct ap_state *ap, const char *ssid, const char *psk,
ap_event_cb_t event_cb) struct l_dbus_message *message)
{ {
struct netdev *netdev = device_get_netdev(device); struct netdev *netdev = device_get_netdev(ap->device);
struct wiphy *wiphy = device_get_wiphy(device); struct wiphy *wiphy = device_get_wiphy(ap->device);
struct ap_state *ap;
struct l_genl_msg *cmd; struct l_genl_msg *cmd;
const struct l_queue_entry *entry; const struct l_queue_entry *entry;
uint32_t id; 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->ssid = l_strdup(ssid);
ap->psk = l_strdup(psk); ap->psk = l_strdup(psk);
ap->event_cb = event_cb;
/* TODO: Start a Get Survey to decide the channel */ /* TODO: Start a Get Survey to decide the channel */
ap->channel = 6; ap->channel = 6;
/* TODO: Add all ciphers supported by wiphy */ /* 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); ap->netdev_watch_id = netdev_watch_add(netdev, ap_netdev_notify, ap);
if (!ap_list) ap->pending = l_dbus_message_ref(message);
ap_list = l_queue_new();
l_queue_push_tail(ap_list, ap);
return 0; return 0;
error: error:
ap_free(ap); ap_reset(ap);
return -EIO; 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; ap->start_stop_cmd_id = 0;
if (l_genl_msg_get_error(msg) < 0) if (!ap->pending)
l_error("STOP_AP failed: %i", l_genl_msg_get_error(msg)); goto end;
else
l_info("STOP_AP ok");
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) 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; 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 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); cmd = ap_build_cmd_stop_ap(ap);
if (!cmd) if (!cmd)
@ -1393,13 +1393,70 @@ int ap_stop(struct device *device)
return -EIO; return -EIO;
} }
ap->pending = l_dbus_message_ref(message);
return 0; 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; 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, * TODO: Check wiphy supports AP mode, supported channels,
* check wiphy's NL80211_ATTR_TX_FRAME_TYPES. * check wiphy's NL80211_ATTR_TX_FRAME_TYPES.
@ -1408,5 +1465,24 @@ void ap_init(struct l_genl_family *in)
void ap_exit(void) 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); 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, bool ap_add_interface(struct device *device);
ap_event_cb_t event_cb); bool ap_remove_interface(struct device *device);
int ap_stop(struct device *device);
void ap_init(struct l_genl_family *in); bool ap_init(struct l_genl_family *in);
void ap_exit(void); void ap_exit(void);

View File

@ -94,6 +94,8 @@ struct device {
bool seen_hidden_networks : 1; bool seen_hidden_networks : 1;
uint32_t ap_roam_watch; uint32_t ap_roam_watch;
enum device_mode mode;
}; };
struct signal_agent { struct signal_agent {
@ -169,8 +171,6 @@ static const char *device_state_to_string(enum device_state state)
return "disconnecting"; return "disconnecting";
case DEVICE_STATE_ROAMING: case DEVICE_STATE_ROAMING:
return "roaming"; return "roaming";
case DEVICE_STATE_AP:
return "accesspoint";
} }
return "invalid"; return "invalid";
@ -407,7 +407,7 @@ static bool new_scan_results(uint32_t wiphy_id, uint32_t ifindex, int err,
if (err) if (err)
return false; return false;
if (device->state == DEVICE_STATE_AP) if (device->mode == DEVICE_MODE_AP)
return false; return false;
device_set_scan_results(device, bss_list); device_set_scan_results(device, bss_list);
@ -464,6 +464,11 @@ enum device_state device_get_state(struct device *device)
return device->state; 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) static void periodic_scan_trigger(int err, void *user_data)
{ {
struct device *device = user_data; struct device *device = user_data;
@ -537,9 +542,6 @@ static void device_enter_state(struct device *device, enum device_state state)
break; break;
case DEVICE_STATE_ROAMING: case DEVICE_STATE_ROAMING:
break; break;
case DEVICE_STATE_AP:
periodic_scan_stop(device);
break;
} }
disconnected = device->state <= DEVICE_STATE_AUTOCONNECT; 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); return dbus_error_busy(message);
if (device->state == DEVICE_STATE_OFF || if (device->state == DEVICE_STATE_OFF ||
device->state == DEVICE_STATE_AP) device->mode != DEVICE_MODE_STATION)
return dbus_error_failed(message); return dbus_error_failed(message);
device->scan_pending = l_dbus_message_ref(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); 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; if (device->mode == DEVICE_MODE_AP)
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)
return dbus_error_already_exists(message); return dbus_error_already_exists(message);
if (device->state != DEVICE_STATE_DISCONNECTED && if (device->state != DEVICE_STATE_DISCONNECTED &&
device->state != DEVICE_STATE_AUTOCONNECT) device->state != DEVICE_STATE_AUTOCONNECT)
return dbus_error_busy(message); return dbus_error_busy(message);
if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk)) periodic_scan_stop(device);
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);
/* Drop all state we can related to client mode */ /* 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); l_queue_destroy(device->networks_sorted, NULL);
device->networks_sorted = l_queue_new(); 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; return NULL;
} }
static struct l_dbus_message *device_stop_ap(struct l_dbus *dbus, static struct l_dbus_message *device_set_mode_sta(struct device *device,
struct l_dbus_message *message, struct l_dbus_message *message)
void *user_data)
{ {
struct device *device = user_data; if (device->mode != DEVICE_MODE_AP)
if (device->state != DEVICE_STATE_AP)
return dbus_error_not_found(message); return dbus_error_not_found(message);
if (device->stop_ap_pending) netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
return dbus_error_busy(message);
if (ap_stop(device) < 0) device->mode = DEVICE_MODE_STATION;
return dbus_error_failed(message);
if (device->start_ap_pending) ap_remove_interface(device);
dbus_pending_reply(&device->start_ap_pending,
dbus_error_aborted(device->start_ap_pending));
device->stop_ap_pending = l_dbus_message_ref(message);
return NULL; return NULL;
} }
@ -2339,6 +2282,13 @@ static bool device_property_get_state(struct l_dbus *dbus,
struct device *device = user_data; struct device *device = user_data;
const char *statestr = "unknown"; 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) { switch (device->state) {
case DEVICE_STATE_CONNECTED: case DEVICE_STATE_CONNECTED:
statestr = "connected"; statestr = "connected";
@ -2357,9 +2307,6 @@ static bool device_property_get_state(struct l_dbus *dbus,
case DEVICE_STATE_ROAMING: case DEVICE_STATE_ROAMING:
statestr = "roaming"; statestr = "roaming";
break; break;
case DEVICE_STATE_AP:
statestr = "accesspoint";
break;
} }
l_dbus_message_builder_append_basic(builder, 's', statestr); 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; 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) static void setup_device_interface(struct l_dbus_interface *interface)
{ {
l_dbus_interface_method(interface, "Scan", 0, 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, l_dbus_interface_method(interface, "UnregisterSignalLevelAgent", 0,
device_signal_agent_unregister, device_signal_agent_unregister,
"", "o", "path"); "", "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", l_dbus_interface_property(interface, "Name", 0, "s",
device_property_get_name, NULL); device_property_get_name, NULL);
l_dbus_interface_property(interface, "Address", 0, "s", 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); device_property_get_state, NULL);
l_dbus_interface_property(interface, "Adapter", 0, "o", l_dbus_interface_property(interface, "Adapter", 0, "o",
device_property_get_adapter, NULL); 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, static void device_netdev_notify(struct netdev *netdev,

View File

@ -40,8 +40,12 @@ enum device_state {
DEVICE_STATE_CONNECTING, /* Connecting */ DEVICE_STATE_CONNECTING, /* Connecting */
DEVICE_STATE_CONNECTED, DEVICE_STATE_CONNECTED,
DEVICE_STATE_DISCONNECTING, DEVICE_STATE_DISCONNECTING,
DEVICE_STATE_ROAMING, DEVICE_STATE_ROAMING
DEVICE_STATE_AP, };
enum device_mode {
DEVICE_MODE_STATION,
DEVICE_MODE_AP
}; };
typedef void (*device_watch_func_t)(struct device *device, 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); uint32_t device_get_ifindex(struct device *device);
const uint8_t *device_get_address(struct device *device); const uint8_t *device_get_address(struct device *device);
enum device_state device_get_state(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, uint32_t device_add_state_watch(struct device *device,
device_state_watch_func_t func, 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); 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 * For now we assign the target pointer directly, since we should not
* be triggering any more scans while disconnecting / connecting * 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_AUTOCONNECT:
case DEVICE_STATE_OFF: case DEVICE_STATE_OFF:
case DEVICE_STATE_ROAMING: case DEVICE_STATE_ROAMING:
case DEVICE_STATE_AP:
l_warn("wsc_check_can_connect: invalid device state"); l_warn("wsc_check_can_connect: invalid device state");
break; break;
} }