P2PS: Process P2PS provisioning commands

This extends wpas_p2p_prov_disc() implementation to accept P2PS
parameters. None of the callers are yet using this functionality; the
following commit introduces a user.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Krishna Vamsi 2014-12-10 19:55:18 +05:30 committed by Jouni Malinen
parent 369678ad14
commit 6d9085145c
8 changed files with 268 additions and 9 deletions

View file

@ -159,6 +159,13 @@ struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
}
void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
{
if (p2p && intended_addr)
os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
}
u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev = NULL;

View file

@ -1955,6 +1955,13 @@ const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
*/
unsigned int p2p_get_group_num_members(struct p2p_group *group);
/**
* p2p_client_limit_reached - Check if client limit is reached
* @group: P2P group context from p2p_group_init()
* Returns: 1 if no of clients limit reached
*/
int p2p_client_limit_reached(struct p2p_group *group);
/**
* p2p_iterate_group_members - Iterate group members
* @group: P2P group context from p2p_group_init()
@ -2170,6 +2177,8 @@ void p2p_loop_on_known_peers(struct p2p_data *p2p,
void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
struct p2ps_advertisement *
p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,

View file

@ -988,6 +988,15 @@ unsigned int p2p_get_group_num_members(struct p2p_group *group)
}
int p2p_client_limit_reached(struct p2p_group *group)
{
if (!group || !group->cfg)
return 1;
return group->num_members >= group->cfg->max_clients;
}
const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
{
struct p2p_group_member *iter = *next;

View file

@ -4699,7 +4699,7 @@ static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
else if (os_strstr(pos, " auto") != NULL)
use = WPAS_P2P_PD_AUTO;
return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
}

View file

@ -730,7 +730,7 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
wpa_s = wpa_s->p2p_dev;
if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
WPAS_P2P_PD_FOR_GO_NEG) < 0)
WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
return wpas_dbus_error_unknown_error(message,
"Failed to send provision discovery request");

View file

@ -123,6 +123,8 @@ static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
static void wpas_stop_listen(void *ctx);
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type type);
/*
@ -475,6 +477,34 @@ static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
}
/* Find an interface for a P2P group where we are the GO */
static struct wpa_supplicant *
wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *save = NULL;
struct wpa_ssid *s;
if (!wpa_s)
return NULL;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled || !s->p2p_group ||
s->mode != WPAS_MODE_P2P_GO)
continue;
/* Prefer a group with connected clients */
if (p2p_get_group_num_members(wpa_s->p2p_group))
return wpa_s;
save = wpa_s;
}
}
/* No group with connected clients, so pick the one without (if any) */
return save;
}
/* Find an active P2P group where we are the GO */
static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
u8 *bssid)
@ -516,6 +546,193 @@ wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
}
static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
{
struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
struct wpa_ssid *s;
u8 conncap = P2PS_SETUP_NONE;
unsigned int owned_members = 0;
unsigned int owner = 0;
unsigned int client = 0;
struct wpa_supplicant *go_wpa_s;
struct wpa_ssid *persistent_go;
int p2p_no_group_iface;
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
/*
* For non-concurrent capable devices:
* If persistent_go, then no new.
* If GO, then no client.
* If client, then no GO.
*/
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
go_wpa_s, persistent_go);
for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
tmp_wpa_s = tmp_wpa_s->next) {
for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
wpa_printf(MSG_DEBUG,
"P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
tmp_wpa_s, s, s->disabled,
s->p2p_group, s->mode);
if (!s->disabled && s->p2p_group) {
if (s->mode == WPAS_MODE_P2P_GO) {
owned_members +=
p2p_get_group_num_members(
tmp_wpa_s->p2p_group);
owner++;
} else
client++;
}
}
}
/* If not concurrent, restrict our choices */
if (p2p_no_group_iface) {
wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
if (client)
return P2PS_SETUP_NONE;
if (go_wpa_s) {
if (role == P2PS_SETUP_CLIENT ||
incoming == P2PS_SETUP_GROUP_OWNER ||
p2p_client_limit_reached(go_wpa_s->p2p_group))
return P2PS_SETUP_NONE;
return P2PS_SETUP_GROUP_OWNER;
}
if (persistent_go) {
if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
if (!incoming)
return P2PS_SETUP_GROUP_OWNER |
P2PS_SETUP_CLIENT;
if (incoming == P2PS_SETUP_NEW) {
u8 r;
if (os_get_random(&r, sizeof(r)) < 0 ||
(r & 1))
return P2PS_SETUP_CLIENT;
return P2PS_SETUP_GROUP_OWNER;
}
}
}
}
/* If a required role has been specified, handle it here */
if (role && role != P2PS_SETUP_NEW) {
switch (incoming) {
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
conncap = role;
goto grp_owner;
case P2PS_SETUP_GROUP_OWNER:
/*
* Must be a complimentary role - cannot be a client to
* more than one peer.
*/
if (incoming == role || client)
return P2PS_SETUP_NONE;
return P2PS_SETUP_CLIENT;
case P2PS_SETUP_CLIENT:
/* Must be a complimentary role */
if (incoming != role) {
conncap = P2PS_SETUP_GROUP_OWNER;
goto grp_owner;
}
default:
return P2PS_SETUP_NONE;
}
}
/*
* For now, we only will support ownership of one group, and being a
* client of one group. Therefore, if we have either an existing GO
* group, or an existing client group, we will not do a new GO
* negotiation, but rather try to re-use the existing groups.
*/
switch (incoming) {
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
if (client)
conncap = P2PS_SETUP_GROUP_OWNER;
else if (!owned_members)
conncap = P2PS_SETUP_NEW;
else if (incoming == P2PS_SETUP_NONE)
conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
else
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_CLIENT:
conncap = P2PS_SETUP_GROUP_OWNER;
break;
case P2PS_SETUP_GROUP_OWNER:
if (!client)
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
if (client)
conncap = P2PS_SETUP_GROUP_OWNER;
else {
u8 r;
if (os_get_random(&r, sizeof(r)) < 0 ||
(r & 1))
conncap = P2PS_SETUP_CLIENT;
else
conncap = P2PS_SETUP_GROUP_OWNER;
}
break;
default:
return P2PS_SETUP_NONE;
}
grp_owner:
if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
(!incoming && (conncap & P2PS_SETUP_NEW))) {
if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
conncap &= ~P2PS_SETUP_GROUP_OWNER;
wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
owner, owned_members, conncap);
s = wpas_p2p_get_persistent_go(wpa_s);
if (!s && !owner && p2p_no_group_iface) {
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->own_addr);
} else if (!s && !owner) {
if (wpas_p2p_add_group_interface(wpa_s,
WPA_IF_P2P_GO) < 0) {
wpa_printf(MSG_ERROR,
"P2P: Failed to allocate a new interface for the group");
return P2PS_SETUP_NONE;
}
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->pending_interface_addr);
}
}
return conncap;
}
static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
enum p2p_group_removal_reason removal_reason)
{
@ -6293,13 +6510,24 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
enum wpas_p2p_prov_disc_use use)
enum wpas_p2p_prov_disc_use use,
struct p2ps_provision *p2ps_prov)
{
u16 config_methods;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->pending_pd_use = NORMAL_PD;
if (os_strncmp(config_method, "display", 7) == 0)
if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
p2ps_prov->conncap = p2ps_group_capability(
wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
wpa_printf(MSG_DEBUG,
"P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
__func__, p2ps_prov->conncap,
p2ps_prov->adv_id, p2ps_prov->conncap,
p2ps_prov->status, p2ps_prov->info);
config_methods = 0;
} else if (os_strncmp(config_method, "display", 7) == 0)
config_methods = WPS_CONFIG_DISPLAY;
else if (os_strncmp(config_method, "keypad", 6) == 0)
config_methods = WPS_CONFIG_KEYPAD;
@ -6308,6 +6536,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
config_methods = WPS_CONFIG_PUSHBUTTON;
else {
wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
os_free(p2ps_prov);
return -1;
}
@ -6328,10 +6557,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
return 0;
}
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
os_free(p2ps_prov);
return -1;
}
return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, NULL,
return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
0, 1);
}

View file

@ -15,6 +15,7 @@ enum p2p_send_action_result;
struct p2p_peer_info;
struct p2p_channels;
struct wps_event_fail;
struct p2ps_provision;
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
const char *conf_p2p_dev);
@ -41,11 +42,13 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
enum wpas_p2p_prov_disc_use {
WPAS_P2P_PD_FOR_GO_NEG,
WPAS_P2P_PD_FOR_JOIN,
WPAS_P2P_PD_AUTO
WPAS_P2P_PD_AUTO,
WPAS_P2P_PD_FOR_ASP
};
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
enum wpas_p2p_prov_disc_use use);
enum wpas_p2p_prov_disc_use use,
struct p2ps_provision *p2ps_prov);
void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
const u8 *data, size_t data_len,
enum p2p_send_action_result result);

View file

@ -771,7 +771,7 @@ struct wpa_supplicant {
int force_long_sd;
u16 pending_pd_config_methods;
enum {
NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
} pending_pd_use;
/*