Merge "bandsteering:  Robustness to STAs which re-join the network."
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 380167e..82fd7c4 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -549,7 +549,7 @@
 	enum ssid_match_result res;
 
 	if (request_logging_path) {
-		maybe_write_timestamp_file(mgmt, hapd, LOG_PROBE);
+		maybe_write_timestamp_file(mgmt->sa, hapd, LOG_PROBE);
 	}
 
 	ie = mgmt->u.probe_req.variable;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 7289598..9140f3e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1586,7 +1586,7 @@
 	}
 
 	if (request_logging_path) {
-		maybe_write_timestamp_file(mgmt, hapd, LOG_ASSOC);
+		maybe_write_timestamp_file(mgmt->sa, hapd, LOG_ASSOC);
 	}
 
 
@@ -1641,9 +1641,18 @@
 	sta = ap_get_sta(hapd, mgmt->sa);
 
 	if (steering_timestamp_path) {
+		/* If the STA has successfully associated on the bandsteering target
+		 * interface, reset the bandsteering state so that we try to bandsteer it
+		 * again.
+		 */
+		if (read_timestamp_file(mgmt->sa, LOG_ASSOC_SUCCESSFUL, STEERING_PATH, NULL)) {
+			delete_timestamp_file(mgmt->sa, LOG_ASSOC_SUCCESSFUL, STEERING_PATH);
+			delete_timestamp_file(mgmt->sa, LOG_ASSOC, LOGGING_PATH);
+		}
+
 		os_get_reltime(&now);
-		if (read_timestamp_file(mgmt, LOG_PROBE, STEERING_PATH, &probe_time)) {
-			if (!read_timestamp_file(mgmt, LOG_ASSOC, LOGGING_PATH,
+		if (read_timestamp_file(mgmt->sa, LOG_PROBE, STEERING_PATH, &probe_time)) {
+			if (!read_timestamp_file(mgmt->sa, LOG_ASSOC, LOGGING_PATH,
 			                         &bandsteer_until) ||
 			    os_reltime_before(&now, &bandsteer_until)) {
 				wpa_printf(MSG_INFO, "Rejecting " MACSTR " until %d sec %d usec",
@@ -1652,7 +1661,7 @@
 				resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 				goto fail;
 			} else {
-				write_timestamp_file(mgmt, hapd, LOG_BANDSTEERING_FAILED, now);
+				write_timestamp_file(mgmt->sa, hapd, LOG_BANDSTEERING_FAILED, &now);
 			}
 		}
 	}
@@ -2324,6 +2333,13 @@
 		ap_sta_set_authorized(hapd, sta, 1);
 	}
 
+
+	if (request_logging_path && !steering_timestamp_path) {
+		/* Log the successful associate, so that the interface steering to this one
+		 * knows it can keep bandsteering if it sees another assoc request. */
+		write_timestamp_file(mgmt->da, hapd, LOG_ASSOC_SUCCESSFUL, NULL);
+	}
+
 	if (reassoc)
 		mlme_reassociate_indication(hapd, sta);
 	else
diff --git a/src/ap/steering.c b/src/ap/steering.c
index f7a7f99..2916262 100644
--- a/src/ap/steering.c
+++ b/src/ap/steering.c
@@ -17,7 +17,7 @@
 #include "hostapd.h"
 #include "steering.h"
 
-static int get_timestamp_filename(const struct ieee80211_mgmt *mgmt,
+static int get_timestamp_filename(const u8 *mac,
                                   logged_request_type type,
                                   steering_path_type path_type, char *buf,
                                   size_t len) {
@@ -29,7 +29,7 @@
 	}
 
 	if (os_snprintf(buf, len, "%s/" COMPACT_MACSTR ".%d", path,
-	                MAC2STR(mgmt->sa), type) < 0) {
+	                MAC2STR(mac), type) < 0) {
 		wpa_printf(MSG_ERROR, "os_snprintf couldn't format filename: %s",
 		           strerror(errno));
 		return 0;
@@ -38,15 +38,15 @@
 	return 1;
 }
 
-int write_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int write_timestamp_file(const u8 *mac,
                          const struct hostapd_data *hapd,
                          logged_request_type type,
-                         const struct os_reltime timestamp) {
+                         const struct os_reltime *timestamp) {
 	FILE *f;
 	char filename[1024], tmp_filename[1024];
 	int success = 0;
 
-	if (!get_timestamp_filename(mgmt, type, LOGGING_PATH, filename,
+	if (!get_timestamp_filename(mac, type, LOGGING_PATH, filename,
 	                            sizeof(filename))) {
 		return 0;
 	}
@@ -67,10 +67,12 @@
 		return 0;
 	}
 
-	if (fprintf(f, "%d %d", timestamp.sec, timestamp.usec) < 0) {
-		wpa_printf(MSG_ERROR, "fprintf to %s: %s", tmp_filename, strerror(errno));
-	} else {
-		success = 1;
+	if (timestamp) {
+		if (fprintf(f, "%d %d", timestamp->sec, timestamp->usec) < 0) {
+			wpa_printf(MSG_ERROR, "fprintf to %s: %s", tmp_filename, strerror(errno));
+		} else {
+			success = 1;
+		}
 	}
 
 	if (fclose(f) == EOF) {
@@ -87,7 +89,7 @@
 	return success;
 }
 
-int maybe_write_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int maybe_write_timestamp_file(const u8 *mac,
                                const struct hostapd_data *hapd,
                                logged_request_type type) {
 	struct os_reltime now, prev_logged_timestamp, new_timestamp;
@@ -102,33 +104,33 @@
 		           strerror(errno));
 		return 0;
 	}
-	if (!read_timestamp_file(mgmt, type, LOGGING_PATH, &prev_logged_timestamp) ||
+	if (!read_timestamp_file(mac, type, LOGGING_PATH, &prev_logged_timestamp) ||
 	    os_reltime_expired(&now, &prev_logged_timestamp,
 	                       BANDSTEERING_EXPIRATION_SECONDS)) {
 		new_timestamp.sec = now.sec + BANDSTEERING_DELAY_SECONDS;
 		new_timestamp.usec = now.usec;
-		if (!write_timestamp_file(mgmt, hapd, type, new_timestamp)) {
+		if (!write_timestamp_file(mac, hapd, type, &new_timestamp)) {
 			wpa_printf(MSG_ERROR, "Failed to write timestamp file.");
 			return 0;
 		} else {
 			wpa_printf(MSG_INFO, "Set timestamp for " MACSTR " (type=%d)",
-			           MAC2STR(mgmt->sa), type);
+			           MAC2STR(mac), type);
 			return 1;
 		}
 	}
 }
 
-int read_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int read_timestamp_file(const u8 *mac,
                         logged_request_type type,
                         steering_path_type path_type,
                         struct os_reltime *timestamp) {
 	FILE *f;
 	char filename[1024];
-	int success = 0;
+	int success = 1;
 	struct stat st;
 	os_time_t sec = 0, usec = 0;
 
-	if (!get_timestamp_filename(mgmt, type, path_type, filename,
+	if (!get_timestamp_filename(mac, type, path_type, filename,
 	                            sizeof(filename))) {
 		return 0;
 	}
@@ -143,10 +145,11 @@
 		return 0;
 	}
 
-	if (fscanf(f, "%d %d", &timestamp->sec, &timestamp->usec) != 2) {
-		wpa_printf(MSG_ERROR, "fscanf from %s: %s", filename, strerror(errno));
-	} else {
-		success = 1;
+	if (timestamp) {
+		if (fscanf(f, "%d %d", &timestamp->sec, &timestamp->usec) != 2) {
+			wpa_printf(MSG_ERROR, "fscanf from %s: %s", filename, strerror(errno));
+			success = 0;
+		}
 	}
 
 	if (fclose(f) == EOF) {
@@ -157,6 +160,29 @@
 	return success;
 }
 
+int delete_timestamp_file(const u8 *mac,
+                          logged_request_type type,
+                          steering_path_type path_type) {
+	char filename[1024];
+	struct stat st;
+
+	if (!get_timestamp_filename(mac, type, path_type, filename,
+	                            sizeof(filename))) {
+		return 0;
+	}
+
+	if (stat(filename, &st) == -1) {
+		return 1;
+	}
+
+	if (unlink(filename) == -1) {
+		wpa_printf(MSG_ERROR, "unlink(%s): %s", filename, strerror(errno));
+		return 0;
+	}
+
+	return 1;
+}
+
 int file_ctime_lt(const struct dirent **a, const struct dirent **b) {
 	struct stat astat, bstat;
 
@@ -204,8 +230,7 @@
 			filename = namelist[i]->d_name;
 			if (filename[0] != '.' && !error) {
 				if (unlink(filename) == -1) {
-					wpa_printf(MSG_ERROR, "unlink(%s): %s",
-					           unlink(filename), strerror(errno));
+					wpa_printf(MSG_ERROR, "unlink(%s): %s", filename, strerror(errno));
 					error = 1;
 				} else {
 					++num_timestamp_files_deleted;
diff --git a/src/ap/steering.h b/src/ap/steering.h
index 98b0c40..a17d79c 100644
--- a/src/ap/steering.h
+++ b/src/ap/steering.h
@@ -28,6 +28,7 @@
   LOG_PROBE,
   LOG_ASSOC,
   LOG_BANDSTEERING_FAILED,
+  LOG_ASSOC_SUCCESSFUL,
   NUM_LOGGED_REQUEST_TYPES } logged_request_type;
 typedef enum {
   LOGGING_PATH,
@@ -38,17 +39,17 @@
  * Writes timestamp for the source address in mgmt to request_logging_path.
  * Returns 1 if the write succeeded, 0 otherwise.
  */
-int write_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int write_timestamp_file(const u8 *mac,
                          const struct hostapd_data *hapd,
                          logged_request_type type,
-                         const struct os_reltime timestamp);
+                         const struct os_reltime *timestamp);
 
 /**
  * Calls write_timestamp_file unless there is an existing file younger than
  * BANDSTEERING_EXPIRATION_SECONDS. Also garbage collects before writing.
  * Returns 0 on write or garbage collection failure, 1 otherwise.
  */
-int maybe_write_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int maybe_write_timestamp_file(const u8 *mac,
                                const struct hostapd_data *hapd,
                                logged_request_type type);
 
@@ -57,12 +58,21 @@
  * (based on path) for the source address in mgmt, putting the result in
  * timestamp.  Returns 1 if the read succeeded, 0 otherwise.
  */
-int read_timestamp_file(const struct ieee80211_mgmt *mgmt,
+int read_timestamp_file(const u8 *mac,
                         logged_request_type type,
                         steering_path_type path_type,
                         struct os_reltime *timestamp);
 
 /**
+ * Deletes a timestamp file from either request_logging_path or
+ * steering_timestamp_path (based on path) for the source address in mgmt.
+ * Returns 1 if the delete succeeded or the file does not exist, 0 otherwise.
+ */
+int delete_timestamp_file(const u8 *mac,
+                          logged_request_type type,
+                          steering_path_type path_type);
+
+/**
  * Delete all but the most recent MAX_TIMESTAMP_FILES files of type LOG_PROBE in
  * request_logging_path.  Returns the number of files deleted.
  */