P2P: Add initial version of P2P Module
This commit is contained in:
parent
c2af2afb3b
commit
b22128efdc
26 changed files with 12899 additions and 3 deletions
|
@ -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
30
src/ap/p2p_hostapd.c
Normal 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
33
src/ap/p2p_hostapd.h
Normal 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 */
|
|
@ -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
9
src/p2p/Makefile
Normal 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
2900
src/p2p/p2p.c
Normal file
File diff suppressed because it is too large
Load diff
1216
src/p2p/p2p.h
Normal file
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
389
src/p2p/p2p_build.c
Normal 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
357
src/p2p/p2p_dev_disc.c
Normal 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
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
628
src/p2p/p2p_group.c
Normal 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
580
src/p2p/p2p_i.h
Normal 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
474
src/p2p/p2p_invitation.c
Normal 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,
|
||||
®_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
682
src/p2p/p2p_parse.c
Normal 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
340
src/p2p/p2p_pd.c
Normal 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
526
src/p2p/p2p_sd.c
Normal 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
260
src/p2p/p2p_utils.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
3139
wpa_supplicant/p2p_supplicant.c
Normal file
3139
wpa_supplicant/p2p_supplicant.c
Normal file
File diff suppressed because it is too large
Load diff
110
wpa_supplicant/p2p_supplicant.h
Normal file
110
wpa_supplicant/p2p_supplicant.h
Normal 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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue