wsc: Refactor WSC D-Bus interface logic

Split the WSC D-Bus interface class (struct wsc) into a base class
common to station mode and P2P mode (struct wsc_dbus) and station-
specific logic like scanning, saving the credentials as a known network
and triggering the station-mode connection (struct wsc_station_dbus).

Make the base class and its utilities public in wsc.h for P2P use.
This commit is contained in:
Andrew Zaborowski 2020-01-16 05:29:30 +01:00 committed by Denis Kenzior
parent b10264dcc7
commit ce16ba3bf8
2 changed files with 234 additions and 139 deletions

360
src/wsc.c
View File

@ -61,20 +61,6 @@ struct wsc_enrollee {
bool disconnecting : 1;
};
struct wsc {
struct netdev *netdev;
struct station *station;
struct wsc_enrollee *enrollee;
struct l_dbus_message *pending;
struct l_dbus_message *pending_cancel;
uint8_t *wsc_ies;
size_t wsc_ies_size;
struct l_timeout *walk_timer;
uint32_t scan_id;
struct scan_bss *target;
uint32_t station_state_watch;
};
static struct l_dbus_message *wsc_error_session_overlap(
struct l_dbus_message *msg)
{
@ -469,7 +455,20 @@ void wsc_enrollee_destroy(struct wsc_enrollee *wsce)
wsc_enrollee_free(wsce);
}
static void wsc_try_credentials(struct wsc *wsc,
struct wsc_station_dbus {
struct wsc_dbus super;
struct wsc_enrollee *enrollee;
struct scan_bss *target;
struct netdev *netdev;
struct station *station;
uint8_t *wsc_ies;
size_t wsc_ies_size;
struct l_timeout *walk_timer;
uint32_t scan_id;
uint32_t station_state_watch;
};
static void wsc_try_credentials(struct wsc_station_dbus *wsc,
struct wsc_credentials_info *creds,
unsigned int n_creds)
{
@ -510,15 +509,15 @@ static void wsc_try_credentials(struct wsc *wsc,
}
station_connect_network(wsc->station, network, bss,
wsc->pending);
l_dbus_message_unref(wsc->pending);
wsc->pending = NULL;
wsc->super.pending_connect);
l_dbus_message_unref(wsc->super.pending_connect);
wsc->super.pending_connect = NULL;
return;
}
dbus_pending_reply(&wsc->pending,
wsc_error_not_reachable(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
wsc_error_not_reachable(wsc->super.pending_connect));
station_set_autoconnect(wsc->station, true);
}
@ -559,11 +558,10 @@ static void wsc_store_credentials(struct wsc_credentials_info *creds,
}
}
/* Done callback for when WSC is triggered by DBus methods */
static void wsc_dbus_done_cb(int err, struct wsc_credentials_info *creds,
unsigned int n_creds, void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_station_dbus *wsc = user_data;
wsc->enrollee = NULL;
wsc->target = NULL;
@ -578,27 +576,31 @@ static void wsc_dbus_done_cb(int err, struct wsc_credentials_info *creds,
break;
case -ECANCELED:
/* Send reply if we haven't already sent one e.g. in Cancel() */
if (wsc->pending)
dbus_pending_reply(&wsc->pending,
dbus_error_aborted(wsc->pending));
if (wsc->super.pending_connect)
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_aborted(
wsc->super.pending_connect));
if (wsc->pending_cancel)
dbus_pending_reply(&wsc->pending_cancel,
if (wsc->super.pending_cancel)
dbus_pending_reply(&wsc->super.pending_cancel,
l_dbus_message_new_method_return(
wsc->pending_cancel));
wsc->super.pending_cancel));
return;
case -ENOKEY:
dbus_pending_reply(&wsc->pending,
wsc_error_no_credentials(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
wsc_error_no_credentials(
wsc->super.pending_connect));
return;
case -EBUSY:
dbus_pending_reply(&wsc->pending,
dbus_error_busy(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_busy(
wsc->super.pending_connect));
return;
default:
dbus_pending_reply(&wsc->pending,
dbus_error_failed(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_failed(
wsc->super.pending_connect));
return;
}
@ -606,12 +608,14 @@ static void wsc_dbus_done_cb(int err, struct wsc_credentials_info *creds,
wsc_try_credentials(wsc, creds, n_creds);
}
static void wsc_connect(struct wsc *wsc)
static void wsc_connect(struct wsc_station_dbus *wsc)
{
const char *pin = NULL;
if (!strcmp(l_dbus_message_get_member(wsc->pending), "StartPin"))
l_dbus_message_get_arguments(wsc->pending, "s", &pin);
if (!strcmp(l_dbus_message_get_member(wsc->super.pending_connect),
"StartPin"))
l_dbus_message_get_arguments(wsc->super.pending_connect, "s",
&pin);
wsc->enrollee = wsc_enrollee_new(wsc->netdev, wsc->target, pin,
wsc_dbus_done_cb, wsc);
@ -623,7 +627,7 @@ static void wsc_connect(struct wsc *wsc)
static void station_state_watch(enum station_state state, void *userdata)
{
struct wsc *wsc = userdata;
struct wsc_station_dbus *wsc = userdata;
if (state != STATION_STATE_DISCONNECTED)
return;
@ -636,7 +640,8 @@ static void station_state_watch(enum station_state state, void *userdata)
wsc_connect(wsc);
}
static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target)
static void wsc_check_can_connect(struct wsc_station_dbus *wsc,
struct scan_bss *target)
{
l_debug("%p", wsc);
@ -671,10 +676,11 @@ static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target)
}
error:
wsc->target = NULL;
dbus_pending_reply(&wsc->pending, dbus_error_failed(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_failed(wsc->super.pending_connect));
}
static void wsc_cancel_scan(struct wsc *wsc)
static void wsc_cancel_scan(struct wsc_station_dbus *wsc)
{
l_free(wsc->wsc_ies);
wsc->wsc_ies = 0;
@ -692,30 +698,32 @@ static void wsc_cancel_scan(struct wsc *wsc)
static void walk_timeout(struct l_timeout *timeout, void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_station_dbus *wsc = user_data;
wsc_cancel_scan(wsc);
if (wsc->pending)
dbus_pending_reply(&wsc->pending,
wsc_error_walk_time_expired(wsc->pending));
if (wsc->super.pending_connect)
dbus_pending_reply(&wsc->super.pending_connect,
wsc_error_walk_time_expired(
wsc->super.pending_connect));
}
static void pin_timeout(struct l_timeout *timeout, void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_station_dbus *wsc = user_data;
wsc_cancel_scan(wsc);
if (wsc->pending)
dbus_pending_reply(&wsc->pending,
wsc_error_time_expired(wsc->pending));
if (wsc->super.pending_connect)
dbus_pending_reply(&wsc->super.pending_connect,
wsc_error_time_expired(
wsc->super.pending_connect));
}
static bool push_button_scan_results(int err, struct l_queue *bss_list,
void *userdata)
{
struct wsc *wsc = userdata;
struct wsc_station_dbus *wsc = userdata;
struct scan_bss *bss_2g;
struct scan_bss *bss_5g;
struct scan_bss *target;
@ -726,8 +734,8 @@ static bool push_button_scan_results(int err, struct l_queue *bss_list,
if (err) {
wsc_cancel_scan(wsc);
dbus_pending_reply(&wsc->pending,
dbus_error_failed(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_failed(wsc->super.pending_connect));
return false;
}
@ -828,8 +836,8 @@ static bool push_button_scan_results(int err, struct l_queue *bss_list,
session_overlap:
wsc_cancel_scan(wsc);
dbus_pending_reply(&wsc->pending,
wsc_error_session_overlap(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
wsc_error_session_overlap(wsc->super.pending_connect));
return false;
}
@ -873,15 +881,15 @@ static bool pin_scan_results(int err, struct l_queue *bss_list, void *userdata)
{
static const uint8_t wildcard_address[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct wsc *wsc = userdata;
struct wsc_station_dbus *wsc = userdata;
struct scan_bss *target = NULL;
const struct l_queue_entry *bss_entry;
struct wsc_probe_response probe_response;
if (err) {
wsc_cancel_scan(wsc);
dbus_pending_reply(&wsc->pending,
dbus_error_failed(wsc->pending));
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_failed(wsc->super.pending_connect));
return false;
}
@ -973,7 +981,7 @@ static bool pin_scan_results(int err, struct l_queue *bss_list, void *userdata)
return true;
}
static bool wsc_initiate_scan(struct wsc *wsc,
static bool wsc_initiate_scan(struct wsc_station_dbus *wsc,
enum wsc_device_password_id dpid,
scan_notify_func_t callback)
{
@ -1037,28 +1045,117 @@ static bool wsc_initiate_scan(struct wsc *wsc,
return true;
}
static const char *wsc_station_dbus_get_path(struct wsc_dbus *super)
{
struct wsc_station_dbus *wsc =
l_container_of(super, struct wsc_station_dbus, super);
return netdev_get_path(wsc->netdev);
}
static void wsc_station_dbus_connect(struct wsc_dbus *super,
const char *pin)
{
struct wsc_station_dbus *wsc =
l_container_of(super, struct wsc_station_dbus, super);
scan_notify_func_t scan_callback;
enum wsc_device_password_id dpid;
wsc->station = station_find(netdev_get_ifindex(wsc->netdev));
if (!wsc->station) {
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_not_available(
wsc->super.pending_connect));
return;
}
if (pin) {
if (strlen(pin) == 4 || wsc_pin_is_checksum_valid(pin))
dpid = WSC_DEVICE_PASSWORD_ID_DEFAULT;
else
dpid = WSC_DEVICE_PASSWORD_ID_USER_SPECIFIED;
scan_callback = pin_scan_results;
} else {
dpid = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
scan_callback = push_button_scan_results;
}
if (!wsc_initiate_scan(wsc, dpid, scan_callback)) {
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_failed(
wsc->super.pending_connect));
return;
}
if (pin) {
wsc->walk_timer = l_timeout_create(60, pin_timeout, wsc, NULL);
} else {
wsc->walk_timer = l_timeout_create(WALK_TIME, walk_timeout, wsc,
NULL);
}
}
static void wsc_station_dbus_cancel(struct wsc_dbus *super)
{
struct wsc_station_dbus *wsc =
l_container_of(super, struct wsc_station_dbus, super);
wsc_cancel_scan(wsc);
if (wsc->station_state_watch) {
station_remove_state_watch(wsc->station,
wsc->station_state_watch);
wsc->station_state_watch = 0;
wsc->target = NULL;
}
dbus_pending_reply(&wsc->super.pending_connect,
dbus_error_aborted(wsc->super.pending_connect));
if (wsc->enrollee)
wsc_enrollee_cancel(wsc->enrollee, true);
else
dbus_pending_reply(&wsc->super.pending_cancel,
l_dbus_message_new_method_return(
wsc->super.pending_cancel));
}
static void wsc_station_dbus_remove(struct wsc_dbus *super)
{
struct wsc_station_dbus *wsc =
l_container_of(super, struct wsc_station_dbus, super);
wsc_cancel_scan(wsc);
if (wsc->station_state_watch) {
station_remove_state_watch(wsc->station,
wsc->station_state_watch);
wsc->station_state_watch = 0;
}
if (wsc->enrollee)
wsc_enrollee_destroy(wsc->enrollee);
l_free(wsc);
}
static struct l_dbus_message *wsc_push_button(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_dbus *wsc = user_data;
l_debug("");
if (wsc->pending)
if (!l_dbus_message_get_arguments(message, ""))
return dbus_error_invalid_args(message);
if (wsc->pending_connect)
return dbus_error_busy(message);
wsc->station = station_find(netdev_get_ifindex(wsc->netdev));
if (!wsc->station)
return dbus_error_not_available(message);
if (!wsc_initiate_scan(wsc, WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON,
push_button_scan_results))
return dbus_error_failed(message);
wsc->walk_timer = l_timeout_create(WALK_TIME, walk_timeout, wsc, NULL);
wsc->pending = l_dbus_message_ref(message);
wsc->pending_connect = l_dbus_message_ref(message);
wsc->connect(wsc, NULL);
return NULL;
}
@ -1066,13 +1163,13 @@ static struct l_dbus_message *wsc_generate_pin(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_dbus *wsc = user_data;
struct l_dbus_message *reply;
char pin[9];
l_debug("");
if (wsc->pending)
if (wsc->pending_connect)
return dbus_error_busy(message);
if (!wsc_pin_generate(pin))
@ -1088,36 +1185,22 @@ static struct l_dbus_message *wsc_start_pin(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_dbus *wsc = user_data;
const char *pin;
enum wsc_device_password_id dpid;
l_debug("");
if (wsc->pending)
if (wsc->pending_connect)
return dbus_error_busy(message);
wsc->station = station_find(netdev_get_ifindex(wsc->netdev));
if (!wsc->station)
return dbus_error_not_available(message);
if (!l_dbus_message_get_arguments(message, "s", &pin))
return dbus_error_invalid_args(message);
if (!wsc_pin_is_valid(pin))
return dbus_error_invalid_format(message);
if (strlen(pin) == 4 || wsc_pin_is_checksum_valid(pin))
dpid = WSC_DEVICE_PASSWORD_ID_DEFAULT;
else
dpid = WSC_DEVICE_PASSWORD_ID_USER_SPECIFIED;
if (!wsc_initiate_scan(wsc, dpid, pin_scan_results))
return dbus_error_failed(message);
wsc->walk_timer = l_timeout_create(60, pin_timeout, wsc, NULL);
wsc->pending = l_dbus_message_ref(message);
wsc->pending_connect = l_dbus_message_ref(message);
wsc->connect(wsc, pin);
return NULL;
}
@ -1125,33 +1208,22 @@ static struct l_dbus_message *wsc_cancel(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
struct wsc *wsc = user_data;
struct wsc_dbus *wsc = user_data;
l_debug("");
if (!wsc->pending)
if (!l_dbus_message_get_arguments(message, ""))
return dbus_error_invalid_args(message);
if (!wsc->pending_connect)
return dbus_error_not_available(message);
if (wsc->pending_cancel)
return dbus_error_busy(message);
wsc_cancel_scan(wsc);
if (wsc->station_state_watch) {
station_remove_state_watch(wsc->station,
wsc->station_state_watch);
wsc->station_state_watch = 0;
wsc->target = NULL;
}
dbus_pending_reply(&wsc->pending, dbus_error_aborted(wsc->pending));
if (wsc->enrollee) {
wsc_enrollee_cancel(wsc->enrollee, true);
wsc->pending_cancel = l_dbus_message_ref(message);
return NULL;
} else
return l_dbus_message_new_method_return(message);
wsc->pending_cancel = l_dbus_message_ref(message);
wsc->cancel(wsc);
return NULL;
}
static void setup_wsc_interface(struct l_dbus_interface *interface)
@ -1166,36 +1238,46 @@ static void setup_wsc_interface(struct l_dbus_interface *interface)
wsc_cancel, "", "");
}
static void wsc_free(void *userdata)
bool wsc_dbus_add_interface(struct wsc_dbus *wsc)
{
struct wsc *wsc = userdata;
struct l_dbus *dbus = dbus_get_bus();
wsc_cancel_scan(wsc);
if (wsc->station_state_watch) {
station_remove_state_watch(wsc->station,
wsc->station_state_watch);
wsc->station_state_watch = 0;
if (!l_dbus_object_add_interface(dbus, wsc->get_path(wsc),
IWD_WSC_INTERFACE, wsc)) {
l_info("Unable to register %s interface", IWD_WSC_INTERFACE);
return false;
}
if (wsc->pending)
dbus_pending_reply(&wsc->pending,
dbus_error_not_available(wsc->pending));
return true;
}
void wsc_dbus_remove_interface(struct wsc_dbus *wsc)
{
struct l_dbus *dbus = dbus_get_bus();
l_dbus_object_remove_interface(dbus, wsc->get_path(wsc),
IWD_WSC_INTERFACE);
}
static void wsc_dbus_free(void *user_data)
{
struct wsc_dbus *wsc = user_data;
if (wsc->pending_connect)
dbus_pending_reply(&wsc->pending_connect,
dbus_error_not_available(
wsc->pending_connect));
if (wsc->pending_cancel)
dbus_pending_reply(&wsc->pending_cancel,
dbus_error_aborted(wsc->pending_cancel));
if (wsc->enrollee)
wsc_enrollee_destroy(wsc->enrollee);
l_free(wsc);
wsc->remove(wsc);
}
static void wsc_add_interface(struct netdev *netdev)
static void wsc_add_station(struct netdev *netdev)
{
struct l_dbus *dbus = dbus_get_bus();
struct wsc *wsc;
struct wsc_station_dbus *wsc;
if (!wiphy_get_max_scan_ie_len(netdev_get_wiphy(netdev))) {
l_debug("Simple Configuration isn't supported by ifindex %u",
@ -1204,18 +1286,18 @@ static void wsc_add_interface(struct netdev *netdev)
return;
}
wsc = l_new(struct wsc, 1);
wsc = l_new(struct wsc_station_dbus, 1);
wsc->netdev = netdev;
wsc->super.get_path = wsc_station_dbus_get_path;
wsc->super.connect = wsc_station_dbus_connect;
wsc->super.cancel = wsc_station_dbus_cancel;
wsc->super.remove = wsc_station_dbus_remove;
if (!l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
IWD_WSC_INTERFACE,
wsc)) {
wsc_free(wsc);
l_info("Unable to register %s interface", IWD_WSC_INTERFACE);
}
if (!wsc_dbus_add_interface(&wsc->super))
wsc_station_dbus_remove(&wsc->super);
}
static void wsc_remove_interface(struct netdev *netdev)
static void wsc_remove_station(struct netdev *netdev)
{
struct l_dbus *dbus = dbus_get_bus();
@ -1231,11 +1313,11 @@ static void wsc_netdev_watch(struct netdev *netdev,
case NETDEV_WATCH_EVENT_NEW:
if (netdev_get_iftype(netdev) == NETDEV_IFTYPE_STATION &&
netdev_get_is_up(netdev))
wsc_add_interface(netdev);
wsc_add_station(netdev);
break;
case NETDEV_WATCH_EVENT_DOWN:
case NETDEV_WATCH_EVENT_DEL:
wsc_remove_interface(netdev);
wsc_remove_station(netdev);
break;
default:
break;
@ -1248,7 +1330,7 @@ static int wsc_init(void)
netdev_watch = netdev_watch_add(wsc_netdev_watch, NULL, NULL);
l_dbus_register_interface(dbus_get_bus(), IWD_WSC_INTERFACE,
setup_wsc_interface,
wsc_free, false);
wsc_dbus_free, false);
return 0;
}

View File

@ -42,3 +42,16 @@ struct wsc_enrollee *wsc_enrollee_new(struct netdev *netdev,
wsc_done_cb_t done_cb, void *user_data);
void wsc_enrollee_cancel(struct wsc_enrollee *wsce, bool defer_cb);
void wsc_enrollee_destroy(struct wsc_enrollee *wsce);
struct wsc_dbus {
struct l_dbus_message *pending_connect;
struct l_dbus_message *pending_cancel;
const char *(*get_path)(struct wsc_dbus *wsc);
void (*connect)(struct wsc_dbus *wsc, const char *pin);
void (*cancel)(struct wsc_dbus *wsc);
void (*remove)(struct wsc_dbus *wsc);
};
bool wsc_dbus_add_interface(struct wsc_dbus *wsc);
void wsc_dbus_remove_interface(struct wsc_dbus *wsc);