]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Add mechanism to track unconnected stations
authorJouni Malinen <j@w1.fi>
Sat, 5 Sep 2015 14:11:11 +0000 (17:11 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 5 Sep 2015 14:11:11 +0000 (17:11 +0300)
hostapd can now be configured to track unconnected stations based on
Probe Request frames seen from them. This can be used, e.g., to detect
dualband capable station before they have associated. Such information
could then be used to provide guidance on which colocated BSS to use in
case of a dualband AP that operates concurrently on multiple bands under
the control of a single hostapd process.

Signed-off-by: Jouni Malinen <j@w1.fi>
hostapd/config_file.c
hostapd/ctrl_iface.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/beacon.c
src/ap/beacon.h
src/ap/hostapd.c
src/ap/hostapd.h

index e91c86c039bd4057b00c470f2e268279fd52cb1e..336eba99c324fee084f09cf44ccf51205bd83e1f 100644 (file)
@@ -3399,6 +3399,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                }
                conf->fst_cfg.llt = (u32) val;
 #endif /* CONFIG_FST */
+       } else if (os_strcmp(buf, "track_sta_max_num") == 0) {
+               conf->track_sta_max_num = atoi(pos);
+       } else if (os_strcmp(buf, "track_sta_max_age") == 0) {
+               conf->track_sta_max_age = atoi(pos);
        } else {
                wpa_printf(MSG_ERROR,
                           "Line %d: unknown configuration item '%s'",
index 306c53ca3b987c397e1242d564b2adcfbbd11a6b..cb6fb1757708237bedc9e785684c7f623e1807ff 100644 (file)
@@ -2007,6 +2007,39 @@ static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
 }
 
 
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
+                                            char *buf, size_t buflen)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       char *pos, *end;
+       struct hostapd_sta_info *info;
+       struct os_reltime now;
+
+       sta_track_expire(iface, 0);
+
+       pos = buf;
+       end = buf + buflen;
+
+       os_get_reltime(&now);
+       dl_list_for_each_reverse(info, &iface->sta_seen,
+                                struct hostapd_sta_info, list) {
+               struct os_reltime age;
+               int ret;
+
+               os_reltime_sub(&now, &info->last_seen, &age);
+               ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+                                 MAC2STR(info->addr), (unsigned int) age.sec);
+               if (os_snprintf_error(end - pos, ret))
+                       break;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+#endif /* NEED_AP_MLME */
+
+
 static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
                                              char *buf, char *reply,
                                              int reply_size,
@@ -2238,6 +2271,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
        } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
                reply_len = hostapd_ctrl_iface_log_level(
                        hapd, buf + 9, reply, reply_size);
+#ifdef NEED_AP_MLME
+       } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
+               reply_len = hostapd_ctrl_iface_track_sta_list(
+                       hapd, reply, reply_size);
+#endif /* NEED_AP_MLME */
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index 4a829ead401205398b487310bd29e7620db1997d..d9121973c80ab06677f4ffd648ca2db34065e1b7 100644 (file)
@@ -1279,6 +1279,15 @@ own_ip_addr=127.0.0.1
 # default: 60
 #ap_table_expiration_time=3600
 
+# Maximum number of stations to track on the operating channel
+# This can be used to detect dualband capable stations before they have
+# associated, e.g., to provide guidance on which colocated BSS to use.
+# Default: 0 (disabled)
+#track_sta_max_num=100
+
+# Maximum age of a station tracking entry in seconds
+# Default: 180
+#track_sta_max_age=180
 
 ##### Wi-Fi Protected Setup (WPS) #############################################
 
index 8727375490ccd614d021d7c0bb5158a33ecc6748..be2fa8ba6bfebdf385a8396ac8c92a3e08c66ff4 100644 (file)
@@ -172,6 +172,7 @@ struct hostapd_config * hostapd_config_defaults(void)
 
        conf->ap_table_max_size = 255;
        conf->ap_table_expiration_time = 60;
+       conf->track_sta_max_age = 180;
 
 #ifdef CONFIG_TESTING_OPTIONS
        conf->ignore_probe_probability = 0.0;
index c9a37643c14f797798bfa2551efd62f4a0186440..3434214244e829d0f1b2dd249d6faa5032af92b8 100644 (file)
@@ -586,6 +586,9 @@ struct hostapd_config {
        int ap_table_max_size;
        int ap_table_expiration_time;
 
+       unsigned int track_sta_max_num;
+       unsigned int track_sta_max_age;
+
        char country[3]; /* first two octets: country code as described in
                          * ISO/IEC 3166-1. Third octet:
                          * ' ' (ascii 32): all environments
index 98d6832949cadedc985012cebf2303b26b88e913..46fff82c7e2962be87e732a6ad29b6e77b31e4f3 100644 (file)
@@ -552,6 +552,76 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
 }
 
 
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+       struct os_reltime now;
+       struct hostapd_sta_info *info;
+
+       if (!iface->num_sta_seen)
+               return;
+
+       os_get_reltime(&now);
+       while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+                                    list))) {
+               if (!force &&
+                   !os_reltime_expired(&now, &info->last_seen,
+                                       iface->conf->track_sta_max_age))
+                       break;
+               force = 0;
+
+               wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+                          MACSTR, iface->bss[0]->conf->iface,
+                          MAC2STR(info->addr));
+               dl_list_del(&info->list);
+               iface->num_sta_seen--;
+               os_free(info);
+       }
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+                                              const u8 *addr)
+{
+       struct hostapd_sta_info *info;
+
+       dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+               if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+                       return info;
+
+       return NULL;
+}
+
+
+static void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+{
+       struct hostapd_sta_info *info;
+
+       info = sta_track_get(iface, addr);
+       if (info) {
+               /* Move the most recent entry to the end of the list */
+               dl_list_del(&info->list);
+               dl_list_add_tail(&iface->sta_seen, &info->list);
+               os_get_reltime(&info->last_seen);
+               return;
+       }
+
+       /* Add a new entry */
+       info = os_zalloc(sizeof(*info));
+       os_memcpy(info->addr, addr, ETH_ALEN);
+       os_get_reltime(&info->last_seen);
+
+       if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+               /* Expire oldest entry to make room for a new one */
+               sta_track_expire(iface, 1);
+       }
+
+       wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+                  MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+       dl_list_add_tail(&iface->sta_seen, &info->list);
+       iface->num_sta_seen++;
+}
+
+
 void handle_probe_req(struct hostapd_data *hapd,
                      const struct ieee80211_mgmt *mgmt, size_t len,
                      int ssi_signal)
@@ -567,6 +637,8 @@ void handle_probe_req(struct hostapd_data *hapd,
        ie = mgmt->u.probe_req.variable;
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
                return;
+       if (hapd->iconf->track_sta_max_num)
+               sta_track_add(hapd->iface, mgmt->sa);
        ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
 
        for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
index 722159a7500c661699e1fb1439e9152a23bbcaa3..21df9fae8f97478a09306fcfe8ca60f02b701bd3 100644 (file)
@@ -21,5 +21,6 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface);
 int ieee802_11_build_ap_params(struct hostapd_data *hapd,
                               struct wpa_driver_ap_params *params);
 void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+void sta_track_expire(struct hostapd_iface *iface, int force);
 
 #endif /* BEACON_H */
index cf8c93d51538b4358bd5ec261a1b3434d6289d98..e4d7bfc9b9209e17688e04cfbd09994ebf57cbb5 100644 (file)
@@ -355,6 +355,22 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
 }
 
 
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+       struct hostapd_sta_info *info;
+
+       if (!iface->num_sta_seen)
+               return;
+
+       while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+                                    list))) {
+               dl_list_del(&info->list);
+               iface->num_sta_seen--;
+               os_free(info);
+       }
+}
+
+
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
        wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
@@ -370,6 +386,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
        os_free(iface->basic_rates);
        iface->basic_rates = NULL;
        ap_list_deinit(iface);
+       sta_track_deinit(iface);
 }
 
 
@@ -1623,6 +1640,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
        hostapd_tx_queue_params(iface);
 
        ap_list_init(iface);
+       dl_list_init(&iface->sta_seen);
 
        hostapd_set_acl(hapd);
 
index 89a1e8b5d6c9c53faadbcded6faf98d018f850d7..dcf51f00f78d38e27ab2a1118710a697aeb999f0 100644 (file)
@@ -281,6 +281,12 @@ struct hostapd_data {
 };
 
 
+struct hostapd_sta_info {
+       struct dl_list list;
+       u8 addr[ETH_ALEN];
+       struct os_reltime last_seen;
+};
+
 /**
  * struct hostapd_iface - hostapd per-interface data structure
  */
@@ -409,6 +415,9 @@ struct hostapd_iface {
 
        void (*scan_cb)(struct hostapd_iface *iface);
        int num_ht40_scan_tries;
+
+       struct dl_list sta_seen; /* struct hostapd_sta_info */
+       unsigned int num_sta_seen;
 };
 
 /* hostapd.c */