Captive portal: work around race condition between captiveportal_disconnect_all() and captiveportal_prune_old()
Captive portal: work around race condition between captiveportal_disconnect_all() and captiveportal_prune_old()

Captiveportal_disconnect_all() loops through the active users and disconnects them immediately but doesn't remove them from the user database, only adding them to a list that is processed after the end of the loop.

Since the loop can take several seconds if there are many users connected, captiveportal_prune_old() can be called after some users have been disconnected but before they're removed from the database.

When this happens and the user has an idle timeout set, captiveportal_prune_old() tries to find the last activity time and gets an invalid value because the user has already been removed from the fw table, so it uses the login time as last activity time.

Finally, if the login time is more than one idle timeout in the past, it tries to disconnect again the user and sends a RADIUS Accounting-Stop packet with termination-cause Idle-Timeout and zero Acct-Input-Octets/Acct-Output-Octets that overwrites the correct packet sent by captiveportal_disconnect_all().

To work around it, remove the users from the database before disconnecting them (this fixes only the case where captiveportal_disconnect_all() runs before captiveportal_prune_old()).

NewEraCracker/pfsense,brunostein/pfsense,pfsense/pfsense,pfsense/pfsense,dennypage/pfsense,dennypage/pfsense,brunostein/pfsense,brunostein/pfsense,dennypage/pfsense,jxmx/pfsense,ptorsten/pfsense,phil-davis/pfsense,phil-davis/pfsense,jxmx/pfsense,NOYB/pfsense,NOYB/pfsense,pfsense/pfsense,NewEraCracker/pfsense,phil-davis/pfsense,pfsense/pfsense,NOYB/pfsense,phil-davis/pfsense,NewEraCracker/pfsense,NewEraCracker/pfsense,ptorsten/pfsense,dennypage/pfsense,ptorsten/pfsense,jxmx/pfsense