diff options
author | sam <sam@FreeBSD.org> | 2005-06-05 22:35:03 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2005-06-05 22:35:03 +0000 |
commit | 21e78f430e299464b3c902bec78f8daa1f0e2e71 (patch) | |
tree | a7d225a062cd128980707f3fa918dec2d015c46b /contrib/hostapd/sta_info.c | |
download | FreeBSD-src-21e78f430e299464b3c902bec78f8daa1f0e2e71.zip FreeBSD-src-21e78f430e299464b3c902bec78f8daa1f0e2e71.tar.gz |
Stripped down import of hostapd v0.3.7
Diffstat (limited to 'contrib/hostapd/sta_info.c')
-rw-r--r-- | contrib/hostapd/sta_info.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/contrib/hostapd/sta_info.c b/contrib/hostapd/sta_info.c new file mode 100644 index 0000000..9a4daa3 --- /dev/null +++ b/contrib/hostapd/sta_info.c @@ -0,0 +1,354 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / Station table + * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "hostapd.h" +#include "sta_info.h" +#include "eloop.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "radius.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "radius_client.h" +#include "driver.h" + + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (cb(hapd, sta, ctx)) + return 1; + } + + return 0; +} + + +struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta)]; + while (s != NULL && memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta) +{ + struct sta_info *tmp; + + if (hapd->sta_list == sta) { + hapd->sta_list = sta->next; + return; + } + + tmp = hapd->sta_list; + while (tmp != NULL && tmp->next != sta) + tmp = tmp->next; + if (tmp == NULL) { + printf("Could not remove STA " MACSTR " from list.\n", + MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta) +{ + sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; + hapd->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printf("AP: could not remove STA " MACSTR " from hash table\n", + MAC2STR(sta->addr)); +} + + +void ap_free_sta(hostapd *hapd, struct sta_info *sta) +{ + accounting_sta_stop(hapd, sta); + if (!(sta->flags & WLAN_STA_PREAUTH)) + hostapd_sta_remove(hapd, sta->addr); + + ap_sta_hash_del(hapd, sta); + ap_sta_list_del(hapd, sta); + + if (sta->aid > 0) + hapd->sta_aid[sta->aid - 1] = NULL; + + hapd->num_sta--; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + + ieee802_1x_free_station(sta); + wpa_free_station(sta); + radius_client_flush_auth(hapd->radius, sta->addr); + + if (sta->last_assoc_req) + free(sta->last_assoc_req); + + free(sta->challenge); + free(sta->wpa_ie); + + free(sta); +} + + +void hostapd_free_stas(hostapd *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + sta = sta->next; + printf("Removing station " MACSTR "\n", MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned long next_time = 0; + + if (sta->timeout_next == STA_REMOVE) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "local deauth request"); + ap_free_sta(hapd, sta); + return; + } + + if ((sta->flags & WLAN_STA_ASSOC) && + (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC)) { + int inactive_sec; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Checking STA " MACSTR " inactivity:\n", + MAC2STR(sta->addr)); + inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); + if (inactive_sec == -1) { + printf(" Could not get station info from kernel " + "driver for " MACSTR ".\n", + MAC2STR(sta->addr)); + } else if (inactive_sec < AP_MAX_INACTIVITY && + sta->flags & WLAN_STA_ASSOC) { + /* station activity detected; reset timeout state */ + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Station has been active\n"); + sta->timeout_next = STA_NULLFUNC; + next_time = AP_MAX_INACTIVITY - inactive_sec; + } + } + + if ((sta->flags & WLAN_STA_ASSOC) && + sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Station has ACKed data poll\n"); + /* data nullfunc frame poll did not produce TX errors; assume + * station ACKed it */ + sta->timeout_next = STA_NULLFUNC; + next_time = AP_MAX_INACTIVITY; + } + + if (next_time) { + eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, + sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC && + (sta->flags & WLAN_STA_ASSOC)) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + struct ieee80211_hdr hdr; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Polling STA with data frame\n"); + sta->flags |= WLAN_STA_PENDING_POLL; + + /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + memset(&hdr, 0, sizeof(hdr)); + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr.frame_control |= host_to_le16(BIT(1)); + hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); + memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN); + memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); + + if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) + perror("ap_handle_timer: send"); + } else if (sta->timeout_next != STA_REMOVE) { + int deauth = sta->timeout_next == STA_DEAUTH; + + printf(" Sending %s info to STA " MACSTR "\n", + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); + + if (deauth) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } else { + hostapd_sta_disassoc( + hapd, sta->addr, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + } + } + + switch (sta->timeout_next) { + case STA_NULLFUNC: + sta->timeout_next = STA_DISASSOC; + eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, + hapd, sta); + break; + case STA_DISASSOC: + sta->flags &= ~WLAN_STA_ASSOC; + ieee802_1x_set_port_enabled(hapd, sta, 0); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated due to " + "inactivity"); + sta->timeout_next = STA_DEAUTH; + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); + break; + case STA_DEAUTH: + case STA_REMOVE: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "inactivity"); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + ap_free_sta(hapd, sta); + break; + } +} + + +void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (!(sta->flags & WLAN_STA_AUTH)) + return; + + hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "session timeout"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; + ap_free_sta(hapd, sta); +} + + +void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, + u32 session_timeout) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " + "seconds", session_timeout); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, + hapd, sta); +} + + +void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); +} + + +struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return sta; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n"); + if (hapd->num_sta >= MAX_STA_COUNT) { + /* FIX: might try to remove some old STAs first? */ + printf(" no more room for new STAs (%d/%d)\n", + hapd->num_sta, MAX_STA_COUNT); + return NULL; + } + + sta = (struct sta_info *) malloc(sizeof(struct sta_info)); + if (sta == NULL) { + printf(" malloc failed\n"); + return NULL; + } + memset(sta, 0, sizeof(struct sta_info)); + sta->acct_interim_interval = hapd->conf->radius_acct_interim_interval; + + /* initialize STA info data */ + eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer, + hapd, sta); + memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + + return sta; +} |