diff mbox

ath9k_htc: properly set MAC address and BSSID mask

Message ID 52972749.70200@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Mathy Vanhoef Nov. 28, 2013, 11:21 a.m. UTC
Third time's the charm? 
--
From: "Mathy Vanhoef" <vanhoefm@gmail.com>

Pick the MAC address of the first virtual interface as the new hardware MAC
address. Set BSSID mask according to this MAC address. This fixes CVE-2013-4579.

Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
---



--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

John W. Linville Dec. 6, 2013, 4:25 p.m. UTC | #1
Can we get some comments on this patch?  It is attempting to address
a CVE.

John

On Thu, Nov 28, 2013 at 12:21:45PM +0100, Mathy Vanhoef wrote:
> Third time's the charm? 
> --
> From: "Mathy Vanhoef" <vanhoefm@gmail.com>
> 
> Pick the MAC address of the first virtual interface as the new hardware MAC
> address. Set BSSID mask according to this MAC address. This fixes CVE-2013-4579.
> 
> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
> ---
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> index d441045..84359c3 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> @@ -147,21 +147,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
>  	struct ath9k_vif_iter_data *iter_data = data;
>  	int i;
>  
> -	for (i = 0; i < ETH_ALEN; i++)
> -		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
> +	if (iter_data->hw_macaddr != NULL) {
> +		for (i = 0; i < ETH_ALEN; i++)
> +			iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
> +	} else {
> +		iter_data->hw_macaddr = mac;
> +	}
>  }
>  
> -static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
> +static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
>  				     struct ieee80211_vif *vif)
>  {
>  	struct ath_common *common = ath9k_hw_common(priv->ah);
>  	struct ath9k_vif_iter_data iter_data;
>  
>  	/*
> -	 * Use the hardware MAC address as reference, the hardware uses it
> -	 * together with the BSSID mask when matching addresses.
> +	 * Pick the MAC address of the first interface as the new hardware
> +	 * MAC address. The hardware will use it together with the BSSID mask
> +	 * when matching addresses.
>  	 */
> -	iter_data.hw_macaddr = common->macaddr;
> +	iter_data.hw_macaddr = NULL;
>  	memset(&iter_data.mask, 0xff, ETH_ALEN);
>  
>  	if (vif)
> @@ -173,6 +178,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>  		ath9k_htc_bssid_iter, &iter_data);
>  
>  	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
> +
> +	if (iter_data.hw_macaddr)
> +		memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
> +
>  	ath_hw_setbssidmask(common);
>  }
>  
> @@ -1083,7 +1092,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
>  		goto out;
>  	}
>  
> -	ath9k_htc_set_bssid_mask(priv, vif);
> +	ath9k_htc_set_mac_bssid_mask(priv, vif);
>  
>  	priv->vif_slot |= (1 << avp->index);
>  	priv->nvifs++;
> @@ -1148,7 +1157,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
>  
>  	ath9k_htc_set_opmode(priv);
>  
> -	ath9k_htc_set_bssid_mask(priv, vif);
> +	ath9k_htc_set_mac_bssid_mask(priv, vif);
>  
>  	/*
>  	 * Stop ANI only if there are no associated station interfaces.
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index b6aad69..99ab0aa 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -885,8 +885,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
>  	struct ath_common *common = ath9k_hw_common(ah);
>  
>  	/*
> -	 * Use the hardware MAC address as reference, the hardware uses it
> -	 * together with the BSSID mask when matching addresses.
> +	 * Pick the MAC address of the first interface as the new hardware
> +	 * MAC address. The hardware will use it together with the BSSID mask
> +	 * when matching addresses.
>  	 */
>  	memset(iter_data, 0, sizeof(*iter_data));
>  	memset(&iter_data->mask, 0xff, ETH_ALEN);
> 
> 
> 
>
John W. Linville Dec. 6, 2013, 4:29 p.m. UTC | #2
On Thu, Nov 28, 2013 at 12:21:45PM +0100, Mathy Vanhoef wrote:
> Third time's the charm? 
> --
> From: "Mathy Vanhoef" <vanhoefm@gmail.com>
> 
> Pick the MAC address of the first virtual interface as the new hardware MAC
> address. Set BSSID mask according to this MAC address. This fixes CVE-2013-4579.
> 
> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
> ---
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> index d441045..84359c3 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
> @@ -147,21 +147,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
>  	struct ath9k_vif_iter_data *iter_data = data;
>  	int i;
>  
> -	for (i = 0; i < ETH_ALEN; i++)
> -		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
> +	if (iter_data->hw_macaddr != NULL) {
> +		for (i = 0; i < ETH_ALEN; i++)
> +			iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
> +	} else {
> +		iter_data->hw_macaddr = mac;
> +	}
>  }
>  
> -static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
> +static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
>  				     struct ieee80211_vif *vif)
>  {

I'm not sure I see the point of renaming this function -- just
personal preference?

>  	struct ath_common *common = ath9k_hw_common(priv->ah);
>  	struct ath9k_vif_iter_data iter_data;
>  
>  	/*
> -	 * Use the hardware MAC address as reference, the hardware uses it
> -	 * together with the BSSID mask when matching addresses.
> +	 * Pick the MAC address of the first interface as the new hardware
> +	 * MAC address. The hardware will use it together with the BSSID mask
> +	 * when matching addresses.
>  	 */
> -	iter_data.hw_macaddr = common->macaddr;
> +	iter_data.hw_macaddr = NULL;
>  	memset(&iter_data.mask, 0xff, ETH_ALEN);
>  
>  	if (vif)
> @@ -173,6 +178,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>  		ath9k_htc_bssid_iter, &iter_data);
>  
>  	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
> +
> +	if (iter_data.hw_macaddr)
> +		memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
> +
>  	ath_hw_setbssidmask(common);
>  }
>  
> @@ -1083,7 +1092,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
>  		goto out;
>  	}
>  
> -	ath9k_htc_set_bssid_mask(priv, vif);
> +	ath9k_htc_set_mac_bssid_mask(priv, vif);
>  
>  	priv->vif_slot |= (1 << avp->index);
>  	priv->nvifs++;
> @@ -1148,7 +1157,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
>  
>  	ath9k_htc_set_opmode(priv);
>  
> -	ath9k_htc_set_bssid_mask(priv, vif);
> +	ath9k_htc_set_mac_bssid_mask(priv, vif);
>  
>  	/*
>  	 * Stop ANI only if there are no associated station interfaces.
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index b6aad69..99ab0aa 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -885,8 +885,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
>  	struct ath_common *common = ath9k_hw_common(ah);
>  
>  	/*
> -	 * Use the hardware MAC address as reference, the hardware uses it
> -	 * together with the BSSID mask when matching addresses.
> +	 * Pick the MAC address of the first interface as the new hardware
> +	 * MAC address. The hardware will use it together with the BSSID mask
> +	 * when matching addresses.
>  	 */
>  	memset(iter_data, 0, sizeof(*iter_data));
>  	memset(&iter_data->mask, 0xff, ETH_ALEN);
> 
> 
> 
>
Mathy Vanhoef Dec. 6, 2013, 8:30 p.m. UTC | #3
It was renamed to make it clear the function now changes both the
BSSID and MAC registers.

On Fri, Dec 6, 2013 at 5:29 PM, John W. Linville <linville@tuxdriver.com> wrote:
> On Thu, Nov 28, 2013 at 12:21:45PM +0100, Mathy Vanhoef wrote:
>> Third time's the charm?
>> --
>> From: "Mathy Vanhoef" <vanhoefm@gmail.com>
>>
>> Pick the MAC address of the first virtual interface as the new hardware MAC
>> address. Set BSSID mask according to this MAC address. This fixes CVE-2013-4579.
>>
>> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
>> ---
>> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>> index d441045..84359c3 100644
>> --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>> @@ -147,21 +147,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
>>       struct ath9k_vif_iter_data *iter_data = data;
>>       int i;
>>
>> -     for (i = 0; i < ETH_ALEN; i++)
>> -             iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
>> +     if (iter_data->hw_macaddr != NULL) {
>> +             for (i = 0; i < ETH_ALEN; i++)
>> +                     iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
>> +     } else {
>> +             iter_data->hw_macaddr = mac;
>> +     }
>>  }
>>
>> -static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>> +static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
>>                                    struct ieee80211_vif *vif)
>>  {
>
> I'm not sure I see the point of renaming this function -- just
> personal preference?
>
>>       struct ath_common *common = ath9k_hw_common(priv->ah);
>>       struct ath9k_vif_iter_data iter_data;
>>
>>       /*
>> -      * Use the hardware MAC address as reference, the hardware uses it
>> -      * together with the BSSID mask when matching addresses.
>> +      * Pick the MAC address of the first interface as the new hardware
>> +      * MAC address. The hardware will use it together with the BSSID mask
>> +      * when matching addresses.
>>        */
>> -     iter_data.hw_macaddr = common->macaddr;
>> +     iter_data.hw_macaddr = NULL;
>>       memset(&iter_data.mask, 0xff, ETH_ALEN);
>>
>>       if (vif)
>> @@ -173,6 +178,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>>               ath9k_htc_bssid_iter, &iter_data);
>>
>>       memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
>> +
>> +     if (iter_data.hw_macaddr)
>> +             memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
>> +
>>       ath_hw_setbssidmask(common);
>>  }
>>
>> @@ -1083,7 +1092,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
>>               goto out;
>>       }
>>
>> -     ath9k_htc_set_bssid_mask(priv, vif);
>> +     ath9k_htc_set_mac_bssid_mask(priv, vif);
>>
>>       priv->vif_slot |= (1 << avp->index);
>>       priv->nvifs++;
>> @@ -1148,7 +1157,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
>>
>>       ath9k_htc_set_opmode(priv);
>>
>> -     ath9k_htc_set_bssid_mask(priv, vif);
>> +     ath9k_htc_set_mac_bssid_mask(priv, vif);
>>
>>       /*
>>        * Stop ANI only if there are no associated station interfaces.
>> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
>> index b6aad69..99ab0aa 100644
>> --- a/drivers/net/wireless/ath/ath9k/main.c
>> +++ b/drivers/net/wireless/ath/ath9k/main.c
>> @@ -885,8 +885,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
>>       struct ath_common *common = ath9k_hw_common(ah);
>>
>>       /*
>> -      * Use the hardware MAC address as reference, the hardware uses it
>> -      * together with the BSSID mask when matching addresses.
>> +      * Pick the MAC address of the first interface as the new hardware
>> +      * MAC address. The hardware will use it together with the BSSID mask
>> +      * when matching addresses.
>>        */
>>       memset(iter_data, 0, sizeof(*iter_data));
>>       memset(&iter_data->mask, 0xff, ETH_ALEN);
>>
>>
>>
>>
>
> --
> John W. Linville                Someday the world will need a hero, and you
> linville@tuxdriver.com                  might be all we have.  Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oleksij Rempel Dec. 7, 2013, 4:49 p.m. UTC | #4
I have only one doubt in this patch.
It will overwrite original MAC. If you wont to restore it, then you need
to reload module.

If no body against it, i can live it too.

Am 06.12.2013 21:30, schrieb Mathy:
> It was renamed to make it clear the function now changes both the
> BSSID and MAC registers.
> 
> On Fri, Dec 6, 2013 at 5:29 PM, John W. Linville <linville@tuxdriver.com> wrote:
>> On Thu, Nov 28, 2013 at 12:21:45PM +0100, Mathy Vanhoef wrote:
>>> Third time's the charm?
>>> --
>>> From: "Mathy Vanhoef" <vanhoefm@gmail.com>
>>>
>>> Pick the MAC address of the first virtual interface as the new hardware MAC
>>> address. Set BSSID mask according to this MAC address. This fixes CVE-2013-4579.
>>>
>>> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
>>> ---
>>> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>>> index d441045..84359c3 100644
>>> --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>>> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
>>> @@ -147,21 +147,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
>>>       struct ath9k_vif_iter_data *iter_data = data;
>>>       int i;
>>>
>>> -     for (i = 0; i < ETH_ALEN; i++)
>>> -             iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
>>> +     if (iter_data->hw_macaddr != NULL) {
>>> +             for (i = 0; i < ETH_ALEN; i++)
>>> +                     iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
>>> +     } else {
>>> +             iter_data->hw_macaddr = mac;
>>> +     }
>>>  }
>>>
>>> -static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>>> +static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
>>>                                    struct ieee80211_vif *vif)
>>>  {
>>
>> I'm not sure I see the point of renaming this function -- just
>> personal preference?
>>
>>>       struct ath_common *common = ath9k_hw_common(priv->ah);
>>>       struct ath9k_vif_iter_data iter_data;
>>>
>>>       /*
>>> -      * Use the hardware MAC address as reference, the hardware uses it
>>> -      * together with the BSSID mask when matching addresses.
>>> +      * Pick the MAC address of the first interface as the new hardware
>>> +      * MAC address. The hardware will use it together with the BSSID mask
>>> +      * when matching addresses.
>>>        */
>>> -     iter_data.hw_macaddr = common->macaddr;
>>> +     iter_data.hw_macaddr = NULL;
>>>       memset(&iter_data.mask, 0xff, ETH_ALEN);
>>>
>>>       if (vif)
>>> @@ -173,6 +178,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
>>>               ath9k_htc_bssid_iter, &iter_data);
>>>
>>>       memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
>>> +
>>> +     if (iter_data.hw_macaddr)
>>> +             memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
>>> +
>>>       ath_hw_setbssidmask(common);
>>>  }
>>>
>>> @@ -1083,7 +1092,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
>>>               goto out;
>>>       }
>>>
>>> -     ath9k_htc_set_bssid_mask(priv, vif);
>>> +     ath9k_htc_set_mac_bssid_mask(priv, vif);
>>>
>>>       priv->vif_slot |= (1 << avp->index);
>>>       priv->nvifs++;
>>> @@ -1148,7 +1157,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
>>>
>>>       ath9k_htc_set_opmode(priv);
>>>
>>> -     ath9k_htc_set_bssid_mask(priv, vif);
>>> +     ath9k_htc_set_mac_bssid_mask(priv, vif);
>>>
>>>       /*
>>>        * Stop ANI only if there are no associated station interfaces.
>>> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
>>> index b6aad69..99ab0aa 100644
>>> --- a/drivers/net/wireless/ath/ath9k/main.c
>>> +++ b/drivers/net/wireless/ath/ath9k/main.c
>>> @@ -885,8 +885,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
>>>       struct ath_common *common = ath9k_hw_common(ah);
>>>
>>>       /*
>>> -      * Use the hardware MAC address as reference, the hardware uses it
>>> -      * together with the BSSID mask when matching addresses.
>>> +      * Pick the MAC address of the first interface as the new hardware
>>> +      * MAC address. The hardware will use it together with the BSSID mask
>>> +      * when matching addresses.
>>>        */
>>>       memset(iter_data, 0, sizeof(*iter_data));
>>>       memset(&iter_data->mask, 0xff, ETH_ALEN);
>>>
>>>
>>>
>>>
>>
>> --
>> John W. Linville                Someday the world will need a hero, and you
>> linville@tuxdriver.com                  might be all we have.  Be ready.
Ben Greear Dec. 8, 2013, 12:50 p.m. UTC | #5
On 12/07/2013 08:49 AM, Oleksij Rempel wrote:
> I have only one doubt in this patch.
> It will overwrite original MAC. If you wont to restore it, then you need
> to reload module.
>
> If no body against it, i can live it too.

I believe ethtool allows an API to read the eeprom-MAC addr.  Maybe
add support for that API to the ath9k_htc driver so that the original
MAC can be read that way?

Thanks,
Ben
Mathy Vanhoef Dec. 8, 2013, 6:09 p.m. UTC | #6
The common->macaddr field is also updated in the ath9k driver
(function ath9k_calculate_iter_data in main.c).

I just did a quick test. When using macchanger to assign a random MAC
address, the original (permanent) MAC address is still retrieved
properly. So there is no need to reload the module, macchanger can
simply restore the permanent MAC. I assume the permanent MAC address
is saved elsewhere?

On Sun, Dec 8, 2013 at 1:50 PM, Ben Greear <greearb@candelatech.com> wrote:
> On 12/07/2013 08:49 AM, Oleksij Rempel wrote:
>>
>> I have only one doubt in this patch.
>> It will overwrite original MAC. If you wont to restore it, then you need
>> to reload module.
>>
>> If no body against it, i can live it too.
>
>
> I believe ethtool allows an API to read the eeprom-MAC addr.  Maybe
> add support for that API to the ath9k_htc driver so that the original
> MAC can be read that way?
>
> Thanks,
> Ben
>
> --
> Ben Greear <greearb@candelatech.com>
> Candela Technologies Inc  http://www.candelatech.com
>
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oleksij Rempel Dec. 9, 2013, 2:06 p.m. UTC | #7
Am 08.12.2013 19:09, schrieb Mathy:
> The common->macaddr field is also updated in the ath9k driver
> (function ath9k_calculate_iter_data in main.c).
> 
> I just did a quick test. When using macchanger to assign a random MAC
> address, the original (permanent) MAC address is still retrieved
> properly. So there is no need to reload the module, macchanger can
> simply restore the permanent MAC. I assume the permanent MAC address
> is saved elsewhere?

Right now i have no time to digg deeper in it. I'm ok with this patch.
Thanks a lot for your work!
diff mbox

Patch

diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index d441045..84359c3 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -147,21 +147,26 @@  static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 	struct ath9k_vif_iter_data *iter_data = data;
 	int i;
 
-	for (i = 0; i < ETH_ALEN; i++)
-		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
+	if (iter_data->hw_macaddr != NULL) {
+		for (i = 0; i < ETH_ALEN; i++)
+			iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
+	} else {
+		iter_data->hw_macaddr = mac;
+	}
 }
 
-static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
+static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
 				     struct ieee80211_vif *vif)
 {
 	struct ath_common *common = ath9k_hw_common(priv->ah);
 	struct ath9k_vif_iter_data iter_data;
 
 	/*
-	 * Use the hardware MAC address as reference, the hardware uses it
-	 * together with the BSSID mask when matching addresses.
+	 * Pick the MAC address of the first interface as the new hardware
+	 * MAC address. The hardware will use it together with the BSSID mask
+	 * when matching addresses.
 	 */
-	iter_data.hw_macaddr = common->macaddr;
+	iter_data.hw_macaddr = NULL;
 	memset(&iter_data.mask, 0xff, ETH_ALEN);
 
 	if (vif)
@@ -173,6 +178,10 @@  static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
 		ath9k_htc_bssid_iter, &iter_data);
 
 	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
+
+	if (iter_data.hw_macaddr)
+		memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
+
 	ath_hw_setbssidmask(common);
 }
 
@@ -1083,7 +1092,7 @@  static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ath9k_htc_set_bssid_mask(priv, vif);
+	ath9k_htc_set_mac_bssid_mask(priv, vif);
 
 	priv->vif_slot |= (1 << avp->index);
 	priv->nvifs++;
@@ -1148,7 +1157,7 @@  static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
 
 	ath9k_htc_set_opmode(priv);
 
-	ath9k_htc_set_bssid_mask(priv, vif);
+	ath9k_htc_set_mac_bssid_mask(priv, vif);
 
 	/*
 	 * Stop ANI only if there are no associated station interfaces.
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index b6aad69..99ab0aa 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -885,8 +885,9 @@  void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
 	struct ath_common *common = ath9k_hw_common(ah);
 
 	/*
-	 * Use the hardware MAC address as reference, the hardware uses it
-	 * together with the BSSID mask when matching addresses.
+	 * Pick the MAC address of the first interface as the new hardware
+	 * MAC address. The hardware will use it together with the BSSID mask
+	 * when matching addresses.
 	 */
 	memset(iter_data, 0, sizeof(*iter_data));
 	memset(&iter_data->mask, 0xff, ETH_ALEN);