p2p: Create the P2P-Client interface

Once we've found the provisioning BSS create the P2P-Client interface
that we're going to use for the actual provisioning and the final P2P
connection.
This commit is contained in:
Andrew Zaborowski 2020-04-25 11:09:32 +02:00 committed by Denis Kenzior
parent d77fdd087b
commit 85f09d9318
1 changed files with 180 additions and 5 deletions

185
src/p2p.c
View File

@ -86,7 +86,11 @@ struct p2p_device {
char *conn_pin;
uint8_t conn_addr[6];
uint16_t conn_password_id;
unsigned int conn_num;
struct scan_bss *conn_wsc_bss;
struct netdev *conn_netdev;
uint32_t conn_netdev_watch_id;
uint32_t conn_new_intf_cmd_id;
struct l_timeout *config_timeout;
unsigned long go_config_delay;
@ -306,6 +310,47 @@ static void p2p_connection_reset(struct p2p_device *dev)
l_timeout_remove(dev->config_timeout);
l_timeout_remove(dev->go_neg_req_timeout);
if (dev->conn_new_intf_cmd_id)
/*
* Note this may result in the interface being created
* and unused, we don't have its ifindex or wdev_id here
* to be able to delete it. Could use a separate netlink
* socket for each connection or disallowing .Disconnect
* calls while this command runs.
*/
l_genl_family_cancel(dev->nl80211, dev->conn_new_intf_cmd_id);
if (dev->conn_netdev) {
struct l_genl_msg *msg;
uint64_t wdev_id = netdev_get_wdev_id(dev->conn_netdev);
msg = l_genl_msg_new(NL80211_CMD_DEL_INTERFACE);
l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &wdev_id);
if (!l_genl_family_send(dev->nl80211, msg, NULL, NULL, NULL)) {
l_genl_msg_unref(msg);
l_error("Sending DEL_INTERFACE for %s failed",
netdev_get_name(dev->conn_netdev));
}
netdev_destroy(dev->conn_netdev);
dev->conn_netdev = NULL;
}
/*
* Removing the netdev above makes sure that both the WSC connection
* and the final WPA2 connection (wsc.c and netdev.c) no longer need
* the bss so we can free it now -- if it wasn't freed as a result
* of wsc_enrollee_cancel or netdev_destroy triggering
* p2p_peer_provision_done in the first place.
*/
if (dev->conn_wsc_bss) {
scan_bss_free(dev->conn_wsc_bss);
dev->conn_wsc_bss = NULL;
}
netdev_watch_remove(dev->conn_netdev_watch_id);
frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_CONNECT);
frame_xchg_stop(dev->wdev_id);
@ -426,11 +471,117 @@ static const struct frame_xchg_prefix p2p_frame_pd_resp = {
.len = 7,
};
static void p2p_device_interface_create(struct p2p_device *dev)
static void p2p_provision_connect(struct p2p_device *dev)
{
/* TODO */
}
static void p2p_device_netdev_watch_destroy(void *user_data)
{
struct p2p_device *dev = user_data;
dev->conn_netdev_watch_id = 0;
}
static void p2p_device_netdev_notify(struct netdev *netdev,
enum netdev_watch_event event,
void *user_data)
{
struct p2p_device *dev = user_data;
if (dev->conn_netdev != netdev)
return;
switch (event) {
case NETDEV_WATCH_EVENT_UP:
case NETDEV_WATCH_EVENT_NEW:
if (!dev->conn_wsc_bss || !netdev_get_is_up(netdev))
break;
p2p_provision_connect(dev);
break;
case NETDEV_WATCH_EVENT_DEL:
dev->conn_netdev = NULL;
/* Fall through */
case NETDEV_WATCH_EVENT_DOWN:
case NETDEV_WATCH_EVENT_ADDRESS_CHANGE:
p2p_connect_failed(dev);
break;
default:
break;
}
}
static void p2p_device_new_interface_cb(struct l_genl_msg *msg,
void *user_data)
{
struct p2p_device *dev = user_data;
l_debug("");
if (l_genl_msg_get_error(msg) < 0) {
l_error("NEW_INTERFACE failed: %s",
strerror(-l_genl_msg_get_error(msg)));
p2p_connect_failed(dev);
return;
}
/* Create the netdev so we don't have to parse the message ourselves */
dev->conn_netdev = netdev_create_from_genl(msg, dev->conn_addr);
if (!dev->conn_netdev) {
p2p_connect_failed(dev);
return;
}
/*
* Register a watch for each connection rather than having one
* global watch. Each connection's watch will receive events
* related to all other connections too, and will check that its
* conn_netdev != netdev and exit immediately. This is not ideal
* but it's the same complexity (n^2) as that of one global watch
* that receives all events and iterates over p2p_device_list to
* find the connection.
*/
dev->conn_netdev_watch_id = netdev_watch_add(p2p_device_netdev_notify,
dev, p2p_device_netdev_watch_destroy);
}
static void p2p_device_new_interface_destroy(void *user_data)
{
struct p2p_device *dev = user_data;
dev->conn_new_intf_cmd_id = 0;
}
static void p2p_device_interface_create(struct p2p_device *dev)
{
uint32_t iftype = NL80211_IFTYPE_P2P_CLIENT;
char ifname[32];
uint32_t wiphy_id = dev->wdev_id >> 32;
struct l_genl_msg *msg;
snprintf(ifname, sizeof(ifname), "wlan%i-p2p-cl%i",
wiphy_id, dev->conn_num++);
l_debug("creating %s", ifname);
msg = l_genl_msg_new(NL80211_CMD_NEW_INTERFACE);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy_id);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFNAME,
strlen(ifname) + 1, ifname);
l_genl_msg_append_attr(msg, NL80211_ATTR_4ADDR, 1, "\0");
l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, "");
dev->conn_new_intf_cmd_id = l_genl_family_send(dev->nl80211, msg,
p2p_device_new_interface_cb, dev,
p2p_device_new_interface_destroy);
if (!dev->conn_new_intf_cmd_id) {
l_genl_msg_unref(msg);
l_error("Error sending NEW_INTERFACE for %s", ifname);
p2p_connect_failed(dev);
}
}
static void p2p_scan_destroy(void *user_data)
{
struct p2p_device *dev = user_data;
@ -2364,6 +2515,15 @@ static void p2p_device_enable_destroy(void *user_data)
dev->start_stop_cmd_id = 0;
}
static bool p2p_peer_remove_disconnected(void *peer, void *conn_peer)
{
if (peer == conn_peer)
return false;
p2p_peer_put(peer);
return true;
}
static void p2p_device_start_stop(struct p2p_device *dev,
l_dbus_property_complete_cb_t complete,
struct l_dbus_message *message)
@ -2395,13 +2555,28 @@ static void p2p_device_start_stop(struct p2p_device *dev,
if (dev->enabled) {
/*
* Stopping the P2P device, drop all peers as we can't start
* new connections from now on.
* new connections from now on. Check if we have a connection
* being set up without a .conn_netdev and without
* .conn_wsc_bss -- this will mean the connection is still in
* the PD or GO Negotiation phase or inside the scan. Those
* phases happen on the device interface so the connection
* gets immediately aborted.
*/
if (dev->conn_peer)
if (dev->conn_peer && !dev->conn_netdev && !dev->conn_wsc_bss)
p2p_connect_failed(dev);
l_queue_destroy(dev->peer_list, p2p_peer_put);
dev->peer_list = NULL;
if (!dev->conn_peer) {
l_queue_destroy(dev->peer_list, p2p_peer_put);
dev->peer_list = NULL;
} else
/*
* If the connection already depends on its own
* netdev only, we can let it continue until the user
* decides to disconnect.
*/
l_queue_foreach_remove(dev->peer_list,
p2p_peer_remove_disconnected,
dev->conn_peer);
}
}