diff options
Diffstat (limited to 'src/p2p')
-rw-r--r-- | src/p2p/Makefile | 27 | ||||
-rw-r--r-- | src/p2p/p2p.c | 282 | ||||
-rw-r--r-- | src/p2p/p2p.h | 136 | ||||
-rw-r--r-- | src/p2p/p2p_build.c | 360 | ||||
-rw-r--r-- | src/p2p/p2p_dev_disc.c | 2 | ||||
-rw-r--r-- | src/p2p/p2p_go_neg.c | 213 | ||||
-rw-r--r-- | src/p2p/p2p_group.c | 40 | ||||
-rw-r--r-- | src/p2p/p2p_i.h | 38 | ||||
-rw-r--r-- | src/p2p/p2p_invitation.c | 23 | ||||
-rw-r--r-- | src/p2p/p2p_parse.c | 25 | ||||
-rw-r--r-- | src/p2p/p2p_pd.c | 400 | ||||
-rw-r--r-- | src/p2p/p2p_utils.c | 58 |
12 files changed, 1211 insertions, 393 deletions
diff --git a/src/p2p/Makefile b/src/p2p/Makefile index adfd3dfd5b9b..5587fcf281d3 100644 --- a/src/p2p/Makefile +++ b/src/p2p/Makefile @@ -1,8 +1,29 @@ -all: - @echo Nothing to be made. +all: libp2p.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_WIFI_DISPLAY +CFLAGS += -DCONFIG_WPS_NFC + +LIB_OBJS= \ + p2p_build.o \ + p2p.o \ + p2p_dev_disc.o \ + p2p_go_neg.o \ + p2p_group.o \ + p2p_invitation.o \ + p2p_parse.o \ + p2p_pd.o \ + p2p_sd.o \ + p2p_utils.o + +libp2p.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 6adb3dc2049f..767706c01d6b 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -48,9 +48,8 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ -#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) -static void p2p_expire_peers(struct p2p_data *p2p) +void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; @@ -103,15 +102,6 @@ static void p2p_expire_peers(struct p2p_data *p2p) } -static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct p2p_data *p2p = eloop_ctx; - p2p_expire_peers(p2p); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); -} - - static const char * p2p_state_txt(int state) { switch (state) { @@ -297,7 +287,7 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) return; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; @@ -346,7 +336,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) return 0; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return -1; @@ -468,7 +458,8 @@ static void p2p_copy_client_info(struct p2p_device *dev, static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, - const u8 *gi, size_t gi_len) + const u8 *gi, size_t gi_len, + struct os_reltime *rx_time) { struct p2p_group_info info; size_t c; @@ -536,10 +527,11 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_reltime(&dev->last_seen); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); + dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT; } return 0; @@ -758,26 +750,35 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, /* * Update the device entry only if the new peer - * entry is newer than the one previously stored. + * entry is newer than the one previously stored, or if + * the device was previously seen as a P2P Client in a group + * and the new entry isn't older than a threshold. */ if (dev->last_seen.sec > 0 && - os_reltime_before(rx_time, &dev->last_seen)) { - p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + os_reltime_before(rx_time, &dev->last_seen) && + (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) || + os_reltime_expired(&dev->last_seen, rx_time, + P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) { + p2p_dbg(p2p, + "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, - (unsigned int) dev->last_seen.usec); + (unsigned int) dev->last_seen.usec, + dev->flags); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); - dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && + msg.ssid[1] <= sizeof(dev->oper_ssid) && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { @@ -843,7 +844,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, - msg.group_info, msg.group_info_len); + msg.group_info, msg.group_info_len, + rx_time); } p2p_parse_free(&msg); @@ -1127,8 +1129,10 @@ static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) adv_array = (u8 *) str_buf; adv_len = os_strlen(str); + if (adv_len >= sizeof(str_buf)) + return 0; - for (i = 0; str[i] && i < adv_len; i++) { + for (i = 0; i < adv_len; i++) { if (str[i] >= 'A' && str[i] <= 'Z') str_buf[i] = str[i] - 'A' + 'a'; else @@ -1182,27 +1186,25 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, * An empty seek string means no hash values, but still an ASP * search. */ + p2p_dbg(p2p, "ASP search"); p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { u8 buf[P2PS_HASH_LEN]; - int i; + int i, count = 0; - p2p->p2ps_seek_count = seek_count; for (i = 0; i < seek_count; i++) { if (!p2ps_gen_hash(p2p, seek[i], buf)) continue; - /* If asking for wildcard, don't do others */ - if (os_memcmp(buf, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - p2p->p2ps_seek_count = 0; - break; - } - - os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, - P2PS_HASH_LEN); + p2p_dbg(p2p, "Seek service %s hash " MACSTR, + seek[i], MAC2STR(buf)); + os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN], + buf, P2PS_HASH_LEN); + count++; } + + p2p->p2ps_seek_count = count; p2p->p2ps_seek = 1; } else { p2p->p2ps_seek_count = 0; @@ -1212,7 +1214,8 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, /* Special case to perform wildcard search */ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { p2p->p2ps_seek_count = 1; - os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash, + P2PS_HASH_LEN); } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; @@ -1380,7 +1383,7 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -2147,7 +2150,9 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) } -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count) { struct wpabuf *buf; u8 *len; @@ -2162,7 +2167,7 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); - if (p2p->query_count) + if (query_count) extra += MAX_SVC_ADV_IE_LEN; buf = wpabuf_alloc(1000 + extra); @@ -2199,9 +2204,8 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); - if (p2p->query_count) { - p2p_buf_add_service_instance(buf, p2p, p2p->query_count, - p2p->query_hash, + if (query_count) { + p2p_buf_add_service_instance(buf, p2p, query_count, query_hash, p2p->p2ps_adv_list); } @@ -2212,18 +2216,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { struct p2ps_advertisement *adv_data; + int any_wfa; p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); - /* Wildcard always matches if we have actual services */ - if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) - return p2p->p2ps_adv_list != NULL; + /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */ + any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0; adv_data = p2p->p2ps_adv_list; while (adv_data) { - p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) - return 1; + return 1; /* exact hash match */ + if (any_wfa && + os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; /* WFA service match */ adv_data = adv_data->next; } @@ -2233,13 +2240,15 @@ static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { struct ieee802_11_elems elems; struct wpabuf *buf; struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; + u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2291,53 +2300,42 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_P2P; } - p2p->p2ps_svc_found = 0; - if (msg.service_hash && msg.service_hash_count) { const u8 *hash = msg.service_hash; - u8 *dest = p2p->query_hash; u8 i; + int p2ps_svc_found = 0; + + p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz", + p2p->in_listen, p2p->drv_in_listen, rx_freq, + p2p->cfg->channel, p2p->pending_listen_freq); + + if (!p2p->in_listen && !p2p->drv_in_listen && + p2p->pending_listen_freq && rx_freq && + rx_freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz", + rx_freq, p2p->pending_listen_freq); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } - p2p->query_count = 0; for (i = 0; i < msg.service_hash_count; i++) { if (p2p_service_find_asp(p2p, hash)) { - p2p->p2ps_svc_found = 1; - - if (!os_memcmp(hash, p2p->wild_card_hash, - P2PS_HASH_LEN)) { - /* We found match(es) but wildcard - * will return all */ - p2p->query_count = 1; - os_memcpy(p2p->query_hash, hash, - P2PS_HASH_LEN); - break; - } - - /* Save each matching hash */ - if (p2p->query_count < P2P_MAX_QUERY_HASH) { - os_memcpy(dest, hash, P2PS_HASH_LEN); - dest += P2PS_HASH_LEN; - p2p->query_count++; - } else { - /* We found match(es) but too many to - * return all */ - p2p->query_count = 0; - break; - } + p2p_dbg(p2p, "Service Hash match found: " + MACSTR, MAC2STR(hash)); + p2ps_svc_found = 1; + break; } hash += P2PS_HASH_LEN; } - p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); - /* Probed hash unknown */ - if (!p2p->p2ps_svc_found) { + if (!p2ps_svc_found) { + p2p_dbg(p2p, "No Service Hash match found"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } } else { /* This is not a P2PS Probe Request */ - p2p->query_count = 0; p2p_dbg(p2p, "No P2PS Hash in Probe Request"); if (!p2p->in_listen || !p2p->drv_in_listen) { @@ -2366,11 +2364,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } - p2p_parse_free(&msg); if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); + p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2382,7 +2380,9 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, * really only used for discovery purposes, not to learn exact BSS * parameters. */ - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, msg.service_hash, + msg.service_hash_count); + p2p_parse_free(&msg); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; @@ -2392,8 +2392,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = NULL; - resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_RESP << 4)); @@ -2422,32 +2422,50 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, wpabuf_put_u8(buf, 480 / 5); wpabuf_put_u8(buf, 540 / 5); + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + wpabuf_free(ies); + wpabuf_free(buf); + return P2P_PREQ_NOT_PROCESSED; + } + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, p2p->cfg->channel); + wpabuf_put_u8(buf, channel); wpabuf_put_buf(buf, ies); wpabuf_free(ies); - p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); wpabuf_free(buf); - return P2P_PREQ_NOT_PROCESSED; + return P2P_PREQ_PROCESSED; } enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); - p2p->query_count = 0; + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); + if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) + return res; + /* + * Activate a pending GO Negotiation/Invite flow if a received Probe + * Request frame is from an expected peer. Some devices may share the + * same address for P2P and non-P2P STA running simultaneously. The + * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe() + * return values verified above ensure we are handling a Probe Request + * frame from a P2P peer. + */ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) @@ -2457,7 +2475,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -2469,7 +2487,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } return res; @@ -2484,10 +2502,21 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, size_t tmplen; int res; u8 group_capab; + struct p2p_message msg; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0) + return 0; + + p2p_dbg(p2p, "BSS P2P manageability %s", + msg.manageability ? "enabled" : "disabled"); + + if (!msg.manageability) + return 0; + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2650,13 +2679,14 @@ int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, - const char *svc_info) + const char *svc_info, const u8 *cpt_priority) { struct p2ps_advertisement *adv_data, *tmp, **prev; u8 buf[P2PS_HASH_LEN]; size_t adv_data_len, adv_len, info_len = 0; + int i; - if (!p2p || !adv_str || !adv_str[0]) + if (!p2p || !adv_str || !adv_str[0] || !cpt_priority) return -1; if (!(config_methods & p2p->cfg->config_methods)) { @@ -2685,6 +2715,11 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, adv_data->auto_accept = (u8) auto_accept; os_memcpy(adv_data->svc_name, adv_str, adv_len); + for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) { + adv_data->cpt_priority[i] = cpt_priority[i]; + adv_data->cpt_mask |= cpt_priority[i]; + } + if (svc_info && info_len) { adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; os_memcpy(adv_data->svc_info, svc_info, info_len); @@ -2723,13 +2758,33 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, inserted: p2p_dbg(p2p, - "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", - adv_id, adv_data->config_methods, svc_state, adv_str); + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x", + adv_id, adv_data->config_methods, svc_state, adv_str, + adv_data->cpt_mask); return 0; } +void p2p_service_flush_asp(struct p2p_data *p2p) +{ + struct p2ps_advertisement *adv, *prev; + + if (!p2p) + return; + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + + p2p->p2ps_adv_list = NULL; + p2p_dbg(p2p, "All ASP advertisements flushed"); +} + + int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; @@ -2861,9 +2916,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) dl_list_init(&p2p->devices); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); - p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; @@ -2878,8 +2930,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { - struct p2ps_advertisement *adv, *prev; - #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2894,7 +2944,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); @@ -2908,18 +2957,12 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); - - adv = p2p->p2ps_adv_list; - while (adv) { - prev = adv; - adv = adv->next; - os_free(prev); - } + p2p_service_flush_asp(p2p); os_free(p2p); } @@ -2937,6 +2980,7 @@ void p2p_flush(struct p2p_data *p2p) p2p_free_sd_queries(p2p); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; + p2p->ssid_set = 0; } @@ -4120,7 +4164,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), @@ -4164,6 +4208,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", + dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ? + "[LAST_SEEN_AS_GROUP_CLIENT]" : "", dev->status, dev->invitation_reqs); if (os_snprintf_error(end - pos, res)) @@ -5215,6 +5261,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + p2p_parse_free(&msg); return -1; } @@ -5226,6 +5273,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + p2p_parse_free(&msg); return -1; } role = msg.oob_go_neg_channel[5]; @@ -5246,6 +5294,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); + p2p_parse_free(&msg); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); @@ -5334,3 +5383,20 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, -1); } + + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size) +{ + unsigned int i; + + if (size > P2P_MAX_PREF_CHANNELS) + size = P2P_MAX_PREF_CHANNELS; + p2p->num_pref_freq = size; + for (i = 0; i < size; i++) { + p2p->pref_freq_list[i] = pref_freq_list[i]; + p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", + i, p2p->pref_freq_list[i]); + } +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 2402db6a7eb9..b4060be477b6 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -9,7 +9,8 @@ #ifndef P2P_H #define P2P_H -#include "wps/wps_defs.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 @@ -20,6 +21,12 @@ #define P2PS_WILD_HASH_STR "org.wi-fi.wfds" #define P2PS_HASH_LEN 6 #define P2P_MAX_QUERY_HASH 6 +#define P2PS_FEATURE_CAPAB_CPT_MAX 2 + +/** + * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels + */ +#define P2P_MAX_PREF_CHANNELS 100 /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes @@ -95,7 +102,7 @@ struct p2p_go_neg_results { /** * ssid - SSID of the group */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID in octets @@ -155,6 +162,11 @@ struct p2p_go_neg_results { struct p2ps_provision { /** + * pd_seeker - P2PS provision discovery seeker role + */ + unsigned int pd_seeker:1; + + /** * status - Remote returned provisioning status code */ int status; @@ -195,6 +207,23 @@ struct p2ps_provision { u8 adv_mac[ETH_ALEN]; /** + * cpt_mask - Supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * info - Vendor defined extra Provisioning information */ char info[0]; @@ -234,6 +263,23 @@ struct p2ps_advertisement { u8 hash[P2PS_HASH_LEN]; /** + * cpt_mask - supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordinatin Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage */ char svc_name[0]; @@ -268,27 +314,27 @@ struct p2p_peer_info { /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ - char manufacturer[65]; + char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ - char model_name[33]; + char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ - char model_number[33]; + char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ - char serial_number[33]; + char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; /** * level - Signal level @@ -316,7 +362,7 @@ struct p2p_peer_info { * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ - u8 wps_sec_dev_type_list[128]; + u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; /** * wps_sec_dev_type_list_len - Length of secondary device type list @@ -495,7 +541,7 @@ struct p2p_config { * This data will be added to the end of the SSID after the * DIRECT-<random two octets> prefix. */ - u8 ssid_postfix[32 - 9]; + u8 ssid_postfix[SSID_MAX_LEN - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data @@ -569,12 +615,14 @@ struct p2p_config { * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) + * @freq: Forced frequency (in MHz) to use or 0. * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be - * sent on the same channel or to be dropped if the driver is not - * anymore listening to Probe Request frames. + * sent on the same channel, unless otherwise specified, or to be + * dropped if the driver is not listening to Probe Request frames + * anymore. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which @@ -585,7 +633,8 @@ struct p2p_config { * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ - int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf, + unsigned int freq); /** * send_action - Transmit an Action frame @@ -704,6 +753,7 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID + * @go_intent: Peer's GO Intent * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the @@ -712,7 +762,8 @@ struct p2p_config { * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ - void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent); /** * go_neg_completed - Notification of GO Negotiation results @@ -949,18 +1000,21 @@ struct p2p_config { /** * Determine if we have a persistent group we share with remote peer + * and allocate interface for this group if needed * @ctx: Callback context from cb_ctx * @addr: Peer device address to search for * @ssid: Persistent group SSID or %NULL if any * @ssid_len: Length of @ssid - * @go_dev_addr: Buffer for returning intended GO P2P Device Address + * @go_dev_addr: Buffer for returning GO P2P Device Address * @ret_ssid: Buffer for returning group SSID * @ret_ssid_len: Buffer for returning length of @ssid + * @intended_iface_addr: Buffer for returning intended iface address * Returns: 1 if a matching persistent group was found, 0 otherwise */ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len); + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr); /** * Get information about a possible local GO role @@ -1001,7 +1055,8 @@ struct p2p_config { u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info); + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1023,6 +1078,20 @@ struct p2p_config { * P2PS_SETUP_* bitmap is used as the parameters and return value. */ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @ctx: Callback context from cb_ctx + * @go: Whether the use if for GO role + * @len: Length of freq_list in entries (both IN and OUT) + * @freq_list: Buffer for returning the preferred frequencies (MHz) + * Returns: 0 on success, -1 on failure + * + * This function can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *ctx, int go, + unsigned int *len, unsigned int *freq_list); }; @@ -1460,11 +1529,13 @@ enum p2p_probe_req_status { * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets + * @rx_freq: Probe Request frame RX frequency * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len); + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq); /** * p2p_rx_action - Report received Action frame @@ -1607,7 +1678,7 @@ struct p2p_group_config { /** * ssid - Group SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID @@ -2214,7 +2285,7 @@ struct p2p_nfc_params { size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; - u8 go_ssid[32]; + u8 go_ssid[SSID_MAX_LEN]; size_t go_ssid_len; }; @@ -2240,8 +2311,33 @@ struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +void p2p_service_flush_asp(struct p2p_data *p2p); struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); +/** + * p2p_expire_peers - Periodic cleanup function to expire peers + * @p2p: P2P module context from p2p_init() + * + * This is a cleanup function that the entity calling p2p_init() is + * expected to call periodically to clean up expired peer entries. + */ +void p2p_expire_peers(struct p2p_data *p2p); + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size); + +/** + * p2p_group_get_common_freqs - Get the group common frequencies + * @group: P2P group context from p2p_group_init() + * @common_freqs: On return will hold the group common frequencies + * @num: On return will hold the number of group common frequencies + * Returns: 0 on success, -1 otherwise + */ +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 92c920662edb..793d28ba7bdd 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/qca-vendor.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -109,6 +110,44 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, } +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, + unsigned int size) +{ + unsigned int i, count = 0; + u8 op_class, op_channel; + + if (!size) + return; + + /* + * First, determine the number of P2P supported channels in the + * pref_freq_list returned from driver. This is needed for calculations + * of the vendor IE size. + */ + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) == 0) + count++; + } + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + count * sizeof(u16)); + wpabuf_put_be24(buf, OUI_QCA); + wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) < 0) { + wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", + preferred_freq_list[i]); + continue; + } + wpabuf_put_u8(buf, op_class); + wpabuf_put_u8(buf, op_channel); + } +} + + void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan) { @@ -353,10 +392,10 @@ void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) /* Service Hash */ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); - wpabuf_put_data(buf, p2p->query_hash, + wpabuf_put_data(buf, p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", - p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); + p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); } @@ -404,152 +443,221 @@ void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) } -void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, - u8 hash_count, const u8 *hash, - struct p2ps_advertisement *adv_list) +static int p2ps_wildcard_hash(struct p2p_data *p2p, + const u8 *hash, u8 hash_count) +{ + u8 i; + const u8 *test = hash; + + for (i = 0; i < hash_count; i++) { + if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return 1; + test += P2PS_HASH_LEN; + } + + return 0; +} + + +static int p2p_wfa_service_adv(struct p2p_data *p2p) { struct p2ps_advertisement *adv; - struct wpabuf *tmp_buf; - u8 *tag_len = NULL, *ie_len = NULL; - size_t svc_len = 0, remaining = 0, total_len = 0; - if (!adv_list || !hash) - return; + for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) { + if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; + } - /* Allocate temp buffer, allowing for overflow of 1 instance */ - tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); - if (!tmp_buf) - return; + return 0; +} - for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; - adv = adv->next) { - u8 count = hash_count; - const u8 *test = hash; - while (count--) { - /* Check for wildcard */ - if (os_memcmp(test, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - total_len = MAX_SVC_ADV_LEN + 1; - goto wild_hash; - } +static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p, + u32 adv_id, u16 config_methods, + const char *svc_name, u8 **ie_len, u8 **pos, + size_t *total_len, u8 *attr_len) +{ + size_t svc_len; + size_t remaining; + size_t info_len; - if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) - goto hash_match; + p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id); + svc_len = os_strlen(svc_name); + info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) + + svc_len; - test += P2PS_HASH_LEN; - } + if (info_len + *total_len > MAX_SVC_ADV_LEN) { + p2p_dbg(p2p, + "Unsufficient buffer, failed to add advertised service info"); + return -1; + } - /* No matches found - Skip this Adv Instance */ - continue; - -hash_match: - if (!tag_len) { - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - remaining = 255 - 4; - if (!ie_len) { - wpabuf_put_u8(tmp_buf, - P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(tmp_buf, sizeof(u16)); - remaining -= (sizeof(u8) + sizeof(u16)); - } - } + if (svc_len > 255) { + p2p_dbg(p2p, + "Invalid service name length (%u bytes), failed to add advertised service info", + (unsigned int) svc_len); + return -1; + } - svc_len = os_strlen(adv->svc_name); + if (*ie_len) { + int ie_data_len = (*pos - *ie_len) - 1; - if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { - /* Can't fit... return wildcard */ - total_len = MAX_SVC_ADV_LEN + 1; - break; + if (ie_data_len < 0 || ie_data_len > 255) { + p2p_dbg(p2p, + "Invalid IE length, failed to add advertised service info"); + return -1; } + remaining = 255 - ie_data_len; + } else { + /* + * Adding new P2P IE header takes 6 extra bytes: + * - 2 byte IE header (1 byte IE id and 1 byte length) + * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below + */ + *ie_len = p2p_buf_add_ie_hdr(buf); + remaining = 255 - 4; + } - if (remaining <= (sizeof(adv->id) + - sizeof(adv->config_methods))) { - size_t front = remaining; - size_t back = (sizeof(adv->id) + - sizeof(adv->config_methods)) - front; - u8 holder[sizeof(adv->id) + - sizeof(adv->config_methods)]; - - /* This works even if front or back == 0 */ - WPA_PUT_LE32(holder, adv->id); - WPA_PUT_BE16(&holder[sizeof(adv->id)], - adv->config_methods); - wpabuf_put_data(tmp_buf, holder, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - wpabuf_put_data(tmp_buf, &holder[front], back); - remaining = 255 - (sizeof(adv->id) + - sizeof(adv->config_methods)) - back; - } else { - wpabuf_put_le32(tmp_buf, adv->id); - wpabuf_put_be16(tmp_buf, adv->config_methods); - remaining -= (sizeof(adv->id) + - sizeof(adv->config_methods)); - } + if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) { + /* + * Split adv_id, config_methods, and svc_name_len between two + * IEs. + */ + size_t front = remaining; + size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front; + u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)]; - /* We are guaranteed at least one byte for svc_len */ - wpabuf_put_u8(tmp_buf, svc_len); - remaining -= sizeof(u8); - - if (remaining < svc_len) { - size_t front = remaining; - size_t back = svc_len - front; - - wpabuf_put_data(tmp_buf, adv->svc_name, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - - /* In rare cases, we must split across 3 attributes */ - if (back > 255 - 4) { - wpabuf_put_data(tmp_buf, - &adv->svc_name[front], 255 - 4); - back -= 255 - 4; - front += 255 - 4; - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - } - - wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); - remaining = 255 - 4 - back; - } else { - wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); - remaining -= svc_len; - } + WPA_PUT_LE32(holder, adv_id); + WPA_PUT_BE16(&holder[sizeof(u32)], config_methods); + holder[sizeof(u32) + sizeof(u16)] = svc_len; + + if (front) + wpabuf_put_data(buf, holder, front); + + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); - /* adv_id config_methods svc_string */ - total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + wpabuf_put_data(buf, &holder[front], back); + remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) - + back; + } else { + wpabuf_put_le32(buf, adv_id); + wpabuf_put_be16(buf, config_methods); + wpabuf_put_u8(buf, svc_len); + remaining -= sizeof(adv_id) + sizeof(config_methods) + + sizeof(u8); } - if (tag_len) - p2p_buf_update_ie_hdr(tmp_buf, tag_len); + if (remaining < svc_len) { + /* split svc_name between two or three IEs */ + size_t front = remaining; + size_t back = svc_len - front; - if (ie_len) - WPA_PUT_LE16(ie_len, (u16) total_len); + if (front) + wpabuf_put_data(buf, svc_name, front); -wild_hash: - /* If all fit, return matching instances, otherwise the wildcard */ - if (total_len <= MAX_SVC_ADV_LEN) { - wpabuf_put_buf(buf, tmp_buf); + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(buf, &svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + } + + wpabuf_put_data(buf, &svc_name[front], back); + remaining = 255 - 4 - back; } else { - char *wild_card = P2PS_WILD_HASH_STR; - u8 wild_len; - - /* Insert wildcard instance */ - tag_len = p2p_buf_add_ie_hdr(buf); - wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(buf, sizeof(u16)); - - wild_len = (u8) os_strlen(wild_card); - wpabuf_put_le32(buf, 0); - wpabuf_put_be16(buf, 0); - wpabuf_put_u8(buf, wild_len); - wpabuf_put_data(buf, wild_card, wild_len); - - WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); - p2p_buf_update_ie_hdr(buf, tag_len); + wpabuf_put_data(buf, svc_name, svc_len); + remaining -= svc_len; } + p2p_buf_update_ie_hdr(buf, *ie_len); + + /* set *ie_len to NULL if a new IE has to be added on the next call */ + if (!remaining) + *ie_len = NULL; + + /* set *pos to point to the next byte to update */ + *pos = wpabuf_put(buf, 0); + + *total_len += info_len; + WPA_PUT_LE16(attr_len, (u16) *total_len); + return 0; +} + + +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 hash_count, const u8 *hash, + struct p2ps_advertisement *adv_list) +{ + struct p2ps_advertisement *adv; + int p2ps_wildcard; + size_t total_len; + struct wpabuf *tmp_buf = NULL; + u8 *pos, *attr_len, *ie_len = NULL; + + if (!adv_list || !hash || !hash_count) + return; + + wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values", + hash, hash_count * P2PS_HASH_LEN); + p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) && + p2p_wfa_service_adv(p2p); + + /* Allocate temp buffer, allowing for overflow of 1 instance */ + tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); + if (!tmp_buf) + return; + + /* + * Attribute data can be split into a number of IEs. Start with the + * first IE and the attribute headers here. + */ + ie_len = p2p_buf_add_ie_hdr(tmp_buf); + + total_len = 0; + + wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE); + attr_len = wpabuf_put(tmp_buf, sizeof(u16)); + WPA_PUT_LE16(attr_len, (u16) total_len); + p2p_buf_update_ie_hdr(tmp_buf, ie_len); + pos = wpabuf_put(tmp_buf, 0); + + if (p2ps_wildcard) { + /* org.wi-fi.wfds match found */ + p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR, + &ie_len, &pos, &total_len, attr_len); + } + + /* add advertised service info of matching services */ + for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; + adv = adv->next) { + const u8 *test = hash; + u8 i; + + for (i = 0; i < hash_count; i++) { + /* exact name hash match */ + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 && + p2p_buf_add_service_info(tmp_buf, p2p, + adv->id, + adv->config_methods, + adv->svc_name, + &ie_len, &pos, + &total_len, + attr_len)) + break; + + test += P2PS_HASH_LEN; + } + } + + if (total_len) + wpabuf_put_buf(buf, tmp_buf); wpabuf_free(tmp_buf); } diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c index 86bae1a2c0db..98805fee2409 100644 --- a/src/p2p/p2p_dev_disc.c +++ b/src/p2p/p2p_dev_disc.c @@ -314,7 +314,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 98abf9d2293e..83b43563d945 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->op_reg_class, p2p->op_channel); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) @@ -312,7 +315,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - if (peer && peer->go_state == REMOTE_GO) { + if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -379,7 +382,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -542,6 +545,195 @@ int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, } +static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + p2p_dbg(p2p, + "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device"); + + /* + * Search for a common channel in our preferred frequency list which is + * also supported by the peer device. + */ + for (i = 0; i < size && !found; i++) { + /* + * Make sure that the common frequency is: + * 1. Supported by peer + * 2. Allowed for P2P use. + */ + oper_freq = freq_list[i]; + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); + continue; + } + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + for (j = 0; j < msg->channel_list_len; j++) { + + if (op_channel != msg->channel_list[j]) + continue; + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + /* + * Peer device supports a Preferred Frequency List. + * Search for a common channel in the preferred frequency lists + * of both peer and local devices. + */ + for (i = 0; i < size && !found; i++) { + for (j = 2; j < (msg->pref_freq_list_len / 2); j++) { + oper_freq = p2p_channel_to_freq( + msg->pref_freq_list[2 * j], + msg->pref_freq_list[2 * j + 1]); + if (freq_list[i] != oper_freq) + continue; + + /* + * Make sure that the found frequency is: + * 1. Supported + * 2. Allowed for P2P use. + */ + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", + oper_freq); + continue; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || + !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "No common preferred channels found! Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg) +{ + unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; + unsigned int i; + u8 op_class, op_channel; + + /* + * Use the preferred channel list from the driver only if there is no + * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating + * channel hardcoded in the configuration file. + */ + if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan || + (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel) + return; + + /* Obtain our preferred frequency list from driver based on P2P role. */ + size = P2P_MAX_PREF_CHANNELS; + if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, + freq_list)) + return; + + /* + * Check if peer's preference of operating channel is in + * our preferred channel list. + */ + for (i = 0; i < size; i++) { + if (freq_list[i] == (unsigned int) dev->oper_freq) + break; + } + if (i != size) { + /* Peer operating channel preference matches our preference */ + if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < + 0) { + p2p_dbg(p2p, + "Peer operating channel preference is unsupported frequency %u MHz", + freq_list[i]); + } else { + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; + } + } + + p2p_dbg(p2p, + "Peer operating channel preference: %d MHz is not in our preferred channel list", + dev->oper_freq); + + /* + Check if peer's preferred channel list is + * _not_ included in the GO Negotiation Request or Invitation Request. + */ + if (msg->pref_freq_list_len == 0) + p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size); + else + p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size); +} + + void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -668,7 +860,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, - msg.dev_password_id); + msg.dev_password_id, + msg.go_intent ? (*msg.go_intent >> 1) : + 0); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; @@ -797,6 +991,12 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; @@ -1148,6 +1348,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; + /* + * Use the driver preferred frequency list extension if local device is + * GO. + */ + if (go) + p2p_check_pref_chan(p2p, go, dev, &msg); + p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 41ca99faaf48..0d6699346568 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -1071,3 +1071,43 @@ void p2p_loop_on_all_groups(struct p2p_data *p2p, 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; +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 6af19ceda450..0ce4058fe3e6 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -14,6 +14,12 @@ #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 +/* + * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P + * Device over the P2P Client Info received from a GO. + */ +#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1 + enum p2p_role_indication; /* @@ -47,6 +53,9 @@ struct p2p_device { * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. + * + * This field is also used during P2PS PD to store the intended GO + * address of the peer. */ u8 interface_addr[ETH_ALEN]; @@ -71,7 +80,7 @@ struct p2p_device { char country[3]; struct p2p_channels channels; int oper_freq; - u8 oper_ssid[32]; + u8 oper_ssid[SSID_MAX_LEN]; size_t oper_ssid_len; /** @@ -107,6 +116,8 @@ struct p2p_device { #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) #define P2P_DEV_P2PS_REPORTED BIT(20) #define P2P_DEV_PD_PEER_P2PS BIT(21) +#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22) + unsigned int flags; int status; /* enum p2p_status_code */ @@ -322,7 +333,7 @@ struct p2p_data { /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - ssid length in octets @@ -403,7 +414,7 @@ struct p2p_data { enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; - u8 inv_ssid[32]; + u8 inv_ssid[SSID_MAX_LEN]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; @@ -506,11 +517,9 @@ struct p2p_data { struct p2ps_advertisement *p2ps_adv_list; struct p2ps_provision *p2ps_prov; u8 wild_card_hash[P2PS_HASH_LEN]; - u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; - u8 query_count; u8 p2ps_seek; + u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; u8 p2ps_seek_count; - u8 p2ps_svc_found; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; @@ -529,6 +538,9 @@ struct p2p_data { u16 authorized_oob_dev_pw_id; struct wpabuf **vendor_elem; + + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + unsigned int num_pref_freq; }; /** @@ -578,7 +590,7 @@ struct p2p_message { const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; u16 config_methods; /* WPS IE */ @@ -631,6 +643,9 @@ struct p2p_message { const u8 *persistent_dev; const u8 *persistent_ssid; size_t persistent_ssid_len; + + const u8 *pref_freq_list; + size_t pref_freq_list_len; }; @@ -757,6 +772,8 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, u32 size); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -786,6 +803,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -795,6 +814,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); +void p2ps_prov_free(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, @@ -840,7 +860,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 558c6dd0c58f..108e5b7f93e4 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); @@ -134,6 +137,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -158,6 +164,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + return buf; } @@ -337,6 +346,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p_reselect_channel(p2p, &intersection); } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { @@ -387,7 +402,7 @@ fail: } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id) { - if (msg.group_id_len - ETH_ALEN <= 32) { + if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; @@ -528,6 +543,12 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, peer_oper_freq = 0; } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, 0, dev, &msg); + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq, peer_oper_freq); diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index fd6a4610d839..bd1e68bd4241 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -149,7 +149,8 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || nlen > 32) { + if (data + len - pos < (int) nlen || + nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d (buf len %d)", (int) nlen, (int) (data + len - pos)); @@ -160,8 +161,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, for (i = 0; i < nlen; i++) { if (msg->device_name[i] == '\0') break; - if (msg->device_name[i] > 0 && - msg->device_name[i] < 32) + if (is_ctrl_char(msg->device_name[i])) msg->device_name[i] = '_'; } wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR @@ -203,7 +203,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, MAC2STR(msg->group_bssid)); break; case P2P_ATTR_GROUP_ID: - if (len < ETH_ALEN || len > ETH_ALEN + 32) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " "attribute length %d", len); return -1; @@ -371,9 +371,9 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, break; case P2P_ATTR_PERSISTENT_GROUP: { - if (len < ETH_ALEN) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, - "P2P: Too short Persistent Group Info (length %u)", + "P2P: Invalid Persistent Group Info (length %u)", len); return -1; } @@ -516,7 +516,7 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) struct ieee802_11_elems elems; ieee802_11_parse_elems(data, len, &elems, 0); - if (elems.ds_params && elems.ds_params_len >= 1) + if (elems.ds_params) msg->ds_params = elems.ds_params; if (elems.ssid) msg->ssid = elems.ssid - 2; @@ -548,6 +548,9 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) } #endif /* CONFIG_WIFI_DISPLAY */ + msg->pref_freq_list = elems.pref_freq_list; + msg->pref_freq_list_len = elems.pref_freq_list_len; + return 0; } @@ -674,8 +677,8 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, t += 2; if (count > cend - t) return -1; /* invalid Device Name TLV */ - if (count >= 32) - count = 32; + if (count >= WPS_DEV_NAME_MAX_LEN) + count = WPS_DEV_NAME_MAX_LEN; cli->dev_name = (const char *) t; cli->dev_name_len = count; @@ -703,7 +706,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, for (i = 0; i < info.num_clients; i++) { struct p2p_client_info *cli; - char name[33]; + char name[WPS_DEV_NAME_MAX_LEN + 1]; char devtype[WPS_DEV_TYPE_BUFSIZE]; u8 s; int count; @@ -742,7 +745,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, name[cli->dev_name_len] = '\0'; count = (int) cli->dev_name_len - 1; while (count >= 0) { - if (name[count] > 0 && name[count] < 32) + if (is_ctrl_char(name[count])) name[count] = '_'; count--; } diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 328b1e029ce5..890094551821 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -44,7 +44,7 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; @@ -57,7 +57,11 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) if (found) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); - p2p_buf_add_intended_addr(buf, intended_addr); + + if (group_iface) + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr(buf, intended_addr); } else { if (!p2p->ssid_set) { p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); @@ -82,11 +86,12 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, struct wpabuf *buf, u16 config_methods) { struct p2ps_provision *prov = p2p->p2ps_prov; - u8 feat_cap_mask[] = { 1, 0 }; + struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; int shared_group = 0; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; /* If we might be explicite group owner, add GO details */ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | @@ -101,7 +106,7 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, if (p2p->cfg->get_persistent_group) { shared_group = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, - go_dev_addr, ssid, &ssid_len); + go_dev_addr, ssid, &ssid_len, intended_addr); } /* Add Operating Channel if conncap includes GO */ @@ -146,12 +151,17 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); - if (shared_group) + if (shared_group) { p2p_buf_add_persistent_group_info(buf, go_dev_addr, ssid, ssid_len); + /* Add intended interface address if it is not added yet */ + if ((prov->conncap == P2PS_SETUP_NONE || + prov->conncap == P2PS_SETUP_CLIENT) && + !is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr(buf, intended_addr); + } } @@ -232,7 +242,9 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, const u8 *group_id, size_t group_id_len, const u8 *persist_ssid, - size_t persist_ssid_len) + size_t persist_ssid_len, + const u8 *fcap, + u16 fcap_len) { struct wpabuf *buf; size_t extra = 0; @@ -270,7 +282,6 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, /* Add P2P IE for P2PS */ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { - u8 feat_cap_mask[] = { 1, 0 }; u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; @@ -293,18 +304,23 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, if (persist_ssid && p2p->cfg->get_persistent_group && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; persist = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, persist_ssid, persist_ssid_len, go_dev_addr, - ssid, &ssid_len); - if (persist) + ssid, &ssid_len, intended_addr); + if (persist) { p2p_buf_add_persistent_group_info( buf, go_dev_addr, ssid, ssid_len); + if (!is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr( + buf, intended_addr); + } } if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) @@ -344,8 +360,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, fcap_len, fcap); p2p_buf_update_ie_hdr(buf, len); } else if (status != P2P_SC_SUCCESS || adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); @@ -400,6 +415,18 @@ static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, } +static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) +{ + int i; + + for (i = 0; cpt_priority[i]; i++) + if (req_cpt_mask & cpt_priority[i]) + return cpt_priority[i]; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -415,9 +442,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u32 session_id = 0; u8 session_mac[ETH_ALEN]; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; + u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + struct p2ps_feature_capab resp_fcap = { 0, 0 }; + struct p2ps_feature_capab *req_fcap; if (p2p_parse(data, len, &msg)) return; @@ -425,6 +455,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); + group_mac = msg.intended_addr; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -441,9 +472,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (!(msg.wps_config_methods & - (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { + if (msg.adv_id) + allowed_config_methods |= WPS_CONFIG_P2PS; + else + allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; + + if (!(msg.wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } @@ -501,14 +535,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, os_memset(session_mac, 0, ETH_ALEN); os_memset(adv_mac, 0, ETH_ALEN); - os_memset(group_mac, 0, ETH_ALEN); + /* Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backwards compatible, therefore we allow processing of + * msg.feature_cap exceeding the size of the p2ps_feature_capab + * structure. + * Note 2: Vverification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if struct + * p2ps_feature_capab is extended to include additional fields and it + * affects the structure size. + */ if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && + msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && (msg.status || msg.conn_cap)) { u8 remote_conncap; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; os_memcpy(session_mac, msg.session_mac, ETH_ALEN); os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -533,9 +576,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", auto_accept, remote_conncap, conncap); - if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { + resp_fcap.cpt = + p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, + resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { p2p_dbg(p2p, "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", p2ps_adv->config_methods, @@ -624,6 +680,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->conncap, remote_conncap, conncap); + resp_fcap.cpt = p2ps_own_preferred_cpt( + p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, + req_fcap->cpt, resp_fcap.cpt); + /* * Ensure that if we asked for PIN originally, * our method is consistent with original @@ -634,14 +699,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, else if (method & WPS_CONFIG_KEYPAD) method = WPS_CONFIG_DISPLAY; - /* Reject this "Deferred Accept* if incompatible - * conncap or method */ if (!conncap || - !(msg.wps_config_methods & method)) + !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - else + } else { reject = P2P_SC_SUCCESS; + } p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -659,7 +732,9 @@ out: config_methods, adv_id, msg.group_id, msg.group_id_len, msg.persistent_ssid, - msg.persistent_ssid_len); + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -696,7 +771,7 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -709,7 +784,7 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -719,7 +794,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, @@ -728,7 +805,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -737,7 +816,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -748,7 +829,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, NULL); + 0, 1, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { size_t buf_len = msg.session_info_len; char *buf = os_malloc(2 * buf_len + 1); @@ -764,14 +847,45 @@ out: adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, buf); + 0, 1, buf, + (const u8 *) &resp_fcap, sizeof(resp_fcap)); os_free(buf); } } - if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { + /* + * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it either on legacy P2P PD or on P2PS PD only if we need to + * enter/show PIN. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD request, response status SUCCESS + * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, + * response status: SUCCESS + * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 5. P2PS follow-on with SUCCESS_DEFERRED, + * advertiser role: DISPLAY, autoaccept: FALSE, + * seeker: KEYPAD, response status: SUCCESS + */ + if (p2p->cfg->prov_disc_req && + ((reject == P2P_SC_SUCCESS && !msg.adv_id) || + (!msg.status && + (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && + passwd_id == DEV_PW_USER_SPECIFIED) || + (!msg.status && + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (reject == P2P_SC_SUCCESS && + msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { const u8 *dev_addr = sa; + if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, @@ -783,10 +897,133 @@ out: 0, msg.group_id, msg.group_id_len); } + + if (dev && reject == P2P_SC_SUCCESS) { + switch (config_methods) { + case WPS_CONFIG_DISPLAY: + dev->wps_prov_info = WPS_CONFIG_KEYPAD; + break; + case WPS_CONFIG_KEYPAD: + dev->wps_prov_info = WPS_CONFIG_DISPLAY; + break; + case WPS_CONFIG_PUSHBUTTON: + dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; + break; + case WPS_CONFIG_P2PS: + dev->wps_prov_info = WPS_CONFIG_P2PS; + break; + default: + dev->wps_prov_info = 0; + break; + } + + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, + ETH_ALEN); + } p2p_parse_free(&msg); } +static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, + struct p2p_message *msg) +{ + u8 conn_cap_go = 0; + u8 conn_cap_cli = 0; + u32 session_id; + u32 adv_id; + +#define P2PS_PD_RESP_CHECK(_val, _attr) \ + do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ + return -1; \ + } \ + } while (0) + + P2PS_PD_RESP_CHECK(1, status); + P2PS_PD_RESP_CHECK(1, adv_id); + P2PS_PD_RESP_CHECK(1, adv_mac); + P2PS_PD_RESP_CHECK(1, capability); + P2PS_PD_RESP_CHECK(1, p2p_device_info); + P2PS_PD_RESP_CHECK(1, session_id); + P2PS_PD_RESP_CHECK(1, session_mac); + P2PS_PD_RESP_CHECK(1, feature_cap); + + session_id = WPA_GET_LE32(msg->session_id); + adv_id = WPA_GET_LE32(msg->adv_id); + + if (p2p->p2ps_prov->session_id != session_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, + ETH_ALEN)) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session MAC"); + return -1; + } + + if (p2p->p2ps_prov->adv_id != adv_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement MAC"); + return -1; + } + + if (msg->listen_channel) { + p2p_dbg(p2p, + "Ignore malformed PD Response - unexpected Listen Channel"); + return -1; + } + + if (*msg->status == P2P_SC_SUCCESS && + !(!!msg->conn_cap ^ !!msg->persistent_dev)) { + p2p_dbg(p2p, + "Ignore malformed PD Response - either conn_cap or persistent group should be present"); + return -1; + } + + if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, + "Ignore malformed PD Response - persistent group is present, but the status isn't success"); + return -1; + } + + if (msg->conn_cap) { + conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; + conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; + } + + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + channel_list); + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + config_timeout); + + P2PS_PD_RESP_CHECK(conn_cap_go, group_id); + P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); + P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); + /* + * TODO: Also validate that operating channel is present if the device + * is a GO in a persistent group. We can't do it here since we don't + * know what is the role of the peer. It should be probably done in + * p2ps_prov_complete callback, but currently operating channel isn't + * passed to it. + */ + +#undef P2PS_PD_RESP_CHECK + + return 0; +} + + void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { @@ -794,24 +1031,26 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; u8 status = P2P_SC_SUCCESS; - int success = 0; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; + int p2ps_seeker; if (p2p_parse(data, len, &msg)) return; + if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { + p2p_parse_free(&msg); + return; + } + /* Parse the P2PS members present */ if (msg.status) status = *msg.status; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); - else - os_memset(group_mac, 0, ETH_ALEN); + group_mac = msg.intended_addr; if (msg.adv_mac) os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -859,6 +1098,8 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->pending_action_state = P2P_NO_PENDING_ACTION; } + p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; + /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. @@ -881,8 +1122,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } @@ -909,7 +1149,6 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, } if ((msg.conn_cap || msg.persistent_dev) && - msg.adv_id && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) { @@ -918,23 +1157,20 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, conncap, passwd_id, msg.persistent_ssid, - msg.persistent_ssid_len, 1, 0, NULL); + msg.persistent_ssid_len, 1, 0, NULL, + msg.feature_cap, msg.feature_cap_len); } - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; - } - - if (status != P2P_SC_SUCCESS && - status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && - status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + p2ps_prov_free(p2p); + } else if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - 0, 0, NULL, 0, 1, 0, NULL); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + p2ps_prov_free(p2p); } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -950,8 +1186,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, if (!deferred_sess_resp) { p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } utf8_escape((char *) msg.session_info, info_len, @@ -970,24 +1205,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_INFO_UNAVAILABLE, adv_id, adv_mac, NULL); - } else if (msg.wps_config_methods != dev->req_config_methods || - status != P2P_SC_SUCCESS) { + } else if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED, 0, - NULL, NULL); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); - success = 1; out: dev->req_config_methods = 0; @@ -999,7 +1233,28 @@ out: p2p_connect_send(p2p, dev); return; } - if (success && p2p->cfg->prov_disc_resp) + + /* + * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it only for a legacy P2P PD or for P2PS PD scenarios where + * show/enter PIN events are needed. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD response with a status SUCCESS + * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, + * response status: SUCCESS, local method KEYPAD + * 3. P2PS, advertiser method: KEYPAD,Seeker side, + * response status: INFO_CURRENTLY_UNAVAILABLE, + * local method: DISPLAY + */ + if (p2p->cfg->prov_disc_resp && + ((status == P2P_SC_SUCCESS && !adv_id) || + (p2ps_seeker && status == P2P_SC_SUCCESS && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (p2ps_seeker && + status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_USER_SPECIFIED))) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); @@ -1120,7 +1375,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, /* Reset provisioning info */ dev->wps_prov_info = 0; - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; @@ -1176,3 +1431,10 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->pd_retries = 0; p2p->pd_force_freq = 0; } + + +void p2ps_prov_free(struct p2p_data *p2p) +{ + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; +} diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index f32751d79ca8..2e2aa8ad06f0 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/defs.h" #include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -67,50 +68,11 @@ int p2p_channel_to_freq(int op_class, int channel) */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { - /* TODO: more operating classes */ - if (freq >= 2412 && freq <= 2472) { - if ((freq - 2407) % 5) - return -1; - - *op_class = 81; /* 2.407 GHz, channels 1..13 */ - *channel = (freq - 2407) / 5; - return 0; - } - - if (freq == 2484) { - *op_class = 82; /* channel 14 */ - *channel = 14; - return 0; - } - - if (freq >= 5180 && freq <= 5240) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 115; /* 5 GHz, channels 36..48 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 5745 && freq <= 5805) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 124; /* 5 GHz, channels 149..161 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 58320 && freq <= 64800) { - if ((freq - 58320) % 2160) - return -1; - - *op_class = 180; /* 60 GHz, channels 1..4 */ - *channel = (freq - 56160) / 2160; - return 0; - } + if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == + NUM_HOSTAPD_MODES) + return -1; - return -1; + return 0; } @@ -497,12 +459,22 @@ int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, break; for (j = 0; j < c->channels; j++) { int freq; + unsigned int k; + if (idx + 1 == max_len) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; + + for (k = 0; k < idx; k++) { + if (freq_list[k] == freq) + break; + } + + if (k < idx) + continue; freq_list[idx++] = freq; } } |