Beispiel #1
0
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, struct p2ps_provision *p2ps_prov, u16 config_methods, int join, int force_freq, int user_initiated_pd)
{
	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)) {
		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr));
		os_free(p2ps_prov);
		return -1;
	}

	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods);
	if (config_methods == 0 && !p2ps_prov) {
		os_free(p2ps_prov);
		return -1;
	}

	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
		/* Use cached method from deferred provisioning */
		p2ps_prov->method = p2p->p2ps_prov->method;
	}

	/* Reset provisioning info */
	dev->wps_prov_info = 0;
	p2ps_prov_free(p2p);
	p2p->p2ps_prov = p2ps_prov;

	dev->req_config_methods = config_methods;
	if (join) {
		dev->flags |= P2P_DEV_PD_FOR_JOIN;
	} else {
		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
	}

	if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) {
		p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods);
		return 0;
	}

	p2p->user_initiated_pd = user_initiated_pd;
	p2p->pd_force_freq = force_freq;

	if (p2p->user_initiated_pd) {
		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
	}

	/*
	 * Assign dialog token here to use the same value in each retry within
	 * the same PD exchange.
	 */
	dev->dialog_token++;
	if (dev->dialog_token == 0) {
		dev->dialog_token = 1;
	}

	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
}
Beispiel #2
0
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;
	unsigned int wait_time;

	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
	if (freq <= 0) {
		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
			MACSTR " to send SD Request",
			MAC2STR(dev->info.p2p_device_addr));
		return -1;
	}

	query = p2p_pending_sd_req(p2p, dev);
	if (query == NULL)
		return -1;

	p2p_dbg(p2p, "Start Service Discovery with " MACSTR,
		MAC2STR(dev->info.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;

	wait_time = 5000;
	if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
		wait_time = p2p->cfg->max_listen;
	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
			    wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
		p2p_dbg(p2p, "Failed to send Action frame");
		ret = -1;
	}

	/* Update the pending broadcast SD query count for this device */
	dev->sd_pending_bcast_queries--;

	/*
	 * If there are no pending broadcast queries for this device, mark it as
	 * done (-1).
	 */
	if (dev->sd_pending_bcast_queries == 0)
		dev->sd_pending_bcast_queries = -1;

	wpabuf_free(req);

	return ret;
}
Beispiel #3
0
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;
	unsigned int wait_time;

	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
	if (freq <= 0) {
		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
			MACSTR " to send SD Request",
			MAC2STR(dev->info.p2p_device_addr));
		return -1;
	}

	query = p2p_pending_sd_req(p2p, dev);
	if (query == NULL)
		return -1;
	if (p2p->state == P2P_SEARCH &&
	    os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr,
		      ETH_ALEN) == 0) {
		p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR
			" due to it being the first no-ACK peer in this search iteration",
			MAC2STR(dev->info.p2p_device_addr));
		return -2;
	}

	p2p_dbg(p2p, "Start Service Discovery with " MACSTR,
		MAC2STR(dev->info.p2p_device_addr));

	req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
	if (req == NULL)
		return -1;

	dev->sd_reqs++;
	p2p->sd_peer = dev;
	p2p->sd_query = query;
	p2p->pending_action_state = P2P_PENDING_SD;

	wait_time = 5000;
	if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
		wait_time = p2p->cfg->max_listen;
	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
			    wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
		p2p_dbg(p2p, "Failed to send Action frame");
		ret = -1;
	}

	wpabuf_free(req);

	return ret;
}
Beispiel #4
0
void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
		     u8 dialog_token, const struct wpabuf *resp_tlvs)
{
	struct wpabuf *resp;
	size_t max_len;

	/*
	 * In the 60 GHz, we have a smaller maximum frame length for management
	 * frames.
	 */
	max_len = (freq > 56160) ? 928 : 1400;

	/* TODO: fix the length limit to match with the maximum frame length */
	if (wpabuf_len(resp_tlvs) > max_len) {
		p2p_dbg(p2p, "SD response long enough to require fragmentation");
		if (p2p->sd_resp) {
			/*
			 * TODO: Could consider storing the fragmented response
			 * separately for each peer to avoid having to drop old
			 * one if there is more than one pending SD query.
			 * Though, that would eat more memory, so there are
			 * also benefits to just using a single buffer.
			 */
			p2p_dbg(p2p, "Drop previous SD response");
			wpabuf_free(p2p->sd_resp);
		}
		p2p->sd_resp = wpabuf_dup(resp_tlvs);
		if (p2p->sd_resp == NULL) {
			p2p_err(p2p, "Failed to allocate SD response fragmentation area");
			return;
		}
		os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
		p2p->sd_resp_dialog_token = dialog_token;
		p2p->sd_resp_pos = 0;
		p2p->sd_frag_id = 0;
		resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
					     1, p2p->srv_update_indic, NULL);
	} else {
		p2p_dbg(p2p, "SD response fits in initial response");
		resp = p2p_build_sd_response(dialog_token,
					     WLAN_STATUS_SUCCESS, 0,
					     p2p->srv_update_indic, resp_tlvs);
	}
	if (resp == NULL)
		return;

	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
			    p2p->cfg->dev_addr,
			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
		p2p_dbg(p2p, "Failed to send Action frame");

	wpabuf_free(resp);
}
Beispiel #5
0
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
                           int join, int force_freq)
{
    struct wpabuf *req;
    int freq;

    if (force_freq > 0)
        freq = force_freq;
    else
        freq = dev->listen_freq > 0 ? dev->listen_freq :
               dev->oper_freq;
    if (freq <= 0) {
        p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
                MACSTR " to send Provision Discovery Request",
                MAC2STR(dev->info.p2p_device_addr));
        return -1;
    }

    if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
        if (!(dev->info.dev_capab &
                P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
            p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
                    " that is in a group and is not discoverable",
                    MAC2STR(dev->info.p2p_device_addr));
            return -1;
        }
        /* TODO: use device discoverability request through GO */
    }

    req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
                                  dev->req_config_methods,
                                  join ? dev : NULL);
    if (req == NULL)
        return -1;

    if (p2p->state != P2P_IDLE)
        p2p_stop_listen_for_freq(p2p, freq);
    p2p->pending_action_state = P2P_PENDING_PD;
    if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
                        p2p->cfg->dev_addr, dev->info.p2p_device_addr,
                        wpabuf_head(req), wpabuf_len(req), 200) < 0) {
        p2p_dbg(p2p, "Failed to send Action frame");
        wpabuf_free(req);
        return -1;
    }

    os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);

    wpabuf_free(req);
    return 0;
}
int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev,
		 os_time_t *remaining_usec)
{
	struct wpabuf *req;
	int ret = 0;
	struct p2p_sd_query *query;
	int freq;
	unsigned int wait_time;

	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
	if (freq <= 0) {
		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
			MACSTR " to send SD Request",
			MAC2STR(dev->info.p2p_device_addr));
		return -1;
	}

	query = p2p_pending_sd_req(p2p, dev, remaining_usec);
	if (query == NULL)
		return -1;

	p2p_dbg(p2p, "Start Service Discovery with " MACSTR,
		MAC2STR(dev->info.p2p_device_addr));

	req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
	if (req == NULL)
		return -1;

	dev->sd_reqs++;
	p2p->sd_peer = dev;
	p2p->sd_query = query;
	p2p->pending_action_state = P2P_PENDING_SD;
	os_get_reltime(&dev->last_sd_bc_time);

	wait_time = 5000;
	if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
		wait_time = p2p->cfg->max_listen;
	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
			    wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
		p2p_dbg(p2p, "Failed to send Action frame");
		ret = -1;
	}

	wpabuf_free(req);

	return ret;
}
Beispiel #7
0
void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
{
	p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success);

	if (p2p->invite_peer == NULL) {
		p2p_dbg(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, success ? 500000 : 100000);
}
void p2p_channels_dump(struct p2p_data *p2p, const char *title,
		       const struct p2p_channels *chan)
{
	char buf[500], *pos, *end;
	size_t i, j;
	int ret;

	pos = buf;
	end = pos + sizeof(buf);

	for (i = 0; i < chan->reg_classes; i++) {
		const struct p2p_reg_class *c;
		c = &chan->reg_class[i];
		ret = os_snprintf(pos, end - pos, " %u:", c->reg_class);
		if (os_snprintf_error(end - pos, ret))
			break;
		pos += ret;

		for (j = 0; j < c->channels; j++) {
			ret = os_snprintf(pos, end - pos, "%s%u",
					  j == 0 ? "" : ",",
					  c->channel[j]);
			if (os_snprintf_error(end - pos, ret))
				break;
			pos += ret;
		}
	}
	*pos = '\0';

	p2p_dbg(p2p, "%s:%s", title, buf);
}
void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
{
	p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success);
	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);

	if (!success)
		p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported");

	if (p2p->cfg->invitation_received) {
		p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
					      p2p->inv_sa,
					      p2p->inv_group_bssid_ptr,
					      p2p->inv_ssid, p2p->inv_ssid_len,
					      p2p->inv_go_dev_addr,
					      p2p->inv_status,
					      p2p->inv_op_freq);
	}
}
int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
{
	if (p2p_unlink_sd_query(p2p, req)) {
		p2p_dbg(p2p, "Cancel pending SD query %p", req);
		p2p_free_sd_query(req);
		return 0;
	}
	return -1;
}
Beispiel #11
0
int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
			  const u8 *searching_dev, int rx_freq)
{
	struct p2p_group_member *m;
	struct wpabuf *req;
	struct p2p_data *p2p = group->p2p;
	int freq;

	m = p2p_group_get_client(group, dev_id);
	if (m == NULL || m->client_info == NULL) {
		p2p_dbg(group->p2p, "Requested client was not in this group "
			MACSTR, MAC2STR(group->cfg->interface_addr));
		return -1;
	}

	if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
		p2p_dbg(group->p2p, "Requested client does not support client discoverability");
		return -1;
	}

	p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to "
		MACSTR, MAC2STR(dev_id));

	req = p2p_build_go_disc_req();
	if (req == NULL)
		return -1;

	/* TODO: Should really use group operating frequency here */
	freq = rx_freq;

	p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
	if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
				  group->cfg->interface_addr,
				  group->cfg->interface_addr,
				  wpabuf_head(req), wpabuf_len(req), 200) < 0)
	{
		p2p_dbg(p2p, "Failed to send Action frame");
	}

	wpabuf_free(req);

	return 0;
}
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
		    const u8 *go_dev_addr, int dev_pw_id)
{
	struct wpabuf *req;
	int freq;

	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
	if (freq <= 0)
		freq = dev->oob_go_neg_freq;
	if (freq <= 0) {
		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
			MACSTR " to send Invitation Request",
			MAC2STR(dev->info.p2p_device_addr));
		return -1;
	}

	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
	if (req == NULL)
		return -1;
	if (p2p->state != P2P_IDLE)
		p2p_stop_listen_for_freq(p2p, freq);
	p2p_dbg(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_send_action(p2p, freq, dev->info.p2p_device_addr,
			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
			    wpabuf_head(req), wpabuf_len(req), 500) < 0) {
		p2p_dbg(p2p, "Failed to send Action frame");
		/* Use P2P find to recover and retry */
		p2p_set_timeout(p2p, 0, 0);
	} else {
		dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
	}

	wpabuf_free(req);

	return 0;
}
Beispiel #13
0
void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
{
	if (p2p_group_remove_member(group, addr)) {
		p2p_dbg(group->p2p, "Remove client " MACSTR
			" from group; num_members=%u/%u",
			MAC2STR(addr), group->num_members,
			group->cfg->max_clients);
		if (group->num_members == group->cfg->max_clients - 1)
			group->beacon_update = 1;
		p2p_group_update_ies(group);
		if (group->num_members == 0)
			group->cfg->idle_update(group->cfg->cb_ctx, 1);
	}
}
Beispiel #14
0
static struct wpabuf *
wifi_display_build_go_ie(struct p2p_group *group)
{
	struct wpabuf *wfd_subelems, *wfd_ie;
	struct p2p_group_member *m;
	u8 *len;
	unsigned int count = 0;

	if (!group->p2p->wfd_ie_probe_resp)
		return NULL;

	wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
				    group->num_members * 24 + 100);
	if (wfd_subelems == NULL)
		return NULL;
	if (group->p2p->wfd_dev_info)
		wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
	if (group->p2p->wfd_assoc_bssid)
		wpabuf_put_buf(wfd_subelems,
			       group->p2p->wfd_assoc_bssid);
	if (group->p2p->wfd_coupled_sink_info)
		wpabuf_put_buf(wfd_subelems,
			       group->p2p->wfd_coupled_sink_info);

	/* Build WFD Session Info */
	wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
	len = wpabuf_put(wfd_subelems, 2);
	m = group->members;
	while (m) {
		if (wifi_display_add_dev_info_descr(wfd_subelems, m))
			count++;
		m = m->next;
	}

	if (count == 0) {
		/* No Wi-Fi Display clients - do not include subelement */
		wfd_subelems->used -= 3;
	} else {
		WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
			     2);
		p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors",
			count);
	}

	wfd_ie = wifi_display_encaps(wfd_subelems);
	wpabuf_free(wfd_subelems);

	return wfd_ie;
}
Beispiel #15
0
u8 p2p_group_presence_req(struct p2p_group *group,
			  const u8 *client_interface_addr,
			  const u8 *noa, size_t noa_len)
{
	struct p2p_group_member *m;
	u8 curr_noa[50];
	int curr_noa_len;

	m = p2p_group_get_client_iface(group, client_interface_addr);
	if (m == NULL || m->client_info == NULL) {
		p2p_dbg(group->p2p, "Client was not in this group");
		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
	}

	wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);

	if (group->p2p->cfg->get_noa)
		curr_noa_len = group->p2p->cfg->get_noa(
			group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
			curr_noa, sizeof(curr_noa));
	else
		curr_noa_len = -1;
	if (curr_noa_len < 0)
		p2p_dbg(group->p2p, "Failed to fetch current NoA");
	else if (curr_noa_len == 0)
		p2p_dbg(group->p2p, "No NoA being advertized");
	else
		wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
			    curr_noa_len);

	/* TODO: properly process request and store copy */
	if (curr_noa_len > 0 || curr_noa_len == -1)
		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;

	return P2P_SC_SUCCESS;
}
Beispiel #16
0
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
					 struct p2p_device *dev)
{
	struct p2p_sd_query *q;
	int wsd = 0;
	int count = 0;

	if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
		return NULL; /* peer does not support SD */
#ifdef CONFIG_WIFI_DISPLAY
	if (wfd_wsd_supported(dev->info.wfd_subelems))
		wsd = 1;
#endif /* CONFIG_WIFI_DISPLAY */

	for (q = p2p->sd_queries; q; q = q->next) {
		/* Use WSD only if the peer indicates support or it */
		if (q->wsd && !wsd)
			continue;
		/* if the query is a broadcast query */
		if (q->for_all_peers) {
			/*
			 * check if there are any broadcast queries pending for
			 * this device
			 */
			if (dev->sd_pending_bcast_queries <= 0)
				return NULL;
			/* query number that needs to be send to the device */
			if (count == dev->sd_pending_bcast_queries - 1)
				goto found;
			count++;
		}
		if (!q->for_all_peers &&
		    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
		    0)
			goto found;
	}

	return NULL;

found:
	if (dev->sd_reqs > 100) {
		p2p_dbg(p2p, "Too many SD request attempts to " MACSTR
			" - skip remaining queries",
			MAC2STR(dev->info.p2p_device_addr));
		return NULL;
	}
	return q;
}
static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
				      u8 dialog_token, int freq)
{
	struct wpabuf *req;

	req = gas_build_comeback_req(dialog_token);
	if (req == NULL)
		return;

	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
			    wpabuf_head(req), wpabuf_len(req), 200) < 0)
		p2p_dbg(p2p, "Failed to send Action frame");

	wpabuf_free(req);
}
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;
	p2p_dbg(p2p, "Added SD Query %p", q);

	if (dst == NULL) {
		struct p2p_device *dev;

		p2p->num_p2p_sd_queries++;

		/* Update all the devices for the newly added broadcast query */
		dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
#ifdef CONFIG_MTK_P2P
			dev->flags &= ~P2P_DEV_SD_INFO;
#endif
			if (dev->sd_pending_bcast_queries <= 0)
				dev->sd_pending_bcast_queries = 1;
			else
				dev->sd_pending_bcast_queries++;
		}
	}

	return q;
}
Beispiel #19
0
int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
			  const u8 *ie, size_t len)
{
	struct p2p_group_member *m;

	if (group == NULL)
		return -1;

	p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);

	m = os_zalloc(sizeof(*m));
	if (m == NULL)
		return -1;
	os_memcpy(m->addr, addr, ETH_ALEN);
	m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
	if (m->p2p_ie) {
		m->client_info = p2p_build_client_info(addr, m->p2p_ie,
						       &m->dev_capab,
						       m->dev_addr);
	}
#ifdef CONFIG_WIFI_DISPLAY
	m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
#endif /* CONFIG_WIFI_DISPLAY */

	p2p_group_remove_member(group, addr);

	m->next = group->members;
	group->members = m;
	group->num_members++;
	p2p_dbg(group->p2p,  "Add client " MACSTR
		" to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
		MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
		m->client_info ? 1 : 0,
		group->num_members, group->cfg->max_clients);
	if (group->num_members == group->cfg->max_clients)
		group->beacon_update = 1;
	p2p_group_update_ies(group);
	if (group->num_members == 1)
		group->cfg->idle_update(group->cfg->cb_ctx, 0);

	return 0;
}
Beispiel #20
0
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
			   int join, int force_freq)
{
	struct wpabuf *req;
	int freq;

	if (force_freq > 0)
		freq = force_freq;
	else
		freq = dev->listen_freq > 0 ? dev->listen_freq :
			dev->oper_freq;
	if (freq <= 0) {
		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
			MACSTR " to send Provision Discovery Request",
			MAC2STR(dev->info.p2p_device_addr));
		return -1;
	}

	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
		if (!(dev->info.dev_capab &
		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
			p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
				" that is in a group and is not discoverable",
				MAC2STR(dev->info.p2p_device_addr));
			return -1;
		}
		/* TODO: use device discoverability request through GO */
	}

	if (p2p->p2ps_prov) {
		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
				dev->req_config_methods = WPS_CONFIG_KEYPAD;
			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
				dev->req_config_methods = WPS_CONFIG_DISPLAY;
			else
				dev->req_config_methods = WPS_CONFIG_P2PS;
		} else {
			/* Order of preference, based on peer's capabilities */
			if (p2p->p2ps_prov->method)
				dev->req_config_methods =
					p2p->p2ps_prov->method;
			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
				dev->req_config_methods = WPS_CONFIG_P2PS;
			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
				dev->req_config_methods = WPS_CONFIG_DISPLAY;
			else
				dev->req_config_methods = WPS_CONFIG_KEYPAD;
		}
		p2p_dbg(p2p,
			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
			dev->req_config_methods);

		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
					p2p->p2ps_prov->pref_freq, 1) < 0)
			return -1;
	}

	req = p2p_build_prov_disc_req(p2p, dev, join);
	if (req == NULL)
		return -1;

	if (p2p->state != P2P_IDLE)
		p2p_stop_listen_for_freq(p2p, freq);
	p2p->pending_action_state = P2P_PENDING_PD;
	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
		p2p_dbg(p2p, "Failed to send Action frame");
		wpabuf_free(req);
		return -1;
	}

	os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);

	wpabuf_free(req);
	return 0;
}
void p2p_rx_gas_comeback_resp(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 status_code;
	u8 frag_id;
	u8 more_frags;
	u16 comeback_delay;
	u16 slen;

	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);

	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
		p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from "
			MACSTR, MAC2STR(sa));
		return;
	}
	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
	p2p_clear_timeout(p2p);

	p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)",
		MAC2STR(sa), (int) len);

	if (len < 6 + 2) {
		p2p_dbg(p2p, "Too short GAS Comeback Response frame");
		return;
	}

	dialog_token = *pos++;
	/* TODO: check dialog_token match */
	status_code = WPA_GET_LE16(pos);
	pos += 2;
	frag_id = *pos & 0x7f;
	more_frags = (*pos & 0x80) >> 7;
	pos++;
	comeback_delay = WPA_GET_LE16(pos);
	pos += 2;
	p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
		"comeback_delay=%u",
		dialog_token, status_code, frag_id, more_frags,
		comeback_delay);
	/* TODO: check frag_id match */
	if (status_code) {
		p2p_dbg(p2p, "Service Discovery failed: status code %u",
			status_code);
		return;
	}

	if (*pos != WLAN_EID_ADV_PROTO) {
		p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u",
			*pos);
		return;
	}
	pos++;

	slen = *pos++;
	next = pos + slen;
	if (next > end || slen < 2) {
		p2p_dbg(p2p, "Invalid IE in GAS Comeback Response");
		return;
	}
	pos++; /* skip QueryRespLenLimit and PAME-BI */

	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
		p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u",
			*pos);
		return;
	}

	pos = next;
	/* Query Response */
	if (pos + 2 > end) {
		p2p_dbg(p2p, "Too short Query Response");
		return;
	}
	slen = WPA_GET_LE16(pos);
	pos += 2;
	p2p_dbg(p2p, "Query Response Length: %d", slen);
	if (pos + slen > end) {
		p2p_dbg(p2p, "Not enough Query Response data");
		return;
	}
	if (slen == 0) {
		p2p_dbg(p2p, "No Query Response data");
		return;
	}
	end = pos + slen;

	if (p2p->sd_rx_resp) {
		 /*
		  * ANQP header is only included in the first fragment; rest of
		  * the fragments start with continue TLVs.
		  */
		goto skip_nqp_header;
	}

	/* ANQP Query Response */
	if (pos + 4 > end)
		return;
	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
		return;
	}
	pos += 2;

	slen = WPA_GET_LE16(pos);
	pos += 2;
	p2p_dbg(p2p, "ANQP Query Response length: %u", slen);
	if (slen < 3 + 1) {
		p2p_dbg(p2p, "Invalid ANQP Query Response length");
		return;
	}
	if (pos + 4 > end)
		return;

	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
			WPA_GET_BE32(pos));
		return;
	}
	pos += 4;

	if (pos + 2 > end)
		return;
	p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
	p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic);
	pos += 2;

skip_nqp_header:
	if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
		return;
	wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
	p2p_dbg(p2p, "Current SD reassembly buffer length: %u",
		(unsigned int) wpabuf_len(p2p->sd_rx_resp));

	if (more_frags) {
		p2p_dbg(p2p, "More fragments remains");
		/* TODO: what would be a good size limit? */
		if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
			wpabuf_free(p2p->sd_rx_resp);
			p2p->sd_rx_resp = NULL;
			p2p_dbg(p2p, "Too long SD response - drop it");
			return;
		}
		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
		return;
	}

	p2p->sd_peer = NULL;

	if (p2p->sd_query) {
		if (!p2p->sd_query->for_all_peers) {
			struct p2p_sd_query *q;
			p2p_dbg(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,
				      p2p->sd_rx_update_indic,
				      wpabuf_head(p2p->sd_rx_resp),
				      wpabuf_len(p2p->sd_rx_resp));
	wpabuf_free(p2p->sd_rx_resp);
	p2p->sd_rx_resp = NULL;

	p2p_continue_find(p2p);
}
void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
			     const u8 *data, size_t len, int rx_freq)
{
	struct wpabuf *resp;
	u8 dialog_token;
	size_t frag_len;
	int more = 0;

	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
	if (len < 1)
		return;
	dialog_token = *data;
	p2p_dbg(p2p, "Dialog Token: %u", dialog_token);
	if (dialog_token != p2p->sd_resp_dialog_token) {
		p2p_dbg(p2p, "No pending SD response fragment for dialog token %u",
			dialog_token);
		return;
	}

	if (p2p->sd_resp == NULL) {
		p2p_dbg(p2p, "No pending SD response fragment available");
		return;
	}
	if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
		p2p_dbg(p2p, "No pending SD response fragment for " MACSTR,
			MAC2STR(sa));
		return;
	}

	frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
	if (frag_len > 1400) {
		frag_len = 1400;
		more = 1;
	}
	resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
					   p2p->srv_update_indic,
					   wpabuf_head_u8(p2p->sd_resp) +
					   p2p->sd_resp_pos, frag_len,
					   p2p->sd_frag_id, more,
					   wpabuf_len(p2p->sd_resp));
	if (resp == NULL)
		return;
	p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)",
		p2p->sd_frag_id, more, (int) frag_len);
	p2p->sd_frag_id++;
	p2p->sd_resp_pos += frag_len;

	if (more) {
		p2p_dbg(p2p, "%d more bytes remain to be sent",
			(int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
	} else {
		p2p_dbg(p2p, "All fragments of SD response sent");
		wpabuf_free(p2p->sd_resp);
		p2p->sd_resp = NULL;
	}

	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
	if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
			    p2p->cfg->dev_addr,
			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
		p2p_dbg(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, int rx_freq)
{
	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->info.p2p_device_addr, ETH_ALEN) != 0) {
		p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from "
			MACSTR, MAC2STR(sa));
		return;
	}
	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
	p2p_clear_timeout(p2p);

	p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)",
		MAC2STR(sa), (int) len);

	if (len < 5 + 2) {
		p2p_dbg(p2p, "Too short GAS Initial Response frame");
		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;
	p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u",
		dialog_token, status_code, comeback_delay);
	if (status_code) {
		p2p_dbg(p2p, "Service Discovery failed: status code %u",
			status_code);
		return;
	}

	if (*pos != WLAN_EID_ADV_PROTO) {
		p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos);
		return;
	}
	pos++;

	slen = *pos++;
	next = pos + slen;
	if (next > end || slen < 2) {
		p2p_dbg(p2p, "Invalid IE in GAS Initial Response");
		return;
	}
	pos++; /* skip QueryRespLenLimit and PAME-BI */

	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
		p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u",
			*pos);
		return;
	}

	pos = next;
	/* Query Response */
	if (pos + 2 > end) {
		p2p_dbg(p2p, "Too short Query Response");
		return;
	}
	slen = WPA_GET_LE16(pos);
	pos += 2;
	p2p_dbg(p2p, "Query Response Length: %d", slen);
	if (pos + slen > end) {
		p2p_dbg(p2p, "Not enough Query Response data");
		return;
	}
	end = pos + slen;

	if (comeback_delay) {
		p2p_dbg(p2p, "Fragmented response - request fragments");
		if (p2p->sd_rx_resp) {
			p2p_dbg(p2p, "Drop old SD reassembly buffer");
			wpabuf_free(p2p->sd_rx_resp);
			p2p->sd_rx_resp = NULL;
		}
		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
		return;
	}

	/* ANQP Query Response */
	if (pos + 4 > end)
		return;
	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
		return;
	}
	pos += 2;

	slen = WPA_GET_LE16(pos);
	pos += 2;
	if (pos + slen > end || slen < 3 + 1) {
		p2p_dbg(p2p, "Invalid ANQP Query Response length");
		return;
	}

	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
			WPA_GET_BE32(pos));
		return;
	}
	pos += 4;

	if (pos + 2 > end)
		return;
	update_indic = WPA_GET_LE16(pos);
	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
	pos += 2;

	p2p->sd_peer = NULL;

	if (p2p->sd_query) {
		if (!p2p->sd_query->for_all_peers) {
			struct p2p_sd_query *q;
			p2p_dbg(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_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->reg_class,
					   p2p->cfg->channel);
	if (freq < 0)
		return;

	if (len < 1 + 2)
		return;

	dialog_token = *pos++;
	p2p_dbg(p2p, "GAS Initial Request from " MACSTR
		" (dialog token %u, freq %d)",
		MAC2STR(sa), dialog_token, rx_freq);

	if (*pos != WLAN_EID_ADV_PROTO) {
		p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos);
		return;
	}
	pos++;

	slen = *pos++;
	next = pos + slen;
	if (next > end || slen < 2) {
		p2p_dbg(p2p, "Invalid IE in GAS Initial Request");
		return;
	}
	pos++; /* skip QueryRespLenLimit and PAME-BI */

	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
		p2p_dbg(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;

	/* ANQP Query Request */
	if (pos + 4 > end)
		return;
	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
		return;
	}
	pos += 2;

	slen = WPA_GET_LE16(pos);
	pos += 2;
	if (pos + slen > end || slen < 3 + 1) {
		p2p_dbg(p2p, "Invalid ANQP Query Request length");
		return;
	}

	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
			WPA_GET_BE32(pos));
		return;
	}
	pos += 4;

	if (pos + 2 > end)
		return;
	update_indic = WPA_GET_LE16(pos);
	p2p_dbg(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() */
}
Beispiel #25
0
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;

    p2p_dbg(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 || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
        p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
                MACSTR, MAC2STR(sa));

        if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
                           0)) {
            p2p_dbg(p2p, "Provision Discovery Request add device failed "
                    MACSTR, MAC2STR(sa));
        }
    } else if (msg.wfd_subelems) {
        wpabuf_free(dev->info.wfd_subelems);
        dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
    }

    if (!(msg.wps_config_methods &
            (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
             WPS_CONFIG_PUSHBUTTON))) {
        p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
        goto out;
    }

    if (msg.group_id) {
        size_t i;
        for (i = 0; i < p2p->num_groups; i++) {
            if (p2p_group_is_group_id_match(p2p->groups[i],
                                            msg.group_id,
                                            msg.group_id_len))
                break;
        }
        if (i == p2p->num_groups) {
            p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
            goto out;
        }
    }

    if (dev)
        dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
                        P2P_DEV_PD_PEER_KEYPAD);
    if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
        p2p_dbg(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) {
        p2p_dbg(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,
                                    msg.group_id, msg.group_id_len);
    if (resp == NULL) {
        p2p_parse_free(&msg);
        return;
    }
    p2p_dbg(p2p, "Sending Provision Discovery Response");
    if (rx_freq > 0)
        freq = rx_freq;
    else
        freq = p2p_channel_to_freq(p2p->cfg->reg_class,
                                   p2p->cfg->channel);
    if (freq < 0) {
        p2p_dbg(p2p, "Unknown regulatory class/channel");
        wpabuf_free(resp);
        p2p_parse_free(&msg);
        return;
    }
    p2p->pending_action_state = P2P_NO_PENDING_ACTION;
    if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
                        p2p->cfg->dev_addr,
                        wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
        p2p_dbg(p2p, "Failed to send Action frame");
    } else
        p2p->send_action_in_progress = 1;

    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,
                                msg.group_id, msg.group_id_len);
    }
    p2p_parse_free(&msg);
}
Beispiel #26
0
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, req_config_methods;
    int success = 0;

    if (p2p_parse(data, len, &msg))
        return;

    p2p_dbg(p2p, "Received Provision 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) {
        p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
                " with no pending request", MAC2STR(sa));
        p2p_parse_free(&msg);
        return;
    }

    if (dev->dialog_token != msg.dialog_token) {
        p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
                msg.dialog_token, dev->dialog_token);
        p2p_parse_free(&msg);
        return;
    }

    if (p2p->pending_action_state == P2P_PENDING_PD) {
        os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
    }

    /*
     * Use a local copy of the requested config methods since
     * p2p_reset_pending_pd() can clear this in the peer entry.
     */
    req_config_methods = dev->req_config_methods;

    /*
     * If the response is from the peer to whom a user initiated request
     * was sent earlier, we reset that state info here.
     */
    if (p2p->user_initiated_pd &&
            os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
        p2p_reset_pending_pd(p2p);

    if (msg.wps_config_methods != req_config_methods) {
        p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
                msg.wps_config_methods, req_config_methods);
        if (p2p->cfg->prov_disc_fail)
            p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
                                     P2P_PROV_DISC_REJECTED);
        p2p_parse_free(&msg);
        goto out;
    }

    report_config_methods = req_config_methods;
    dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
                    P2P_DEV_PD_PEER_KEYPAD);
    if (req_config_methods & WPS_CONFIG_DISPLAY) {
        p2p_dbg(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) {
        p2p_dbg(p2p, "Peer " MACSTR
                " accepted to write our PIN using keypad",
                MAC2STR(sa));
        dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
    }

    /* Store the provisioning info */
    dev->wps_prov_info = msg.wps_config_methods;

    p2p_parse_free(&msg);
    success = 1;

out:
    dev->req_config_methods = 0;
    p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
    if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
        p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
                MACSTR, MAC2STR(dev->info.p2p_device_addr));
        dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
        p2p_connect_send(p2p, dev);
        return;
    }
    if (success && p2p->cfg->prov_disc_resp)
        p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
                                 report_config_methods);

    if (p2p->state == P2P_PD_DURING_FIND) {
        p2p_clear_timeout(p2p);
        p2p_continue_find(p2p);
    }
}
Beispiel #27
0
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;
	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
	struct wpabuf *resp;
	u32 adv_id = 0;
	struct p2ps_advertisement *p2ps_adv = NULL;
	u8 conncap = P2PS_SETUP_NEW;
	u8 auto_accept = 0;
	u32 session_id = 0;
	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
	const u8 *group_mac;
	int passwd_id = DEV_PW_DEFAULT;
	u16 config_methods;
	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
	struct p2ps_feature_capab resp_fcap = { 0, 0 };
	struct p2ps_feature_capab *req_fcap = NULL;
	u8 remote_conncap;
	u16 method;

	if (p2p_parse(data, len, &msg))
		return;

	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
		" with config methods 0x%x (freq=%d)",
		MAC2STR(sa), msg.wps_config_methods, rx_freq);
	group_mac = msg.intended_addr;

	dev = p2p_get_device(p2p, sa);
	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
		p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
			MACSTR, MAC2STR(sa));

		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
				   0)) {
			p2p_dbg(p2p, "Provision Discovery Request add device failed "
				MACSTR, MAC2STR(sa));
			goto out;
		}

		if (!dev) {
			dev = p2p_get_device(p2p, sa);
			if (!dev) {
				p2p_dbg(p2p,
					"Provision Discovery device not found "
					MACSTR, MAC2STR(sa));
				goto out;
			}
		}
	} else if (msg.wfd_subelems) {
		wpabuf_free(dev->info.wfd_subelems);
		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
	}

	if (!msg.adv_id) {
		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
		if (!(msg.wps_config_methods & allowed_config_methods)) {
			p2p_dbg(p2p,
				"Unsupported Config Methods in Provision Discovery Request");
			goto out;
		}

		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
		if (msg.group_id) {
			size_t i;

			for (i = 0; i < p2p->num_groups; i++) {
				if (p2p_group_is_group_id_match(
					    p2p->groups[i],
					    msg.group_id, msg.group_id_len))
					break;
			}
			if (i == p2p->num_groups) {
				p2p_dbg(p2p,
					"PD request for unknown P2P Group ID - reject");
				goto out;
			}
		}
	} else {
		allowed_config_methods |= WPS_CONFIG_P2PS;

		/*
		 * Set adv_id here, so in case of an error, a P2PS PD Response
		 * will be sent.
		 */
		adv_id = WPA_GET_LE32(msg.adv_id);
		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
			reject = P2P_SC_FAIL_INVALID_PARAMS;
			goto out;
		}

		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;

		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);

		session_id = WPA_GET_LE32(msg.session_id);

		if (msg.conn_cap)
			conncap = *msg.conn_cap;

		/*
		 * We need to verify a P2PS config methog in an initial PD
		 * request or in a follow-on PD request with the status
		 * SUCCESS_DEFERRED.
		 */
		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
		    !(msg.wps_config_methods & allowed_config_methods)) {
			p2p_dbg(p2p,
				"Unsupported Config Methods in Provision Discovery Request");
			goto out;
		}

		/*
		 * TODO: since we don't support multiple PD, reject PD request
		 * if we are in the middle of P2PS PD with some other peer
		 */
	}

	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
			P2P_DEV_PD_PEER_KEYPAD |
			P2P_DEV_PD_PEER_P2PS);

	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
		p2p_dbg(p2p, "Peer " MACSTR
			" requested us to show a PIN on display", MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
		passwd_id = DEV_PW_USER_SPECIFIED;
	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
		p2p_dbg(p2p, "Peer " MACSTR
			" requested us to write its PIN using keypad",
			MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
			MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_P2PS;
		passwd_id = DEV_PW_P2PS_DEFAULT;
	}

	/* Remove stale persistent groups */
	if (p2p->cfg->remove_stale_groups) {
		p2p->cfg->remove_stale_groups(
			p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
			msg.persistent_dev,
			msg.persistent_ssid, msg.persistent_ssid_len);
	}

	reject = P2P_SC_SUCCESS;

	/*
	 * End of a legacy P2P PD Request processing, from this point continue
	 * with P2PS one.
	 */
	if (!msg.adv_id)
		goto out;

	remote_conncap = conncap;

	if (!msg.status) {
		unsigned int forced_freq, pref_freq;

		if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
			p2p_dbg(p2p,
				"P2PS PD adv mac does not match the local one");
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
			goto out;
		}

		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
		if (!p2ps_adv) {
			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
			goto out;
		}
		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);

		auto_accept = p2ps_adv->auto_accept;
		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
							  conncap, auto_accept,
							  &forced_freq,
							  &pref_freq);

		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
			auto_accept, remote_conncap, conncap);

		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);

		resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
						       req_fcap->cpt);

		p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
			p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);

		if (!resp_fcap.cpt) {
			p2p_dbg(p2p,
				"Incompatible P2PS feature capability CPT bitmask");
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
		} else if (p2ps_adv->config_methods &&
			   !(msg.wps_config_methods &
			     p2ps_adv->config_methods)) {
			p2p_dbg(p2p,
				"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
				p2ps_adv->config_methods,
				msg.wps_config_methods);
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
		} else if (!p2ps_adv->state) {
			p2p_dbg(p2p, "P2PS state unavailable");
			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
		} else if (!conncap) {
			p2p_dbg(p2p, "Conncap resolution failed");
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
		}

		if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
			p2p_dbg(p2p, "Keypad - always defer");
			auto_accept = 0;
		}

		if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
		    msg.channel_list && msg.channel_list_len &&
		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
					    msg.channel_list,
					    msg.channel_list_len) < 0) {
			p2p_dbg(p2p,
				"No common channels - force deferred flow");
			auto_accept = 0;
		}

		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
		     msg.persistent_dev) && msg.operating_channel) {
			struct p2p_channels intersect;

			/*
			 * There are cases where only the operating channel is
			 * provided. This requires saving the channel as the
			 * supported channel list, and verifying that it is
			 * supported.
			 */
			if (dev->channels.reg_classes == 0 ||
			    !p2p_channels_includes(&dev->channels,
						   msg.operating_channel[3],
						   msg.operating_channel[4])) {
				struct p2p_channels *ch = &dev->channels;

				os_memset(ch, 0, sizeof(*ch));
				ch->reg_class[0].reg_class =
					msg.operating_channel[3];
				ch->reg_class[0].channel[0] =
					msg.operating_channel[4];
				ch->reg_class[0].channels = 1;
				ch->reg_classes = 1;
			}

			p2p_channels_intersect(&p2p->channels, &dev->channels,
					       &intersect);

			if (intersect.reg_classes == 0) {
				p2p_dbg(p2p,
					"No common channels - force deferred flow");
				auto_accept = 0;
			}
		}

		if (auto_accept || reject != P2P_SC_SUCCESS) {
			struct p2ps_provision *tmp;

			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
						 msg.wps_config_methods,
						 session_mac, adv_mac) < 0) {
				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
				goto out;
			}

			tmp = p2p->p2ps_prov;
			tmp->force_freq = forced_freq;
			tmp->pref_freq = pref_freq;
			if (conncap) {
				tmp->conncap = conncap;
				tmp->status = P2P_SC_SUCCESS;
			} else {
				tmp->conncap = auto_accept;
				tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
			}

			if (reject != P2P_SC_SUCCESS)
				goto out;
		}
	}

	if (!msg.status && !auto_accept &&
	    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
		struct p2ps_provision *tmp;

		if (!conncap) {
			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
			goto out;
		}

		if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
					 msg.wps_config_methods,
					 session_mac, adv_mac) < 0) {
			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
			goto out;
		}
		tmp = p2p->p2ps_prov;
		reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
		tmp->status = reject;
	}

	/* Not a P2PS Follow-on PD */
	if (!msg.status)
		goto out;

	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
		reject = *msg.status;
		goto out;
	}

	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
		goto out;

	if (p2p->p2ps_prov->adv_id != adv_id ||
	    os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
		p2p_dbg(p2p,
			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
		goto out;
	}

	if (p2p->p2ps_prov->session_id != session_id ||
	    os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
		goto out;
	}

	method = p2p->p2ps_prov->method;

	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
						  remote_conncap,
						  p2p->p2ps_prov->conncap,
						  &p2p->p2ps_prov->force_freq,
						  &p2p->p2ps_prov->pref_freq);

	resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
					       req_fcap->cpt);

	p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
		p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);

	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
			    p2p->p2ps_prov->pref_freq, 0);

	/*
	 * Ensure that if we asked for PIN originally, our method is consistent
	 * with original request.
	 */
	if (method & WPS_CONFIG_DISPLAY)
		method = WPS_CONFIG_KEYPAD;
	else if (method & WPS_CONFIG_KEYPAD)
		method = WPS_CONFIG_DISPLAY;

	if (!conncap || !(msg.wps_config_methods & method)) {
		/*
		 * Reject this "Deferred Accept*
		 * if incompatible conncap or method
		 */
		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
	} else if (!resp_fcap.cpt) {
		p2p_dbg(p2p,
			"Incompatible P2PS feature capability CPT bitmask");
		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
	} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
		   msg.channel_list && msg.channel_list_len &&
		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
					   msg.channel_list,
					   msg.channel_list_len) < 0) {
		p2p_dbg(p2p,
			"No common channels in Follow-On Provision Discovery Request");
		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
	} else {
		reject = P2P_SC_SUCCESS;
	}

	dev->oper_freq = 0;
	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
		u8 tmp;

		if (msg.operating_channel)
			dev->oper_freq =
				p2p_channel_to_freq(msg.operating_channel[3],
						    msg.operating_channel[4]);

		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
	}

	p2p->p2ps_prov->status = reject;
	p2p->p2ps_prov->conncap = conncap;

out:
	if (reject == P2P_SC_SUCCESS ||
	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
		config_methods = msg.wps_config_methods;
	else
		config_methods = 0;

	/*
	 * Send PD Response for an initial PD Request or for follow-on
	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
	 */
	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
		resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
						reject, config_methods, adv_id,
						msg.group_id, msg.group_id_len,
						msg.persistent_ssid,
						msg.persistent_ssid_len,
						(const u8 *) &resp_fcap,
						sizeof(resp_fcap));
		if (!resp) {
			p2p_parse_free(&msg);
			return;
		}
		p2p_dbg(p2p, "Sending Provision Discovery Response");
		if (rx_freq > 0)
			freq = rx_freq;
		else
			freq = p2p_channel_to_freq(p2p->cfg->reg_class,
						   p2p->cfg->channel);
		if (freq < 0) {
			p2p_dbg(p2p, "Unknown regulatory class/channel");
			wpabuf_free(resp);
			p2p_parse_free(&msg);
			return;
		}
		p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
		if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
				    p2p->cfg->dev_addr,
				    wpabuf_head(resp), wpabuf_len(resp),
				    200) < 0)
			p2p_dbg(p2p, "Failed to send Action frame");
		else
			p2p->send_action_in_progress = 1;

		wpabuf_free(resp);
	}

	if (!dev) {
		p2p_parse_free(&msg);
		return;
	}

	freq = 0;
	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
		freq = p2p_channel_to_freq(p2p->op_reg_class,
					   p2p->op_channel);
		if (freq < 0)
			freq = 0;
	}

	if (!p2p->cfg->p2ps_prov_complete) {
		/* Don't emit anything */
	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
		reject = *msg.status;
		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
					     sa, adv_mac, session_mac,
					     NULL, adv_id, session_id,
					     0, 0, msg.persistent_ssid,
					     msg.persistent_ssid_len,
					     0, 0, NULL, NULL, 0, freq);
	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
		   p2p->p2ps_prov) {
		p2p->p2ps_prov->status = reject;
		p2p->p2ps_prov->conncap = conncap;

		if (reject != P2P_SC_SUCCESS)
			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
						     sa, adv_mac, session_mac,
						     NULL, adv_id,
						     session_id, conncap, 0,
						     msg.persistent_ssid,
						     msg.persistent_ssid_len, 0,
						     0, NULL, NULL, 0, freq);
		else
			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
						     *msg.status,
						     sa, adv_mac, session_mac,
						     group_mac, adv_id,
						     session_id, conncap,
						     passwd_id,
						     msg.persistent_ssid,
						     msg.persistent_ssid_len, 0,
						     0, NULL,
						     (const u8 *) &resp_fcap,
						     sizeof(resp_fcap), freq);
	} else if (msg.status && p2p->p2ps_prov) {
		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
					     adv_mac, session_mac, group_mac,
					     adv_id, session_id, conncap,
					     passwd_id,
					     msg.persistent_ssid,
					     msg.persistent_ssid_len,
					     0, 0, NULL,
					     (const u8 *) &resp_fcap,
					     sizeof(resp_fcap), freq);
	} else if (msg.status) {
	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
					     sa, adv_mac, session_mac,
					     group_mac, adv_id, session_id,
					     conncap, passwd_id,
					     msg.persistent_ssid,
					     msg.persistent_ssid_len,
					     0, 0, NULL,
					     (const u8 *) &resp_fcap,
					     sizeof(resp_fcap), freq);
	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
		   (!msg.session_info || !msg.session_info_len)) {
		p2p->p2ps_prov->method = msg.wps_config_methods;

		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
					     sa, adv_mac, session_mac,
					     group_mac, adv_id, session_id,
					     conncap, passwd_id,
					     msg.persistent_ssid,
					     msg.persistent_ssid_len,
					     0, 1, NULL,
					     (const u8 *) &resp_fcap,
					     sizeof(resp_fcap), freq);
	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
		size_t buf_len = msg.session_info_len;
		char *buf = os_malloc(2 * buf_len + 1);

		if (buf) {
			p2p->p2ps_prov->method = msg.wps_config_methods;

			utf8_escape((char *) msg.session_info, buf_len,
				    buf, 2 * buf_len + 1);

			p2p->cfg->p2ps_prov_complete(
				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
				adv_mac, session_mac, group_mac, adv_id,
				session_id, conncap, passwd_id,
				msg.persistent_ssid, msg.persistent_ssid_len,
				0, 1, buf,
				(const u8 *) &resp_fcap, sizeof(resp_fcap),
				freq);

			os_free(buf);
		}
	}

	/*
	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
	 * enter/show PIN.
	 *
	 * The callback is called in the following cases:
	 * 1. Legacy P2P PD request, response status SUCCESS
	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
	 *    response status: SUCCESS
	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
	 *    response status: INFO_CURRENTLY_UNAVAILABLE
	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
	 *    response status: INFO_CURRENTLY_UNAVAILABLE
	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
	 *    advertiser role: DISPLAY, autoaccept: FALSE,
	 *    seeker: KEYPAD, response status: SUCCESS
	 */
	if (p2p->cfg->prov_disc_req &&
	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
	     (!msg.status &&
	     (reject == P2P_SC_SUCCESS ||
	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
	      passwd_id == DEV_PW_USER_SPECIFIED) ||
	     (!msg.status &&
	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
	     (reject == P2P_SC_SUCCESS &&
	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
		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,
					msg.group_id, msg.group_id_len);
	}

	if (reject == P2P_SC_SUCCESS) {
		switch (config_methods) {
		case WPS_CONFIG_DISPLAY:
			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
			break;
		case WPS_CONFIG_KEYPAD:
			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
			break;
		case WPS_CONFIG_PUSHBUTTON:
			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
			break;
		case WPS_CONFIG_P2PS:
			dev->wps_prov_info = WPS_CONFIG_P2PS;
			break;
		default:
			dev->wps_prov_info = 0;
			break;
		}

		if (msg.intended_addr)
			os_memcpy(dev->interface_addr, msg.intended_addr,
				  ETH_ALEN);
	}
	p2p_parse_free(&msg);
}
Beispiel #28
0
/* Check if the message contains a valid P2PS PD Request */
static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
				const u8 *addr)
{
	u8 group_id = 0;
	u8 intended_addr = 0;
	u8 operating_channel = 0;
	u8 channel_list = 0;
	u8 config_timeout = 0;
	u8 listen_channel = 0;

#define P2PS_PD_REQ_CHECK(_val, _attr) \
do { \
	if ((_val) && !msg->_attr) { \
		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
		return -1; \
	} \
} while (0)

	P2PS_PD_REQ_CHECK(1, adv_id);
	P2PS_PD_REQ_CHECK(1, session_id);
	P2PS_PD_REQ_CHECK(1, session_mac);
	P2PS_PD_REQ_CHECK(1, adv_mac);
	P2PS_PD_REQ_CHECK(1, capability);
	P2PS_PD_REQ_CHECK(1, p2p_device_info);
	P2PS_PD_REQ_CHECK(1, feature_cap);

	/*
	 * We don't need to check Connection Capability, Persistent Group,
	 * and related attributes for follow-on PD Request with a status
	 * other than SUCCESS_DEFERRED.
	 */
	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
		return 0;

	P2PS_PD_REQ_CHECK(1, conn_cap);

	/*
	 * Note 1: A feature capability attribute structure can be changed
	 * in the future. The assumption is that such modifications are
	 * backward compatible, therefore we allow processing of msg.feature_cap
	 * exceeding the size of the p2ps_feature_capab structure.
	 * Note 2: Verification of msg.feature_cap_len below has to be changed
	 * to allow 2 byte feature capability processing if
	 * struct p2ps_feature_capab is extended to include additional fields
	 * and it affects the structure size.
	 */
	if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
		return -1;
	}

	switch (*msg->conn_cap) {
	case P2PS_SETUP_NEW:
		group_id = 1;
		intended_addr = 1;
		operating_channel = 1;
		channel_list = 1;
		config_timeout = 1;
		listen_channel = 1;
		break;
	case P2PS_SETUP_CLIENT:
		channel_list = 1;
		listen_channel = 1;
		break;
	case P2PS_SETUP_GROUP_OWNER:
		group_id = 1;
		intended_addr = 1;
		operating_channel = 1;
		break;
	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
		group_id = 1;
		operating_channel = 1;
		intended_addr = 1;
		channel_list = 1;
		config_timeout = 1;
		break;
	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
		group_id = 1;
		intended_addr = 1;
		operating_channel = 1;
		channel_list = 1;
		config_timeout = 1;
		break;
	default:
		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
		return -1;
	}

	if (msg->persistent_dev) {
		channel_list = 1;
		config_timeout = 1;
		if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
			intended_addr = 1;
			operating_channel = 1;
		}
	}

	P2PS_PD_REQ_CHECK(group_id, group_id);
	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
	P2PS_PD_REQ_CHECK(channel_list, channel_list);
	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);

#undef P2PS_PD_REQ_CHECK

	return 0;
}
Beispiel #29
0
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, req_config_methods;
	u8 status = P2P_SC_SUCCESS;
	u32 adv_id = 0;
	u8 conncap = P2PS_SETUP_NEW;
	u8 adv_mac[ETH_ALEN];
	const u8 *group_mac;
	int passwd_id = DEV_PW_DEFAULT;
	int p2ps_seeker;

	if (p2p_parse(data, len, &msg))
		return;

	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
		p2p_parse_free(&msg);
		return;
	}

	/* Parse the P2PS members present */
	if (msg.status)
		status = *msg.status;

	group_mac = msg.intended_addr;

	if (msg.adv_mac)
		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
	else
		os_memset(adv_mac, 0, ETH_ALEN);

	if (msg.adv_id)
		adv_id = WPA_GET_LE32(msg.adv_id);

	if (msg.conn_cap) {
		conncap = *msg.conn_cap;

		/* Switch bits to local relative */
		switch (conncap) {
		case P2PS_SETUP_GROUP_OWNER:
			conncap = P2PS_SETUP_CLIENT;
			break;
		case P2PS_SETUP_CLIENT:
			conncap = P2PS_SETUP_GROUP_OWNER;
			break;
		}
	}

	p2p_dbg(p2p, "Received Provision 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) {
		p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
			" with no pending request", MAC2STR(sa));
		p2p_parse_free(&msg);
		return;
	}

	if (dev->dialog_token != msg.dialog_token) {
		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
			msg.dialog_token, dev->dialog_token);
		p2p_parse_free(&msg);
		return;
	}

	if (p2p->pending_action_state == P2P_PENDING_PD) {
		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
	}

	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;

	/*
	 * Use a local copy of the requested config methods since
	 * p2p_reset_pending_pd() can clear this in the peer entry.
	 */
	req_config_methods = dev->req_config_methods;

	/*
	 * If the response is from the peer to whom a user initiated request
	 * was sent earlier, we reset that state info here.
	 */
	if (p2p->user_initiated_pd &&
	    os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
		p2p_reset_pending_pd(p2p);

	if (msg.wps_config_methods != req_config_methods) {
		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
			msg.wps_config_methods, req_config_methods);
		if (p2p->cfg->prov_disc_fail)
			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
						 P2P_PROV_DISC_REJECTED,
						 adv_id, adv_mac, NULL);
		p2p_parse_free(&msg);
		p2ps_prov_free(p2p);
		goto out;
	}

	report_config_methods = req_config_methods;
	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
			P2P_DEV_PD_PEER_KEYPAD |
			P2P_DEV_PD_PEER_P2PS);
	if (req_config_methods & WPS_CONFIG_DISPLAY) {
		p2p_dbg(p2p, "Peer " MACSTR
			" accepted to show a PIN on display", MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
		p2p_dbg(p2p, "Peer " MACSTR
			" accepted to write our PIN using keypad",
			MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
		passwd_id = DEV_PW_USER_SPECIFIED;
	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
			MAC2STR(sa));
		dev->flags |= P2P_DEV_PD_PEER_P2PS;
		passwd_id = DEV_PW_P2PS_DEFAULT;
	}

	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
	    p2p->p2ps_prov) {
		dev->oper_freq = 0;

		/*
		 * Save the reported channel list and operating frequency.
		 * Note that the specification mandates that the responder
		 * should include in the channel list only channels reported by
		 * the initiator, so this is only a sanity check, and if this
		 * fails the flow would continue, although it would probably
		 * fail. Same is true for the operating channel.
		 */
		if (msg.channel_list && msg.channel_list_len &&
		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
					    msg.channel_list,
					    msg.channel_list_len) < 0)
			p2p_dbg(p2p, "P2PS PD Response - no common channels");

		if (msg.operating_channel) {
			if (p2p_channels_includes(&p2p->channels,
						  msg.operating_channel[3],
						  msg.operating_channel[4]) &&
			    p2p_channels_includes(&dev->channels,
						  msg.operating_channel[3],
						  msg.operating_channel[4])) {
				dev->oper_freq =
					p2p_channel_to_freq(
						msg.operating_channel[3],
						msg.operating_channel[4]);
			} else {
				p2p_dbg(p2p,
					"P2PS PD Response - invalid operating channel");
			}
		}

		if (p2p->cfg->p2ps_prov_complete) {
			int freq = 0;

			if (conncap == P2PS_SETUP_GROUP_OWNER) {
				u8 tmp;

				/*
				 * Re-select the operating channel as it is
				 * possible that original channel is no longer
				 * valid. This should not really fail.
				 */
				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
					p2p_dbg(p2p,
						"P2PS PD channel selection failed");

				freq = p2p_channel_to_freq(p2p->op_reg_class,
							   p2p->op_channel);
				if (freq < 0)
					freq = 0;
			}

			p2p->cfg->p2ps_prov_complete(
				p2p->cfg->cb_ctx, status, sa, adv_mac,
				p2p->p2ps_prov->session_mac,
				group_mac, adv_id, p2p->p2ps_prov->session_id,
				conncap, passwd_id, msg.persistent_ssid,
				msg.persistent_ssid_len, 1, 0, NULL,
				msg.feature_cap, msg.feature_cap_len, freq);
		}
		p2ps_prov_free(p2p);
	} else if (status != P2P_SC_SUCCESS &&
		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
		if (p2p->cfg->p2ps_prov_complete)
			p2p->cfg->p2ps_prov_complete(
				p2p->cfg->cb_ctx, status, sa, adv_mac,
				p2p->p2ps_prov->session_mac,
				group_mac, adv_id, p2p->p2ps_prov->session_id,
				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0);
		p2ps_prov_free(p2p);
	}

	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
		if (p2p->cfg->remove_stale_groups) {
			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
						      dev->info.p2p_device_addr,
						      NULL, NULL, 0);
		}

		if (msg.session_info && msg.session_info_len) {
			size_t info_len = msg.session_info_len;
			char *deferred_sess_resp = os_malloc(2 * info_len + 1);

			if (!deferred_sess_resp) {
				p2p_parse_free(&msg);
				p2ps_prov_free(p2p);
				goto out;
			}
			utf8_escape((char *) msg.session_info, info_len,
				    deferred_sess_resp, 2 * info_len + 1);

			if (p2p->cfg->prov_disc_fail)
				p2p->cfg->prov_disc_fail(
					p2p->cfg->cb_ctx, sa,
					P2P_PROV_DISC_INFO_UNAVAILABLE,
					adv_id, adv_mac,
					deferred_sess_resp);
			os_free(deferred_sess_resp);
		} else
			if (p2p->cfg->prov_disc_fail)
				p2p->cfg->prov_disc_fail(
					p2p->cfg->cb_ctx, sa,
					P2P_PROV_DISC_INFO_UNAVAILABLE,
					adv_id, adv_mac, NULL);
	} else if (status != P2P_SC_SUCCESS) {
		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
		if (p2p->cfg->prov_disc_fail)
			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
						 P2P_PROV_DISC_REJECTED,
						 adv_id, adv_mac, NULL);
		p2p_parse_free(&msg);
		p2ps_prov_free(p2p);
		goto out;
	}

	/* Store the provisioning info */
	dev->wps_prov_info = msg.wps_config_methods;
	if (msg.intended_addr)
		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);

	p2p_parse_free(&msg);

out:
	dev->req_config_methods = 0;
	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
		p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
			MACSTR, MAC2STR(dev->info.p2p_device_addr));
		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
		p2p_connect_send(p2p, dev);
		return;
	}

	/*
	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
	 * show/enter PIN events are needed.
	 *
	 * The callback is called in the following cases:
	 * 1. Legacy P2P PD response with a status SUCCESS
	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
	 *    response status: SUCCESS, local method KEYPAD
	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
	 *    local method: DISPLAY
	 */
	if (p2p->cfg->prov_disc_resp &&
	    ((status == P2P_SC_SUCCESS && !adv_id) ||
	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
	     (p2ps_seeker &&
	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
	      passwd_id == DEV_PW_USER_SPECIFIED)))
		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
					 report_config_methods);

	if (p2p->state == P2P_PD_DURING_FIND) {
		p2p_clear_timeout(p2p);
		p2p_continue_find(p2p);
	}
}
Beispiel #30
0
static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
				     struct p2p_message *msg)
{
	u8 conn_cap_go = 0;
	u8 conn_cap_cli = 0;
	u32 session_id;
	u32 adv_id;

#define P2PS_PD_RESP_CHECK(_val, _attr) \
	do { \
		if ((_val) && !msg->_attr) { \
			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
			return -1; \
		} \
	} while (0)

	P2PS_PD_RESP_CHECK(1, status);
	P2PS_PD_RESP_CHECK(1, adv_id);
	P2PS_PD_RESP_CHECK(1, adv_mac);
	P2PS_PD_RESP_CHECK(1, capability);
	P2PS_PD_RESP_CHECK(1, p2p_device_info);
	P2PS_PD_RESP_CHECK(1, session_id);
	P2PS_PD_RESP_CHECK(1, session_mac);
	P2PS_PD_RESP_CHECK(1, feature_cap);

	session_id = WPA_GET_LE32(msg->session_id);
	adv_id = WPA_GET_LE32(msg->adv_id);

	if (p2p->p2ps_prov->session_id != session_id) {
		p2p_dbg(p2p,
			"Ignore PD Response with unexpected Session ID");
		return -1;
	}

	if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
		      ETH_ALEN)) {
		p2p_dbg(p2p,
			"Ignore PD Response with unexpected Session MAC");
		return -1;
	}

	if (p2p->p2ps_prov->adv_id != adv_id) {
		p2p_dbg(p2p,
			"Ignore PD Response with unexpected Advertisement ID");
		return -1;
	}

	if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
		p2p_dbg(p2p,
			"Ignore PD Response with unexpected Advertisement MAC");
		return -1;
	}

	if (msg->listen_channel) {
		p2p_dbg(p2p,
			"Ignore malformed PD Response - unexpected Listen Channel");
		return -1;
	}

	if (*msg->status == P2P_SC_SUCCESS &&
	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
		p2p_dbg(p2p,
			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
		return -1;
	}

	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
		p2p_dbg(p2p,
			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
		return -1;
	}

	if (msg->conn_cap) {
		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
	}

	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
			   channel_list);
	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
			   config_timeout);

	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
	/*
	 * TODO: Also validate that operating channel is present if the device
	 * is a GO in a persistent group. We can't do it here since we don't
	 * know what is the role of the peer. It should be probably done in
	 * p2ps_prov_complete callback, but currently operating channel isn't
	 * passed to it.
	 */

#undef P2PS_PD_RESP_CHECK

	return 0;
}