hostap/src/p2p/p2p_group.c
Jouni Malinen 947b5a1532 P2P: Stop listen state if Action frame TX is needed on another channel
This speeds up P2P responses to frames received on an operating channel
in case there is an ongoing P2P listen operation on another channel.
This is applicable to drivers that support multiple channels in
concurrently.

This addresses an issue showing up in the
p2ps_channel_active_go_and_station_different_mcc test case where the
Provision Discovery Request frame can be received on the operating
channel of a group instead of the Listen channel. The response was
delayed until the listen operation timed out and this took too long time
for the peer to receive the response.

Signed-off-by: Jouni Malinen <j@w1.fi>
2019-02-17 16:53:03 +02:00

1131 lines
26 KiB
C

/*
* Wi-Fi Direct - P2P group operations
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
struct p2p_group_member {
struct p2p_group_member *next;
u8 addr[ETH_ALEN]; /* P2P Interface Address */
u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
struct wpabuf *p2p_ie;
struct wpabuf *wfd_ie;
struct wpabuf *client_info;
u8 dev_capab;
};
/**
* struct p2p_group - Internal P2P module per-group data
*/
struct p2p_group {
struct p2p_data *p2p;
struct p2p_group_config *cfg;
struct p2p_group_member *members;
unsigned int num_members;
int group_formation;
int beacon_update;
struct wpabuf *noa;
struct wpabuf *wfd_ie;
};
struct p2p_group * p2p_group_init(struct p2p_data *p2p,
struct p2p_group_config *config)
{
struct p2p_group *group, **groups;
group = os_zalloc(sizeof(*group));
if (group == NULL)
return NULL;
groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
sizeof(struct p2p_group *));
if (groups == NULL) {
os_free(group);
return NULL;
}
groups[p2p->num_groups++] = group;
p2p->groups = groups;
group->p2p = p2p;
group->cfg = config;
group->group_formation = 1;
group->beacon_update = 1;
p2p_group_update_ies(group);
group->cfg->idle_update(group->cfg->cb_ctx, 1);
return group;
}
static void p2p_group_free_member(struct p2p_group_member *m)
{
wpabuf_free(m->wfd_ie);
wpabuf_free(m->p2p_ie);
wpabuf_free(m->client_info);
os_free(m);
}
static void p2p_group_free_members(struct p2p_group *group)
{
struct p2p_group_member *m, *prev;
m = group->members;
group->members = NULL;
group->num_members = 0;
while (m) {
prev = m;
m = m->next;
p2p_group_free_member(prev);
}
}
void p2p_group_deinit(struct p2p_group *group)
{
size_t g;
struct p2p_data *p2p;
if (group == NULL)
return;
p2p = group->p2p;
for (g = 0; g < p2p->num_groups; g++) {
if (p2p->groups[g] == group) {
while (g + 1 < p2p->num_groups) {
p2p->groups[g] = p2p->groups[g + 1];
g++;
}
p2p->num_groups--;
break;
}
}
p2p_group_free_members(group);
os_free(group->cfg);
wpabuf_free(group->noa);
wpabuf_free(group->wfd_ie);
os_free(group);
}
static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
{
if (m->client_info == NULL)
return;
if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
return;
wpabuf_put_buf(ie, m->client_info);
}
static void p2p_group_add_common_ies(struct p2p_group *group,
struct wpabuf *ie)
{
u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
/* P2P Capability */
dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
if (group->cfg->persistent_group) {
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
if (group->cfg->persistent_group == 2)
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
}
if (group->p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
if (group->group_formation)
group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
if (group->p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (group->num_members >= group->cfg->max_clients)
group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
if (group->cfg->ip_addr_alloc)
group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
p2p_buf_add_capability(ie, dev_capab, group_capab);
}
static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
{
if (noa == NULL)
return;
/* Notice of Absence */
wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
wpabuf_put_le16(ie, wpabuf_len(noa));
wpabuf_put_buf(ie, noa);
}
static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
{
struct wpabuf *ie;
const u8 *pos, *end;
size_t len;
if (subelems == NULL)
return NULL;
len = wpabuf_len(subelems) + 100;
ie = wpabuf_alloc(len);
if (ie == NULL)
return NULL;
pos = wpabuf_head(subelems);
end = pos + wpabuf_len(subelems);
while (end > pos) {
size_t frag_len = end - pos;
if (frag_len > 251)
frag_len = 251;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(ie, 4 + frag_len);
wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
wpabuf_put_data(ie, pos, frag_len);
pos += frag_len;
}
return ie;
}
static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
{
struct wpabuf *ie;
u8 *len;
size_t extra = 0;
#ifdef CONFIG_WIFI_DISPLAY
if (group->p2p->wfd_ie_beacon)
extra = wpabuf_len(group->p2p->wfd_ie_beacon);
#endif /* CONFIG_WIFI_DISPLAY */
if (group->p2p->vendor_elem &&
group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
ie = wpabuf_alloc(257 + extra);
if (ie == NULL)
return NULL;
#ifdef CONFIG_WIFI_DISPLAY
if (group->p2p->wfd_ie_beacon)
wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
#endif /* CONFIG_WIFI_DISPLAY */
if (group->p2p->vendor_elem &&
group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
wpabuf_put_buf(ie,
group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
len = p2p_buf_add_ie_hdr(ie);
p2p_group_add_common_ies(group, ie);
p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
p2p_group_add_noa(ie, group->noa);
p2p_buf_update_ie_hdr(ie, len);
return ie;
}
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
{
return g->wfd_ie;
}
struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
{
struct wpabuf *ie;
const u8 *pos, *end;
if (subelems == NULL)
return NULL;
ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
if (ie == NULL)
return NULL;
pos = wpabuf_head(subelems);
end = pos + wpabuf_len(subelems);
while (end > pos) {
size_t frag_len = end - pos;
if (frag_len > 251)
frag_len = 251;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(ie, 4 + frag_len);
wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
wpabuf_put_data(ie, pos, frag_len);
pos += frag_len;
}
return ie;
}
static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
struct p2p_group_member *m)
{
const u8 *pos, *end;
const u8 *dev_info = NULL;
const u8 *assoc_bssid = NULL;
const u8 *coupled_sink = NULL;
u8 zero_addr[ETH_ALEN];
if (m->wfd_ie == NULL)
return 0;
os_memset(zero_addr, 0, ETH_ALEN);
pos = wpabuf_head_u8(m->wfd_ie);
end = pos + wpabuf_len(m->wfd_ie);
while (end - pos >= 3) {
u8 id;
u16 len;
id = *pos++;
len = WPA_GET_BE16(pos);
pos += 2;
if (len > end - pos)
break;
switch (id) {
case WFD_SUBELEM_DEVICE_INFO:
if (len < 6)
break;
dev_info = pos;
break;
case WFD_SUBELEM_ASSOCIATED_BSSID:
if (len < ETH_ALEN)
break;
assoc_bssid = pos;
break;
case WFD_SUBELEM_COUPLED_SINK:
if (len < 1 + ETH_ALEN)
break;
coupled_sink = pos;
break;
}
pos += len;
}
if (dev_info == NULL)
return 0;
wpabuf_put_u8(buf, 23);
wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
if (assoc_bssid)
wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
else
wpabuf_put_data(buf, zero_addr, ETH_ALEN);
wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
if (coupled_sink) {
wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
} else {
wpabuf_put_u8(buf, 0);
wpabuf_put_data(buf, zero_addr, ETH_ALEN);
}
return 1;
}
static struct wpabuf *
wifi_display_build_go_ie(struct p2p_group *group)
{
struct wpabuf *wfd_subelems, *wfd_ie;
struct p2p_group_member *m;
u8 *len;
unsigned int count = 0;
if (!group->p2p->wfd_ie_probe_resp)
return NULL;
wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
group->num_members * 24 + 100);
if (wfd_subelems == NULL)
return NULL;
if (group->p2p->wfd_dev_info)
wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
if (group->p2p->wfd_r2_dev_info)
wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info);
if (group->p2p->wfd_assoc_bssid)
wpabuf_put_buf(wfd_subelems,
group->p2p->wfd_assoc_bssid);
if (group->p2p->wfd_coupled_sink_info)
wpabuf_put_buf(wfd_subelems,
group->p2p->wfd_coupled_sink_info);
/* Build WFD Session Info */
wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
len = wpabuf_put(wfd_subelems, 2);
m = group->members;
while (m) {
if (wifi_display_add_dev_info_descr(wfd_subelems, m))
count++;
m = m->next;
}
if (count == 0) {
/* No Wi-Fi Display clients - do not include subelement */
wfd_subelems->used -= 3;
} else {
WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
2);
p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors",
count);
}
wfd_ie = wifi_display_encaps(wfd_subelems);
wpabuf_free(wfd_subelems);
return wfd_ie;
}
static void wifi_display_group_update(struct p2p_group *group)
{
wpabuf_free(group->wfd_ie);
group->wfd_ie = wifi_display_build_go_ie(group);
}
#endif /* CONFIG_WIFI_DISPLAY */
void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
int max_clients)
{
u8 *group_info;
int count = 0;
struct p2p_group_member *m;
p2p_dbg(group->p2p, "* P2P Group Info");
group_info = wpabuf_put(buf, 0);
wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
wpabuf_put_le16(buf, 0); /* Length to be filled */
for (m = group->members; m; m = m->next) {
p2p_client_info(buf, m);
count++;
if (max_clients >= 0 && count >= max_clients)
break;
}
WPA_PUT_LE16(group_info + 1,
(u8 *) wpabuf_put(buf, 0) - group_info - 3);
}
void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
{
p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
group->cfg->ssid_len);
}
static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
{
struct wpabuf *p2p_subelems, *ie;
p2p_subelems = wpabuf_alloc(500);
if (p2p_subelems == NULL)
return NULL;
p2p_group_add_common_ies(group, p2p_subelems);
p2p_group_add_noa(p2p_subelems, group->noa);
/* P2P Device Info */
p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
/* P2P Group Info: Only when at least one P2P Client is connected */
if (group->members)
p2p_buf_add_group_info(group, p2p_subelems, -1);
ie = p2p_group_encaps_probe_resp(p2p_subelems);
wpabuf_free(p2p_subelems);
if (group->p2p->vendor_elem &&
group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
struct wpabuf *extra;
extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
ie = wpabuf_concat(extra, ie);
}
#ifdef CONFIG_WIFI_DISPLAY
if (group->wfd_ie) {
struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
ie = wpabuf_concat(wfd, ie);
}
#endif /* CONFIG_WIFI_DISPLAY */
return ie;
}
void p2p_group_update_ies(struct p2p_group *group)
{
struct wpabuf *beacon_ie;
struct wpabuf *probe_resp_ie;
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_group_update(group);
#endif /* CONFIG_WIFI_DISPLAY */
probe_resp_ie = p2p_group_build_probe_resp_ie(group);
if (probe_resp_ie == NULL)
return;
wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
probe_resp_ie);
if (group->beacon_update) {
beacon_ie = p2p_group_build_beacon_ie(group);
if (beacon_ie)
group->beacon_update = 0;
wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
beacon_ie);
} else
beacon_ie = NULL;
group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
}
/**
* p2p_build_client_info - Build P2P Client Info Descriptor
* @addr: MAC address of the peer device
* @p2p_ie: P2P IE from (Re)Association Request
* @dev_capab: Buffer for returning Device Capability
* @dev_addr: Buffer for returning P2P Device Address
* Returns: P2P Client Info Descriptor or %NULL on failure
*
* This function builds P2P Client Info Descriptor based on the information
* available from (Re)Association Request frame. Group owner can use this to
* build the P2P Group Info attribute for Probe Response frames.
*/
static struct wpabuf * p2p_build_client_info(const u8 *addr,
struct wpabuf *p2p_ie,
u8 *dev_capab, u8 *dev_addr)
{
const u8 *spos;
struct p2p_message msg;
u8 *len_pos;
struct wpabuf *buf;
if (p2p_ie == NULL)
return NULL;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
msg.capability == NULL || msg.p2p_device_info == NULL)
return NULL;
buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
if (buf == NULL)
return NULL;
*dev_capab = msg.capability[0];
os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
spos = msg.p2p_device_info; /* P2P Device address */
/* P2P Client Info Descriptor */
/* Length to be set */
len_pos = wpabuf_put(buf, 1);
/* P2P Device address */
wpabuf_put_data(buf, spos, ETH_ALEN);
/* P2P Interface address */
wpabuf_put_data(buf, addr, ETH_ALEN);
/* Device Capability Bitmap */
wpabuf_put_u8(buf, msg.capability[0]);
/*
* Config Methods, Primary Device Type, Number of Secondary Device
* Types, Secondary Device Type List, Device Name copied from
* Device Info
*/
wpabuf_put_data(buf, spos + ETH_ALEN,
msg.p2p_device_info_len - ETH_ALEN);
*len_pos = wpabuf_len(buf) - 1;
return buf;
}
static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
{
struct p2p_group_member *m, *prev;
if (group == NULL)
return 0;
m = group->members;
prev = NULL;
while (m) {
if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
break;
prev = m;
m = m->next;
}
if (m == NULL)
return 0;
if (prev)
prev->next = m->next;
else
group->members = m->next;
p2p_group_free_member(m);
group->num_members--;
return 1;
}
int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
const u8 *ie, size_t len)
{
struct p2p_group_member *m;
if (group == NULL)
return -1;
p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);
m = os_zalloc(sizeof(*m));
if (m == NULL)
return -1;
os_memcpy(m->addr, addr, ETH_ALEN);
m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
if (m->p2p_ie) {
m->client_info = p2p_build_client_info(addr, m->p2p_ie,
&m->dev_capab,
m->dev_addr);
}
#ifdef CONFIG_WIFI_DISPLAY
m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
#endif /* CONFIG_WIFI_DISPLAY */
p2p_group_remove_member(group, addr);
m->next = group->members;
group->members = m;
group->num_members++;
p2p_dbg(group->p2p, "Add client " MACSTR
" to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
m->client_info ? 1 : 0,
group->num_members, group->cfg->max_clients);
if (group->num_members == group->cfg->max_clients)
group->beacon_update = 1;
p2p_group_update_ies(group);
if (group->num_members == 1)
group->cfg->idle_update(group->cfg->cb_ctx, 0);
return 0;
}
struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
{
struct wpabuf *resp;
u8 *rlen;
size_t extra = 0;
#ifdef CONFIG_WIFI_DISPLAY
if (group->wfd_ie)
extra = wpabuf_len(group->wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
if (group->p2p->vendor_elem &&
group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
/*
* (Re)Association Response - P2P IE
* Status attribute (shall be present when association request is
* denied)
* Extended Listen Timing (may be present)
*/
resp = wpabuf_alloc(20 + extra);
if (resp == NULL)
return NULL;
#ifdef CONFIG_WIFI_DISPLAY
if (group->wfd_ie)
wpabuf_put_buf(resp, group->wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
if (group->p2p->vendor_elem &&
group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
wpabuf_put_buf(resp,
group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
rlen = p2p_buf_add_ie_hdr(resp);
if (status != P2P_SC_SUCCESS)
p2p_buf_add_status(resp, status);
p2p_buf_update_ie_hdr(resp, rlen);
return resp;
}
void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
{
if (p2p_group_remove_member(group, addr)) {
p2p_dbg(group->p2p, "Remove client " MACSTR
" from group; num_members=%u/%u",
MAC2STR(addr), group->num_members,
group->cfg->max_clients);
if (group->num_members == group->cfg->max_clients - 1)
group->beacon_update = 1;
p2p_group_update_ies(group);
if (group->num_members == 0)
group->cfg->idle_update(group->cfg->cb_ctx, 1);
}
}
/**
* p2p_match_dev_type_member - Match client device type with requested type
* @m: Group member
* @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
* Returns: 1 on match, 0 on mismatch
*
* This function can be used to match the Requested Device Type attribute in
* WPS IE with the device types of a group member for deciding whether a GO
* should reply to a Probe Request frame.
*/
static int p2p_match_dev_type_member(struct p2p_group_member *m,
struct wpabuf *wps)
{
const u8 *pos, *end;
struct wps_parse_attr attr;
u8 num_sec;
if (m->client_info == NULL || wps == NULL)
return 0;
pos = wpabuf_head(m->client_info);
end = pos + wpabuf_len(m->client_info);
pos += 1 + 2 * ETH_ALEN + 1 + 2;
if (end - pos < WPS_DEV_TYPE_LEN + 1)
return 0;
if (wps_parse_msg(wps, &attr))
return 1; /* assume no Requested Device Type attributes */
if (attr.num_req_dev_type == 0)
return 1; /* no Requested Device Type attributes -> match */
if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
return 1; /* Match with client Primary Device Type */
pos += WPS_DEV_TYPE_LEN;
num_sec = *pos++;
if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
return 0;
while (num_sec > 0) {
num_sec--;
if (dev_type_list_match(pos, attr.req_dev_type,
attr.num_req_dev_type))
return 1; /* Match with client Secondary Device Type */
pos += WPS_DEV_TYPE_LEN;
}
/* No matching device type found */
return 0;
}
int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
{
struct p2p_group_member *m;
if (p2p_match_dev_type(group->p2p, wps))
return 1; /* Match with own device type */
for (m = group->members; m; m = m->next) {
if (p2p_match_dev_type_member(m, wps))
return 1; /* Match with group client device type */
}
/* No match with Requested Device Type */
return 0;
}
int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
{
struct p2p_group_member *m;
struct p2p_message msg;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p, &msg))
return 1; /* Failed to parse - assume no filter on Device ID */
if (!msg.device_id)
return 1; /* No filter on Device ID */
if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
return 1; /* Match with our P2P Device Address */
for (m = group->members; m; m = m->next) {
if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
return 1; /* Match with group client P2P Device Address */
}
/* No match with Device ID */
return 0;
}
void p2p_group_notif_formation_done(struct p2p_group *group)
{
if (group == NULL)
return;
group->group_formation = 0;
group->beacon_update = 1;
p2p_group_update_ies(group);
}
int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
size_t noa_len)
{
if (noa == NULL) {
wpabuf_free(group->noa);
group->noa = NULL;
} else {
if (group->noa) {
if (wpabuf_size(group->noa) >= noa_len) {
group->noa->used = 0;
wpabuf_put_data(group->noa, noa, noa_len);
} else {
wpabuf_free(group->noa);
group->noa = NULL;
}
}
if (!group->noa) {
group->noa = wpabuf_alloc_copy(noa, noa_len);
if (group->noa == NULL)
return -1;
}
}
group->beacon_update = 1;
p2p_group_update_ies(group);
return 0;
}
static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
const u8 *dev_id)
{
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
return m;
}
return NULL;
}
const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group,
const u8 *dev_addr)
{
struct p2p_group_member *m;
if (!group)
return NULL;
m = p2p_group_get_client(group, dev_addr);
if (m)
return m->addr;
return NULL;
}
static struct p2p_group_member * p2p_group_get_client_iface(
struct p2p_group *group, const u8 *interface_addr)
{
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
return m;
}
return NULL;
}
const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
{
struct p2p_group_member *m;
if (group == NULL)
return NULL;
m = p2p_group_get_client_iface(group, addr);
if (m && !is_zero_ether_addr(m->dev_addr))
return m->dev_addr;
return NULL;
}
static struct wpabuf * p2p_build_go_disc_req(void)
{
struct wpabuf *buf;
buf = wpabuf_alloc(100);
if (buf == NULL)
return NULL;
p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
return buf;
}
int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
const u8 *searching_dev, int rx_freq)
{
struct p2p_group_member *m;
struct wpabuf *req;
struct p2p_data *p2p = group->p2p;
int freq;
m = p2p_group_get_client(group, dev_id);
if (m == NULL || m->client_info == NULL) {
p2p_dbg(group->p2p, "Requested client was not in this group "
MACSTR, MAC2STR(group->cfg->interface_addr));
return -1;
}
if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
p2p_dbg(group->p2p, "Requested client does not support client discoverability");
return -1;
}
p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to "
MACSTR, MAC2STR(dev_id));
req = p2p_build_go_disc_req();
if (req == NULL)
return -1;
/* TODO: Should really use group operating frequency here */
freq = rx_freq;
p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
group->cfg->interface_addr,
group->cfg->interface_addr,
wpabuf_head(req), wpabuf_len(req), 200, NULL)
< 0)
{
p2p_dbg(p2p, "Failed to send Action frame");
}
wpabuf_free(req);
return 0;
}
const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
{
return group->cfg->interface_addr;
}
u8 p2p_group_presence_req(struct p2p_group *group,
const u8 *client_interface_addr,
const u8 *noa, size_t noa_len)
{
struct p2p_group_member *m;
u8 curr_noa[50];
int curr_noa_len;
m = p2p_group_get_client_iface(group, client_interface_addr);
if (m == NULL || m->client_info == NULL) {
p2p_dbg(group->p2p, "Client was not in this group");
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
}
wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
if (group->p2p->cfg->get_noa)
curr_noa_len = group->p2p->cfg->get_noa(
group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
curr_noa, sizeof(curr_noa));
else
curr_noa_len = -1;
if (curr_noa_len < 0)
p2p_dbg(group->p2p, "Failed to fetch current NoA");
else if (curr_noa_len == 0)
p2p_dbg(group->p2p, "No NoA being advertized");
else
wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
curr_noa_len);
/* TODO: properly process request and store copy */
if (curr_noa_len > 0 || curr_noa_len == -1)
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
return P2P_SC_SUCCESS;
}
unsigned int p2p_get_group_num_members(struct p2p_group *group)
{
if (!group)
return 0;
return group->num_members;
}
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;
if (!iter)
iter = group->members;
else
iter = iter->next;
*next = iter;
if (!iter)
return NULL;
return iter->dev_addr;
}
int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
{
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
return 1;
}
return 0;
}
int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
size_t group_id_len)
{
if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
return 0;
if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
return 0;
return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
group->cfg->ssid_len) == 0;
}
void p2p_group_force_beacon_update_ies(struct p2p_group *group)
{
group->beacon_update = 1;
p2p_group_update_ies(group);
}
int p2p_group_get_freq(struct p2p_group *group)
{
return group->cfg->freq;
}
const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
{
return group->cfg;
}
void p2p_loop_on_all_groups(struct p2p_data *p2p,
int (*group_callback)(struct p2p_group *group,
void *user_data),
void *user_data)
{
unsigned int i;
for (i = 0; i < p2p->num_groups; i++) {
if (!group_callback(p2p->groups[i], user_data))
break;
}
}
int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
unsigned int *num)
{
struct p2p_channels intersect, res;
struct p2p_group_member *m;
if (!group || !common_freqs || !num)
return -1;
os_memset(&intersect, 0, sizeof(intersect));
os_memset(&res, 0, sizeof(res));
p2p_channels_union(&intersect, &group->p2p->cfg->channels,
&intersect);
p2p_channels_dump(group->p2p,
"Group common freqs before iterating members",
&intersect);
for (m = group->members; m; m = m->next) {
struct p2p_device *dev;
dev = p2p_get_device(group->p2p, m->dev_addr);
if (!dev || dev->channels.reg_classes == 0)
continue;
p2p_channels_intersect(&intersect, &dev->channels, &res);
intersect = res;
}
p2p_channels_dump(group->p2p, "Group common channels", &intersect);
os_memset(common_freqs, 0, *num * sizeof(int));
*num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
return 0;
}