50a9efe713
org.wi-fi.wfds service is not a replacement for non-WFA service matches. Do not try to replace the results with that if there is not sufficient room for the response. Instead, reply with all the matching services that fit into the message. org.wi-fi.wfds is the first entry in the list (if matching request/service is present), so it won't get overridden by other services. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
797 lines
21 KiB
C
797 lines
21 KiB
C
/*
|
|
* P2P - IE builder
|
|
* 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 "wps/wps_i.h"
|
|
#include "p2p_i.h"
|
|
|
|
|
|
void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
|
|
{
|
|
wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
|
|
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
|
|
|
|
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
|
|
wpabuf_put_u8(buf, dialog_token);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
|
|
u8 dialog_token)
|
|
{
|
|
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
|
|
wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
|
|
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
|
|
|
|
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
|
|
wpabuf_put_u8(buf, dialog_token);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
|
|
}
|
|
|
|
|
|
u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
|
|
{
|
|
u8 *len;
|
|
|
|
/* P2P IE header */
|
|
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
|
len = wpabuf_put(buf, 1); /* IE length to be filled */
|
|
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
|
|
wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
|
|
return len;
|
|
}
|
|
|
|
|
|
void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
|
|
{
|
|
/* Update P2P IE Length */
|
|
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
|
|
}
|
|
|
|
|
|
void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
|
|
{
|
|
/* P2P Capability */
|
|
wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
|
|
wpabuf_put_le16(buf, 2);
|
|
wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
|
|
wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
|
|
wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
|
|
dev_capab, group_capab);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
|
|
{
|
|
/* Group Owner Intent */
|
|
wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
|
|
wpabuf_put_le16(buf, 1);
|
|
wpabuf_put_u8(buf, go_intent);
|
|
wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
|
|
go_intent >> 1, go_intent & 0x01);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
|
|
u8 reg_class, u8 channel)
|
|
{
|
|
/* Listen Channel */
|
|
wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
|
|
wpabuf_put_le16(buf, 5);
|
|
wpabuf_put_data(buf, country, 3);
|
|
wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
|
|
wpabuf_put_u8(buf, channel); /* Channel Number */
|
|
wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
|
|
"Channel %u", reg_class, channel);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
|
|
u8 reg_class, u8 channel)
|
|
{
|
|
/* Operating Channel */
|
|
wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
|
|
wpabuf_put_le16(buf, 5);
|
|
wpabuf_put_data(buf, country, 3);
|
|
wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
|
|
wpabuf_put_u8(buf, channel); /* Channel Number */
|
|
wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
|
|
"Channel %u", reg_class, channel);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
|
|
struct p2p_channels *chan)
|
|
{
|
|
u8 *len;
|
|
size_t i;
|
|
|
|
/* Channel List */
|
|
wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
|
|
len = wpabuf_put(buf, 2); /* IE length to be filled */
|
|
wpabuf_put_data(buf, country, 3); /* Country String */
|
|
|
|
for (i = 0; i < chan->reg_classes; i++) {
|
|
struct p2p_reg_class *c = &chan->reg_class[i];
|
|
wpabuf_put_u8(buf, c->reg_class);
|
|
wpabuf_put_u8(buf, c->channels);
|
|
wpabuf_put_data(buf, c->channel, c->channels);
|
|
}
|
|
|
|
/* Update attribute length */
|
|
WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
|
|
len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_status(struct wpabuf *buf, u8 status)
|
|
{
|
|
/* Status */
|
|
wpabuf_put_u8(buf, P2P_ATTR_STATUS);
|
|
wpabuf_put_le16(buf, 1);
|
|
wpabuf_put_u8(buf, status);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
|
|
struct p2p_device *peer)
|
|
{
|
|
u8 *len;
|
|
u16 methods;
|
|
size_t nlen, i;
|
|
|
|
/* P2P Device Info */
|
|
wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
|
|
len = wpabuf_put(buf, 2); /* IE length to be filled */
|
|
|
|
/* P2P Device address */
|
|
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
|
|
|
|
/* Config Methods */
|
|
methods = 0;
|
|
if (peer && peer->wps_method != WPS_NOT_READY) {
|
|
if (peer->wps_method == WPS_PBC)
|
|
methods |= WPS_CONFIG_PUSHBUTTON;
|
|
else if (peer->wps_method == WPS_PIN_DISPLAY ||
|
|
peer->wps_method == WPS_PIN_KEYPAD) {
|
|
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
|
|
methods |= WPS_CONFIG_P2PS;
|
|
}
|
|
} else if (p2p->cfg->config_methods) {
|
|
methods |= p2p->cfg->config_methods &
|
|
(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
|
|
WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
|
|
} else {
|
|
methods |= WPS_CONFIG_PUSHBUTTON;
|
|
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
|
|
methods |= WPS_CONFIG_P2PS;
|
|
}
|
|
wpabuf_put_be16(buf, methods);
|
|
|
|
/* Primary Device Type */
|
|
wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
|
|
sizeof(p2p->cfg->pri_dev_type));
|
|
|
|
/* Number of Secondary Device Types */
|
|
wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
|
|
|
|
/* Secondary Device Type List */
|
|
for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
|
|
wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
|
|
WPS_DEV_TYPE_LEN);
|
|
|
|
/* Device Name */
|
|
nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
|
|
wpabuf_put_be16(buf, ATTR_DEV_NAME);
|
|
wpabuf_put_be16(buf, nlen);
|
|
wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
|
|
|
|
/* Update attribute length */
|
|
WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Device Info");
|
|
}
|
|
|
|
|
|
void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
|
|
{
|
|
/* P2P Device ID */
|
|
wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
|
|
wpabuf_put_le16(buf, ETH_ALEN);
|
|
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
|
|
}
|
|
|
|
|
|
void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
|
|
u8 client_timeout)
|
|
{
|
|
/* Configuration Timeout */
|
|
wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
|
|
wpabuf_put_le16(buf, 2);
|
|
wpabuf_put_u8(buf, go_timeout);
|
|
wpabuf_put_u8(buf, client_timeout);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) "
|
|
"client %d (*10ms)", go_timeout, client_timeout);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
|
|
{
|
|
/* Intended P2P Interface Address */
|
|
wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
|
|
wpabuf_put_le16(buf, ETH_ALEN);
|
|
wpabuf_put_data(buf, interface_addr, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
|
|
MAC2STR(interface_addr));
|
|
}
|
|
|
|
|
|
void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
|
|
{
|
|
/* P2P Group BSSID */
|
|
wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
|
|
wpabuf_put_le16(buf, ETH_ALEN);
|
|
wpabuf_put_data(buf, bssid, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
|
|
MAC2STR(bssid));
|
|
}
|
|
|
|
|
|
void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
/* P2P Group ID */
|
|
wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
|
|
wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
|
|
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
|
|
wpabuf_put_data(buf, ssid, ssid_len);
|
|
wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
|
|
MAC2STR(dev_addr));
|
|
wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
|
|
{
|
|
/* Invitation Flags */
|
|
wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
|
|
wpabuf_put_le16(buf, 1);
|
|
wpabuf_put_u8(buf, flags);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
|
|
}
|
|
|
|
|
|
static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
|
|
{
|
|
if (desc == NULL)
|
|
return;
|
|
|
|
wpabuf_put_u8(buf, desc->count_type);
|
|
wpabuf_put_le32(buf, desc->duration);
|
|
wpabuf_put_le32(buf, desc->interval);
|
|
wpabuf_put_le32(buf, desc->start_time);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
|
|
struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
|
|
{
|
|
/* Notice of Absence */
|
|
wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
|
|
wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
|
|
wpabuf_put_u8(buf, noa_index);
|
|
wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
|
|
p2p_buf_add_noa_desc(buf, desc1);
|
|
p2p_buf_add_noa_desc(buf, desc2);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
|
|
}
|
|
|
|
|
|
void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
|
|
u16 interval)
|
|
{
|
|
/* Extended Listen Timing */
|
|
wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
|
|
wpabuf_put_le16(buf, 4);
|
|
wpabuf_put_le16(buf, period);
|
|
wpabuf_put_le16(buf, interval);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec "
|
|
"interval %u msec)", period, interval);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
|
|
{
|
|
/* P2P Interface */
|
|
wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
|
|
wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
|
|
/* P2P Device address */
|
|
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
|
|
/*
|
|
* FIX: Fetch interface address list from driver. Do not include
|
|
* the P2P Device address if it is never used as interface address.
|
|
*/
|
|
/* P2P Interface Address Count */
|
|
wpabuf_put_u8(buf, 1);
|
|
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
|
|
u8 oper_class, u8 channel,
|
|
enum p2p_role_indication role)
|
|
{
|
|
/* OOB Group Owner Negotiation Channel */
|
|
wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
|
|
wpabuf_put_le16(buf, 6);
|
|
wpabuf_put_data(buf, country, 3);
|
|
wpabuf_put_u8(buf, oper_class); /* Operating Class */
|
|
wpabuf_put_u8(buf, channel); /* Channel Number */
|
|
wpabuf_put_u8(buf, (u8) role); /* Role indication */
|
|
wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
|
|
"Class %u Channel %u Role %d",
|
|
oper_class, channel, role);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
|
|
{
|
|
if (!p2p)
|
|
return;
|
|
|
|
/* Service Hash */
|
|
wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
|
|
wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
|
|
wpabuf_put_data(buf, p2p->p2ps_seek_hash,
|
|
p2p->p2ps_seek_count * P2PS_HASH_LEN);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
|
|
p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
|
|
{
|
|
size_t info_len = 0;
|
|
|
|
if (info && info[0])
|
|
info_len = os_strlen(info);
|
|
|
|
/* Session Information Data Info */
|
|
wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
|
|
wpabuf_put_le16(buf, (u16) info_len);
|
|
|
|
if (info) {
|
|
wpabuf_put_data(buf, info, info_len);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
|
|
}
|
|
}
|
|
|
|
|
|
void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
|
|
{
|
|
/* Connection Capability Info */
|
|
wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
|
|
wpabuf_put_le16(buf, 1);
|
|
wpabuf_put_u8(buf, connection_cap);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
|
|
connection_cap);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
|
|
{
|
|
if (!buf || !mac)
|
|
return;
|
|
|
|
/* Advertisement ID Info */
|
|
wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
|
|
wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
|
|
wpabuf_put_le32(buf, id);
|
|
wpabuf_put_data(buf, mac, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
|
|
id, MAC2STR(mac));
|
|
}
|
|
|
|
|
|
static int p2ps_wildcard_hash(struct p2p_data *p2p,
|
|
const u8 *hash, u8 hash_count)
|
|
{
|
|
u8 i;
|
|
const u8 *test = hash;
|
|
|
|
for (i = 0; i < hash_count; i++) {
|
|
if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
|
|
return 1;
|
|
test += P2PS_HASH_LEN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int p2p_wfa_service_adv(struct p2p_data *p2p)
|
|
{
|
|
struct p2ps_advertisement *adv;
|
|
|
|
for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
|
|
if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
|
|
os_strlen(P2PS_WILD_HASH_STR)) == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
|
|
u32 adv_id, u16 config_methods,
|
|
const char *svc_name, u8 **ie_len, u8 **pos,
|
|
size_t *total_len, u8 *attr_len)
|
|
{
|
|
size_t svc_len;
|
|
size_t remaining;
|
|
size_t info_len;
|
|
|
|
p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
|
|
svc_len = os_strlen(svc_name);
|
|
info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
|
|
svc_len;
|
|
|
|
if (info_len + *total_len > MAX_SVC_ADV_LEN) {
|
|
p2p_dbg(p2p,
|
|
"Unsufficient buffer, failed to add advertised service info");
|
|
return -1;
|
|
}
|
|
|
|
if (svc_len > 255) {
|
|
p2p_dbg(p2p,
|
|
"Invalid service name length (%u bytes), failed to add advertised service info",
|
|
(unsigned int) svc_len);
|
|
return -1;
|
|
}
|
|
|
|
if (*ie_len) {
|
|
int ie_data_len = (*pos - *ie_len) - 1;
|
|
|
|
if (ie_data_len < 0 || ie_data_len > 255) {
|
|
p2p_dbg(p2p,
|
|
"Invalid IE length, failed to add advertised service info");
|
|
return -1;
|
|
}
|
|
remaining = 255 - ie_data_len;
|
|
} else {
|
|
/*
|
|
* Adding new P2P IE header takes 6 extra bytes:
|
|
* - 2 byte IE header (1 byte IE id and 1 byte length)
|
|
* - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
|
|
*/
|
|
*ie_len = p2p_buf_add_ie_hdr(buf);
|
|
remaining = 255 - 4;
|
|
}
|
|
|
|
if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
|
|
/*
|
|
* Split adv_id, config_methods, and svc_name_len between two
|
|
* IEs.
|
|
*/
|
|
size_t front = remaining;
|
|
size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
|
|
u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
|
|
|
|
WPA_PUT_LE32(holder, adv_id);
|
|
WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
|
|
holder[sizeof(u32) + sizeof(u16)] = svc_len;
|
|
|
|
if (front)
|
|
wpabuf_put_data(buf, holder, front);
|
|
|
|
p2p_buf_update_ie_hdr(buf, *ie_len);
|
|
*ie_len = p2p_buf_add_ie_hdr(buf);
|
|
|
|
wpabuf_put_data(buf, &holder[front], back);
|
|
remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
|
|
back;
|
|
} else {
|
|
wpabuf_put_le32(buf, adv_id);
|
|
wpabuf_put_be16(buf, config_methods);
|
|
wpabuf_put_u8(buf, svc_len);
|
|
remaining -= sizeof(adv_id) + sizeof(config_methods) +
|
|
sizeof(u8);
|
|
}
|
|
|
|
if (remaining < svc_len) {
|
|
/* split svc_name between two or three IEs */
|
|
size_t front = remaining;
|
|
size_t back = svc_len - front;
|
|
|
|
if (front)
|
|
wpabuf_put_data(buf, svc_name, front);
|
|
|
|
p2p_buf_update_ie_hdr(buf, *ie_len);
|
|
*ie_len = p2p_buf_add_ie_hdr(buf);
|
|
|
|
/* In rare cases, we must split across 3 attributes */
|
|
if (back > 255 - 4) {
|
|
wpabuf_put_data(buf, &svc_name[front], 255 - 4);
|
|
back -= 255 - 4;
|
|
front += 255 - 4;
|
|
p2p_buf_update_ie_hdr(buf, *ie_len);
|
|
*ie_len = p2p_buf_add_ie_hdr(buf);
|
|
}
|
|
|
|
wpabuf_put_data(buf, &svc_name[front], back);
|
|
remaining = 255 - 4 - back;
|
|
} else {
|
|
wpabuf_put_data(buf, svc_name, svc_len);
|
|
remaining -= svc_len;
|
|
}
|
|
|
|
p2p_buf_update_ie_hdr(buf, *ie_len);
|
|
|
|
/* set *ie_len to NULL if a new IE has to be added on the next call */
|
|
if (!remaining)
|
|
*ie_len = NULL;
|
|
|
|
/* set *pos to point to the next byte to update */
|
|
*pos = wpabuf_put(buf, 0);
|
|
|
|
*total_len += info_len;
|
|
WPA_PUT_LE16(attr_len, (u16) *total_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
|
|
u8 hash_count, const u8 *hash,
|
|
struct p2ps_advertisement *adv_list)
|
|
{
|
|
struct p2ps_advertisement *adv;
|
|
int p2ps_wildcard, found = 0;
|
|
size_t total_len;
|
|
struct wpabuf *tmp_buf = NULL;
|
|
u8 *pos, *attr_len, *ie_len = NULL;
|
|
|
|
if (!adv_list || !hash || !hash_count)
|
|
return;
|
|
|
|
wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
|
|
hash, hash_count * P2PS_HASH_LEN);
|
|
p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
|
|
p2p_wfa_service_adv(p2p);
|
|
|
|
/* Allocate temp buffer, allowing for overflow of 1 instance */
|
|
tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
|
|
if (!tmp_buf)
|
|
return;
|
|
|
|
/*
|
|
* Attribute data can be split into a number of IEs. Start with the
|
|
* first IE and the attribute headers here.
|
|
*/
|
|
ie_len = p2p_buf_add_ie_hdr(tmp_buf);
|
|
|
|
total_len = 0;
|
|
|
|
wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
|
|
attr_len = wpabuf_put(tmp_buf, sizeof(u16));
|
|
WPA_PUT_LE16(attr_len, (u16) total_len);
|
|
p2p_buf_update_ie_hdr(tmp_buf, ie_len);
|
|
pos = wpabuf_put(tmp_buf, 0);
|
|
|
|
if (p2ps_wildcard) {
|
|
/* org.wi-fi.wfds match found */
|
|
p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
|
|
&ie_len, &pos, &total_len, attr_len);
|
|
found++;
|
|
}
|
|
|
|
/* add advertised service info of matching services */
|
|
for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
|
|
adv = adv->next) {
|
|
const u8 *test = hash;
|
|
u8 i;
|
|
|
|
for (i = 0; i < hash_count; i++) {
|
|
/* exact name hash match */
|
|
if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
|
|
p2p_buf_add_service_info(tmp_buf, p2p,
|
|
adv->id,
|
|
adv->config_methods,
|
|
adv->svc_name,
|
|
&ie_len, &pos,
|
|
&total_len,
|
|
attr_len))
|
|
break;
|
|
found++;
|
|
test += P2PS_HASH_LEN;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
wpabuf_put_buf(buf, tmp_buf);
|
|
wpabuf_free(tmp_buf);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
|
|
{
|
|
if (!buf || !mac)
|
|
return;
|
|
|
|
/* Session ID Info */
|
|
wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
|
|
wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
|
|
wpabuf_put_le32(buf, id);
|
|
wpabuf_put_data(buf, mac, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
|
|
id, MAC2STR(mac));
|
|
}
|
|
|
|
|
|
void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
|
|
{
|
|
if (!buf || !len || !mask)
|
|
return;
|
|
|
|
/* Feature Capability */
|
|
wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
|
|
wpabuf_put_le16(buf, len);
|
|
wpabuf_put_data(buf, mask, len);
|
|
wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
|
|
}
|
|
|
|
|
|
void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
/* P2P Group ID */
|
|
wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
|
|
wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
|
|
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
|
|
wpabuf_put_data(buf, ssid, ssid_len);
|
|
wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
|
|
MAC2STR(dev_addr));
|
|
}
|
|
|
|
|
|
static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
|
|
const char *val)
|
|
{
|
|
size_t len;
|
|
|
|
len = val ? os_strlen(val) : 0;
|
|
if (wpabuf_tailroom(buf) < 4 + len)
|
|
return -1;
|
|
wpabuf_put_be16(buf, attr);
|
|
#ifndef CONFIG_WPS_STRICT
|
|
if (len == 0) {
|
|
/*
|
|
* Some deployed WPS implementations fail to parse zeor-length
|
|
* attributes. As a workaround, send a space character if the
|
|
* device attribute string is empty.
|
|
*/
|
|
if (wpabuf_tailroom(buf) < 3)
|
|
return -1;
|
|
wpabuf_put_be16(buf, 1);
|
|
wpabuf_put_u8(buf, ' ');
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_WPS_STRICT */
|
|
wpabuf_put_be16(buf, len);
|
|
if (val)
|
|
wpabuf_put_data(buf, val, len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
|
|
int all_attr)
|
|
{
|
|
u8 *len;
|
|
int i;
|
|
|
|
if (wpabuf_tailroom(buf) < 6)
|
|
return -1;
|
|
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
|
|
len = wpabuf_put(buf, 1);
|
|
wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
|
|
|
|
if (wps_build_version(buf) < 0)
|
|
return -1;
|
|
|
|
if (all_attr) {
|
|
if (wpabuf_tailroom(buf) < 5)
|
|
return -1;
|
|
wpabuf_put_be16(buf, ATTR_WPS_STATE);
|
|
wpabuf_put_be16(buf, 1);
|
|
wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
|
|
}
|
|
|
|
if (pw_id >= 0) {
|
|
if (wpabuf_tailroom(buf) < 6)
|
|
return -1;
|
|
/* Device Password ID */
|
|
wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
|
|
wpabuf_put_be16(buf, 2);
|
|
wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
|
|
pw_id);
|
|
wpabuf_put_be16(buf, pw_id);
|
|
}
|
|
|
|
if (all_attr) {
|
|
if (wpabuf_tailroom(buf) < 5)
|
|
return -1;
|
|
wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
|
|
wpabuf_put_be16(buf, 1);
|
|
wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
|
|
|
|
if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
|
|
p2p_add_wps_string(buf, ATTR_MANUFACTURER,
|
|
p2p->cfg->manufacturer) < 0 ||
|
|
p2p_add_wps_string(buf, ATTR_MODEL_NAME,
|
|
p2p->cfg->model_name) < 0 ||
|
|
p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
|
|
p2p->cfg->model_number) < 0 ||
|
|
p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
|
|
p2p->cfg->serial_number) < 0)
|
|
return -1;
|
|
|
|
if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
|
|
return -1;
|
|
wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
|
|
wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
|
|
wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
|
|
|
|
if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
|
|
< 0)
|
|
return -1;
|
|
|
|
if (wpabuf_tailroom(buf) < 6)
|
|
return -1;
|
|
wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
|
|
wpabuf_put_be16(buf, 2);
|
|
wpabuf_put_be16(buf, p2p->cfg->config_methods);
|
|
}
|
|
|
|
if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
|
|
return -1;
|
|
|
|
if (all_attr && p2p->cfg->num_sec_dev_types) {
|
|
if (wpabuf_tailroom(buf) <
|
|
4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
|
|
return -1;
|
|
wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
|
|
wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
|
|
p2p->cfg->num_sec_dev_types);
|
|
wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
|
|
WPS_DEV_TYPE_LEN *
|
|
p2p->cfg->num_sec_dev_types);
|
|
}
|
|
|
|
/* Add the WPS vendor extensions */
|
|
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
|
|
if (p2p->wps_vendor_ext[i] == NULL)
|
|
break;
|
|
if (wpabuf_tailroom(buf) <
|
|
4 + wpabuf_len(p2p->wps_vendor_ext[i]))
|
|
continue;
|
|
wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
|
|
wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
|
|
wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
|
|
}
|
|
|
|
p2p_buf_update_ie_hdr(buf, len);
|
|
|
|
return 0;
|
|
}
|