diff mbox

[10/10] qtnfmac: support MAC address based access control

Message ID 20171113102815.11254-11-sergey.matyukevich.os@quantenna.com (mailing list archive)
State Superseded
Delegated to: Kalle Valo
Headers show

Commit Message

Sergey Matyukevich Nov. 13, 2017, 10:28 a.m. UTC
From: Vasily Ulyanov <vulyanov@quantenna.com>

This allows a running AP to blacklist STAs by their MAC addresses
respecting the configured policy (either accept or deny unless listed).
It can be setup on .start_ap or with .set_mac_acl commands.

Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
---
 drivers/net/wireless/quantenna/qtnfmac/cfg80211.c  | 19 +++++++
 drivers/net/wireless/quantenna/qtnfmac/commands.c  | 62 ++++++++++++++++++++++
 drivers/net/wireless/quantenna/qtnfmac/commands.h  |  2 +
 drivers/net/wireless/quantenna/qtnfmac/core.h      |  1 +
 drivers/net/wireless/quantenna/qtnfmac/qlink.h     | 37 ++++++++++++-
 .../net/wireless/quantenna/qtnfmac/qlink_util.c    | 18 +++++++
 .../net/wireless/quantenna/qtnfmac/qlink_util.h    |  2 +
 7 files changed, 140 insertions(+), 1 deletion(-)

Comments

Kalle Valo Dec. 4, 2017, 3:01 p.m. UTC | #1
Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> writes:

> From: Vasily Ulyanov <vulyanov@quantenna.com>
>
> This allows a running AP to blacklist STAs by their MAC addresses
> respecting the configured policy (either accept or deny unless listed).
> It can be setup on .start_ap or with .set_mac_acl commands.
>
> Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>

[...]

> @@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
>  	wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
>  	wiphy->mgmt_stypes = qtnf_mgmt_stypes;
>  	wiphy->max_remain_on_channel_duration = 5000;
> +	wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
>  
>  	wiphy->iface_combinations = iface_comb;
>  	wiphy->n_iface_combinations = 1;
> @@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
>  			WIPHY_FLAG_AP_UAPSD |
>  			WIPHY_FLAG_HAS_CHANNEL_SWITCH;
>  
> +	if (wiphy->max_acl_mac_addrs > 0)
> +		wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;

Conditonally enabling WIPHY_FLAG_HAVE_AP_SME looks somewhat suspicious
to me and from a quick search I don't see any other driver doing
something similar. Can you explain why AP_SME is related to MAC ACL?
Sergey Matyukevich Dec. 5, 2017, 4 p.m. UTC | #2
Hello Kalle,

> Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> writes:
> 
> > From: Vasily Ulyanov <vulyanov@quantenna.com>
> >
> > This allows a running AP to blacklist STAs by their MAC addresses
> > respecting the configured policy (either accept or deny unless listed).
> > It can be setup on .start_ap or with .set_mac_acl commands.
> >
> > Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
> 
> [...]
> 
> > @@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> >       wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
> >       wiphy->mgmt_stypes = qtnf_mgmt_stypes;
> >       wiphy->max_remain_on_channel_duration = 5000;
> > +     wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
> >
> >       wiphy->iface_combinations = iface_comb;
> >       wiphy->n_iface_combinations = 1;
> > @@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> >                       WIPHY_FLAG_AP_UAPSD |
> >                       WIPHY_FLAG_HAS_CHANNEL_SWITCH;
> >
> > +     if (wiphy->max_acl_mac_addrs > 0)
> > +             wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
> 
> Conditonally enabling WIPHY_FLAG_HAVE_AP_SME looks somewhat suspicious
> to me and from a quick search I don't see any other driver doing
> something similar. Can you explain why AP_SME is related to MAC ACL?

Wireless core performs several sanity check on wiphy registration: see
wiphy_register implementation in net/wireless/core.c. One of those
checks is as follows: if max_acl_mac_addrs is non-zero, then two
conditions should be fulfilled:
- cfg80211 set_mac_acl callback should be available
- WIPHY_FLAG_HAVE_AP_SME should be set

The first condition is perfectly sane: it should be possible to
set MACs to enable ACL feature. The second condition is that clear
to me, but we have to comply in order to pass wiphy_registration.
I assume that it somehow related to hostapd logic, but I haven't
yet check that myself.

The conditional enablement of WIPHY_FLAG_HAVE_AP_SME is easy to
explain. We enable use firmware/hardware features to implement
MAC-based ACL. So we enable it only if firmware report non-zero
max_acl_mac_addrs value.

Regards,
Sergey
Sergey Matyukevich Dec. 18, 2017, 12:43 p.m. UTC | #3
Hello Kalle,

> > > This allows a running AP to blacklist STAs by their MAC addresses
> > > respecting the configured policy (either accept or deny unless listed).
> > > It can be setup on .start_ap or with .set_mac_acl commands.
> > >
> > > Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
> >
> > [...]
> >
> > > @@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> > >       wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
> > >       wiphy->mgmt_stypes = qtnf_mgmt_stypes;
> > >       wiphy->max_remain_on_channel_duration = 5000;
> > > +     wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
> > >
> > >       wiphy->iface_combinations = iface_comb;
> > >       wiphy->n_iface_combinations = 1;
> > > @@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> > >                       WIPHY_FLAG_AP_UAPSD |
> > >                       WIPHY_FLAG_HAS_CHANNEL_SWITCH;
> > >
> > > +     if (wiphy->max_acl_mac_addrs > 0)
> > > +             wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
> >
> > Conditonally enabling WIPHY_FLAG_HAVE_AP_SME looks somewhat suspicious
> > to me and from a quick search I don't see any other driver doing
> > something similar. Can you explain why AP_SME is related to MAC ACL?
> 
> Wireless core performs several sanity check on wiphy registration: see
> wiphy_register implementation in net/wireless/core.c. One of those
> checks is as follows: if max_acl_mac_addrs is non-zero, then two
> conditions should be fulfilled:
> - cfg80211 set_mac_acl callback should be available
> - WIPHY_FLAG_HAVE_AP_SME should be set
> 
> The first condition is perfectly sane: it should be possible to
> set MACs to enable ACL feature. The second condition is that clear
> to me, but we have to comply in order to pass wiphy_registration.
> I assume that it somehow related to hostapd logic, but I haven't
> yet check that myself.
> 
> The conditional enablement of WIPHY_FLAG_HAVE_AP_SME is easy to
> explain. We enable use firmware/hardware features to implement
> MAC-based ACL. So we enable it only if firmware report non-zero
> max_acl_mac_addrs value.

I noticed that this patch series is marked as 'Deferred' in linux-wireless
patchwork. Could you please take a look at my answers to your review comments.
Let us know if you still have concerns, so that we could proceed accordingly,
e.g. fix things and resubmit this series.

Regards,
Sergey
Kalle Valo Dec. 18, 2017, 2:01 p.m. UTC | #4
Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> writes:

> Hello Kalle,
>
>> Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> writes:
>> 
>> > From: Vasily Ulyanov <vulyanov@quantenna.com>
>> >
>> > This allows a running AP to blacklist STAs by their MAC addresses
>> > respecting the configured policy (either accept or deny unless listed).
>> > It can be setup on .start_ap or with .set_mac_acl commands.
>> >
>> > Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
>> 
>> [...]
>> 
>> > @@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
>> >       wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
>> >       wiphy->mgmt_stypes = qtnf_mgmt_stypes;
>> >       wiphy->max_remain_on_channel_duration = 5000;
>> > +     wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
>> >
>> >       wiphy->iface_combinations = iface_comb;
>> >       wiphy->n_iface_combinations = 1;
>> > @@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
>> >                       WIPHY_FLAG_AP_UAPSD |
>> >                       WIPHY_FLAG_HAS_CHANNEL_SWITCH;
>> >
>> > +     if (wiphy->max_acl_mac_addrs > 0)
>> > +             wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
>> 
>> Conditonally enabling WIPHY_FLAG_HAVE_AP_SME looks somewhat suspicious
>> to me and from a quick search I don't see any other driver doing
>> something similar. Can you explain why AP_SME is related to MAC ACL?
>
> Wireless core performs several sanity check on wiphy registration: see
> wiphy_register implementation in net/wireless/core.c. One of those
> checks is as follows: if max_acl_mac_addrs is non-zero, then two
> conditions should be fulfilled:
> - cfg80211 set_mac_acl callback should be available
> - WIPHY_FLAG_HAVE_AP_SME should be set
>
> The first condition is perfectly sane: it should be possible to
> set MACs to enable ACL feature. The second condition is that clear
> to me, but we have to comply in order to pass wiphy_registration.
> I assume that it somehow related to hostapd logic, but I haven't
> yet check that myself.
>
> The conditional enablement of WIPHY_FLAG_HAVE_AP_SME is easy to
> explain. We enable use firmware/hardware features to implement
> MAC-based ACL. So we enable it only if firmware report non-zero
> max_acl_mac_addrs value.

To me this looks like an ugly hack, either your firmware has AP_SME
support or not. It should not be enabled based on what settings user
space provides. If cfg80211 is giving you problems you should fix
cfg80211, not try to a workaround it in the driver like the vendor
drivers do. We work differently in upstream.

But Johannes (CCed) might think differently, and if he acks this, then
I'll of course take this.
Sergey Matyukevich Dec. 18, 2017, 4:18 p.m. UTC | #5
Hello Kalle,

> >> > This allows a running AP to blacklist STAs by their MAC addresses
> >> > respecting the configured policy (either accept or deny unless listed).
> >> > It can be setup on .start_ap or with .set_mac_acl commands.
> >> >
> >> > Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
> >>
> >> [...]
> >>
> >> > @@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> >> >       wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
> >> >       wiphy->mgmt_stypes = qtnf_mgmt_stypes;
> >> >       wiphy->max_remain_on_channel_duration = 5000;
> >> > +     wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
> >> >
> >> >       wiphy->iface_combinations = iface_comb;
> >> >       wiphy->n_iface_combinations = 1;
> >> > @@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
> >> >                       WIPHY_FLAG_AP_UAPSD |
> >> >                       WIPHY_FLAG_HAS_CHANNEL_SWITCH;
> >> >
> >> > +     if (wiphy->max_acl_mac_addrs > 0)
> >> > +             wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
> >>
> >> Conditonally enabling WIPHY_FLAG_HAVE_AP_SME looks somewhat suspicious
> >> to me and from a quick search I don't see any other driver doing
> >> something similar. Can you explain why AP_SME is related to MAC ACL?
> >
> > Wireless core performs several sanity check on wiphy registration: see
> > wiphy_register implementation in net/wireless/core.c. One of those
> > checks is as follows: if max_acl_mac_addrs is non-zero, then two
> > conditions should be fulfilled:
> > - cfg80211 set_mac_acl callback should be available
> > - WIPHY_FLAG_HAVE_AP_SME should be set
> >
> > The first condition is perfectly sane: it should be possible to
> > set MACs to enable ACL feature. The second condition is that clear
> > to me, but we have to comply in order to pass wiphy_registration.
> > I assume that it somehow related to hostapd logic, but I haven't
> > yet check that myself.
> >
> > The conditional enablement of WIPHY_FLAG_HAVE_AP_SME is easy to
> > explain. We enable use firmware/hardware features to implement
> > MAC-based ACL. So we enable it only if firmware report non-zero
> > max_acl_mac_addrs value.
> 
> To me this looks like an ugly hack, either your firmware has AP_SME
> support or not. It should not be enabled based on what settings user
> space provides. If cfg80211 is giving you problems you should fix
> cfg80211, not try to a workaround it in the driver like the vendor
> drivers do. We work differently in upstream.
> 
> But Johannes (CCed) might think differently, and if he acks this, then
> I'll of course take this.

I took a closer look at this patch. It turns out that this conditional
enablement of WIPHY_FLAG_HAVE_AP_SME is completely redundant here. This
capability is already enabled unconditionally (though not visible in diff)
for normal hostapd operations with FullMAC driver which mostly
handles STAs in FW.

Thanks for catching. In fact, those two lines should be removed.
I will update the patch and resubmit the whole series.


Meanwhile now it is not yet clear to me what should be done for driver which
supports MAC-based ACL, but not full-fledged AP SME. Wireless core expects
WIPHY_FLAG_HAVE_AP_SME to be set if driver supports MAC-based ACL. On the
other hand, hostapd handles WIPHY_FLAG_HAVE_AP_SME and max_acl_mac_addrs
independently, expecting certain things from drivers that advertise
WIPHY_FLAG_HAVE_AP_SME.

Regards,
Sergey
Johannes Berg Dec. 19, 2017, 9:38 a.m. UTC | #6
On Mon, 2017-12-18 at 19:18 +0300, Sergey Matyukevich wrote:

> Meanwhile now it is not yet clear to me what should be done for driver which
> supports MAC-based ACL, but not full-fledged AP SME.

Are you sure that such a device can even exist? It'd have to drop the
auth frames, so they can't be handled by the host? Is there much point
in that?

johannes
Sergey Matyukevich Dec. 19, 2017, 10:29 a.m. UTC | #7
Hello Johannes,

> > Meanwhile now it is not yet clear to me what should be done for driver which
> > supports MAC-based ACL, but not full-fledged AP SME.
> 
> Are you sure that such a device can even exist? It'd have to drop the
> auth frames, so they can't be handled by the host? Is there much point
> in that?

I guess it should be possible to do some kind of source address filtering
in hardware. But it looks like your question is whether it makes sense
or not. Probably not, I have no idea.

By the way, what do you think about making MAC-based ACL capability the
first bit in enum nl80211_ap_sme_features ?

Regards,
Sergey
Johannes Berg Dec. 19, 2017, 10:35 a.m. UTC | #8
Hi,

> I guess it should be possible to do some kind of source address filtering
> in hardware. But it looks like your question is whether it makes sense
> or not. Probably not, I have no idea.

Either way, I see no reason to support it if nobody has a driver for it
:)

> By the way, what do you think about making MAC-based ACL capability the
> first bit in enum nl80211_ap_sme_features ?

No issues with that; I thought we already had some ACL feature bit
though?

johannes
Sergey Matyukevich Dec. 19, 2017, 10:42 a.m. UTC | #9
Hello Johannes,

> > I guess it should be possible to do some kind of source address filtering
> > in hardware. But it looks like your question is whether it makes sense
> > or not. Probably not, I have no idea.
> 
> Either way, I see no reason to support it if nobody has a driver for it
> :)
> 
> > By the way, what do you think about making MAC-based ACL capability the
> > first bit in enum nl80211_ap_sme_features ?
> 
> No issues with that; I thought we already had some ACL feature bit
> though?

Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
is commented out. For MAC-based ACL the following things are being checked
on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
max_acl_mac_addrs, and set_mac_acl cfg80211 callback.

I will send an RFC patch, lets see how it is going to look like...

Regards,
Sergey
Johannes Berg Dec. 19, 2017, 10:59 a.m. UTC | #10
On Tue, 2017-12-19 at 13:42 +0300, Sergey Matyukevich wrote:

> Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
> is commented out. For MAC-based ACL the following things are being checked
> on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
> max_acl_mac_addrs, and set_mac_acl cfg80211 callback.

I guess that's enough then? Userspace can check max_acl_mac_addrs as
well, so it can just use that?

johannes
Sergey Matyukevich Dec. 19, 2017, 11:19 a.m. UTC | #11
> > Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
> > is commented out. For MAC-based ACL the following things are being checked
> > on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
> > max_acl_mac_addrs, and set_mac_acl cfg80211 callback.
> 
> I guess that's enough then? Userspace can check max_acl_mac_addrs as
> well, so it can just use that?

Correct, that is what hostapd is doing. I was simply surprised by the fact
that MAC-based ACL support implies full-fledged AP SME support. Though
your almost convinced me that this is ok and other wireless cards simply
do not exist.

Regards,
Sergey
Arend van Spriel Dec. 19, 2017, 12:37 p.m. UTC | #12
On 12/19/2017 12:19 PM, Sergey Matyukevich wrote:
>>> Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
>>> is commented out. For MAC-based ACL the following things are being checked
>>> on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
>>> max_acl_mac_addrs, and set_mac_acl cfg80211 callback.
>>
>> I guess that's enough then? Userspace can check max_acl_mac_addrs as
>> well, so it can just use that?
>
> Correct, that is what hostapd is doing. I was simply surprised by the fact
> that MAC-based ACL support implies full-fledged AP SME support. Though
> your almost convinced me that this is ok and other wireless cards simply
> do not exist.

So the question seems to be here: what shall drivers/firmware implement 
to allow flag WIPHY_FLAG_HAVE_AP_SME being set. The kerneldoc is a bit 
short in providing guidance:

  * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME

Regards,
Arend
Johannes Berg Dec. 19, 2017, 4:58 p.m. UTC | #13
On Tue, 2017-12-19 at 13:37 +0100, Arend van Spriel wrote:
> On 12/19/2017 12:19 PM, Sergey Matyukevich wrote:
> > > > Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
> > > > is commented out. For MAC-based ACL the following things are being checked
> > > > on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
> > > > max_acl_mac_addrs, and set_mac_acl cfg80211 callback.
> > > 
> > > I guess that's enough then? Userspace can check max_acl_mac_addrs as
> > > well, so it can just use that?
> > 
> > Correct, that is what hostapd is doing. I was simply surprised by the fact
> > that MAC-based ACL support implies full-fledged AP SME support. Though
> > your almost convinced me that this is ok and other wireless cards simply
> > do not exist.
> 
> So the question seems to be here: what shall drivers/firmware implement 
> to allow flag WIPHY_FLAG_HAVE_AP_SME being set. The kerneldoc is a bit 
> short in providing guidance:
> 
>   * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME

They should implement the AP SME? :)

That is, handling auth/assoc/etc.

With the SAE-"offload"-to-host those lines are blurring again though.

johannes
Arend van Spriel Dec. 19, 2017, 10:13 p.m. UTC | #14
On Tue, Dec 19, 2017 at 5:58 PM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> On Tue, 2017-12-19 at 13:37 +0100, Arend van Spriel wrote:
>> On 12/19/2017 12:19 PM, Sergey Matyukevich wrote:
>> > > > Not yet. At the moment enum nl80211_ap_sme_features in uapi/linux/nl80211.h
>> > > > is commented out. For MAC-based ACL the following things are being checked
>> > > > on wiphy registration: complete flag WIPHY_FLAG_HAVE_AP_SME, non-zero
>> > > > max_acl_mac_addrs, and set_mac_acl cfg80211 callback.
>> > >
>> > > I guess that's enough then? Userspace can check max_acl_mac_addrs as
>> > > well, so it can just use that?
>> >
>> > Correct, that is what hostapd is doing. I was simply surprised by the fact
>> > that MAC-based ACL support implies full-fledged AP SME support. Though
>> > your almost convinced me that this is ok and other wireless cards simply
>> > do not exist.
>>
>> So the question seems to be here: what shall drivers/firmware implement
>> to allow flag WIPHY_FLAG_HAVE_AP_SME being set. The kerneldoc is a bit
>> short in providing guidance:
>>
>>   * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME
>
> They should implement the AP SME? :)
>
> That is, handling auth/assoc/etc.

So basically everything to setup 802.11 connection. So what about the
.*_station() callbacks? Anyway, I can understand that people start
looking at the checks done in wiphy_register() as a last resort in
finding (some sort of) documentation.

> With the SAE-"offload"-to-host those lines are blurring again though.

Yeah. Thanks for (inadvertently) reminding me to chime in on that.

Regards,
Arend
diff mbox

Patch

diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 452def343ad3..89dc9cab647d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -778,6 +778,20 @@  static int qtnf_start_radar_detection(struct wiphy *wiphy,
 	return ret;
 }
 
+static int qtnf_set_mac_acl(struct wiphy *wiphy,
+			    struct net_device *dev,
+			    const struct cfg80211_acl_data *params)
+{
+	struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
+	int ret;
+
+	ret = qtnf_cmd_set_mac_acl(vif, params);
+	if (ret)
+		pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret);
+
+	return ret;
+}
+
 static struct cfg80211_ops qtn_cfg80211_ops = {
 	.add_virtual_intf	= qtnf_add_virtual_intf,
 	.change_virtual_intf	= qtnf_change_virtual_intf,
@@ -803,6 +817,7 @@  static struct cfg80211_ops qtn_cfg80211_ops = {
 	.get_channel		= qtnf_get_channel,
 	.channel_switch		= qtnf_channel_switch,
 	.start_radar_detection	= qtnf_start_radar_detection,
+	.set_mac_acl		= qtnf_set_mac_acl,
 };
 
 static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
@@ -918,6 +933,7 @@  int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
 	wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
 	wiphy->mgmt_stypes = qtnf_mgmt_stypes;
 	wiphy->max_remain_on_channel_duration = 5000;
+	wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
 
 	wiphy->iface_combinations = iface_comb;
 	wiphy->n_iface_combinations = 1;
@@ -932,6 +948,9 @@  int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
 			WIPHY_FLAG_AP_UAPSD |
 			WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
+	if (wiphy->max_acl_mac_addrs > 0)
+		wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
+
 	wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
 				    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2;
 
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index 38b9c1078058..6ffe4837bbdb 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -162,6 +162,14 @@  static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type,
 		memcpy(tlv->ie_data, buf, len);
 }
 
+static inline size_t qtnf_cmd_acl_data_size(const struct cfg80211_acl_data *acl)
+{
+	size_t size = sizeof(struct qlink_acl_data) +
+		      acl->n_acl_entries * sizeof(struct qlink_mac_address);
+
+	return size;
+}
+
 static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
 				      const struct cfg80211_ap_settings *s)
 {
@@ -178,6 +186,9 @@  static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
 	if (cfg80211_chandef_valid(&s->chandef))
 		len += sizeof(struct qlink_tlv_chandef);
 
+	if (s->acl)
+		len += qtnf_cmd_acl_data_size(s->acl);
+
 	if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
 		pr_err("VIF%u.%u: can not fit AP settings: %u\n",
 		       vif->mac->macid, vif->vifid, len);
@@ -283,6 +294,16 @@  int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
 		memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
 	}
 
+	if (s->acl) {
+		size_t acl_size = qtnf_cmd_acl_data_size(s->acl);
+		struct qlink_tlv_hdr *tlv =
+			skb_put(cmd_skb, sizeof(*tlv) + acl_size);
+
+		tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
+		tlv->len = cpu_to_le16(acl_size);
+		qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val);
+	}
+
 	qtnf_bus_lock(vif->mac->bus);
 
 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code);
@@ -1206,6 +1227,7 @@  qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac,
 	mac_info->radar_detect_widths =
 			qlink_chan_width_mask_to_nl(le16_to_cpu(
 					resp_info->radar_detect_widths));
+	mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs);
 
 	memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask,
 	       sizeof(mac_info->ht_cap_mod_mask));
@@ -2609,3 +2631,43 @@  int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
 
 	return ret;
 }
+
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+			 const struct cfg80211_acl_data *params)
+{
+	struct qtnf_bus *bus = vif->mac->bus;
+	struct sk_buff *cmd_skb;
+	struct qlink_cmd_set_mac_acl *cmd;
+	u16 res_code;
+	int ret;
+
+	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
+					    QLINK_CMD_SET_MAC_ACL,
+					    sizeof(*cmd) +
+					    qtnf_cmd_acl_data_size(params));
+	if (unlikely(!cmd_skb))
+		return -ENOMEM;
+
+	cmd = (struct qlink_cmd_set_mac_acl *)cmd_skb->data;
+	qlink_acl_data_cfg2q(params, &cmd->acl);
+
+	qtnf_bus_lock(bus);
+	ret = qtnf_cmd_send(bus, cmd_skb, &res_code);
+	qtnf_bus_unlock(bus);
+
+	if (unlikely(ret))
+		return ret;
+
+	switch (res_code) {
+	case QLINK_CMD_RESULT_OK:
+		break;
+	case QLINK_CMD_RESULT_INVALID:
+		ret = -EINVAL;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h
index 07a957af9a58..69a7d56f7e58 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h
@@ -79,5 +79,7 @@  int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef);
 int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
 		       const struct cfg80211_chan_def *chdef,
 		       u32 cac_time_ms);
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+			 const struct cfg80211_acl_data *params);
 
 #endif /* QLINK_COMMANDS_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index e7bd21ed371b..c10900162297 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -103,6 +103,7 @@  struct qtnf_mac_info {
 	u8 sretry_limit;
 	u8 coverage_class;
 	u8 radar_detect_widths;
+	u32 max_acl_mac_addrs;
 	struct ieee80211_ht_cap ht_cap_mod_mask;
 	struct ieee80211_vht_cap vht_cap_mod_mask;
 	struct ieee80211_iface_limit *limits;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 5d98000b0f5b..6a1f960228a1 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -19,7 +19,7 @@ 
 
 #include <linux/ieee80211.h>
 
-#define QLINK_PROTO_VER		9
+#define QLINK_PROTO_VER		10
 
 #define QLINK_MACID_RSVD		0xFF
 #define QLINK_VIFID_RSVD		0xFF
@@ -239,6 +239,7 @@  enum qlink_cmd_type {
 	QLINK_CMD_START_CAC		= 0x001D,
 	QLINK_CMD_START_AP		= 0x0021,
 	QLINK_CMD_STOP_AP		= 0x0022,
+	QLINK_CMD_SET_MAC_ACL		= 0x0023,
 	QLINK_CMD_GET_STA_INFO		= 0x0030,
 	QLINK_CMD_ADD_KEY		= 0x0040,
 	QLINK_CMD_DEL_KEY		= 0x0041,
@@ -640,6 +641,38 @@  struct qlink_cmd_start_cac {
 	__le32 cac_time_ms;
 } __packed;
 
+enum qlink_acl_policy {
+	QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+	QLINK_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+struct qlink_mac_address {
+	u8 addr[ETH_ALEN];
+} __packed;
+
+/**
+ * struct qlink_acl_data - ACL data
+ *
+ * @policy: filter policy, one of &enum qlink_acl_policy.
+ * @num_entries: number of MAC addresses in array.
+ * @mac_address: MAC addresses array.
+ */
+struct qlink_acl_data {
+	__le32 policy;
+	__le32 num_entries;
+	struct qlink_mac_address mac_addrs[0];
+} __packed;
+
+/**
+ * struct qlink_cmd_set_mac_acl - data for QLINK_CMD_SET_MAC_ACL command
+ *
+ * @acl: ACL data.
+ */
+struct qlink_cmd_set_mac_acl {
+	struct qlink_cmd chdr;
+	struct qlink_acl_data acl;
+} __packed;
+
 /* QLINK Command Responses messages related definitions
  */
 
@@ -701,6 +734,7 @@  struct qlink_resp_get_mac_info {
 	struct ieee80211_ht_cap ht_cap_mod_mask;
 	__le16 max_ap_assoc_sta;
 	__le16 radar_detect_widths;
+	__le32 max_acl_mac_addrs;
 	u8 bands_cap;
 	u8 rsvd[1];
 	u8 var_info[0];
@@ -1049,6 +1083,7 @@  enum qlink_tlv_id {
 	QTN_TLV_ID_SEQ			= 0x0303,
 	QTN_TLV_ID_IE_SET		= 0x0305,
 	QTN_TLV_ID_EXT_CAPABILITY_MASK	= 0x0306,
+	QTN_TLV_ID_ACL_DATA		= 0x0307,
 };
 
 struct qlink_tlv_hdr {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
index 19981d6440b6..aeeda81b09ea 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
@@ -176,3 +176,21 @@  bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
 
 	return arr[idx] & mask;
 }
+
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+			  struct qlink_acl_data *qacl)
+{
+	switch (acl->acl_policy) {
+	case NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED:
+		qacl->policy =
+			cpu_to_le32(QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED);
+		break;
+	case NL80211_ACL_POLICY_DENY_UNLESS_LISTED:
+		qacl->policy = cpu_to_le32(QLINK_ACL_POLICY_DENY_UNLESS_LISTED);
+		break;
+	}
+
+	qacl->num_entries = cpu_to_le32(acl->n_acl_entries);
+	memcpy(qacl->mac_addrs, acl->mac_addrs,
+	       acl->n_acl_entries * sizeof(*qacl->mac_addrs));
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
index 6c24561eb41f..54caeb38917c 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
@@ -71,5 +71,7 @@  void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef,
 enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val);
 bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
 			   unsigned int arr_max_len);
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+			  struct qlink_acl_data *qacl);
 
 #endif /* _QTN_FMAC_QLINK_UTIL_H_ */