aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Zaborowski <andrew.zaborowski@intel.com>2020-04-25 11:09:32 +0200
committerDenis Kenzior <denkenz@gmail.com>2020-04-27 13:51:08 -0500
commit85f09d931869f43859af64e771340b49af6ae76e (patch)
tree5d6ee5405f791ec3248aa16ac9bc6c0daeb522ef
parentd77fdd087b8110ba86416b3fd2bb3194ddcfce01 (diff)
downloadiwd-85f09d931869f43859af64e771340b49af6ae76e.tar.gz
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.
-rw-r--r--src/p2p.c185
1 files changed, 180 insertions, 5 deletions
diff --git a/src/p2p.c b/src/p2p.c
index 0e57b375f..c2c127090 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -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);
}
}