diff mbox

[PATCHv2] hostapd: reload bss mac files on SIGUSR2

Message ID 1339368270-11500-1-git-send-email-ordex@autistici.org
State Changes Requested
Headers show

Commit Message

Antonio Quartulli June 10, 2012, 10:44 p.m. UTC
This patch allows the administrator to modify the provided MAC list for the
BSS ACL mechanism at runtime. In the current implementation no runtime change
is possible: if the MAC list is modified, hostapd has to be restart so
disconnecting all the current clients.

After manually modify the MAC files, the USR2 signal will make hostapd reload
them and possibly disconnect new not allowed stations.

Signed-hostap: Antonio Quartulli <ordex@autistici.org>
---

v2:
 fixed signed-hostap line

 hostapd/config_file.c |   98 +++++++++++++++++++++++++++++++++++++++++++++++++
 hostapd/config_file.h |    1 +
 hostapd/main.c        |   14 +++++++
 src/ap/ap_config.h    |    3 +-
 src/ap/hostapd.c      |    3 --
 src/ap/sta_info.c     |   32 ++++++++++++++++
 src/ap/sta_info.h     |    3 ++
 7 files changed, 150 insertions(+), 4 deletions(-)

Comments

Baruch Siach June 11, 2012, 5:04 a.m. UTC | #1
Hi Antonio,

On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> This patch allows the administrator to modify the provided MAC list for the
> BSS ACL mechanism at runtime. In the current implementation no runtime change
> is possible: if the MAC list is modified, hostapd has to be restart so
> disconnecting all the current clients.
> 
> After manually modify the MAC files, the USR2 signal will make hostapd reload
> them and possibly disconnect new not allowed stations.

Is there a reason not to use ctrl_iface? It seems to me like a more natural 
interface than system global signal numbers.

baruch
Antonio Quartulli June 11, 2012, 6:59 a.m. UTC | #2
On Mon, Jun 11, 2012 at 08:04:08AM +0300, Baruch Siach wrote:
> Hi Antonio,
> 
> On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> > This patch allows the administrator to modify the provided MAC list for the
> > BSS ACL mechanism at runtime. In the current implementation no runtime change
> > is possible: if the MAC list is modified, hostapd has to be restart so
> > disconnecting all the current clients.
> > 
> > After manually modify the MAC files, the USR2 signal will make hostapd reload
> > them and possibly disconnect new not allowed stations.
> 
> Is there a reason not to use ctrl_iface?

mh..not really. I didn't think about that.

> It seems to me like a more natural interface than system global signal numbers.

In my mind I had the classic SIGHUP used to reload config files in general and
so I thought this was a good track.

Do you think it is better to move to ctrl_iface?

Cheers,
Baruch Siach June 11, 2012, 7:47 a.m. UTC | #3
Hi Antonio,

On Mon, Jun 11, 2012 at 08:59:32AM +0200, Antonio Quartulli wrote:
> On Mon, Jun 11, 2012 at 08:04:08AM +0300, Baruch Siach wrote:
> > On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> > > This patch allows the administrator to modify the provided MAC list for the
> > > BSS ACL mechanism at runtime. In the current implementation no runtime change
> > > is possible: if the MAC list is modified, hostapd has to be restart so
> > > disconnecting all the current clients.
> > > 
> > > After manually modify the MAC files, the USR2 signal will make hostapd reload
> > > them and possibly disconnect new not allowed stations.
> > 
> > Is there a reason not to use ctrl_iface?
> 
> mh..not really. I didn't think about that.
> 
> > It seems to me like a more natural interface than system global signal numbers.
> 
> In my mind I had the classic SIGHUP used to reload config files in general and
> so I thought this was a good track.

Well, it is true that many daemons use SIGHUP to reload their configuration 
files at run-time. I guess that reason for that is the lack of proper control 
interface.

> Do you think it is better to move to ctrl_iface?

Once you have ctrl_iface in place, using it to reload a specific part of your 
configuration should make for a better interface consistency, IMHO. Especially 
so since ctrl_iface now supports SET/GET of arbitrary configuration 
parameters.  One advantage of ctrl_iface is that it can report failure (e.g.  
file not found, parsing failure) to the ctrl_iface client.

baruch
Antonio Quartulli June 11, 2012, 8:13 a.m. UTC | #4
On Mon, Jun 11, 2012 at 10:47:30AM +0300, Baruch Siach wrote:
> Hi Antonio,
> 
> On Mon, Jun 11, 2012 at 08:59:32AM +0200, Antonio Quartulli wrote:
> > On Mon, Jun 11, 2012 at 08:04:08AM +0300, Baruch Siach wrote:
> > > On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> > > > This patch allows the administrator to modify the provided MAC list for the
> > > > BSS ACL mechanism at runtime. In the current implementation no runtime change
> > > > is possible: if the MAC list is modified, hostapd has to be restart so
> > > > disconnecting all the current clients.
> > > > 
> > > > After manually modify the MAC files, the USR2 signal will make hostapd reload
> > > > them and possibly disconnect new not allowed stations.
> > > 
> > > Is there a reason not to use ctrl_iface?
> > 
> > mh..not really. I didn't think about that.
> > 
> > > It seems to me like a more natural interface than system global signal numbers.
> > 
> > In my mind I had the classic SIGHUP used to reload config files in general and
> > so I thought this was a good track.
> 
> Well, it is true that many daemons use SIGHUP to reload their configuration 
> files at run-time. I guess that reason for that is the lack of proper control 
> interface.
> 
> > Do you think it is better to move to ctrl_iface?
> 
> Once you have ctrl_iface in place, using it to reload a specific part of your 
> configuration should make for a better interface consistency, IMHO. Especially 
> so since ctrl_iface now supports SET/GET of arbitrary configuration 
> parameters.  One advantage of ctrl_iface is that it can report failure (e.g.  
> file not found, parsing failure) to the ctrl_iface client.

Well, that's true. But here I do not want to change any params, but I just want
to ask hostapd to re-read the files that it has read at startup. Moreover this
should probably be doable even if the ctrl_iface has not been activated.

Anyway, I'll think about that. Thank you for your feedback!

Cheers,
Antonio Quartulli June 13, 2012, 9:15 a.m. UTC | #5
Hello

On Mon, Jun 11, 2012 at 10:13:57AM +0200, Antonio Quartulli wrote:
> > Once you have ctrl_iface in place, using it to reload a specific part of your 
> > configuration should make for a better interface consistency, IMHO. Especially 
> > so since ctrl_iface now supports SET/GET of arbitrary configuration 
> > parameters.  One advantage of ctrl_iface is that it can report failure (e.g.  
> > file not found, parsing failure) to the ctrl_iface client.
> 
> Well, that's true. But here I do not want to change any params, but I just want
> to ask hostapd to re-read the files that it has read at startup. Moreover this
> should probably be doable even if the ctrl_iface has not been activated.
> 
> Anyway, I'll think about that. Thank you for your feedback!

Well, in the end I think it could be ok to use SIGUSR2..If the others agree I
would leave it as is.

Jouni? what do you think about it?

Thank you
Jouni Malinen June 17, 2012, 8:44 a.m. UTC | #6
On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> This patch allows the administrator to modify the provided MAC list for the
> BSS ACL mechanism at runtime. In the current implementation no runtime change
> is possible: if the MAC list is modified, hostapd has to be restart so
> disconnecting all the current clients.
> 
> After manually modify the MAC files, the USR2 signal will make hostapd reload
> them and possibly disconnect new not allowed stations.

Why is this needed? Doesn't SIGHUP already reload the files? Using a
USR2 signal would require more justification since it is the only
generic signal that remains available and I'm not sure whether this
functionality is really in need of a global signal (i.e., per-interface
ctrl_iface would be cleaner).

> diff --git a/hostapd/config_file.c b/hostapd/config_file.c
> @@ -1391,8 +1394,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
>  				wpa_printf(MSG_ERROR, "Line %d: unknown "
>  					   "macaddr_acl %d",
>  					   line, bss->macaddr_acl);
> +				bss->accept_mac_filename[0] = '\0';
> +				bss->deny_mac_filename[0] = '\0';
>  			}
>  		} else if (os_strcmp(buf, "accept_mac_file") == 0) {
> +			os_strncpy(bss->accept_mac_filename, pos, 256);
> +			bss->accept_mac_filename[255] = '\0';

os_strlcpy() should be used instead of those two calls.

> +int hostapd_reload_macfile(struct hostapd_iface *iface)
...
> +		/* reload accept mac file if any */
> +		if (bss->accept_mac_filename[0] != '\0') {
> +			wpa_printf(MSG_DEBUG, "Reading %s",
> +				   bss->accept_mac_filename);
> +			os_free(bss->accept_mac);
> +			bss->accept_mac = NULL;
> +			bss->num_accept_mac = 0;
> +			if (hostapd_config_read_maclist(
> +						bss->accept_mac_filename,
> +						&bss->accept_mac,
> +						&bss->num_accept_mac))
> +				wpa_printf(MSG_ERROR, "Failed to read "
> +					   "accept_mac_file '%s'",
> +					   bss->accept_mac_filename);
> +		}

Will that error leave hostapd running with empty MAC ACL list? If so, it
would be better to either leave the old list in use or prevent the use
of the BSS after such failure.

> +		/* deauthenticate listed stations */
> +		if (bss->macaddr_acl == ACCEPT_UNLESS_DENIED)
> +			for (j = 0; j < bss->num_deny_mac; j++)
> +				hostapd_deauth_station(iface->bss[i],
> +					bss->deny_mac[j].addr,
> +					WLAN_REASON_PREV_AUTH_NOT_VALID);

Is this sending Deauthentication frame to every MAC address that is in
the deny list even if those STAs are not authenticated at the moment?
That would expose the list by broadcasting it.. This does not sound
reasonable, i.e., only the STAs that are actually authenticated should
be deauthenticated.

> diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
> @@ -207,14 +207,15 @@ struct hostapd_bss_config {
>  	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
>  	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
>  					* frames */
> -
>  	enum {

Please do not include unrelated changes..

> diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
> @@ -106,7 +106,6 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
>  	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
>  }
>  
> -
>  int hostapd_reload_config(struct hostapd_iface *iface)

... same here..

> diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
> +void hostapd_free_sta(struct hostapd_data *hapd, u8 *addr, u16 reason)
> +{
> +	struct sta_info *sta, *prev;
> +
> +	prev = sta = hapd->sta_list;
> +
> +	while (sta && memcmp(sta->addr, addr, ETH_ALEN)) {
> +		prev = sta;
> +		sta = sta->next;
> +	}

Should use ap_get_sta() instead of another loop to go through the STA
list.

> +	if (sta == hapd->sta_list)
> +		hapd->sta_list = sta->next;
> +	else
> +		prev->next = sta->next;
> +
> +	hostapd_free_sta_info(hapd, sta, reason);

Why are the STA list pointers modified here instead of just leaving this
to ap_free_sta() -> ap_sta_list_del() that gets called from
hostapd_free_sta_info()?
Antonio Quartulli June 18, 2012, 9:21 p.m. UTC | #7
On Sun, Jun 17, 2012 at 11:44:54 +0300, Jouni Malinen wrote:
> On Mon, Jun 11, 2012 at 12:44:30AM +0200, Antonio Quartulli wrote:
> > This patch allows the administrator to modify the provided MAC list for the
> > BSS ACL mechanism at runtime. In the current implementation no runtime change
> > is possible: if the MAC list is modified, hostapd has to be restart so
> > disconnecting all the current clients.
> > 
> > After manually modify the MAC files, the USR2 signal will make hostapd reload
> > them and possibly disconnect new not allowed stations.
> 
> Why is this needed? Doesn't SIGHUP already reload the files? Using a
> USR2 signal would require more justification since it is the only
> generic signal that remains available and I'm not sure whether this
> functionality is really in need of a global signal (i.e., per-interface
> ctrl_iface would be cleaner).

Clients disconenctions is what I exactly would like to avoid.
If the mac-list is modified such that I added a new allowed station,
why should I disconnect all the other while updating the list?
Imagine hostapd is offering internet connectivity in a public hotspot...If I
were a client I would not be that happy ;P


> 
> > diff --git a/hostapd/config_file.c b/hostapd/config_file.c
> > @@ -1391,8 +1394,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
> >  				wpa_printf(MSG_ERROR, "Line %d: unknown "
> >  					   "macaddr_acl %d",
> >  					   line, bss->macaddr_acl);
> > +				bss->accept_mac_filename[0] = '\0';
> > +				bss->deny_mac_filename[0] = '\0';
> >  			}
> >  		} else if (os_strcmp(buf, "accept_mac_file") == 0) {
> > +			os_strncpy(bss->accept_mac_filename, pos, 256);
> > +			bss->accept_mac_filename[255] = '\0';
> 
> os_strlcpy() should be used instead of those two calls.

oh, thanks

> 
> > +int hostapd_reload_macfile(struct hostapd_iface *iface)
> ...
> > +		/* reload accept mac file if any */
> > +		if (bss->accept_mac_filename[0] != '\0') {
> > +			wpa_printf(MSG_DEBUG, "Reading %s",
> > +				   bss->accept_mac_filename);
> > +			os_free(bss->accept_mac);
> > +			bss->accept_mac = NULL;
> > +			bss->num_accept_mac = 0;
> > +			if (hostapd_config_read_maclist(
> > +						bss->accept_mac_filename,
> > +						&bss->accept_mac,
> > +						&bss->num_accept_mac))
> > +				wpa_printf(MSG_ERROR, "Failed to read "
> > +					   "accept_mac_file '%s'",
> > +					   bss->accept_mac_filename);
> > +		}
> 
> Will that error leave hostapd running with empty MAC ACL list? If so, it
> would be better to either leave the old list in use or prevent the use
> of the BSS after such failure.

I think that preventing this BSS to be used would be better. thanks

> 
> > +		/* deauthenticate listed stations */
> > +		if (bss->macaddr_acl == ACCEPT_UNLESS_DENIED)
> > +			for (j = 0; j < bss->num_deny_mac; j++)
> > +				hostapd_deauth_station(iface->bss[i],
> > +					bss->deny_mac[j].addr,
> > +					WLAN_REASON_PREV_AUTH_NOT_VALID);
> 
> Is this sending Deauthentication frame to every MAC address that is in
> the deny list even if those STAs are not authenticated at the moment?
> That would expose the list by broadcasting it.. This does not sound
> reasonable, i.e., only the STAs that are actually authenticated should
> be deauthenticated.

I definitely agree. Here comes the fact that I don't have a deep knowledge of
the code yet :(

> 
> > diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
> > @@ -207,14 +207,15 @@ struct hostapd_bss_config {
> >  	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
> >  	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
> >  					* frames */
> > -
> >  	enum {
> 
> Please do not include unrelated changes..

sorry, will remove

> 
> > diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
> > @@ -106,7 +106,6 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
> >  	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
> >  }
> >  
> > -
> >  int hostapd_reload_config(struct hostapd_iface *iface)
> 
> ... same here..
> 
> > diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
> > +void hostapd_free_sta(struct hostapd_data *hapd, u8 *addr, u16 reason)
> > +{
> > +	struct sta_info *sta, *prev;
> > +
> > +	prev = sta = hapd->sta_list;
> > +
> > +	while (sta && memcmp(sta->addr, addr, ETH_ALEN)) {
> > +		prev = sta;
> > +		sta = sta->next;
> > +	}
> 
> Should use ap_get_sta() instead of another loop to go through the STA
> list.
> 
> > +	if (sta == hapd->sta_list)
> > +		hapd->sta_list = sta->next;
> > +	else
> > +		prev->next = sta->next;
> > +
> > +	hostapd_free_sta_info(hapd, sta, reason);
> 
> Why are the STA list pointers modified here instead of just leaving this
> to ap_free_sta() -> ap_sta_list_del() that gets called from
> hostapd_free_sta_info()?

The idea here was to optimise, and instead of letting ap_free_sta() iterate over
the list each time, I was going through the list manually and deleting the sta
when needed

Thank you very much for your advise Jouni.

Regards,
Jouni Malinen June 30, 2012, 1:33 p.m. UTC | #8
On Mon, Jun 18, 2012 at 11:21:07PM +0200, Antonio Quartulli wrote:
> Clients disconenctions is what I exactly would like to avoid.
> If the mac-list is modified such that I added a new allowed station,
> why should I disconnect all the other while updating the list?

How frequently do you think the MAC ACL gets changed? In general, I
would not want to rely on MAC address -based authentication for
anything, so it is a bit difficult to understand why extra complexity
would be needed to optimize this operation.

> Imagine hostapd is offering internet connectivity in a public hotspot...If I
> were a client I would not be that happy ;P

Depends on how frequently this happens and well, for managed hotspots, I
would rather use RADIUS-based mechanisms for authentication/accounting
and Disconnect-Request to dynamically terminate an ongoing session.

> The idea here was to optimise, and instead of letting ap_free_sta() iterate over
> the list each time, I was going through the list manually and deleting the sta
> when needed

I've done optimizations on the code that was figuring out when a station
needs to be disconnected on configuration changes (including MAC ACL),
but that was removed since it was quite complex and there did not seem
to be enough justification to force the code to be maintained. This old
implementation is included in 0.5.x if you want to take a look at how
this was addressed:
http://w1.fi/cgi-bin/viewcvs.cgi/hostap/hostapd/reconfig.c?view=markup&pathrev=hostap_0_5_branch
diff mbox

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index eab8ad4..a52309d 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -19,6 +19,9 @@ 
 #include "radius/radius_client.h"
 #include "ap/wpa_auth.h"
 #include "ap/ap_config.h"
+#include "ap/hostapd.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/sta_info.h"
 #include "config_file.h"
 
 
@@ -1391,8 +1394,12 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 				wpa_printf(MSG_ERROR, "Line %d: unknown "
 					   "macaddr_acl %d",
 					   line, bss->macaddr_acl);
+				bss->accept_mac_filename[0] = '\0';
+				bss->deny_mac_filename[0] = '\0';
 			}
 		} else if (os_strcmp(buf, "accept_mac_file") == 0) {
+			os_strncpy(bss->accept_mac_filename, pos, 256);
+			bss->accept_mac_filename[255] = '\0';
 			if (hostapd_config_read_maclist(pos, &bss->accept_mac,
 							&bss->num_accept_mac))
 			{
@@ -1402,6 +1409,8 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 				errors++;
 			}
 		} else if (os_strcmp(buf, "deny_mac_file") == 0) {
+			os_strncpy(bss->deny_mac_filename, pos, 256);
+			bss->deny_mac_filename[255] = '\0';
 			if (hostapd_config_read_maclist(pos, &bss->deny_mac,
 							&bss->num_deny_mac)) {
 				wpa_printf(MSG_ERROR, "Line %d: Failed to "
@@ -2495,3 +2504,92 @@  int hostapd_set_iface(struct hostapd_config *conf,
 
 	return 0;
 }
+
+static void hostapd_deauth_station(struct hostapd_data *hapd, u8 *addr,
+				   u16 reason)
+{
+	if (hostapd_drv_none(hapd))
+		return;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate " MACSTR,
+		MAC2STR(addr));
+
+	hostapd_drv_sta_deauth(hapd, addr, reason);
+	hostapd_free_sta(hapd, addr, reason);
+}
+
+int hostapd_reload_macfile(struct hostapd_iface *iface)
+{
+	struct hostapd_bss_config *bss;
+	struct sta_info *sta, *prev, *next;
+	size_t i, j;
+
+	wpa_printf(MSG_DEBUG, "Reloading macfiles");
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i]->conf;
+
+		/* reload accept mac file if any */
+		if (bss->accept_mac_filename[0] != '\0') {
+			wpa_printf(MSG_DEBUG, "Reading %s",
+				   bss->accept_mac_filename);
+			os_free(bss->accept_mac);
+			bss->accept_mac = NULL;
+			bss->num_accept_mac = 0;
+			if (hostapd_config_read_maclist(
+						bss->accept_mac_filename,
+						&bss->accept_mac,
+						&bss->num_accept_mac))
+				wpa_printf(MSG_ERROR, "Failed to read "
+					   "accept_mac_file '%s'",
+					   bss->accept_mac_filename);
+		}
+
+		/* reload deny mac file if any */
+		if (bss->deny_mac_filename[0] != '\0') {
+			wpa_printf(MSG_DEBUG, "Reading %s",
+				   bss->deny_mac_filename);
+			os_free(bss->deny_mac);
+			bss->deny_mac = NULL;
+			bss->num_deny_mac = 0;
+			if (hostapd_config_read_maclist(bss->deny_mac_filename,
+							&bss->deny_mac,
+							&bss->num_deny_mac))
+				wpa_printf(MSG_ERROR, "Failed to read "
+					   "deny_mac_file '%s'",
+					   bss->deny_mac_filename);
+		}
+
+		/* deauthenticate listed stations */
+		if (bss->macaddr_acl == ACCEPT_UNLESS_DENIED)
+			for (j = 0; j < bss->num_deny_mac; j++)
+				hostapd_deauth_station(iface->bss[i],
+					bss->deny_mac[j].addr,
+					WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+		/* deauthenticate all the stations that are not listed */
+		if (bss->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+			prev = sta = iface->bss[i]->sta_list;
+			while (sta) {
+				next = sta->next;
+				for (j = 0; j < bss->num_accept_mac; j++)
+					if (!memcmp(sta->addr,
+						    bss->accept_mac[j].addr,
+						    ETH_ALEN))
+						break;
+				if (j == bss->num_accept_mac) {
+					if (iface->bss[i]->sta_list == sta)
+						iface->bss[i]->sta_list =
+								sta->next;
+					else
+						prev->next = sta->next;
+					hostapd_deauth_station(iface->bss[i],
+							       sta->addr,
+					       WLAN_REASON_PREV_AUTH_NOT_VALID);
+				} else
+					prev = sta;
+				sta = next;
+			}
+		}
+	}
+	return 0;
+}
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index fba57b8..90a306b 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -13,5 +13,6 @@  struct hostapd_config * hostapd_config_read(const char *fname);
 int hostapd_set_iface(struct hostapd_config *conf,
 		      struct hostapd_bss_config *bss, char *field,
 		      char *value);
+int hostapd_reload_macfile(struct hostapd_iface *iface);
 
 #endif /* CONFIG_FILE_H */
diff --git a/hostapd/main.c b/hostapd/main.c
index d8c2776..85a376a 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -363,6 +363,10 @@  static void handle_reload(int sig, void *signal_ctx)
 	hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
 }
 
+int handle_reload_macfile_iface(struct hostapd_iface *iface, void *ctx)
+{
+	return hostapd_reload_macfile(iface);
+}
 
 static void handle_dump_state(int sig, void *signal_ctx)
 {
@@ -371,6 +375,15 @@  static void handle_dump_state(int sig, void *signal_ctx)
 	hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL);
 #endif /* HOSTAPD_DUMP_STATE */
 }
+
+static void handle_reload_macfile(int sig, void *signal_ctx)
+{
+	struct hapd_interfaces *interfaces = signal_ctx;
+	wpa_printf(MSG_DEBUG, "USR2 catched, reloading macfiles..");
+	hostapd_for_each_interface(interfaces, handle_reload_macfile_iface,
+				   NULL);
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
@@ -398,6 +411,7 @@  static int hostapd_global_init(struct hapd_interfaces *interfaces,
 #ifndef CONFIG_NATIVE_WINDOWS
 	eloop_register_signal(SIGHUP, handle_reload, interfaces);
 	eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
+	eloop_register_signal(SIGUSR2, handle_reload_macfile, interfaces);
 #endif /* CONFIG_NATIVE_WINDOWS */
 	eloop_register_signal_terminate(handle_term, interfaces);
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 78c9068..8e23528 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -207,14 +207,15 @@  struct hostapd_bss_config {
 	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
 	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
 					* frames */
-
 	enum {
 		ACCEPT_UNLESS_DENIED = 0,
 		DENY_UNLESS_ACCEPTED = 1,
 		USE_EXTERNAL_RADIUS_AUTH = 2
 	} macaddr_acl;
+	char accept_mac_filename[256];
 	struct mac_acl_entry *accept_mac;
 	int num_accept_mac;
+	char deny_mac_filename[256];
 	struct mac_acl_entry *deny_mac;
 	int num_deny_mac;
 	int wds_sta;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9d6fd7b..d17b231 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -106,7 +106,6 @@  static void hostapd_reload_bss(struct hostapd_data *hapd)
 	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
 }
 
-
 int hostapd_reload_config(struct hostapd_iface *iface)
 {
 	struct hostapd_data *hapd = iface->bss[0];
@@ -379,7 +378,6 @@  static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 	return 0;
 }
 
-
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 {
 	int ret = 0;
@@ -402,7 +400,6 @@  static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 	return ret;
 }
 
-
 /**
  * hostapd_validate_bssid_configuration - Validate BSSID configuration
  * @iface: Pointer to interface data
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index a7dffc7..0a4c673 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -239,6 +239,38 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	os_free(sta);
 }
 
+void hostapd_free_sta_info(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason)
+{
+	if (sta->flags & WLAN_STA_AUTH) {
+		mlme_deauthenticate_indication(hapd, sta, reason);
+	}
+	wpa_printf(MSG_DEBUG, "Removing station " MACSTR, MAC2STR(sta->addr));
+	ap_free_sta(hapd, sta);
+}
+
+void hostapd_free_sta(struct hostapd_data *hapd, u8 *addr, u16 reason)
+{
+	struct sta_info *sta, *prev;
+
+	prev = sta = hapd->sta_list;
+
+	while (sta && memcmp(sta->addr, addr, ETH_ALEN)) {
+		prev = sta;
+		sta = sta->next;
+	}
+
+	/* station not found */
+	if (!sta)
+		return;
+
+	if (sta == hapd->sta_list)
+		hapd->sta_list = sta->next;
+	else
+		prev->next = sta->next;
+
+	hostapd_free_sta_info(hapd, sta, reason);
+}
 
 void hostapd_free_stas(struct hostapd_data *hapd)
 {
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index cef428d..2ebe2db 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -144,6 +144,7 @@  int ap_for_each_sta(struct hostapd_data *hapd,
 struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
 void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_sta(struct hostapd_data *hapd, u8 *addr, u16 reason);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
 void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
@@ -174,6 +175,8 @@  static inline int ap_sta_is_authorized(struct sta_info *sta)
 	return sta->flags & WLAN_STA_AUTHORIZED;
 }
 
+void hostapd_free_sta_info(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason);
 void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);