P2P: Add initial version of P2P Module

This commit is contained in:
Jouni Malinen 2010-07-18 14:30:25 -07:00 committed by Jouni Malinen
parent c2af2afb3b
commit b22128efdc
26 changed files with 12899 additions and 3 deletions

View File

@ -1,4 +1,4 @@
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
all:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done

30
src/ap/p2p_hostapd.c Normal file
View File

@ -0,0 +1,30 @@
/*
* hostapd / P2P integration
* Copyright (c) 2009, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "p2p/p2p.h"
#include "sta_info.h"
#include "p2p_hostapd.h"
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
if (sta->p2p_ie == NULL)
return 0;
return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
}

33
src/ap/p2p_hostapd.h Normal file
View File

@ -0,0 +1,33 @@
/*
* hostapd / P2P integration
* Copyright (c) 2009, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef P2P_HOSTAPD_H
#define P2P_HOSTAPD_H
#ifdef CONFIG_P2P
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen);
#else /* CONFIG_P2P */
int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
return 0;
}
#endif /* CONFIG_P2P */
#endif /* P2P_HOSTAPD_H */

View File

@ -90,6 +90,32 @@ extern "C" {
#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
/** P2P device found */
#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
/** A P2P device requested GO negotiation, but we were not ready to start the
* negotiation */
#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
/* parameters: <peer address> <PIN> */
#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
/* parameters: <src addr> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
/* hostapd control interface - fixed message prefixes */
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "

9
src/p2p/Makefile Normal file
View File

@ -0,0 +1,9 @@
all:
@echo Nothing to be made.
clean:
for d in $(SUBDIRS); do make -C $$d clean; done
rm -f *~ *.o *.d
install:
@echo Nothing to be made.

2900
src/p2p/p2p.c Normal file

File diff suppressed because it is too large Load Diff

1216
src/p2p/p2p.h Normal file

File diff suppressed because it is too large Load Diff

389
src/p2p/p2p_build.c Normal file
View File

@ -0,0 +1,389 @@
/*
* P2P - IE builder
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING 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_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, P2P_OUI_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_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, P2P_OUI_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_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, P2P_OUI_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_printf(MSG_DEBUG, "P2P: * Channel List");
}
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) {
if (peer->wps_method == WPS_PBC)
methods |= WPS_CONFIG_PUSHBUTTON;
else if (peer->wps_method == WPS_PIN_LABEL)
methods |= WPS_CONFIG_LABEL;
else if (peer->wps_method == WPS_PIN_DISPLAY ||
peer->wps_method == WPS_PIN_KEYPAD)
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
} else {
methods |= WPS_CONFIG_PUSHBUTTON;
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
}
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));
}
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_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
int all_attr)
{
u8 *len;
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(buf, 1);
wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
wps_build_version(buf);
if (all_attr) {
wpabuf_put_be16(buf, ATTR_WPS_STATE);
wpabuf_put_be16(buf, 1);
wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
}
/* 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) {
size_t nlen;
wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
wpabuf_put_be16(buf, 1);
wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
#if 0
/* FIX */
wps_build_uuid_e(buf, reg->wps->uuid);
wps_build_manufacturer(dev, buf);
wps_build_model_name(dev, buf);
wps_build_model_number(dev, buf);
wps_build_serial_number(dev, buf);
#endif
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);
wpabuf_put_be16(buf, ATTR_DEV_NAME);
nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
wpabuf_put_be16(buf, nlen);
if (p2p->cfg->dev_name)
wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
wpabuf_put_be16(buf, 2);
wpabuf_put_be16(buf, 0); /* FIX: ? */
}
wps_build_version2(buf);
p2p_buf_update_ie_hdr(buf, len);
}

357
src/p2p/p2p_dev_disc.c Normal file
View File

@ -0,0 +1,357 @@
/*
* Wi-Fi Direct - P2P Device Discoverability procedure
* Copyright (c) 2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "p2p_i.h"
#include "p2p.h"
static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
struct p2p_device *go,
const u8 *dev_id)
{
struct wpabuf *buf;
u8 *len;
buf = wpabuf_alloc(100);
if (buf == NULL)
return NULL;
go->dialog_token++;
if (go->dialog_token == 0)
go->dialog_token = 1;
p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_device_id(buf, dev_id);
p2p_buf_add_group_id(buf, go->p2p_device_addr, go->oper_ssid,
go->oper_ssid_len);
p2p_buf_update_ie_hdr(buf, len);
return buf;
}
void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Device Discoverability Request TX callback: success=%d",
success);
if (!success) {
/*
* Use P2P find, if needed, to find the other device or to
* retry device discoverability.
*/
p2p_set_state(p2p, P2P_CONNECT);
p2p_set_timeout(p2p, 0, 100000);
return;
}
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: GO acknowledged Device Discoverability Request - wait "
"for response");
/*
* TODO: is the remain-on-channel from Action frame TX long enough for
* most cases or should we try to increase its duration and/or start
* another remain-on-channel if needed once the previous one expires?
*/
}
int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
{
struct p2p_device *go;
struct wpabuf *req;
go = p2p_get_device(p2p, dev->member_in_go_dev);
if (go == NULL || dev->oper_freq <= 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Could not find peer entry for GO and frequency "
"to send Device Discoverability Request");
return -1;
}
req = p2p_build_dev_disc_req(p2p, go, dev->p2p_device_addr);
if (req == NULL)
return -1;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Sending Device Discoverability Request to GO " MACSTR
" for client " MACSTR,
MAC2STR(go->p2p_device_addr), MAC2STR(dev->p2p_device_addr));
p2p->pending_client_disc_go = go;
os_memcpy(p2p->pending_client_disc_addr, dev->p2p_device_addr,
ETH_ALEN);
p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, dev->oper_freq,
go->p2p_device_addr, p2p->cfg->dev_addr,
go->p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 1000) < 0)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
wpabuf_free(req);
/* TODO: how to recover from failure? */
return -1;
}
wpabuf_free(req);
return 0;
}
static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
{
struct wpabuf *buf;
u8 *len;
buf = wpabuf_alloc(100);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_status(buf, status);
p2p_buf_update_ie_hdr(buf, len);
return buf;
}
void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Device Discoverability Response TX callback: success=%d",
success);
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
const u8 *addr, int freq, u8 status)
{
struct wpabuf *resp;
resp = p2p_build_dev_disc_resp(dialog_token, status);
if (resp == NULL)
return;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Sending Device Discoverability Response to " MACSTR
" (status %u freq %d)",
MAC2STR(addr), status, freq);
p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, addr,
p2p->cfg->dev_addr, p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) <
0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
}
wpabuf_free(resp);
}
void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_message msg;
size_t g;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Device Discoverability Request from " MACSTR
" (freq=%d)", MAC2STR(sa), rx_freq);
if (p2p_parse(data, len, &msg))
return;
if (msg.dialog_token == 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invalid Dialog Token 0 (must be nonzero) in "
"Device Discoverability Request");
p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
P2P_SC_FAIL_INVALID_PARAMS);
p2p_parse_free(&msg);
return;
}
if (msg.device_id == NULL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: P2P Device ID attribute missing from Device "
"Discoverability Request");
p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
P2P_SC_FAIL_INVALID_PARAMS);
p2p_parse_free(&msg);
return;
}
for (g = 0; g < p2p->num_groups; g++) {
if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
rx_freq) == 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled "
"GO Discoverability Request for the target "
"device");
/*
* P2P group code will use a callback to indicate TX
* status, so that we can reply to the request once the
* target client has acknowledged the request or it has
* timed out.
*/
p2p->pending_dev_disc_dialog_token = msg.dialog_token;
os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
p2p->pending_dev_disc_freq = rx_freq;
p2p_parse_free(&msg);
return;
}
}
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client "
"was not found in any group or did not support client "
"discoverability");
p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
p2p_parse_free(&msg);
}
void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_message msg;
struct p2p_device *go;
u8 status;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Device Discoverability Response from " MACSTR,
MAC2STR(sa));
go = p2p->pending_client_disc_go;
if (go == NULL || os_memcmp(sa, go->p2p_device_addr, ETH_ALEN) != 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected "
"Device Discoverability Response");
return;
}
if (p2p_parse(data, len, &msg))
return;
if (msg.status == NULL) {
p2p_parse_free(&msg);
return;
}
if (msg.dialog_token != go->dialog_token) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device "
"Discoverability Response with unexpected dialog "
"token %u (expected %u)",
msg.dialog_token, go->dialog_token);
p2p_parse_free(&msg);
return;
}
status = *msg.status;
p2p_parse_free(&msg);
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Device Discoverability Response status %u", status);
if (p2p->go_neg_peer == NULL ||
os_memcmp(p2p->pending_client_disc_addr,
p2p->go_neg_peer->p2p_device_addr, ETH_ALEN) != 0 ||
os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->p2p_device_addr,
ETH_ALEN) != 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending "
"operation with the client discoverability peer "
"anymore");
return;
}
if (status == 0) {
/*
* Peer is expected to be awake for at least 100 TU; try to
* connect immediately.
*/
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Client discoverability request succeeded");
p2p_set_timeout(p2p, 0, 0);
} else {
/*
* Client discoverability request failed; try to connect from
* timeout.
*/
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Client discoverability request failed");
p2p_set_timeout(p2p, 0, 500000);
}
}
void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: GO Discoverability Request TX callback: success=%d",
success);
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (p2p->pending_dev_disc_dialog_token == 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device "
"Discoverability Request");
return;
}
p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
p2p->pending_dev_disc_addr,
p2p->pending_dev_disc_freq,
success ? P2P_SC_SUCCESS :
P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
p2p->pending_dev_disc_dialog_token = 0;
}
void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
unsigned int tu;
struct wpabuf *ies;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received GO Discoverability Request - remain awake for "
"100 TU");
ies = p2p_build_probe_resp_ies(p2p);
if (ies == NULL)
return;
/* Remain awake 100 TU on operating channel */
p2p->pending_client_disc_freq = rx_freq;
tu = 100;
if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
ies) < 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to start listen mode for client "
"discoverability");
}
wpabuf_free(ies);
}

1064
src/p2p/p2p_go_neg.c Normal file

File diff suppressed because it is too large Load Diff

628
src/p2p/p2p_group.c Normal file
View File

@ -0,0 +1,628 @@
/*
* Wi-Fi Direct - P2P group operations
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.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 *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;
int group_formation;
int beacon_update;
struct wpabuf *noa;
};
static void p2p_group_update_ies(struct p2p_group *group);
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(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);
return group;
}
static void p2p_group_free_member(struct p2p_group_member *m)
{
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;
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 = group->p2p;
if (group == NULL)
return;
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);
os_free(group);
}
static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
{
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 = 0, group_capab = 0;
/* P2P Capability */
dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
if (group->cfg->persistent_group)
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
if (group->group_formation)
group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
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_build_beacon_ie(struct p2p_group *group)
{
struct wpabuf *ie;
u8 *len;
ie = wpabuf_alloc(257);
if (ie == NULL)
return NULL;
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;
}
static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
{
u8 *group_info;
struct wpabuf *ie;
struct p2p_group_member *m;
u8 *len;
ie = wpabuf_alloc(257);
if (ie == NULL)
return NULL;
len = p2p_buf_add_ie_hdr(ie);
p2p_group_add_common_ies(group, ie);
p2p_group_add_noa(ie, group->noa);
/* P2P Device Info */
p2p_buf_add_device_info(ie, group->p2p, NULL);
if (group->members) {
/* P2P Group Info */
group_info = wpabuf_put(ie, 0);
wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
wpabuf_put_le16(ie, 0); /* Length to be filled */
for (m = group->members; m; m = m->next)
p2p_client_info(ie, m);
WPA_PUT_LE16(group_info + 1,
(u8 *) wpabuf_put(ie, 0) - group_info - 3);
}
p2p_buf_update_ie_hdr(ie, len);
return ie;
}
static void p2p_group_update_ies(struct p2p_group *group)
{
struct wpabuf *beacon_ie;
struct wpabuf *probe_resp_ie;
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;
}
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;
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 == NULL) {
p2p_group_free_member(m);
return -1;
}
m->client_info = p2p_build_client_info(addr, m->p2p_ie, &m->dev_capab,
m->dev_addr);
if (m->client_info == NULL) {
p2p_group_free_member(m);
return -1;
}
m->next = group->members;
group->members = m;
p2p_group_update_ies(group);
return 0;
}
struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
{
struct wpabuf *resp;
u8 *rlen;
/*
* (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);
if (resp == NULL)
return NULL;
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)
{
struct p2p_group_member *m, *prev;
if (group == NULL)
return;
m = group->members;
prev = NULL;
while (m) {
if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
break;
prev = m;
m = m->next;
}
if (m) {
if (prev)
prev->next = m->next;
else
group->members = m->next;
p2p_group_free_member(m);
p2p_group_update_ies(group);
}
}
/**
* 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;
}
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->size = 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;
}
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;
}
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) {
wpa_printf(MSG_DEBUG, "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)) {
wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
"client discoverability");
return -1;
}
wpa_printf(MSG_DEBUG, "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) < 0)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"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) {
wpa_printf(MSG_DEBUG, "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)
wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
else if (curr_noa_len == 0)
wpa_printf(MSG_DEBUG, "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)
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
return P2P_SC_SUCCESS;
}

580
src/p2p/p2p_i.h Normal file
View File

@ -0,0 +1,580 @@
/*
* P2P - Internal definitions for P2P module
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef P2P_I_H
#define P2P_I_H
#include "utils/list.h"
#include "p2p.h"
/* TODO: add removal of expired P2P device entries */
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
REMOTE_GO
};
/**
* struct p2p_device - P2P Device data (internal to P2P module)
*/
struct p2p_device {
struct dl_list list;
struct os_time last_seen;
int listen_freq;
int level;
enum p2p_wps_method wps_method;
u8 p2p_device_addr[ETH_ALEN]; /* P2P Device Address of the peer */
u8 pri_dev_type[8];
char device_name[33];
u16 config_methods;
u8 dev_capab;
u8 group_capab;
/*
* If the peer was discovered based on an interface address (e.g., GO
* from Beacon/Probe Response), the interface address is stored here.
* p2p_device_addr must still be set in such a case to the unique
* identifier for the P2P Device.
*/
u8 interface_addr[ETH_ALEN];
/*
* P2P Device Address of the GO in whose group this P2P Device is a
* client.
*/
u8 member_in_go_dev[ETH_ALEN];
/*
* P2P Interface Address of the GO in whose group this P2P Device is a
* client.
*/
u8 member_in_go_iface[ETH_ALEN];
int go_neg_req_sent;
enum p2p_go_state go_state;
u8 dialog_token;
u8 intended_addr[ETH_ALEN];
char country[3];
struct p2p_channels channels;
int oper_freq;
u8 oper_ssid[32];
size_t oper_ssid_len;
/**
* req_config_methods - Pending provisioning discovery methods
*/
u16 req_config_methods;
#define P2P_DEV_PROBE_REQ_ONLY BIT(0)
#define P2P_DEV_REPORTED BIT(1)
#define P2P_DEV_NOT_YET_READY BIT(2)
#define P2P_DEV_SD_INFO BIT(3)
#define P2P_DEV_SD_SCHEDULE BIT(4)
#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
#define P2P_DEV_USER_REJECTED BIT(7)
#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
unsigned int flags;
int status; /* enum p2p_status_code */
unsigned int wait_count;
unsigned int invitation_reqs;
u16 ext_listen_period;
u16 ext_listen_interval;
};
struct p2p_sd_query {
struct p2p_sd_query *next;
u8 peer[ETH_ALEN];
int for_all_peers;
struct wpabuf *tlvs;
};
/**
* struct p2p_data - P2P module data (internal to P2P module)
*/
struct p2p_data {
/**
* cfg - P2P module configuration
*
* This is included in the same memory allocation with the
* struct p2p_data and as such, must not be freed separately.
*/
struct p2p_config *cfg;
/**
* state - The current P2P state
*/
enum p2p_state {
/**
* P2P_IDLE - Idle
*/
P2P_IDLE,
/**
* P2P_SEARCH - Search (Device Discovery)
*/
P2P_SEARCH,
/**
* P2P_CONNECT - Trying to start GO Negotiation
*/
P2P_CONNECT,
/**
* P2P_CONNECT_LISTEN - Listen during GO Negotiation start
*/
P2P_CONNECT_LISTEN,
/**
* P2P_GO_NEG - In GO Negotiation
*/
P2P_GO_NEG,
/**
* P2P_LISTEN_ONLY - Listen only
*/
P2P_LISTEN_ONLY,
/**
* P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
*/
P2P_WAIT_PEER_CONNECT,
/**
* P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
*/
P2P_WAIT_PEER_IDLE,
/**
* P2P_SD_DURING_FIND - Service Discovery during find
*/
P2P_SD_DURING_FIND,
/**
* P2P_PROVISIONING - Provisioning (during group formation)
*/
P2P_PROVISIONING,
/**
* P2P_PD_DURING_FIND - Provision Discovery during find
*/
P2P_PD_DURING_FIND,
/**
* P2P_INVITE - Trying to start Invite
*/
P2P_INVITE,
/**
* P2P_INVITE_LISTEN - Listen during Invite
*/
P2P_INVITE_LISTEN,
} state;
/**
* min_disc_int - minDiscoverableInterval
*/
int min_disc_int;
/**
* max_disc_int - maxDiscoverableInterval
*/
int max_disc_int;
/**
* devices - List of known P2P Device peers
*/
struct dl_list devices;
/**
* go_neg_peer - Pointer to GO Negotiation peer
*/
struct p2p_device *go_neg_peer;
/**
* invite_peer - Pointer to Invite peer
*/
struct p2p_device *invite_peer;
const u8 *invite_go_dev_addr;
u8 invite_go_dev_addr_buf[ETH_ALEN];
/**
* sd_peer - Pointer to Service Discovery peer
*/
struct p2p_device *sd_peer;
/**
* sd_query - Pointer to Service Discovery query
*/
struct p2p_sd_query *sd_query;
/* GO Negotiation data */
/**
* intended_addr - Local Intended P2P Interface Address
*
* This address is used during group owner negotiation as the Intended
* P2P Interface Address and the group interface will be created with
* address as the local address in case of successfully completed
* negotiation.
*/
u8 intended_addr[ETH_ALEN];
/**
* go_intent - Local GO Intent to be used during GO Negotiation
*/
u8 go_intent;
/**
* next_tie_breaker - Next tie-breaker value to use in GO Negotiation
*/
u8 next_tie_breaker;
/**
* ssid - Selected SSID for GO Negotiation (if local end will be GO)
*/
u8 ssid[32];
/**
* ssid_len - ssid length in octets
*/
size_t ssid_len;
/**
* Regulatory class for own operational channel
*/
u8 op_reg_class;
/**
* op_channel - Own operational channel
*/
u8 op_channel;
/**
* channels - Own supported regulatory classes and channels
*
* List of supposerted channels per regulatory class. The regulatory
* classes are defined in IEEE Std 802.11-2007 Annex J and the
* numbering of the clases depends on the configured country code.
*/
struct p2p_channels channels;
enum p2p_pending_action_state {
P2P_NO_PENDING_ACTION,
P2P_PENDING_GO_NEG_REQUEST,
P2P_PENDING_GO_NEG_RESPONSE,
P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
P2P_PENDING_GO_NEG_CONFIRM,
P2P_PENDING_SD,
P2P_PENDING_PD,
P2P_PENDING_INVITATION_REQUEST,
P2P_PENDING_INVITATION_RESPONSE,
P2P_PENDING_DEV_DISC_REQUEST,
P2P_PENDING_DEV_DISC_RESPONSE,
P2P_PENDING_GO_DISC_REQ
} pending_action_state;
unsigned int pending_listen_freq;
unsigned int pending_listen_sec;
unsigned int pending_listen_usec;
u8 dev_capab;
int in_listen;
int drv_in_listen;
/**
* sd_queries - Pending service discovery queries
*/
struct p2p_sd_query *sd_queries;
/**
* srv_update_indic - Service Update Indicator for local services
*/
u16 srv_update_indic;
/* P2P Invitation data */
enum p2p_invite_role inv_role;
u8 inv_bssid[ETH_ALEN];
int inv_bssid_set;
u8 inv_ssid[32];
size_t inv_ssid_len;
u8 inv_sa[ETH_ALEN];
u8 inv_group_bssid[ETH_ALEN];
u8 *inv_group_bssid_ptr;
u8 inv_go_dev_addr[ETH_ALEN];
u8 inv_status;
int inv_op_freq;
int inv_persistent;
enum p2p_discovery_type find_type;
u8 last_prog_scan_class;
u8 last_prog_scan_chan;
int p2p_scan_running;
enum p2p_after_scan {
P2P_AFTER_SCAN_NOTHING,
P2P_AFTER_SCAN_LISTEN,
P2P_AFTER_SCAN_CONNECT
} start_after_scan;
u8 after_scan_peer[ETH_ALEN];
struct p2p_group **groups;
size_t num_groups;
struct p2p_device *pending_client_disc_go;
u8 pending_client_disc_addr[ETH_ALEN];
u8 pending_dev_disc_dialog_token;
u8 pending_dev_disc_addr[ETH_ALEN];
int pending_dev_disc_freq;
unsigned int pending_client_disc_freq;
int ext_listen_only;
unsigned int ext_listen_period;
unsigned int ext_listen_interval;
unsigned int ext_listen_interval_sec;
unsigned int ext_listen_interval_usec;
};
/**
* struct p2p_message - Parsed P2P message (or P2P IE)
*/
struct p2p_message {
struct wpabuf *p2p_attributes;
struct wpabuf *wps_attributes;
u8 dialog_token;
const u8 *capability;
const u8 *go_intent;
const u8 *status;
const u8 *listen_channel;
const u8 *operating_channel;
const u8 *channel_list;
u8 channel_list_len;
const u8 *config_timeout;
const u8 *intended_addr;
const u8 *group_bssid;
const u8 *invitation_flags;
const u8 *group_info;
size_t group_info_len;
const u8 *group_id;
size_t group_id_len;
const u8 *device_id;
const u8 *manageability;
const u8 *noa;
size_t noa_len;
const u8 *ext_listen_timing;
const u8 *minor_reason_code;
/* P2P Device Info */
const u8 *p2p_device_info;
size_t p2p_device_info_len;
const u8 *p2p_device_addr;
const u8 *pri_dev_type;
u8 num_sec_dev_types;
char device_name[33];
u16 config_methods;
/* WPS IE */
u16 dev_password_id;
u16 wps_config_methods;
const u8 *wps_pri_dev_type;
/* DS Parameter Set IE */
const u8 *ds_params;
/* SSID IE */
const u8 *ssid;
};
#define P2P_MAX_GROUP_ENTRIES 50
struct p2p_group_info {
unsigned int num_clients;
struct p2p_client_info {
const u8 *p2p_device_addr;
const u8 *p2p_interface_addr;
u8 dev_capab;
u16 config_methods;
const u8 *pri_dev_type;
u8 num_sec_dev_types;
const u8 *sec_dev_types;
const char *dev_name;
size_t dev_name_len;
} client[P2P_MAX_GROUP_ENTRIES];
};
/* p2p_utils.c */
int p2p_random(char *buf, size_t len);
int p2p_channel_to_freq(const char *country, int reg_class, int channel);
int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
u8 channel);
/* p2p_parse.c */
int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
void p2p_parse_free(struct p2p_message *msg);
int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
int p2p_group_info_parse(const u8 *gi, size_t gi_len,
struct p2p_group_info *info);
/* p2p_build.c */
struct p2p_noa_desc {
u8 count_type;
u32 duration;
u32 interval;
u32 start_time;
};
/* p2p_group.c */
const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
u8 p2p_group_presence_req(struct p2p_group *group,
const u8 *client_interface_addr,
const u8 *noa, size_t noa_len);
void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
u8 dialog_token);
u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
void p2p_buf_add_status(struct wpabuf *buf, u8 status);
void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
struct p2p_device *peer);
void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel);
void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel);
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
struct p2p_channels *chan);
void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
u8 client_timeout);
void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len);
void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
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);
void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
u16 interval);
void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
int all_attr);
/* p2p_sd.c */
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
struct p2p_device *dev);
void p2p_free_sd_queries(struct p2p_data *p2p);
void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
/* p2p_go_neg.c */
int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
struct p2p_device *dev,
const u8 *channel_list, size_t channel_list_len);
void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
/* p2p_pd.c */
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join);
/* p2p_invitation.c */
void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
const u8 *go_dev_addr);
void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
/* p2p_dev_disc.c */
void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
/* p2p.c */
void p2p_set_state(struct p2p_data *p2p, int new_state);
void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
unsigned int usec);
void p2p_clear_timeout(struct p2p_data *p2p);
void p2p_continue_find(struct p2p_data *p2p);
struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
const u8 *addr,
struct p2p_message *msg);
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg);
struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
const u8 *addr);
void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
int status);
void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
size_t num_req_dev_type);
struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
#endif /* P2P_I_H */

474
src/p2p/p2p_invitation.c Normal file
View File

@ -0,0 +1,474 @@
/*
* Wi-Fi Direct - P2P Invitation procedure
* Copyright (c) 2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "p2p_i.h"
#include "p2p.h"
static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
struct p2p_device *peer,
const u8 *go_dev_addr)
{
struct wpabuf *buf;
u8 *len;
const u8 *dev_addr;
buf = wpabuf_alloc(1000);
if (buf == NULL)
return NULL;
peer->dialog_token++;
if (peer->dialog_token == 0)
peer->dialog_token = 1;
p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
peer->dialog_token);
len = p2p_buf_add_ie_hdr(buf);
if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO)
p2p_buf_add_config_timeout(buf, 0, 0);
else
p2p_buf_add_config_timeout(buf, 100, 20);
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class, p2p->op_channel);
if (p2p->inv_bssid_set)
p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
if (go_dev_addr)
dev_addr = go_dev_addr;
else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
dev_addr = peer->p2p_device_addr;
else
dev_addr = p2p->cfg->dev_addr;
p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
P2P_INVITATION_FLAGS_TYPE : 0);
p2p_buf_update_ie_hdr(buf, len);
return buf;
}
static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
struct p2p_device *peer,
u8 dialog_token, u8 status,
const u8 *group_bssid,
u8 reg_class, u8 channel,
struct p2p_channels *channels)
{
struct wpabuf *buf;
u8 *len;
buf = wpabuf_alloc(1000);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_status(buf, status);
p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
if (reg_class && channel)
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
reg_class, channel);
if (group_bssid)
p2p_buf_add_group_bssid(buf, group_bssid);
if (channels)
p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
p2p_buf_update_ie_hdr(buf, len);
return buf;
}
void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_device *dev;
struct p2p_message msg;
struct wpabuf *resp = NULL;
u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
int freq;
int go = 0;
u8 group_bssid[ETH_ALEN], *bssid;
int op_freq = 0;
u8 reg_class = 0, channel = 0;
struct p2p_channels intersection, *channels = NULL;
int persistent;
os_memset(group_bssid, 0, sizeof(group_bssid));
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Invitation Request from " MACSTR " (freq=%d)",
MAC2STR(sa), rx_freq);
if (p2p_parse(data, len, &msg))
return;
dev = p2p_get_device(p2p, sa);
if (dev == NULL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Reject Invitation Request from unknown peer "
MACSTR, MAC2STR(sa));
status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
goto fail;
}
if (!msg.group_id || !msg.channel_list) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Mandatory attribute missing in Invitation "
"Request from " MACSTR, MAC2STR(sa));
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (msg.invitation_flags)
persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
else {
/* Invitation Flags is a mandatory attribute starting from P2P
* spec 1.06. As a backwards compatibility mechanism, assume
* the request was for a persistent group if the attribute is
* missing.
*/
wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
"attribute missing from Invitation Request");
persistent = 1;
}
if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
msg.channel_list, msg.channel_list_len) <
0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: No common channels found");
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
if (p2p->cfg->invitation_process) {
status = p2p->cfg->invitation_process(
p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
&go, group_bssid, &op_freq, persistent);
}
if (op_freq) {
if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
&reg_class, &channel) < 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unknown forced freq %d MHz from "
"invitation_process()", op_freq);
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
&intersection);
if (!p2p_channels_includes(&intersection, reg_class, channel))
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: forced freq %d MHz not in the supported "
"channels interaction", op_freq);
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
if (status == P2P_SC_SUCCESS)
channels = &intersection;
} else {
op_freq = p2p_channel_to_freq(p2p->cfg->country,
p2p->cfg->op_reg_class,
p2p->cfg->op_channel);
if (op_freq < 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unknown operational channel "
"(country=%c%c reg_class=%u channel=%u)",
p2p->cfg->country[0], p2p->cfg->country[1],
p2p->cfg->op_reg_class, p2p->cfg->op_channel);
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
&intersection);
if (status == P2P_SC_SUCCESS) {
reg_class = p2p->cfg->op_reg_class;
channel = p2p->cfg->op_channel;
channels = &intersection;
}
}
fail:
if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
bssid = group_bssid;
else
bssid = NULL;
resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
bssid, reg_class, channel, channels);
if (resp == NULL)
goto out;
if (rx_freq > 0)
freq = rx_freq;
else
freq = p2p_channel_to_freq(p2p->cfg->country,
p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unknown regulatory class/channel");
goto out;
}
/*
* Store copy of invitation data to be used when processing TX status
* callback for the Acton frame.
*/
os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
if (msg.group_bssid) {
os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
} else
p2p->inv_group_bssid_ptr = NULL;
if (msg.group_id_len - ETH_ALEN <= 32) {
os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
msg.group_id_len - ETH_ALEN);
p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
}
os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
p2p->inv_status = status;
p2p->inv_op_freq = op_freq;
p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, sa,
p2p->cfg->dev_addr, p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) <
0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
}
out:
wpabuf_free(resp);
p2p_parse_free(&msg);
}
void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_device *dev;
struct p2p_message msg;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Invitation Response from " MACSTR,
MAC2STR(sa));
dev = p2p_get_device(p2p, sa);
if (dev == NULL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore Invitation Response from unknown peer "
MACSTR, MAC2STR(sa));
return;
}
if (dev != p2p->invite_peer) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore unexpected Invitation Response from peer "
MACSTR, MAC2STR(sa));
return;
}
if (p2p_parse(data, len, &msg))
return;
if (!msg.status) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Mandatory Status attribute missing in "
"Invitation Response from " MACSTR, MAC2STR(sa));
p2p_parse_free(&msg);
return;
}
if (p2p->cfg->invitation_result)
p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
msg.group_bssid);
p2p_parse_free(&msg);
p2p_clear_timeout(p2p);
p2p_set_state(p2p, P2P_IDLE);
p2p->invite_peer = NULL;
}
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
const u8 *go_dev_addr)
{
struct wpabuf *req;
int freq;
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
if (freq <= 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: No Listen/Operating frequency known for the "
"peer " MACSTR " to send Invitation Request",
MAC2STR(dev->p2p_device_addr));
return -1;
}
req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
if (req == NULL)
return -1;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Sending Invitation Request");
p2p_set_state(p2p, P2P_INVITE);
p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
p2p->invite_peer = dev;
dev->invitation_reqs++;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
dev->p2p_device_addr, p2p->cfg->dev_addr,
dev->p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 200) < 0)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
/* Use P2P find to recover and retry */
p2p_set_timeout(p2p, 0, 0);
}
wpabuf_free(req);
return 0;
}
void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invitation Request TX callback: success=%d", success);
if (p2p->invite_peer == NULL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: No pending Invite");
return;
}
/*
* Use P2P find, if needed, to find the other device from its listen
* channel.
*/
p2p_set_state(p2p, P2P_INVITE);
p2p_set_timeout(p2p, 0, 100000);
}
void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invitation Response TX callback: success=%d", success);
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (success && p2p->cfg->invitation_received) {
p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
p2p->inv_sa,
p2p->inv_group_bssid,
p2p->inv_ssid, p2p->inv_ssid_len,
p2p->inv_go_dev_addr,
p2p->inv_status,
p2p->inv_op_freq);
}
}
int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
const u8 *bssid, const u8 *ssid, size_t ssid_len,
unsigned int force_freq, const u8 *go_dev_addr,
int persistent_group)
{
struct p2p_device *dev;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Request to invite peer " MACSTR " role=%d",
MAC2STR(peer), role);
if (bssid)
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
if (go_dev_addr) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invitation for GO Device Address " MACSTR,
MAC2STR(go_dev_addr));
os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
} else
p2p->invite_go_dev_addr = NULL;
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID",
ssid, ssid_len);
dev = p2p_get_device(p2p, peer);
if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Cannot invite unknown P2P Device " MACSTR,
MAC2STR(peer));
return -1;
}
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
if (!(dev->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Cannot invite a P2P Device " MACSTR
" that is in a group and is not discoverable",
MAC2STR(peer));
}
/* TODO: use device discoverability request through GO */
}
dev->invitation_reqs = 0;
if (force_freq) {
if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
&p2p->op_reg_class, &p2p->op_channel) <
0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported frequency %u MHz",
force_freq);
return -1;
}
p2p->channels.reg_classes = 1;
p2p->channels.reg_class[0].channels = 1;
p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
} else {
p2p->op_reg_class = p2p->cfg->op_reg_class;
p2p->op_channel = p2p->cfg->op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
sizeof(struct p2p_channels));
}
if (p2p->state != P2P_IDLE)
p2p_stop_find(p2p);
p2p->inv_role = role;
p2p->inv_bssid_set = bssid != NULL;
if (bssid)
os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
os_memcpy(p2p->inv_ssid, ssid, ssid_len);
p2p->inv_ssid_len = ssid_len;
p2p->inv_persistent = persistent_group;
return p2p_invite_send(p2p, dev, go_dev_addr);
}

682
src/p2p/p2p_parse.c Normal file
View File

@ -0,0 +1,682 @@
/*
* P2P - IE parser
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
struct p2p_message *msg)
{
const u8 *pos;
size_t i, nlen;
char devtype[WPS_DEV_TYPE_BUFSIZE];
switch (id) {
case P2P_ATTR_CAPABILITY:
if (len < 2) {
wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
"attribute (length %d)", len);
return -1;
}
msg->capability = data;
wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
"Group Capability %02x",
data[0], data[1]);
break;
case P2P_ATTR_DEVICE_ID:
if (len < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
"attribute (length %d)", len);
return -1;
}
msg->device_id = data;
wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
MAC2STR(msg->device_id));
break;
case P2P_ATTR_GROUP_OWNER_INTENT:
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
"attribute (length %d)", len);
return -1;
}
msg->go_intent = data;
wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
"Tie breaker %u", data[0] >> 1, data[0] & 0x01);
break;
case P2P_ATTR_STATUS:
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short Status "
"attribute (length %d)", len);
return -1;
}
msg->status = data;
wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
break;
case P2P_ATTR_LISTEN_CHANNEL:
if (len == 0) {
wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
"null channel");
break;
}
if (len < 5) {
wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
"attribute (length %d)", len);
return -1;
}
msg->listen_channel = data;
wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
"Country %c%c(0x%02x) Regulatory "
"Class %d Channel Number %d", data[0], data[1],
data[2], data[3], data[4]);
break;
case P2P_ATTR_OPERATING_CHANNEL:
if (len == 0) {
wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
"Ignore null channel");
break;
}
if (len < 5) {
wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
"Channel attribute (length %d)", len);
return -1;
}
msg->operating_channel = data;
wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
"Country %c%c(0x%02x) Regulatory "
"Class %d Channel Number %d", data[0], data[1],
data[2], data[3], data[4]);
break;
case P2P_ATTR_CHANNEL_LIST:
if (len < 3) {
wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
"attribute (length %d)", len);
return -1;
}
msg->channel_list = data;
msg->channel_list_len = len;
wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
"'%c%c(0x%02x)'", data[0], data[1], data[2]);
wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
msg->channel_list, msg->channel_list_len);
break;
case P2P_ATTR_GROUP_INFO:
msg->group_info = data;
msg->group_info_len = len;
wpa_printf(MSG_DEBUG, "P2P: * Group Info");
break;
case P2P_ATTR_DEVICE_INFO:
if (len < ETH_ALEN + 2 + 8 + 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
"attribute (length %d)", len);
return -1;
}
msg->p2p_device_info = data;
msg->p2p_device_info_len = len;
pos = data;
msg->p2p_device_addr = pos;
pos += ETH_ALEN;
msg->config_methods = WPA_GET_BE16(pos);
pos += 2;
msg->pri_dev_type = pos;
pos += 8;
msg->num_sec_dev_types = *pos++;
if (msg->num_sec_dev_types * 8 > data + len - pos) {
wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
return -1;
}
pos += msg->num_sec_dev_types * 8;
if (data + len - pos < 4) {
wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
"length %d", (int) (data + len - pos));
return -1;
}
if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
"header", pos, 4);
return -1;
}
pos += 2;
nlen = WPA_GET_BE16(pos);
pos += 2;
if (data + len - pos < (int) nlen || nlen > 32) {
wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
"length %d (buf len %d)", (int) nlen,
(int) (data + len - pos));
return -1;
}
os_memcpy(msg->device_name, pos, nlen);
for (i = 0; i < nlen; i++) {
if (msg->device_name[i] == '\0')
break;
if (msg->device_name[i] < 32)
msg->device_name[i] = '_';
}
wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
" primary device type %s device name '%s' "
"config methods 0x%x",
MAC2STR(msg->p2p_device_addr),
wps_dev_type_bin2str(msg->pri_dev_type, devtype,
sizeof(devtype)),
msg->device_name, msg->config_methods);
break;
case P2P_ATTR_CONFIGURATION_TIMEOUT:
if (len < 2) {
wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
"Timeout attribute (length %d)", len);
return -1;
}
msg->config_timeout = data;
wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
break;
case P2P_ATTR_INTENDED_INTERFACE_ADDR:
if (len < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
"Interface Address attribute (length %d)",
len);
return -1;
}
msg->intended_addr = data;
wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
MACSTR, MAC2STR(msg->intended_addr));
break;
case P2P_ATTR_GROUP_BSSID:
if (len < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
"attribute (length %d)", len);
return -1;
}
msg->group_bssid = data;
wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
MAC2STR(msg->group_bssid));
break;
case P2P_ATTR_GROUP_ID:
if (len < ETH_ALEN || len > ETH_ALEN + 32) {
wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
"attribute length %d", len);
return -1;
}
msg->group_id = data;
msg->group_id_len = len;
wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
MACSTR, MAC2STR(msg->group_id));
wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
msg->group_id + ETH_ALEN,
msg->group_id_len - ETH_ALEN);
break;
case P2P_ATTR_INVITATION_FLAGS:
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
"Flag attribute (length %d)", len);
return -1;
}
msg->invitation_flags = data;
wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
data[0]);
break;
case P2P_ATTR_MANAGEABILITY:
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
"attribute (length %d)", len);
return -1;
}
msg->manageability = data;
wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
data[0]);
break;
case P2P_ATTR_NOTICE_OF_ABSENCE:
if (len < 2) {
wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
"Absence attribute (length %d)", len);
return -1;
}
msg->noa = data;
msg->noa_len = len;
wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
break;
case P2P_ATTR_EXT_LISTEN_TIMING:
if (len < 4) {
wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
"Timing attribute (length %d)", len);
return -1;
}
msg->ext_listen_timing = data;
wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
"(period %u msec interval %u msec)",
WPA_GET_LE16(msg->ext_listen_timing),
WPA_GET_LE16(msg->ext_listen_timing + 2));
break;
case P2P_ATTR_MINOR_REASON_CODE:
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
"Code attribute (length %d)", len);
return -1;
}
msg->minor_reason_code = data;
wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
*msg->minor_reason_code);
break;
default:
wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
"(length %d)", id, len);
break;
}
return 0;
}
/**
* p2p_parse_p2p_ie - Parse P2P IE
* @buf: Concatenated P2P IE(s) payload
* @msg: Buffer for returning parsed attributes
* Returns: 0 on success, -1 on failure
*
* Note: Caller is responsible for clearing the msg data structure before
* calling this function.
*/
int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
{
const u8 *pos = wpabuf_head_u8(buf);
const u8 *end = pos + wpabuf_len(buf);
wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
while (pos < end) {
u16 attr_len;
if (pos + 2 >= end) {
wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
return -1;
}
attr_len = WPA_GET_LE16(pos + 1);
wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
pos[0], attr_len);
if (pos + 3 + attr_len > end) {
wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
"(len=%u left=%d)",
attr_len, (int) (end - pos - 3));
wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
return -1;
}
if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
return -1;
pos += 3 + attr_len;
}
return 0;
}
static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
{
struct wps_parse_attr attr;
wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
if (wps_parse_msg(buf, &attr))
return -1;
if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
!msg->device_name[0])
os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
if (attr.config_methods) {
msg->wps_config_methods =
WPA_GET_BE16(attr.config_methods);
wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
msg->wps_config_methods);
}
if (attr.dev_password_id) {
msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
msg->dev_password_id);
}
if (attr.primary_dev_type) {
char devtype[WPS_DEV_TYPE_BUFSIZE];
msg->wps_pri_dev_type = attr.primary_dev_type;
wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
sizeof(devtype)));
}
return 0;
}
/**
* p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
* @data: IEs from the message
* @len: Length of data buffer in octets
* @msg: Buffer for returning parsed attributes
* Returns: 0 on success, -1 on failure
*
* Note: Caller is responsible for clearing the msg data structure before
* calling this function.
*
* Note: Caller must free temporary memory allocations by calling
* p2p_parse_free() when the parsed data is not needed anymore.
*/
int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
{
struct ieee802_11_elems elems;
ieee802_11_parse_elems(data, len, &elems, 0);
if (elems.ds_params && elems.ds_params_len >= 1)
msg->ds_params = elems.ds_params;
if (elems.ssid)
msg->ssid = elems.ssid - 2;
msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
WPS_DEV_OUI_WFA);
if (msg->wps_attributes &&
p2p_parse_wps_ie(msg->wps_attributes, msg)) {
p2p_parse_free(msg);
return -1;
}
msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
P2P_IE_VENDOR_TYPE);
if (msg->p2p_attributes &&
p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
if (msg->p2p_attributes)
wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
msg->p2p_attributes);
p2p_parse_free(msg);
return -1;
}
return 0;
}
/**
* p2p_parse - Parse a P2P Action frame contents
* @data: Action frame payload after Category and Code fields
* @len: Length of data buffer in octets
* @msg: Buffer for returning parsed attributes
* Returns: 0 on success, -1 on failure
*
* Note: Caller must free temporary memory allocations by calling
* p2p_parse_free() when the parsed data is not needed anymore.
*/
int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
{
os_memset(msg, 0, sizeof(*msg));
wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
if (len < 1) {
wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
return -1;
}
msg->dialog_token = data[0];
wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
return p2p_parse_ies(data + 1, len - 1, msg);
}
/**
* p2p_parse_free - Free temporary data from P2P parsing
* @msg: Parsed attributes
*/
void p2p_parse_free(struct p2p_message *msg)
{
wpabuf_free(msg->p2p_attributes);
msg->p2p_attributes = NULL;
wpabuf_free(msg->wps_attributes);
msg->wps_attributes = NULL;
}
int p2p_group_info_parse(const u8 *gi, size_t gi_len,
struct p2p_group_info *info)
{
const u8 *g, *gend;
os_memset(info, 0, sizeof(*info));
if (gi == NULL)
return 0;
g = gi;
gend = gi + gi_len;
while (g < gend) {
struct p2p_client_info *cli;
const u8 *t, *cend;
int count;
cli = &info->client[info->num_clients];
cend = g + 1 + g[0];
if (cend > gend)
return -1; /* invalid data */
/* g at start of P2P Client Info Descriptor */
/* t at Device Capability Bitmap */
t = g + 1 + 2 * ETH_ALEN;
if (t > cend)
return -1; /* invalid data */
cli->p2p_device_addr = g + 1;
cli->p2p_interface_addr = g + 1 + ETH_ALEN;
cli->dev_capab = t[0];
if (t + 1 + 2 + 8 + 1 > cend)
return -1; /* invalid data */
cli->config_methods = WPA_GET_BE16(&t[1]);
cli->pri_dev_type = &t[3];
t += 1 + 2 + 8;
/* t at Number of Secondary Device Types */
cli->num_sec_dev_types = *t++;
if (t + 8 * cli->num_sec_dev_types > cend)
return -1; /* invalid data */
cli->sec_dev_types = t;
t += 8 * cli->num_sec_dev_types;
/* t at Device Name in WPS TLV format */
if (t + 2 + 2 > cend)
return -1; /* invalid data */
if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
return -1; /* invalid Device Name TLV */
t += 2;
count = WPA_GET_BE16(t);
t += 2;
if (count > cend - t)
return -1; /* invalid Device Name TLV */
if (count >= 32)
count = 32;
cli->dev_name = (const char *) t;
cli->dev_name_len = count;
g = cend;
info->num_clients++;
if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
return -1;
}
return 0;
}
static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
char *end)
{
char *pos = buf;
int ret;
struct p2p_group_info info;
unsigned int i;
if (p2p_group_info_parse(gi, gi_len, &info) < 0)
return 0;
for (i = 0; i < info.num_clients; i++) {
struct p2p_client_info *cli;
char name[33];
char devtype[WPS_DEV_TYPE_BUFSIZE];
u8 s;
int count;
cli = &info.client[i];
ret = os_snprintf(pos, end - pos, "p2p_group_client: "
"dev=" MACSTR " iface=" MACSTR,
MAC2STR(cli->p2p_device_addr),
MAC2STR(cli->p2p_interface_addr));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
ret = os_snprintf(pos, end - pos,
" dev_capab=0x%x config_methods=0x%x "
"dev_type=%s",
cli->dev_capab, cli->config_methods,
wps_dev_type_bin2str(cli->pri_dev_type,
devtype,
sizeof(devtype)));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
for (s = 0; s < cli->num_sec_dev_types; s++) {
ret = os_snprintf(pos, end - pos, " dev_type=%s",
wps_dev_type_bin2str(
&cli->sec_dev_types[s * 8],
devtype, sizeof(devtype)));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
os_memcpy(name, cli->dev_name, cli->dev_name_len);
name[cli->dev_name_len] = '\0';
count = (int) cli->dev_name_len - 1;
while (count >= 0) {
if (name[count] < 32)
name[count] = '_';
count--;
}
ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
return pos - buf;
}
/**
* p2p_attr_text - Build text format description of P2P IE attributes
* @data: P2P IE contents
* @buf: Buffer for returning text
* @end: Pointer to the end of the buf area
* Returns: Number of octets written to the buffer or -1 on faikure
*
* This function can be used to parse P2P IE contents into text format
* field=value lines.
*/
int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
{
struct p2p_message msg;
char *pos = buf;
int ret;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(data, &msg))
return -1;
if (msg.capability) {
ret = os_snprintf(pos, end - pos,
"p2p_dev_capab=0x%x\n"
"p2p_group_capab=0x%x\n",
msg.capability[0], msg.capability[1]);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (msg.pri_dev_type) {
char devtype[WPS_DEV_TYPE_BUFSIZE];
ret = os_snprintf(pos, end - pos,
"p2p_primary_device_type=%s\n",
wps_dev_type_bin2str(msg.pri_dev_type,
devtype,
sizeof(devtype)));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
msg.device_name);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
if (msg.p2p_device_addr) {
ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
"\n",
MAC2STR(msg.p2p_device_addr));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
msg.config_methods);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
pos, end);
if (ret < 0)
return pos - buf;
pos += ret;
return pos - buf;
}
u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
{
struct p2p_message msg;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p_ie, &msg))
return 0;
if (!msg.capability)
return 0;
return msg.capability[1];
}
const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
{
struct p2p_message msg;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p_ie, &msg))
return NULL;
if (msg.p2p_device_addr)
return msg.p2p_device_addr;
if (msg.device_id)
return msg.device_id;
return NULL;
}

340
src/p2p/p2p_pd.c Normal file
View File

@ -0,0 +1,340 @@
/*
* Wi-Fi Direct - P2P provision discovery
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "wps/wps_defs.h"
#include "p2p_i.h"
#include "p2p.h"
static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
u16 config_methods)
{
u8 *len;
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(buf, 1);
wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
/* Config Methods */
wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
wpabuf_put_be16(buf, 2);
wpabuf_put_be16(buf, config_methods);
p2p_buf_update_ie_hdr(buf, len);
}
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
u8 dialog_token,
u16 config_methods,
struct p2p_device *go)
{
struct wpabuf *buf;
u8 *len;
buf = wpabuf_alloc(1000);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_capability(buf, p2p->dev_capab, 0);
p2p_buf_add_device_info(buf, p2p, NULL);
if (go) {
p2p_buf_add_group_id(buf, go->p2p_device_addr, go->oper_ssid,
go->oper_ssid_len);
}
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
return buf;
}
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
u8 dialog_token,
u16 config_methods)
{
struct wpabuf *buf;
buf = wpabuf_alloc(100);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
return buf;
}
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_message msg;
struct p2p_device *dev;
int freq;
int reject = 1;
struct wpabuf *resp;
if (p2p_parse(data, len, &msg))
return;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Provision Discovery Request from " MACSTR
" with config methods 0x%x (freq=%d)",
MAC2STR(sa), msg.wps_config_methods, rx_freq);
dev = p2p_get_device(p2p, sa);
if (dev == NULL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Provision Discovery Request from "
"unknown peer " MACSTR, MAC2STR(sa));
}
if (!(msg.wps_config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
WPS_CONFIG_PUSHBUTTON))) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
"Config Methods in Provision Discovery Request");
goto out;
}
if (dev)
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
P2P_DEV_PD_PEER_KEYPAD);
if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
" requested us to show a PIN on display", MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
" requested us to write its PIN using keypad",
MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
}
reject = 0;
out:
resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
reject ? 0 : msg.wps_config_methods);
if (resp == NULL) {
p2p_parse_free(&msg);
return;
}
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Sending Provision Discovery Response");
if (rx_freq > 0)
freq = rx_freq;
else
freq = p2p_channel_to_freq(p2p->cfg->country,
p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unknown regulatory class/channel");
wpabuf_free(resp);
p2p_parse_free(&msg);
return;
}
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, sa,
p2p->cfg->dev_addr, p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) <
0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
}
wpabuf_free(resp);
if (!reject && p2p->cfg->prov_disc_req) {
const u8 *dev_addr = sa;
if (msg.p2p_device_addr)
dev_addr = msg.p2p_device_addr;
p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
msg.wps_config_methods,
dev_addr, msg.pri_dev_type,
msg.device_name, msg.config_methods,
msg.capability ? msg.capability[0] : 0,
msg.capability ? msg.capability[1] :
0);
}
p2p_parse_free(&msg);
}
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_message msg;
struct p2p_device *dev;
u16 report_config_methods = 0;
if (p2p_parse(data, len, &msg))
return;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received Provisioning Discovery Response from " MACSTR
" with config methods 0x%x",
MAC2STR(sa), msg.wps_config_methods);
dev = p2p_get_device(p2p, sa);
if (dev == NULL || !dev->req_config_methods) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore Provisioning Discovery Response from "
MACSTR " with no pending request", MAC2STR(sa));
p2p_parse_free(&msg);
return;
}
if (dev->dialog_token != msg.dialog_token) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore Provisioning Discovery Response with "
"unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
p2p_parse_free(&msg);
return;
}
if (msg.wps_config_methods != dev->req_config_methods) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
"our Provisioning Discovery Request");
p2p_parse_free(&msg);
goto out;
}
report_config_methods = dev->req_config_methods;
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
P2P_DEV_PD_PEER_KEYPAD);
if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
" accepted to show a PIN on display", MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
" accepted to write our PIN using keypad",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
}
p2p_parse_free(&msg);
out:
dev->req_config_methods = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (p2p->cfg->prov_disc_resp)
p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
report_config_methods);
}
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join)
{
struct wpabuf *req;
int freq;
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
if (freq <= 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: No Listen/Operating frequency known for the "
"peer " MACSTR " to send Provision Discovery Request",
MAC2STR(dev->p2p_device_addr));
return -1;
}
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
if (!(dev->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Cannot use PD with P2P Device " MACSTR
" that is in a group and is not discoverable",
MAC2STR(dev->p2p_device_addr));
return -1;
}
/* TODO: use device discoverability request through GO */
}
dev->dialog_token++;
if (dev->dialog_token == 0)
dev->dialog_token = 1;
req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
dev->req_config_methods,
join ? dev : NULL);
if (req == NULL)
return -1;
p2p->pending_action_state = P2P_PENDING_PD;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
dev->p2p_device_addr, p2p->cfg->dev_addr,
dev->p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 200) < 0)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
wpabuf_free(req);
return -1;
}
wpabuf_free(req);
return 0;
}
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
u16 config_methods, int join)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL)
dev = p2p_get_device_interface(p2p, peer_addr);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
"Discovery Request destination " MACSTR
" not yet known", MAC2STR(peer_addr));
return -1;
}
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
"Request with " MACSTR " (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
if (config_methods == 0)
return -1;
dev->req_config_methods = config_methods;
if (p2p->go_neg_peer ||
(p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
p2p->state != P2P_LISTEN_ONLY)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
"operations; postpone Provision Discovery Request "
"with " MACSTR " (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
return 0;
}
return p2p_send_prov_disc_req(p2p, dev, join);
}

526
src/p2p/p2p_sd.c Normal file
View File

@ -0,0 +1,526 @@
/*
* Wi-Fi Direct - P2P service discovery
* Copyright (c) 2009, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "p2p_i.h"
#include "p2p.h"
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
struct p2p_device *dev)
{
struct p2p_sd_query *q;
if (!(dev->dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
return 0; /* peer does not support SD */
for (q = p2p->sd_queries; q; q = q->next) {
if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
return q;
if (!q->for_all_peers &&
os_memcmp(q->peer, dev->p2p_device_addr, ETH_ALEN) == 0)
return q;
}
return NULL;
}
static int p2p_unlink_sd_query(struct p2p_data *p2p,
struct p2p_sd_query *query)
{
struct p2p_sd_query *q, *prev;
q = p2p->sd_queries;
prev = NULL;
while (q) {
if (q == query) {
if (prev)
prev->next = q->next;
else
p2p->sd_queries = q->next;
if (p2p->sd_query == query)
p2p->sd_query = NULL;
return 1;
}
prev = q;
q = q->next;
}
return 0;
}
static void p2p_free_sd_query(struct p2p_sd_query *q)
{
if (q == NULL)
return;
wpabuf_free(q->tlvs);
os_free(q);
}
void p2p_free_sd_queries(struct p2p_data *p2p)
{
struct p2p_sd_query *q, *prev;
q = p2p->sd_queries;
p2p->sd_queries = NULL;
while (q) {
prev = q;
q = q->next;
p2p_free_sd_query(prev);
}
}
static struct wpabuf * p2p_build_sd_query(u16 update_indic,
struct wpabuf *tlvs)
{
struct wpabuf *buf;
u8 *len_pos, *len_pos2;
buf = wpabuf_alloc(1000 + wpabuf_len(tlvs));
if (buf == NULL)
return NULL;
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
wpabuf_put_u8(buf, 0); /* Dialog Token */
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
wpabuf_put_u8(buf, 2); /* Length */
wpabuf_put_u8(buf, 0); /* QueryRespLenLimit | PAME-BI */
wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
/* Query Request */
len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
/* NQP Query Request Frame */
wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, P2P_OUI_TYPE);
wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
wpabuf_put_buf(buf, tlvs);
WPA_PUT_LE16(len_pos2, (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
return buf;
}
static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
u16 update_indic,
const struct wpabuf *tlvs)
{
struct wpabuf *buf;
u8 *len_pos, *len_pos2;
buf = wpabuf_alloc(1000 + wpabuf_len(tlvs));
if (buf == NULL)
return NULL;
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
wpabuf_put_u8(buf, dialog_token);
wpabuf_put_le16(buf, status_code);
wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
wpabuf_put_u8(buf, 2); /* Length */
wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */
wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
/* Query Response */
len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
/* NQP Query Response Frame */
wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, P2P_OUI_TYPE);
wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
wpabuf_put_buf(buf, tlvs);
WPA_PUT_LE16(len_pos2, (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
return buf;
}
int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
{
struct wpabuf *req;
int ret = 0;
struct p2p_sd_query *query;
int freq;
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
if (freq <= 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: No Listen/Operating frequency known for the "
"peer " MACSTR " to send SD Request",
MAC2STR(dev->p2p_device_addr));
return -1;
}
query = p2p_pending_sd_req(p2p, dev);
if (query == NULL)
return -1;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Start Service Discovery with " MACSTR,
MAC2STR(dev->p2p_device_addr));
req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
if (req == NULL)
return -1;
p2p->sd_peer = dev;
p2p->sd_query = query;
p2p->pending_action_state = P2P_PENDING_SD;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
dev->p2p_device_addr, p2p->cfg->dev_addr,
dev->p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 5000) < 0)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
ret = -1;
}
wpabuf_free(req);
return ret;
}
void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
const u8 *pos = data;
const u8 *end = data + len;
const u8 *next;
u8 dialog_token;
u16 slen;
int freq;
u16 update_indic;
if (p2p->cfg->sd_request == NULL)
return;
if (rx_freq > 0)
freq = rx_freq;
else
freq = p2p_channel_to_freq(p2p->cfg->country,
p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0)
return;
if (len < 1 + 2)
return;
dialog_token = *pos++;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
"freq %d)",
MAC2STR(sa), dialog_token, rx_freq);
if (*pos != WLAN_EID_ADV_PROTO) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unexpected IE in GAS Initial Request: %u", *pos);
return;
}
pos++;
slen = *pos++;
next = pos + slen;
if (next > end || slen < 2) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invalid IE in GAS Initial Request");
return;
}
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != NATIVE_QUERY_PROTOCOL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported GAS advertisement protocol id %u",
*pos);
return;
}
pos = next;
/* Query Request */
if (pos + 2 > end)
return;
slen = WPA_GET_LE16(pos);
pos += 2;
if (pos + slen > end)
return;
end = pos + slen;
/* NQP Query Request */
if (pos + 4 > end)
return;
if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
return;
}
pos += 2;
slen = WPA_GET_LE16(pos);
pos += 2;
if (pos + slen > end || slen < 3 + 1) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invalid NQP Query Request length");
return;
}
if (WPA_GET_BE24(pos) != OUI_WFA) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
return;
}
pos += 3;
if (*pos != P2P_OUI_TYPE) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP vendor type %u", *pos);
return;
}
pos++;
if (pos + 2 > end)
return;
update_indic = WPA_GET_LE16(pos);
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Service Update Indicator: %u", update_indic);
pos += 2;
p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
update_indic, pos, end - pos);
/* the response will be indicated with a call to p2p_sd_response() */
}
void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
u8 dialog_token, const struct wpabuf *resp_tlvs)
{
struct wpabuf *resp;
resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
p2p->srv_update_indic, resp_tlvs);
if (resp == NULL)
return;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) <
0)
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Failed to send Action frame");
wpabuf_free(resp);
}
void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
const u8 *pos = data;
const u8 *end = data + len;
const u8 *next;
u8 dialog_token;
u16 status_code;
u16 comeback_delay;
u16 slen;
u16 update_indic;
if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
os_memcmp(sa, p2p->sd_peer->p2p_device_addr, ETH_ALEN) != 0) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Ignore unexpected GAS Initial Response from "
MACSTR, MAC2STR(sa));
return;
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_clear_timeout(p2p);
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Received GAS Initial Response from " MACSTR,
MAC2STR(sa));
if (len < 5 + 2)
return;
dialog_token = *pos++;
/* TODO: check dialog_token match */
status_code = WPA_GET_LE16(pos);
pos += 2;
comeback_delay = WPA_GET_LE16(pos);
pos += 2;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: dialog_token=%u status_code=%u comeback_delay=%u",
dialog_token, status_code, comeback_delay);
if (*pos != WLAN_EID_ADV_PROTO) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unexpected IE in GAS Initial Response: %u",
*pos);
return;
}
pos++;
slen = *pos++;
next = pos + slen;
if (next > end || slen < 2) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invalid IE in GAS Initial Response");
return;
}
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != NATIVE_QUERY_PROTOCOL) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported GAS advertisement protocol id %u",
*pos);
return;
}
pos = next;
/* Query Response */
if (pos + 2 > end)
return;
slen = WPA_GET_LE16(pos);
pos += 2;
if (pos + slen > end)
return;
end = pos + slen;
/* NQP Query Response */
if (pos + 4 > end)
return;
if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
return;
}
pos += 2;
slen = WPA_GET_LE16(pos);
pos += 2;
if (pos + slen > end || slen < 3 + 1) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invalid NQP Query Response length");
return;
}
if (WPA_GET_BE24(pos) != OUI_WFA) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
return;
}
pos += 3;
if (*pos != P2P_OUI_TYPE) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Unsupported NQP vendor type %u", *pos);
return;
}
pos++;
if (pos + 2 > end)
return;
update_indic = WPA_GET_LE16(pos);
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Service Update Indicator: %u", update_indic);
pos += 2;
p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
p2p->sd_peer = NULL;
if (p2p->sd_query) {
if (!p2p->sd_query->for_all_peers) {
struct p2p_sd_query *q;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Remove completed SD query %p",
p2p->sd_query);
q = p2p->sd_query;
p2p_unlink_sd_query(p2p, p2p->sd_query);
p2p_free_sd_query(q);
}
p2p->sd_query = NULL;
}
if (p2p->cfg->sd_response)
p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
pos, end - pos);
p2p_continue_find(p2p);
}
void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
const struct wpabuf *tlvs)
{
struct p2p_sd_query *q;
q = os_zalloc(sizeof(*q));
if (q == NULL)
return NULL;
if (dst)
os_memcpy(q->peer, dst, ETH_ALEN);
else
q->for_all_peers = 1;
q->tlvs = wpabuf_dup(tlvs);
if (q->tlvs == NULL) {
p2p_free_sd_query(q);
return NULL;
}
q->next = p2p->sd_queries;
p2p->sd_queries = q;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
return q;
}
void p2p_sd_service_update(struct p2p_data *p2p)
{
p2p->srv_update_indic++;
}
int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
{
if (p2p_unlink_sd_query(p2p, req)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Cancel pending SD query %p", req);
p2p_free_sd_query(req);
return 0;
}
return -1;
}

260
src/p2p/p2p_utils.c Normal file
View File

@ -0,0 +1,260 @@
/*
* P2P - generic helper functions
* Copyright (c) 2009, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "p2p_i.h"
/**
* p2p_random - Generate random string for SSID and passphrase
* @buf: Buffer for returning the result
* @len: Number of octets to write to the buffer
* Returns: 0 on success, -1 on failure
*
* This function generates a random string using the following character set:
* 'A'-'Z', 'a'-'z', '0'-'9'.
*/
int p2p_random(char *buf, size_t len)
{
u8 val;
size_t i;
u8 letters = 'Z' - 'A' + 1;
u8 numbers = 10;
if (os_get_random((unsigned char *) buf, len))
return -1;
/* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
for (i = 0; i < len; i++) {
val = buf[i];
val %= 2 * letters + numbers;
if (val < letters)
buf[i] = 'A' + val;
else if (val < 2 * letters)
buf[i] = 'a' + (val - letters);
else
buf[i] = '0' + (val - 2 * letters);
}
return 0;
}
static int p2p_channel_to_freq_j4(int reg_class, int channel)
{
/* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
/* TODO: more regulatory classes */
switch (reg_class) {
case 81:
/* channels 1..13 */
if (channel < 1 || channel > 13)
return -1;
return 2407 + 5 * channel;
case 82:
/* channel 14 */
if (channel != 14)
return -1;
return 2414 + 5 * channel;
case 83: /* channels 1..9; 40 MHz */
case 84: /* channels 5..13; 40 MHz */
if (channel < 1 || channel > 13)
return -1;
return 2407 + 5 * channel;
case 115: /* channels 36,40,44,48; indoor only */
case 118: /* channels 52,56,60,64; dfs */
if (channel < 36 || channel > 64)
return -1;
return 5000 + 5 * channel;
case 124: /* channels 149,153,157,161 */
case 125: /* channels 149,153,157,161,165,169 */
if (channel < 149 || channel > 161)
return -1;
return 5000 + 5 * channel;
case 116: /* channels 36,44; 40 MHz; indoor only */
case 117: /* channels 40,48; 40 MHz; indoor only */
case 119: /* channels 52,60; 40 MHz; dfs */
case 120: /* channels 56,64; 40 MHz; dfs */
if (channel < 36 || channel > 64)
return -1;
return 5000 + 5 * channel;
case 126: /* channels 149,157; 40 MHz */
case 127: /* channels 153,161; 40 MHz */
if (channel < 149 || channel > 161)
return -1;
return 5000 + 5 * channel;
}
return -1;
}
/**
* p2p_channel_to_freq - Convert channel info to frequency
* @country: Country code
* @reg_class: Regulatory class
* @channel: Channel number
* Returns: Frequency in MHz or -1 if the specified channel is unknown
*/
int p2p_channel_to_freq(const char *country, int reg_class, int channel)
{
if (country[2] == 0x04)
return p2p_channel_to_freq_j4(reg_class, channel);
/* These are mainly for backwards compatibility; to be removed */
switch (reg_class) {
case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
if (channel < 36 || channel > 48)
return -1;
return 5000 + 5 * channel;
case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
if (channel < 149 || channel > 161)
return -1;
return 5000 + 5 * channel;
case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
case 12: /* US/12 = 2.407 GHz, channels 1..11 */
case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
if (channel < 1 || channel > 13)
return -1;
return 2407 + 5 * channel;
case 31: /* JP/31 = 2.414 GHz, channel 14 */
if (channel != 14)
return -1;
return 2414 + 5 * channel;
}
return -1;
}
/**
* p2p_freq_to_channel - Convert frequency into channel info
* @country: Country code
* @reg_class: Buffer for returning regulatory class
* @channel: Buffer for returning channel number
* Returns: 0 on success, -1 if the specified frequency is unknown
*/
int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
u8 *channel)
{
/* TODO: more operating classes */
if (freq >= 2412 && freq <= 2472) {
*reg_class = 81; /* 2.407 GHz, channels 1..13 */
*channel = (freq - 2407) / 5;
return 0;
}
if (freq == 2484) {
*reg_class = 82; /* channel 14 */
*channel = 14;
return 0;
}
if (freq >= 5180 && freq <= 5240) {
*reg_class = 115; /* 5 GHz, channels 36..48 */
*channel = (freq - 5000) / 5;
return 0;
}
if (freq >= 5745 && freq <= 5805) {
*reg_class = 124; /* 5 GHz, channels 149..161 */
*channel = (freq - 5000) / 5;
return 0;
}
return -1;
}
static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
const struct p2p_reg_class *b,
struct p2p_reg_class *res)
{
size_t i, j;
res->reg_class = a->reg_class;
for (i = 0; i < a->channels; i++) {
for (j = 0; j < b->channels; j++) {
if (a->channel[i] != b->channel[j])
continue;
res->channel[res->channels] = a->channel[i];
res->channels++;
if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
return;
}
}
}
/**
* p2p_channels_intersect - Intersection of supported channel lists
* @a: First set of supported channels
* @b: Second set of supported channels
* @res: Data structure for returning the intersection of support channels
*
* This function can be used to find a common set of supported channels. Both
* input channels sets are assumed to use the same country code. If different
* country codes are used, the regulatory class numbers may not be matched
* correctly and results are undefined.
*/
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res)
{
size_t i, j;
os_memset(res, 0, sizeof(*res));
for (i = 0; i < a->reg_classes; i++) {
const struct p2p_reg_class *a_reg = &a->reg_class[i];
for (j = 0; j < b->reg_classes; j++) {
const struct p2p_reg_class *b_reg = &b->reg_class[j];
if (a_reg->reg_class != b_reg->reg_class)
continue;
p2p_reg_class_intersect(
a_reg, b_reg,
&res->reg_class[res->reg_classes]);
if (res->reg_class[res->reg_classes].channels) {
res->reg_classes++;
if (res->reg_classes == P2P_MAX_REG_CLASSES)
return;
}
}
}
}
/**
* p2p_channels_includes - Check whether a channel is included in the list
* @channels: List of supported channels
* @reg_class: Regulatory class of the channel to search
* @channel: Channel number of the channel to search
* Returns: 1 if channel was found or 0 if not
*/
int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
u8 channel)
{
size_t i, j;
for (i = 0; i < channels->reg_classes; i++) {
const struct p2p_reg_class *reg = &channels->reg_class[i];
if (reg->reg_class != reg_class)
continue;
for (j = 0; j < reg->channels; j++) {
if (reg->channel[j] == channel)
return 1;
}
}
return 0;
}

View File

@ -117,6 +117,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
WPA_PUT_LE16(pos, data);
}
static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
{
u8 *pos = wpabuf_put(buf, 4);
WPA_PUT_LE32(pos, data);
}
static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
{
u8 *pos = wpabuf_put(buf, 2);

View File

@ -166,6 +166,26 @@ CFLAGS += -DCONFIG_IBSS_RSN
OBJS += ibss_rsn.o
endif
ifdef CONFIG_P2P
OBJS += p2p_supplicant.o
OBJS += ../src/p2p/p2p.o
OBJS += ../src/p2p/p2p_utils.o
OBJS += ../src/p2p/p2p_parse.o
OBJS += ../src/p2p/p2p_build.o
OBJS += ../src/p2p/p2p_go_neg.o
OBJS += ../src/p2p/p2p_sd.o
OBJS += ../src/p2p/p2p_pd.o
OBJS += ../src/p2p/p2p_invitation.o
OBJS += ../src/p2p/p2p_dev_disc.o
OBJS += ../src/p2p/p2p_group.o
OBJS += ../src/ap/p2p_hostapd.o
CFLAGS += -DCONFIG_P2P
NEED_80211_COMMON=y
ifdef CONFIG_P2P_STRICT
CFLAGS += -DCONFIG_P2P_STRICT
endif
endif
ifdef CONFIG_NO_WPA2
CFLAGS += -DCONFIG_NO_WPA2
endif

View File

@ -22,6 +22,7 @@
#ifdef NEED_AP_MLME
#include "ap/ieee802_11.h"
#endif /* NEED_AP_MLME */
#include "ap/beacon.h"
#include "ap/ieee802_1x.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
@ -471,6 +472,23 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
#endif /* CONFIG_CTRL_IFACE */
int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct hostapd_data *hapd;
if (ssid == NULL || wpa_s->ap_iface == NULL)
return -1;
ieee802_11_set_beacons(iface);
hapd = iface->bss[0];
hapd->drv.set_ap_wps_ie(hapd);
return 0;
}
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr)
{

View File

@ -37,6 +37,7 @@ void ap_tx_status(void *ctx, const u8 *addr,
void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
/*
* wpa_supplicant - P2P
* Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef P2P_SUPPLICANT_H
#define P2P_SUPPLICANT_H
enum p2p_wps_method;
int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
void wpas_p2p_deinit_global(struct wpa_global *global);
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int join, int auth, int go_intent,
int freq);
void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration);
void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq);
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
int freq);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
int freq);
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
int persistent_group,
int group_formation);
void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
int registrar);
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method);
void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
const u8 *data, size_t data_len, int ack);
int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
char *end);
enum p2p_discovery_type;
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type);
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
u8 *buf, size_t len, int p2p_group);
int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
const u8 *ie, size_t ie_len);
void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
const u8 *sa, const u8 *bssid,
u8 category, const u8 *data, size_t len, int freq);
void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
void wpas_dev_found(void *ctx, const u8 *addr, const u8 *dev_addr,
const u8 *pri_dev_type, const char *dev_name,
u16 config_methods, u8 dev_capab, u8 group_capab);
void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
void wpas_go_neg_req_rx(void *ctx, const u8 *src);
void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
const u8 *dev_addr, const u8 *pri_dev_type,
const char *dev_name, u16 supp_config_methods,
u8 dev_capab, u8 group_capab);
void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
u16 update_indic, const u8 *tlvs, size_t tlvs_len);
void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
const u8 *tlvs, size_t tlvs_len);
void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs);
void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
u8 version, const char *query);
int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req);
void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
const u8 *dst, u8 dialog_token,
const struct wpabuf *resp_tlvs);
void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *query, struct wpabuf *resp);
int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
const struct wpabuf *query);
int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service);
int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service);
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr);
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr);
void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
u32 interval1, u32 duration2, u32 interval2);
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
unsigned int interval);
void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len);
void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len);
void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
#endif /* P2P_SUPPLICANT_H */

View File

@ -42,6 +42,7 @@
#include "ibss_rsn.h"
#include "sme.h"
#include "ap.h"
#include "p2p_supplicant.h"
#include "notify.h"
#include "bgscan.h"
#include "bss.h"
@ -427,6 +428,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_AP
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
wpas_p2p_deinit(wpa_s);
#endif /* CONFIG_P2P */
}
@ -545,6 +550,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
wpa_s->reassociated_connection = 1;
wpa_drv_set_operstate(wpa_s, 1);
wpa_s->after_wps = 0;
#ifdef CONFIG_P2P
wpas_p2p_completed(wpa_s);
#endif /* CONFIG_P2P */
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
@ -586,7 +594,7 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx)
}
static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
{
enum wpa_states old_state = wpa_s->wpa_state;
@ -1884,6 +1892,7 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
return NULL;
wpa_s->scan_req = 1;
wpa_s->new_connection = 1;
wpa_s->parent = wpa_s;
return wpa_s;
}
@ -2092,6 +2101,13 @@ next_driver:
}
#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_P2P
if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
wpa_printf(MSG_ERROR, "Failed to init P2P");
return -1;
}
#endif /* CONFIG_P2P */
if (wpa_bss_init(wpa_s) < 0)
return -1;
@ -2220,6 +2236,8 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
if (global->p2p_group_formation == wpa_s)
global->p2p_group_formation = NULL;
wpa_supplicant_deinit_iface(wpa_s, 1);
os_free(wpa_s);
@ -2279,6 +2297,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
dl_list_init(&global->p2p_srv_bonjour);
dl_list_init(&global->p2p_srv_upnp);
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
@ -2392,6 +2412,10 @@ void wpa_supplicant_deinit(struct wpa_global *global)
if (global == NULL)
return;
#ifdef CONFIG_P2P
wpas_p2p_deinit_global(global);
#endif /* CONFIG_P2P */
while (global->ifaces)
wpa_supplicant_remove_iface(global, global->ifaces);
@ -2434,5 +2458,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
wpas_p2p_update_config(wpa_s);
#endif /* CONFIG_P2P */
wpa_s->conf->changed_parameters = 0;
}

View File

@ -182,6 +182,18 @@ struct wpa_params {
char *override_ctrl_interface;
};
struct p2p_srv_bonjour {
struct dl_list list;
struct wpabuf *query;
struct wpabuf *resp;
};
struct p2p_srv_upnp {
struct dl_list list;
u8 version;
char *service;
};
/**
* struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
*
@ -196,6 +208,12 @@ struct wpa_global {
void **drv_priv;
size_t drv_count;
struct os_time suspend_time;
struct p2p_data *p2p;
struct wpa_supplicant *p2p_group_formation;
u8 p2p_dev_addr[ETH_ALEN];
struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
int p2p_disabled;
};
@ -303,6 +321,7 @@ struct wpa_client_mlme {
*/
struct wpa_supplicant {
struct wpa_global *global;
struct wpa_supplicant *parent;
struct wpa_supplicant *next;
struct l2_packet_data *l2;
struct l2_packet_data *l2_br;
@ -409,7 +428,7 @@ struct wpa_supplicant {
u8 ssid[32];
size_t ssid_len;
int freq;
u8 assoc_req_ie[80];
u8 assoc_req_ie[200];
size_t assoc_req_ie_len;
int mfp;
int ft_used;
@ -429,6 +448,42 @@ struct wpa_supplicant {
void *ap_configured_cb_data;
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
struct p2p_go_neg_results *go_params;
int create_p2p_iface;
u8 pending_interface_addr[ETH_ALEN];
char pending_interface_name[IFNAMSIZ];
int pending_interface_type;
int p2p_group_idx;
unsigned int off_channel_freq;
struct wpabuf *pending_action_tx;
u8 pending_action_src[ETH_ALEN];
u8 pending_action_dst[ETH_ALEN];
u8 pending_action_bssid[ETH_ALEN];
unsigned int pending_action_freq;
int pending_action_without_roc;
unsigned int pending_listen_freq;
unsigned int pending_listen_duration;
enum {
NOT_P2P_GROUP_INTERFACE,
P2P_GROUP_INTERFACE_PENDING,
P2P_GROUP_INTERFACE_GO,
P2P_GROUP_INTERFACE_CLIENT
} p2p_group_interface;
struct p2p_group *p2p_group;
int p2p_long_listen;
char p2p_pin[10];
int p2p_sd_over_ctrl_iface;
int p2p_in_provisioning;
int pending_invite_ssid_id;
int show_group_started;
u8 go_dev_addr[ETH_ALEN];
int pending_pd_before_join;
u8 pending_join_iface_addr[ETH_ALEN];
u8 pending_join_dev_addr[ETH_ALEN];
int pending_join_wps_method;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;
const struct bgscan_ops *bgscan;
void *bgscan_priv;
@ -501,6 +556,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
enum wpa_cipher cipher_suite2driver(int cipher);
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
/* events.c */
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);

View File

@ -32,6 +32,7 @@
#include "blacklist.h"
#include "bss.h"
#include "scan.h"
#include "p2p_supplicant.h"
#include "wps_supplicant.h"
@ -399,6 +400,9 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
wpa_s->wps_success = 1;
wpas_notify_wps_event_success(wpa_s);
#ifdef CONFIG_P2P
wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
#endif /* CONFIG_P2P */
}