|
@@ -123,6 +123,8 @@ static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
|
|
|
static void wpas_stop_listen(void *ctx);
|
|
|
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
|
|
|
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
|
|
|
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
|
|
|
+ enum wpa_driver_if_type type);
|
|
|
|
|
|
|
|
|
|
|
@@ -475,6 +477,34 @@ static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
|
|
|
}
|
|
|
|
|
|
|
|
|
+
|
|
|
+static struct wpa_supplicant *
|
|
|
+wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
|
|
|
+{
|
|
|
+ struct wpa_supplicant *save = NULL;
|
|
|
+ struct wpa_ssid *s;
|
|
|
+
|
|
|
+ if (!wpa_s)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
|
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
|
+ if (s->disabled || !s->p2p_group ||
|
|
|
+ s->mode != WPAS_MODE_P2P_GO)
|
|
|
+ continue;
|
|
|
+
|
|
|
+
|
|
|
+ if (p2p_get_group_num_members(wpa_s->p2p_group))
|
|
|
+ return wpa_s;
|
|
|
+ save = wpa_s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return save;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
|
|
|
static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
|
|
|
u8 *bssid)
|
|
@@ -516,6 +546,193 @@ wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
|
|
|
}
|
|
|
|
|
|
|
|
|
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
|
|
|
+{
|
|
|
+ struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
|
|
|
+ struct wpa_ssid *s;
|
|
|
+ u8 conncap = P2PS_SETUP_NONE;
|
|
|
+ unsigned int owned_members = 0;
|
|
|
+ unsigned int owner = 0;
|
|
|
+ unsigned int client = 0;
|
|
|
+ struct wpa_supplicant *go_wpa_s;
|
|
|
+ struct wpa_ssid *persistent_go;
|
|
|
+ int p2p_no_group_iface;
|
|
|
+
|
|
|
+ wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
|
|
|
+
|
|
|
+
|
|
|
+ * For non-concurrent capable devices:
|
|
|
+ * If persistent_go, then no new.
|
|
|
+ * If GO, then no client.
|
|
|
+ * If client, then no GO.
|
|
|
+ */
|
|
|
+ go_wpa_s = wpas_p2p_get_go_group(wpa_s);
|
|
|
+ persistent_go = wpas_p2p_get_persistent_go(wpa_s);
|
|
|
+ p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
|
|
|
+
|
|
|
+ wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
|
|
|
+ go_wpa_s, persistent_go);
|
|
|
+
|
|
|
+ for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
|
|
|
+ tmp_wpa_s = tmp_wpa_s->next) {
|
|
|
+ for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
|
|
|
+ wpa_printf(MSG_DEBUG,
|
|
|
+ "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
|
|
|
+ tmp_wpa_s, s, s->disabled,
|
|
|
+ s->p2p_group, s->mode);
|
|
|
+ if (!s->disabled && s->p2p_group) {
|
|
|
+ if (s->mode == WPAS_MODE_P2P_GO) {
|
|
|
+ owned_members +=
|
|
|
+ p2p_get_group_num_members(
|
|
|
+ tmp_wpa_s->p2p_group);
|
|
|
+ owner++;
|
|
|
+ } else
|
|
|
+ client++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (p2p_no_group_iface) {
|
|
|
+ wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
|
|
|
+
|
|
|
+ if (client)
|
|
|
+ return P2PS_SETUP_NONE;
|
|
|
+
|
|
|
+ if (go_wpa_s) {
|
|
|
+ if (role == P2PS_SETUP_CLIENT ||
|
|
|
+ incoming == P2PS_SETUP_GROUP_OWNER ||
|
|
|
+ p2p_client_limit_reached(go_wpa_s->p2p_group))
|
|
|
+ return P2PS_SETUP_NONE;
|
|
|
+
|
|
|
+ return P2PS_SETUP_GROUP_OWNER;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (persistent_go) {
|
|
|
+ if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
|
|
|
+ if (!incoming)
|
|
|
+ return P2PS_SETUP_GROUP_OWNER |
|
|
|
+ P2PS_SETUP_CLIENT;
|
|
|
+ if (incoming == P2PS_SETUP_NEW) {
|
|
|
+ u8 r;
|
|
|
+
|
|
|
+ if (os_get_random(&r, sizeof(r)) < 0 ||
|
|
|
+ (r & 1))
|
|
|
+ return P2PS_SETUP_CLIENT;
|
|
|
+ return P2PS_SETUP_GROUP_OWNER;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (role && role != P2PS_SETUP_NEW) {
|
|
|
+ switch (incoming) {
|
|
|
+ case P2PS_SETUP_NONE:
|
|
|
+ case P2PS_SETUP_NEW:
|
|
|
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
|
|
|
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
|
|
|
+ conncap = role;
|
|
|
+ goto grp_owner;
|
|
|
+
|
|
|
+ case P2PS_SETUP_GROUP_OWNER:
|
|
|
+
|
|
|
+ * Must be a complimentary role - cannot be a client to
|
|
|
+ * more than one peer.
|
|
|
+ */
|
|
|
+ if (incoming == role || client)
|
|
|
+ return P2PS_SETUP_NONE;
|
|
|
+
|
|
|
+ return P2PS_SETUP_CLIENT;
|
|
|
+
|
|
|
+ case P2PS_SETUP_CLIENT:
|
|
|
+
|
|
|
+ if (incoming != role) {
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER;
|
|
|
+ goto grp_owner;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ return P2PS_SETUP_NONE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * For now, we only will support ownership of one group, and being a
|
|
|
+ * client of one group. Therefore, if we have either an existing GO
|
|
|
+ * group, or an existing client group, we will not do a new GO
|
|
|
+ * negotiation, but rather try to re-use the existing groups.
|
|
|
+ */
|
|
|
+ switch (incoming) {
|
|
|
+ case P2PS_SETUP_NONE:
|
|
|
+ case P2PS_SETUP_NEW:
|
|
|
+ if (client)
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER;
|
|
|
+ else if (!owned_members)
|
|
|
+ conncap = P2PS_SETUP_NEW;
|
|
|
+ else if (incoming == P2PS_SETUP_NONE)
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
|
|
|
+ else
|
|
|
+ conncap = P2PS_SETUP_CLIENT;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case P2PS_SETUP_CLIENT:
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case P2PS_SETUP_GROUP_OWNER:
|
|
|
+ if (!client)
|
|
|
+ conncap = P2PS_SETUP_CLIENT;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
|
|
|
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
|
|
|
+ if (client)
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER;
|
|
|
+ else {
|
|
|
+ u8 r;
|
|
|
+
|
|
|
+ if (os_get_random(&r, sizeof(r)) < 0 ||
|
|
|
+ (r & 1))
|
|
|
+ conncap = P2PS_SETUP_CLIENT;
|
|
|
+ else
|
|
|
+ conncap = P2PS_SETUP_GROUP_OWNER;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return P2PS_SETUP_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+grp_owner:
|
|
|
+ if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
|
|
|
+ (!incoming && (conncap & P2PS_SETUP_NEW))) {
|
|
|
+ if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
|
|
|
+ conncap &= ~P2PS_SETUP_GROUP_OWNER;
|
|
|
+ wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
|
|
|
+ owner, owned_members, conncap);
|
|
|
+
|
|
|
+ s = wpas_p2p_get_persistent_go(wpa_s);
|
|
|
+
|
|
|
+ if (!s && !owner && p2p_no_group_iface) {
|
|
|
+ p2p_set_intended_addr(wpa_s->global->p2p,
|
|
|
+ wpa_s->own_addr);
|
|
|
+ } else if (!s && !owner) {
|
|
|
+ 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 P2PS_SETUP_NONE;
|
|
|
+ }
|
|
|
+ p2p_set_intended_addr(wpa_s->global->p2p,
|
|
|
+ wpa_s->pending_interface_addr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return conncap;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
|
|
|
enum p2p_group_removal_reason removal_reason)
|
|
|
{
|
|
@@ -6293,13 +6510,24 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
|
|
|
|
|
|
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
|
const char *config_method,
|
|
|
- enum wpas_p2p_prov_disc_use use)
|
|
|
+ enum wpas_p2p_prov_disc_use use,
|
|
|
+ struct p2ps_provision *p2ps_prov)
|
|
|
{
|
|
|
u16 config_methods;
|
|
|
|
|
|
wpa_s->p2p_fallback_to_go_neg = 0;
|
|
|
wpa_s->pending_pd_use = NORMAL_PD;
|
|
|
- if (os_strncmp(config_method, "display", 7) == 0)
|
|
|
+ if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
|
|
|
+ p2ps_prov->conncap = p2ps_group_capability(
|
|
|
+ wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
|
|
|
+ wpa_printf(MSG_DEBUG,
|
|
|
+ "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
|
|
|
+ __func__, p2ps_prov->conncap,
|
|
|
+ p2ps_prov->adv_id, p2ps_prov->conncap,
|
|
|
+ p2ps_prov->status, p2ps_prov->info);
|
|
|
+
|
|
|
+ config_methods = 0;
|
|
|
+ } else if (os_strncmp(config_method, "display", 7) == 0)
|
|
|
config_methods = WPS_CONFIG_DISPLAY;
|
|
|
else if (os_strncmp(config_method, "keypad", 6) == 0)
|
|
|
config_methods = WPS_CONFIG_KEYPAD;
|
|
@@ -6308,6 +6536,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
|
config_methods = WPS_CONFIG_PUSHBUTTON;
|
|
|
else {
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
|
|
|
+ os_free(p2ps_prov);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
@@ -6328,10 +6557,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
|
|
|
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
|
|
|
+ os_free(p2ps_prov);
|
|
|
return -1;
|
|
|
+ }
|
|
|
|
|
|
- return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, NULL,
|
|
|
+ return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
|
|
|
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
|
|
|
0, 1);
|
|
|
}
|