Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / p2p / p2p_group.c
index 4ee84cd..0d66993 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"
@@ -17,6 +11,7 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
@@ -28,6 +23,7 @@ struct p2p_group_member {
        u8 addr[ETH_ALEN]; /* P2P Interface Address */
        u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
        struct wpabuf *p2p_ie;
+       struct wpabuf *wfd_ie;
        struct wpabuf *client_info;
        u8 dev_capab;
 };
@@ -39,15 +35,14 @@ 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;
+       struct wpabuf *wfd_ie;
 };
 
 
-static void p2p_group_update_ies(struct p2p_group *group);
-
-
 struct p2p_group * p2p_group_init(struct p2p_data *p2p,
                                  struct p2p_group_config *config)
 {
@@ -57,8 +52,8 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
        if (group == NULL)
                return NULL;
 
-       groups = os_realloc(p2p->groups, (p2p->num_groups + 1) *
-                           sizeof(struct p2p_group *));
+       groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
+                                 sizeof(struct p2p_group *));
        if (groups == NULL) {
                os_free(group);
                return NULL;
@@ -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;
 }
@@ -78,6 +74,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 
 static void p2p_group_free_member(struct p2p_group_member *m)
 {
+       wpabuf_free(m->wfd_ie);
        wpabuf_free(m->p2p_ie);
        wpabuf_free(m->client_info);
        os_free(m);
@@ -89,6 +86,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;
@@ -121,12 +119,15 @@ void p2p_group_deinit(struct p2p_group *group)
        p2p_group_free_members(group);
        os_free(group->cfg);
        wpabuf_free(group->noa);
+       wpabuf_free(group->wfd_ie);
        os_free(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,20 +137,25 @@ 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;
+               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;
+       group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
        p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -165,15 +171,68 @@ static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
 }
 
 
+static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
+{
+       struct wpabuf *ie;
+       const u8 *pos, *end;
+       size_t len;
+
+       if (subelems == NULL)
+               return NULL;
+
+       len = wpabuf_len(subelems) + 100;
+
+       ie = wpabuf_alloc(len);
+       if (ie == NULL)
+               return NULL;
+
+       pos = wpabuf_head(subelems);
+       end = pos + wpabuf_len(subelems);
+
+       while (end > pos) {
+               size_t frag_len = end - pos;
+               if (frag_len > 251)
+                       frag_len = 251;
+               wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+               wpabuf_put_u8(ie, 4 + frag_len);
+               wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       return ie;
+}
+
+
 static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
        struct wpabuf *ie;
        u8 *len;
+       size_t extra = 0;
 
-       ie = wpabuf_alloc(257);
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->p2p->wfd_ie_beacon)
+               extra = wpabuf_len(group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+               extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
+       ie = wpabuf_alloc(257 + extra);
        if (ie == NULL)
                return NULL;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->p2p->wfd_ie_beacon)
+               wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+               wpabuf_put_buf(ie,
+                              group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
        len = p2p_buf_add_ie_hdr(ie);
        p2p_group_add_common_ies(group, ie);
        p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
@@ -184,44 +243,248 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 }
 
 
-static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+#ifdef CONFIG_WIFI_DISPLAY
+
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
+{
+       return g->wfd_ie;
+}
+
+
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
 {
-       u8 *group_info;
        struct wpabuf *ie;
+       const u8 *pos, *end;
+
+       if (subelems == NULL)
+               return NULL;
+
+       ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
+       if (ie == NULL)
+               return NULL;
+
+       pos = wpabuf_head(subelems);
+       end = pos + wpabuf_len(subelems);
+
+       while (end > pos) {
+               size_t frag_len = end - pos;
+               if (frag_len > 251)
+                       frag_len = 251;
+               wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+               wpabuf_put_u8(ie, 4 + frag_len);
+               wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       return ie;
+}
+
+
+static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
+                                          struct p2p_group_member *m)
+{
+       const u8 *pos, *end;
+       const u8 *dev_info = NULL;
+       const u8 *assoc_bssid = NULL;
+       const u8 *coupled_sink = NULL;
+       u8 zero_addr[ETH_ALEN];
+
+       if (m->wfd_ie == NULL)
+               return 0;
+
+       os_memset(zero_addr, 0, ETH_ALEN);
+       pos = wpabuf_head_u8(m->wfd_ie);
+       end = pos + wpabuf_len(m->wfd_ie);
+       while (pos + 1 < end) {
+               u8 id;
+               u16 len;
+
+               id = *pos++;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + len > end)
+                       break;
+
+               switch (id) {
+               case WFD_SUBELEM_DEVICE_INFO:
+                       if (len < 6)
+                               break;
+                       dev_info = pos;
+                       break;
+               case WFD_SUBELEM_ASSOCIATED_BSSID:
+                       if (len < ETH_ALEN)
+                               break;
+                       assoc_bssid = pos;
+                       break;
+               case WFD_SUBELEM_COUPLED_SINK:
+                       if (len < 1 + ETH_ALEN)
+                               break;
+                       coupled_sink = pos;
+                       break;
+               }
+
+               pos += len;
+       }
+
+       if (dev_info == NULL)
+               return 0;
+
+       wpabuf_put_u8(buf, 23);
+       wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
+       if (assoc_bssid)
+               wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
+       else
+               wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+       wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
+       wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
+       if (coupled_sink) {
+               wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
+       } else {
+               wpabuf_put_u8(buf, 0);
+               wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+       }
+
+       return 1;
+}
+
+
+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;
 
-       ie = wpabuf_alloc(257);
-       if (ie == NULL)
+       if (!group->p2p->wfd_ie_probe_resp)
                return NULL;
 
-       len = p2p_buf_add_ie_hdr(ie);
+       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;
+       }
 
-       p2p_group_add_common_ies(group, ie);
-       p2p_group_add_noa(ie, group->noa);
+       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);
+       }
 
-       /* P2P Device Info */
-       p2p_buf_add_device_info(ie, group->p2p, NULL);
-
-       /* P2P Group Info */
-       group_info = wpabuf_put(ie, 0);
-       wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
-       wpabuf_put_le16(ie, 0); /* Length to be filled */
-       for (m = group->members; m; m = m->next)
-               p2p_client_info(ie, m);
+       wfd_ie = wifi_display_encaps(wfd_subelems);
+       wpabuf_free(wfd_subelems);
+
+       return wfd_ie;
+}
+
+static void wifi_display_group_update(struct p2p_group *group)
+{
+       wpabuf_free(group->wfd_ie);
+       group->wfd_ie = wifi_display_build_go_ie(group);
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+                           int max_clients)
+{
+       u8 *group_info;
+       int count = 0;
+       struct p2p_group_member *m;
+
+       p2p_dbg(group->p2p, "* P2P Group Info");
+       group_info = wpabuf_put(buf, 0);
+       wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+       wpabuf_put_le16(buf, 0); /* Length to be filled */
+       for (m = group->members; m; m = m->next) {
+               p2p_client_info(buf, m);
+               count++;
+               if (max_clients >= 0 && count >= max_clients)
+                       break;
+       }
        WPA_PUT_LE16(group_info + 1,
-                    (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+                    (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+       p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+                            group->cfg->ssid_len);
+}
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+       struct wpabuf *p2p_subelems, *ie;
+
+       p2p_subelems = wpabuf_alloc(500);
+       if (p2p_subelems == NULL)
+               return NULL;
+
+       p2p_group_add_common_ies(group, p2p_subelems);
+       p2p_group_add_noa(p2p_subelems, group->noa);
+
+       /* P2P Device Info */
+       p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
+
+       /* P2P Group Info: Only when at least one P2P Client is connected */
+       if (group->members)
+               p2p_buf_add_group_info(group, p2p_subelems, -1);
+
+       ie = p2p_group_encaps_probe_resp(p2p_subelems);
+       wpabuf_free(p2p_subelems);
+
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
+               struct wpabuf *extra;
+               extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
+               ie = wpabuf_concat(extra, ie);
+       }
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie) {
+               struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
+               ie = wpabuf_concat(wfd, ie);
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       p2p_buf_update_ie_hdr(ie, len);
        return ie;
 }
 
 
-static void p2p_group_update_ies(struct p2p_group *group)
+void p2p_group_update_ies(struct p2p_group *group)
 {
        struct wpabuf *beacon_ie;
        struct wpabuf *probe_resp_ie;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       wifi_display_group_update(group);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        probe_resp_ie = p2p_group_build_probe_resp_ie(group);
        if (probe_resp_ie == NULL)
                return;
@@ -303,6 +566,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)
 {
@@ -311,37 +604,37 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
        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 == 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);
        }
+#ifdef CONFIG_WIFI_DISPLAY
+       m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       m->client_info = p2p_build_client_info(addr, m->p2p_ie, &m->dev_capab,
-                                              m->dev_addr);
-       if (m->client_info == NULL) {
-               /*
-                * This can happen, e.g., when a P2P client connects to a P2P
-                * group using the infrastructure WLAN interface instead of
-                * P2P group interface. In that case, the P2P client may behave
-                * as if the GO would be a P2P Manager WLAN AP.
-                */
-               wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG,
-                       "P2P: Could not build Client Info from P2P IE - "
-                       "assume " MACSTR " is not a P2P client",
-                       MAC2STR(addr));
-               p2p_group_free_member(m);
-               return 0;
-       }
+       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;
 }
@@ -351,6 +644,16 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
 {
        struct wpabuf *resp;
        u8 *rlen;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               extra = wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+               extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
 
        /*
         * (Re)Association Response - P2P IE
@@ -358,9 +661,20 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
         *      denied)
         * Extended Listen Timing (may be present)
         */
-       resp = wpabuf_alloc(20);
+       resp = wpabuf_alloc(20 + extra);
        if (resp == NULL)
                return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               wpabuf_put_buf(resp, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+               wpabuf_put_buf(resp,
+                              group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
        rlen = p2p_buf_add_ie_hdr(resp);
        if (status != P2P_SC_SUCCESS)
                p2p_buf_add_status(resp, status);
@@ -372,27 +686,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)) {
+               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);
        }
 }
 
@@ -467,6 +770,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)
@@ -486,7 +814,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);
@@ -535,6 +863,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;
@@ -558,21 +899,19 @@ 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) {
-               wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this "
-                          "group " MACSTR,
-                          MAC2STR(group->cfg->interface_addr));
+       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)) {
-               wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
-                          "client discoverability");
+               p2p_dbg(group->p2p, "Requested client does not support client discoverability");
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be "
-                  "sent to " MACSTR, MAC2STR(dev_id));
+       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)
@@ -587,8 +926,7 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
                                  group->cfg->interface_addr,
                                  wpabuf_head(req), wpabuf_len(req), 200) < 0)
        {
-               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-                       "P2P: Failed to send Action frame");
+               p2p_dbg(p2p, "Failed to send Action frame");
        }
 
        wpabuf_free(req);
@@ -612,8 +950,8 @@ 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) {
-               wpa_printf(MSG_DEBUG, "P2P: Client was not in this group");
+       if (m == NULL || m->client_info == NULL) {
+               p2p_dbg(group->p2p, "Client was not in this group");
                return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
        }
 
@@ -626,16 +964,150 @@ u8 p2p_group_presence_req(struct p2p_group *group,
        else
                curr_noa_len = -1;
        if (curr_noa_len < 0)
-               wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+               p2p_dbg(group->p2p, "Failed to fetch current NoA");
        else if (curr_noa_len == 0)
-               wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized");
+               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)
+       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)
+{
+       if (!group)
+               return 0;
+
+       return group->num_members;
+}
+
+
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+       if (!group || !group->cfg)
+               return 1;
+
+       return group->num_members >= group->cfg->max_clients;
+}
+
+
+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->dev_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;
+}
+
+
+void p2p_group_force_beacon_update_ies(struct p2p_group *group)
+{
+       group->beacon_update = 1;
+       p2p_group_update_ies(group);
+}
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+       return group->cfg->freq;
+}
+
+
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
+{
+       return group->cfg;
+}
+
+
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+                           int (*group_callback)(struct p2p_group *group,
+                                                 void *user_data),
+                           void *user_data)
+{
+       unsigned int i;
+
+       for (i = 0; i < p2p->num_groups; i++) {
+               if (!group_callback(p2p->groups[i], user_data))
+                       break;
+       }
+}
+
+
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+                              unsigned int *num)
+
+{
+       struct p2p_channels intersect, res;
+       struct p2p_group_member *m;
+
+       if (!group || !common_freqs || !num)
+               return -1;
+
+       os_memset(&intersect, 0, sizeof(intersect));
+       os_memset(&res, 0, sizeof(res));
+
+       p2p_channels_union(&intersect, &group->p2p->cfg->channels,
+                          &intersect);
+
+       p2p_channels_dump(group->p2p,
+                         "Group common freqs before iterating members",
+                         &intersect);
+
+       for (m = group->members; m; m = m->next) {
+               struct p2p_device *dev;
+
+               dev = p2p_get_device(group->p2p, m->dev_addr);
+               if (!dev)
+                       continue;
+
+               p2p_channels_intersect(&intersect, &dev->channels, &res);
+               intersect = res;
+       }
+
+       p2p_channels_dump(group->p2p, "Group common channels", &intersect);
+
+       os_memset(common_freqs, 0, *num * sizeof(int));
+       *num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
+
+       return 0;
+}