mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 10:39:23 +01:00
ap: Delay ap_free if called inside event handler
ap.c has been mostly careful to call the event handler at the end of any externally called function to allow methods like ap_free() to be called within the handler, but that isn't enough. For example in ap_del_station we may end up emitting two events: STATION_REMOVED and DHCP_LEASE_EXPIRED. Use a slightly more complicated mechanism to explicitly guard ap_free calls inside the event handler. To make it easier, simplify cleanup in ap_assoc_reassoc with the use of _auto_. In ap_del_station reorder the actions to send the STATION_REMOVED event first as the DHCP_LEASE_EXPIRED is a consequence of the former and it makes sense for the handler to react to it first.
This commit is contained in:
parent
d9cd657135
commit
c473290b47
117
src/ap.c
117
src/ap.c
@ -101,6 +101,8 @@ struct ap_state {
|
|||||||
bool started : 1;
|
bool started : 1;
|
||||||
bool gtk_set : 1;
|
bool gtk_set : 1;
|
||||||
bool netconfig_set_addr4 : 1;
|
bool netconfig_set_addr4 : 1;
|
||||||
|
bool in_event : 1;
|
||||||
|
bool free_pending : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sta_state {
|
struct sta_state {
|
||||||
@ -250,6 +252,35 @@ static void ap_reset(struct ap_state *ap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ap_event_done(struct ap_state *ap, bool prev_in_event)
|
||||||
|
{
|
||||||
|
ap->in_event = prev_in_event;
|
||||||
|
|
||||||
|
if (!prev_in_event && ap->free_pending) {
|
||||||
|
ap_free(ap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ap->free_pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the AP is considered freed and the caller must avoid
|
||||||
|
* accessing @ap.
|
||||||
|
*/
|
||||||
|
static bool ap_event(struct ap_state *ap, enum ap_event_type event,
|
||||||
|
const void *event_data)
|
||||||
|
{
|
||||||
|
bool prev = ap->in_event;
|
||||||
|
|
||||||
|
if (ap->free_pending)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ap->in_event = true;
|
||||||
|
ap->ops->handle_event(event, event_data, ap->user_data);
|
||||||
|
return ap_event_done(ap, prev);
|
||||||
|
}
|
||||||
|
|
||||||
static void ap_del_station(struct sta_state *sta, uint16_t reason,
|
static void ap_del_station(struct sta_state *sta, uint16_t reason,
|
||||||
bool disassociate)
|
bool disassociate)
|
||||||
{
|
{
|
||||||
@ -283,6 +314,14 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
|
|||||||
|
|
||||||
ap_stop_handshake(sta);
|
ap_stop_handshake(sta);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the event handler tears the AP down, we've made sure above that
|
||||||
|
* a subsequent ap_sta_free(sta) has no need to access sta->ap.
|
||||||
|
*/
|
||||||
|
if (send_event)
|
||||||
|
if (ap_event(ap, AP_EVENT_STATION_REMOVED, &event_data))
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expire any DHCP leases owned by this client when it disconnects to
|
* Expire any DHCP leases owned by this client when it disconnects to
|
||||||
* make it harder for somebody to DoS the IP pool. If the client
|
* make it harder for somebody to DoS the IP pool. If the client
|
||||||
@ -290,17 +329,20 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
|
|||||||
* will get their IP back.
|
* will get their IP back.
|
||||||
*/
|
*/
|
||||||
if (ap->netconfig_dhcp) {
|
if (ap->netconfig_dhcp) {
|
||||||
sta->ip_alloc_lease = NULL;
|
bool prev = ap->in_event;
|
||||||
l_dhcp_server_expire_by_mac(ap->netconfig_dhcp, sta->addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the event handler tears the AP down, we've made sure above that
|
* If the LEASE_EXPIRED event in ap_dhcp_event_cb triggers an
|
||||||
* a subsequent ap_sta_free(sta) has no need to access sta->ap.
|
* ap_free(), delay cleanup to avoid destroying the DHCP
|
||||||
|
* server midway through l_dhcp_server_expire_by_mac().
|
||||||
*/
|
*/
|
||||||
if (send_event)
|
ap->in_event = true;
|
||||||
ap->ops->handle_event(AP_EVENT_STATION_REMOVED, &event_data,
|
|
||||||
ap->user_data);
|
sta->ip_alloc_lease = NULL;
|
||||||
|
l_dhcp_server_expire_by_mac(ap->netconfig_dhcp, sta->addr);
|
||||||
|
|
||||||
|
ap_event_done(ap, prev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ap_sta_match_addr(const void *a, const void *b)
|
static bool ap_sta_match_addr(const void *a, const void *b)
|
||||||
@ -335,19 +377,16 @@ static void ap_del_key_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
static void ap_new_rsna(struct sta_state *sta)
|
static void ap_new_rsna(struct sta_state *sta)
|
||||||
{
|
{
|
||||||
struct ap_state *ap = sta->ap;
|
struct ap_state *ap = sta->ap;
|
||||||
|
struct ap_event_station_added_data event_data = {};
|
||||||
|
|
||||||
l_debug("STA "MAC" authenticated", MAC_STR(sta->addr));
|
l_debug("STA "MAC" authenticated", MAC_STR(sta->addr));
|
||||||
|
|
||||||
sta->rsna = true;
|
sta->rsna = true;
|
||||||
|
|
||||||
if (ap->ops->handle_event) {
|
|
||||||
struct ap_event_station_added_data event_data = {};
|
|
||||||
event_data.mac = sta->addr;
|
event_data.mac = sta->addr;
|
||||||
event_data.assoc_ies = sta->assoc_ies;
|
event_data.assoc_ies = sta->assoc_ies;
|
||||||
event_data.assoc_ies_len = sta->assoc_ies_len;
|
event_data.assoc_ies_len = sta->assoc_ies_len;
|
||||||
ap->ops->handle_event(AP_EVENT_STATION_ADDED, &event_data,
|
ap_event(ap, AP_EVENT_STATION_ADDED, &event_data);
|
||||||
ap->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_drop_rsna(struct sta_state *sta)
|
static void ap_drop_rsna(struct sta_state *sta)
|
||||||
@ -356,6 +395,7 @@ static void ap_drop_rsna(struct sta_state *sta)
|
|||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
uint32_t ifindex = netdev_get_ifindex(sta->ap->netdev);
|
uint32_t ifindex = netdev_get_ifindex(sta->ap->netdev);
|
||||||
uint8_t key_id = 0;
|
uint8_t key_id = 0;
|
||||||
|
struct ap_event_station_removed_data event_data = {};
|
||||||
|
|
||||||
sta->rsna = false;
|
sta->rsna = false;
|
||||||
|
|
||||||
@ -380,12 +420,8 @@ static void ap_drop_rsna(struct sta_state *sta)
|
|||||||
|
|
||||||
ap_stop_handshake(sta);
|
ap_stop_handshake(sta);
|
||||||
|
|
||||||
if (ap->ops->handle_event) {
|
|
||||||
struct ap_event_station_removed_data event_data = {};
|
|
||||||
event_data.mac = sta->addr;
|
event_data.mac = sta->addr;
|
||||||
ap->ops->handle_event(AP_EVENT_STATION_REMOVED, &event_data,
|
ap_event(ap, AP_EVENT_STATION_REMOVED, &event_data);
|
||||||
ap->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
||||||
@ -405,7 +441,7 @@ static void ap_wsc_exit_pbc(struct ap_state *ap)
|
|||||||
ap->wsc_dpid = 0;
|
ap->wsc_dpid = 0;
|
||||||
ap_update_beacon(ap);
|
ap_update_beacon(ap);
|
||||||
|
|
||||||
ap->ops->handle_event(AP_EVENT_PBC_MODE_EXIT, NULL, ap->user_data);
|
ap_event(ap, AP_EVENT_PBC_MODE_EXIT, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ap_pbc_record_expiry_data {
|
struct ap_pbc_record_expiry_data {
|
||||||
@ -1090,9 +1126,7 @@ static void ap_wsc_handshake_event(struct handshake_state *hs,
|
|||||||
&expiry_data);
|
&expiry_data);
|
||||||
|
|
||||||
event_data.mac = sta->addr;
|
event_data.mac = sta->addr;
|
||||||
sta->ap->ops->handle_event(AP_EVENT_REGISTRATION_SUCCESS,
|
ap_event(sta->ap, AP_EVENT_REGISTRATION_SUCCESS, &event_data);
|
||||||
&event_data,
|
|
||||||
sta->ap->user_data);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1594,11 +1628,11 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
const char *ssid = NULL;
|
const char *ssid = NULL;
|
||||||
const uint8_t *rsn = NULL;
|
const uint8_t *rsn = NULL;
|
||||||
size_t ssid_len = 0;
|
size_t ssid_len = 0;
|
||||||
struct l_uintset *rates = NULL;
|
_auto_(l_uintset_free) struct l_uintset *rates = NULL;
|
||||||
struct ie_rsn_info rsn_info;
|
struct ie_rsn_info rsn_info;
|
||||||
int err;
|
int err;
|
||||||
struct ie_tlv_iter iter;
|
struct ie_tlv_iter iter;
|
||||||
uint8_t *wsc_data = NULL;
|
_auto_(l_free) uint8_t *wsc_data = NULL;
|
||||||
ssize_t wsc_data_len;
|
ssize_t wsc_data_len;
|
||||||
bool fils_ip_req = false;
|
bool fils_ip_req = false;
|
||||||
struct ie_fils_ip_addr_request_info fils_ip_req_info;
|
struct ie_fils_ip_addr_request_info fils_ip_req_info;
|
||||||
@ -1685,9 +1719,6 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
goto bad_frame;
|
goto bad_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
l_free(wsc_data);
|
|
||||||
wsc_data = NULL;
|
|
||||||
|
|
||||||
if (wsc_req.request_type !=
|
if (wsc_req.request_type !=
|
||||||
WSC_REQUEST_TYPE_ENROLLEE_OPEN_8021X) {
|
WSC_REQUEST_TYPE_ENROLLEE_OPEN_8021X) {
|
||||||
err = MMPDU_REASON_CODE_INVALID_IE;
|
err = MMPDU_REASON_CODE_INVALID_IE;
|
||||||
@ -1728,8 +1759,9 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
event_data.mac = sta->addr;
|
event_data.mac = sta->addr;
|
||||||
event_data.assoc_ies = ies;
|
event_data.assoc_ies = ies;
|
||||||
event_data.assoc_ies_len = ies_len;
|
event_data.assoc_ies_len = ies_len;
|
||||||
ap->ops->handle_event(AP_EVENT_REGISTRATION_START, &event_data,
|
|
||||||
ap->user_data);
|
if (ap_event(ap, AP_EVENT_REGISTRATION_START, &event_data))
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we're starting the PBC Registration Protocol
|
* Since we're starting the PBC Registration Protocol
|
||||||
@ -1781,7 +1813,7 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
if (sta->rates)
|
if (sta->rates)
|
||||||
l_uintset_free(sta->rates);
|
l_uintset_free(sta->rates);
|
||||||
|
|
||||||
sta->rates = rates;
|
sta->rates = l_steal_ptr(rates);
|
||||||
|
|
||||||
l_free(sta->assoc_ies);
|
l_free(sta->assoc_ies);
|
||||||
|
|
||||||
@ -1823,11 +1855,6 @@ bad_frame:
|
|||||||
else if (sta->associated)
|
else if (sta->associated)
|
||||||
ap_stop_handshake(sta);
|
ap_stop_handshake(sta);
|
||||||
|
|
||||||
if (rates)
|
|
||||||
l_uintset_free(rates);
|
|
||||||
|
|
||||||
l_free(wsc_data);
|
|
||||||
|
|
||||||
if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
|
if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
|
||||||
req, (void *) ies + ies_len - (void *) req,
|
req, (void *) ies + ies_len - (void *) req,
|
||||||
NULL, ap_fail_assoc_resp_cb))
|
NULL, ap_fail_assoc_resp_cb))
|
||||||
@ -2209,6 +2236,7 @@ static void ap_start_failed(struct ap_state *ap, int err)
|
|||||||
{
|
{
|
||||||
struct ap_event_start_failed_data data = { err };
|
struct ap_event_start_failed_data data = { err };
|
||||||
|
|
||||||
|
ap->in_event = true;
|
||||||
ap->ops->handle_event(AP_EVENT_START_FAILED, &data, ap->user_data);
|
ap->ops->handle_event(AP_EVENT_START_FAILED, &data, ap->user_data);
|
||||||
ap_reset(ap);
|
ap_reset(ap);
|
||||||
l_genl_family_free(ap->nl80211);
|
l_genl_family_free(ap->nl80211);
|
||||||
@ -2224,13 +2252,11 @@ static void ap_dhcp_event_cb(struct l_dhcp_server *server,
|
|||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case L_DHCP_SERVER_EVENT_NEW_LEASE:
|
case L_DHCP_SERVER_EVENT_NEW_LEASE:
|
||||||
ap->ops->handle_event(AP_EVENT_DHCP_NEW_LEASE, lease,
|
ap_event(ap, AP_EVENT_DHCP_NEW_LEASE, lease);
|
||||||
ap->user_data);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L_DHCP_SERVER_EVENT_LEASE_EXPIRED:
|
case L_DHCP_SERVER_EVENT_LEASE_EXPIRED:
|
||||||
ap->ops->handle_event(AP_EVENT_DHCP_LEASE_EXPIRED, lease,
|
ap_event(ap, AP_EVENT_DHCP_LEASE_EXPIRED, lease);
|
||||||
ap->user_data);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2267,7 +2293,7 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ap->started = true;
|
ap->started = true;
|
||||||
ap->ops->handle_event(AP_EVENT_STARTED, NULL, ap->user_data);
|
ap_event(ap, AP_EVENT_STARTED, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
||||||
@ -2552,6 +2578,8 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
|
|||||||
|
|
||||||
switch (l_genl_msg_get_command(msg)) {
|
switch (l_genl_msg_get_command(msg)) {
|
||||||
case NL80211_CMD_STOP_AP:
|
case NL80211_CMD_STOP_AP:
|
||||||
|
ap->in_event = true;
|
||||||
|
|
||||||
if (ap->start_stop_cmd_id) {
|
if (ap->start_stop_cmd_id) {
|
||||||
struct ap_event_start_failed_data data = { -ECANCELED };
|
struct ap_event_start_failed_data data = { -ECANCELED };
|
||||||
|
|
||||||
@ -3304,7 +3332,9 @@ void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
|
|||||||
|
|
||||||
if (ap->started) {
|
if (ap->started) {
|
||||||
ap->started = false;
|
ap->started = false;
|
||||||
ap->ops->handle_event(AP_EVENT_STOPPING, NULL, ap->user_data);
|
|
||||||
|
if (ap_event(ap, AP_EVENT_STOPPING, NULL))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ap_reset(ap);
|
ap_reset(ap);
|
||||||
@ -3355,6 +3385,11 @@ free_ap:
|
|||||||
/* Free @ap without a graceful shutdown */
|
/* Free @ap without a graceful shutdown */
|
||||||
void ap_free(struct ap_state *ap)
|
void ap_free(struct ap_state *ap)
|
||||||
{
|
{
|
||||||
|
if (ap->in_event) {
|
||||||
|
ap->free_pending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ap_reset(ap);
|
ap_reset(ap);
|
||||||
l_genl_family_free(ap->nl80211);
|
l_genl_family_free(ap->nl80211);
|
||||||
l_free(ap);
|
l_free(ap);
|
||||||
|
Loading…
Reference in New Issue
Block a user