Add a driver op for enabling Probe Request reporting in station mode
authorJouni Malinen <jouni.malinen@atheros.com>
Sun, 3 Jan 2010 11:30:22 +0000 (13:30 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 3 Jan 2010 11:30:22 +0000 (13:30 +0200)
src/drivers/driver.h
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c
src/drivers/driver_test.c
wpa_supplicant/driver_i.h

index 51c3356..1dd358a 100644 (file)
@@ -1518,6 +1518,22 @@ struct wpa_driver_ops {
         * Returns: 0 on success, -1 on failure
         */
        int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
+
+       /**
+        * probe_req_report - Request Probe Request frames to be indicated
+        * @priv: Private driver interface data
+        * @report: Whether to report received Probe Request frames
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This command can be used to request the driver to indicate when
+        * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
+        * Since this operation may require extra resources, e.g., due to less
+        * optimal hardware/firmware RX filtering, many drivers may disable
+        * Probe Request reporting at least in station mode. This command is
+        * used to notify the driver when the Probe Request frames need to be
+        * reported, e.g., during remain-on-channel operations.
+        */
+       int (*probe_req_report)(void *priv, int report);
 };
 
 /**
@@ -1732,7 +1748,11 @@ enum wpa_event_type {
         *
         * This event is used to indicate when a Probe Request frame has been
         * received. Information about the received frame is included in
-        * union wpa_event_data::rx_probe_req.
+        * union wpa_event_data::rx_probe_req. The driver is required to report
+        * these events only after successfully completed probe_req_report()
+        * commands to request the events (i.e., report parameter is non-zero)
+        * in station mode. In AP mode, Probe Request frames should always be
+        * reported.
         */
        EVENT_RX_PROBE_REQ
 };
index f3dc1cf..d3e56cf 100644 (file)
@@ -3245,5 +3245,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
        NULL /* set_ht_params */,
        NULL /* set_ap_wps_ie */,
        NULL /* set_supp_port */,
-       NULL /* set_wds_sta */
+       NULL /* set_wds_sta */,
+       NULL /* probe_req_report */
 };
index f4e45e7..b669c87 100644 (file)
@@ -95,6 +95,7 @@ struct wpa_driver_nl80211_data {
 
        int monitor_sock;
        int monitor_ifidx;
+       int probe_req_report;
 
        unsigned int beacon_set:1;
 
@@ -134,6 +135,9 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                                        const char *ifname);
 #endif /* HOSTAPD */
 
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -1292,10 +1296,6 @@ static void wpa_driver_nl80211_deinit(void *priv)
        struct wpa_driver_nl80211_data *drv = priv;
 
        nl80211_remove_monitor_interface(drv);
-       if (drv->monitor_sock >= 0) {
-               eloop_unregister_read_sock(drv->monitor_sock);
-               close(drv->monitor_sock);
-       }
 
        if (drv->nlmode == NL80211_IFTYPE_AP)
                wpa_driver_nl80211_del_beacon(drv);
@@ -1339,6 +1339,9 @@ static void wpa_driver_nl80211_deinit(void *priv)
        nl_handle_destroy(drv->nl_handle_event);
        nl_cb_put(drv->nl_cb);
 
+       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+                            drv, NULL);
+
        os_free(drv);
 }
 
@@ -2772,6 +2775,12 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
+       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+                          "frame since Probe Request reporting is disabled");
+               return;
+       }
+
        if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
                printf("received invalid radiotap frame\n");
                return;
@@ -2975,6 +2984,11 @@ static void nl80211_remove_monitor_interface(
                nl80211_remove_iface(drv, drv->monitor_ifidx);
                drv->monitor_ifidx = -1;
        }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
 }
 
 
@@ -4403,6 +4417,62 @@ static int wpa_driver_nl80211_if_remove(void *priv,
 }
 
 
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->monitor_ifidx < 0)
+               return; /* monitor interface already removed */
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               return; /* not in station mode anymore */
+
+       if (drv->probe_req_report)
+               return; /* reporting enabled */
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+                  "Probe Request reporting needed anymore");
+       nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in station mode (iftype=%d)",
+                          drv->nlmode);
+               return -1;
+       }
+       drv->probe_req_report = report;
+
+       if (report) {
+               eloop_cancel_timeout(
+                       wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+               if (drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv))
+                       return -1;
+       } else {
+               /*
+                * It takes a while to remove the monitor interface, so try to
+                * avoid doing this if it is needed again shortly. Instead,
+                * schedule the interface to be removed later if no need for it
+                * is seen.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+                          "to be removed after 10 seconds of no use");
+               eloop_register_timeout(
+                       10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+       }
+
+       return 0;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -4451,4 +4521,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_sta_vlan = i802_set_sta_vlan,
        .set_wds_sta = i802_set_wds_sta,
 #endif /* HOSTAPD */
+       .probe_req_report = wpa_driver_nl80211_probe_req_report,
 };
index 493821a..9cdeac1 100644 (file)
@@ -100,6 +100,8 @@ struct wpa_driver_test_data {
        struct test_client_socket *cli;
        struct test_driver_bss *bss;
        int udp_port;
+
+       int probe_req_report;
 };
 
 
@@ -1779,6 +1781,24 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
        event.mlme_rx.buf = data;
        event.mlme_rx.len = data_len;
        wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
+
+       if (drv->probe_req_report && data_len >= 24) {
+               const struct ieee80211_mgmt *mgmt;
+               u16 fc;
+
+               mgmt = (const struct ieee80211_mgmt *) data;
+               fc = le_to_host16(mgmt->frame_control);
+               if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
+                       os_memset(&event, 0, sizeof(event));
+                       event.rx_probe_req.sa = mgmt->sa;
+                       event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+                       event.rx_probe_req.ie_len =
+                               data_len - (mgmt->u.probe_req.variable - data);
+                       wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
+                                            &event);
+               }
+       }
 }
 
 
@@ -2442,6 +2462,15 @@ fail:
 }
 
 
+static int wpa_driver_test_probe_req_report(void *priv, int report)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report);
+       drv->probe_req_report = report;
+       return 0;
+}
+
+
 const struct wpa_driver_ops wpa_driver_test_ops = {
        "test",
        "wpa_supplicant test driver",
@@ -2485,4 +2514,5 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        .init2 = wpa_driver_test_init2,
        .get_interfaces = wpa_driver_test_get_interfaces,
        .scan2 = wpa_driver_test_scan,
+       .probe_req_report = wpa_driver_test_probe_req_report,
 };
index 7e4af80..9dbbc85 100644 (file)
@@ -383,4 +383,13 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
        return 0;
 }
 
+static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
+                                          int report)
+{
+       if (wpa_s->driver->probe_req_report)
+               return wpa_s->driver->probe_req_report(wpa_s->drv_priv,
+                                                      report);
+       return -1;
+}
+
 #endif /* DRIVER_I_H */