diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 172f20ffa..9fceca65d 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -520,6 +520,7 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) #ifdef CONFIG_IEEE80211R_AP wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; + wpa_ft_deinit(wpa_auth); #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index b9cdcaca9..bab56191b 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -43,6 +43,8 @@ struct ft_rrb_frame { #define FT_PACKET_R0KH_R1KH_PULL 0x01 #define FT_PACKET_R0KH_R1KH_RESP 0x02 #define FT_PACKET_R0KH_R1KH_PUSH 0x03 +#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04 +#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05 /* packet layout * IEEE 802 extended OUI ethertype frame header @@ -61,6 +63,7 @@ struct ft_rrb_frame { #define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */ +#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */ #define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */ #define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */ @@ -81,26 +84,40 @@ struct ft_rrb_tlv { /* followed by data of length len */ } STRUCT_PACKED; +struct ft_rrb_seq { + le32 dom; + le32 seq; + le32 ts; +} STRUCT_PACKED; + /* session TLVs: * required: PMK_R1, PMK_R1_NAME, PAIRWISE * * pull frame TLVs: * auth: - * required: NONCE, R0KH_ID, R1KH_ID + * required: SEQ, NONCE, R0KH_ID, R1KH_ID * encrypted: * required: PMK_R0_NAME, S1KH_ID * * response frame TLVs: * auth: - * required: NONCE, R0KH_ID, R1KH_ID + * required: SEQ, NONCE, R0KH_ID, R1KH_ID * encrypted: * required: S1KH_ID, session TLVs * * push frame TLVs: * auth: - * required: TIMESTAMP, R0KH_ID, R1KH_ID + * required: SEQ, R0KH_ID, R1KH_ID * encrypted: * required: S1KH_ID, PMK_R0_NAME, session TLVs + * + * sequence number request frame TLVs: + * auth: + * required: R0KH_ID, R1KH_ID, NONCE + * + * sequence number response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID */ #ifdef _MSC_VER @@ -114,6 +131,7 @@ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; +struct ft_remote_seq; struct ft_remote_r0kh { @@ -122,6 +140,7 @@ struct ft_remote_r0kh { u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; u8 key[32]; + struct ft_remote_seq *seq; }; @@ -130,6 +149,7 @@ struct ft_remote_r1kh { u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; u8 key[32]; + struct ft_remote_seq *seq; }; @@ -349,6 +369,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *dst_addr, u8 oui_suffix, 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_AP */ 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 885b85528..9db9b26c6 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -26,10 +26,15 @@ #ifdef CONFIG_IEEE80211R_AP +const unsigned int ftRRBseqTimeout = 10; +const unsigned int ftRRBmaxQueueLen = 100; + + static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); struct tlv_list { u16 type; @@ -531,6 +536,301 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, } +/* A packet to be handled after seq response */ +struct ft_remote_item { + struct dl_list list; + + u8 nonce[FT_RRB_NONCE_LEN]; + struct os_reltime nonce_ts; + + u8 src_addr[ETH_ALEN]; + u8 *enc; + size_t enc_len; + u8 *auth; + size_t auth_len; + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer); +}; + + +static void wpa_ft_rrb_seq_free(struct ft_remote_item *item) +{ + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item); + dl_list_del(&item->list); + bin_clear_free(item->enc, item->enc_len); + os_free(item->auth); + os_free(item); +} + + +static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, int cb) +{ + struct ft_remote_item *item, *n; + + dl_list_for_each_safe(item, n, &rkh_seq->rx.queue, + struct ft_remote_item, list) { + if (cb && item->cb) + item->cb(wpa_auth, item->src_addr, item->enc, + item->enc_len, item->auth, item->auth_len, 1); + wpa_ft_rrb_seq_free(item); + } +} + + +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ft_remote_item *item = timeout_ctx; + + wpa_ft_rrb_seq_free(item); +} + + +static int +wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + const u8 *f_r1kh_id, const u8 *key, size_t key_len, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + struct ft_remote_item *item = NULL; + u8 *packet = NULL; + size_t packet_len; + struct tlv_list seq_req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = NULL /* to be filled: item->nonce */ }, + { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len, + .data = f_r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) { + wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long"); + goto err; + } + + item = os_zalloc(sizeof(*item)); + if (!item) + goto err; + + os_memcpy(item->src_addr, src_addr, ETH_ALEN); + item->cb = cb; + + if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes"); + goto err; + } + + if (os_get_reltime(&item->nonce_ts) < 0) + goto err; + + if (enc && enc_len > 0) { + item->enc = os_memdup(enc, enc_len); + item->enc_len = enc_len; + if (!item->enc) + goto err; + } + + if (auth && auth_len > 0) { + item->auth = os_memdup(auth, auth_len); + item->auth_len = auth_len; + if (!item->auth) + goto err; + } + + eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout, + wpa_auth, item); + + seq_req_auth[0].data = item->nonce; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + &packet, &packet_len) < 0) { + item = NULL; /* some other seq resp might still accept this */ + goto err; + } + + dl_list_add(&rkh_seq->rx.queue, &item->list); + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + packet, packet_len); + + os_free(packet); + + return 0; +err: + wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request"); + if (item) { + os_free(item->auth); + bin_clear_free(item->enc, item->enc_len); + os_free(item); + } + + return -1; +} + + +#define FT_RRB_SEQ_OK 0 +#define FT_RRB_SEQ_DROP 1 +#define FT_RRB_SEQ_DEFER 2 + +static int +wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, int no_defer) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, rkh_off; + struct os_reltime now; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len); + msg_both = (const struct ft_rrb_seq *) f_seq; + + if (rkh_seq->rx.num_last == 0) { + /* first packet from remote */ + goto defer; + } + + if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) { + /* remote might have rebooted */ + goto defer; + } + + if (os_get_reltime(&now) == 0) { + u32 msg_ts_now_remote, msg_ts_off; + struct os_reltime now_remote; + + os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote); + msg_ts_now_remote = now_remote.sec; + msg_ts_off = le_to_host32(msg_both->ts) - + (msg_ts_now_remote - ftRRBseqTimeout); + if (msg_ts_off > 2 * ftRRBseqTimeout) + goto defer; + } + + msg_seq = le_to_host32(msg_both->seq); + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + msg_off = msg_seq - rkh_off; + if (msg_off > 0xC0000000) + goto out; /* too old message, drop it */ + + if (msg_off <= 0x40000000) { + for (i = 0; i < rkh_seq->rx.num_last; i++) { + if (rkh_seq->rx.last[i] == msg_seq) + goto out; /* duplicate message, drop it */ + } + + return FT_RRB_SEQ_OK; + } + +defer: + if (no_defer) + goto out; + + wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from " + MACSTR, msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DEFER; +out: + wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR, + msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DROP; +} + + +static void +wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *auth, size_t auth_len, + const char *msgtype) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, min_off, rkh_off; + int minidx = 0; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_seq = le_to_host32(msg_both->seq); + + if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) { + rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq; + rkh_seq->rx.num_last++; + return; + } + + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + for (i = 0; i < rkh_seq->rx.num_last; i++) { + msg_off = rkh_seq->rx.last[i] - rkh_off; + min_off = rkh_seq->rx.last[minidx] - rkh_off; + if (msg_off < min_off && i != rkh_seq->rx.offsetidx) + minidx = i; + } + rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq; + rkh_seq->rx.offsetidx = minidx; + + return; +out: + /* RRB_GET_AUTH should never fail here as + * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */ + wpa_printf(MSG_ERROR, "FT: %s() failed", __func__); +} + + +static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq, + struct ft_rrb_seq *f_seq) +{ + struct os_reltime now; + + if (os_get_reltime(&now) < 0) + return -1; + + if (!rkh_seq->tx.dom) { + if (random_get_bytes((u8 *) &rkh_seq->tx.seq, + sizeof(rkh_seq->tx.seq))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.seq = now.usec; + } + if (random_get_bytes((u8 *) &rkh_seq->tx.dom, + sizeof(rkh_seq->tx.dom))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.dom = now.usec; + } + rkh_seq->tx.dom |= 1; + } + + f_seq->dom = host_to_le32(rkh_seq->tx.dom); + f_seq->seq = host_to_le32(rkh_seq->tx.seq); + f_seq->ts = host_to_le32(now.sec); + + rkh_seq->tx.seq++; + + return 0; +} + + struct wpa_ft_pmk_r0_sa { struct wpa_ft_pmk_r0_sa *next; u8 pmk_r0[PMK_LEN]; @@ -689,49 +989,85 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, } +static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) +{ + if (r0kh->seq) + return 0; + + r0kh->seq = os_zalloc(sizeof(*r0kh->seq)); + if (!r0kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq"); + return -1; + } + + dl_list_init(&r0kh->seq->rx.queue); + + return 0; +} + + static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, - const u8 *src_addr, const u8 *f_r0kh_id, - size_t f_r0kh_id_len, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, struct ft_remote_r0kh **r0kh_out) { struct ft_remote_r0kh *r0kh; + *r0kh_out = NULL; + for (r0kh = wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { - if (src_addr && os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) - continue; - if (f_r0kh_id && - (r0kh->id_len != f_r0kh_id_len || - os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) != 0)) - continue; - break; + if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && + os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) { + *r0kh_out = r0kh; + break; + } } - if (!r0kh) + if (!*r0kh_out) wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); - *r0kh_out = r0kh; + if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) + *r0kh_out = NULL; +} + + +static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) +{ + if (r1kh->seq) + return 0; + + r1kh->seq = os_zalloc(sizeof(*r1kh->seq)); + if (!r1kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq"); + return -1; + } + + dl_list_init(&r1kh->seq->rx.queue); + + return 0; } static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, - const u8 *src_addr, const u8 *f_r1kh_id, + const u8 *f_r1kh_id, struct ft_remote_r1kh **r1kh_out) { struct ft_remote_r1kh *r1kh; + *r1kh_out = NULL; + for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { - if (src_addr && os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) - continue; if (f_r1kh_id && - os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) != 0) - continue; - break; + os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) { + *r1kh_out = r1kh; + break; + } } - if (!r1kh) + if (!*r1kh_out) wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); - *r1kh_out = r1kh; + if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) + *r1kh_out = NULL; } @@ -758,14 +1094,46 @@ static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth, } +static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh; + struct ft_remote_r1kh *r1kh; + + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); + + for (r0kh = wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { + if (!r0kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + r0kh->seq = NULL; + } + + for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (!r1kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + r1kh->seq = NULL; + } +} + + +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) +{ + wpa_ft_deinit_seq(wpa_auth); +} + + static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh; u8 *packet = NULL; - const u8 *key; + const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; size_t packet_len, key_len; + struct ft_rrb_seq f_seq; struct tlv_list req_enc[] = { { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, .data = pmk_r0_name }, @@ -776,32 +1144,48 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, struct tlv_list req_auth[] = { { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, .data = sm->ft_pending_pull_nonce }, + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, .data = sm->r0kh_id }, { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, - .data = sm->wpa_auth->conf.r1_key_holder }, + .data = f_r1kh_id }, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, }; - wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, NULL, sm->r0kh_id, sm->r0kh_id_len, + wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, &r0kh); if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } + key = r0kh->key; key_len = sizeof(r0kh->key); wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); + if (r0kh->seq->rx.num_last == 0) { + /* A sequence request will be sent out anyway when pull + * response is received. Send it out now to avoid one RTT. */ + wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr, + r0kh->id, r0kh->id_len, f_r1kh_id, key, + key_len, NULL, 0, NULL, 0, NULL); + } + if (random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } + if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, &packet, &packet_len) < 0) @@ -1911,7 +2295,8 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *enc, size_t enc_len, - const u8 *auth, size_t auth_len) + const u8 *auth, size_t auth_len, + int no_defer) { const char *msgtype = "pull request"; u8 *plain = NULL, *packet = NULL; @@ -1919,13 +2304,15 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, struct ft_remote_r1kh *r1kh; const u8 *key; size_t key_len; + int seq_ret; const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; size_t f_pmk_r0_name_len; const struct wpa_ft_pmk_r0_sa *r0; int ret; struct tlv_list resp[2]; - struct tlv_list resp_auth[4]; + struct tlv_list resp_auth[5]; + struct ft_rrb_seq f_seq; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); @@ -1940,8 +2327,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); - wpa_ft_rrb_lookup_r1kh(wpa_auth, src_addr, f_r1kh_id, &r1kh); - if (!r1kh) + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); + if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) goto out; key = r1kh->key; key_len = sizeof(r1kh->key); @@ -1949,11 +2336,27 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); + seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, auth, + auth_len, msgtype, no_defer); + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, src_addr, FT_PACKET_R0KH_R1KH_PULL, &plain, &plain_len) < 0) goto out; + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, + &wpa_ft_rrb_rx_pull); + goto out; + } + + wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, + msgtype); + RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, f_pmk_r0_name_len); @@ -1961,6 +2364,11 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; + } + resp[0].type = FT_RRB_S1KH_ID; resp[0].len = f_s1kh_id_len; resp[0].data = f_s1kh_id; @@ -1971,15 +2379,18 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, resp_auth[0].type = FT_RRB_NONCE; resp_auth[0].len = f_nonce_len; resp_auth[0].data = f_nonce; - resp_auth[1].type = FT_RRB_R0KH_ID; - resp_auth[1].len = f_r0kh_id_len; - resp_auth[1].data = f_r0kh_id; - resp_auth[2].type = FT_RRB_R1KH_ID; - resp_auth[2].len = f_r1kh_id_len; - resp_auth[2].data = f_r1kh_id; - resp_auth[3].type = FT_RRB_LAST_EMPTY; - resp_auth[3].len = 0; - resp_auth[3].data = NULL; + resp_auth[1].type = FT_RRB_SEQ; + resp_auth[1].len = sizeof(f_seq); + resp_auth[1].data = (u8 *) &f_seq; + resp_auth[2].type = FT_RRB_R0KH_ID; + resp_auth[2].len = f_r0kh_id_len; + resp_auth[2].data = f_r0kh_id; + resp_auth[3].type = FT_RRB_R1KH_ID; + resp_auth[3].len = f_r1kh_id_len; + resp_auth[3].data = f_r1kh_id; + resp_auth[4].type = FT_RRB_LAST_EMPTY; + resp_auth[4].len = 0; + resp_auth[4].data = NULL; if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); @@ -2011,13 +2422,19 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, const u8 *src_addr, u8 type, const u8 *enc, size_t enc_len, const u8 *auth, size_t auth_len, - const char *msgtype, u8 *s1kh_id_out) + const char *msgtype, u8 *s1kh_id_out, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) { u8 *plain = NULL; size_t plain_len = 0; struct ft_remote_r0kh *r0kh; const u8 *key; size_t key_len; + int seq_ret; const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id; const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len; @@ -2036,17 +2453,31 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, goto out; } - wpa_ft_rrb_lookup_r0kh(wpa_auth, src_addr, f_r0kh_id, f_r0kh_id_len, - &r0kh); - if (!r0kh) + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh); + if (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) goto out; key = r0kh->key; key_len = sizeof(r0kh->key); + seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, auth, + auth_len, msgtype, cb ? 0 : 1); + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, src_addr, type, &plain, &plain_len) < 0) goto out; + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, cb); + goto out; + } + + wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, + msgtype); + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); @@ -2133,7 +2564,8 @@ static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx) static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *enc, size_t enc_len, - const u8 *auth, size_t auth_len) + const u8 *auth, size_t auth_len, + int no_defer) { const char *msgtype = "pull response"; int ret = -1; @@ -2156,7 +2588,8 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, } ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, - enc, enc_len, auth, auth_len, msgtype, s1kh_id); + enc, enc_len, auth, auth_len, msgtype, s1kh_id, + no_defer ? NULL : &wpa_ft_rrb_rx_resp); if (ret < 0) return -1; @@ -2176,31 +2609,233 @@ out: static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *enc, size_t enc_len, - const u8 *auth, size_t auth_len) + const u8 *auth, size_t auth_len, int no_defer) { const char *msgtype = "push"; - struct os_time now; - struct os_time tsend; - const u8 *f_timestamp; - size_t f_timestamp_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - RRB_GET_AUTH(FT_RRB_TIMESTAMP, timestamp, msgtype, sizeof(le32)); - tsend.sec = WPA_GET_LE32(f_timestamp); - wpa_printf(MSG_DEBUG, "FT: timestamp=%ld", tsend.sec); - os_get_time(&now); - if ((now.sec > tsend.sec && now.sec - tsend.sec > 60) || - (now.sec < tsend.sec && tsend.sec - now.sec > 60)) { - wpa_printf(MSG_DEBUG, - "FT(RRB): push did not have a valid timestamp: sender time %ld own time %ld", - tsend.sec, now.sec); + if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, + enc, enc_len, auth, auth_len, msgtype, NULL, + no_defer ? NULL : wpa_ft_rrb_rx_push) < 0) return -1; + + return 0; +} + + +static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, int type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + struct ft_remote_seq **rkh_seq, + u8 **key, size_t *key_len) +{ + struct ft_remote_r0kh *r0kh = NULL; + struct ft_remote_r1kh *r1kh = NULL; + const u8 *f_r0kh_id, *f_r1kh_id; + size_t f_r0kh_id_len, f_r1kh_id_len; + int to_r0kh, to_r1kh; + u8 *plain = NULL; + size_t plain_len = 0; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len); + to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id); + + if (to_r0kh && to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID"); + goto out; } - if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, - enc, enc_len, auth, auth_len, msgtype, NULL) < 0) - return -1; + if (!to_r0kh && !to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID"); + goto out; + } + + if (!to_r0kh) { + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh); + if (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + goto out; + } + + *key = r0kh->key; + *key_len = sizeof(r0kh->key); + *rkh_seq = r0kh->seq; + } + + if (!to_r1kh) { + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); + if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", + f_r1kh_id, FT_R1KH_ID_LEN); + goto out; + } + + *key = r1kh->key; + *key_len = sizeof(r1kh->key); + *rkh_seq = r1kh->seq; + } + + if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + os_free(plain); + + return 0; +out: + return -1; +} + + +static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + int ret = -1; + struct ft_rrb_seq f_seq; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; + struct ft_remote_seq *rkh_seq; + u8 *packet = NULL, *key = NULL; + size_t packet_len = 0, key_len = 0; + struct tlv_list seq_resp_auth[5]; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number request"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len); + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; + } + + seq_resp_auth[0].type = FT_RRB_NONCE; + seq_resp_auth[0].len = f_nonce_len; + seq_resp_auth[0].data = f_nonce; + seq_resp_auth[1].type = FT_RRB_SEQ; + seq_resp_auth[1].len = sizeof(f_seq); + seq_resp_auth[1].data = (u8 *) &f_seq; + seq_resp_auth[2].type = FT_RRB_R0KH_ID; + seq_resp_auth[2].len = f_r0kh_id_len; + seq_resp_auth[2].data = f_r0kh_id; + seq_resp_auth[3].type = FT_RRB_R1KH_ID; + seq_resp_auth[3].len = FT_R1KH_ID_LEN; + seq_resp_auth[3].data = f_r1kh_id; + seq_resp_auth[4].type = FT_RRB_LAST_EMPTY; + seq_resp_auth[4].len = 0; + seq_resp_auth[4].data = NULL; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + &packet, &packet_len) < 0) + goto out; + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_SEQ_RESP, packet, + packet_len); + +out: + os_free(packet); + + return ret; +} + + +static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + u8 *key = NULL; + size_t key_len = 0; + const u8 *f_nonce, *f_seq; + size_t f_nonce_len, f_seq_len; + struct ft_remote_seq *rkh_seq = NULL; + struct ft_remote_item *item; + struct os_reltime now, now_remote; + int seq_ret, found; + const struct ft_rrb_seq *msg_both; + u32 msg_dom, msg_seq; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number response"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, + f_nonce_len); + + found = 0; + dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item, + list) { + if (os_memcmp_const(f_nonce, item->nonce, + FT_RRB_NONCE_LEN) != 0 || + os_get_reltime(&now) < 0 || + os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout)) + continue; + + found = 1; + break; + } + if (!found) { + wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce"); + goto out; + } + + seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, + auth_len, "seq response", 1); + if (seq_ret == FT_RRB_SEQ_OK) { + wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number"); + wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth, + auth_len, "seq response"); + } else { + wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number"); + + RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response", + sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_dom = le_to_host32(msg_both->dom); + msg_seq = le_to_host32(msg_both->seq); + now_remote.sec = le_to_host32(msg_both->ts); + now_remote.usec = 0; + + rkh_seq->rx.num_last = 2; + rkh_seq->rx.dom = msg_dom; + rkh_seq->rx.offsetidx = 0; + /* Accept some older, possibly cached packets as well */ + rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG - + dl_list_len(&rkh_seq->rx.queue); + rkh_seq->rx.last[1] = msg_seq; + + /* local time - offset = remote time + * <=> local time - remote time = offset */ + os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset); + } + + wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1); return 0; out: @@ -2369,13 +3004,24 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, switch (oui_suffix) { case FT_PACKET_R0KH_R1KH_PULL: - wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen); + wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, + 0); break; case FT_PACKET_R0KH_R1KH_RESP: - wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen); + wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, + 0); break; case FT_PACKET_R0KH_R1KH_PUSH: - wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen); + wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, + 0); + break; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, + 0); + break; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, + alen, 0); break; } } @@ -2386,10 +3032,9 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, struct ft_remote_r1kh *r1kh, const u8 *s1kh_id) { - struct os_time now; u8 *packet; size_t packet_len; - u8 f_timestamp[sizeof(le32)]; + struct ft_rrb_seq f_seq; struct tlv_list push[] = { { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, .data = s1kh_id }, @@ -2398,8 +3043,8 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, }; struct tlv_list push_auth[] = { - { .type = FT_RRB_TIMESTAMP, .len = sizeof(f_timestamp), - .data = f_timestamp }, + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, { .type = FT_RRB_R0KH_ID, .len = wpa_auth->conf.r0_key_holder_len, .data = wpa_auth->conf.r0_key_holder }, @@ -2408,8 +3053,10 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, }; - os_get_time(&now); - WPA_PUT_LE32(f_timestamp, now.sec); + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0, r1kh->id, s1kh_id, push_auth, wpa_auth->addr, @@ -2447,10 +3094,10 @@ 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)); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { + for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + continue; wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); - r1kh = r1kh->next; } } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 3e698e8cb..b364fcee6 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -578,6 +578,10 @@ static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, return hapd->oui_resp; case FT_PACKET_R0KH_R1KH_PUSH: return hapd->oui_push; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + return hapd->oui_sreq; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + return hapd->oui_sresp; #endif /* CONFIG_IEEE80211R_AP */ default: return NULL; @@ -836,6 +840,18 @@ static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, if (!hapd->oui_push) return -1; + hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_REQ, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sreq) + return -1; + + hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sresp) + return -1; + return 0; } @@ -848,6 +864,10 @@ static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) hapd->oui_resp = NULL; eth_p_oui_unregister(hapd->oui_push); hapd->oui_push = NULL; + eth_p_oui_unregister(hapd->oui_sreq); + hapd->oui_sreq = NULL; + eth_p_oui_unregister(hapd->oui_sresp); + hapd->oui_sresp = NULL; } #endif /* CONFIG_IEEE80211R_AP */ diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 3279ad4cf..aebf75c1f 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -9,6 +9,8 @@ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H +#include "utils/list.h" + /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 @@ -211,6 +213,38 @@ struct wpa_authenticator { }; +#ifdef CONFIG_IEEE80211R_AP + +#define FT_REMOTE_SEQ_BACKLOG 16 +struct ft_remote_seq_rx { + u32 dom; + struct os_reltime time_offset; /* local time - offset = remote time */ + + /* accepted sequence numbers: (offset ... offset + 0x40000000] + * (except those in last) + * dropped sequence numbers: (offset - 0x40000000 ... offset] + * all others trigger SEQ_REQ message (except first message) + */ + u32 last[FT_REMOTE_SEQ_BACKLOG]; + unsigned int num_last; + u32 offsetidx; + + struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */ +}; + +struct ft_remote_seq_tx { + u32 dom; /* non zero if initialized */ + u32 seq; +}; + +struct ft_remote_seq { + struct ft_remote_seq_rx rx; + struct ft_remote_seq_tx tx; +}; + +#endif /* CONFIG_IEEE80211R_AP */ + + int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,