[PATCH 21/44] FT: cache newly detected R0KH / R1KH when using wildcard

michael-dev at fami-braun.de michael-dev at fami-braun.de
Wed Feb 24 03:53:27 PST 2016


From: Michael Braun <michael-dev at fami-braun.de>

This implements temporary positive caching for R0KH and R1KH seen when
using wilcard r0kh/r1kh configuration. Cache entries expire after 1d.

In order to free newly created stations later, the r*kh_list start pointer
in conf needs to be updateable from wpa_auto_ft.c, where only wconf is
accessed.

Signed-off-by: Michael Braun <michael-dev at fami-braun.de>
---
 hostapd/config_file.c  |   2 +
 hostapd/hostapd.conf   |  11 +++-
 src/ap/ap_config.c     |   1 +
 src/ap/ap_config.h     |   1 +
 src/ap/wpa_auth.c      |   1 +
 src/ap/wpa_auth.h      |   6 +-
 src/ap/wpa_auth_ft.c   | 155 ++++++++++++++++++++++++++++++++++++++++++++++---
 src/ap/wpa_auth_glue.c |   5 +-
 8 files changed, 169 insertions(+), 13 deletions(-)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 6d0d4d2..3d22a86 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2543,6 +2543,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->r0_key_lifetime = atoi(pos);
 	} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
 		bss->reassociation_deadline = atoi(pos);
+	} else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
+		bss->rkh_pos_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "r0kh") == 0) {
 		if (add_r0kh(bss, pos) < 0) {
 			wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 7508e03..da64428 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1302,7 +1302,9 @@ own_ip_addr=127.0.0.1
 #r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
 #r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
 # And so on.. One line per R0KH.
-# Wildcard entry:
+# Wildcard entry: Upon receiving a request from an R1KH not yet known,
+#                 it will be added to this list and thus receive push
+#                 notifications.
 #r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
 
 # List of R1KHs in the same Mobility Domain
@@ -1313,9 +1315,14 @@ own_ip_addr=127.0.0.1
 #r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
 #r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
 # And so on.. One line per R1KH.
-# Wildcard entry:
+# Wildcard entry: Upon receiving a response from R0KH, it will be added to this
+#                 list, so subsequent requests won't be broadcasted.
 #r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
 
+# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
+# Special values: 0 -> do not cache; -1 -> do not expire
+#rkh_pos_timeout = 86400 (default = 1d)
+
 # Whether PMK-R1 push is enabled at R0KH
 # 0 = do not push PMK-R1 to all configured R1KHs (default)
 # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 7890c07..13aa202 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -93,6 +93,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 #ifdef CONFIG_IEEE80211R
 	bss->ft_over_ds = 1;
 	bss->r0_key_lifetime = 60; /* same as eap_reauth_period */
+	bss->rkh_pos_timeout = 86400;
 #endif /* CONFIG_IEEE80211R */
 
 	bss->radius_das_time_window = 300;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 92266c0..e8503f6 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -325,6 +325,7 @@ struct hostapd_bss_config {
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
 	u32 reassociation_deadline;
 	struct ft_remote_r0kh *r0kh_list;
 	struct ft_remote_r1kh *r1kh_list;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 4ded9bb..8b05790 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -516,6 +516,7 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
 #ifdef CONFIG_IEEE80211R
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
+	wpa_ft_deinit(wpa_auth);
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_P2P
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 40838c3..01ed200 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -182,9 +182,10 @@ struct wpa_auth_config {
 	size_t r0_key_holder_len;
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
 	u32 r0_key_lifetime;
+	int rkh_pos_timeout;
 	u32 reassociation_deadline;
-	struct ft_remote_r0kh *r0kh_list;
-	struct ft_remote_r1kh *r1kh_list;
+	struct ft_remote_r0kh **r0kh_list;
+	struct ft_remote_r1kh **r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
 	int ft_psk_generate_local;
@@ -343,6 +344,7 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len);
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
 #endif /* CONFIG_IEEE80211R */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 816f6af..e116496 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -432,6 +432,113 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 }
 
 
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return;
+	r0kh = *wpa_auth->conf.r0kh_list;
+	while (r0kh) {
+		if (r0kh != timeout_ctx) {
+			r0kh = r0kh->next;
+			continue;
+		}
+		if (prev)
+			prev->next = r0kh->next;
+		else
+			*wpa_auth->conf.r0kh_list = r0kh->next;
+		os_free(r0kh);
+		break;
+	}
+}
+
+
+static void wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+				struct ft_remote_r0kh *r0kh_wildcard,
+				const u8 *src_addr,
+				u8 *r0kh_id, size_t id_len, int timeout)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return;
+
+	r0kh = os_zalloc(sizeof(*r0kh));
+	if (r0kh == NULL)
+		return;
+
+	os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+	os_memcpy(r0kh->id, r0kh_id, sizeof(r0kh->id));
+	r0kh->id_len = id_len;
+	if (r0kh_wildcard)
+		os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+	r0kh->next = *wpa_auth->conf.r0kh_list;
+	*wpa_auth->conf.r0kh_list = r0kh;
+	timeout = wpa_auth->conf.rkh_pos_timeout;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+	r1kh = *wpa_auth->conf.r1kh_list;
+	while (r1kh) {
+		if (r1kh != timeout_ctx) {
+			r1kh = r1kh->next;
+			continue;
+		}
+		if (prev)
+			prev->next = r1kh->next;
+		else
+			*wpa_auth->conf.r1kh_list = r1kh->next;
+		os_free(r1kh);
+		break;
+	}
+}
+
+
+static void wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+				struct ft_remote_r1kh *r1kh_wildcard,
+				const u8 *src_addr, u8 *r1kh_id, int timeout)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+
+	r1kh = os_zalloc(sizeof(*r1kh));
+	if (r1kh == NULL)
+		return;
+
+	os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+	os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+	os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+	r1kh->next = *wpa_auth->conf.r1kh_list;
+	*wpa_auth->conf.r1kh_list = r1kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+				       wpa_auth, r1kh);
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, ELOOP_ALL_CTX);
+	eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, ELOOP_ALL_CTX);
+}
+
+
 static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 			      const u8 *ies, size_t ies_len,
 			      const u8 *pmk_r0_name)
@@ -439,7 +546,9 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 	struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL;
 	struct ft_r0kh_r1kh_pull_frame frame, f;
 
-	r0kh = sm->wpa_auth->conf.r0kh_list;
+	if (!sm->wpa_auth->conf.r0kh_list)
+		return -1;
+	r0kh = *sm->wpa_auth->conf.r0kh_list;
 	while (r0kh) {
 		if (r0kh->id_len == sm->r0kh_id_len &&
 		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
@@ -1628,7 +1737,9 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 	if (data_len < sizeof(f))
 		return -1;
 
-	r1kh = wpa_auth->conf.r1kh_list;
+	if (!wpa_auth->conf.r1kh_list)
+		return -1;
+	r1kh = *wpa_auth->conf.r1kh_list;
 	while (r1kh) {
 		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
 			break;
@@ -1673,6 +1784,10 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
 		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
 
+	if (r1kh == r1kh_wildcard && wpa_auth->conf.rkh_pos_timeout)
+		wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+				    f.r1kh_id, wpa_auth->conf.rkh_pos_timeout);
+
 	os_memset(&resp, 0, sizeof(resp));
 	resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
 	resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
@@ -1742,9 +1857,16 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+struct ft_pull_resp_cb_ctx {
+	struct ft_r0kh_r1kh_resp_frame *frame;
+	struct ft_remote_r0kh *r0kh_wildcard;
+	u8 src_addr[ETH_ALEN];
+};
+
 static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
 {
-	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+	struct ft_pull_resp_cb_ctx *info = ctx;
+	struct ft_r0kh_r1kh_resp_frame *frame = info->frame;
 
 	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
 		return 0;
@@ -1757,6 +1879,13 @@ static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
 	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
 		   MACSTR " - process from timeout", MAC2STR(sm->addr));
 	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+
+	if (info->r0kh_wildcard && sm->wpa_auth->conf.rkh_pos_timeout)
+		wpa_ft_rrb_add_r0kh(sm->wpa_auth, info->r0kh_wildcard,
+				    info->src_addr, sm->r0kh_id,
+				    sm->r0kh_id_len,
+				    sm->wpa_auth->conf.rkh_pos_timeout);
+
 	return 1;
 }
 
@@ -1771,6 +1900,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 	struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL;
 	int pairwise, res;
 	int expiresIn;
+	struct ft_pull_resp_cb_ctx ctx;
 	int maxExpiresIn = wpa_auth->conf.r0_key_lifetime * 60;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
@@ -1778,7 +1908,9 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 	if (data_len < sizeof(f))
 		return -1;
 
-	r0kh = wpa_auth->conf.r0kh_list;
+	if (!wpa_auth->conf.r0kh_list)
+		return -1;
+	r0kh = *wpa_auth->conf.r0kh_list;
 	while (r0kh) {
 		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
 			break;
@@ -1837,7 +1969,12 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
 				  pairwise, f.vlan, expiresIn);
 	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
-	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
+
+	ctx.frame = &f;
+	os_memcpy(ctx.src_addr, src_addr, ETH_ALEN);
+	ctx.r0kh_wildcard = (r0kh == r0kh_wildcard) ? r0kh_wildcard : NULL;
+
+	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &ctx);
 	os_memset(f.pmk_r1, 0, PMK_LEN);
 
 	return res ? 0 : -1;
@@ -1863,7 +2000,9 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 	if (data_len < sizeof(f))
 		return -1;
 
-	r0kh = wpa_auth->conf.r0kh_list;
+	if (!wpa_auth->conf.r0kh_list)
+		return -1;
+	r0kh = *wpa_auth->conf.r0kh_list;
 	while (r0kh) {
 		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
 			break;
@@ -2120,6 +2259,8 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
 
 	if (!wpa_auth->conf.pmk_r1_push)
 		return;
+	if (!wpa_auth->conf.r1kh_list)
+		return;
 
 	dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
 		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
@@ -2138,7 +2279,7 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
 	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
 		   "for STA " MACSTR, MAC2STR(addr));
 
-	for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
 		if (is_zero_ether_addr(r1kh->addr) ||
 		    is_zero_ether_addr(r1kh->id))
 			continue;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 3736d4a..0fc4996 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -77,8 +77,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
 	wconf->r0_key_lifetime = conf->r0_key_lifetime;
 	wconf->reassociation_deadline = conf->reassociation_deadline;
-	wconf->r0kh_list = conf->r0kh_list;
-	wconf->r1kh_list = conf->r1kh_list;
+	wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+	wconf->r0kh_list = &conf->r0kh_list;
+	wconf->r1kh_list = &conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
 	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
-- 
1.9.1




More information about the Hostap mailing list