aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2010-07-18 21:30:25 (GMT)
committerJouni Malinen <j@w1.fi>2010-09-09 14:17:17 (GMT)
commitb22128efdc01099b3ad9bb62d3dc168316691b2a (patch)
tree527b7ce9a0785b255f79c1e6e25587218bc4692e
parentc2af2afb3bbafb7e20eff4b67a4b9818c60fe51e (diff)
downloadhostap-b22128efdc01099b3ad9bb62d3dc168316691b2a.zip
hostap-b22128efdc01099b3ad9bb62d3dc168316691b2a.tar.gz
hostap-b22128efdc01099b3ad9bb62d3dc168316691b2a.tar.bz2
P2P: Add initial version of P2P Module
-rw-r--r--src/Makefile2
-rw-r--r--src/ap/p2p_hostapd.c30
-rw-r--r--src/ap/p2p_hostapd.h33
-rw-r--r--src/common/wpa_ctrl.h26
-rw-r--r--src/p2p/Makefile9
-rw-r--r--src/p2p/p2p.c2900
-rw-r--r--src/p2p/p2p.h1216
-rw-r--r--src/p2p/p2p_build.c389
-rw-r--r--src/p2p/p2p_dev_disc.c357
-rw-r--r--src/p2p/p2p_go_neg.c1064
-rw-r--r--src/p2p/p2p_group.c628
-rw-r--r--src/p2p/p2p_i.h580
-rw-r--r--src/p2p/p2p_invitation.c474
-rw-r--r--src/p2p/p2p_parse.c682
-rw-r--r--src/p2p/p2p_pd.c340
-rw-r--r--src/p2p/p2p_sd.c526
-rw-r--r--src/p2p/p2p_utils.c260
-rw-r--r--src/utils/wpabuf.h6
-rw-r--r--wpa_supplicant/Makefile20
-rw-r--r--wpa_supplicant/ap.c18
-rw-r--r--wpa_supplicant/ap.h1
-rw-r--r--wpa_supplicant/p2p_supplicant.c3139
-rw-r--r--wpa_supplicant/p2p_supplicant.h110
-rw-r--r--wpa_supplicant/wpa_supplicant.c30
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h58
-rw-r--r--wpa_supplicant/wps_supplicant.c4
26 files changed, 12899 insertions, 3 deletions
diff --git a/src/Makefile b/src/Makefile
index f47da7b..d73a175 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..7f99830
--- /dev/null
+++ b/src/ap/p2p_hostapd.c
@@ -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);
+}
diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..31aed15
--- /dev/null
+++ b/src/ap/p2p_hostapd.h
@@ -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 */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 5780484..88412ba 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.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 "
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
new file mode 100644
index 0000000..cffba62
--- /dev/null
+++ b/src/p2p/Makefile
@@ -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.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
new file mode 100644
index 0000000..6fb1a0c
--- /dev/null
+++ b/src/p2p/p2p.c
@@ -0,0 +1,2900 @@
+/*
+ * Wi-Fi Direct - 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq);
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data,
+ size_t len);
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+/**
+ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
+ * entries will be removed
+ */
+#define P2P_PEER_EXPIRATION_AGE 300
+
+#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
+
+static void p2p_expire_peers(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *n;
+ struct os_time now;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+ if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
+ continue;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
+ "entry " MACSTR, MAC2STR(dev->p2p_device_addr));
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+}
+
+
+static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ p2p_expire_peers(p2p);
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+ switch (state) {
+ case P2P_IDLE:
+ return "IDLE";
+ case P2P_SEARCH:
+ return "SEARCH";
+ case P2P_CONNECT:
+ return "CONNECT";
+ case P2P_CONNECT_LISTEN:
+ return "CONNECT_LISTEN";
+ case P2P_GO_NEG:
+ return "GO_NEG";
+ case P2P_LISTEN_ONLY:
+ return "LISTEN_ONLY";
+ case P2P_WAIT_PEER_CONNECT:
+ return "WAIT_PEER_CONNECT";
+ case P2P_WAIT_PEER_IDLE:
+ return "WAIT_PEER_IDLE";
+ case P2P_SD_DURING_FIND:
+ return "SD_DURING_FIND";
+ case P2P_PROVISIONING:
+ return "PROVISIONING";
+ case P2P_PD_DURING_FIND:
+ return "PD_DURING_FIND";
+ case P2P_INVITE:
+ return "INVITE";
+ case P2P_INVITE_LISTEN:
+ return "INVITE_LISTEN";
+ default:
+ return "?";
+ }
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
+ p2p_state_txt(p2p->state), p2p_state_txt(new_state));
+ p2p->state = new_state;
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Set timeout (state=%s): %u.%06u sec",
+ p2p_state_txt(p2p->state), sec, usec);
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+ eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_clear_timeout(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+ int status)
+{
+ struct p2p_go_neg_results res;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->go_neg_peer = NULL;
+
+ os_memset(&res, 0, sizeof(res));
+ res.status = status;
+ if (peer) {
+ os_memcpy(res.peer_device_addr, peer->p2p_device_addr,
+ ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr,
+ ETH_ALEN);
+ }
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p)
+{
+ unsigned int r, tu;
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting short listen state (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ 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");
+ return;
+ }
+
+ os_get_random((u8 *) &r, sizeof(r));
+ tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+ p2p->min_disc_int) * 100;
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = 0;
+ p2p->pending_listen_usec = 1024 * tu;
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
+ ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ }
+ wpabuf_free(ies);
+}
+
+
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
+{
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Going to listen(only) state");
+
+ 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");
+ return -1;
+ }
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = timeout / 1000;
+ p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay start of listen state");
+ p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
+ return 0;
+ }
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return -1;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ wpabuf_free(ies);
+ return -1;
+ }
+ wpabuf_free(ies);
+
+ p2p_set_state(p2p, P2P_LISTEN_ONLY);
+
+ return 0;
+}
+
+
+static void p2p_device_clear_reported(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+ dev->flags &= ~P2P_DEV_REPORTED;
+}
+
+
+/**
+ * p2p_get_device - Fetch a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->p2p_device_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Interface Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_create_device - Create a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL on failure
+ *
+ * If there is already an entry for the peer, it will be returned instead of
+ * creating a new one.
+ */
+static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev, *oldest = NULL;
+ size_t count = 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ count++;
+ if (oldest == NULL ||
+ os_time_before(&dev->last_seen, &oldest->last_seen))
+ oldest = dev;
+ }
+ if (count + 1 > p2p->cfg->max_peers && oldest) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove oldest peer entry to make room for a new "
+ "peer");
+ dl_list_del(&oldest->list);
+ p2p_device_free(p2p, oldest);
+ }
+
+ dev = os_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ return NULL;
+ dl_list_add(&p2p->devices, &dev->list);
+ os_memcpy(dev->p2p_device_addr, addr, ETH_ALEN);
+
+ return dev;
+}
+
+
+static void p2p_copy_client_info(struct p2p_device *dev,
+ struct p2p_client_info *cli)
+{
+ os_memcpy(dev->device_name, cli->dev_name, cli->dev_name_len);
+ dev->device_name[cli->dev_name_len] = '\0';
+ dev->dev_capab = cli->dev_capab;
+ dev->config_methods = cli->config_methods;
+ os_memcpy(dev->pri_dev_type, cli->pri_dev_type, 8);
+}
+
+
+static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
+ const u8 *go_interface_addr, int freq,
+ const u8 *gi, size_t gi_len)
+{
+ struct p2p_group_info info;
+ size_t c;
+ struct p2p_device *dev;
+
+ if (gi == NULL)
+ return 0;
+
+ if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+ return -1;
+
+ /*
+ * Clear old data for this group; if the devices are still in the
+ * group, the information will be restored in the loop following this.
+ */
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcpy(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN) == 0) {
+ os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
+ os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
+ }
+ }
+
+ for (c = 0; c < info.num_clients; c++) {
+ struct p2p_client_info *cli = &info.client[c];
+ dev = p2p_get_device(p2p, cli->p2p_device_addr);
+ if (dev) {
+ /*
+ * Update information only if we have not received this
+ * directly from the client.
+ */
+ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+ P2P_DEV_PROBE_REQ_ONLY))
+ p2p_copy_client_info(dev, cli);
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ }
+ } else {
+ dev = p2p_create_device(p2p, cli->p2p_device_addr);
+ if (dev == NULL)
+ continue;
+ dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+ p2p_copy_client_info(dev, cli);
+ dev->oper_freq = freq;
+ p2p->cfg->dev_found(
+ p2p->cfg->cb_ctx, dev->p2p_device_addr,
+ dev->p2p_device_addr, dev->pri_dev_type,
+ dev->device_name, dev->config_methods,
+ dev->dev_capab, 0);
+ }
+
+ os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
+ ETH_ALEN);
+ os_get_time(&dev->last_seen);
+ os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
+ os_memcpy(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source address of Beacon or Probe Response frame (may be either
+ * P2P Device Address or P2P Interface Address)
+ * @level: Signal level (signal strength of the received frame from the peer)
+ * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the scan result is for a GO, the clients in the group will also be added
+ * to the peer table.
+ */
+static int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+ int level, const u8 *ies, size_t ies_len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ const u8 *p2p_dev_addr;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ies, ies_len, &msg)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P IE for a device entry");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr)
+ p2p_dev_addr = msg.p2p_device_addr;
+ else if (msg.device_id)
+ p2p_dev_addr = msg.device_id;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore scan data without P2P Device Info or "
+ "P2P Device Id");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ dev = p2p_create_device(p2p, p2p_dev_addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+ os_get_time(&dev->last_seen);
+ dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+
+ if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+ os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+ if (msg.ssid &&
+ (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0)) {
+ os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
+ dev->oper_ssid_len = msg.ssid[1];
+ }
+
+ if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
+ *msg.ds_params >= 1 && *msg.ds_params <= 14) {
+ int ds_freq;
+ if (*msg.ds_params == 14)
+ ds_freq = 2484;
+ else
+ ds_freq = 2407 + *msg.ds_params * 5;
+ if (freq != ds_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on DS "
+ "Parameter Set IE: %d -> %d MHz",
+ freq, ds_freq);
+ freq = ds_freq;
+ }
+ }
+
+ if (dev->listen_freq && dev->listen_freq != freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on scan "
+ "results (" MACSTR " %d -> %d MHz (DS param %d)",
+ MAC2STR(dev->p2p_device_addr), dev->listen_freq, freq,
+ msg.ds_params ? *msg.ds_params : -1);
+ }
+ dev->listen_freq = freq;
+ dev->level = level;
+
+ if (msg.pri_dev_type)
+ os_memcpy(dev->pri_dev_type, msg.pri_dev_type,
+ sizeof(dev->pri_dev_type));
+ os_memcpy(dev->device_name, msg.device_name, sizeof(dev->device_name));
+ dev->config_methods = msg.config_methods ? msg.config_methods :
+ msg.wps_config_methods;
+ if (msg.capability) {
+ dev->dev_capab = msg.capability[0];
+ dev->group_capab = msg.capability[1];
+ }
+
+ if (msg.ext_listen_timing) {
+ dev->ext_listen_period = WPA_GET_LE16(msg.ext_listen_timing);
+ dev->ext_listen_interval =
+ WPA_GET_LE16(msg.ext_listen_timing + 2);
+ }
+
+ p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
+ msg.group_info_len);
+
+ p2p_parse_free(&msg);
+
+ if (p2p_pending_sd_req(p2p, dev))
+ dev->flags |= P2P_DEV_SD_SCHEDULE;
+
+ if (dev->flags & P2P_DEV_REPORTED)
+ return 0;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer found with Listen frequency %d MHz", freq);
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return 0;
+ }
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, dev->p2p_device_addr,
+ dev->pri_dev_type, dev->device_name,
+ dev->config_methods, dev->dev_capab,
+ dev->group_capab);
+ dev->flags |= P2P_DEV_REPORTED;
+
+ return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ if (p2p->go_neg_peer == dev)
+ p2p->go_neg_peer = NULL;
+ if (p2p->invite_peer == dev)
+ p2p->invite_peer = NULL;
+ if (p2p->sd_peer == dev)
+ p2p->sd_peer = NULL;
+ if (p2p->pending_client_disc_go == dev)
+ p2p->pending_client_disc_go = NULL;
+
+ os_free(dev);
+}
+
+
+static int p2p_get_next_prog_freq(struct p2p_data *p2p)
+{
+ struct p2p_channels *c;
+ struct p2p_reg_class *cla;
+ size_t cl, ch;
+ int found = 0;
+ u8 reg_class;
+ u8 channel;
+ int freq;
+
+ c = &p2p->cfg->channels;
+ for (cl = 0; cl < c->reg_classes; cl++) {
+ cla = &c->reg_class[cl];
+ if (cla->reg_class != p2p->last_prog_scan_class)
+ continue;
+ for (ch = 0; ch < cla->channels; ch++) {
+ if (cla->channel[ch] == p2p->last_prog_scan_chan) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ /* Start from beginning */
+ reg_class = c->reg_class[0].reg_class;
+ channel = c->reg_class[0].channel[0];
+ } else {
+ /* Pick the next channel */
+ ch++;
+ if (ch == cla->channels) {
+ cl++;
+ if (cl == c->reg_classes)
+ cl = 0;
+ ch = 0;
+ }
+ reg_class = c->reg_class[cl].reg_class;
+ channel = c->reg_class[cl].channel[ch];
+ }
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search "
+ "channel: reg_class %u channel %u -> %d MHz",
+ reg_class, channel, freq);
+ p2p->last_prog_scan_class = reg_class;
+ p2p->last_prog_scan_chan = channel;
+
+ if (freq == 2412 || freq == 2437 || freq == 2462)
+ return 0; /* No need to add social channels */
+ return freq;
+}
+
+
+static void p2p_search(struct p2p_data *p2p)
+{
+ int freq = 0;
+ enum p2p_scan_type type;
+
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
+ "in Listen state - wait for it to end before "
+ "continuing");
+ return;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+
+ if (p2p->go_neg_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during GO Negotiation start.
+ */
+ freq = p2p->go_neg_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->go_neg_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (GO Neg)", freq);
+ } else if (p2p->invite_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during Invite start.
+ */
+ freq = p2p->invite_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->invite_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (Invite)", freq);
+ } else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+ (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+ type = P2P_SCAN_SOCIAL_PLUS_ONE;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "(+ freq %u)", freq);
+ } else {
+ type = P2P_SCAN_SOCIAL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+ }
+
+ if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Scan request failed");
+ p2p_continue_find(p2p);
+ }
+}
+
+
+static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop");
+ p2p_stop_find(p2p);
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ enum p2p_after_scan op;
+
+ op = p2p->start_after_scan;
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ switch (op) {
+ case P2P_AFTER_SCAN_NOTHING:
+ break;
+ case P2P_AFTER_SCAN_LISTEN:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested Listen state");
+ p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
+ p2p->pending_listen_usec / 1000);
+ return 1;
+ case P2P_AFTER_SCAN_CONNECT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested connect with " MACSTR,
+ MAC2STR(p2p->after_scan_peer));
+ dev = p2p_get_device(p2p, p2p->after_scan_peer);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not "
+ "known anymore");
+ break;
+ }
+ p2p_connect_send(p2p, dev);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * p2p_scan recovery timeout
+ *
+ * Many drivers are using 30 second timeout on scan results. Allow a bit larger
+ * timeout for this to avoid hitting P2P timeout unnecessarily.
+ */
+#define P2P_SCAN_TIMEOUT 35
+
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ int running;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout "
+ "(running=%d)", p2p->p2p_scan_running);
+ running = p2p->p2p_scan_running;
+ /* Make sure we recover from missed scan results callback */
+ p2p->p2p_scan_running = 0;
+
+ if (running)
+ p2p_run_after_scan(p2p);
+}
+
+
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type)
+{
+ int res;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+ type);
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is "
+ "already running");
+ }
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p_clear_timeout(p2p);
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->find_type = type;
+ p2p_device_clear_reported(p2p);
+ p2p_set_state(p2p, P2P_SEARCH);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ if (timeout)
+ eloop_register_timeout(timeout, 0, p2p_find_timeout,
+ p2p, NULL);
+ switch (type) {
+ case P2P_FIND_START_WITH_FULL:
+ case P2P_FIND_PROGRESSIVE:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0);
+ break;
+ case P2P_FIND_ONLY_SOCIAL:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0);
+ break;
+ default:
+ return -1;
+ }
+
+ if (res == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "p2p_scan");
+ }
+
+ return res;
+}
+
+
+void p2p_stop_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ p2p->sd_peer = NULL;
+ p2p->invite_peer = NULL;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to start group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group);
+
+ 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));
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Own preference for operation channel: "
+ "Regulatory Class %u Channel %u%s",
+ p2p->op_reg_class, p2p->op_channel,
+ force_freq ? " (forced)" : "");
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to unknown P2P Device " MACSTR,
+ MAC2STR(peer_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 connect to P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+ if (dev->oper_freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to P2P Device " MACSTR
+ " with incomplete information",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ /*
+ * First, try to connect directly. If the peer does not
+ * acknowledge frames, assume it is sleeping and use device
+ * discoverability via the GO at that point.
+ */
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ if (persistent_group)
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ else
+ dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay connect send");
+ p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
+ os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
+ return 0;
+ }
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+
+ return p2p_connect_send(p2p, dev);
+}
+
+
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to authorize group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group);
+
+ 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));
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Own preference for operation channel: "
+ "Regulatory Class %u Channel %u%s",
+ p2p->op_reg_class, p2p->op_channel,
+ force_freq ? " (forced)" : "");
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot authorize unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ if (persistent_group)
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ else
+ dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+
+ return 0;
+}
+
+
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+ struct p2p_device *dev, struct p2p_message *msg)
+{
+ os_get_time(&dev->last_seen);
+
+ if (msg->pri_dev_type)
+ os_memcpy(dev->pri_dev_type, msg->pri_dev_type,
+ sizeof(dev->pri_dev_type));
+ os_memcpy(dev->device_name, msg->device_name,
+ sizeof(dev->device_name));
+ dev->config_methods = msg->config_methods ? msg->config_methods :
+ msg->wps_config_methods;
+ if (msg->capability) {
+ dev->dev_capab = msg->capability[0];
+ dev->group_capab = msg->capability[1];
+ }
+ if (msg->listen_channel) {
+ int freq;
+ freq = p2p_channel_to_freq((char *) msg->listen_channel,
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown peer Listen channel: "
+ "country=%c%c(0x%02x) reg_class=%u channel=%u",
+ msg->listen_channel[0],
+ msg->listen_channel[1],
+ msg->listen_channel[2],
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+ "peer " MACSTR " Listen channel: %u -> %u MHz",
+ MAC2STR(dev->p2p_device_addr),
+ dev->listen_freq, freq);
+ dev->listen_freq = freq;
+ }
+ }
+ if (msg->ext_listen_timing) {
+ dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
+ dev->ext_listen_interval =
+ WPA_GET_LE16(msg->ext_listen_timing + 2);
+ }
+
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Completed device entry based on data from "
+ "GO Negotiation Request");
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on GO Neg Req: "
+ MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
+ "listen_freq=%d",
+ MAC2STR(dev->p2p_device_addr), dev->dev_capab,
+ dev->group_capab, dev->device_name, dev->listen_freq);
+ }
+
+ dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
+
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return;
+ }
+
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, dev->p2p_device_addr,
+ dev->pri_dev_type, dev->device_name,
+ dev->config_methods, dev->dev_capab,
+ dev->group_capab);
+}
+
+
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
+{
+ os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+ p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
+ os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
+ p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
+ *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
+}
+
+
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
+{
+ p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+ p2p_random(params->passphrase, 8);
+ return 0;
+}
+
+
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
+{
+ struct p2p_go_neg_results res;
+ int go = peer->go_state == LOCAL_GO;
+ struct p2p_channels intersection;
+ int freqs;
+ size_t i, j;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR " completed (%s will be "
+ "GO)", MAC2STR(peer->p2p_device_addr),
+ go ? "local end" : "peer");
+
+ os_memset(&res, 0, sizeof(res));
+ res.role_go = go;
+ os_memcpy(res.peer_device_addr, peer->p2p_device_addr, ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+ res.wps_method = peer->wps_method;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+ res.persistent_group = 1;
+
+ if (go) {
+ /* Setup AP mode for WPS provisioning */
+ res.freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+ res.ssid_len = p2p->ssid_len;
+ p2p_random(res.passphrase, 8);
+ } else
+ res.freq = peer->oper_freq;
+
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &intersection);
+ freqs = 0;
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c = &intersection.reg_class[i];
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ for (j = 0; j < c->channels; j++) {
+ int freq;
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ freq = p2p_channel_to_freq(peer->country, c->reg_class,
+ c->channel[j]);
+ if (freq < 0)
+ continue;
+ res.freq_list[freqs++] = freq;
+ }
+ }
+
+ p2p_clear_timeout(p2p);
+ peer->go_neg_req_sent = 0;
+ peer->wps_method = WPS_NOT_READY;
+
+ p2p_set_state(p2p, P2P_PROVISIONING);
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
+
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case P2P_GO_NEG_REQ:
+ p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_RESP:
+ p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_CONF:
+ p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_INVITATION_REQ:
+ p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
+ rx_freq);
+ break;
+ case P2P_INVITATION_RESP:
+ p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_PROV_DISC_REQ:
+ p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_PROV_DISC_RESP:
+ p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_DEV_DISC_REQ:
+ p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_DEV_DISC_RESP:
+ p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported P2P Public Action frame type %d",
+ data[0]);
+ break;
+ }
+}
+
+
+void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len,
+ int freq)
+{
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ data++;
+ len--;
+ if (len < 3)
+ return;
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+
+ data += 3;
+ len -= 3;
+ if (len < 1)
+ return;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+
+ p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1);
+ break;
+ }
+}
+
+
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq)
+{
+ if (category == WLAN_ACTION_PUBLIC) {
+ p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
+ return;
+ }
+
+ if (category != WLAN_ACTION_VENDOR_SPECIFIC)
+ return;
+
+ if (len < 4)
+ return;
+
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+ data += 3;
+ len -= 3;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+ data++;
+ len--;
+
+ /* P2P action frame */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
+
+ if (len < 1)
+ return;
+ switch (data[0]) {
+ case P2P_NOA:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - Notice of Absence");
+ /* TODO */
+ break;
+ case P2P_PRESENCE_REQ:
+ p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ case P2P_PRESENCE_RESP:
+ p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
+ break;
+ case P2P_GO_DISC_REQ:
+ p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - unknown type %u", data[0]);
+ break;
+ }
+}
+
+
+static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->go_neg_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+}
+
+
+static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->invite_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+}
+
+
+static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
+ {
+ p2p_parse_free(&msg);
+ return; /* not a P2P probe */
+ }
+
+ if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0) {
+ /* The Probe Request is not part of P2P Device Discovery. It is
+ * not known whether the source address of the frame is the P2P
+ * Device Address or P2P Interface Address. Do not add a new
+ * peer entry based on this frames.
+ */
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ if (dev->country[0] == 0 && msg.listen_channel)
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ p2p_parse_free(&msg);
+ return; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ os_get_time(&dev->last_seen);
+ dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
+
+ if (msg.capability) {
+ dev->dev_capab = msg.capability[0];
+ dev->group_capab = msg.capability[1];
+ }
+
+ if (msg.listen_channel) {
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ dev->listen_freq = p2p_channel_to_freq(dev->country,
+ msg.listen_channel[3],
+ msg.listen_channel[4]);
+ }
+
+ os_memcpy(dev->device_name, msg.device_name, sizeof(dev->device_name));
+
+ if (msg.wps_pri_dev_type)
+ os_memcpy(dev->pri_dev_type, msg.wps_pri_dev_type,
+ sizeof(dev->pri_dev_type));
+
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on Probe Req: " MACSTR
+ " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
+ MAC2STR(dev->p2p_device_addr), dev->dev_capab,
+ dev->group_capab, dev->device_name, dev->listen_freq);
+}
+
+
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+ const u8 *addr,
+ struct p2p_message *msg)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ os_get_time(&dev->last_seen);
+ return dev; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL)
+ return NULL;
+
+ p2p_add_dev_info(p2p, addr, dev, msg);
+
+ return dev;
+}
+
+
+static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
+{
+ if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
+ return 1;
+ if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
+ WPA_GET_BE32(&req_dev_type[2]) == 0 &&
+ WPA_GET_BE16(&req_dev_type[6]) == 0)
+ return 1; /* Category match with wildcard OUI/sub-category */
+ return 0;
+}
+
+
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+ size_t num_req_dev_type)
+{
+ size_t i;
+ for (i = 0; i < num_req_dev_type; i++) {
+ if (dev_type_match(dev_type, req_dev_type[i]))
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * p2p_match_dev_type - Match local device type with requested type
+ * @p2p: P2P module context from p2p_init()
+ * @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 local device types for deciding whether to reply to a Probe
+ * Request frame.
+ */
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+ size_t i;
+
+ 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(p2p->cfg->pri_dev_type, attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Primary Device Type matches */
+
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
+ attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Secondary Device Type matches */
+
+ /* No matching device type found */
+ return 0;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ /* TODO: add more info into WPS IE; maybe get from WPS module? */
+ p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+
+ /* P2P IE */
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+ size_t ie_len)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *buf;
+ struct ieee80211_mgmt *resp;
+ struct wpabuf *wps;
+ struct wpabuf *ies;
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ return;
+ }
+
+ if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+ ParseFailed) {
+ /* Ignore invalid Probe Request frames */
+ return;
+ }
+
+ if (elems.p2p == NULL) {
+ /* not a P2P probe - ignore it */
+ return;
+ }
+
+ if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
+ 0) {
+ /* not using P2P Wildcard SSID - ignore */
+ return;
+ }
+
+ /* Check Requested Device Type match */
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_match_dev_type(p2p, wps)) {
+ wpabuf_free(wps);
+ /* No match with Requested Device Type */
+ return;
+ }
+ wpabuf_free(wps);
+
+ if (!p2p->cfg->send_probe_resp)
+ return; /* Response generated elsewhere */
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reply to P2P Probe Request in Listen state");
+
+ /*
+ * We do not really have a specific BSS that this frame is advertising,
+ * so build a frame that has some information in valid format. This is
+ * really only used for discovery purposes, not to learn exact BSS
+ * parameters.
+ */
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ buf = wpabuf_alloc(200 + wpabuf_len(ies));
+ if (buf == NULL) {
+ wpabuf_free(ies);
+ return;
+ }
+
+ resp = NULL;
+ resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+
+ resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_RESP << 4));
+ os_memcpy(resp->da, addr, ETH_ALEN);
+ os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+ resp->u.probe_resp.beacon_int = host_to_le16(100);
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ resp->u.probe_resp.capab_info =
+ host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+ WLAN_CAPABILITY_PRIVACY |
+ WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ wpabuf_put_u8(buf, WLAN_EID_SSID);
+ wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+ wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+ wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+ wpabuf_put_u8(buf, 8);
+ wpabuf_put_u8(buf, (60 / 5) | 0x80);
+ wpabuf_put_u8(buf, 90 / 5);
+ wpabuf_put_u8(buf, (120 / 5) | 0x80);
+ wpabuf_put_u8(buf, 180 / 5);
+ wpabuf_put_u8(buf, (240 / 5) | 0x80);
+ wpabuf_put_u8(buf, 360 / 5);
+ wpabuf_put_u8(buf, 480 / 5);
+ wpabuf_put_u8(buf, 540 / 5);
+
+ wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, p2p->cfg->channel);
+
+ wpabuf_put_buf(buf, ies);
+ wpabuf_free(ies);
+
+ p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+
+ wpabuf_free(buf);
+}
+
+
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+ size_t ie_len)
+{
+ p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+ p2p_reply_probe(p2p, addr, ie, ie_len);
+
+ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
+ p2p->go_neg_peer &&
+ os_memcmp(addr, p2p->go_neg_peer->p2p_device_addr, ETH_ALEN) == 0)
+ {
+ /* Received a Probe Request from GO Negotiation peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation from timeout");
+ eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+ return 1;
+ }
+
+ if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
+ p2p->invite_peer &&
+ os_memcmp(addr, p2p->invite_peer->p2p_device_addr, ETH_ALEN) == 0)
+ {
+ /* Received a Probe Request from Invite peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found Invite peer - try to start Invite from "
+ "timeout");
+ eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
+ u8 *buf, size_t len)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ size_t tmplen;
+ int res;
+
+ if (!(p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+ return 0;
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * P2P Interface attribute (present if concurrent device)
+ */
+ tmp = wpabuf_alloc(200);
+ if (tmp == NULL)
+ return -1;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+ if (p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER)
+ p2p_buf_add_p2p_interface(tmp, p2p);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ struct p2p_device *peer;
+ size_t tmplen;
+ int res;
+
+ if (!p2p_group)
+ return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len);
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * Extended Listen Timing (may be present)
+ * P2P Device Info attribute (shall be present)
+ */
+ tmp = wpabuf_alloc(200);
+ if (tmp == NULL)
+ return -1;
+
+ peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+ p2p_buf_add_device_info(tmp, p2p, peer);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return 0;
+
+ ret = p2p_attr_text(p2p_ie, buf, end);
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
+static void p2p_clear_go_neg(struct p2p_data *p2p)
+{
+ p2p->go_neg_peer = NULL;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore WPS registration success notification");
+ return; /* No pending Group Formation */
+ }
+
+ if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore WPS registration success notification "
+ "for " MACSTR " (GO Negotiation peer " MACSTR ")",
+ MAC2STR(mac_addr),
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+ return; /* Ignore unexpected peer address */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation completed successfully with " MACSTR,
+ MAC2STR(mac_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+void p2p_group_formation_failed(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore group formation failure notification");
+ return; /* No pending Group Formation */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation failed with " MACSTR,
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+struct p2p_data * p2p_init(const struct p2p_config *cfg)
+{
+ struct p2p_data *p2p;
+
+ if (cfg->max_peers < 1)
+ return NULL;
+
+ p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
+ if (p2p == NULL)
+ return NULL;
+ p2p->cfg = (struct p2p_config *) (p2p + 1);
+ os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
+ if (cfg->dev_name)
+ p2p->cfg->dev_name = os_strdup(cfg->dev_name);
+
+ p2p->min_disc_int = 1;
+ p2p->max_disc_int = 3;
+
+ os_get_random(&p2p->next_tie_breaker, 1);
+ p2p->next_tie_breaker &= 0x01;
+ if (cfg->sd_request)
+ p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+ p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+ if (cfg->concurrent_operations)
+ p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+ dl_list_init(&p2p->devices);
+
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+
+ return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+ eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ p2p_flush(p2p);
+ os_free(p2p->cfg->dev_name);
+ os_free(p2p->groups);
+ os_free(p2p);
+}
+
+
+void p2p_flush(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *prev;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
+ list) {
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+ p2p_free_sd_queries(p2p);
+}
+
+
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
+{
+ os_free(p2p->cfg->dev_name);
+ if (dev_name) {
+ p2p->cfg->dev_name = os_strdup(dev_name);
+ if (p2p->cfg->dev_name == NULL)
+ return -1;
+ } else
+ p2p->cfg->dev_name = NULL;
+ return 0;
+}
+
+
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
+{
+ os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
+ return 0;
+}
+
+
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types)
+{
+ if (num_dev_types > P2P_SEC_DEVICE_TYPES)
+ num_dev_types = P2P_SEC_DEVICE_TYPES;
+ p2p->cfg->num_sec_dev_types = num_dev_types;
+ os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
+ return 0;
+}
+
+
+int p2p_set_country(struct p2p_data *p2p, const char *country)
+{
+ os_memcpy(p2p->cfg->country, country, 3);
+ return 0;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ p2p_set_state(p2p, P2P_SEARCH);
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (dev->flags & P2P_DEV_SD_SCHEDULE) {
+ if (p2p_start_sd(p2p, dev) == 0)
+ return;
+ else
+ break;
+ } else if (dev->req_config_methods) {
+ if (p2p_send_prov_disc_req(p2p, dev, 0) == 0)
+ return;
+ }
+ }
+
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query TX callback: success=%d",
+ success);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success) {
+ if (p2p->sd_peer) {
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ if (p2p->sd_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No SD peer entry known");
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ /* Wait for response from the peer */
+ p2p_set_state(p2p, P2P_SD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request TX callback: success=%d",
+ success);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success) {
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ /* Wait for response from the peer */
+ if (p2p->state == P2P_SEARCH)
+ p2p_set_state(p2p, P2P_PD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ int level, const u8 *ies, size_t ies_len)
+{
+ p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+
+ if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
+ os_memcmp(p2p->go_neg_peer->p2p_device_addr, bssid, ETH_ALEN) == 0)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation");
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+ if (!p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not "
+ "running, but scan results received");
+ }
+ p2p->p2p_scan_running = 0;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+
+ if (p2p_run_after_scan(p2p))
+ return;
+ if (p2p->state == P2P_SEARCH)
+ p2p_continue_find(p2p);
+}
+
+
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
+{
+ u8 *len = p2p_buf_add_ie_hdr(ies);
+ p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+ if (p2p->cfg->reg_class && p2p->cfg->channel)
+ p2p_buf_add_listen_channel(ies, p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ /* TODO: p2p_buf_add_operating_channel() if GO */
+ p2p_buf_update_ie_hdr(ies, len);
+}
+
+
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
+{
+ return p2p_attr_text(p2p_ie, buf, end);
+}
+
+
+static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Request TX callback: success=%d",
+ success);
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending GO Negotiation");
+ return;
+ }
+
+ if (success) {
+ dev->go_neg_req_sent++;
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_set_state(p2p, P2P_IDLE);
+ return;
+ }
+ }
+
+ if (!success &&
+ (dev->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+ !is_zero_ether_addr(dev->member_in_go_dev)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer " MACSTR " did not acknowledge request - "
+ "try to use device discoverability through its GO",
+ MAC2STR(dev->p2p_device_addr));
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_send_dev_disc_req(p2p, dev);
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response TX callback: success=%d",
+ success);
+ if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore TX callback event - GO Negotiation is "
+ "not running anymore");
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response (failure) TX callback: "
+ "success=%d", success);
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p, int success)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Confirm TX callback: success=%d",
+ success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (!success) {
+ /*
+ * It looks like the TX status for GO Negotiation Confirm is
+ * often showing failure even when the peer has actually
+ * received the frame. Since the peer may change channels
+ * immediately after having received the frame, we may not see
+ * an Ack for retries, so just dropping a single frame may
+ * trigger this. To allow the group formation to succeed if the
+ * peer did indeed receive the frame, continue regardless of
+ * the TX status.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Assume GO Negotiation Confirm TX was actually "
+ "received by the peer even though Ack was not "
+ "reported");
+ }
+
+ dev = p2p->go_neg_peer;
+ if (dev == NULL)
+ return;
+
+ p2p_go_complete(p2p, dev);
+}
+
+
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, int success)
+{
+ enum p2p_pending_action_state state;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR " success=%d",
+ p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+ MAC2STR(bssid), success);
+ state = p2p->pending_action_state;
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ switch (state) {
+ case P2P_NO_PENDING_ACTION:
+ break;
+ case P2P_PENDING_GO_NEG_REQUEST:
+ p2p_go_neg_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE:
+ p2p_go_neg_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
+ p2p_go_neg_resp_failure_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_CONFIRM:
+ p2p_go_neg_conf_cb(p2p, success);
+ break;
+ case P2P_PENDING_SD:
+ p2p_sd_cb(p2p, success);
+ break;
+ case P2P_PENDING_PD:
+ p2p_prov_disc_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_REQUEST:
+ p2p_invitation_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_RESPONSE:
+ p2p_invitation_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_REQUEST:
+ p2p_dev_disc_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_RESPONSE:
+ p2p_dev_disc_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_DISC_REQ:
+ p2p_go_disc_req_cb(p2p, success);
+ break;
+ }
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration)
+{
+ if (freq == p2p->pending_client_disc_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability remain-awake completed");
+ p2p->pending_client_disc_freq = 0;
+ return;
+ }
+
+ if (freq != p2p->pending_listen_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected listen callback for freq=%u "
+ "duration=%u (pending_listen_freq=%u)",
+ freq, duration, p2p->pending_listen_freq);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting Listen timeout(%u,%u) on freq=%u based on "
+ "callback",
+ p2p->pending_listen_sec, p2p->pending_listen_usec,
+ p2p->pending_listen_freq);
+ p2p->in_listen = 1;
+ p2p->drv_in_listen = 1;
+ if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
+ /*
+ * Add 20 msec extra wait to avoid race condition with driver
+ * remain-on-channel end event, i.e., give driver more time to
+ * complete the operation before our timeout expires.
+ */
+ p2p_set_timeout(p2p, p2p->pending_listen_sec,
+ p2p->pending_listen_usec + 20000);
+ }
+
+ p2p->pending_listen_freq = 0;
+}
+
+
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen "
+ "state (freq=%u)", freq);
+ p2p->drv_in_listen = 0;
+ if (p2p->in_listen)
+ return 0; /* Internal timeout will trigger the next step */
+
+ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ } else if (p2p->state == P2P_SEARCH) {
+ p2p_search(p2p);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void p2p_timeout_connect(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer) {
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+ "still in Listen state; wait for it to "
+ "complete");
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ } else
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
+{
+ /*
+ * TODO: could remain constantly in Listen state for some time if there
+ * are no other concurrent uses for the radio. For now, go to listen
+ * state once per second to give other uses a chance to use the radio.
+ */
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 1, 0);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown GO Neg peer - stop GO Neg wait");
+ return;
+ }
+
+ dev->wait_count++;
+ if (dev->wait_count >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on waiting peer to become ready for GO "
+ "Negotiation");
+ p2p_go_neg_failed(p2p, dev, -1);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Go to Listen state while waiting for the peer to become "
+ "ready for GO Negotiation");
+ p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query timeout");
+ if (p2p->sd_peer) {
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request timeout");
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_invite(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_INVITE_LISTEN);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_invite_listen(struct p2p_data *p2p)
+{
+ if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_invite_send(p2p, p2p->invite_peer,
+ p2p->invite_go_dev_addr);
+ } else {
+ if (p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request retry limit reached");
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(
+ p2p->cfg->cb_ctx, -1, NULL);
+ }
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ p2p->in_listen = 0;
+
+ switch (p2p->state) {
+ case P2P_IDLE:
+ break;
+ case P2P_SEARCH:
+ p2p_search(p2p);
+ break;
+ case P2P_CONNECT:
+ p2p_timeout_connect(p2p);
+ break;
+ case P2P_CONNECT_LISTEN:
+ p2p_timeout_connect_listen(p2p);
+ break;
+ case P2P_GO_NEG:
+ break;
+ case P2P_LISTEN_ONLY:
+ if (p2p->ext_listen_only) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Extended Listen Timing - Listen State "
+ "completed");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+ break;
+ case P2P_WAIT_PEER_CONNECT:
+ p2p_timeout_wait_peer_connect(p2p);
+ break;
+ case P2P_WAIT_PEER_IDLE:
+ p2p_timeout_wait_peer_idle(p2p);
+ break;
+ case P2P_SD_DURING_FIND:
+ p2p_timeout_sd_during_find(p2p);
+ break;
+ case P2P_PROVISIONING:
+ break;
+ case P2P_PD_DURING_FIND:
+ p2p_timeout_prov_disc_during_find(p2p);
+ break;
+ case P2P_INVITE:
+ p2p_timeout_invite(p2p);
+ break;
+ case P2P_INVITE_LISTEN:
+ p2p_timeout_invite_listen(p2p);
+ break;
+ }
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+ "connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " unknown", MAC2STR(peer_addr));
+ return -1;
+ }
+ dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
+ dev->flags |= P2P_DEV_USER_REJECTED;
+ return 0;
+}
+
+
+static const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+ switch (method) {
+ case WPS_NOT_READY:
+ return "not-ready";
+ case WPS_PIN_LABEL:
+ return "Label";
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ }
+
+ return "??";
+}
+
+
+static const char * p2p_go_state_text(enum p2p_go_state go_state)
+{
+ switch (go_state) {
+ case UNKNOWN_GO:
+ return "unknown";
+ case LOCAL_GO:
+ return "local";
+ case REMOTE_GO:
+ return "remote";
+ }
+
+ return "??";
+}
+
+
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+ char *buf, size_t buflen)
+{
+ struct p2p_device *dev;
+ int res;
+ char *pos, *end;
+ struct os_time now;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ if (addr)
+ dev = p2p_get_device(p2p, addr);
+ else
+ dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+
+ if (dev && next) {
+ dev = dl_list_first(&dev->list, struct p2p_device, list);
+ if (&dev->list == &p2p->devices)
+ dev = NULL;
+ }
+
+ if (dev == NULL)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, MACSTR "\n",
+ MAC2STR(dev->p2p_device_addr));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ os_get_time(&now);
+ res = os_snprintf(pos, end - pos,
+ "age=%d\n"
+ "listen_freq=%d\n"
+ "level=%d\n"
+ "wps_method=%s\n"
+ "interface_addr=" MACSTR "\n"
+ "member_in_go_dev=" MACSTR "\n"
+ "member_in_go_iface=" MACSTR "\n"
+ "pri_dev_type=%s\n"
+ "device_name=%s\n"
+ "config_methods=0x%x\n"
+ "dev_capab=0x%x\n"
+ "group_capab=0x%x\n"
+ "go_neg_req_sent=%d\n"
+ "go_state=%s\n"
+ "dialog_token=%u\n"
+ "intended_addr=" MACSTR "\n"
+ "country=%c%c\n"
+ "oper_freq=%d\n"
+ "req_config_methods=0x%x\n"
+ "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "status=%d\n"
+ "wait_count=%u\n"
+ "invitation_reqs=%u\n",
+ (int) (now.sec - dev->last_seen.sec),
+ dev->listen_freq,
+ dev->level,
+ p2p_wps_method_text(dev->wps_method),
+ MAC2STR(dev->interface_addr),
+ MAC2STR(dev->member_in_go_dev),
+ MAC2STR(dev->member_in_go_iface),
+ wps_dev_type_bin2str(dev->pri_dev_type,
+ devtype, sizeof(devtype)),
+ dev->device_name,
+ dev->config_methods,
+ dev->dev_capab,
+ dev->group_capab,
+ dev->go_neg_req_sent,
+ p2p_go_state_text(dev->go_state),
+ dev->dialog_token,
+ MAC2STR(dev->intended_addr),
+ dev->country[0] ? dev->country[0] : '_',
+ dev->country[1] ? dev->country[1] : '_',
+ dev->oper_freq,
+ dev->req_config_methods,
+ dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
+ "[PROBE_REQ_ONLY]" : "",
+ dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
+ dev->flags & P2P_DEV_NOT_YET_READY ?
+ "[NOT_YET_READY]" : "",
+ dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
+ dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
+ "",
+ dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+ "[PD_PEER_DISPLAY]" : "",
+ dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+ "[PD_PEER_KEYPAD]" : "",
+ dev->flags & P2P_DEV_USER_REJECTED ?
+ "[USER_REJECTED]" : "",
+ dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
+ "[PEER_WAITING_RESPONSE]" : "",
+ dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
+ "[PREFER_PERSISTENT_GROUP]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
+ "[WAIT_GO_NEG_RESPONSE]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
+ "[WAIT_GO_NEG_CONFIRM]" : "",
+ dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
+ "[GROUP_CLIENT_ONLY]" : "",
+ dev->status,
+ dev->wait_count,
+ dev->invitation_reqs);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ if (dev->ext_listen_period) {
+ res = os_snprintf(pos, end - pos,
+ "ext_listen_period=%u\n"
+ "ext_listen_interval=%u\n",
+ dev->ext_listen_period,
+ dev->ext_listen_interval);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ if (dev->oper_ssid_len) {
+ res = os_snprintf(pos, end - pos,
+ "oper_ssid=%s\n",
+ wpa_ssid_txt(dev->oper_ssid,
+ dev->oper_ssid_len));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ return pos - buf;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ }
+}
+
+
+static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
+ u32 duration2, u32 interval2)
+{
+ struct wpabuf *req;
+ struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
+ u8 *len;
+
+ req = wpabuf_alloc(100);
+ if (req == NULL)
+ return NULL;
+
+ if (duration1 || interval1) {
+ os_memset(&desc1, 0, sizeof(desc1));
+ desc1.count_type = 1;
+ desc1.duration = duration1;
+ desc1.interval = interval1;
+ ptr1 = &desc1;
+
+ if (duration2 || interval2) {
+ os_memset(&desc2, 0, sizeof(desc2));
+ desc2.count_type = 2;
+ desc2.duration = duration2;
+ desc2.interval = interval2;
+ ptr2 = &desc2;
+ }
+ }
+
+ p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
+ len = p2p_buf_add_ie_hdr(req);
+ p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
+ p2p_buf_update_ie_hdr(req, len);
+
+ return req;
+}
+
+
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2)
+{
+ struct wpabuf *req;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to "
+ "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u "
+ "int1=%u dur2=%u int2=%u",
+ MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
+ freq, duration1, interval1, duration2, interval2);
+
+ req = p2p_build_presence_req(duration1, interval1, duration2,
+ interval2);
+ if (req == NULL)
+ return -1;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, go_interface_addr,
+ own_interface_addr,
+ go_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;
+}
+
+
+static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
+ size_t noa_len, u8 dialog_token)
+{
+ struct wpabuf *resp;
+ u8 *len;
+
+ resp = wpabuf_alloc(100 + noa_len);
+ if (resp == NULL)
+ return NULL;
+
+ p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
+ len = p2p_buf_add_ie_hdr(resp);
+ p2p_buf_add_status(resp, status);
+ if (noa) {
+ wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(resp, noa_len);
+ wpabuf_put_data(resp, noa, noa_len);
+ } else
+ p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
+ p2p_buf_update_ie_hdr(resp, len);
+
+ return resp;
+}
+
+
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq)
+{
+ struct p2p_message msg;
+ u8 status;
+ struct wpabuf *resp;
+ size_t g;
+ struct p2p_group *group = NULL;
+ int parsed = 0;
+ u8 noa[50];
+ int noa_len;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Request");
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
+ ETH_ALEN) == 0) {
+ group = p2p->groups[g];
+ break;
+ }
+ }
+ if (group == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore P2P Presence Request for unknown group "
+ MACSTR, MAC2STR(da));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ parsed = 1;
+
+ if (msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No NoA attribute in P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
+
+fail:
+ if (p2p->cfg->get_noa)
+ noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
+ sizeof(noa));
+ else
+ noa_len = -1;
+ resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
+ noa_len > 0 ? noa_len : 0,
+ msg.dialog_token);
+ if (parsed)
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, rx_freq, sa, da, da,
+ 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);
+}
+
+
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Response");
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Response");
+ return;
+ }
+
+ if (msg.status == NULL || msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status or NoA attribute in P2P Presence "
+ "Response");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was rejected: status %u",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was accepted");
+ wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
+ msg.noa, msg.noa_len);
+ /* TODO: process NoA */
+ p2p_parse_free(&msg);
+}
+
+
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ if (p2p->ext_listen_interval) {
+ /* Schedule next extended listen timeout */
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+ }
+
+ if (p2p->state != P2P_IDLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+ "Listen timeout in active state (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+ p2p->ext_listen_only = 1;
+ if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "Listen state for Extended Listen Timing");
+ p2p->ext_listen_only = 0;
+ }
+}
+
+
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval)
+{
+ if (period > 65535 || interval > 65535 || period > interval ||
+ (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid Extended Listen Timing request: "
+ "period=%u interval=%u", period, interval);
+ return -1;
+ }
+
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+
+ if (interval == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Disabling Extended Listen Timing");
+ p2p->ext_listen_period = 0;
+ p2p->ext_listen_interval = 0;
+ return 0;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Enabling Extended Listen Timing: period %u msec, "
+ "interval %u msec", period, interval);
+ p2p->ext_listen_period = period;
+ p2p->ext_listen_interval = interval;
+ p2p->ext_listen_interval_sec = interval / 1000;
+ p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
+
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+
+ return 0;
+}
+
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Deauthentication notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Disassociation notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+ }
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: "
+ "reg_class %u channel %u", reg_class, channel);
+ p2p->cfg->reg_class = reg_class;
+ p2p->cfg->channel = channel;
+
+ return 0;
+}
+
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
+{
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len);
+ if (postfix == NULL) {
+ p2p->cfg->ssid_postfix_len = 0;
+ return 0;
+ }
+ if (len > sizeof(p2p->cfg->ssid_postfix))
+ return -1;
+ os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
+ p2p->cfg->ssid_postfix_len = len;
+ return 0;
+}
+
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr)
+{
+ struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+ if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
+ return -1;
+ os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
+ return 0;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
new file mode 100644
index 0000000..a1b7cab
--- /dev/null
+++ b/src/p2p/p2p.h
@@ -0,0 +1,1216 @@
+/*
+ * Wi-Fi Direct - 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_H
+#define P2P_H
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+ /**
+ * struct p2p_reg_class - Supported regulatory class
+ */
+ struct p2p_reg_class {
+ /**
+ * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /**
+ * reg_classes - Number of reg_class entries in use
+ */
+ size_t reg_classes;
+};
+
+enum p2p_wps_method {
+ WPS_NOT_READY, WPS_PIN_LABEL, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+ /**
+ * status - Negotiation result (Status Code)
+ *
+ * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+ * failed negotiation.
+ */
+ int status;
+
+ /**
+ * role_go - Whether local end is Group Owner
+ */
+ int role_go;
+
+ /**
+ * freq - Frequency of the group operational channel in MHz
+ */
+ int freq;
+
+ /**
+ * ssid - SSID of the group
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of SSID in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * passphrase - WPA2-Personal passphrase for the group (GO only)
+ */
+ char passphrase[64];
+
+ /**
+ * peer_device_addr - P2P Device Address of the peer
+ */
+ u8 peer_device_addr[ETH_ALEN];
+
+ /**
+ * peer_interface_addr - P2P Interface Address of the peer
+ */
+ u8 peer_interface_addr[ETH_ALEN];
+
+ /**
+ * wps_method - WPS method to be used during provisioning
+ */
+ enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+ /**
+ * freq_list - Zero-terminated list of possible operational channels
+ */
+ int freq_list[P2P_MAX_CHANNELS];
+
+ /**
+ * persistent_group - Whether the group should be made persistent
+ */
+ int persistent_group;
+};
+
+struct p2p_data;
+
+enum p2p_scan_type {
+ P2P_SCAN_SOCIAL,
+ P2P_SCAN_FULL,
+ P2P_SCAN_SPECIFIC,
+ P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+ /**
+ * country - Country code to use in P2P operations
+ */
+ char country[3];
+
+ /**
+ * reg_class - Regulatory class for own listen channel
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Own listen channel
+ */
+ u8 channel;
+
+ /**
+ * 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;
+
+ /**
+ * pri_dev_type - Primary Device Type (see WPS)
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+ */
+#define P2P_SEC_DEVICE_TYPES 5
+
+ /**
+ * sec_dev_type - Optional secondary device types
+ */
+ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+ /**
+ * dev_addr - P2P Device Address
+ */
+ u8 dev_addr[ETH_ALEN];
+
+ /**
+ * dev_name - Device Name
+ */
+ char *dev_name;
+
+ /**
+ * num_sec_dev_types - Number of sec_dev_type entries
+ */
+ size_t num_sec_dev_types;
+
+ /**
+ * concurrent_operations - Whether concurrent operations are supported
+ */
+ int concurrent_operations;
+
+ /**
+ * max_peers - Maximum number of discovered peers to remember
+ *
+ * If more peers are discovered, older entries will be removed to make
+ * room for the new ones.
+ */
+ size_t max_peers;
+
+ /**
+ * ssid_postfix - Postfix data to add to the SSID
+ *
+ * This data will be added to the end of the SSID after the
+ * DIRECT-<random two octets> prefix.
+ */
+ u8 ssid_postfix[32 - 9];
+
+ /**
+ * ssid_postfix_len - Length of the ssid_postfix data
+ */
+ size_t ssid_postfix_len;
+
+ /**
+ * msg_ctx - Context to use with wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+
+ /* Callbacks to request lower layer driver operations */
+
+ /**
+ * p2p_scan - Request a P2P scan/search
+ * @ctx: Callback context from cb_ctx
+ * @type: Scan type
+ * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback function is used to request a P2P scan or search
+ * operation to be completed. Type type argument specifies which type
+ * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+ * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+ * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+ * request a scan of a single channel specified by freq.
+ * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+ * plus one extra channel specified by freq.
+ *
+ * The full scan is used for the initial scan to find group owners from
+ * all. The other types are used during search phase scan of the social
+ * channels (with potential variation if the Listen channel of the
+ * target peer is known or if other channels are scanned in steps).
+ *
+ * The scan results are returned after this call by calling
+ * p2p_scan_res_handler() for each scan result that has a P2P IE and
+ * then calling p2p_scan_res_handled() to indicate that all scan
+ * results have been indicated.
+ */
+ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq);
+
+ /**
+ * send_probe_resp - Transmit a Probe Response frame
+ * @ctx: Callback context from cb_ctx
+ * @buf: Probe Response frame (including the header and body)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to reply to Probe Request frames that were
+ * indicated with a call to p2p_probe_req_rx(). The response is to be
+ * sent on the same channel or to be dropped if the driver is not
+ * anymore listening to Probe Request frames.
+ *
+ * Alternatively, the responsibility for building the Probe Response
+ * frames in Listen state may be in another system component in which
+ * case this function need to be implemented (i.e., the function
+ * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+ * Response frames in such a case are available from the
+ * start_listen() callback. It should be noted that the received Probe
+ * Request frames must be indicated by calling p2p_probe_req_rx() even
+ * if this send_probe_resp() is not used.
+ */
+ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @buf: Frame body (starting from Category field)
+ * @len: Length of buf in octets
+ * @wait_time: How many msec to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * The Action frame may not be transmitted immediately and the status
+ * of the transmission must be reported by calling
+ * p2p_send_action_cb() once the frame has either been transmitted or
+ * it has been dropped due to excessive retries or other failure to
+ * transmit.
+ */
+ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+
+ /**
+ * send_action_done - Notify that Action frame sequence was completed
+ * @ctx: Callback context from cb_ctx
+ *
+ * This function is called when the Action frame sequence that was
+ * started with send_action() has been completed, i.e., when there is
+ * no need to wait for a response from the destination peer anymore.
+ */
+ void (*send_action_done)(void *ctx);
+
+ /**
+ * start_listen - Start Listen state
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency of the listen channel in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ * @probe_resp_ie: IE(s) to be added to Probe Response frames
+ * Returns: 0 on success, -1 on failure
+ *
+ * This Listen state may not start immediately since the driver may
+ * have other pending operations to complete first. Once the Listen
+ * state has started, p2p_listen_cb() must be called to notify the P2P
+ * module. Once the Listen state is stopped, p2p_listen_end() must be
+ * called to notify the P2P module that the driver is not in the Listen
+ * state anymore.
+ *
+ * If the send_probe_resp() is not used for generating the response,
+ * the IEs from probe_resp_ie need to be added to the end of the Probe
+ * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+ * information can be ignored.
+ */
+ int (*start_listen)(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie);
+ /**
+ * stop_listen - Stop Listen state
+ * @ctx: Callback context from cb_ctx
+ *
+ * This callback can be used to stop a Listen state operation that was
+ * previously requested with start_listen().
+ */
+ void (*stop_listen)(void *ctx);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @ctx: Callback context from cb_ctx
+ * @interface_addr: P2P Interface Address of the GO
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len);
+
+ /* Callbacks to notify events to upper layer management entity */
+
+ /**
+ * dev_found - Notification of a found P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @addr: Source address of the message triggering this notification
+ * @dev_addr: P2P Device Address of the found P2P Device
+ * @pri_dev_type: Primary Device Type
+ * @dev_name: Device Name
+ * @config_methods: Configuration Methods
+ * @dev_capab: Device Capabilities
+ * @group_capab: Group Capabilities
+ *
+ * This callback is used to notify that a new P2P Device has been
+ * found. This may happen, e.g., during Search state based on scan
+ * results or during Listen state based on receive Probe Request and
+ * Group Owner Negotiation Request.
+ */
+ void (*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);
+
+ /**
+ * go_neg_req_rx - Notification of a receive GO Negotiation Request
+ * @ctx: Callback context from cb_ctx
+ * @src: Source address of the message triggering this notification
+ *
+ * This callback is used to notify that a P2P Device is requesting
+ * group owner negotiation with us, but we do not have all the
+ * necessary information to start GO Negotiation. This indicates that
+ * the local user has not authorized the connection yet by providing a
+ * PIN or PBC button press. This information can be provided with a
+ * call to p2p_connect().
+ */
+ void (*go_neg_req_rx)(void *ctx, const u8 *src);
+
+ /**
+ * go_neg_completed - Notification of GO Negotiation results
+ * @ctx: Callback context from cb_ctx
+ * @res: GO Negotiation results
+ *
+ * This callback is used to notify that Group Owner Negotiation has
+ * been completed. Non-zero struct p2p_go_neg_results::status indicates
+ * failed negotiation. In case of success, this function is responsible
+ * for creating a new group interface (or using the existing interface
+ * depending on driver features), setting up the group interface in
+ * proper mode based on struct p2p_go_neg_results::role_go and
+ * initializing WPS provisioning either as a Registrar (if GO) or as an
+ * Enrollee. Successful WPS provisioning must be indicated by calling
+ * p2p_wps_success_cb(). The callee is responsible for timing out group
+ * formation if WPS provisioning cannot be completed successfully
+ * within 15 seconds.
+ */
+ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+ /**
+ * sd_request - Callback on Service Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency (in MHz) of the channel
+ * @sa: Source address of the request
+ * @dialog_token: Dialog token
+ * @update_indic: Service Update Indicator from the source of request
+ * @tlvs: P2P Service Request TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * request. Response to the query must be indicated by calling
+ * p2p_sd_response() with the context information from the arguments to
+ * this callback function.
+ *
+ * This callback handler can be set to %NULL to indicate that service
+ * discovery is not supported.
+ */
+ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * sd_response - Callback on Service Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the request
+ * @update_indic: Service Update Indicator from the source of response
+ * @tlvs: P2P Service Response TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * response. This callback handler can be set to %NULL if no service
+ * discovery requests are used. The information provided with this call
+ * is replies to the queries scheduled with p2p_sd_request().
+ */
+ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * prov_disc_req - Callback on Provisiong Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the request
+ * @config_methods: Requested WPS Config Method
+ * @dev_addr: P2P Device Address of the found P2P Device
+ * @pri_dev_type: Primary Device Type
+ * @dev_name: Device Name
+ * @supp_config_methods: Supported configuration Methods
+ * @dev_capab: Device Capabilities
+ * @group_capab: Group Capabilities
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Request frame that the P2P module accepted.
+ */
+ void (*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);
+
+ /**
+ * prov_disc_resp - Callback on Provisiong Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Response frame for a pending request scheduled with
+ * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+ * provision discovery is not used.
+ */
+ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+ /**
+ * invitation_process - Optional callback for processing Invitations
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID from the request or %NULL if not included
+ * @go_dev_addr: GO Device Address from P2P Group ID
+ * @ssid: SSID from P2P Group ID
+ * @ssid_len: Length of ssid buffer in octets
+ * @go: Variable for returning whether the local end is GO in the group
+ * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+ * @force_freq: Variable for returning forced frequency for the group
+ * @persistent_group: Whether this is an invitation to reinvoke a
+ * persistent group (instead of invitation to join an active
+ * group)
+ * Returns: Status code (P2P_SC_*)
+ *
+ * This optional callback can be used to implement persistent reconnect
+ * by allowing automatic restarting of persistent groups without user
+ * interaction. If this callback is not implemented (i.e., is %NULL),
+ * the received Invitation Request frames are replied with
+ * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+ * invitation_result() callback.
+ *
+ * If the requested parameters are acceptable and the group is known,
+ * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+ * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+ * can be returned if there is not enough data to provide immediate
+ * response, i.e., if some sort of user interaction is needed. The
+ * invitation_received() callback will be called in that case
+ * immediately after this call.
+ */
+ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group);
+
+ /**
+ * invitation_received - Callback on Invitation Request RX
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @ssid: SSID of the group
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: GO Device Address
+ * @status: Response Status
+ * @op_freq: Operational frequency for the group
+ *
+ * This callback is used to indicate sending of an Invitation Response
+ * for a received Invitation Request. If status == 0 (success), the
+ * upper layer code is responsible for starting the group. status == 1
+ * indicates need to get user authorization for the group. Other status
+ * values indicate that the invitation request was rejected.
+ */
+ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq);
+
+ /**
+ * invitation_result - Callback on Invitation result
+ * @ctx: Callback context from cb_ctx
+ * @status: Negotiation result (Status Code)
+ * @bssid: P2P Group BSSID or %NULL if not received
+ *
+ * This callback is used to indicate result of an Invitation procedure
+ * started with a call to p2p_invite(). The indicated status code is
+ * the value received from the peer in Invitation Response with 0
+ * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+ * local failure in transmitting the Invitation Request.
+ */
+ void (*invitation_result)(void *ctx, int status, const u8 *bssid);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+ P2P_FIND_START_WITH_FULL,
+ P2P_FIND_ONLY_SOCIAL,
+ P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the device
+ * discoverable on the listen channel for an extended set of time. At least in
+ * its current form, this is mainly used for testing purposes and may not be of
+ * much use for normal P2P operations.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to display a PIN
+ * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us
+ * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame
+ * is transmitted once immediately and if no response is received, the frame
+ * will be sent again whenever the target device is discovered during device
+ * dsicovery (start with a p2p_find() call). Response from the peer is
+ * indicated with the p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function needs to be called whenever there is a change in availability
+ * of the local services. This will increment the Service Update Indicator
+ * value which will be used in SD Request and Response frames.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+ P2P_INVITE_ROLE_GO,
+ P2P_INVITE_ROLE_ACTIVE_GO,
+ P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+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);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is used to report failed group formation. This can happen
+ * either due to failed WPS provisioning or due to 15 second timeout during
+ * the provisioning phase.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * Returns: 0 to indicate the frame was not processed or 1 if it was
+ */
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+ size_t ie_len);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ * frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * start of a pending operation, e.g., to start a pending GO negotiation.
+ */
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ int level, const u8 *ies, size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @success: Whether the transmission succeeded
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, int success);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+ /**
+ * persistent_group - Whether the group is persistent
+ */
+ int persistent_group;
+
+ /**
+ * interface_addr - P2P Interface Address of the group
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * ie_update - Notification of IE update
+ * @ctx: Callback context from cb_ctx
+ * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+ * @proberesp_ies: P2P Ie for Probe Response frames
+ *
+ * P2P module uses this callback function to notify whenever the P2P IE
+ * in Beacon or Probe Response frames should be updated based on group
+ * events.
+ *
+ * The callee is responsible for freeing the returned buffer(s) with
+ * wpabuf_free().
+ */
+ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @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. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @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 failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @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 failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information in text format
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ */
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+ char *buf, size_t buflen);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_manageD_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr);
+
+#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
new file mode 100644
index 0000000..c45ae89
--- /dev/null
+++ b/src/p2p/p2p_build.c
@@ -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);
+}
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
new file mode 100644
index 0000000..ac2a177
--- /dev/null
+++ b/src/p2p/p2p_dev_disc.c
@@ -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);
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
new file mode 100644
index 0000000..dcd5dbd
--- /dev/null
+++ b/src/p2p/p2p_go_neg.c
@@ -0,0 +1,1064 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * 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 int p2p_go_det(u8 own_intent, u8 peer_value)
+{
+ u8 peer_intent = peer_value >> 1;
+ if (own_intent == peer_intent) {
+ if (own_intent == P2P_MAX_GO_INTENT)
+ return -1; /* both devices want to become GO */
+
+ /* Use tie breaker bit to determine GO */
+ return (peer_value & 0x01) ? 0 : 1;
+ }
+
+ return own_intent > peer_intent;
+}
+
+
+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)
+{
+ const u8 *pos, *end;
+ struct p2p_channels *ch;
+ size_t channels;
+ struct p2p_channels intersection;
+
+ ch = &dev->channels;
+ os_memset(ch, 0, sizeof(*ch));
+ pos = channel_list;
+ end = channel_list + channel_list_len;
+
+ if (end - pos < 3)
+ return -1;
+ os_memcpy(dev->country, pos, 3);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
+ if (pos[3] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Mismatching country (ours=%c%c peer's=%c%c",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ pos[0], pos[1]);
+ return -1;
+ }
+ pos += 3;
+
+ while (pos + 2 < end) {
+ struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
+ cl->reg_class = *pos++;
+ if (pos + 1 + pos[0] > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Invalid peer Channel List");
+ return -1;
+ }
+ channels = *pos++;
+ cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
+ P2P_MAX_REG_CLASS_CHANNELS : channels;
+ os_memcpy(cl->channel, pos, cl->channels);
+ pos += channels;
+ ch->reg_classes++;
+ if (ch->reg_classes == P2P_MAX_REG_CLASSES)
+ break;
+ }
+
+ p2p_channels_intersect(own, &dev->channels, &intersection);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d "
+ "peer reg_classes %d intersection reg_classes %d",
+ (int) own->reg_classes,
+ (int) dev->channels.reg_classes,
+ (int) intersection.reg_classes);
+ if (intersection.reg_classes == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: No common channels found");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len)
+{
+ return p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ channel_list, channel_list_len);
+}
+
+
+static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_LABEL:
+ return DEV_PW_DEFAULT;
+ case WPS_PIN_DISPLAY:
+ return DEV_PW_REGISTRAR_SPECIFIED;
+ case WPS_PIN_KEYPAD:
+ return DEV_PW_USER_SPECIFIED;
+ case WPS_PBC:
+ return DEV_PW_PUSHBUTTON;
+ default:
+ return DEV_PW_DEFAULT;
+ }
+}
+
+
+static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_LABEL:
+ return "Label";
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ default:
+ return "??";
+ }
+}
+
+
+static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+ struct p2p_device *peer)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+
+ 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_GO_NEG_REQ, peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ group_capab = 0;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+ p2p->next_tie_breaker);
+ p2p->next_tie_breaker = !p2p->next_tie_breaker;
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ p2p_buf_add_device_info(buf, p2p, peer);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+ return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ 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 GO Negotiation Request",
+ MAC2STR(dev->p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_go_neg_req(p2p, dev);
+ if (req == NULL)
+ return -1;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Request");
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
+ p2p->go_neg_peer = dev;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ 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;
+}
+
+
+static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ u8 tie_breaker)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Response");
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer && (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP))
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ if (peer && peer->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating "
+ "Channel attribute");
+ } else {
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ }
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ if (status || peer == NULL) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else if (peer->go_state == REMOTE_GO) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else {
+ struct p2p_channels res;
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ }
+ p2p_buf_add_device_info(buf, p2p, peer);
+ if (peer && peer->go_state == LOCAL_GO) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf,
+ p2p_wps_method_pw_id(peer ? peer->wps_method :
+ WPS_NOT_READY), 0);
+
+ return buf;
+}
+
+
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev = NULL;
+ struct wpabuf *resp;
+ struct p2p_message msg;
+ u8 status = P2P_SC_FAIL_INVALID_PARAMS;
+ int tie_breaker = 0;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Request from " MACSTR
+ "(freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (msg.go_intent)
+ tie_breaker = *msg.go_intent & 0x01;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory GO Intent attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.listen_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen Channel attribute received");
+ goto fail;
+ }
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ goto fail;
+ }
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ goto fail;
+ }
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No P2P Device Info attribute received");
+ goto fail;
+ }
+
+ if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Negotiation Request SA=" MACSTR
+ " != dev_addr=" MACSTR,
+ MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
+ goto fail;
+ }
+
+ dev = p2p_get_device(p2p, sa);
+
+ if (msg.status && *msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Status attribute (%d) in GO "
+ "Negotiation Request", *msg.status);
+ goto fail;
+ }
+
+ if (dev == NULL)
+ dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
+ else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+ p2p_add_dev_info(p2p, sa, dev, &msg);
+ if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User has rejected this peer");
+ status = P2P_SC_FAIL_REJECTED_BY_USER;
+ } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ if (dev)
+ dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+ p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa);
+ } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Already in Group Formation with another peer");
+ status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else {
+ int go;
+
+ if (!p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+ "GO Negotiation with previously authorized "
+ "peer");
+ /* TODO: check if force_freq is needed */
+ 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));
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ goto fail;
+ }
+
+ if (dev->go_neg_req_sent &&
+ os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not reply since peer has higher "
+ "address and GO Neg Request already sent");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, 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;
+ }
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_DEFAULT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Label");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_LABEL &&
+ dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection,
+ p2p->op_reg_class,
+ p2p->op_channel)) {
+ struct p2p_reg_class *cl;
+ cl = &intersection.reg_class[0];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Selected operating channel "
+ "(reg_class %u channel %u) not "
+ "acceptable to the peer - pick "
+ "another channel (reg_class %u "
+ "channel %u)",
+ p2p->op_reg_class, p2p->op_channel,
+ cl->reg_class, cl->channel[0]);
+ p2p->op_reg_class = cl->reg_class;
+ p2p->op_channel = cl->channel[0];
+ }
+
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ }
+
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+ dev->dialog_token = msg.dialog_token;
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+ p2p->go_neg_peer = dev;
+ status = P2P_SC_SUCCESS;
+ }
+
+fail:
+ resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
+ !tie_breaker);
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation 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);
+ return;
+ }
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ } else
+ p2p->pending_action_state =
+ P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
+ 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);
+}
+
+
+static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *resp_chan, int go)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ struct p2p_channels res;
+ u8 group_capab;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Confirm");
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ if (go || resp_chan == NULL)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+ resp_chan[3], resp_chan[4]);
+ p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ if (go) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct wpabuf *conf;
+ int go = -1;
+ struct p2p_message msg;
+ u8 status = P2P_SC_SUCCESS;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Response from " MACSTR
+ " (freq=%d)", MAC2STR(sa), rx_freq);
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Response - "
+ "ignore");
+ p2p_parse_free(&msg);
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ dev->go_neg_req_sent = 0;
+ if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Wait for the peer to become ready for "
+ "GO Negotiation");
+ dev->flags |= P2P_DEV_NOT_YET_READY;
+ dev->wait_count = 0;
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 0, 0);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Stop GO Negotiation attempt");
+ p2p_go_neg_failed(p2p, dev, *msg.status);
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Device Info attribute missing "
+ "from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto fail;
+ }
+
+ if (!go && msg.group_id) {
+ /* TODO: Store SSID for Provisioning step */
+ } else if (!go) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.operating_channel && !go) {
+ /*
+ * Note: P2P Client may omit Operating Channel attribute to
+ * indicate it does not have a preference.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, 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 (msg.operating_channel) {
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+ } else
+ dev->oper_freq = 0;
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_DEFAULT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Label");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_LABEL &&
+ dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ p2p->op_channel)) {
+ struct p2p_reg_class *cl;
+ cl = &intersection.reg_class[0];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Selected operating channel "
+ "(reg_class %u channel %u) not "
+ "acceptable to the peer - pick "
+ "another channel (reg_class %u "
+ "channel %u)",
+ p2p->op_reg_class, p2p->op_channel,
+ cl->reg_class, cl->channel[0]);
+ p2p->op_reg_class = cl->reg_class;
+ p2p->op_channel = cl->channel[0];
+ }
+
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ }
+
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+ conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
+ msg.operating_channel, go);
+ p2p_parse_free(&msg);
+ if (conf == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Confirm");
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ } else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = dev->listen_freq;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, sa,
+ p2p->cfg->dev_addr, sa,
+ wpabuf_head(conf), wpabuf_len(conf), 200) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ p2p_go_neg_failed(p2p, dev, -1);
+ }
+ wpabuf_free(conf);
+}
+
+
+void p2p_process_go_neg_conf(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 GO Negotiation Confirm from " MACSTR,
+ MAC2STR(sa));
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting "
+ "for TX status on GO Negotiation Response since we "
+ "already received Confirmation");
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Confirm - "
+ "ignore");
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ p2p_parse_free(&msg);
+ return;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (dev->go_state == REMOTE_GO && msg.group_id) {
+ /* TODO: Store SSID for Provisioning step */
+ } else if (dev->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ p2p_parse_free(&msg);
+
+ if (dev->go_state == UNKNOWN_GO) {
+ /*
+ * This should not happen since GO negotiation has already
+ * been completed.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Neg state - do not know which end "
+ "becomes GO");
+ return;
+ }
+
+ p2p_go_complete(p2p, dev);
+}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
new file mode 100644
index 0000000..ab0f9a1
--- /dev/null
+++ b/src/p2p/p2p_group.c
@@ -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;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
new file mode 100644
index 0000000..ccd46d3
--- /dev/null
+++ b/src/p2p/p2p_i.h
@@ -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 */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
new file mode 100644
index 0000000..89662b2
--- /dev/null
+++ b/src/p2p/p2p_invitation.c
@@ -0,0 +1,474 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ const u8 *dev_addr;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ peer->dialog_token++;
+ if (peer->dialog_token == 0)
+ peer->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
+ peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO)
+ p2p_buf_add_config_timeout(buf, 0, 0);
+ else
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ if (p2p->inv_bssid_set)
+ p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ if (go_dev_addr)
+ dev_addr = go_dev_addr;
+ else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+ dev_addr = peer->p2p_device_addr;
+ else
+ dev_addr = p2p->cfg->dev_addr;
+ p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
+ p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+ P2P_INVITATION_FLAGS_TYPE : 0);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *group_bssid,
+ u8 reg_class, u8 channel,
+ struct p2p_channels *channels)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
+ dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
+ if (reg_class && channel)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ reg_class, channel);
+ if (group_bssid)
+ p2p_buf_add_group_bssid(buf, group_bssid);
+ if (channels)
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ struct wpabuf *resp = NULL;
+ u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ int freq;
+ int go = 0;
+ u8 group_bssid[ETH_ALEN], *bssid;
+ int op_freq = 0;
+ u8 reg_class = 0, channel = 0;
+ struct p2p_channels intersection, *channels = NULL;
+ int persistent;
+
+ os_memset(group_bssid, 0, sizeof(group_bssid));
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Request from " MACSTR " (freq=%d)",
+ MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reject Invitation Request from unknown peer "
+ MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ goto fail;
+ }
+
+ if (!msg.group_id || !msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory attribute missing in Invitation "
+ "Request from " MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (msg.invitation_flags)
+ persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
+ else {
+ /* Invitation Flags is a mandatory attribute starting from P2P
+ * spec 1.06. As a backwards compatibility mechanism, assume
+ * the request was for a persistent group if the attribute is
+ * missing.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
+ "attribute missing from Invitation Request");
+ persistent = 1;
+ }
+
+ if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+ msg.channel_list, msg.channel_list_len) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (p2p->cfg->invitation_process) {
+ status = p2p->cfg->invitation_process(
+ p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
+ msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
+ &go, group_bssid, &op_freq, persistent);
+ }
+
+ if (op_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+ &reg_class, &channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown forced freq %d MHz from "
+ "invitation_process()", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (!p2p_channels_includes(&intersection, reg_class, channel))
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: forced freq %d MHz not in the supported "
+ "channels interaction", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (status == P2P_SC_SUCCESS)
+ channels = &intersection;
+ } else {
+ op_freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ if (op_freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown operational channel "
+ "(country=%c%c reg_class=%u channel=%u)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (status == P2P_SC_SUCCESS) {
+ reg_class = p2p->cfg->op_reg_class;
+ channel = p2p->cfg->op_channel;
+ channels = &intersection;
+ }
+ }
+
+fail:
+ if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
+ bssid = group_bssid;
+ else
+ bssid = NULL;
+ resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
+ bssid, reg_class, channel, channels);
+
+ if (resp == NULL)
+ goto out;
+
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ goto out;
+ }
+
+ /*
+ * Store copy of invitation data to be used when processing TX status
+ * callback for the Acton frame.
+ */
+ os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
+ if (msg.group_bssid) {
+ os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
+ p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
+ } else
+ p2p->inv_group_bssid_ptr = NULL;
+ if (msg.group_id_len - ETH_ALEN <= 32) {
+ os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+ msg.group_id_len - ETH_ALEN);
+ p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+ }
+ os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+ p2p->inv_status = status;
+ p2p->inv_op_freq = op_freq;
+
+ p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, sa,
+ p2p->cfg->dev_addr, p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+out:
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Response from " MACSTR,
+ MAC2STR(sa));
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Invitation Response from unknown peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (dev != p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected Invitation Response from peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Status attribute missing in "
+ "Invitation Response from " MACSTR, MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+ msg.group_bssid);
+
+ p2p_parse_free(&msg);
+
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->invite_peer = NULL;
+}
+
+
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send Invitation Request",
+ MAC2STR(dev->p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+ if (req == NULL)
+ return -1;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Invitation Request");
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+ p2p->invite_peer = dev;
+ dev->invitation_reqs++;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
+ dev->p2p_device_addr, p2p->cfg->dev_addr,
+ dev->p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request TX callback: success=%d", success);
+
+ if (p2p->invite_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Invite");
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Response TX callback: success=%d", success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (success && p2p->cfg->invitation_received) {
+ p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
+ p2p->inv_sa,
+ p2p->inv_group_bssid,
+ p2p->inv_ssid, p2p->inv_ssid_len,
+ p2p->inv_go_dev_addr,
+ p2p->inv_status,
+ p2p->inv_op_freq);
+ }
+}
+
+
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to invite peer " MACSTR " role=%d",
+ MAC2STR(peer), role);
+ if (bssid)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+ if (go_dev_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for GO Device Address " MACSTR,
+ MAC2STR(go_dev_addr));
+ os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
+ p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
+ } else
+ p2p->invite_go_dev_addr = NULL;
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID",
+ ssid, ssid_len);
+
+ dev = p2p_get_device(p2p, peer);
+ if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite unknown P2P Device " MACSTR,
+ MAC2STR(peer));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite a P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer));
+ }
+ /* TODO: use device discoverability request through GO */
+ }
+
+ dev->invitation_reqs = 0;
+
+ if (force_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &p2p->op_reg_class, &p2p->op_channel) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ p2p->channels.reg_classes = 1;
+ p2p->channels.reg_class[0].channels = 1;
+ p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+ p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+ } else {
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ }
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ p2p->inv_role = role;
+ p2p->inv_bssid_set = bssid != NULL;
+ if (bssid)
+ os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
+ os_memcpy(p2p->inv_ssid, ssid, ssid_len);
+ p2p->inv_ssid_len = ssid_len;
+ p2p->inv_persistent = persistent_group;
+ return p2p_invite_send(p2p, dev, go_dev_addr);
+}
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
new file mode 100644
index 0000000..07b9b51
--- /dev/null
+++ b/src/p2p/p2p_parse.c
@@ -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;
+}
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
new file mode 100644
index 0000000..2a16315
--- /dev/null
+++ b/src/p2p/p2p_pd.c
@@ -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);
+}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
new file mode 100644
index 0000000..7ac4a27
--- /dev/null
+++ b/src/p2p/p2p_sd.c
@@ -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;
+}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
new file mode 100644
index 0000000..7e8870a
--- /dev/null
+++ b/src/p2p/p2p_utils.c
@@ -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;
+}
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index a150455..cccfcc8 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -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);
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 3301e61..c113dd6 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -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
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index ab63d6f..85a8bcd 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -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)
{
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 381a432..b162d14 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -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);
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
new file mode 100644
index 0000000..16864c7
--- /dev/null
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -0,0 +1,3139 @@
+/*
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p/p2p.h"
+#include "ap/hostapd.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "mlme.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
+ (int) scan_res->num);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
+ bss->freq, bss->level,
+ (const u8 *) (bss + 1),
+ bss->ie_len) > 0)
+ return;
+ }
+
+ p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_driver_scan_params params;
+ int ret;
+ struct wpabuf *wps_ie, *ies;
+ int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+
+ os_memset(&params, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE);
+ if (wps_ie == NULL)
+ return -1;
+
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ return -1;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies);
+
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ switch (type) {
+ case P2P_SCAN_SOCIAL:
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_FULL:
+ break;
+ case P2P_SCAN_SPECIFIC:
+ social_channels[0] = freq;
+ social_channels[1] = 0;
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_SOCIAL_PLUS_ONE:
+ social_channels[3] = freq;
+ params.freqs = social_channels;
+ break;
+ }
+
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+ ret = ieee80211_sta_req_scan(wpa_s, &params);
+ else
+ ret = wpa_drv_scan(wpa_s, &params);
+
+ wpabuf_free(ies);
+
+ return ret;
+}
+
+
+#ifdef CONFIG_CLIENT_MLME
+static void p2p_rx_action_mlme(void *ctx, const u8 *buf, size_t len, int freq)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+
+ if (wpa_s->global->p2p_disabled)
+ return;
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return;
+ p2p_rx_action(wpa_s->global->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ len - hdr_len, freq);
+}
+#endif /* CONFIG_CLIENT_MLME */
+
+
+static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
+{
+ switch (p2p_group_interface) {
+ case P2P_GROUP_INTERFACE_PENDING:
+ return WPA_IF_P2P_GROUP;
+ case P2P_GROUP_INTERFACE_GO:
+ return WPA_IF_P2P_GO;
+ case P2P_GROUP_INTERFACE_CLIENT:
+ return WPA_IF_P2P_CLIENT;
+ }
+
+ return WPA_IF_P2P_GROUP;
+}
+
+
+static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ char *gtype;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ /*
+ * The current SSID was not known, but there may still be a
+ * pending P2P group interface waiting for provisioning.
+ */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->p2p_group &&
+ (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+ break;
+ ssid = ssid->next;
+ }
+ }
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
+ gtype = "GO";
+ else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+ (ssid && ssid->mode == WPAS_MODE_INFRA)) {
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ gtype = "client";
+ } else
+ gtype = "GO";
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
+ wpa_s->ifname, gtype);
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+ struct wpa_global *global;
+ char *ifname;
+ enum wpa_driver_if_type type;
+ wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
+ wpa_s->ifname);
+ global = wpa_s->global;
+ ifname = os_strdup(wpa_s->ifname);
+ type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+ wpa_supplicant_remove_iface(wpa_s->global, wpa_s);
+ wpa_s = global->ifaces;
+ if (wpa_s && ifname)
+ wpa_drv_if_remove(wpa_s, type, ifname);
+ os_free(ifname);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
+ if (ssid && (ssid->p2p_group ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
+ int id = ssid->id;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->current_ssid = NULL;
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, id);
+ wpa_supplicant_clear_status(wpa_s);
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
+ "found");
+ }
+ wpa_supplicant_ap_deinit(wpa_s);
+}
+
+
+static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
+ u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_bss *bss;
+ const u8 *bssid;
+ struct wpabuf *p2p;
+ u8 group_capab;
+ const u8 *addr;
+
+ if (wpa_s->go_params)
+ bssid = wpa_s->go_params->peer_interface_addr;
+ else
+ bssid = wpa_s->bssid;
+
+ bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+ if (bss == NULL) {
+ u8 iface_addr[ETH_ALEN];
+ if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
+ iface_addr) == 0)
+ bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
+ }
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR " not found",
+ MAC2STR(bssid));
+ return 0;
+ }
+
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR
+ " did not include P2P IE", MAC2STR(bssid));
+ wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
+ (u8 *) (bss + 1), bss->ie_len);
+ wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
+ ((u8 *) bss + 1) + bss->ie_len,
+ bss->beacon_ie_len);
+ return 0;
+ }
+
+ group_capab = p2p_get_group_capab(p2p);
+ addr = p2p_get_go_dev_addr(p2p);
+ wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
+ "group_capab=0x%x", group_capab);
+ if (addr) {
+ os_memcpy(go_dev_addr, addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
+ MAC2STR(addr));
+ } else
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ wpabuf_free(p2p);
+
+ wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
+ "go_dev_addr=" MACSTR,
+ MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
+
+ return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+}
+
+
+static void wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const u8 *go_dev_addr)
+{
+ struct wpa_ssid *s;
+ int changed = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
+ "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid->ssid_len &&
+ os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
+ break;
+ }
+
+ if (s) {
+ wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
+ "entry");
+ if (ssid->passphrase && !s->passphrase)
+ changed = 1;
+ else if (ssid->passphrase && s->passphrase &&
+ os_strcmp(ssid->passphrase, s->passphrase) != 0)
+ changed = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
+ "entry");
+ changed = 1;
+ s = wpa_config_add_network(wpa_s->conf);
+ if (s == NULL)
+ return;
+ wpa_config_set_network_defaults(s);
+ }
+
+ s->p2p_group = 1;
+ s->p2p_persistent_group = 1;
+ s->disabled = 2;
+ s->bssid_set = 1;
+ os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
+ s->mode = ssid->mode;
+ s->auth_alg = WPA_AUTH_ALG_OPEN;
+ s->key_mgmt = WPA_KEY_MGMT_PSK;
+ s->proto = WPA_PROTO_RSN;
+ s->pairwise_cipher = WPA_CIPHER_CCMP;
+ if (ssid->passphrase) {
+ os_free(s->passphrase);
+ s->passphrase = os_strdup(ssid->passphrase);
+ }
+ if (ssid->psk_set) {
+ s->psk_set = 1;
+ os_memcpy(s->psk, ssid->psk, 32);
+ }
+ if (s->passphrase && !s->psk_set)
+ wpa_config_update_psk(s);
+ if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
+ os_free(s->ssid);
+ s->ssid = os_malloc(ssid->ssid_len);
+ }
+ if (s->ssid) {
+ s->ssid_len = ssid->ssid_len;
+ os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
+ }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (changed && wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+ int success)
+{
+ struct wpa_ssid *ssid;
+ const char *ssid_txt;
+ int client;
+ int persistent;
+ u8 go_dev_addr[ETH_ALEN];
+
+ /*
+ * This callback is likely called for the main interface. Update wpa_s
+ * to use the group interface if a new interface was created for the
+ * group.
+ */
+ if (wpa_s->global->p2p_group_formation)
+ wpa_s = wpa_s->global->p2p_group_formation;
+ wpa_s->p2p_in_provisioning = 0;
+
+ if (!success) {
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE);
+ wpas_p2p_group_delete(wpa_s);
+ return;
+ }
+
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS);
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+ ssid->mode = WPAS_MODE_P2P_GO;
+ p2p_group_notif_formation_done(wpa_s->p2p_group);
+ wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
+ }
+
+ persistent = 0;
+ if (ssid) {
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ client = ssid->mode == WPAS_MODE_INFRA;
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ persistent = ssid->p2p_persistent_group;
+ os_memcpy(go_dev_addr, wpa_s->parent->own_addr,
+ ETH_ALEN);
+ } else
+ persistent = wpas_p2p_persistent_group(wpa_s,
+ go_dev_addr,
+ ssid->ssid,
+ ssid->ssid_len);
+ } else {
+ ssid_txt = "";
+ client = wpa_s->p2p_group_interface ==
+ P2P_GROUP_INTERFACE_CLIENT;
+ }
+
+ wpa_s->show_group_started = 0;
+ if (client) {
+ /*
+ * Indicate event only after successfully completed 4-way
+ * handshake, i.e., when the interface is ready for data
+ * packets.
+ */
+ wpa_s->show_group_started = 1;
+ } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" psk=%s go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+ MACSTR "%s",
+ wpa_s->ifname, ssid_txt,
+ ssid && ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ }
+
+ if (persistent)
+ wpas_p2p_store_persistent_group(wpa_s->parent, ssid,
+ go_dev_addr);
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_supplicant *iface;
+ int res;
+ int without_roc;
+
+ without_roc = wpa_s->pending_action_without_roc;
+ wpa_s->pending_action_without_roc = 0;
+
+ if (wpa_s->pending_action_tx == NULL)
+ return;
+
+ if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+ wpa_s->pending_action_freq != 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
+ "waiting for another freq=%u (off_channel_freq=%u)",
+ wpa_s->pending_action_freq,
+ wpa_s->off_channel_freq);
+ if (without_roc && wpa_s->off_channel_freq == 0) {
+ /*
+ * We may get here if wpas_send_action() found us to be
+ * on the correct channel, but remain-on-channel cancel
+ * event was received before getting here.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Schedule "
+ "remain-on-channel to send Action frame");
+ if (wpa_drv_remain_on_channel(
+ wpa_s, wpa_s->pending_action_freq, 200) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request "
+ "driver to remain on channel (%u "
+ "MHz) for Action Frame TX",
+ wpa_s->pending_action_freq);
+ }
+ }
+ return;
+ }
+
+ /*
+ * This call is likely going to be on the P2P device instance if the
+ * driver uses a separate interface for that purpose. However, some
+ * Action frames are actually sent within a P2P Group and when that is
+ * the case, we need to follow power saving (e.g., GO buffering the
+ * frame for a client in PS mode or a client following the adverised
+ * NoA from its GO). To make that easier for the driver, select the
+ * correct group interface here.
+ */
+ if (os_memcmp(wpa_s->pending_action_src, wpa_s->own_addr, ETH_ALEN) !=
+ 0) {
+ /*
+ * Try to find a group interface that matches with the source
+ * address.
+ */
+ iface = wpa_s->global->ifaces;
+ while (iface) {
+ if (os_memcmp(wpa_s->pending_action_src,
+ iface->own_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (iface) {
+ wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+ "instead of interface %s for Action TX",
+ iface->ifname, wpa_s->ifname);
+ } else
+ iface = wpa_s;
+ } else
+ iface = wpa_s;
+
+ wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
+ MACSTR " using interface %s",
+ MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+ res = wpa_drv_send_action(iface, wpa_s->pending_action_freq,
+ wpa_s->pending_action_dst,
+ wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx));
+ if (res) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to send the pending "
+ "Action frame");
+ /*
+ * Use fake TX status event to allow P2P state machine to
+ * continue.
+ */
+ wpas_send_action_tx_status(
+ wpa_s, wpa_s->pending_action_dst,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx), 0);
+ }
+}
+
+
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const u8 *data, size_t data_len, int ack)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - no "
+ "pending operation");
+ return;
+ }
+
+ if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - unknown "
+ "destination address");
+ return;
+ }
+
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+
+ p2p_send_action_cb(wpa_s->global->p2p, wpa_s->pending_action_freq,
+ wpa_s->pending_action_dst,
+ wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ ack);
+
+ if (wpa_s->pending_pd_before_join &&
+ (os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr,
+ ETH_ALEN) == 0 ||
+ os_memcmp(wpa_s->pending_action_dst,
+ wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation");
+ wpas_p2p_join_start(wpa_s);
+ }
+}
+
+
+static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "P2P: Send action frame: freq=%d dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR,
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
+
+ if (wpa_s->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
+ "to " MACSTR, MAC2STR(wpa_s->pending_action_dst));
+ wpabuf_free(wpa_s->pending_action_tx);
+ }
+ wpa_s->pending_action_tx = wpabuf_alloc(len);
+ if (wpa_s->pending_action_tx == NULL)
+ return -1;
+ wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+ os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+ wpa_s->pending_action_freq = freq;
+
+ if (wpa_s->off_channel_freq == freq || freq == 0) {
+ /* Already on requested channel; send immediately */
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ wpa_s->pending_action_without_roc = 1;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+ eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+ return 0;
+ }
+ wpa_s->pending_action_without_roc = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
+ "once the driver gets to the requested channel");
+ if (wait_time > wpa_s->max_remain_on_chan)
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_send_action_done(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification");
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ if (wpa_s->off_channel_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ }
+}
+
+
+static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params)
+{
+ if (wpa_s->go_params == NULL) {
+ wpa_s->go_params = os_malloc(sizeof(*params));
+ if (wpa_s->go_params == NULL)
+ return -1;
+ }
+ os_memcpy(wpa_s->go_params, params, sizeof(*params));
+ return 0;
+}
+
+
+static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpa_supplicant_ap_deinit(wpa_s);
+ wpas_copy_go_neg_results(wpa_s, res);
+ if (res->wps_method == WPS_PBC)
+ wpas_wps_start_pbc(wpa_s, NULL /* res->peer_interface_addr */,
+ 1);
+ else
+ wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
+ wpa_s->p2p_pin, 1);
+}
+
+
+static void p2p_go_configured(void *ctx, void *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct p2p_go_neg_results *params = data;
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
+ wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+ MACSTR "%s",
+ wpa_s->ifname,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ params->passphrase ? params->passphrase : "",
+ MAC2STR(wpa_s->parent->own_addr),
+ params->persistent_group ? " [PERSISTENT]" : "");
+ if (params->persistent_group)
+ wpas_p2p_store_persistent_group(
+ wpa_s->parent, ssid,
+ wpa_s->parent->own_addr);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
+ if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
+ params->peer_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
+ "filtering");
+ return;
+ }
+ if (params->wps_method == WPS_PBC)
+ wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr);
+ else if (wpa_s->p2p_pin[0])
+ wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
+ wpa_s->p2p_pin, NULL, 0);
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+}
+
+
+static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int group_formation)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+ return;
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return;
+
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->p2p_group = 1;
+ ssid->p2p_persistent_group = params->persistent_group;
+ ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
+ WPAS_MODE_P2P_GO;
+ ssid->frequency = params->freq;
+ ssid->ssid = os_zalloc(params->ssid_len + 1);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ }
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->passphrase = os_strdup(params->passphrase);
+
+ wpa_s->ap_configured_cb = p2p_go_configured;
+ wpa_s->ap_configured_cb_ctx = wpa_s;
+ wpa_s->ap_configured_cb_data = wpa_s->go_params;
+ wpa_s->connect_without_scan = 1;
+ wpa_s->reassociate = 1;
+ wpa_s->disconnected = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
+ const struct wpa_supplicant *src)
+{
+ struct wpa_config *d;
+ const struct wpa_config *s;
+
+ d = dst->conf;
+ s = src->conf;
+
+#define C(n) if (s->n) d->n = os_strdup(s->n)
+ C(device_name);
+ C(manufacturer);
+ C(model_name);
+ C(model_number);
+ C(serial_number);
+ C(device_type);
+ C(config_methods);
+#undef C
+}
+
+
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type)
+{
+ char ifname[120], force_ifname[120];
+
+ if (wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
+ "- skip creation of a new one");
+ if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
+ "unknown?! ifname='%s'",
+ wpa_s->pending_interface_name);
+ return -1;
+ }
+ return 0;
+ }
+
+ os_snprintf(ifname, sizeof(ifname), "%s-p2p-%d", wpa_s->ifname,
+ wpa_s->p2p_group_idx);
+ force_ifname[0] = '\0';
+
+ wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
+ ifname);
+ wpa_s->p2p_group_idx++;
+
+ wpa_s->pending_interface_type = type;
+ if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
+ wpa_s->pending_interface_addr) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
+ "interface");
+ return -1;
+ }
+
+ if (force_ifname[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
+ force_ifname);
+ os_strlcpy(wpa_s->pending_interface_name, force_ifname,
+ sizeof(wpa_s->pending_interface_name));
+ } else
+ os_strlcpy(wpa_s->pending_interface_name, ifname,
+ sizeof(wpa_s->pending_interface_name));
+ wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
+ MACSTR, wpa_s->pending_interface_name,
+ MAC2STR(wpa_s->pending_interface_addr));
+
+ return 0;
+}
+
+
+static void wpas_p2p_remove_pending_group_interface(
+ struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->pending_interface_name[0] ||
+ is_zero_ether_addr(wpa_s->pending_interface_addr))
+ return; /* No pending virtual interface */
+
+ wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
+ wpa_s->pending_interface_name);
+ wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
+ wpa_s->pending_interface_name);
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
+{
+ struct wpa_interface iface;
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_ERROR, "P2P: No pending group interface");
+ return NULL;
+ }
+
+ os_memset(&iface, 0, sizeof(iface));
+ iface.ifname = wpa_s->pending_interface_name;
+ iface.driver = wpa_s->driver->name;
+ iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+ iface.driver_param = wpa_s->conf->driver_param;
+ group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+ if (group_wpa_s == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+ "wpa_supplicant interface");
+ return NULL;
+ }
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->parent = wpa_s;
+ group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+ P2P_GROUP_INTERFACE_CLIENT;
+ wpa_s->global->p2p_group_formation = group_wpa_s;
+
+ wpas_p2p_clone_config(group_wpa_s, wpa_s);
+
+ return group_wpa_s;
+}
+
+
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+ if (wpa_s->global->p2p)
+ p2p_group_formation_failed(wpa_s->global->p2p);
+ wpas_group_formation_completed(wpa_s, 0);
+}
+
+
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->off_channel_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ }
+
+ if (res->status) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d",
+ res->status);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+
+ if (wpa_s->create_p2p_iface) {
+ struct wpa_supplicant *group_wpa_s =
+ wpas_p2p_init_group_interface(wpa_s, res->role_go);
+ if (group_wpa_s == NULL) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+ if (group_wpa_s != wpa_s)
+ os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group_wpa_s->p2p_pin));
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->p2p_in_provisioning = 1;
+
+ if (res->role_go)
+ wpas_start_wps_go(group_wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(group_wpa_s, res);
+ } else {
+ wpa_s->p2p_in_provisioning = 1;
+ wpa_s->global->p2p_group_formation = wpa_s;
+
+ if (res->role_go)
+ wpas_start_wps_go(wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(ctx, res);
+ }
+
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ /* TODO: add peer Config Timeout */
+ eloop_register_timeout(15, 0, wpas_p2p_group_formation_timeout, wpa_s,
+ NULL);
+}
+
+
+void wpas_go_neg_req_rx(void *ctx, const u8 *src)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR,
+ MAC2STR(src));
+}
+
+
+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)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+ " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x",
+ MAC2STR(addr), MAC2STR(dev_addr),
+ wps_dev_type_bin2str(pri_dev_type, devtype, sizeof(devtype)),
+ dev_name, config_methods, dev_capab, group_capab);
+}
+
+
+static int wpas_start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie);
+
+ if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+ "report received Probe Request frames");
+ return -1;
+ }
+
+ wpa_s->pending_listen_freq = freq;
+ wpa_s->pending_listen_duration = duration;
+
+ if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+ "to remain on channel (%u MHz) for Listen "
+ "state", freq);
+ wpa_s->pending_listen_freq = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_stop_listen(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->off_channel_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ }
+ wpa_drv_probe_req_report(wpa_s, 0);
+}
+
+
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+ size_t len;
+
+ len = wpabuf_len(query);
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (len == wpabuf_len(bsrv->query) &&
+ os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+ len) == 0)
+ return bsrv;
+ }
+ return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version == usrv->version &&
+ os_strcmp(service, usrv->service) == 0)
+ return usrv;
+ }
+ return NULL;
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ u8 *len_pos;
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, srv_proto);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (wpabuf_tailroom(resp) <
+ 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_bonjour *bsrv;
+ struct wpabuf buf;
+ u8 *len_pos;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+ query, query_len);
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ wpabuf_set(&buf, query, query_len);
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf);
+ if (bsrv == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+ "available");
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ return;
+ }
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp));
+
+ if (wpabuf_tailroom(resp) >=
+ wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) {
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ }
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ return;
+ }
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, usrv->version);
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ wpabuf_put_str(resp, usrv->service);
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+ u8 version;
+ char *str;
+ int count = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+ query, query_len);
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ version = query[0];
+ str = os_malloc(query_len);
+ if (str == NULL)
+ return;
+ os_memcpy(str, query + 1, query_len - 1);
+ str[query_len - 1] = '\0';
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version != usrv->version)
+ continue;
+
+ if (os_strcmp(str, "ssdp:all") != 0 &&
+ os_strstr(usrv->service, str) == NULL)
+ continue;
+
+ if (wpabuf_tailroom(resp) < 2)
+ break;
+ if (count == 0) {
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, version);
+ } else
+ wpabuf_put_u8(resp, ',');
+
+ count++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+ break;
+ wpabuf_put_str(resp, usrv->service);
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+ "available");
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+ /* Response Data: empty */
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ struct wpabuf *resp;
+ u8 srv_proto, srv_trans_id;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+ tlvs, tlvs_len);
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+ MACSTR " %u %u %s",
+ freq, MAC2STR(sa), dialog_token, update_indic,
+ buf);
+ os_free(buf);
+ }
+
+ if (wpa_s->p2p_sd_over_ctrl_iface)
+ return; /* to be processed by an external program */
+
+ resp = wpabuf_alloc(10000);
+ if (resp == NULL)
+ return;
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+ "length");
+ wpabuf_free(resp);
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+ pos, tlv_end - pos);
+
+ switch (srv_proto) {
+ case P2P_SERV_ALL_SERVICES:
+ wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+ "for all services");
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+ dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: No service "
+ "discovery protocols available");
+ wpas_sd_add_proto_not_avail(
+ resp, P2P_SERV_ALL_SERVICES,
+ srv_trans_id);
+ break;
+ }
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ break;
+ case P2P_SERV_BONJOUR:
+ wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ case P2P_SERV_UPNP:
+ wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+ "protocol %u", srv_proto);
+ wpas_sd_add_proto_not_avail(resp, srv_proto,
+ srv_trans_id);
+ break;
+ }
+
+ pos = tlv_end;
+ }
+
+ wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+ wpabuf_free(resp);
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+ tlvs, tlvs_len);
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_RESP MACSTR
+ " %u %s",
+ MAC2STR(sa), update_indic, buf);
+ os_free(buf);
+ }
+
+ while (pos < end) {
+ u8 srv_proto, srv_trans_id, status;
+
+ wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+ "length");
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+ status = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+ status);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+ pos, tlv_end - pos);
+
+ pos = tlv_end;
+ }
+}
+
+
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ return p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query)
+{
+ struct wpabuf *tlvs;
+ void *ret;
+
+ tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+ if (tlvs == NULL)
+ return NULL;
+ wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+ wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+ wpabuf_put_u8(tlvs, version);
+ wpabuf_put_str(tlvs, query);
+ ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req)
+{
+ return p2p_sd_cancel_request(wpa_s->global->p2p, req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+ resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+ p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+ dl_list_del(&bsrv->list);
+ wpabuf_free(bsrv->query);
+ wpabuf_free(bsrv->resp);
+ os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+ dl_list_del(&usrv->list);
+ os_free(usrv->service);
+ os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_srv_bonjour *bsrv, *bn;
+ struct p2p_srv_upnp *usrv, *un;
+
+ dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list)
+ wpas_p2p_srv_bonjour_free(bsrv);
+
+ dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list)
+ wpas_p2p_srv_upnp_free(usrv);
+
+ wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv) {
+ wpabuf_free(query);
+ wpabuf_free(bsrv->resp);
+ bsrv->resp = resp;
+ return 0;
+ }
+
+ bsrv = os_zalloc(sizeof(*bsrv));
+ if (bsrv == NULL)
+ return -1;
+ bsrv->query = query;
+ bsrv->resp = resp;
+ dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv == NULL)
+ return -1;
+ wpas_p2p_srv_bonjour_free(bsrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+ return 0; /* Already listed */
+ usrv = os_zalloc(sizeof(*usrv));
+ if (usrv == NULL)
+ return -1;
+ usrv->version = version;
+ usrv->service = os_strdup(service);
+ if (usrv->service == NULL) {
+ os_free(usrv);
+ return -1;
+ }
+ dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+ if (usrv == NULL)
+ return -1;
+ wpas_p2p_srv_upnp_free(usrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s",
+ MAC2STR(peer), wps_generate_pin(), params);
+}
+
+
+static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s",
+ MAC2STR(peer), params);
+}
+
+
+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)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ char params[200];
+ u8 empty_dev_type[8];
+
+ if (pri_dev_type == NULL) {
+ os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
+ pri_dev_type = empty_dev_type;
+ }
+ os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x",
+ MAC2STR(dev_addr),
+ wps_dev_type_bin2str(pri_dev_type, devtype,
+ sizeof(devtype)),
+ dev_name, supp_config_methods, dev_capab, group_capab);
+ params[sizeof(params) - 1] = '\0';
+
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ wpas_prov_disc_local_display(wpa_s, peer, params);
+ else if (config_methods & WPS_CONFIG_KEYPAD)
+ wpas_prov_disc_local_keypad(wpa_s, peer, params);
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR
+ "%s", MAC2STR(peer), params);
+}
+
+
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ wpas_prov_disc_local_keypad(wpa_s, peer, "");
+ else if (config_methods & WPS_CONFIG_KEYPAD)
+ wpas_prov_disc_local_display(wpa_s, peer, "");
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
+ MAC2STR(peer));
+}
+
+
+static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+ u8 cur_bssid[ETH_ALEN];
+ int res;
+
+ if (!persistent_group) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " to join an active group", MAC2STR(sa));
+ /*
+ * Do not accept the invitation automatically; notify user and
+ * request approval.
+ */
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ }
+
+ if (!wpa_s->conf->persistent_reconnect)
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (!s) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " requested reinvocation of an unknown group",
+ MAC2STR(sa));
+ return P2P_SC_FAIL_UNKNOWN_GROUP;
+ }
+
+ if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
+ *go = 1;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_printf(MSG_DEBUG, "P2P: The only available "
+ "interface is already in use - reject "
+ "invitation");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+ } else if (s->mode == WPAS_MODE_P2P_GO) {
+ *go = 1;
+ if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
+ {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface address for the group");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->pending_interface_addr,
+ ETH_ALEN);
+ }
+
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
+ wpa_s->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "the channel we are already using");
+ *force_freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "with the channel we are already using on a "
+ "shared interface");
+ *force_freq = res;
+ }
+
+ return P2P_SC_SUCCESS;
+}
+
+
+static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (status == P2P_SC_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was accepted; op_freq=%d MHz",
+ MAC2STR(sa), op_freq);
+ if (s) {
+ wpas_p2p_group_add_persistent(
+ wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+ }
+ return;
+ }
+
+ if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was rejected (status %u)", MAC2STR(sa), status);
+ return;
+ }
+
+ if (!s) {
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " bssid=" MACSTR " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr),
+ MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr));
+ }
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR
+ " persistent=%d", MAC2STR(sa), s->id);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid;
+
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d " MACSTR,
+ status, MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d ", status);
+ }
+
+ if (status != P2P_SC_SUCCESS) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf,
+ wpa_s->pending_invite_ssid_id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
+ "data matching with invitation");
+ return;
+ }
+
+ wpas_p2p_group_add_persistent(wpa_s, ssid,
+ ssid->mode == WPAS_MODE_P2P_GO, 0);
+}
+
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+ struct p2p_config *p2p)
+{
+ struct hostapd_hw_modes *modes;
+ u16 num_modes, flags;
+ int i, cla;
+ int band24 = 0, band5_low = 0, band5_high = 0;
+
+ /* TODO: more detailed selection of channels within reg_class based on
+ * driver capabilities */
+
+ modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, &flags);
+ if (modes == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
+ "of all supported channels; assume dualband "
+ "support");
+ band24 = band5_low = band5_high = 1;
+ } else {
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *mode;
+ mode = &modes[i];
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G) {
+ band24 = 1;
+ } else if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+ int j;
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *ch;
+ ch = &mode->channels[j];
+ if (ch->chan == 36)
+ band5_low = 1;
+ else if (ch->chan == 157)
+ band5_high = 1;
+ }
+ }
+ }
+ }
+
+ cla = 0;
+
+ if (band24) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+ "2.4 GHz band");
+
+ /* Operating class 81 - 2.4 GHz band channels 1..13 */
+ p2p->channels.reg_class[cla].reg_class = 81;
+ p2p->channels.reg_class[cla].channels = 13;
+ for (i = 0; i < 13; i++)
+ p2p->channels.reg_class[cla].channel[i] = i + 1;
+ cla++;
+
+ /* Operating class 82 - 2.4 GHz band channel 14 */
+ p2p->channels.reg_class[cla].reg_class = 82;
+ p2p->channels.reg_class[cla].channels = 1;
+ p2p->channels.reg_class[cla].channel[0] = 14;
+ cla++;
+
+#if 0
+ /* Operating class 83 - 2.4 GHz band channels 1..9; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 83;
+ p2p->channels.reg_class[cla].channels = 9;
+ for (i = 0; i < 9; i++)
+ p2p->channels.reg_class[cla].channel[i] = i + 1;
+ cla++;
+
+ /* Operating class 84 - 2.4 GHz band channels 5..13; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 84;
+ p2p->channels.reg_class[cla].channels = 9;
+ for (i = 0; i < 9; i++)
+ p2p->channels.reg_class[cla].channel[i] = i + 5;
+ cla++;
+#endif
+ }
+
+ if (band5_low) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+ "lower 5 GHz band");
+
+ /* Operating class 115 - 5 GHz, channels 36-48 */
+ p2p->channels.reg_class[cla].reg_class = 115;
+ p2p->channels.reg_class[cla].channels = 4;
+ p2p->channels.reg_class[cla].channel[0] = 36;
+ p2p->channels.reg_class[cla].channel[1] = 40;
+ p2p->channels.reg_class[cla].channel[2] = 44;
+ p2p->channels.reg_class[cla].channel[3] = 48;
+ cla++;
+
+#if 0
+ /* Operating class 116 - 5 GHz, channels 36,44; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 116;
+ p2p->channels.reg_class[cla].channels = 2;
+ p2p->channels.reg_class[cla].channel[0] = 36;
+ p2p->channels.reg_class[cla].channel[1] = 44;
+ cla++;
+
+ /* Operating class 117 - 5 GHz, channels 40,48; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 117;
+ p2p->channels.reg_class[cla].channels = 2;
+ p2p->channels.reg_class[cla].channel[0] = 40;
+ p2p->channels.reg_class[cla].channel[1] = 48;
+ cla++;
+#endif
+ }
+
+ if (band5_high) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+ "higher 5 GHz band");
+
+ /* Operating class 124 - 5 GHz, channels 149,153,157,161 */
+ p2p->channels.reg_class[cla].reg_class = 124;
+ p2p->channels.reg_class[cla].channels = 4;
+ p2p->channels.reg_class[cla].channel[0] = 149;
+ p2p->channels.reg_class[cla].channel[1] = 153;
+ p2p->channels.reg_class[cla].channel[2] = 157;
+ p2p->channels.reg_class[cla].channel[3] = 161;
+ cla++;
+
+#if 0
+ /* Operating class 126 - 5 GHz, channels 149,157; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 126;
+ p2p->channels.reg_class[cla].channels = 2;
+ p2p->channels.reg_class[cla].channel[0] = 149;
+ p2p->channels.reg_class[cla].channel[1] = 157;
+ cla++;
+
+ /* Operating class 127 - 5 GHz, channels 153,161; 40 MHz */
+ p2p->channels.reg_class[cla].reg_class = 127;
+ p2p->channels.reg_class[cla].channels = 2;
+ p2p->channels.reg_class[cla].channel[0] = 153;
+ p2p->channels.reg_class[cla].channel[1] = 161;
+ cla++;
+#endif
+ }
+
+ p2p->channels.reg_classes = cla;
+
+ if (modes)
+ ieee80211_sta_free_hw_features(modes, num_modes);
+
+ return 0;
+}
+
+
+static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (wpa_s == NULL)
+ return -1;
+
+ return wpa_drv_get_noa(wpa_s, buf, buf_len);
+}
+
+
+/**
+ * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+ struct p2p_config p2p;
+ unsigned int r;
+ int i;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+ return 0;
+
+#ifdef CONFIG_CLIENT_MLME
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) {
+ wpa_s->mlme.public_action_cb = p2p_rx_action_mlme;
+ wpa_s->mlme.public_action_cb_ctx = wpa_s;
+ }
+#endif /* CONFIG_CLIENT_MLME */
+
+ if (wpa_drv_disable_11b_rates(wpa_s, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to disable 11b rates");
+ /* Continue anyway; this is not really a fatal error */
+ }
+
+ if (global->p2p)
+ return 0;
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.msg_ctx = wpa_s;
+ p2p.cb_ctx = wpa_s;
+ p2p.p2p_scan = wpas_p2p_scan;
+ p2p.send_action = wpas_send_action;
+ p2p.send_action_done = wpas_send_action_done;
+ p2p.go_neg_completed = wpas_go_neg_completed;
+ p2p.go_neg_req_rx = wpas_go_neg_req_rx;
+ p2p.dev_found = wpas_dev_found;
+ p2p.start_listen = wpas_start_listen;
+ p2p.stop_listen = wpas_stop_listen;
+ p2p.send_probe_resp = wpas_send_probe_resp;
+ p2p.sd_request = wpas_sd_request;
+ p2p.sd_response = wpas_sd_response;
+ p2p.prov_disc_req = wpas_prov_disc_req;
+ p2p.prov_disc_resp = wpas_prov_disc_resp;
+ p2p.invitation_process = wpas_invitation_process;
+ p2p.invitation_received = wpas_invitation_received;
+ p2p.invitation_result = wpas_invitation_result;
+ p2p.get_noa = wpas_get_noa;
+
+ os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(p2p.dev_addr, wpa_s->own_addr, ETH_ALEN);
+ p2p.dev_name = wpa_s->conf->device_name;
+
+ if (wpa_s->conf->p2p_listen_reg_class &&
+ wpa_s->conf->p2p_listen_channel) {
+ p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
+ p2p.channel = wpa_s->conf->p2p_listen_channel;
+ } else {
+ p2p.reg_class = 81;
+ /*
+ * Pick one of the social channels randomly as the listen
+ * channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.channel = 1 + (r % 3) * 5;
+ }
+
+ if (wpa_s->conf->p2p_oper_reg_class &&
+ wpa_s->conf->p2p_oper_channel) {
+ p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+ p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+ } else {
+ p2p.op_reg_class = 81;
+ /*
+ * For initial tests, pick the operation channel randomly.
+ * TODO: Use scan results (etc.) to select the best channel.
+ */
+ p2p.op_channel = 1 + r % 11;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d "
+ "Own preferred operation channel: %d",
+ p2p.channel, p2p.op_channel);
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ os_memcpy(p2p.country, wpa_s->conf->country, 2);
+ p2p.country[2] = 0x04;
+ } else
+ os_memcpy(p2p.country, "US\x04", 3);
+
+ if (wpas_p2p_setup_channels(wpa_s, &p2p)) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
+ "channel list");
+ return -1;
+ }
+
+ if (wpa_s->conf->device_type &&
+ wps_dev_type_str2bin(wpa_s->conf->device_type, p2p.pri_dev_type) <
+ 0) {
+ wpa_printf(MSG_ERROR, "P2P: Invalid device_type");
+ return -1;
+ }
+
+ for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
+ if (wpa_s->conf->sec_device_type[i] == NULL)
+ continue;
+ if (wps_dev_type_str2bin(
+ wpa_s->conf->sec_device_type[i],
+ p2p.sec_dev_type[p2p.num_sec_dev_types]) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Invalid sec_device_type");
+ return -1;
+ }
+ p2p.num_sec_dev_types++;
+ if (p2p.num_sec_dev_types == P2P_SEC_DEVICE_TYPES)
+ break;
+ }
+
+ p2p.concurrent_operations = !!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_CONCURRENT);
+
+ p2p.max_peers = 100;
+
+ if (wpa_s->conf->p2p_ssid_postfix) {
+ p2p.ssid_postfix_len =
+ os_strlen(wpa_s->conf->p2p_ssid_postfix);
+ if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
+ p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
+ os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
+ p2p.ssid_postfix_len);
+ }
+
+ global->p2p = p2p_init(&p2p);
+ if (global->p2p == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_deinit - Deinitialize per-interface P2P data
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ *
+ * This function deinitialize per-interface P2P data.
+ */
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver && wpa_s->drv_priv)
+ wpa_drv_probe_req_report(wpa_s, 0);
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+
+ /* TODO: remove group interface from the driver if this wpa_s instance
+ * is on top of a P2P group interface */
+}
+
+
+/**
+ * wpas_p2p_deinit_global - Deinitialize global P2P module
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function deinitializes the global (per device) P2P module.
+ */
+void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s, *tmp;
+ char *ifname;
+
+ if (global->p2p == NULL)
+ return;
+
+ /* Remove remaining P2P group interfaces */
+ wpa_s = global->ifaces;
+ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+ wpa_s = wpa_s->next;
+ while (wpa_s) {
+ enum wpa_driver_if_type type;
+ tmp = global->ifaces;
+ while (tmp &&
+ (tmp == wpa_s ||
+ tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+ tmp = tmp->next;
+ }
+ if (tmp == NULL)
+ break;
+ ifname = os_strdup(tmp->ifname);
+ type = wpas_p2p_if_type(tmp->p2p_group_interface);
+ wpa_supplicant_remove_iface(global, tmp);
+ if (ifname)
+ wpa_drv_if_remove(wpa_s, type, ifname);
+ os_free(ifname);
+ }
+
+ p2p_deinit(global->p2p);
+ global->p2p = NULL;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+ return 1; /* P2P group requires a new interface in every case
+ */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
+ return 0; /* driver does not support concurrent operations */
+ if (wpa_s->global->ifaces->next)
+ return 1; /* more that one interface already in use */
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ return 1; /* this interface is already in use */
+ return 0;
+}
+
+
+static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group);
+}
+
+
+static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group);
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+ struct wpa_bss *bss;
+
+ wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+ MACSTR " dev " MACSTR ")",
+ MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+ os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+ wpa_s->pending_join_wps_method = wps_method;
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find(wpa_s);
+
+ bss = wpa_bss_get_bssid(wpa_s, iface_addr);
+ if (bss) {
+ u16 method;
+
+ wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
+ "prior to joining an existing group (GO " MACSTR
+ " freq=%u MHz)",
+ MAC2STR(dev_addr), bss->freq);
+ wpa_s->pending_pd_before_join = 1;
+
+ switch (wps_method) {
+ case WPS_PIN_LABEL:
+ case WPS_PIN_DISPLAY:
+ method = WPS_CONFIG_KEYPAD;
+ break;
+ case WPS_PIN_KEYPAD:
+ method = WPS_CONFIG_DISPLAY;
+ break;
+ case WPS_PBC:
+ method = WPS_CONFIG_PUSHBUTTON;
+ break;
+ default:
+ method = 0;
+ break;
+ }
+
+ if (p2p_prov_disc_req(wpa_s->global->p2p, dev_addr, method, 1)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
+ "Discovery Request before joining an "
+ "existing group");
+ wpa_s->pending_pd_before_join = 0;
+ goto start;
+ }
+
+ /*
+ * Actual join operation will be started from the Action frame
+ * TX status callback.
+ */
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Target BSS/GO not yet in BSS table - "
+ "cannot send Provision Discovery Request");
+
+start:
+ /* Start join operation immediately */
+ return wpas_p2p_join_start(wpa_s);
+}
+
+
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *group;
+ struct p2p_go_neg_results res;
+
+ group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+ if (group == NULL)
+ return -1;
+ if (group != wpa_s)
+ os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group->p2p_pin));
+
+ group->p2p_in_provisioning = 1;
+
+ os_memset(&res, 0, sizeof(res));
+ os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
+ ETH_ALEN);
+ res.wps_method = wpa_s->pending_join_wps_method;
+ wpas_start_wps_enrollee(group, &res);
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_connect - Request P2P Group Formation to be started
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @peer_addr: Address of the peer P2P Device
+ * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
+ * @persistent_group: Whether to create a persistent group
+ * @join: Whether to join an existing group (as a client) instead of starting
+ * Group Owner negotiation; @peer_addr is BSSID in that case
+ * @auth: Whether to only authorize the connection instead of doing that and
+ * initiating Group Owner negotiation
+ * @go_intent: GO Intent or -1 to use default
+ * @freq: Frequency for the group or 0 for auto-selection
+ * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on failure
+ */
+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)
+{
+ int force_freq = 0;
+ u8 bssid[ETH_ALEN];
+ int ret = 0;
+ enum wpa_driver_if_type iftype;
+
+ if (go_intent < 0)
+ go_intent = wpa_s->conf->p2p_go_intent;
+
+ if (!auth)
+ wpa_s->p2p_long_listen = 0;
+
+ if (pin)
+ os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
+ else if (wps_method == WPS_PIN_DISPLAY) {
+ ret = wps_generate_pin();
+ os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
+ ret);
+ wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+ wpa_s->p2p_pin);
+ } else
+ wpa_s->p2p_pin[0] = '\0';
+
+ if (join) {
+ u8 iface_addr[ETH_ALEN];
+ if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
+ iface_addr) < 0)
+ os_memcpy(iface_addr, peer_addr, ETH_ALEN);
+ if (wpas_p2p_join(wpa_s, iface_addr, peer_addr, wps_method) <
+ 0)
+ return -1;
+ return ret;
+ }
+
+ if (freq > 0)
+ force_freq = freq;
+ else if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq)
+ force_freq = wpa_s->assoc_freq;
+ else {
+ force_freq = wpa_drv_shared_freq(wpa_s);
+ if (force_freq < 0)
+ force_freq = 0;
+ }
+
+ if (force_freq > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+ "channel we are already using (%u MHz) on another "
+ "interface", force_freq);
+ }
+
+ wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+ if (!wpa_s->create_p2p_iface) {
+ if (auth) {
+ if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, wpa_s->own_addr,
+ force_freq, persistent_group)
+ < 0)
+ return -1;
+ return ret;
+ }
+ if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, wpa_s->own_addr,
+ force_freq, persistent_group) < 0)
+ return -1;
+ return ret;
+ }
+
+ /* Prepare to add a new interface for the group */
+ iftype = WPA_IF_P2P_GROUP;
+ if (join)
+ iftype = WPA_IF_P2P_CLIENT;
+ else if (go_intent == 15)
+ iftype = WPA_IF_P2P_GO;
+ if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface for the group");
+ return -1;
+ }
+
+ if (auth) {
+ if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent,
+ wpa_s->pending_interface_addr,
+ force_freq, persistent_group) < 0)
+ return -1;
+ return ret;
+ }
+ if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent,
+ wpa_s->pending_interface_addr,
+ force_freq, persistent_group) < 0) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return -1;
+ }
+ return ret;
+}
+
+
+/**
+ * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ * @duration: Duration of the stay on the channel in milliseconds
+ *
+ * This callback is called when the driver indicates that it has started the
+ * requested remain-on-channel duration.
+ */
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ wpa_s->off_channel_freq = freq;
+ wpas_send_action_cb(wpa_s, NULL);
+ if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+ p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
+ wpa_s->pending_listen_duration);
+ wpa_s->pending_listen_freq = 0;
+ }
+}
+
+
+static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
+ unsigned int timeout)
+{
+ /* Limit maximum Listen state time based on driver limitation. */
+ if (timeout > wpa_s->max_remain_on_chan)
+ timeout = wpa_s->max_remain_on_chan;
+
+ return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ *
+ * This callback is called when the driver indicates that a remain-on-channel
+ * operation has been completed, i.e., the duration on the requested channel
+ * has timed out.
+ */
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_s->off_channel_freq = 0;
+ if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
+ return; /* P2P module started a new operation */
+ if (wpa_s->pending_action_tx)
+ return;
+ if (wpa_s->p2p_long_listen > 0)
+ wpa_s->p2p_long_listen -= 5;
+ if (wpa_s->p2p_long_listen > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+ wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen * 1000);
+ }
+}
+
+
+/**
+ * wpas_p2p_group_remove - Remove a P2P group
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @ifname: Network interface name of the group interface or "*" to remove all
+ * groups
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to remove a P2P group. This can be used to disconnect
+ * from a group in which the local end is a P2P Client or to end a P2P Group in
+ * case the local end is the Group Owner. If a virtual network interface was
+ * created for this group, that interface will be removed. Otherwise, only the
+ * configured P2P group network will be removed from the interface.
+ */
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (os_strcmp(ifname, "*") == 0) {
+ struct wpa_supplicant *prev;
+ wpa_s = global->ifaces;
+ while (wpa_s) {
+ prev = wpa_s;
+ wpa_s = wpa_s->next;
+ wpas_p2p_group_delete(prev);
+ }
+ return 0;
+ }
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+
+ if (wpa_s == NULL)
+ return -1;
+
+ wpas_p2p_group_delete(wpa_s);
+
+ return 0;
+}
+
+
+static void wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int freq)
+{
+ u8 bssid[ETH_ALEN];
+ int res;
+
+ os_memset(params, 0, sizeof(*params));
+ params->role_go = 1;
+ params->freq = 2412;
+ if (freq)
+ params->freq = freq;
+ else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+ wpa_s->conf->p2p_oper_channel >= 1 &&
+ wpa_s->conf->p2p_oper_channel <= 11)
+ params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+ else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
+ wpa_s->conf->p2p_oper_reg_class == 118)
+ params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using");
+ params->freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0 && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using on a shared interface");
+ params->freq = res;
+ }
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go)
+{
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpas_p2p_create_iface(wpa_s))
+ return wpa_s;
+
+ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT) < 0)
+ return NULL;
+ group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+ if (group_wpa_s == NULL) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return NULL;
+ }
+
+ return group_wpa_s;
+}
+
+
+/**
+ * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @persistent_group: Whether to create a persistent group
+ * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function creates a new P2P group with the local end as the Group Owner,
+ * i.e., without using Group Owner Negotiation.
+ */
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq)
+{
+ struct p2p_go_neg_results params;
+
+ wpas_p2p_init_go_params(wpa_s, &params, freq);
+ p2p_go_params(wpa_s->global->p2p, &params);
+ params.persistent_group = persistent_group;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
+ if (wpa_s == NULL)
+ return -1;
+ wpas_start_wps_go(wpa_s, &params, 0);
+
+ return 0;
+}
+
+
+static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *params, int addr_allocated)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpa_supplicant_ap_deinit(wpa_s);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->group_cipher = WPA_CIPHER_CCMP;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->ssid = os_malloc(params->ssid_len);
+ if (ssid->ssid == NULL) {
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return -1;
+ }
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ ssid->p2p_group = 1;
+ if (params->psk_set) {
+ os_memcpy(ssid->psk, params->psk, 32);
+ ssid->psk_set = 1;
+ }
+ if (params->passphrase)
+ ssid->passphrase = os_strdup(params->passphrase);
+
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ wpa_s->show_group_started = 1;
+
+ return 0;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int freq)
+{
+ struct p2p_go_neg_results params;
+
+ if (ssid->disabled != 2 || ssid->ssid == NULL)
+ return -1;
+
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+ if (ssid->mode == WPAS_MODE_INFRA)
+ return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+
+ if (ssid->mode != WPAS_MODE_P2P_GO)
+ return -1;
+
+ wpas_p2p_init_go_params(wpa_s, &params, freq);
+
+ params.role_go = 1;
+ if (ssid->passphrase == NULL ||
+ os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
+ "group");
+ return -1;
+ }
+ os_strlcpy(params.passphrase, ssid->passphrase,
+ sizeof(params.passphrase));
+ os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
+ params.ssid_len = ssid->ssid_len;
+ params.persistent_group = 1;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpas_start_wps_go(wpa_s, &params, 0);
+
+ return 0;
+}
+
+
+static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->ap_iface) {
+ struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+ if (beacon_ies) {
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = beacon_ies;
+ }
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = proberesp_ies;
+ } else {
+ wpabuf_free(beacon_ies);
+ wpabuf_free(proberesp_ies);
+ }
+ wpa_supplicant_ap_update_beacon(wpa_s);
+}
+
+
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ int persistent_group,
+ int group_formation)
+{
+ struct p2p_group *group;
+ struct p2p_group_config *cfg;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return NULL;
+
+ cfg->persistent_group = persistent_group;
+ os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+ cfg->cb_ctx = wpa_s;
+ cfg->ie_update = wpas_p2p_ie_update;
+
+ group = p2p_group_init(wpa_s->global->p2p, cfg);
+ if (group == NULL)
+ os_free(cfg);
+ if (!group_formation)
+ p2p_group_notif_formation_done(group);
+ wpa_s->p2p_group = group;
+ return group;
+}
+
+
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar)
+{
+ if (!wpa_s->p2p_in_provisioning) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
+ "provisioning not in progress");
+ return;
+ }
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+ NULL);
+ if (wpa_s->global->p2p)
+ p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
+ wpas_group_formation_completed(wpa_s, 1);
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method)
+{
+ u16 config_methods;
+
+ if (os_strcmp(config_method, "display") == 0)
+ config_methods = WPS_CONFIG_DISPLAY;
+ else if (os_strcmp(config_method, "keypad") == 0)
+ config_methods = WPS_CONFIG_KEYPAD;
+ else if (os_strcmp(config_method, "pbc") == 0 ||
+ os_strcmp(config_method, "pushbutton") == 0)
+ config_methods = WPS_CONFIG_PUSHBUTTON;
+ else
+ return -1;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+ config_methods, 0);
+}
+
+
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end)
+{
+ return p2p_scan_result_text(ies, ies_len, buf, end);
+}
+
+
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type)
+{
+ wpa_s->p2p_long_listen = 0;
+
+ return p2p_find(wpa_s->global->p2p, timeout, type);
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->p2p_long_listen = 0;
+
+ p2p_stop_find(wpa_s->global->p2p);
+
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+}
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_s->p2p_long_listen = 0;
+}
+
+
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
+{
+ int res;
+
+ if (timeout == 0) {
+ /*
+ * This is a request for unlimited Listen state. However, at
+ * least for now, this is mapped to a Listen state for one
+ * hour.
+ */
+ timeout = 3600;
+ }
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+
+ res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
+ if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
+ wpa_s->p2p_long_listen = timeout;
+ eloop_register_timeout(timeout, 0,
+ wpas_p2p_long_listen_timeout,
+ wpa_s, NULL);
+ }
+
+ return res;
+}
+
+
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u8 *buf, size_t len, int p2p_group)
+{
+ if (wpa_s->global->p2p_disabled)
+ return -1;
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_assoc_req_ie(wpa_s->global->p2p, bssid, buf, len,
+ p2p_group);
+}
+
+
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ if (wpa_s->global->p2p_disabled)
+ return 0;
+ if (wpa_s->global->p2p == NULL)
+ return 0;
+
+ return p2p_probe_req_rx(wpa_s->global->p2p, addr, ie, 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)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
+ freq);
+}
+
+
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_scan_ie(wpa_s->global->p2p, ies);
+}
+
+
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+{
+ p2p_group_deinit(wpa_s->p2p_group);
+ wpa_s->p2p_group = NULL;
+}
+
+
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ wpa_s->p2p_long_listen = 0;
+
+ return p2p_reject(wpa_s->global->p2p, addr);
+}
+
+
+/* Invite to reinvoke a persistent group */
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr)
+{
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_GO;
+ if (peer_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Missing peer "
+ "address in invitation command");
+ return -1;
+ }
+ if (wpas_p2p_create_iface(wpa_s)) {
+ if (wpas_p2p_add_group_interface(wpa_s,
+ WPA_IF_P2P_GO) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to "
+ "allocate a new interface for the "
+ "group");
+ return -1;
+ }
+ bssid = wpa_s->pending_interface_addr;
+ } else
+ bssid = wpa_s->own_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ peer_addr = ssid->bssid;
+ }
+ wpa_s->pending_invite_ssid_id = ssid->id;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1);
+}
+
+
+/* Invite to join an active group */
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr)
+{
+ struct wpa_global *global = wpa_s->global;
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+ struct wpa_ssid *ssid;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+ if (wpa_s == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
+ return -1;
+ }
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
+ "invitation");
+ return -1;
+ }
+
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_ACTIVE_GO;
+ bssid = wpa_s->own_addr;
+ if (go_dev_addr == NULL)
+ go_dev_addr = wpa_s->own_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
+ "invite to current group");
+ return -1;
+ }
+ bssid = wpa_s->bssid;
+ if (go_dev_addr == NULL &&
+ !is_zero_ether_addr(wpa_s->go_dev_addr))
+ go_dev_addr = wpa_s->go_dev_addr;
+ }
+ wpa_s->pending_invite_ssid_id = -1;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 0);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *ssid_txt;
+ u8 go_dev_addr[ETH_ALEN];
+ int persistent;
+
+ if (!wpa_s->show_group_started || !ssid)
+ return;
+
+ wpa_s->show_group_started = 0;
+
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ if (ssid->bssid_set)
+ os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+ persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+ ssid->ssid_len);
+ os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
+
+ if (ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" psk=%s go_dev_addr=" MACSTR
+ "%s",
+ wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+ MACSTR "%s",
+ wpa_s->ifname, ssid_txt,
+ ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ }
+
+ if (persistent)
+ wpas_p2p_store_persistent_group(wpa_s->parent, ssid,
+ go_dev_addr);
+}
+
+
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2)
+{
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+ return -1;
+
+ return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->assoc_freq,
+ duration1, interval1, duration2, interval2);
+}
+
+
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval)
+{
+ return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, 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)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_data *p2p = wpa_s->global->p2p;
+
+ if (p2p == NULL)
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
+ p2p_set_dev_name(p2p, wpa_s->conf->device_name);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) {
+ u8 pri_dev_type[8];
+ if (wpa_s->conf->device_type) {
+ if (wps_dev_type_str2bin(wpa_s->conf->device_type,
+ pri_dev_type) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Invalid "
+ "device_type");
+ } else
+ p2p_set_pri_dev_type(p2p, pri_dev_type);
+ }
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+ size_t num = 0;
+ int i;
+ for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
+ if (wpa_s->conf->sec_device_type[i] == NULL)
+ continue;
+ if (wps_dev_type_str2bin(
+ wpa_s->conf->sec_device_type[i],
+ sec_dev_type[num]) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Invalid "
+ "sec_device_type");
+ continue;
+ }
+ num++;
+ if (num == P2P_SEC_DEVICE_TYPES)
+ break;
+ }
+ p2p_set_sec_dev_types(p2p, (void *) sec_dev_type, num);
+ }
+
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = 0x04;
+ p2p_set_country(p2p, country);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
+ p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
+ wpa_s->conf->p2p_ssid_postfix ?
+ os_strlen(wpa_s->conf->p2p_ssid_postfix) :
+ 0);
+ }
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
new file mode 100644
index 0000000..6b28dc3
--- /dev/null
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -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 */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index d6b12e8..b69197e 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -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;
}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 2ee22be..4f4bdf4 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -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);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 2da1697..6d95921 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -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 */
}