p2p_pd.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/* 2 * Wi-Fi Direct - P2P provision discovery 3 * Copyright (c) 2009-2010, Atheros Communications 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15#include "includes.h" 16 17#include "common.h" 18#include "common/ieee802_11_defs.h" 19#include "wps/wps_defs.h" 20#include "p2p_i.h" 21#include "p2p.h" 22 23 24static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, 25 u16 config_methods) 26{ 27 u8 *len; 28 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 29 len = wpabuf_put(buf, 1); 30 wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); 31 32 /* Config Methods */ 33 wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); 34 wpabuf_put_be16(buf, 2); 35 wpabuf_put_be16(buf, config_methods); 36 37 p2p_buf_update_ie_hdr(buf, len); 38} 39 40 41static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, 42 u8 dialog_token, 43 u16 config_methods, 44 struct p2p_device *go) 45{ 46 struct wpabuf *buf; 47 u8 *len; 48 49 buf = wpabuf_alloc(1000); 50 if (buf == NULL) 51 return NULL; 52 53 p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); 54 55 len = p2p_buf_add_ie_hdr(buf); 56 p2p_buf_add_capability(buf, p2p->dev_capab, 0); 57 p2p_buf_add_device_info(buf, p2p, NULL); 58 if (go) { 59 p2p_buf_add_group_id(buf, go->info.p2p_device_addr, 60 go->oper_ssid, go->oper_ssid_len); 61 } 62 p2p_buf_update_ie_hdr(buf, len); 63 64 /* WPS IE with Config Methods attribute */ 65 p2p_build_wps_ie_config_methods(buf, config_methods); 66 67 return buf; 68} 69 70 71static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, 72 u8 dialog_token, 73 u16 config_methods) 74{ 75 struct wpabuf *buf; 76 77 buf = wpabuf_alloc(100); 78 if (buf == NULL) 79 return NULL; 80 81 p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); 82 83 /* WPS IE with Config Methods attribute */ 84 p2p_build_wps_ie_config_methods(buf, config_methods); 85 86 return buf; 87} 88 89 90void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 91 const u8 *data, size_t len, int rx_freq) 92{ 93 struct p2p_message msg; 94 struct p2p_device *dev; 95 int freq; 96 int reject = 1; 97 struct wpabuf *resp; 98 99 if (p2p_parse(data, len, &msg)) 100 return; 101 102 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 103 "P2P: Received Provision Discovery Request from " MACSTR 104 " with config methods 0x%x (freq=%d)", 105 MAC2STR(sa), msg.wps_config_methods, rx_freq); 106 107 dev = p2p_get_device(p2p, sa); 108 if (dev == NULL || !(dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { 109 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 110 "P2P: Provision Discovery Request from " 111 "unknown peer " MACSTR, MAC2STR(sa)); 112 if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { 113 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 114 "P2P: Provision Discovery Request add device " 115 "failed " MACSTR, MAC2STR(sa)); 116 } 117 } 118 119 if (!(msg.wps_config_methods & 120 (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | 121 WPS_CONFIG_PUSHBUTTON))) { 122 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " 123 "Config Methods in Provision Discovery Request"); 124 goto out; 125 } 126 127 if (dev) 128 dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 129 P2P_DEV_PD_PEER_KEYPAD); 130 if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { 131 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR 132 " requested us to show a PIN on display", MAC2STR(sa)); 133 if (dev) 134 dev->flags |= P2P_DEV_PD_PEER_KEYPAD; 135 } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { 136 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR 137 " requested us to write its PIN using keypad", 138 MAC2STR(sa)); 139 if (dev) 140 dev->flags |= P2P_DEV_PD_PEER_DISPLAY; 141 } 142 143 reject = 0; 144 145out: 146 resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, 147 reject ? 0 : msg.wps_config_methods); 148 if (resp == NULL) { 149 p2p_parse_free(&msg); 150 return; 151 } 152 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 153 "P2P: Sending Provision Discovery Response"); 154 if (rx_freq > 0) 155 freq = rx_freq; 156 else 157 freq = p2p_channel_to_freq(p2p->cfg->country, 158 p2p->cfg->reg_class, 159 p2p->cfg->channel); 160 if (freq < 0) { 161 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 162 "P2P: Unknown regulatory class/channel"); 163 wpabuf_free(resp); 164 p2p_parse_free(&msg); 165 return; 166 } 167 p2p->pending_action_state = P2P_NO_PENDING_ACTION; 168 if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, 169 p2p->cfg->dev_addr, 170 wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { 171 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 172 "P2P: Failed to send Action frame"); 173 } 174 175 wpabuf_free(resp); 176 177 if (!reject && p2p->cfg->prov_disc_req) { 178 const u8 *dev_addr = sa; 179 if (msg.p2p_device_addr) 180 dev_addr = msg.p2p_device_addr; 181 p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, 182 msg.wps_config_methods, 183 dev_addr, msg.pri_dev_type, 184 msg.device_name, msg.config_methods, 185 msg.capability ? msg.capability[0] : 0, 186 msg.capability ? msg.capability[1] : 187 0); 188 189 } 190 p2p_parse_free(&msg); 191} 192 193 194void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, 195 const u8 *data, size_t len) 196{ 197 struct p2p_message msg; 198 struct p2p_device *dev; 199 u16 report_config_methods = 0; 200 201 if (p2p_parse(data, len, &msg)) 202 return; 203 204 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 205 "P2P: Received Provisioning Discovery Response from " MACSTR 206 " with config methods 0x%x", 207 MAC2STR(sa), msg.wps_config_methods); 208 209 dev = p2p_get_device(p2p, sa); 210 if (dev == NULL || !dev->req_config_methods) { 211 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 212 "P2P: Ignore Provisioning Discovery Response from " 213 MACSTR " with no pending request", MAC2STR(sa)); 214 p2p_parse_free(&msg); 215 return; 216 } 217 218 if (dev->dialog_token != msg.dialog_token) { 219 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 220 "P2P: Ignore Provisioning Discovery Response with " 221 "unexpected Dialog Token %u (expected %u)", 222 msg.dialog_token, dev->dialog_token); 223 p2p_parse_free(&msg); 224 return; 225 } 226 227 if (msg.wps_config_methods != dev->req_config_methods) { 228 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " 229 "our Provisioning Discovery Request"); 230 p2p_parse_free(&msg); 231 goto out; 232 } 233 234 report_config_methods = dev->req_config_methods; 235 dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 236 P2P_DEV_PD_PEER_KEYPAD); 237 if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { 238 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR 239 " accepted to show a PIN on display", MAC2STR(sa)); 240 dev->flags |= P2P_DEV_PD_PEER_DISPLAY; 241 } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { 242 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR 243 " accepted to write our PIN using keypad", 244 MAC2STR(sa)); 245 dev->flags |= P2P_DEV_PD_PEER_KEYPAD; 246 } 247 p2p_parse_free(&msg); 248 249out: 250 dev->req_config_methods = 0; 251 p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 252 if (p2p->cfg->prov_disc_resp) 253 p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, 254 report_config_methods); 255} 256 257 258int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, 259 int join) 260{ 261 struct wpabuf *req; 262 int freq; 263 264 freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; 265 if (freq <= 0) { 266 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 267 "P2P: No Listen/Operating frequency known for the " 268 "peer " MACSTR " to send Provision Discovery Request", 269 MAC2STR(dev->info.p2p_device_addr)); 270 return -1; 271 } 272 273 if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { 274 if (!(dev->info.dev_capab & 275 P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { 276 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 277 "P2P: Cannot use PD with P2P Device " MACSTR 278 " that is in a group and is not discoverable", 279 MAC2STR(dev->info.p2p_device_addr)); 280 return -1; 281 } 282 /* TODO: use device discoverability request through GO */ 283 } 284 285 dev->dialog_token++; 286 if (dev->dialog_token == 0) 287 dev->dialog_token = 1; 288 req = p2p_build_prov_disc_req(p2p, dev->dialog_token, 289 dev->req_config_methods, 290 join ? dev : NULL); 291 if (req == NULL) 292 return -1; 293 294 p2p->pending_action_state = P2P_PENDING_PD; 295 if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, 296 p2p->cfg->dev_addr, dev->info.p2p_device_addr, 297 wpabuf_head(req), wpabuf_len(req), 200) < 0) { 298 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 299 "P2P: Failed to send Action frame"); 300 wpabuf_free(req); 301 return -1; 302 } 303 304 wpabuf_free(req); 305 return 0; 306} 307 308 309int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, 310 u16 config_methods, int join) 311{ 312 struct p2p_device *dev; 313 314 dev = p2p_get_device(p2p, peer_addr); 315 if (dev == NULL) 316 dev = p2p_get_device_interface(p2p, peer_addr); 317 if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { 318 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " 319 "Discovery Request destination " MACSTR 320 " not yet known", MAC2STR(peer_addr)); 321 return -1; 322 } 323 324 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " 325 "Request with " MACSTR " (config methods 0x%x)", 326 MAC2STR(peer_addr), config_methods); 327 if (config_methods == 0) 328 return -1; 329 330 dev->req_config_methods = config_methods; 331 if (join) 332 dev->flags |= P2P_DEV_PD_FOR_JOIN; 333 else 334 dev->flags &= ~P2P_DEV_PD_FOR_JOIN; 335 336 if (p2p->go_neg_peer || 337 (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && 338 p2p->state != P2P_LISTEN_ONLY)) { 339 wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " 340 "operations; postpone Provision Discovery Request " 341 "with " MACSTR " (config methods 0x%x)", 342 MAC2STR(peer_addr), config_methods); 343 return 0; 344 } 345 346 return p2p_send_prov_disc_req(p2p, dev, join); 347} 348