1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo * Wi-Fi Direct - P2P group operations
3f05cddf9SRui Paulo * Copyright (c) 2009-2010, Atheros Communications
4f05cddf9SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7f05cddf9SRui Paulo */
8f05cddf9SRui Paulo
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo
11f05cddf9SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
13f05cddf9SRui Paulo #include "common/ieee802_11_common.h"
145b9c547cSRui Paulo #include "common/wpa_ctrl.h"
15f05cddf9SRui Paulo #include "wps/wps_defs.h"
16f05cddf9SRui Paulo #include "wps/wps_i.h"
17f05cddf9SRui Paulo #include "p2p_i.h"
18f05cddf9SRui Paulo #include "p2p.h"
19f05cddf9SRui Paulo
20f05cddf9SRui Paulo
21f05cddf9SRui Paulo struct p2p_group_member {
22f05cddf9SRui Paulo struct p2p_group_member *next;
23f05cddf9SRui Paulo u8 addr[ETH_ALEN]; /* P2P Interface Address */
24f05cddf9SRui Paulo u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
25f05cddf9SRui Paulo struct wpabuf *p2p_ie;
26f05cddf9SRui Paulo struct wpabuf *wfd_ie;
27f05cddf9SRui Paulo struct wpabuf *client_info;
28f05cddf9SRui Paulo u8 dev_capab;
29f05cddf9SRui Paulo };
30f05cddf9SRui Paulo
31f05cddf9SRui Paulo /**
32f05cddf9SRui Paulo * struct p2p_group - Internal P2P module per-group data
33f05cddf9SRui Paulo */
34f05cddf9SRui Paulo struct p2p_group {
35f05cddf9SRui Paulo struct p2p_data *p2p;
36f05cddf9SRui Paulo struct p2p_group_config *cfg;
37f05cddf9SRui Paulo struct p2p_group_member *members;
38f05cddf9SRui Paulo unsigned int num_members;
39f05cddf9SRui Paulo int group_formation;
40f05cddf9SRui Paulo int beacon_update;
41f05cddf9SRui Paulo struct wpabuf *noa;
42f05cddf9SRui Paulo struct wpabuf *wfd_ie;
43f05cddf9SRui Paulo };
44f05cddf9SRui Paulo
45f05cddf9SRui Paulo
p2p_group_init(struct p2p_data * p2p,struct p2p_group_config * config)46f05cddf9SRui Paulo struct p2p_group * p2p_group_init(struct p2p_data *p2p,
47f05cddf9SRui Paulo struct p2p_group_config *config)
48f05cddf9SRui Paulo {
49f05cddf9SRui Paulo struct p2p_group *group, **groups;
50f05cddf9SRui Paulo
51f05cddf9SRui Paulo group = os_zalloc(sizeof(*group));
52f05cddf9SRui Paulo if (group == NULL)
53f05cddf9SRui Paulo return NULL;
54f05cddf9SRui Paulo
55f05cddf9SRui Paulo groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
56f05cddf9SRui Paulo sizeof(struct p2p_group *));
57f05cddf9SRui Paulo if (groups == NULL) {
58f05cddf9SRui Paulo os_free(group);
59f05cddf9SRui Paulo return NULL;
60f05cddf9SRui Paulo }
61f05cddf9SRui Paulo groups[p2p->num_groups++] = group;
62f05cddf9SRui Paulo p2p->groups = groups;
63f05cddf9SRui Paulo
64f05cddf9SRui Paulo group->p2p = p2p;
65f05cddf9SRui Paulo group->cfg = config;
66f05cddf9SRui Paulo group->group_formation = 1;
67f05cddf9SRui Paulo group->beacon_update = 1;
68f05cddf9SRui Paulo p2p_group_update_ies(group);
69f05cddf9SRui Paulo group->cfg->idle_update(group->cfg->cb_ctx, 1);
70f05cddf9SRui Paulo
71f05cddf9SRui Paulo return group;
72f05cddf9SRui Paulo }
73f05cddf9SRui Paulo
74f05cddf9SRui Paulo
p2p_group_free_member(struct p2p_group_member * m)75f05cddf9SRui Paulo static void p2p_group_free_member(struct p2p_group_member *m)
76f05cddf9SRui Paulo {
77f05cddf9SRui Paulo wpabuf_free(m->wfd_ie);
78f05cddf9SRui Paulo wpabuf_free(m->p2p_ie);
79f05cddf9SRui Paulo wpabuf_free(m->client_info);
80f05cddf9SRui Paulo os_free(m);
81f05cddf9SRui Paulo }
82f05cddf9SRui Paulo
83f05cddf9SRui Paulo
p2p_group_free_members(struct p2p_group * group)84f05cddf9SRui Paulo static void p2p_group_free_members(struct p2p_group *group)
85f05cddf9SRui Paulo {
86f05cddf9SRui Paulo struct p2p_group_member *m, *prev;
87f05cddf9SRui Paulo m = group->members;
88f05cddf9SRui Paulo group->members = NULL;
89f05cddf9SRui Paulo group->num_members = 0;
90f05cddf9SRui Paulo while (m) {
91f05cddf9SRui Paulo prev = m;
92f05cddf9SRui Paulo m = m->next;
93f05cddf9SRui Paulo p2p_group_free_member(prev);
94f05cddf9SRui Paulo }
95f05cddf9SRui Paulo }
96f05cddf9SRui Paulo
97f05cddf9SRui Paulo
p2p_group_deinit(struct p2p_group * group)98f05cddf9SRui Paulo void p2p_group_deinit(struct p2p_group *group)
99f05cddf9SRui Paulo {
100f05cddf9SRui Paulo size_t g;
101f05cddf9SRui Paulo struct p2p_data *p2p;
102f05cddf9SRui Paulo
103f05cddf9SRui Paulo if (group == NULL)
104f05cddf9SRui Paulo return;
105f05cddf9SRui Paulo
106f05cddf9SRui Paulo p2p = group->p2p;
107f05cddf9SRui Paulo
108f05cddf9SRui Paulo for (g = 0; g < p2p->num_groups; g++) {
109f05cddf9SRui Paulo if (p2p->groups[g] == group) {
110f05cddf9SRui Paulo while (g + 1 < p2p->num_groups) {
111f05cddf9SRui Paulo p2p->groups[g] = p2p->groups[g + 1];
112f05cddf9SRui Paulo g++;
113f05cddf9SRui Paulo }
114f05cddf9SRui Paulo p2p->num_groups--;
115f05cddf9SRui Paulo break;
116f05cddf9SRui Paulo }
117f05cddf9SRui Paulo }
118f05cddf9SRui Paulo
119f05cddf9SRui Paulo p2p_group_free_members(group);
120f05cddf9SRui Paulo os_free(group->cfg);
121f05cddf9SRui Paulo wpabuf_free(group->noa);
122f05cddf9SRui Paulo wpabuf_free(group->wfd_ie);
123f05cddf9SRui Paulo os_free(group);
124f05cddf9SRui Paulo }
125f05cddf9SRui Paulo
126f05cddf9SRui Paulo
p2p_client_info(struct wpabuf * ie,struct p2p_group_member * m)127f05cddf9SRui Paulo static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
128f05cddf9SRui Paulo {
129f05cddf9SRui Paulo if (m->client_info == NULL)
130f05cddf9SRui Paulo return;
131f05cddf9SRui Paulo if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
132f05cddf9SRui Paulo return;
133f05cddf9SRui Paulo wpabuf_put_buf(ie, m->client_info);
134f05cddf9SRui Paulo }
135f05cddf9SRui Paulo
136f05cddf9SRui Paulo
p2p_group_add_common_ies(struct p2p_group * group,struct wpabuf * ie)137f05cddf9SRui Paulo static void p2p_group_add_common_ies(struct p2p_group *group,
138f05cddf9SRui Paulo struct wpabuf *ie)
139f05cddf9SRui Paulo {
140f05cddf9SRui Paulo u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
141f05cddf9SRui Paulo
142f05cddf9SRui Paulo /* P2P Capability */
143f05cddf9SRui Paulo dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
144f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
145f05cddf9SRui Paulo if (group->cfg->persistent_group) {
146f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
147f05cddf9SRui Paulo if (group->cfg->persistent_group == 2)
148f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
149f05cddf9SRui Paulo }
150f05cddf9SRui Paulo if (group->p2p->cfg->p2p_intra_bss)
151f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
152f05cddf9SRui Paulo if (group->group_formation)
153f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
154f05cddf9SRui Paulo if (group->p2p->cross_connect)
155f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
156f05cddf9SRui Paulo if (group->num_members >= group->cfg->max_clients)
157f05cddf9SRui Paulo group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
158780fb4a2SCy Schubert if (group->cfg->ip_addr_alloc)
1595b9c547cSRui Paulo group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
160f05cddf9SRui Paulo p2p_buf_add_capability(ie, dev_capab, group_capab);
161f05cddf9SRui Paulo }
162f05cddf9SRui Paulo
163f05cddf9SRui Paulo
p2p_group_add_noa(struct wpabuf * ie,struct wpabuf * noa)164f05cddf9SRui Paulo static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
165f05cddf9SRui Paulo {
166f05cddf9SRui Paulo if (noa == NULL)
167f05cddf9SRui Paulo return;
168f05cddf9SRui Paulo /* Notice of Absence */
169f05cddf9SRui Paulo wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
170f05cddf9SRui Paulo wpabuf_put_le16(ie, wpabuf_len(noa));
171f05cddf9SRui Paulo wpabuf_put_buf(ie, noa);
172f05cddf9SRui Paulo }
173f05cddf9SRui Paulo
174f05cddf9SRui Paulo
p2p_group_encaps_probe_resp(struct wpabuf * subelems)1755b9c547cSRui Paulo static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
1765b9c547cSRui Paulo {
1775b9c547cSRui Paulo struct wpabuf *ie;
1785b9c547cSRui Paulo const u8 *pos, *end;
1795b9c547cSRui Paulo size_t len;
1805b9c547cSRui Paulo
1815b9c547cSRui Paulo if (subelems == NULL)
1825b9c547cSRui Paulo return NULL;
1835b9c547cSRui Paulo
1845b9c547cSRui Paulo len = wpabuf_len(subelems) + 100;
1855b9c547cSRui Paulo
1865b9c547cSRui Paulo ie = wpabuf_alloc(len);
1875b9c547cSRui Paulo if (ie == NULL)
1885b9c547cSRui Paulo return NULL;
1895b9c547cSRui Paulo
1905b9c547cSRui Paulo pos = wpabuf_head(subelems);
1915b9c547cSRui Paulo end = pos + wpabuf_len(subelems);
1925b9c547cSRui Paulo
1935b9c547cSRui Paulo while (end > pos) {
1945b9c547cSRui Paulo size_t frag_len = end - pos;
1955b9c547cSRui Paulo if (frag_len > 251)
1965b9c547cSRui Paulo frag_len = 251;
1975b9c547cSRui Paulo wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
1985b9c547cSRui Paulo wpabuf_put_u8(ie, 4 + frag_len);
1995b9c547cSRui Paulo wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
2005b9c547cSRui Paulo wpabuf_put_data(ie, pos, frag_len);
2015b9c547cSRui Paulo pos += frag_len;
2025b9c547cSRui Paulo }
2035b9c547cSRui Paulo
2045b9c547cSRui Paulo return ie;
2055b9c547cSRui Paulo }
2065b9c547cSRui Paulo
2075b9c547cSRui Paulo
p2p_group_build_beacon_ie(struct p2p_group * group)208f05cddf9SRui Paulo static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
209f05cddf9SRui Paulo {
210f05cddf9SRui Paulo struct wpabuf *ie;
211f05cddf9SRui Paulo u8 *len;
212f05cddf9SRui Paulo size_t extra = 0;
213f05cddf9SRui Paulo
214f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
215f05cddf9SRui Paulo if (group->p2p->wfd_ie_beacon)
216f05cddf9SRui Paulo extra = wpabuf_len(group->p2p->wfd_ie_beacon);
217f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
218f05cddf9SRui Paulo
2195b9c547cSRui Paulo if (group->p2p->vendor_elem &&
2205b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
2215b9c547cSRui Paulo extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
2225b9c547cSRui Paulo
223f05cddf9SRui Paulo ie = wpabuf_alloc(257 + extra);
224f05cddf9SRui Paulo if (ie == NULL)
225f05cddf9SRui Paulo return NULL;
226f05cddf9SRui Paulo
227f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
228f05cddf9SRui Paulo if (group->p2p->wfd_ie_beacon)
229f05cddf9SRui Paulo wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
230f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
231f05cddf9SRui Paulo
2325b9c547cSRui Paulo if (group->p2p->vendor_elem &&
2335b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
2345b9c547cSRui Paulo wpabuf_put_buf(ie,
2355b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
2365b9c547cSRui Paulo
237f05cddf9SRui Paulo len = p2p_buf_add_ie_hdr(ie);
238f05cddf9SRui Paulo p2p_group_add_common_ies(group, ie);
239f05cddf9SRui Paulo p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
240f05cddf9SRui Paulo p2p_group_add_noa(ie, group->noa);
241f05cddf9SRui Paulo p2p_buf_update_ie_hdr(ie, len);
242f05cddf9SRui Paulo
243f05cddf9SRui Paulo return ie;
244f05cddf9SRui Paulo }
245f05cddf9SRui Paulo
246f05cddf9SRui Paulo
247f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
248f05cddf9SRui Paulo
p2p_group_get_wfd_ie(struct p2p_group * g)249f05cddf9SRui Paulo struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
250f05cddf9SRui Paulo {
251f05cddf9SRui Paulo return g->wfd_ie;
252f05cddf9SRui Paulo }
253f05cddf9SRui Paulo
254f05cddf9SRui Paulo
wifi_display_encaps(struct wpabuf * subelems)255f05cddf9SRui Paulo struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
256f05cddf9SRui Paulo {
257f05cddf9SRui Paulo struct wpabuf *ie;
258f05cddf9SRui Paulo const u8 *pos, *end;
259f05cddf9SRui Paulo
260f05cddf9SRui Paulo if (subelems == NULL)
261f05cddf9SRui Paulo return NULL;
262f05cddf9SRui Paulo
263f05cddf9SRui Paulo ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
264f05cddf9SRui Paulo if (ie == NULL)
265f05cddf9SRui Paulo return NULL;
266f05cddf9SRui Paulo
267f05cddf9SRui Paulo pos = wpabuf_head(subelems);
268f05cddf9SRui Paulo end = pos + wpabuf_len(subelems);
269f05cddf9SRui Paulo
270f05cddf9SRui Paulo while (end > pos) {
271f05cddf9SRui Paulo size_t frag_len = end - pos;
272f05cddf9SRui Paulo if (frag_len > 251)
273f05cddf9SRui Paulo frag_len = 251;
274f05cddf9SRui Paulo wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
275f05cddf9SRui Paulo wpabuf_put_u8(ie, 4 + frag_len);
276f05cddf9SRui Paulo wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
277f05cddf9SRui Paulo wpabuf_put_data(ie, pos, frag_len);
278f05cddf9SRui Paulo pos += frag_len;
279f05cddf9SRui Paulo }
280f05cddf9SRui Paulo
281f05cddf9SRui Paulo return ie;
282f05cddf9SRui Paulo }
283f05cddf9SRui Paulo
284f05cddf9SRui Paulo
wifi_display_add_dev_info_descr(struct wpabuf * buf,struct p2p_group_member * m)285f05cddf9SRui Paulo static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
286f05cddf9SRui Paulo struct p2p_group_member *m)
287f05cddf9SRui Paulo {
288f05cddf9SRui Paulo const u8 *pos, *end;
289f05cddf9SRui Paulo const u8 *dev_info = NULL;
290f05cddf9SRui Paulo const u8 *assoc_bssid = NULL;
291f05cddf9SRui Paulo const u8 *coupled_sink = NULL;
292f05cddf9SRui Paulo u8 zero_addr[ETH_ALEN];
293f05cddf9SRui Paulo
294f05cddf9SRui Paulo if (m->wfd_ie == NULL)
295f05cddf9SRui Paulo return 0;
296f05cddf9SRui Paulo
297f05cddf9SRui Paulo os_memset(zero_addr, 0, ETH_ALEN);
298f05cddf9SRui Paulo pos = wpabuf_head_u8(m->wfd_ie);
299f05cddf9SRui Paulo end = pos + wpabuf_len(m->wfd_ie);
300780fb4a2SCy Schubert while (end - pos >= 3) {
301f05cddf9SRui Paulo u8 id;
302f05cddf9SRui Paulo u16 len;
303f05cddf9SRui Paulo
304f05cddf9SRui Paulo id = *pos++;
305f05cddf9SRui Paulo len = WPA_GET_BE16(pos);
306f05cddf9SRui Paulo pos += 2;
307780fb4a2SCy Schubert if (len > end - pos)
308f05cddf9SRui Paulo break;
309f05cddf9SRui Paulo
310f05cddf9SRui Paulo switch (id) {
311f05cddf9SRui Paulo case WFD_SUBELEM_DEVICE_INFO:
312f05cddf9SRui Paulo if (len < 6)
313f05cddf9SRui Paulo break;
314f05cddf9SRui Paulo dev_info = pos;
315f05cddf9SRui Paulo break;
316f05cddf9SRui Paulo case WFD_SUBELEM_ASSOCIATED_BSSID:
317f05cddf9SRui Paulo if (len < ETH_ALEN)
318f05cddf9SRui Paulo break;
319f05cddf9SRui Paulo assoc_bssid = pos;
320f05cddf9SRui Paulo break;
321f05cddf9SRui Paulo case WFD_SUBELEM_COUPLED_SINK:
322f05cddf9SRui Paulo if (len < 1 + ETH_ALEN)
323f05cddf9SRui Paulo break;
324f05cddf9SRui Paulo coupled_sink = pos;
325f05cddf9SRui Paulo break;
326f05cddf9SRui Paulo }
327f05cddf9SRui Paulo
328f05cddf9SRui Paulo pos += len;
329f05cddf9SRui Paulo }
330f05cddf9SRui Paulo
331f05cddf9SRui Paulo if (dev_info == NULL)
332f05cddf9SRui Paulo return 0;
333f05cddf9SRui Paulo
334f05cddf9SRui Paulo wpabuf_put_u8(buf, 23);
335f05cddf9SRui Paulo wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
336f05cddf9SRui Paulo if (assoc_bssid)
337f05cddf9SRui Paulo wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
338f05cddf9SRui Paulo else
339f05cddf9SRui Paulo wpabuf_put_data(buf, zero_addr, ETH_ALEN);
340f05cddf9SRui Paulo wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
341f05cddf9SRui Paulo wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
342f05cddf9SRui Paulo if (coupled_sink) {
343f05cddf9SRui Paulo wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
344f05cddf9SRui Paulo } else {
345f05cddf9SRui Paulo wpabuf_put_u8(buf, 0);
346f05cddf9SRui Paulo wpabuf_put_data(buf, zero_addr, ETH_ALEN);
347f05cddf9SRui Paulo }
348f05cddf9SRui Paulo
349f05cddf9SRui Paulo return 1;
350f05cddf9SRui Paulo }
351f05cddf9SRui Paulo
352f05cddf9SRui Paulo
353f05cddf9SRui Paulo static struct wpabuf *
wifi_display_build_go_ie(struct p2p_group * group)354f05cddf9SRui Paulo wifi_display_build_go_ie(struct p2p_group *group)
355f05cddf9SRui Paulo {
356f05cddf9SRui Paulo struct wpabuf *wfd_subelems, *wfd_ie;
357f05cddf9SRui Paulo struct p2p_group_member *m;
358f05cddf9SRui Paulo u8 *len;
359f05cddf9SRui Paulo unsigned int count = 0;
360f05cddf9SRui Paulo
361f05cddf9SRui Paulo if (!group->p2p->wfd_ie_probe_resp)
362f05cddf9SRui Paulo return NULL;
363f05cddf9SRui Paulo
364f05cddf9SRui Paulo wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
365f05cddf9SRui Paulo group->num_members * 24 + 100);
366f05cddf9SRui Paulo if (wfd_subelems == NULL)
367f05cddf9SRui Paulo return NULL;
368f05cddf9SRui Paulo if (group->p2p->wfd_dev_info)
369f05cddf9SRui Paulo wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
37085732ac8SCy Schubert if (group->p2p->wfd_r2_dev_info)
37185732ac8SCy Schubert wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info);
372f05cddf9SRui Paulo if (group->p2p->wfd_assoc_bssid)
373f05cddf9SRui Paulo wpabuf_put_buf(wfd_subelems,
374f05cddf9SRui Paulo group->p2p->wfd_assoc_bssid);
375f05cddf9SRui Paulo if (group->p2p->wfd_coupled_sink_info)
376f05cddf9SRui Paulo wpabuf_put_buf(wfd_subelems,
377f05cddf9SRui Paulo group->p2p->wfd_coupled_sink_info);
378f05cddf9SRui Paulo
379f05cddf9SRui Paulo /* Build WFD Session Info */
380f05cddf9SRui Paulo wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
381f05cddf9SRui Paulo len = wpabuf_put(wfd_subelems, 2);
382f05cddf9SRui Paulo m = group->members;
383f05cddf9SRui Paulo while (m) {
384f05cddf9SRui Paulo if (wifi_display_add_dev_info_descr(wfd_subelems, m))
385f05cddf9SRui Paulo count++;
386f05cddf9SRui Paulo m = m->next;
387f05cddf9SRui Paulo }
388f05cddf9SRui Paulo
389f05cddf9SRui Paulo if (count == 0) {
390f05cddf9SRui Paulo /* No Wi-Fi Display clients - do not include subelement */
391f05cddf9SRui Paulo wfd_subelems->used -= 3;
392f05cddf9SRui Paulo } else {
393f05cddf9SRui Paulo WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
394f05cddf9SRui Paulo 2);
3955b9c547cSRui Paulo p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors",
396f05cddf9SRui Paulo count);
397f05cddf9SRui Paulo }
398f05cddf9SRui Paulo
399f05cddf9SRui Paulo wfd_ie = wifi_display_encaps(wfd_subelems);
400f05cddf9SRui Paulo wpabuf_free(wfd_subelems);
401f05cddf9SRui Paulo
402f05cddf9SRui Paulo return wfd_ie;
403f05cddf9SRui Paulo }
404f05cddf9SRui Paulo
wifi_display_group_update(struct p2p_group * group)405f05cddf9SRui Paulo static void wifi_display_group_update(struct p2p_group *group)
406f05cddf9SRui Paulo {
407f05cddf9SRui Paulo wpabuf_free(group->wfd_ie);
408f05cddf9SRui Paulo group->wfd_ie = wifi_display_build_go_ie(group);
409f05cddf9SRui Paulo }
410f05cddf9SRui Paulo
411f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
412f05cddf9SRui Paulo
413f05cddf9SRui Paulo
p2p_buf_add_group_info(struct p2p_group * group,struct wpabuf * buf,int max_clients)4145b9c547cSRui Paulo void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
4155b9c547cSRui Paulo int max_clients)
416f05cddf9SRui Paulo {
417f05cddf9SRui Paulo u8 *group_info;
4185b9c547cSRui Paulo int count = 0;
419f05cddf9SRui Paulo struct p2p_group_member *m;
420f05cddf9SRui Paulo
4215b9c547cSRui Paulo p2p_dbg(group->p2p, "* P2P Group Info");
4225b9c547cSRui Paulo group_info = wpabuf_put(buf, 0);
4235b9c547cSRui Paulo wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
4245b9c547cSRui Paulo wpabuf_put_le16(buf, 0); /* Length to be filled */
4255b9c547cSRui Paulo for (m = group->members; m; m = m->next) {
4265b9c547cSRui Paulo p2p_client_info(buf, m);
4275b9c547cSRui Paulo count++;
4285b9c547cSRui Paulo if (max_clients >= 0 && count >= max_clients)
4295b9c547cSRui Paulo break;
4305b9c547cSRui Paulo }
4315b9c547cSRui Paulo WPA_PUT_LE16(group_info + 1,
4325b9c547cSRui Paulo (u8 *) wpabuf_put(buf, 0) - group_info - 3);
4335b9c547cSRui Paulo }
434f05cddf9SRui Paulo
4355b9c547cSRui Paulo
p2p_group_buf_add_id(struct p2p_group * group,struct wpabuf * buf)4365b9c547cSRui Paulo void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
4375b9c547cSRui Paulo {
4385b9c547cSRui Paulo p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
4395b9c547cSRui Paulo group->cfg->ssid_len);
4405b9c547cSRui Paulo }
4415b9c547cSRui Paulo
4425b9c547cSRui Paulo
p2p_group_build_probe_resp_ie(struct p2p_group * group)4435b9c547cSRui Paulo static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
4445b9c547cSRui Paulo {
4455b9c547cSRui Paulo struct wpabuf *p2p_subelems, *ie;
4465b9c547cSRui Paulo
4475b9c547cSRui Paulo p2p_subelems = wpabuf_alloc(500);
4485b9c547cSRui Paulo if (p2p_subelems == NULL)
449f05cddf9SRui Paulo return NULL;
450f05cddf9SRui Paulo
4515b9c547cSRui Paulo p2p_group_add_common_ies(group, p2p_subelems);
4525b9c547cSRui Paulo p2p_group_add_noa(p2p_subelems, group->noa);
453f05cddf9SRui Paulo
454f05cddf9SRui Paulo /* P2P Device Info */
4555b9c547cSRui Paulo p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
456f05cddf9SRui Paulo
4575b9c547cSRui Paulo /* P2P Group Info: Only when at least one P2P Client is connected */
4585b9c547cSRui Paulo if (group->members)
4595b9c547cSRui Paulo p2p_buf_add_group_info(group, p2p_subelems, -1);
460f05cddf9SRui Paulo
4615b9c547cSRui Paulo ie = p2p_group_encaps_probe_resp(p2p_subelems);
4625b9c547cSRui Paulo wpabuf_free(p2p_subelems);
4635b9c547cSRui Paulo
4645b9c547cSRui Paulo if (group->p2p->vendor_elem &&
4655b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
4665b9c547cSRui Paulo struct wpabuf *extra;
4675b9c547cSRui Paulo extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
4685b9c547cSRui Paulo ie = wpabuf_concat(extra, ie);
4695b9c547cSRui Paulo }
4705b9c547cSRui Paulo
4715b9c547cSRui Paulo #ifdef CONFIG_WIFI_DISPLAY
4725b9c547cSRui Paulo if (group->wfd_ie) {
4735b9c547cSRui Paulo struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
4745b9c547cSRui Paulo ie = wpabuf_concat(wfd, ie);
4755b9c547cSRui Paulo }
4765b9c547cSRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
477f05cddf9SRui Paulo
478f05cddf9SRui Paulo return ie;
479f05cddf9SRui Paulo }
480f05cddf9SRui Paulo
481f05cddf9SRui Paulo
p2p_group_update_ies(struct p2p_group * group)482f05cddf9SRui Paulo void p2p_group_update_ies(struct p2p_group *group)
483f05cddf9SRui Paulo {
484f05cddf9SRui Paulo struct wpabuf *beacon_ie;
485f05cddf9SRui Paulo struct wpabuf *probe_resp_ie;
486f05cddf9SRui Paulo
487f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
488f05cddf9SRui Paulo wifi_display_group_update(group);
489f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
490f05cddf9SRui Paulo
491f05cddf9SRui Paulo probe_resp_ie = p2p_group_build_probe_resp_ie(group);
492f05cddf9SRui Paulo if (probe_resp_ie == NULL)
493f05cddf9SRui Paulo return;
494f05cddf9SRui Paulo wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
495f05cddf9SRui Paulo probe_resp_ie);
496f05cddf9SRui Paulo
497f05cddf9SRui Paulo if (group->beacon_update) {
498f05cddf9SRui Paulo beacon_ie = p2p_group_build_beacon_ie(group);
499f05cddf9SRui Paulo if (beacon_ie)
500f05cddf9SRui Paulo group->beacon_update = 0;
501f05cddf9SRui Paulo wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
502f05cddf9SRui Paulo beacon_ie);
503f05cddf9SRui Paulo } else
504f05cddf9SRui Paulo beacon_ie = NULL;
505f05cddf9SRui Paulo
506f05cddf9SRui Paulo group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
507f05cddf9SRui Paulo }
508f05cddf9SRui Paulo
509f05cddf9SRui Paulo
510f05cddf9SRui Paulo /**
511f05cddf9SRui Paulo * p2p_build_client_info - Build P2P Client Info Descriptor
512f05cddf9SRui Paulo * @addr: MAC address of the peer device
513f05cddf9SRui Paulo * @p2p_ie: P2P IE from (Re)Association Request
514f05cddf9SRui Paulo * @dev_capab: Buffer for returning Device Capability
515f05cddf9SRui Paulo * @dev_addr: Buffer for returning P2P Device Address
516f05cddf9SRui Paulo * Returns: P2P Client Info Descriptor or %NULL on failure
517f05cddf9SRui Paulo *
518f05cddf9SRui Paulo * This function builds P2P Client Info Descriptor based on the information
519f05cddf9SRui Paulo * available from (Re)Association Request frame. Group owner can use this to
520f05cddf9SRui Paulo * build the P2P Group Info attribute for Probe Response frames.
521f05cddf9SRui Paulo */
p2p_build_client_info(const u8 * addr,struct wpabuf * p2p_ie,u8 * dev_capab,u8 * dev_addr)522f05cddf9SRui Paulo static struct wpabuf * p2p_build_client_info(const u8 *addr,
523f05cddf9SRui Paulo struct wpabuf *p2p_ie,
524f05cddf9SRui Paulo u8 *dev_capab, u8 *dev_addr)
525f05cddf9SRui Paulo {
526f05cddf9SRui Paulo const u8 *spos;
527f05cddf9SRui Paulo struct p2p_message msg;
528f05cddf9SRui Paulo u8 *len_pos;
529f05cddf9SRui Paulo struct wpabuf *buf;
530f05cddf9SRui Paulo
531f05cddf9SRui Paulo if (p2p_ie == NULL)
532f05cddf9SRui Paulo return NULL;
533f05cddf9SRui Paulo
534f05cddf9SRui Paulo os_memset(&msg, 0, sizeof(msg));
535f05cddf9SRui Paulo if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
536f05cddf9SRui Paulo msg.capability == NULL || msg.p2p_device_info == NULL)
537f05cddf9SRui Paulo return NULL;
538f05cddf9SRui Paulo
539f05cddf9SRui Paulo buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
540f05cddf9SRui Paulo if (buf == NULL)
541f05cddf9SRui Paulo return NULL;
542f05cddf9SRui Paulo
543f05cddf9SRui Paulo *dev_capab = msg.capability[0];
544f05cddf9SRui Paulo os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
545f05cddf9SRui Paulo
546f05cddf9SRui Paulo spos = msg.p2p_device_info; /* P2P Device address */
547f05cddf9SRui Paulo
548f05cddf9SRui Paulo /* P2P Client Info Descriptor */
549f05cddf9SRui Paulo /* Length to be set */
550f05cddf9SRui Paulo len_pos = wpabuf_put(buf, 1);
551f05cddf9SRui Paulo /* P2P Device address */
552f05cddf9SRui Paulo wpabuf_put_data(buf, spos, ETH_ALEN);
553f05cddf9SRui Paulo /* P2P Interface address */
554f05cddf9SRui Paulo wpabuf_put_data(buf, addr, ETH_ALEN);
555f05cddf9SRui Paulo /* Device Capability Bitmap */
556f05cddf9SRui Paulo wpabuf_put_u8(buf, msg.capability[0]);
557f05cddf9SRui Paulo /*
558f05cddf9SRui Paulo * Config Methods, Primary Device Type, Number of Secondary Device
559f05cddf9SRui Paulo * Types, Secondary Device Type List, Device Name copied from
560f05cddf9SRui Paulo * Device Info
561f05cddf9SRui Paulo */
562f05cddf9SRui Paulo wpabuf_put_data(buf, spos + ETH_ALEN,
563f05cddf9SRui Paulo msg.p2p_device_info_len - ETH_ALEN);
564f05cddf9SRui Paulo
565f05cddf9SRui Paulo *len_pos = wpabuf_len(buf) - 1;
566f05cddf9SRui Paulo
567f05cddf9SRui Paulo
568f05cddf9SRui Paulo return buf;
569f05cddf9SRui Paulo }
570f05cddf9SRui Paulo
571f05cddf9SRui Paulo
p2p_group_remove_member(struct p2p_group * group,const u8 * addr)572f05cddf9SRui Paulo static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
573f05cddf9SRui Paulo {
574f05cddf9SRui Paulo struct p2p_group_member *m, *prev;
575f05cddf9SRui Paulo
576f05cddf9SRui Paulo if (group == NULL)
577f05cddf9SRui Paulo return 0;
578f05cddf9SRui Paulo
579f05cddf9SRui Paulo m = group->members;
580f05cddf9SRui Paulo prev = NULL;
581f05cddf9SRui Paulo while (m) {
582*a90b9d01SCy Schubert if (ether_addr_equal(m->addr, addr))
583f05cddf9SRui Paulo break;
584f05cddf9SRui Paulo prev = m;
585f05cddf9SRui Paulo m = m->next;
586f05cddf9SRui Paulo }
587f05cddf9SRui Paulo
588f05cddf9SRui Paulo if (m == NULL)
589f05cddf9SRui Paulo return 0;
590f05cddf9SRui Paulo
591f05cddf9SRui Paulo if (prev)
592f05cddf9SRui Paulo prev->next = m->next;
593f05cddf9SRui Paulo else
594f05cddf9SRui Paulo group->members = m->next;
595f05cddf9SRui Paulo p2p_group_free_member(m);
596f05cddf9SRui Paulo group->num_members--;
597f05cddf9SRui Paulo
598f05cddf9SRui Paulo return 1;
599f05cddf9SRui Paulo }
600f05cddf9SRui Paulo
601f05cddf9SRui Paulo
p2p_group_notif_assoc(struct p2p_group * group,const u8 * addr,const u8 * ie,size_t len)602f05cddf9SRui Paulo int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
603f05cddf9SRui Paulo const u8 *ie, size_t len)
604f05cddf9SRui Paulo {
605f05cddf9SRui Paulo struct p2p_group_member *m;
606f05cddf9SRui Paulo
607f05cddf9SRui Paulo if (group == NULL)
608f05cddf9SRui Paulo return -1;
609f05cddf9SRui Paulo
6105b9c547cSRui Paulo p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);
6115b9c547cSRui Paulo
612f05cddf9SRui Paulo m = os_zalloc(sizeof(*m));
613f05cddf9SRui Paulo if (m == NULL)
614f05cddf9SRui Paulo return -1;
615f05cddf9SRui Paulo os_memcpy(m->addr, addr, ETH_ALEN);
616f05cddf9SRui Paulo m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
617f05cddf9SRui Paulo if (m->p2p_ie) {
618f05cddf9SRui Paulo m->client_info = p2p_build_client_info(addr, m->p2p_ie,
619f05cddf9SRui Paulo &m->dev_capab,
620f05cddf9SRui Paulo m->dev_addr);
621f05cddf9SRui Paulo }
622f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
623f05cddf9SRui Paulo m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
624f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
625f05cddf9SRui Paulo
626f05cddf9SRui Paulo p2p_group_remove_member(group, addr);
627f05cddf9SRui Paulo
628f05cddf9SRui Paulo m->next = group->members;
629f05cddf9SRui Paulo group->members = m;
630f05cddf9SRui Paulo group->num_members++;
6315b9c547cSRui Paulo p2p_dbg(group->p2p, "Add client " MACSTR
632f05cddf9SRui Paulo " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
633f05cddf9SRui Paulo MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
634f05cddf9SRui Paulo m->client_info ? 1 : 0,
635f05cddf9SRui Paulo group->num_members, group->cfg->max_clients);
636f05cddf9SRui Paulo if (group->num_members == group->cfg->max_clients)
637f05cddf9SRui Paulo group->beacon_update = 1;
638f05cddf9SRui Paulo p2p_group_update_ies(group);
639f05cddf9SRui Paulo if (group->num_members == 1)
640f05cddf9SRui Paulo group->cfg->idle_update(group->cfg->cb_ctx, 0);
641f05cddf9SRui Paulo
642f05cddf9SRui Paulo return 0;
643f05cddf9SRui Paulo }
644f05cddf9SRui Paulo
645f05cddf9SRui Paulo
p2p_group_assoc_resp_ie(struct p2p_group * group,u8 status)646f05cddf9SRui Paulo struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
647f05cddf9SRui Paulo {
648f05cddf9SRui Paulo struct wpabuf *resp;
649f05cddf9SRui Paulo u8 *rlen;
650f05cddf9SRui Paulo size_t extra = 0;
651f05cddf9SRui Paulo
652f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
653f05cddf9SRui Paulo if (group->wfd_ie)
654f05cddf9SRui Paulo extra = wpabuf_len(group->wfd_ie);
655f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
656f05cddf9SRui Paulo
6575b9c547cSRui Paulo if (group->p2p->vendor_elem &&
6585b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
6595b9c547cSRui Paulo extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
6605b9c547cSRui Paulo
661f05cddf9SRui Paulo /*
662f05cddf9SRui Paulo * (Re)Association Response - P2P IE
663f05cddf9SRui Paulo * Status attribute (shall be present when association request is
664f05cddf9SRui Paulo * denied)
665f05cddf9SRui Paulo * Extended Listen Timing (may be present)
666f05cddf9SRui Paulo */
667f05cddf9SRui Paulo resp = wpabuf_alloc(20 + extra);
668f05cddf9SRui Paulo if (resp == NULL)
669f05cddf9SRui Paulo return NULL;
670f05cddf9SRui Paulo
671f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
672f05cddf9SRui Paulo if (group->wfd_ie)
673f05cddf9SRui Paulo wpabuf_put_buf(resp, group->wfd_ie);
674f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
675f05cddf9SRui Paulo
6765b9c547cSRui Paulo if (group->p2p->vendor_elem &&
6775b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
6785b9c547cSRui Paulo wpabuf_put_buf(resp,
6795b9c547cSRui Paulo group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
6805b9c547cSRui Paulo
681f05cddf9SRui Paulo rlen = p2p_buf_add_ie_hdr(resp);
682f05cddf9SRui Paulo if (status != P2P_SC_SUCCESS)
683f05cddf9SRui Paulo p2p_buf_add_status(resp, status);
684f05cddf9SRui Paulo p2p_buf_update_ie_hdr(resp, rlen);
685f05cddf9SRui Paulo
686f05cddf9SRui Paulo return resp;
687f05cddf9SRui Paulo }
688f05cddf9SRui Paulo
689f05cddf9SRui Paulo
p2p_group_notif_disassoc(struct p2p_group * group,const u8 * addr)690f05cddf9SRui Paulo void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
691f05cddf9SRui Paulo {
692f05cddf9SRui Paulo if (p2p_group_remove_member(group, addr)) {
6935b9c547cSRui Paulo p2p_dbg(group->p2p, "Remove client " MACSTR
6945b9c547cSRui Paulo " from group; num_members=%u/%u",
695f05cddf9SRui Paulo MAC2STR(addr), group->num_members,
696f05cddf9SRui Paulo group->cfg->max_clients);
697f05cddf9SRui Paulo if (group->num_members == group->cfg->max_clients - 1)
698f05cddf9SRui Paulo group->beacon_update = 1;
699f05cddf9SRui Paulo p2p_group_update_ies(group);
700f05cddf9SRui Paulo if (group->num_members == 0)
701f05cddf9SRui Paulo group->cfg->idle_update(group->cfg->cb_ctx, 1);
702f05cddf9SRui Paulo }
703f05cddf9SRui Paulo }
704f05cddf9SRui Paulo
705f05cddf9SRui Paulo
706f05cddf9SRui Paulo /**
707f05cddf9SRui Paulo * p2p_match_dev_type_member - Match client device type with requested type
708f05cddf9SRui Paulo * @m: Group member
709f05cddf9SRui Paulo * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
710f05cddf9SRui Paulo * Returns: 1 on match, 0 on mismatch
711f05cddf9SRui Paulo *
712f05cddf9SRui Paulo * This function can be used to match the Requested Device Type attribute in
713f05cddf9SRui Paulo * WPS IE with the device types of a group member for deciding whether a GO
714f05cddf9SRui Paulo * should reply to a Probe Request frame.
715f05cddf9SRui Paulo */
p2p_match_dev_type_member(struct p2p_group_member * m,struct wpabuf * wps)716f05cddf9SRui Paulo static int p2p_match_dev_type_member(struct p2p_group_member *m,
717f05cddf9SRui Paulo struct wpabuf *wps)
718f05cddf9SRui Paulo {
719f05cddf9SRui Paulo const u8 *pos, *end;
720f05cddf9SRui Paulo struct wps_parse_attr attr;
721f05cddf9SRui Paulo u8 num_sec;
722f05cddf9SRui Paulo
723f05cddf9SRui Paulo if (m->client_info == NULL || wps == NULL)
724f05cddf9SRui Paulo return 0;
725f05cddf9SRui Paulo
726f05cddf9SRui Paulo pos = wpabuf_head(m->client_info);
727f05cddf9SRui Paulo end = pos + wpabuf_len(m->client_info);
728f05cddf9SRui Paulo
729f05cddf9SRui Paulo pos += 1 + 2 * ETH_ALEN + 1 + 2;
730f05cddf9SRui Paulo if (end - pos < WPS_DEV_TYPE_LEN + 1)
731f05cddf9SRui Paulo return 0;
732f05cddf9SRui Paulo
733f05cddf9SRui Paulo if (wps_parse_msg(wps, &attr))
734f05cddf9SRui Paulo return 1; /* assume no Requested Device Type attributes */
735f05cddf9SRui Paulo
736f05cddf9SRui Paulo if (attr.num_req_dev_type == 0)
737f05cddf9SRui Paulo return 1; /* no Requested Device Type attributes -> match */
738f05cddf9SRui Paulo
739f05cddf9SRui Paulo if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
740f05cddf9SRui Paulo return 1; /* Match with client Primary Device Type */
741f05cddf9SRui Paulo
742f05cddf9SRui Paulo pos += WPS_DEV_TYPE_LEN;
743f05cddf9SRui Paulo num_sec = *pos++;
744f05cddf9SRui Paulo if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
745f05cddf9SRui Paulo return 0;
746f05cddf9SRui Paulo while (num_sec > 0) {
747f05cddf9SRui Paulo num_sec--;
748f05cddf9SRui Paulo if (dev_type_list_match(pos, attr.req_dev_type,
749f05cddf9SRui Paulo attr.num_req_dev_type))
750f05cddf9SRui Paulo return 1; /* Match with client Secondary Device Type */
751f05cddf9SRui Paulo pos += WPS_DEV_TYPE_LEN;
752f05cddf9SRui Paulo }
753f05cddf9SRui Paulo
754f05cddf9SRui Paulo /* No matching device type found */
755f05cddf9SRui Paulo return 0;
756f05cddf9SRui Paulo }
757f05cddf9SRui Paulo
758f05cddf9SRui Paulo
p2p_group_match_dev_type(struct p2p_group * group,struct wpabuf * wps)759f05cddf9SRui Paulo int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
760f05cddf9SRui Paulo {
761f05cddf9SRui Paulo struct p2p_group_member *m;
762f05cddf9SRui Paulo
763f05cddf9SRui Paulo if (p2p_match_dev_type(group->p2p, wps))
764f05cddf9SRui Paulo return 1; /* Match with own device type */
765f05cddf9SRui Paulo
766f05cddf9SRui Paulo for (m = group->members; m; m = m->next) {
767f05cddf9SRui Paulo if (p2p_match_dev_type_member(m, wps))
768f05cddf9SRui Paulo return 1; /* Match with group client device type */
769f05cddf9SRui Paulo }
770f05cddf9SRui Paulo
771f05cddf9SRui Paulo /* No match with Requested Device Type */
772f05cddf9SRui Paulo return 0;
773f05cddf9SRui Paulo }
774f05cddf9SRui Paulo
775f05cddf9SRui Paulo
p2p_group_match_dev_id(struct p2p_group * group,struct wpabuf * p2p)776f05cddf9SRui Paulo int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
777f05cddf9SRui Paulo {
778f05cddf9SRui Paulo struct p2p_group_member *m;
779f05cddf9SRui Paulo struct p2p_message msg;
780f05cddf9SRui Paulo
781f05cddf9SRui Paulo os_memset(&msg, 0, sizeof(msg));
782f05cddf9SRui Paulo if (p2p_parse_p2p_ie(p2p, &msg))
783f05cddf9SRui Paulo return 1; /* Failed to parse - assume no filter on Device ID */
784f05cddf9SRui Paulo
785f05cddf9SRui Paulo if (!msg.device_id)
786f05cddf9SRui Paulo return 1; /* No filter on Device ID */
787f05cddf9SRui Paulo
788*a90b9d01SCy Schubert if (ether_addr_equal(msg.device_id, group->p2p->cfg->dev_addr))
789f05cddf9SRui Paulo return 1; /* Match with our P2P Device Address */
790f05cddf9SRui Paulo
791f05cddf9SRui Paulo for (m = group->members; m; m = m->next) {
792*a90b9d01SCy Schubert if (ether_addr_equal(msg.device_id, m->dev_addr))
793f05cddf9SRui Paulo return 1; /* Match with group client P2P Device Address */
794f05cddf9SRui Paulo }
795f05cddf9SRui Paulo
796f05cddf9SRui Paulo /* No match with Device ID */
797f05cddf9SRui Paulo return 0;
798f05cddf9SRui Paulo }
799f05cddf9SRui Paulo
800f05cddf9SRui Paulo
p2p_group_notif_formation_done(struct p2p_group * group)801f05cddf9SRui Paulo void p2p_group_notif_formation_done(struct p2p_group *group)
802f05cddf9SRui Paulo {
803f05cddf9SRui Paulo if (group == NULL)
804f05cddf9SRui Paulo return;
805f05cddf9SRui Paulo group->group_formation = 0;
806f05cddf9SRui Paulo group->beacon_update = 1;
807f05cddf9SRui Paulo p2p_group_update_ies(group);
808f05cddf9SRui Paulo }
809f05cddf9SRui Paulo
810f05cddf9SRui Paulo
p2p_group_notif_noa(struct p2p_group * group,const u8 * noa,size_t noa_len)811f05cddf9SRui Paulo int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
812f05cddf9SRui Paulo size_t noa_len)
813f05cddf9SRui Paulo {
814f05cddf9SRui Paulo if (noa == NULL) {
815f05cddf9SRui Paulo wpabuf_free(group->noa);
816f05cddf9SRui Paulo group->noa = NULL;
817f05cddf9SRui Paulo } else {
818f05cddf9SRui Paulo if (group->noa) {
819f05cddf9SRui Paulo if (wpabuf_size(group->noa) >= noa_len) {
820f05cddf9SRui Paulo group->noa->used = 0;
821f05cddf9SRui Paulo wpabuf_put_data(group->noa, noa, noa_len);
822f05cddf9SRui Paulo } else {
823f05cddf9SRui Paulo wpabuf_free(group->noa);
824f05cddf9SRui Paulo group->noa = NULL;
825f05cddf9SRui Paulo }
826f05cddf9SRui Paulo }
827f05cddf9SRui Paulo
828f05cddf9SRui Paulo if (!group->noa) {
829f05cddf9SRui Paulo group->noa = wpabuf_alloc_copy(noa, noa_len);
830f05cddf9SRui Paulo if (group->noa == NULL)
831f05cddf9SRui Paulo return -1;
832f05cddf9SRui Paulo }
833f05cddf9SRui Paulo }
834f05cddf9SRui Paulo
835f05cddf9SRui Paulo group->beacon_update = 1;
836f05cddf9SRui Paulo p2p_group_update_ies(group);
837f05cddf9SRui Paulo return 0;
838f05cddf9SRui Paulo }
839f05cddf9SRui Paulo
840f05cddf9SRui Paulo
p2p_group_get_client(struct p2p_group * group,const u8 * dev_id)841f05cddf9SRui Paulo static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
842f05cddf9SRui Paulo const u8 *dev_id)
843f05cddf9SRui Paulo {
844f05cddf9SRui Paulo struct p2p_group_member *m;
845f05cddf9SRui Paulo
846f05cddf9SRui Paulo for (m = group->members; m; m = m->next) {
847*a90b9d01SCy Schubert if (ether_addr_equal(dev_id, m->dev_addr))
848f05cddf9SRui Paulo return m;
849f05cddf9SRui Paulo }
850f05cddf9SRui Paulo
851f05cddf9SRui Paulo return NULL;
852f05cddf9SRui Paulo }
853f05cddf9SRui Paulo
854f05cddf9SRui Paulo
p2p_group_get_client_interface_addr(struct p2p_group * group,const u8 * dev_addr)855780fb4a2SCy Schubert const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group,
856780fb4a2SCy Schubert const u8 *dev_addr)
857780fb4a2SCy Schubert {
858780fb4a2SCy Schubert struct p2p_group_member *m;
859780fb4a2SCy Schubert
860780fb4a2SCy Schubert if (!group)
861780fb4a2SCy Schubert return NULL;
862780fb4a2SCy Schubert m = p2p_group_get_client(group, dev_addr);
863780fb4a2SCy Schubert if (m)
864780fb4a2SCy Schubert return m->addr;
865780fb4a2SCy Schubert return NULL;
866780fb4a2SCy Schubert }
867780fb4a2SCy Schubert
868780fb4a2SCy Schubert
p2p_group_get_client_iface(struct p2p_group * group,const u8 * interface_addr)869f05cddf9SRui Paulo static struct p2p_group_member * p2p_group_get_client_iface(
870f05cddf9SRui Paulo struct p2p_group *group, const u8 *interface_addr)
871f05cddf9SRui Paulo {
872f05cddf9SRui Paulo struct p2p_group_member *m;
873f05cddf9SRui Paulo
874f05cddf9SRui Paulo for (m = group->members; m; m = m->next) {
875*a90b9d01SCy Schubert if (ether_addr_equal(interface_addr, m->addr))
876f05cddf9SRui Paulo return m;
877f05cddf9SRui Paulo }
878f05cddf9SRui Paulo
879f05cddf9SRui Paulo return NULL;
880f05cddf9SRui Paulo }
881f05cddf9SRui Paulo
882f05cddf9SRui Paulo
p2p_group_get_dev_addr(struct p2p_group * group,const u8 * addr)883f05cddf9SRui Paulo const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
884f05cddf9SRui Paulo {
885f05cddf9SRui Paulo struct p2p_group_member *m;
886f05cddf9SRui Paulo
887f05cddf9SRui Paulo if (group == NULL)
888f05cddf9SRui Paulo return NULL;
889f05cddf9SRui Paulo m = p2p_group_get_client_iface(group, addr);
890f05cddf9SRui Paulo if (m && !is_zero_ether_addr(m->dev_addr))
891f05cddf9SRui Paulo return m->dev_addr;
892f05cddf9SRui Paulo return NULL;
893f05cddf9SRui Paulo }
894f05cddf9SRui Paulo
895f05cddf9SRui Paulo
p2p_build_go_disc_req(void)896f05cddf9SRui Paulo static struct wpabuf * p2p_build_go_disc_req(void)
897f05cddf9SRui Paulo {
898f05cddf9SRui Paulo struct wpabuf *buf;
899f05cddf9SRui Paulo
900f05cddf9SRui Paulo buf = wpabuf_alloc(100);
901f05cddf9SRui Paulo if (buf == NULL)
902f05cddf9SRui Paulo return NULL;
903f05cddf9SRui Paulo
904f05cddf9SRui Paulo p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
905f05cddf9SRui Paulo
906f05cddf9SRui Paulo return buf;
907f05cddf9SRui Paulo }
908f05cddf9SRui Paulo
909f05cddf9SRui Paulo
p2p_group_go_discover(struct p2p_group * group,const u8 * dev_id,const u8 * searching_dev,int rx_freq)910f05cddf9SRui Paulo int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
911f05cddf9SRui Paulo const u8 *searching_dev, int rx_freq)
912f05cddf9SRui Paulo {
913f05cddf9SRui Paulo struct p2p_group_member *m;
914f05cddf9SRui Paulo struct wpabuf *req;
915f05cddf9SRui Paulo struct p2p_data *p2p = group->p2p;
916f05cddf9SRui Paulo int freq;
917f05cddf9SRui Paulo
918f05cddf9SRui Paulo m = p2p_group_get_client(group, dev_id);
919f05cddf9SRui Paulo if (m == NULL || m->client_info == NULL) {
9205b9c547cSRui Paulo p2p_dbg(group->p2p, "Requested client was not in this group "
9215b9c547cSRui Paulo MACSTR, MAC2STR(group->cfg->interface_addr));
922f05cddf9SRui Paulo return -1;
923f05cddf9SRui Paulo }
924f05cddf9SRui Paulo
925f05cddf9SRui Paulo if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
9265b9c547cSRui Paulo p2p_dbg(group->p2p, "Requested client does not support client discoverability");
927f05cddf9SRui Paulo return -1;
928f05cddf9SRui Paulo }
929f05cddf9SRui Paulo
9305b9c547cSRui Paulo p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to "
9315b9c547cSRui Paulo MACSTR, MAC2STR(dev_id));
932f05cddf9SRui Paulo
933f05cddf9SRui Paulo req = p2p_build_go_disc_req();
934f05cddf9SRui Paulo if (req == NULL)
935f05cddf9SRui Paulo return -1;
936f05cddf9SRui Paulo
937f05cddf9SRui Paulo /* TODO: Should really use group operating frequency here */
938f05cddf9SRui Paulo freq = rx_freq;
939f05cddf9SRui Paulo
940f05cddf9SRui Paulo p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
941f05cddf9SRui Paulo if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
942f05cddf9SRui Paulo group->cfg->interface_addr,
943f05cddf9SRui Paulo group->cfg->interface_addr,
9444bc52338SCy Schubert wpabuf_head(req), wpabuf_len(req), 200, NULL)
9454bc52338SCy Schubert < 0)
946f05cddf9SRui Paulo {
9475b9c547cSRui Paulo p2p_dbg(p2p, "Failed to send Action frame");
948f05cddf9SRui Paulo }
949f05cddf9SRui Paulo
950f05cddf9SRui Paulo wpabuf_free(req);
951f05cddf9SRui Paulo
952f05cddf9SRui Paulo return 0;
953f05cddf9SRui Paulo }
954f05cddf9SRui Paulo
955f05cddf9SRui Paulo
p2p_group_get_interface_addr(struct p2p_group * group)956f05cddf9SRui Paulo const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
957f05cddf9SRui Paulo {
958f05cddf9SRui Paulo return group->cfg->interface_addr;
959f05cddf9SRui Paulo }
960f05cddf9SRui Paulo
961f05cddf9SRui Paulo
p2p_group_presence_req(struct p2p_group * group,const u8 * client_interface_addr,const u8 * noa,size_t noa_len)962f05cddf9SRui Paulo u8 p2p_group_presence_req(struct p2p_group *group,
963f05cddf9SRui Paulo const u8 *client_interface_addr,
964f05cddf9SRui Paulo const u8 *noa, size_t noa_len)
965f05cddf9SRui Paulo {
966f05cddf9SRui Paulo struct p2p_group_member *m;
967f05cddf9SRui Paulo u8 curr_noa[50];
968f05cddf9SRui Paulo int curr_noa_len;
969f05cddf9SRui Paulo
970f05cddf9SRui Paulo m = p2p_group_get_client_iface(group, client_interface_addr);
971f05cddf9SRui Paulo if (m == NULL || m->client_info == NULL) {
9725b9c547cSRui Paulo p2p_dbg(group->p2p, "Client was not in this group");
973f05cddf9SRui Paulo return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
974f05cddf9SRui Paulo }
975f05cddf9SRui Paulo
976f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
977f05cddf9SRui Paulo
978f05cddf9SRui Paulo if (group->p2p->cfg->get_noa)
979f05cddf9SRui Paulo curr_noa_len = group->p2p->cfg->get_noa(
980f05cddf9SRui Paulo group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
981f05cddf9SRui Paulo curr_noa, sizeof(curr_noa));
982f05cddf9SRui Paulo else
983f05cddf9SRui Paulo curr_noa_len = -1;
984f05cddf9SRui Paulo if (curr_noa_len < 0)
9855b9c547cSRui Paulo p2p_dbg(group->p2p, "Failed to fetch current NoA");
986f05cddf9SRui Paulo else if (curr_noa_len == 0)
9875b9c547cSRui Paulo p2p_dbg(group->p2p, "No NoA being advertized");
988f05cddf9SRui Paulo else
989f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
990f05cddf9SRui Paulo curr_noa_len);
991f05cddf9SRui Paulo
992f05cddf9SRui Paulo /* TODO: properly process request and store copy */
993f05cddf9SRui Paulo if (curr_noa_len > 0 || curr_noa_len == -1)
994f05cddf9SRui Paulo return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
995f05cddf9SRui Paulo
996f05cddf9SRui Paulo return P2P_SC_SUCCESS;
997f05cddf9SRui Paulo }
998f05cddf9SRui Paulo
999f05cddf9SRui Paulo
p2p_get_group_num_members(struct p2p_group * group)1000f05cddf9SRui Paulo unsigned int p2p_get_group_num_members(struct p2p_group *group)
1001f05cddf9SRui Paulo {
10025b9c547cSRui Paulo if (!group)
10035b9c547cSRui Paulo return 0;
10045b9c547cSRui Paulo
1005f05cddf9SRui Paulo return group->num_members;
1006f05cddf9SRui Paulo }
1007f05cddf9SRui Paulo
1008f05cddf9SRui Paulo
p2p_client_limit_reached(struct p2p_group * group)10095b9c547cSRui Paulo int p2p_client_limit_reached(struct p2p_group *group)
10105b9c547cSRui Paulo {
10115b9c547cSRui Paulo if (!group || !group->cfg)
10125b9c547cSRui Paulo return 1;
10135b9c547cSRui Paulo
10145b9c547cSRui Paulo return group->num_members >= group->cfg->max_clients;
10155b9c547cSRui Paulo }
10165b9c547cSRui Paulo
10175b9c547cSRui Paulo
p2p_iterate_group_members(struct p2p_group * group,void ** next)1018f05cddf9SRui Paulo const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
1019f05cddf9SRui Paulo {
1020f05cddf9SRui Paulo struct p2p_group_member *iter = *next;
1021f05cddf9SRui Paulo
1022f05cddf9SRui Paulo if (!iter)
1023f05cddf9SRui Paulo iter = group->members;
1024f05cddf9SRui Paulo else
1025f05cddf9SRui Paulo iter = iter->next;
1026f05cddf9SRui Paulo
1027f05cddf9SRui Paulo *next = iter;
1028f05cddf9SRui Paulo
1029f05cddf9SRui Paulo if (!iter)
1030f05cddf9SRui Paulo return NULL;
1031f05cddf9SRui Paulo
10325b9c547cSRui Paulo return iter->dev_addr;
1033f05cddf9SRui Paulo }
1034f05cddf9SRui Paulo
1035f05cddf9SRui Paulo
p2p_group_is_client_connected(struct p2p_group * group,const u8 * dev_addr)1036f05cddf9SRui Paulo int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
1037f05cddf9SRui Paulo {
1038f05cddf9SRui Paulo struct p2p_group_member *m;
1039f05cddf9SRui Paulo
1040f05cddf9SRui Paulo for (m = group->members; m; m = m->next) {
1041*a90b9d01SCy Schubert if (ether_addr_equal(m->dev_addr, dev_addr))
1042f05cddf9SRui Paulo return 1;
1043f05cddf9SRui Paulo }
1044f05cddf9SRui Paulo
1045f05cddf9SRui Paulo return 0;
1046f05cddf9SRui Paulo }
1047f05cddf9SRui Paulo
1048f05cddf9SRui Paulo
p2p_group_is_group_id_match(struct p2p_group * group,const u8 * group_id,size_t group_id_len)1049f05cddf9SRui Paulo int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
1050f05cddf9SRui Paulo size_t group_id_len)
1051f05cddf9SRui Paulo {
1052f05cddf9SRui Paulo if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
1053f05cddf9SRui Paulo return 0;
1054*a90b9d01SCy Schubert if (!ether_addr_equal(group_id, group->p2p->cfg->dev_addr))
1055f05cddf9SRui Paulo return 0;
1056f05cddf9SRui Paulo return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
1057f05cddf9SRui Paulo group->cfg->ssid_len) == 0;
1058f05cddf9SRui Paulo }
10595b9c547cSRui Paulo
10605b9c547cSRui Paulo
p2p_group_force_beacon_update_ies(struct p2p_group * group)10615b9c547cSRui Paulo void p2p_group_force_beacon_update_ies(struct p2p_group *group)
10625b9c547cSRui Paulo {
10635b9c547cSRui Paulo group->beacon_update = 1;
10645b9c547cSRui Paulo p2p_group_update_ies(group);
10655b9c547cSRui Paulo }
10665b9c547cSRui Paulo
10675b9c547cSRui Paulo
p2p_group_get_freq(struct p2p_group * group)10685b9c547cSRui Paulo int p2p_group_get_freq(struct p2p_group *group)
10695b9c547cSRui Paulo {
10705b9c547cSRui Paulo return group->cfg->freq;
10715b9c547cSRui Paulo }
10725b9c547cSRui Paulo
10735b9c547cSRui Paulo
p2p_group_get_config(struct p2p_group * group)10745b9c547cSRui Paulo const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
10755b9c547cSRui Paulo {
10765b9c547cSRui Paulo return group->cfg;
10775b9c547cSRui Paulo }
10785b9c547cSRui Paulo
10795b9c547cSRui Paulo
p2p_loop_on_all_groups(struct p2p_data * p2p,int (* group_callback)(struct p2p_group * group,void * user_data),void * user_data)10805b9c547cSRui Paulo void p2p_loop_on_all_groups(struct p2p_data *p2p,
10815b9c547cSRui Paulo int (*group_callback)(struct p2p_group *group,
10825b9c547cSRui Paulo void *user_data),
10835b9c547cSRui Paulo void *user_data)
10845b9c547cSRui Paulo {
10855b9c547cSRui Paulo unsigned int i;
10865b9c547cSRui Paulo
10875b9c547cSRui Paulo for (i = 0; i < p2p->num_groups; i++) {
10885b9c547cSRui Paulo if (!group_callback(p2p->groups[i], user_data))
10895b9c547cSRui Paulo break;
10905b9c547cSRui Paulo }
10915b9c547cSRui Paulo }
1092325151a3SRui Paulo
1093325151a3SRui Paulo
p2p_group_get_common_freqs(struct p2p_group * group,int * common_freqs,unsigned int * num)1094325151a3SRui Paulo int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
1095325151a3SRui Paulo unsigned int *num)
1096325151a3SRui Paulo
1097325151a3SRui Paulo {
1098325151a3SRui Paulo struct p2p_channels intersect, res;
1099325151a3SRui Paulo struct p2p_group_member *m;
1100325151a3SRui Paulo
1101325151a3SRui Paulo if (!group || !common_freqs || !num)
1102325151a3SRui Paulo return -1;
1103325151a3SRui Paulo
1104325151a3SRui Paulo os_memset(&intersect, 0, sizeof(intersect));
1105325151a3SRui Paulo os_memset(&res, 0, sizeof(res));
1106325151a3SRui Paulo
1107325151a3SRui Paulo p2p_channels_union(&intersect, &group->p2p->cfg->channels,
1108325151a3SRui Paulo &intersect);
1109325151a3SRui Paulo
1110325151a3SRui Paulo p2p_channels_dump(group->p2p,
1111325151a3SRui Paulo "Group common freqs before iterating members",
1112325151a3SRui Paulo &intersect);
1113325151a3SRui Paulo
1114325151a3SRui Paulo for (m = group->members; m; m = m->next) {
1115325151a3SRui Paulo struct p2p_device *dev;
1116325151a3SRui Paulo
1117325151a3SRui Paulo dev = p2p_get_device(group->p2p, m->dev_addr);
1118780fb4a2SCy Schubert if (!dev || dev->channels.reg_classes == 0)
1119325151a3SRui Paulo continue;
1120325151a3SRui Paulo
1121325151a3SRui Paulo p2p_channels_intersect(&intersect, &dev->channels, &res);
1122325151a3SRui Paulo intersect = res;
1123325151a3SRui Paulo }
1124325151a3SRui Paulo
1125325151a3SRui Paulo p2p_channels_dump(group->p2p, "Group common channels", &intersect);
1126325151a3SRui Paulo
1127325151a3SRui Paulo os_memset(common_freqs, 0, *num * sizeof(int));
1128325151a3SRui Paulo *num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
1129325151a3SRui Paulo
1130325151a3SRui Paulo return 0;
1131325151a3SRui Paulo }
1132