mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-22 23:09:34 +01:00
p2p: Add the WSC interface on peer DBus objects
Add net.connman.iwd.SimpleConfiguration interfaces to peer objects on DBus and handle method calls. Building and transmitting the actual action frames to start the connection sequence is done in the following commits.
This commit is contained in:
parent
3d4725870d
commit
1675c765a3
290
src/p2p.c
290
src/p2p.c
@ -81,6 +81,12 @@ struct p2p_device {
|
|||||||
struct l_queue *discovery_users;
|
struct l_queue *discovery_users;
|
||||||
struct l_queue *peer_list;
|
struct l_queue *peer_list;
|
||||||
|
|
||||||
|
struct p2p_peer *conn_peer;
|
||||||
|
uint16_t conn_config_method;
|
||||||
|
char *conn_pin;
|
||||||
|
uint8_t conn_addr[6];
|
||||||
|
uint16_t conn_password_id;
|
||||||
|
|
||||||
bool enabled : 1;
|
bool enabled : 1;
|
||||||
bool have_roc_cookie : 1;
|
bool have_roc_cookie : 1;
|
||||||
};
|
};
|
||||||
@ -186,10 +192,14 @@ static void p2p_peer_put(void *user_data)
|
|||||||
{
|
{
|
||||||
struct p2p_peer *peer = user_data;
|
struct p2p_peer *peer = user_data;
|
||||||
|
|
||||||
|
/* Removes both interfaces, no need to call wsc_dbus_remove_interface */
|
||||||
l_dbus_unregister_object(dbus_get_bus(), p2p_peer_get_path(peer));
|
l_dbus_unregister_object(dbus_get_bus(), p2p_peer_get_path(peer));
|
||||||
p2p_peer_free(peer);
|
p2p_peer_free(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void p2p_device_discovery_start(struct p2p_device *dev);
|
||||||
|
static void p2p_device_discovery_stop(struct p2p_device *dev);
|
||||||
|
|
||||||
/* TODO: convert to iovecs */
|
/* TODO: convert to iovecs */
|
||||||
static uint8_t *p2p_build_scan_ies(struct p2p_device *dev, uint8_t *buf,
|
static uint8_t *p2p_build_scan_ies(struct p2p_device *dev, uint8_t *buf,
|
||||||
size_t buf_len, size_t *out_len)
|
size_t buf_len, size_t *out_len)
|
||||||
@ -256,6 +266,61 @@ static uint8_t *p2p_build_scan_ies(struct p2p_device *dev, uint8_t *buf,
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void p2p_connection_reset(struct p2p_device *dev)
|
||||||
|
{
|
||||||
|
struct p2p_peer *peer = dev->conn_peer;
|
||||||
|
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* conn_peer is currently not refcounted and we make sure it's always
|
||||||
|
* on the dev->peer_list so we can just drop our reference. Since we
|
||||||
|
* may not have been scanning for a while, don't drop the peer object
|
||||||
|
* now just because it's not been seen in scan results recently, its
|
||||||
|
* age will be checked on the next scan.
|
||||||
|
*/
|
||||||
|
dev->conn_peer = NULL;
|
||||||
|
dev->connections_left++;
|
||||||
|
|
||||||
|
if (dev->conn_pin) {
|
||||||
|
explicit_bzero(dev->conn_pin, strlen(dev->conn_pin));
|
||||||
|
l_free(dev->conn_pin);
|
||||||
|
dev->conn_pin = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
l_dbus_property_changed(dbus_get_bus(), p2p_device_get_path(dev),
|
||||||
|
IWD_P2P_INTERFACE, "AvailableConnections");
|
||||||
|
|
||||||
|
if (!dev->enabled || (dev->enabled && dev->start_stop_cmd_id)) {
|
||||||
|
/*
|
||||||
|
* The device has been disabled in the mean time, all peers
|
||||||
|
* have been removed except this one. Now it's safe to
|
||||||
|
* drop this peer from the scan results too.
|
||||||
|
*/
|
||||||
|
l_queue_destroy(dev->peer_list, p2p_peer_put);
|
||||||
|
dev->peer_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->enabled && !dev->start_stop_cmd_id &&
|
||||||
|
!l_queue_isempty(dev->discovery_users))
|
||||||
|
p2p_device_discovery_start(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_connect_failed(struct p2p_device *dev)
|
||||||
|
{
|
||||||
|
struct p2p_peer *peer = dev->conn_peer;
|
||||||
|
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (peer->wsc.pending_connect)
|
||||||
|
dbus_pending_reply(&peer->wsc.pending_connect,
|
||||||
|
dbus_error_failed(peer->wsc.pending_connect));
|
||||||
|
|
||||||
|
p2p_connection_reset(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static void p2p_scan_destroy(void *user_data)
|
static void p2p_scan_destroy(void *user_data)
|
||||||
{
|
{
|
||||||
struct p2p_device *dev = user_data;
|
struct p2p_device *dev = user_data;
|
||||||
@ -263,6 +328,165 @@ static void p2p_scan_destroy(void *user_data)
|
|||||||
dev->scan_id = 0;
|
dev->scan_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void p2p_start_go_negotiation(struct p2p_device *dev)
|
||||||
|
{
|
||||||
|
/* TODO: start GO Negotiation */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_start_provision_discovery(struct p2p_device *dev)
|
||||||
|
{
|
||||||
|
/* TODO: start Provision Discovery */
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool p2p_peer_get_info(struct p2p_peer *peer,
|
||||||
|
uint16_t *wsc_config_methods,
|
||||||
|
struct p2p_capability_attr **capability)
|
||||||
|
{
|
||||||
|
struct wsc_probe_request wsc_info;
|
||||||
|
|
||||||
|
switch (peer->bss->source_frame) {
|
||||||
|
case SCAN_BSS_PROBE_RESP:
|
||||||
|
if (!peer->bss->p2p_probe_resp_info)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (wsc_config_methods)
|
||||||
|
*wsc_config_methods = peer->bss->p2p_probe_resp_info->
|
||||||
|
device_info.wsc_config_methods;
|
||||||
|
|
||||||
|
*capability = &peer->bss->p2p_probe_resp_info->capability;
|
||||||
|
return true;
|
||||||
|
case SCAN_BSS_PROBE_REQ:
|
||||||
|
if (!peer->bss->p2p_probe_req_info || !peer->bss->wsc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (wsc_parse_probe_request(peer->bss->wsc, peer->bss->wsc_size,
|
||||||
|
&wsc_info) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (wsc_config_methods)
|
||||||
|
*wsc_config_methods = wsc_info.config_methods;
|
||||||
|
|
||||||
|
*capability = &peer->bss->p2p_probe_req_info->capability;
|
||||||
|
return true;
|
||||||
|
case SCAN_BSS_BEACON:
|
||||||
|
if (!peer->bss->p2p_beacon_info || !peer->bss->wsc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (wsc_parse_probe_request(peer->bss->wsc, peer->bss->wsc_size,
|
||||||
|
&wsc_info) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (wsc_config_methods)
|
||||||
|
*wsc_config_methods = wsc_info.config_methods;
|
||||||
|
|
||||||
|
*capability = &peer->bss->p2p_beacon_info->capability;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_peer_connect(struct p2p_peer *peer, const char *pin)
|
||||||
|
{
|
||||||
|
struct p2p_device *dev = peer->dev;
|
||||||
|
uint16_t wsc_config_methods;
|
||||||
|
struct p2p_capability_attr *capability;
|
||||||
|
struct l_dbus_message *message = peer->wsc.pending_connect;
|
||||||
|
struct l_dbus_message *reply;
|
||||||
|
|
||||||
|
if (dev->conn_peer) {
|
||||||
|
reply = dbus_error_busy(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 1, check if the device indicates it supports our WSC method
|
||||||
|
* and check other flags to make sure a connection is possible.
|
||||||
|
*/
|
||||||
|
if (!p2p_peer_get_info(peer, &wsc_config_methods, &capability)) {
|
||||||
|
reply = dbus_error_failed(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->conn_config_method = pin ? WSC_CONFIGURATION_METHOD_KEYPAD :
|
||||||
|
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||||
|
dev->conn_password_id = pin ?
|
||||||
|
(strlen(pin) == 4 || wsc_pin_is_checksum_valid(pin) ?
|
||||||
|
WSC_DEVICE_PASSWORD_ID_DEFAULT :
|
||||||
|
WSC_DEVICE_PASSWORD_ID_USER_SPECIFIED) :
|
||||||
|
WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
|
||||||
|
|
||||||
|
if (!(wsc_config_methods & dev->conn_config_method)) {
|
||||||
|
reply = dbus_error_not_supported(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capability->device_caps & P2P_DEVICE_CAP_DEVICE_LIMIT) {
|
||||||
|
reply = dbus_error_not_supported(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capability->group_caps & P2P_GROUP_CAP_GROUP_LIMIT) {
|
||||||
|
reply = dbus_error_not_supported(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capability->group_caps & P2P_GROUP_CAP_GROUP_FORMATION) {
|
||||||
|
reply = dbus_error_busy(message);
|
||||||
|
goto send_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
p2p_device_discovery_stop(dev);
|
||||||
|
|
||||||
|
/* Generate the interface address for our P2P-Client connection */
|
||||||
|
wiphy_generate_random_address(dev->wiphy, dev->conn_addr);
|
||||||
|
|
||||||
|
dev->conn_peer = peer; /* No ref counting so just set the pointer */
|
||||||
|
dev->conn_pin = l_strdup(pin);
|
||||||
|
dev->connections_left--;
|
||||||
|
l_dbus_property_changed(dbus_get_bus(), p2p_device_get_path(dev),
|
||||||
|
IWD_P2P_INTERFACE, "AvailableConnections");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 2, if peer is already a GO then send the Provision Discovery
|
||||||
|
* before doing WSC. If it's not then do Provision Discovery
|
||||||
|
* optionally as seems to be required by some implementations, and
|
||||||
|
* start GO negotiation following that.
|
||||||
|
* TODO: Add a AlwaysUsePD config setting.
|
||||||
|
*/
|
||||||
|
if (dev->conn_peer->group)
|
||||||
|
p2p_start_provision_discovery(dev);
|
||||||
|
else
|
||||||
|
p2p_start_go_negotiation(dev);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
send_error:
|
||||||
|
dbus_pending_reply(&peer->wsc.pending_connect, reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_peer_disconnect(struct p2p_peer *peer)
|
||||||
|
{
|
||||||
|
struct p2p_device *dev = peer->dev;
|
||||||
|
struct l_dbus_message *message = peer->wsc.pending_cancel;
|
||||||
|
struct l_dbus_message *reply;
|
||||||
|
|
||||||
|
if (dev->conn_peer != peer) {
|
||||||
|
reply = dbus_error_not_connected(message);
|
||||||
|
goto send_reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer->wsc.pending_connect)
|
||||||
|
dbus_pending_reply(&peer->wsc.pending_connect,
|
||||||
|
dbus_error_aborted(peer->wsc.pending_connect));
|
||||||
|
|
||||||
|
p2p_connection_reset(dev);
|
||||||
|
reply = l_dbus_message_new_method_return(message);
|
||||||
|
|
||||||
|
send_reply:
|
||||||
|
dbus_pending_reply(&peer->wsc.pending_cancel, reply);
|
||||||
|
}
|
||||||
|
|
||||||
#define SCAN_INTERVAL_MAX 3
|
#define SCAN_INTERVAL_MAX 3
|
||||||
#define SCAN_INTERVAL_STEP 1
|
#define SCAN_INTERVAL_STEP 1
|
||||||
#define CHANS_PER_SCAN_INITIAL 2
|
#define CHANS_PER_SCAN_INITIAL 2
|
||||||
@ -413,6 +637,29 @@ static void p2p_device_roc_start(struct p2p_device *dev)
|
|||||||
(int) dev->listen_channel, (int) duration);
|
(int) dev->listen_channel, (int) duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *p2p_peer_wsc_get_path(struct wsc_dbus *wsc)
|
||||||
|
{
|
||||||
|
return p2p_peer_get_path(l_container_of(wsc, struct p2p_peer, wsc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_peer_wsc_connect(struct wsc_dbus *wsc, const char *pin)
|
||||||
|
{
|
||||||
|
p2p_peer_connect(l_container_of(wsc, struct p2p_peer, wsc), pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_peer_wsc_cancel(struct wsc_dbus *wsc)
|
||||||
|
{
|
||||||
|
p2p_peer_disconnect(l_container_of(wsc, struct p2p_peer, wsc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2p_peer_wsc_remove(struct wsc_dbus *wsc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The WSC removal is triggered in p2p_peer_put so we call
|
||||||
|
* p2p_peer_free directly from there too.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer)
|
static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer)
|
||||||
{
|
{
|
||||||
if (!strlen(peer->name) || !l_utf8_validate(
|
if (!strlen(peer->name) || !l_utf8_validate(
|
||||||
@ -441,6 +688,17 @@ static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peer->wsc.get_path = p2p_peer_wsc_get_path;
|
||||||
|
peer->wsc.connect = p2p_peer_wsc_connect;
|
||||||
|
peer->wsc.cancel = p2p_peer_wsc_cancel;
|
||||||
|
peer->wsc.remove = p2p_peer_wsc_remove;
|
||||||
|
|
||||||
|
if (!wsc_dbus_add_interface(&peer->wsc)) {
|
||||||
|
l_dbus_unregister_object(dbus_get_bus(),
|
||||||
|
p2p_peer_get_path(peer));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
l_queue_push_tail(dev->peer_list, peer);
|
l_queue_push_tail(dev->peer_list, peer);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -448,6 +706,7 @@ static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer)
|
|||||||
|
|
||||||
struct p2p_peer_move_data {
|
struct p2p_peer_move_data {
|
||||||
struct l_queue *new_list;
|
struct l_queue *new_list;
|
||||||
|
struct p2p_peer *conn_peer;
|
||||||
uint64_t now;
|
uint64_t now;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -456,7 +715,8 @@ static bool p2p_peer_move_recent(void *data, void *user_data)
|
|||||||
struct p2p_peer *peer = data;
|
struct p2p_peer *peer = data;
|
||||||
struct p2p_peer_move_data *move_data = user_data;
|
struct p2p_peer_move_data *move_data = user_data;
|
||||||
|
|
||||||
if (move_data->now > peer->bss->time_stamp + 30 * L_USEC_PER_SEC)
|
if (move_data->now > peer->bss->time_stamp + 30 * L_USEC_PER_SEC &&
|
||||||
|
peer != move_data->conn_peer)
|
||||||
return false; /* Old, keep on the list */
|
return false; /* Old, keep on the list */
|
||||||
|
|
||||||
/* Recently seen or currently connected, move to the new list */
|
/* Recently seen or currently connected, move to the new list */
|
||||||
@ -543,6 +803,7 @@ static bool p2p_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
* dev->peer_list and unref only the remaining peers.
|
* dev->peer_list and unref only the remaining peers.
|
||||||
*/
|
*/
|
||||||
move_data.new_list = dev->peer_list;
|
move_data.new_list = dev->peer_list;
|
||||||
|
move_data.conn_peer = dev->conn_peer;
|
||||||
move_data.now = l_time_now();
|
move_data.now = l_time_now();
|
||||||
l_queue_foreach_remove(old_peer_list, p2p_peer_move_recent, &move_data);
|
l_queue_foreach_remove(old_peer_list, p2p_peer_move_recent, &move_data);
|
||||||
l_queue_destroy(old_peer_list, p2p_peer_put);
|
l_queue_destroy(old_peer_list, p2p_peer_put);
|
||||||
@ -872,6 +1133,9 @@ static void p2p_device_start_stop(struct p2p_device *dev,
|
|||||||
* Stopping the P2P device, drop all peers as we can't start
|
* Stopping the P2P device, drop all peers as we can't start
|
||||||
* new connections from now on.
|
* new connections from now on.
|
||||||
*/
|
*/
|
||||||
|
if (dev->conn_peer)
|
||||||
|
p2p_connect_failed(dev);
|
||||||
|
|
||||||
l_queue_destroy(dev->peer_list, p2p_peer_put);
|
l_queue_destroy(dev->peer_list, p2p_peer_put);
|
||||||
dev->peer_list = NULL;
|
dev->peer_list = NULL;
|
||||||
}
|
}
|
||||||
@ -1108,6 +1372,7 @@ static void p2p_device_free(void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
p2p_device_discovery_stop(dev);
|
p2p_device_discovery_stop(dev);
|
||||||
|
p2p_connection_reset(dev);
|
||||||
l_dbus_unregister_object(dbus_get_bus(), p2p_device_get_path(dev));
|
l_dbus_unregister_object(dbus_get_bus(), p2p_device_get_path(dev));
|
||||||
l_queue_destroy(dev->peer_list, p2p_peer_put);
|
l_queue_destroy(dev->peer_list, p2p_peer_put);
|
||||||
l_queue_destroy(dev->discovery_users, p2p_discovery_user_free);
|
l_queue_destroy(dev->discovery_users, p2p_discovery_user_free);
|
||||||
@ -1303,7 +1568,7 @@ static struct l_dbus_message *p2p_device_request_discovery(struct l_dbus *dbus,
|
|||||||
p2p_device_discovery_destroy);
|
p2p_device_discovery_destroy);
|
||||||
l_queue_push_tail(dev->discovery_users, user);
|
l_queue_push_tail(dev->discovery_users, user);
|
||||||
|
|
||||||
if (first_user && dev->enabled)
|
if (first_user && !dev->conn_peer && dev->enabled)
|
||||||
p2p_device_discovery_start(dev);
|
p2p_device_discovery_start(dev);
|
||||||
|
|
||||||
return l_dbus_message_new_method_return(message);
|
return l_dbus_message_new_method_return(message);
|
||||||
@ -1413,6 +1678,25 @@ static bool p2p_peer_get_connected(struct l_dbus *dbus,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct l_dbus_message *p2p_peer_dbus_disconnect(struct l_dbus *dbus,
|
||||||
|
struct l_dbus_message *message,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct p2p_peer *peer = user_data;
|
||||||
|
|
||||||
|
if (!l_dbus_message_get_arguments(message, ""))
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the message for both WSC.Cancel and Peer.Disconnect the
|
||||||
|
* same way.
|
||||||
|
*/
|
||||||
|
peer->wsc.pending_cancel = l_dbus_message_ref(message);
|
||||||
|
|
||||||
|
p2p_peer_disconnect(peer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void p2p_peer_interface_setup(struct l_dbus_interface *interface)
|
static void p2p_peer_interface_setup(struct l_dbus_interface *interface)
|
||||||
{
|
{
|
||||||
l_dbus_interface_property(interface, "Name", 0, "s",
|
l_dbus_interface_property(interface, "Name", 0, "s",
|
||||||
@ -1423,6 +1707,8 @@ static void p2p_peer_interface_setup(struct l_dbus_interface *interface)
|
|||||||
p2p_peer_get_subcategory, NULL);
|
p2p_peer_get_subcategory, NULL);
|
||||||
l_dbus_interface_property(interface, "Connected", 0, "b",
|
l_dbus_interface_property(interface, "Connected", 0, "b",
|
||||||
p2p_peer_get_connected, NULL);
|
p2p_peer_get_connected, NULL);
|
||||||
|
l_dbus_interface_method(interface, "Disconnect", 0,
|
||||||
|
p2p_peer_dbus_disconnect, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int p2p_init(void)
|
static int p2p_init(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user