Esempio n. 1
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) {
		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->info.p2p_device_addr));
		return -1;
	}

	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
		if (!(dev->info.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->info.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;

	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) {
		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
			"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;
}
Esempio n. 2
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;
}
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->info.p2p_device_addr));
		return -1;
	}

	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
	if (req == NULL)
		return -1;
	if (p2p->state != P2P_IDLE)
		p2p_stop_listen_for_freq(p2p, freq);
	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_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) {
		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;
}
Esempio n. 4
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;
}
Esempio n. 5
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;
	} else if (msg.wfd_subelems) {
		wpabuf_free(dev->info.wfd_subelems);
		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
	}

	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,
				msg.group_id ? msg.group_id + ETH_ALEN : NULL,
				msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
		}
		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, NULL, 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_stop_listen_for_freq(p2p, 0);
		p2p_continue_find(p2p);
	}
}