LCOV - code coverage report
Current view: top level - src/p2p - p2p_pd.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1401264779 Lines: 206 252 81.7 %
Date: 2014-05-28 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Wi-Fi Direct - P2P provision discovery
       3             :  * Copyright (c) 2009-2010, Atheros Communications
       4             :  *
       5             :  * This software may be distributed under the terms of the BSD license.
       6             :  * See README for more details.
       7             :  */
       8             : 
       9             : #include "includes.h"
      10             : 
      11             : #include "common.h"
      12             : #include "common/ieee802_11_defs.h"
      13             : #include "wps/wps_defs.h"
      14             : #include "p2p_i.h"
      15             : #include "p2p.h"
      16             : 
      17             : 
      18             : /*
      19             :  * Number of retries to attempt for provision discovery requests
      20             :  * in case the peer is not listening.
      21             :  */
      22             : #define MAX_PROV_DISC_REQ_RETRIES 120
      23             : 
      24             : 
      25         207 : static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
      26             :                                             u16 config_methods)
      27             : {
      28             :         u8 *len;
      29         207 :         wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
      30         207 :         len = wpabuf_put(buf, 1);
      31         207 :         wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
      32             : 
      33             :         /* Config Methods */
      34         207 :         wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
      35         207 :         wpabuf_put_be16(buf, 2);
      36         207 :         wpabuf_put_be16(buf, config_methods);
      37             : 
      38         207 :         p2p_buf_update_ie_hdr(buf, len);
      39         207 : }
      40             : 
      41             : 
      42         164 : static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
      43             :                                                u8 dialog_token,
      44             :                                                u16 config_methods,
      45             :                                                struct p2p_device *go)
      46             : {
      47             :         struct wpabuf *buf;
      48             :         u8 *len;
      49         164 :         size_t extra = 0;
      50             : 
      51             : #ifdef CONFIG_WIFI_DISPLAY
      52         164 :         if (p2p->wfd_ie_prov_disc_req)
      53           0 :                 extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
      54             : #endif /* CONFIG_WIFI_DISPLAY */
      55             : 
      56         164 :         buf = wpabuf_alloc(1000 + extra);
      57         164 :         if (buf == NULL)
      58           0 :                 return NULL;
      59             : 
      60         164 :         p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
      61             : 
      62         164 :         len = p2p_buf_add_ie_hdr(buf);
      63         164 :         p2p_buf_add_capability(buf, p2p->dev_capab &
      64             :                                ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
      65         164 :         p2p_buf_add_device_info(buf, p2p, NULL);
      66         164 :         if (go) {
      67          66 :                 p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
      68          33 :                                      go->oper_ssid, go->oper_ssid_len);
      69             :         }
      70         164 :         p2p_buf_update_ie_hdr(buf, len);
      71             : 
      72             :         /* WPS IE with Config Methods attribute */
      73         164 :         p2p_build_wps_ie_config_methods(buf, config_methods);
      74             : 
      75             : #ifdef CONFIG_WIFI_DISPLAY
      76         164 :         if (p2p->wfd_ie_prov_disc_req)
      77           0 :                 wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
      78             : #endif /* CONFIG_WIFI_DISPLAY */
      79             : 
      80         164 :         return buf;
      81             : }
      82             : 
      83             : 
      84          43 : static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
      85             :                                                 u8 dialog_token,
      86             :                                                 u16 config_methods,
      87             :                                                 const u8 *group_id,
      88             :                                                 size_t group_id_len)
      89             : {
      90             :         struct wpabuf *buf;
      91          43 :         size_t extra = 0;
      92             : 
      93             : #ifdef CONFIG_WIFI_DISPLAY
      94          43 :         struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
      95          43 :         if (wfd_ie && group_id) {
      96             :                 size_t i;
      97           0 :                 for (i = 0; i < p2p->num_groups; i++) {
      98           0 :                         struct p2p_group *g = p2p->groups[i];
      99             :                         struct wpabuf *ie;
     100           0 :                         if (!p2p_group_is_group_id_match(g, group_id,
     101             :                                                          group_id_len))
     102           0 :                                 continue;
     103           0 :                         ie = p2p_group_get_wfd_ie(g);
     104           0 :                         if (ie) {
     105           0 :                                 wfd_ie = ie;
     106           0 :                                 break;
     107             :                         }
     108             :                 }
     109             :         }
     110          43 :         if (wfd_ie)
     111           0 :                 extra = wpabuf_len(wfd_ie);
     112             : #endif /* CONFIG_WIFI_DISPLAY */
     113             : 
     114          43 :         buf = wpabuf_alloc(100 + extra);
     115          43 :         if (buf == NULL)
     116           0 :                 return NULL;
     117             : 
     118          43 :         p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
     119             : 
     120             :         /* WPS IE with Config Methods attribute */
     121          43 :         p2p_build_wps_ie_config_methods(buf, config_methods);
     122             : 
     123             : #ifdef CONFIG_WIFI_DISPLAY
     124          43 :         if (wfd_ie)
     125           0 :                 wpabuf_put_buf(buf, wfd_ie);
     126             : #endif /* CONFIG_WIFI_DISPLAY */
     127             : 
     128          43 :         return buf;
     129             : }
     130             : 
     131             : 
     132          44 : void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
     133             :                                const u8 *data, size_t len, int rx_freq)
     134             : {
     135             :         struct p2p_message msg;
     136             :         struct p2p_device *dev;
     137             :         int freq;
     138          44 :         int reject = 1;
     139             :         struct wpabuf *resp;
     140             : 
     141          44 :         if (p2p_parse(data, len, &msg))
     142           2 :                 return;
     143             : 
     144         301 :         p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
     145             :                 " with config methods 0x%x (freq=%d)",
     146         301 :                 MAC2STR(sa), msg.wps_config_methods, rx_freq);
     147             : 
     148          43 :         dev = p2p_get_device(p2p, sa);
     149          43 :         if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
     150         216 :                 p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
     151         216 :                         MACSTR, MAC2STR(sa));
     152             : 
     153          72 :                 if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
     154             :                                    0)) {
     155           6 :                         p2p_dbg(p2p, "Provision Discovery Request add device failed "
     156           6 :                                 MACSTR, MAC2STR(sa));
     157             :                 }
     158           7 :         } else if (msg.wfd_subelems) {
     159           0 :                 wpabuf_free(dev->info.wfd_subelems);
     160           0 :                 dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
     161             :         }
     162             : 
     163          43 :         if (!(msg.wps_config_methods &
     164             :               (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
     165             :                WPS_CONFIG_PUSHBUTTON))) {
     166           1 :                 p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
     167           1 :                 goto out;
     168             :         }
     169             : 
     170          42 :         if (msg.group_id) {
     171             :                 size_t i;
     172          34 :                 for (i = 0; i < p2p->num_groups; i++) {
     173          33 :                         if (p2p_group_is_group_id_match(p2p->groups[i],
     174             :                                                         msg.group_id,
     175             :                                                         msg.group_id_len))
     176          33 :                                 break;
     177             :                 }
     178          34 :                 if (i == p2p->num_groups) {
     179           1 :                         p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
     180           1 :                         goto out;
     181             :                 }
     182             :         }
     183             : 
     184          41 :         if (dev)
     185          39 :                 dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
     186             :                                 P2P_DEV_PD_PEER_KEYPAD);
     187          41 :         if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
     188         210 :                 p2p_dbg(p2p, "Peer " MACSTR
     189         210 :                         " requested us to show a PIN on display", MAC2STR(sa));
     190          35 :                 if (dev)
     191          33 :                         dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
     192           6 :         } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
     193           6 :                 p2p_dbg(p2p, "Peer " MACSTR
     194             :                         " requested us to write its PIN using keypad",
     195           6 :                         MAC2STR(sa));
     196           1 :                 if (dev)
     197           1 :                         dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
     198             :         }
     199             : 
     200          41 :         reject = 0;
     201             : 
     202             : out:
     203          84 :         resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
     204          41 :                                         reject ? 0 : msg.wps_config_methods,
     205             :                                         msg.group_id, msg.group_id_len);
     206          43 :         if (resp == NULL) {
     207           0 :                 p2p_parse_free(&msg);
     208           0 :                 return;
     209             :         }
     210          43 :         p2p_dbg(p2p, "Sending Provision Discovery Response");
     211          43 :         if (rx_freq > 0)
     212          43 :                 freq = rx_freq;
     213             :         else
     214           0 :                 freq = p2p_channel_to_freq(p2p->cfg->reg_class,
     215           0 :                                            p2p->cfg->channel);
     216          43 :         if (freq < 0) {
     217           0 :                 p2p_dbg(p2p, "Unknown regulatory class/channel");
     218           0 :                 wpabuf_free(resp);
     219           0 :                 p2p_parse_free(&msg);
     220           0 :                 return;
     221             :         }
     222          43 :         p2p->pending_action_state = P2P_NO_PENDING_ACTION;
     223          86 :         if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
     224          43 :                             p2p->cfg->dev_addr,
     225          43 :                             wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
     226           0 :                 p2p_dbg(p2p, "Failed to send Action frame");
     227             :         } else
     228          43 :                 p2p->send_action_in_progress = 1;
     229             : 
     230          43 :         wpabuf_free(resp);
     231             : 
     232          43 :         if (!reject && p2p->cfg->prov_disc_req) {
     233          41 :                 const u8 *dev_addr = sa;
     234          41 :                 if (msg.p2p_device_addr)
     235          41 :                         dev_addr = msg.p2p_device_addr;
     236         287 :                 p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
     237          41 :                                         msg.wps_config_methods,
     238             :                                         dev_addr, msg.pri_dev_type,
     239          41 :                                         msg.device_name, msg.config_methods,
     240          82 :                                         msg.capability ? msg.capability[0] : 0,
     241          82 :                                         msg.capability ? msg.capability[1] :
     242             :                                         0,
     243             :                                         msg.group_id, msg.group_id_len);
     244             :         }
     245          43 :         p2p_parse_free(&msg);
     246             : }
     247             : 
     248             : 
     249          41 : void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
     250             :                                 const u8 *data, size_t len)
     251             : {
     252             :         struct p2p_message msg;
     253             :         struct p2p_device *dev;
     254          41 :         u16 report_config_methods = 0, req_config_methods;
     255          41 :         int success = 0;
     256             : 
     257          41 :         if (p2p_parse(data, len, &msg))
     258           3 :                 return;
     259             : 
     260         287 :         p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
     261             :                 " with config methods 0x%x",
     262         287 :                 MAC2STR(sa), msg.wps_config_methods);
     263             : 
     264          41 :         dev = p2p_get_device(p2p, sa);
     265          41 :         if (dev == NULL || !dev->req_config_methods) {
     266           0 :                 p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
     267           0 :                         " with no pending request", MAC2STR(sa));
     268           0 :                 p2p_parse_free(&msg);
     269           0 :                 return;
     270             :         }
     271             : 
     272          41 :         if (dev->dialog_token != msg.dialog_token) {
     273           2 :                 p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
     274           2 :                         msg.dialog_token, dev->dialog_token);
     275           1 :                 p2p_parse_free(&msg);
     276           1 :                 return;
     277             :         }
     278             : 
     279          40 :         if (p2p->pending_action_state == P2P_PENDING_PD) {
     280          39 :                 os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
     281          39 :                 p2p->pending_action_state = P2P_NO_PENDING_ACTION;
     282             :         }
     283             : 
     284             :         /*
     285             :          * Use a local copy of the requested config methods since
     286             :          * p2p_reset_pending_pd() can clear this in the peer entry.
     287             :          */
     288          40 :         req_config_methods = dev->req_config_methods;
     289             : 
     290             :         /*
     291             :          * If the response is from the peer to whom a user initiated request
     292             :          * was sent earlier, we reset that state info here.
     293             :          */
     294          79 :         if (p2p->user_initiated_pd &&
     295          39 :             os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
     296           0 :                 p2p_reset_pending_pd(p2p);
     297             : 
     298          40 :         if (msg.wps_config_methods != req_config_methods) {
     299           2 :                 p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
     300           1 :                         msg.wps_config_methods, req_config_methods);
     301           1 :                 if (p2p->cfg->prov_disc_fail)
     302           1 :                         p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
     303             :                                                  P2P_PROV_DISC_REJECTED);
     304           1 :                 p2p_parse_free(&msg);
     305           1 :                 goto out;
     306             :         }
     307             : 
     308          39 :         report_config_methods = req_config_methods;
     309          39 :         dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
     310             :                         P2P_DEV_PD_PEER_KEYPAD);
     311          39 :         if (req_config_methods & WPS_CONFIG_DISPLAY) {
     312         198 :                 p2p_dbg(p2p, "Peer " MACSTR
     313         198 :                         " accepted to show a PIN on display", MAC2STR(sa));
     314          33 :                 dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
     315           6 :         } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
     316           6 :                 p2p_dbg(p2p, "Peer " MACSTR
     317             :                         " accepted to write our PIN using keypad",
     318           6 :                         MAC2STR(sa));
     319           1 :                 dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
     320             :         }
     321             : 
     322             :         /* Store the provisioning info */
     323          39 :         dev->wps_prov_info = msg.wps_config_methods;
     324             : 
     325          39 :         p2p_parse_free(&msg);
     326          39 :         success = 1;
     327             : 
     328             : out:
     329          40 :         dev->req_config_methods = 0;
     330          40 :         p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
     331          40 :         if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
     332          12 :                 p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
     333          12 :                         MACSTR, MAC2STR(dev->info.p2p_device_addr));
     334           2 :                 dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
     335           2 :                 p2p_connect_send(p2p, dev);
     336           2 :                 return;
     337             :         }
     338          38 :         if (success && p2p->cfg->prov_disc_resp)
     339          37 :                 p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
     340             :                                          report_config_methods);
     341             : 
     342          38 :         if (p2p->state == P2P_PD_DURING_FIND) {
     343           3 :                 p2p_clear_timeout(p2p);
     344           3 :                 p2p_continue_find(p2p);
     345             :         }
     346             : }
     347             : 
     348             : 
     349         165 : int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
     350             :                            int join, int force_freq)
     351             : {
     352             :         struct wpabuf *req;
     353             :         int freq;
     354             : 
     355         165 :         if (force_freq > 0)
     356          33 :                 freq = force_freq;
     357             :         else
     358         132 :                 freq = dev->listen_freq > 0 ? dev->listen_freq :
     359             :                         dev->oper_freq;
     360         165 :         if (freq <= 0) {
     361           6 :                 p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
     362             :                         MACSTR " to send Provision Discovery Request",
     363           6 :                         MAC2STR(dev->info.p2p_device_addr));
     364           1 :                 return -1;
     365             :         }
     366             : 
     367         164 :         if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
     368           0 :                 if (!(dev->info.dev_capab &
     369             :                       P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
     370           0 :                         p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
     371             :                                 " that is in a group and is not discoverable",
     372           0 :                                 MAC2STR(dev->info.p2p_device_addr));
     373           0 :                         return -1;
     374             :                 }
     375             :                 /* TODO: use device discoverability request through GO */
     376             :         }
     377             : 
     378         328 :         req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
     379         164 :                                       dev->req_config_methods,
     380             :                                       join ? dev : NULL);
     381         164 :         if (req == NULL)
     382           0 :                 return -1;
     383             : 
     384         164 :         if (p2p->state != P2P_IDLE)
     385           7 :                 p2p_stop_listen_for_freq(p2p, freq);
     386         164 :         p2p->pending_action_state = P2P_PENDING_PD;
     387         328 :         if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
     388         164 :                             p2p->cfg->dev_addr, dev->info.p2p_device_addr,
     389         164 :                             wpabuf_head(req), wpabuf_len(req), 200) < 0) {
     390           0 :                 p2p_dbg(p2p, "Failed to send Action frame");
     391           0 :                 wpabuf_free(req);
     392           0 :                 return -1;
     393             :         }
     394             : 
     395         164 :         os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
     396             : 
     397         164 :         wpabuf_free(req);
     398         164 :         return 0;
     399             : }
     400             : 
     401             : 
     402          43 : int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
     403             :                       u16 config_methods, int join, int force_freq,
     404             :                       int user_initiated_pd)
     405             : {
     406             :         struct p2p_device *dev;
     407             : 
     408          43 :         dev = p2p_get_device(p2p, peer_addr);
     409          43 :         if (dev == NULL)
     410           1 :                 dev = p2p_get_device_interface(p2p, peer_addr);
     411          43 :         if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
     412           6 :                 p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
     413           6 :                         " not yet known", MAC2STR(peer_addr));
     414           1 :                 return -1;
     415             :         }
     416             : 
     417         294 :         p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
     418             :                 " (config methods 0x%x)",
     419         252 :                 MAC2STR(peer_addr), config_methods);
     420          42 :         if (config_methods == 0)
     421           0 :                 return -1;
     422             : 
     423             :         /* Reset provisioning info */
     424          42 :         dev->wps_prov_info = 0;
     425             : 
     426          42 :         dev->req_config_methods = config_methods;
     427          42 :         if (join)
     428          33 :                 dev->flags |= P2P_DEV_PD_FOR_JOIN;
     429             :         else
     430           9 :                 dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
     431             : 
     432          44 :         if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
     433           2 :             p2p->state != P2P_LISTEN_ONLY) {
     434           0 :                 p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
     435             :                         MACSTR " (config methods 0x%x)",
     436           0 :                         MAC2STR(peer_addr), config_methods);
     437           0 :                 return 0;
     438             :         }
     439             : 
     440          42 :         p2p->user_initiated_pd = user_initiated_pd;
     441          42 :         p2p->pd_force_freq = force_freq;
     442             : 
     443          42 :         if (p2p->user_initiated_pd)
     444          41 :                 p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
     445             : 
     446             :         /*
     447             :          * Assign dialog token here to use the same value in each retry within
     448             :          * the same PD exchange.
     449             :          */
     450          42 :         dev->dialog_token++;
     451          42 :         if (dev->dialog_token == 0)
     452           0 :                 dev->dialog_token = 1;
     453             : 
     454          42 :         return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
     455             : }
     456             : 
     457             : 
     458           1 : void p2p_reset_pending_pd(struct p2p_data *p2p)
     459             : {
     460             :         struct p2p_device *dev;
     461             : 
     462           2 :         dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
     463           1 :                 if (os_memcmp(p2p->pending_pd_devaddr,
     464             :                               dev->info.p2p_device_addr, ETH_ALEN))
     465           0 :                         continue;
     466           1 :                 if (!dev->req_config_methods)
     467           0 :                         continue;
     468           1 :                 if (dev->flags & P2P_DEV_PD_FOR_JOIN)
     469           0 :                         continue;
     470             :                 /* Reset the config methods of the device */
     471           1 :                 dev->req_config_methods = 0;
     472             :         }
     473             : 
     474           1 :         p2p->user_initiated_pd = 0;
     475           1 :         os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
     476           1 :         p2p->pd_retries = 0;
     477           1 :         p2p->pd_force_freq = 0;
     478           1 : }

Generated by: LCOV version 1.10