3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-12-23 06:02:37 +01:00

p2p: Start a basic P2P Group after GO Negotiation

Use the ap.c API to start an AP on a P2P_GO interface after we've been
selected as the GO in the GO Negotiation.
This commit is contained in:
Andrew Zaborowski 2020-09-16 11:17:52 +02:00 committed by Denis Kenzior
parent 185b676f31
commit 68cb9d38bf

125
src/p2p.c
View File

@ -55,6 +55,7 @@
#include "src/frame-xchg.h" #include "src/frame-xchg.h"
#include "src/nl80211util.h" #include "src/nl80211util.h"
#include "src/netconfig.h" #include "src/netconfig.h"
#include "src/ap.h"
#include "src/p2p.h" #include "src/p2p.h"
struct p2p_device { struct p2p_device {
@ -110,6 +111,7 @@ struct p2p_device {
uint8_t conn_peer_interface_addr[6]; uint8_t conn_peer_interface_addr[6];
struct p2p_group_id_attr go_group_id; struct p2p_group_id_attr go_group_id;
struct ap_state *group;
bool enabled : 1; bool enabled : 1;
bool have_roc_cookie : 1; bool have_roc_cookie : 1;
@ -123,6 +125,7 @@ struct p2p_device {
bool disconnecting : 1; bool disconnecting : 1;
bool is_go : 1; bool is_go : 1;
bool conn_go_tie_breaker : 1; bool conn_go_tie_breaker : 1;
bool conn_peer_added : 1;
}; };
struct p2p_discovery_user { struct p2p_discovery_user {
@ -217,8 +220,9 @@ static void p2p_discovery_user_free(void *data)
static inline bool p2p_peer_operational(struct p2p_peer *peer) static inline bool p2p_peer_operational(struct p2p_peer *peer)
{ {
return peer && peer->dev->conn_netdev && !peer->dev->conn_wsc_bss && return peer && peer->dev->conn_netdev && !peer->dev->disconnecting &&
!peer->wsc.pending_connect && !peer->dev->disconnecting; ((!peer->dev->is_go && !peer->dev->conn_wsc_bss) ||
(peer->dev->is_go && peer->dev->conn_peer_added));
} }
static bool p2p_peer_match(const void *a, const void *b) static bool p2p_peer_match(const void *a, const void *b)
@ -457,6 +461,14 @@ static void p2p_connection_reset(struct p2p_device *dev)
if (dev->conn_enrollee) if (dev->conn_enrollee)
wsc_enrollee_cancel(dev->conn_enrollee, false); wsc_enrollee_cancel(dev->conn_enrollee, false);
if (dev->group) {
ap_free(dev->group);
dev->group = NULL;
dev->conn_peer_added = false;
}
dev->capability.group_caps = 0;
if (dev->conn_netdev) { if (dev->conn_netdev) {
struct l_genl_msg *msg; struct l_genl_msg *msg;
uint64_t wdev_id = netdev_get_wdev_id(dev->conn_netdev); uint64_t wdev_id = netdev_get_wdev_id(dev->conn_netdev);
@ -628,10 +640,12 @@ static void p2p_peer_connect_done(struct p2p_device *dev)
{ {
struct p2p_peer *peer = dev->conn_peer; struct p2p_peer *peer = dev->conn_peer;
if (!dev->is_go) {
/* We can free anything potentially needed for a retry */ /* We can free anything potentially needed for a retry */
scan_bss_free(dev->conn_wsc_bss); scan_bss_free(dev->conn_wsc_bss);
dev->conn_wsc_bss = NULL; dev->conn_wsc_bss = NULL;
explicit_bzero(dev->conn_psk, 32); explicit_bzero(dev->conn_psk, 32);
}
dbus_pending_reply(&peer->wsc.pending_connect, dbus_pending_reply(&peer->wsc.pending_connect,
l_dbus_message_new_method_return( l_dbus_message_new_method_return(
@ -649,6 +663,96 @@ static void p2p_peer_connect_done(struct p2p_device *dev)
"ConnectedIP"); "ConnectedIP");
} }
static void p2p_group_event(enum ap_event_type type, const void *event_data,
void *user_data)
{
struct p2p_device *dev = user_data;
l_debug("type=%i", type);
switch (type) {
case AP_EVENT_START_FAILED:
case AP_EVENT_STOPPING:
dev->group = NULL;
p2p_connect_failed(dev);
break;
case AP_EVENT_STARTED:
ap_push_button(dev->group);
break;
case AP_EVENT_STATION_ADDED:
dev->conn_peer_added = true;
p2p_peer_connect_done(dev);
break;
case AP_EVENT_STATION_REMOVED:
dev->conn_peer_added = false;
p2p_connect_failed(dev);
break;
case AP_EVENT_REGISTRATION_START:
/* Don't validate the P2P IE or WFD IE at this stage */
break;
case AP_EVENT_REGISTRATION_SUCCESS:
dev->capability.group_caps &= ~P2P_GROUP_CAP_GROUP_FORMATION;
break;
case AP_EVENT_PBC_MODE_EXIT:
break;
};
}
static const struct ap_ops p2p_go_ops = {
.handle_event = p2p_group_event,
};
static void p2p_group_start(struct p2p_device *dev)
{
struct ap_config *config = l_new(struct ap_config, 1);
config->ssid = l_strdup(dev->go_group_id.ssid);
config->channel = dev->listen_channel;
config->wsc_name = l_strdup(dev->device_info.device_name);
config->wsc_primary_device_type = dev->device_info.primary_device_type;
config->no_cck_rates = true;
/*
* Section 3.2.1: "The Credentials for a P2P Group issued to a
* P2P Device shall: [...]
* - Use a Network Key Type of 64 Hex characters."
*
* This implies we have to send the PSK and not the passphrase to
* the WSC clients. For simplicity we directly generate random
* PSKs and don't currently respect the requirement to maintain
* a passphrase. We have no practical use for the passphrase and
* it's a little costlier to generate for the same cryptographic
* strength as the PSK.
*/
if (!l_getrandom(config->psk, 32)) {
l_error("l_getrandom() failed");
ap_config_free(config);
p2p_connect_failed(dev);
return;
}
/*
* Section 3.1.4.4: "It shall only allow association by the
* P2P Device that it is currently in Group Formation with."
*/
config->authorized_macs = l_memdup(dev->conn_peer_interface_addr, 6);
config->authorized_macs_num = 1;
dev->capability.group_caps |= P2P_GROUP_CAP_GO;
dev->capability.group_caps |= P2P_GROUP_CAP_GROUP_FORMATION;
dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, dev);
if (!dev->group) {
ap_config_free(config);
p2p_connect_failed(dev);
return;
}
}
static void p2p_netconfig_event_handler(enum netconfig_event event, static void p2p_netconfig_event_handler(enum netconfig_event event,
void *user_data) void *user_data)
{ {
@ -682,7 +786,7 @@ static void p2p_dhcp_timeout_destroy(void *user_data)
dev->conn_dhcp_timeout = NULL; dev->conn_dhcp_timeout = NULL;
} }
static void p2p_start_dhcp(struct p2p_device *dev) static void p2p_start_dhcp_client(struct p2p_device *dev)
{ {
uint32_t ifindex = netdev_get_ifindex(dev->conn_netdev); uint32_t ifindex = netdev_get_ifindex(dev->conn_netdev);
unsigned int dhcp_timeout_val; unsigned int dhcp_timeout_val;
@ -723,7 +827,7 @@ static void p2p_netdev_connect_cb(struct netdev *netdev,
switch (result) { switch (result) {
case NETDEV_RESULT_OK: case NETDEV_RESULT_OK:
p2p_start_dhcp(dev); p2p_start_dhcp_client(dev);
break; break;
case NETDEV_RESULT_AUTHENTICATION_FAILED: case NETDEV_RESULT_AUTHENTICATION_FAILED:
case NETDEV_RESULT_ASSOCIATION_FAILED: case NETDEV_RESULT_ASSOCIATION_FAILED:
@ -1034,10 +1138,14 @@ static void p2p_device_netdev_notify(struct netdev *netdev,
case NETDEV_WATCH_EVENT_NEW: case NETDEV_WATCH_EVENT_NEW:
/* Ignore the event if we're already connecting/connected */ /* Ignore the event if we're already connecting/connected */
if (dev->conn_enrollee || dev->conn_retry_count || if (dev->conn_enrollee || dev->conn_retry_count ||
!netdev_get_is_up(netdev)) dev->group || !netdev_get_is_up(netdev))
break; break;
if (dev->is_go)
p2p_group_start(dev);
else
p2p_provision_connect(dev); p2p_provision_connect(dev);
break; break;
case NETDEV_WATCH_EVENT_DEL: case NETDEV_WATCH_EVENT_DEL:
dev->conn_netdev = NULL; dev->conn_netdev = NULL;
@ -1094,13 +1202,14 @@ static void p2p_device_new_interface_destroy(void *user_data)
static void p2p_device_interface_create(struct p2p_device *dev) static void p2p_device_interface_create(struct p2p_device *dev)
{ {
uint32_t iftype = NL80211_IFTYPE_P2P_CLIENT; uint32_t iftype = dev->is_go ? NL80211_IFTYPE_P2P_GO :
NL80211_IFTYPE_P2P_CLIENT;
char ifname[32]; char ifname[32];
uint32_t wiphy_id = dev->wdev_id >> 32; uint32_t wiphy_id = dev->wdev_id >> 32;
struct l_genl_msg *msg; struct l_genl_msg *msg;
snprintf(ifname, sizeof(ifname), "wlan%i-p2p-cl%i", snprintf(ifname, sizeof(ifname), "wlan%i-p2p-%s%i",
wiphy_id, dev->conn_num++); wiphy_id, dev->is_go ? "go" : "cl", dev->conn_num++);
l_debug("creating %s", ifname); l_debug("creating %s", ifname);
msg = l_genl_msg_new(NL80211_CMD_NEW_INTERFACE); msg = l_genl_msg_new(NL80211_CMD_NEW_INTERFACE);