--- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -16,7 +16,11 @@ struct vlan_description; struct mld_info; - +struct rate_description { + u32 rx; + u32 tx; +}; + #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER @@ -88,6 +92,7 @@ struct ft_rrb_frame { #define FT_RRB_IDENTITY 15 #define FT_RRB_RADIUS_CUI 16 #define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */ +#define FT_RRB_RATE_LIMIT 18 struct ft_rrb_tlv { le16 type; @@ -368,6 +373,10 @@ struct wpa_auth_callbacks { struct vlan_description *vlan); int (*get_vlan)(void *ctx, const u8 *sta_addr, struct vlan_description *vlan); + int (*set_rate_limit)(void *ctx, const u8 *sta_addr, + struct rate_description *rate); + int (*get_rate_limit)(void *ctx, const u8 *sta_addr, + struct rate_description *rate); int (*set_identity)(void *ctx, const u8 *sta_addr, const u8 *identity, size_t identity_len); size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf); @@ -536,7 +545,7 @@ int wpa_ft_fetch_pmk_r1(struct wpa_authe struct vlan_description *vlan, const u8 **identity, size_t *identity_len, const u8 **radius_cui, size_t *radius_cui_len, - int *session_timeout); + int *session_timeout, struct rate_description *rate); #endif /* CONFIG_IEEE80211R_AP */ --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1200,6 +1200,40 @@ static int hostapd_wpa_auth_get_vlan(voi } +static int hostapd_wpa_auth_set_rate_limit(void *ctx, const u8 *sta_addr, + struct rate_description *rate) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta || !sta->wpa_sm) + return -1; + + memcpy(sta->bandwidth, rate, sizeof(*rate)); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "rate-limit %d %d", sta->bandwidth[0], sta->bandwidth[1]); + + return 0; +} + + +static int hostapd_wpa_auth_get_rate_limit(void *ctx, const u8 *sta_addr, + struct rate_description *rate) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + memcpy(rate, sta->bandwidth, sizeof(*rate)); + + return 0; +} + + static int hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, const u8 *identity, size_t identity_len) @@ -1640,6 +1674,8 @@ int hostapd_setup_wpa(struct hostapd_dat .add_tspec = hostapd_wpa_auth_add_tspec, .set_vlan = hostapd_wpa_auth_set_vlan, .get_vlan = hostapd_wpa_auth_get_vlan, + .set_rate_limit = hostapd_wpa_auth_set_rate_limit, + .get_rate_limit = hostapd_wpa_auth_get_rate_limit, .set_identity = hostapd_wpa_auth_set_identity, .get_identity = hostapd_wpa_auth_get_identity, .set_radius_cui = hostapd_wpa_auth_set_radius_cui, --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -379,6 +379,14 @@ static size_t wpa_ft_vlan_len(const stru return tlv_len; } +static size_t wpa_ft_rate_limit_len(const struct rate_description *rate) +{ + if (!rate || (!rate->rx && !rate->tx)) + return 0; + + return (sizeof(struct ft_rrb_tlv) + 8); +} + static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan, u8 *start, u8 *endpos) @@ -434,10 +442,48 @@ static size_t wpa_ft_vlan_lin(const stru } +static size_t wpa_ft_rate_limit_lin(const struct rate_description *rate, + u8 *start, u8 *endpos) +{ + size_t tlv_len; + int i, len; + struct ft_rrb_tlv *hdr; + u8 *pos = start; + + if (!rate) + return 0; + + tlv_len = 0; + if (rate->rx || rate->tx) { + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_RATE_LIMIT); + hdr->len = host_to_le16(2 * sizeof(le32)); + pos = start + tlv_len; + + tlv_len += sizeof(u32); + if (start + tlv_len > endpos) + return tlv_len; + WPA_PUT_LE32(pos, rate->rx); + pos = start + tlv_len; + tlv_len += sizeof(u32); + if (start + tlv_len > endpos) + return tlv_len; + WPA_PUT_LE32(pos, rate->tx); + pos = start + tlv_len; + } + + return tlv_len; +} + + static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1, const struct tlv_list *tlvs2, const struct vlan_description *vlan, - u8 **plain, size_t *plain_len) + u8 **plain, size_t *plain_len, + const struct rate_description *rate) { u8 *pos, *endpos; size_t tlv_len; @@ -445,6 +491,7 @@ static int wpa_ft_rrb_lin(const struct t tlv_len = wpa_ft_tlv_len(tlvs1); tlv_len += wpa_ft_tlv_len(tlvs2); tlv_len += wpa_ft_vlan_len(vlan); + tlv_len += wpa_ft_rate_limit_len(rate); *plain_len = tlv_len; *plain = os_zalloc(tlv_len); @@ -458,6 +505,7 @@ static int wpa_ft_rrb_lin(const struct t pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); pos += wpa_ft_vlan_lin(vlan, pos, endpos); + pos += wpa_ft_rate_limit_lin(rate, pos, endpos); /* validity check */ if (pos != endpos) { @@ -526,7 +574,8 @@ static int wpa_ft_rrb_build(const u8 *ke const struct tlv_list *tlvs_auth, const struct vlan_description *vlan, const u8 *src_addr, u8 type, - u8 **packet, size_t *packet_len) + u8 **packet, size_t *packet_len, + const struct rate_description *rate) { u8 *plain = NULL, *auth = NULL, *pos, *tmp; size_t plain_len = 0, auth_len = 0; @@ -534,10 +583,10 @@ static int wpa_ft_rrb_build(const u8 *ke size_t pad_len = 0; *packet = NULL; - if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) + if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len, rate) < 0) goto out; - if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0) + if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len, NULL) < 0) goto out; *packet_len = sizeof(u16) + auth_len + plain_len; @@ -700,6 +749,24 @@ static int wpa_ft_get_vlan(struct wpa_au } +static int wpa_ft_get_rate_limit(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct rate_description *rate) +{ + if (!wpa_auth->cb->get_rate_limit) + return -1; + return wpa_auth->cb->get_rate_limit(wpa_auth->cb_ctx, sta_addr, rate); +} + + +static int wpa_ft_set_rate_limit(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct rate_description *rate) +{ + if (!wpa_auth->cb->set_rate_limit) + return -1; + return wpa_auth->cb->set_rate_limit(wpa_auth->cb_ctx, sta_addr, rate); +} + + static int wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, const u8 *identity, size_t identity_len) @@ -1025,7 +1092,7 @@ wpa_ft_rrb_seq_req(struct wpa_authentica if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL, wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, - &packet, &packet_len) < 0) { + &packet, &packet_len, NULL) < 0) { item = NULL; /* some other seq resp might still accept this */ goto err; } @@ -1208,6 +1275,7 @@ struct wpa_ft_pmk_r0_sa { u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ struct vlan_description *vlan; + struct rate_description *rate; os_time_t expiration; /* 0 for no expiration */ u8 *identity; size_t identity_len; @@ -1226,6 +1294,7 @@ struct wpa_ft_pmk_r1_sa { u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ struct vlan_description *vlan; + struct rate_description *rate; u8 *identity; size_t identity_len; u8 *radius_cui; @@ -1254,6 +1323,7 @@ static void wpa_ft_free_pmk_r0(struct wp os_memset(r0->pmk_r0, 0, PMK_LEN_MAX); os_free(r0->vlan); + os_free(r0->rate); os_free(r0->identity); os_free(r0->radius_cui); os_free(r0); @@ -1307,6 +1377,7 @@ static void wpa_ft_free_pmk_r1(struct wp eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL); os_memset(r1->pmk_r1, 0, PMK_LEN_MAX); + os_free(r1->rate); os_free(r1->vlan); os_free(r1->identity); os_free(r1->radius_cui); @@ -1360,7 +1431,8 @@ static int wpa_ft_store_pmk_r0(struct wp const struct vlan_description *vlan, int expires_in, int session_timeout, const u8 *identity, size_t identity_len, - const u8 *radius_cui, size_t radius_cui_len) + const u8 *radius_cui, size_t radius_cui_len, + struct rate_description *rate) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; @@ -1388,6 +1460,14 @@ static int wpa_ft_store_pmk_r0(struct wp } *r0->vlan = *vlan; } + if (rate) { + r0->rate = os_zalloc(sizeof(*rate)); + if (!r0->rate) { + bin_clear_free(r0, sizeof(*r0)); + return -1; + } + *r0->rate = *rate; + } if (identity) { r0->identity = os_malloc(identity_len); if (r0->identity) { @@ -1447,7 +1527,8 @@ static int wpa_ft_store_pmk_r1(struct wp const struct vlan_description *vlan, int expires_in, int session_timeout, const u8 *identity, size_t identity_len, - const u8 *radius_cui, size_t radius_cui_len) + const u8 *radius_cui, size_t radius_cui_len, + struct rate_description *rate) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; int max_expires_in = wpa_auth->conf.r1_max_key_lifetime; @@ -1477,6 +1558,14 @@ static int wpa_ft_store_pmk_r1(struct wp } *r1->vlan = *vlan; } + if (rate) { + r1->rate = os_zalloc(sizeof(*rate)); + if (!r1->rate) { + bin_clear_free(r1, sizeof(*r1)); + return -1; + } + *r1->rate = *rate; + } if (identity) { r1->identity = os_malloc(identity_len); if (r1->identity) { @@ -1513,7 +1602,7 @@ int wpa_ft_fetch_pmk_r1(struct wpa_authe struct vlan_description *vlan, const u8 **identity, size_t *identity_len, const u8 **radius_cui, size_t *radius_cui_len, - int *session_timeout) + int *session_timeout, struct rate_description *rate) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; @@ -1533,6 +1622,12 @@ int wpa_ft_fetch_pmk_r1(struct wpa_authe *vlan = *r1->vlan; if (vlan && !r1->vlan) os_memset(vlan, 0, sizeof(*vlan)); + if (rate) { + if (r1->rate) + *rate = *r1->rate; + else + memset(rate, 0, sizeof(*rate)); + } if (identity && identity_len) { *identity = r1->identity; *identity_len = r1->identity_len; @@ -2059,7 +2154,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL, sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, - &packet, &packet_len) < 0) + &packet, &packet_len, NULL) < 0) return -1; ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); @@ -2088,6 +2183,7 @@ int wpa_ft_store_pmk_fils(struct wpa_sta { int expires_in = sm->wpa_auth->conf.r0_key_lifetime; struct vlan_description vlan; + struct rate_description rate; const u8 *identity, *radius_cui; size_t identity_len, radius_cui_len; int session_timeout; @@ -2099,6 +2195,7 @@ int wpa_ft_store_pmk_fils(struct wpa_sta MAC2STR(sm->addr)); return -1; } + wpa_ft_get_rate_limit(sm->wpa_auth, sm->addr, &rate); identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, @@ -2108,7 +2205,7 @@ int wpa_ft_store_pmk_fils(struct wpa_sta return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, pmk_r0_name, sm->pairwise, &vlan, expires_in, session_timeout, identity, identity_len, - radius_cui, radius_cui_len); + radius_cui, radius_cui_len, &rate); } @@ -2172,6 +2269,7 @@ void wpa_auth_ft_store_keys(struct wpa_s int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; int expires_in = sm->wpa_auth->conf.r0_key_lifetime; struct vlan_description vlan; + struct rate_description rate; const u8 *identity, *radius_cui; size_t identity_len, radius_cui_len; int session_timeout; @@ -2185,6 +2283,8 @@ void wpa_auth_ft_store_keys(struct wpa_s return; } + wpa_ft_get_rate_limit(sm->wpa_auth, sm->addr, &rate); + identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, &radius_cui); @@ -2195,11 +2295,12 @@ void wpa_auth_ft_store_keys(struct wpa_s pmk_r0_name, sm->pairwise, &vlan, expires_in, session_timeout, identity, identity_len, - radius_cui, radius_cui_len); + radius_cui, radius_cui_len, &rate); wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len, sm->pmk_r1_name, sm->pairwise, &vlan, expires_in, session_timeout, identity, - identity_len, radius_cui, radius_cui_len); + identity_len, radius_cui, radius_cui_len, + &rate); } @@ -3100,7 +3201,8 @@ static int wpa_ft_local_derive_pmk_r1(st const u8 **radius_cui, size_t *radius_cui_len, int *out_session_timeout, - size_t *pmk_r1_len) + size_t *pmk_r1_len, + struct rate_description *rate) { struct wpa_auth_config *conf = &wpa_auth->conf; const struct wpa_ft_pmk_r0_sa *r0; @@ -3136,7 +3238,8 @@ static int wpa_ft_local_derive_pmk_r1(st out_pmk_r1_name, sm->pairwise, r0->vlan, expires_in, session_timeout, r0->identity, r0->identity_len, - r0->radius_cui, r0->radius_cui_len); + r0->radius_cui, r0->radius_cui_len, + r0->rate); *out_pairwise = sm->pairwise; if (vlan) { @@ -3146,6 +3249,13 @@ static int wpa_ft_local_derive_pmk_r1(st os_memset(vlan, 0, sizeof(*vlan)); } + if (rate) { + if (r0->rate) + *rate = *r0->rate; + else + os_memset(rate, 0, sizeof(*rate)); + } + if (identity && identity_len) { *identity = r0->identity; *identity_len = r0->identity_len; @@ -3178,6 +3288,7 @@ static int wpa_ft_process_auth_req(struc u8 *pos, *end; int pairwise, session_timeout = 0; struct vlan_description vlan; + struct rate_description rate = {}; const u8 *identity, *radius_cui; size_t identity_len = 0, radius_cui_len = 0; size_t pmk_r1_len, kdk_len, len; @@ -3274,7 +3385,7 @@ static int wpa_ft_process_auth_req(struc pmk_r1, &pmk_r1_len, &pairwise, &vlan, &identity, &identity_len, &radius_cui, &radius_cui_len, - &session_timeout) == 0) { + &session_timeout, &rate) == 0) { wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name (using SHA%zu) from local cache", pmk_r1_len * 8); @@ -3290,7 +3401,7 @@ static int wpa_ft_process_auth_req(struc pmk_r1_name, pmk_r1, &pairwise, &vlan, &identity, &identity_len, &radius_cui, &radius_cui_len, - &session_timeout, &pmk_r1_len) == 0) { + &session_timeout, &pmk_r1_len, &rate) == 0) { wpa_printf(MSG_DEBUG, "FT: Generated PMK-R1 based on local PMK-R0"); goto pmk_r1_derived; @@ -3392,6 +3503,7 @@ pmk_r1_derived: wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); goto out; } + wpa_ft_set_rate_limit(sm->wpa_auth, sm->addr, &rate); if (wpa_ft_set_identity(sm->wpa_auth, sm->addr, identity, identity_len) < 0 || wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, @@ -3973,7 +4085,7 @@ static int wpa_ft_rrb_build_r0(const u8 ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth, pmk_r0->vlan, src_addr, type, - packet, packet_len); + packet, packet_len, pmk_r0->rate); forced_memzero(pmk_r1, sizeof(pmk_r1)); @@ -4113,7 +4225,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth, NULL, wpa_auth->addr, FT_PACKET_R0KH_R1KH_RESP, - &packet, &packet_len); + &packet, &packet_len, NULL); } else { ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id, resp_auth, wpa_auth->addr, @@ -4165,11 +4277,15 @@ static int wpa_ft_rrb_rx_r1(struct wpa_a size_t f_expires_in_len; size_t f_identity_len, f_radius_cui_len; size_t f_session_timeout_len; + size_t f_rate_len; + const u8 *f_rate; int pairwise; int ret = -1; int expires_in; int session_timeout; struct vlan_description vlan; + struct rate_description rate; + int has_rate = 0; size_t pmk_r1_len; RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); @@ -4279,6 +4395,13 @@ static int wpa_ft_rrb_rx_r1(struct wpa_a wpa_printf(MSG_DEBUG, "FT: vlan %d%s", le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : ""); + RRB_GET_OPTIONAL(FT_RRB_RATE_LIMIT, rate, msgtype, 2 * sizeof(le32)); + if (f_rate) { + memcpy(&rate, f_rate, sizeof(rate)); + rate.rx = le_to_host32(rate.rx); + rate.tx = le_to_host32(rate.tx); + has_rate = 1; + }; RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1); if (f_identity) wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity, @@ -4301,7 +4424,7 @@ static int wpa_ft_rrb_rx_r1(struct wpa_a f_pmk_r1_name, pairwise, &vlan, expires_in, session_timeout, f_identity, f_identity_len, f_radius_cui, - f_radius_cui_len) < 0) + f_radius_cui_len, has_rate ? &rate : 0) < 0) goto out; ret = 0; @@ -4614,7 +4737,7 @@ static int wpa_ft_rrb_rx_seq_req(struct if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL, wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, - &packet, &packet_len) < 0) + &packet, &packet_len, NULL) < 0) goto out; wpa_ft_rrb_oui_send(wpa_auth, src_addr,