P2P: Use consistent Device Capability in Beacon/Probe Response
[mech_eap.git] / src / p2p / p2p_group.c
index 8e1b5ae..8d4a3cb 100644 (file)
@@ -2,14 +2,8 @@
  * Wi-Fi Direct - P2P group operations
  * Copyright (c) 2009-2010, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -39,6 +33,7 @@ struct p2p_group {
        struct p2p_data *p2p;
        struct p2p_group_config *cfg;
        struct p2p_group_member *members;
+       unsigned int num_members;
        int group_formation;
        int beacon_update;
        struct wpabuf *noa;
@@ -71,6 +66,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
        group->group_formation = 1;
        group->beacon_update = 1;
        p2p_group_update_ies(group);
+       group->cfg->idle_update(group->cfg->cb_ctx, 1);
 
        return group;
 }
@@ -89,6 +85,7 @@ static void p2p_group_free_members(struct p2p_group *group)
        struct p2p_group_member *m, *prev;
        m = group->members;
        group->members = NULL;
+       group->num_members = 0;
        while (m) {
                prev = m;
                m = m->next;
@@ -127,6 +124,8 @@ void p2p_group_deinit(struct p2p_group *group)
 
 static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
 {
+       if (m->client_info == NULL)
+               return;
        if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
                return;
        wpabuf_put_buf(ie, m->client_info);
@@ -136,19 +135,24 @@ static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
 static void p2p_group_add_common_ies(struct p2p_group *group,
                                     struct wpabuf *ie)
 {
-       u8 dev_capab = 0, group_capab = 0;
+       u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
 
        /* P2P Capability */
-       dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
-       dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+       dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
        group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
-       if (group->cfg->persistent_group)
+       if (group->cfg->persistent_group) {
                group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
-       group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+               if (group->cfg->persistent_group == 2)
+                       group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+       }
+       if (group->p2p->cfg->p2p_intra_bss)
+               group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
        if (group->group_formation)
                group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
        if (group->p2p->cross_connect)
                group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+       if (group->num_members >= group->cfg->max_clients)
+               group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
        p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -302,6 +306,36 @@ static struct wpabuf * p2p_build_client_info(const u8 *addr,
 }
 
 
+static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
+{
+       struct p2p_group_member *m, *prev;
+
+       if (group == NULL)
+               return 0;
+
+       m = group->members;
+       prev = NULL;
+       while (m) {
+               if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+                       break;
+               prev = m;
+               m = m->next;
+       }
+
+       if (m == NULL)
+               return 0;
+
+       if (prev)
+               prev->next = m->next;
+       else
+               group->members = m->next;
+       p2p_group_free_member(m);
+       group->num_members--;
+
+       return 1;
+}
+
+
 int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
                          const u8 *ie, size_t len)
 {
@@ -315,22 +349,26 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
                return -1;
        os_memcpy(m->addr, addr, ETH_ALEN);
        m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
-       if (m->p2p_ie == NULL) {
-               p2p_group_free_member(m);
-               return -1;
+       if (m->p2p_ie) {
+               m->client_info = p2p_build_client_info(addr, m->p2p_ie,
+                                                      &m->dev_capab,
+                                                      m->dev_addr);
        }
 
-       m->client_info = p2p_build_client_info(addr, m->p2p_ie, &m->dev_capab,
-                                              m->dev_addr);
-       if (m->client_info == NULL) {
-               p2p_group_free_member(m);
-               return -1;
-       }
+       p2p_group_remove_member(group, addr);
 
        m->next = group->members;
        group->members = m;
-
+       group->num_members++;
+       wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+               " to group (p2p=%d client_info=%d); num_members=%u/%u",
+               MAC2STR(addr), m->p2p_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;
 }
@@ -361,27 +399,16 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
 
 void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
 {
-       struct p2p_group_member *m, *prev;
-
-       if (group == NULL)
-               return;
-
-       m = group->members;
-       prev = NULL;
-       while (m) {
-               if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
-                       break;
-               prev = m;
-               m = m->next;
-       }
-
-       if (m) {
-               if (prev)
-                       prev->next = m->next;
-               else
-                       group->members = m->next;
-               p2p_group_free_member(m);
+       if (p2p_group_remove_member(group, addr)) {
+               wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "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);
        }
 }
 
@@ -456,6 +483,31 @@ int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
 }
 
 
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
+{
+       struct p2p_group_member *m;
+       struct p2p_message msg;
+
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_p2p_ie(p2p, &msg))
+               return 1; /* Failed to parse - assume no filter on Device ID */
+
+       if (!msg.device_id)
+               return 1; /* No filter on Device ID */
+
+       if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
+               return 1; /* Match with our P2P Device Address */
+
+       for (m = group->members; m; m = m->next) {
+               if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
+                       return 1; /* Match with group client P2P Device Address */
+       }
+
+       /* No match with Device ID */
+       return 0;
+}
+
+
 void p2p_group_notif_formation_done(struct p2p_group *group)
 {
        if (group == NULL)
@@ -475,7 +527,7 @@ int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
        } else {
                if (group->noa) {
                        if (wpabuf_size(group->noa) >= noa_len) {
-                               group->noa->size = 0;
+                               group->noa->used = 0;
                                wpabuf_put_data(group->noa, noa, noa_len);
                        } else {
                                wpabuf_free(group->noa);
@@ -524,6 +576,19 @@ static struct p2p_group_member * p2p_group_get_client_iface(
 }
 
 
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
+{
+       struct p2p_group_member *m;
+
+       if (group == NULL)
+               return NULL;
+       m = p2p_group_get_client_iface(group, addr);
+       if (m && !is_zero_ether_addr(m->dev_addr))
+               return m->dev_addr;
+       return NULL;
+}
+
+
 static struct wpabuf * p2p_build_go_disc_req(void)
 {
        struct wpabuf *buf;
@@ -547,7 +612,7 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
        int freq;
 
        m = p2p_group_get_client(group, dev_id);
-       if (m == NULL) {
+       if (m == NULL || m->client_info == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this "
                           "group " MACSTR,
                           MAC2STR(group->cfg->interface_addr));
@@ -601,7 +666,7 @@ u8 p2p_group_presence_req(struct p2p_group *group,
        int curr_noa_len;
 
        m = p2p_group_get_client_iface(group, client_interface_addr);
-       if (m == NULL) {
+       if (m == NULL || m->client_info == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: Client was not in this group");
                return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
        }
@@ -623,8 +688,57 @@ u8 p2p_group_presence_req(struct p2p_group *group,
                            curr_noa_len);
 
        /* TODO: properly process request and store copy */
-       if (curr_noa_len > 0)
+       if (curr_noa_len > 0 || curr_noa_len == -1)
                return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 
        return P2P_SC_SUCCESS;
 }
+
+
+unsigned int p2p_get_group_num_members(struct p2p_group *group)
+{
+       return group->num_members;
+}
+
+
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
+{
+       struct p2p_group_member *iter = *next;
+
+       if (!iter)
+               iter = group->members;
+       else
+               iter = iter->next;
+
+       *next = iter;
+
+       if (!iter)
+               return NULL;
+
+       return iter->addr;
+}
+
+
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
+{
+       struct p2p_group_member *m;
+
+       for (m = group->members; m; m = m->next) {
+               if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+                               size_t group_id_len)
+{
+       if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
+               return 0;
+       if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+               return 0;
+       return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
+                        group->cfg->ssid_len) == 0;
+}