diff mbox series

wpa_supplicant: multi_ap: only enable 4addr mode if not already enabled

Message ID 20210208172823.11536-1-raphael.melotte@mind.be
State Superseded
Headers show
Series wpa_supplicant: multi_ap: only enable 4addr mode if not already enabled | expand

Commit Message

Raphaël Mélotte Feb. 8, 2021, 5:28 p.m. UTC
If 4addr mode is already enabled, the call to enable it a second time
may fail. If this happens when roaming, it leads to deauthentication.

Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
---
 wpa_supplicant/events.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

Comments

Janusz Dziedzic Feb. 9, 2021, 9:26 a.m. UTC | #1
pon., 8 lut 2021 o 18:30 Raphaël Mélotte <raphael.melotte@mind.be> napisał(a):
>
> If 4addr mode is already enabled, the call to enable it a second time
> may fail. If this happens when roaming, it leads to deauthentication.
>
> Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
> ---
>  wpa_supplicant/events.c | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
> index 199829bcf..7682dae76 100644
> --- a/wpa_supplicant/events.c
> +++ b/wpa_supplicant/events.c
> @@ -2627,11 +2627,13 @@ static void multi_ap_set_4addr_mode(struct wpa_supplicant *wpa_s)
>                 goto fail;
>         }
>
> -       if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> -               wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> -               goto fail;
> +       if (wpa_s->enabled_4addr_mode == 0) {
> +               if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> +                       wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> +                       goto fail;
> +               }
> +               wpa_s->enabled_4addr_mode = 1;
>         }
> -       wpa_s->enabled_4addr_mode = 1;
>         return;
>
Thanks for patch.

Today set_4addr_mode(1) fail with EBUSY if netdev is already in the bridge.
So, maybe we should fix it in cfg80211?

janusz@t2:~$ sudo ifconfig wlp1s0 up

janusz@t2:~$ sudo iw wlp1s0 set 4addr on
janusz@t2:~$ sudo iw wlp1s0 set 4addr on
janusz@t2:~$ sudo iw wlp1s0 set 4addr on

janusz@t2:~$ sudo brctl addbr br0
janusz@t2:~$ sudo brctl addif br0 wlp1s0
janusz@t2:~$ sudo iw wlp1s0 set 4addr on
command failed: Device or resource busy (-16)

janusz@t2:~$ sudo iw wlp1s0 info
Interface wlp1s0
ifindex 3
wdev 0x1
addr 68:94:23:28:a7:25
type managed
wiphy 0
txpower 16.00 dBm
multicast TXQ:
qsz-byt qsz-pkt flows drops marks overlmt hashcol tx-bytes tx-packets
0 0 0 0 0 0 0 0 0
4addr: on

BR
Janusz
Janusz Dziedzic Feb. 10, 2021, 9:07 a.m. UTC | #2
wt., 9 lut 2021 o 10:26 Janusz Dziedzic <janusz.dziedzic@gmail.com> napisał(a):
>
> pon., 8 lut 2021 o 18:30 Raphaël Mélotte <raphael.melotte@mind.be> napisał(a):
> >
> > If 4addr mode is already enabled, the call to enable it a second time
> > may fail. If this happens when roaming, it leads to deauthentication.
> >
> > Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
> > ---
> >  wpa_supplicant/events.c | 10 ++++++----
> >  1 file changed, 6 insertions(+), 4 deletions(-)
> >
> > diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
> > index 199829bcf..7682dae76 100644
> > --- a/wpa_supplicant/events.c
> > +++ b/wpa_supplicant/events.c
> > @@ -2627,11 +2627,13 @@ static void multi_ap_set_4addr_mode(struct wpa_supplicant *wpa_s)
> >                 goto fail;
> >         }
> >
> > -       if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > -               wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > -               goto fail;
> > +       if (wpa_s->enabled_4addr_mode == 0) {
> > +               if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > +                       wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > +                       goto fail;
> > +               }
> > +               wpa_s->enabled_4addr_mode = 1;
> >         }
> > -       wpa_s->enabled_4addr_mode = 1;
> >         return;
> >
> Thanks for patch.
>
> Today set_4addr_mode(1) fail with EBUSY if netdev is already in the bridge.
> So, maybe we should fix it in cfg80211?
>
> janusz@t2:~$ sudo ifconfig wlp1s0 up
>
> janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> janusz@t2:~$ sudo iw wlp1s0 set 4addr on
>
> janusz@t2:~$ sudo brctl addbr br0
> janusz@t2:~$ sudo brctl addif br0 wlp1s0
> janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> command failed: Device or resource busy (-16)
>
> janusz@t2:~$ sudo iw wlp1s0 info
> Interface wlp1s0
> ifindex 3
> wdev 0x1
> addr 68:94:23:28:a7:25
> type managed
> wiphy 0
> txpower 16.00 dBm
> multicast TXQ:
> qsz-byt qsz-pkt flows drops marks overlmt hashcol tx-bytes tx-packets
> 0 0 0 0 0 0 0 0 0
> 4addr: on
>

Did fast patch (didn't check code deeply yet - just check set 4addr
when busy issuey, seems works correctly):

janusz@t1:~/work/linux$ git diff
diff --git a/net/wireless/util.c b/net/wireless/util.c
index b4acc805114b..fec068f48113 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1027,6 +1027,7 @@ int cfg80211_change_iface(struct
cfg80211_registered_device *rdev,

        /* if it's part of a bridge, reject changing type to station/ibss */
        if (netif_is_bridge_port(dev) &&
+           (ntype != otype) &&
            (ntype == NL80211_IFTYPE_ADHOC ||
             ntype == NL80211_IFTYPE_STATION ||
             ntype == NL80211_IFTYPE_P2P_CLIENT))

@Johannes Berg - could you check if this will be correct?

For old kernels/cfg80211 maybe we should introduce
wpa_drv_get_4addr_mode() and set only if required?

BR
Janusz
Jouni Malinen Feb. 10, 2021, 9:38 a.m. UTC | #3
On Wed, Feb 10, 2021 at 10:07:42AM +0100, Janusz Dziedzic wrote:
> wt., 9 lut 2021 o 10:26 Janusz Dziedzic <janusz.dziedzic@gmail.com> napisał(a):
> > pon., 8 lut 2021 o 18:30 Raphaël Mélotte <raphael.melotte@mind.be> napisał(a):
> > > If 4addr mode is already enabled, the call to enable it a second time
> > > may fail. If this happens when roaming, it leads to deauthentication.

What is the case where 4ddr mode would have already been enabled? Does
this happen every time when there is roaming since
multi_ap_set_4addr_mode() did not have this check before? Or are you
considering cases where something external to wpa_supplicant is changing
this parameter?

> > > -       if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > > -               wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > > -               goto fail;
> > > +       if (wpa_s->enabled_4addr_mode == 0) {
> > > +               if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > > +                       wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > > +                       goto fail;
> > > +               }
> > > +               wpa_s->enabled_4addr_mode = 1;
> > >         }
> > > -       wpa_s->enabled_4addr_mode = 1;

This looks safer to me if it is clear that no external entity is messing
up with the kernel parameter.

> > Today set_4addr_mode(1) fail with EBUSY if netdev is already in the bridge.
> > So, maybe we should fix it in cfg80211?

> > janusz@t2:~$ sudo ifconfig wlp1s0 up
> > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > janusz@t2:~$ sudo brctl addbr br0
> > janusz@t2:~$ sudo brctl addif br0 wlp1s0
> > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > command failed: Device or resource busy (-16)

That specific EBUSY seems unnecessary for a no-change operation, but
disabling 4addr mode in station interface that is in a bridge should
still be prevented.
 
> Did fast patch (didn't check code deeply yet - just check set 4addr
> when busy issuey, seems works correctly):

> diff --git a/net/wireless/util.c b/net/wireless/util.c
> @@ -1027,6 +1027,7 @@ int cfg80211_change_iface(struct
> cfg80211_registered_device *rdev,
> 
>         /* if it's part of a bridge, reject changing type to station/ibss */
>         if (netif_is_bridge_port(dev) &&
> +           (ntype != otype) &&
>             (ntype == NL80211_IFTYPE_ADHOC ||
>              ntype == NL80211_IFTYPE_STATION ||
>              ntype == NL80211_IFTYPE_P2P_CLIENT))

How would that prevent disabling of 4addr mode when the netdev is in a
bridge?

> For old kernels/cfg80211 maybe we should introduce
> wpa_drv_get_4addr_mode() and set only if required?

Do we really need that complexity? Isn't the internal tracking with
wpa_s->enabled_4addr_mode sufficient? In other words, simply apply this
patch as-is..
Janusz Dziedzic Feb. 10, 2021, 11:37 a.m. UTC | #4
śr., 10 lut 2021 o 10:38 Jouni Malinen <j@w1.fi> napisał(a):
>
> On Wed, Feb 10, 2021 at 10:07:42AM +0100, Janusz Dziedzic wrote:
> > wt., 9 lut 2021 o 10:26 Janusz Dziedzic <janusz.dziedzic@gmail.com> napisał(a):
> > > pon., 8 lut 2021 o 18:30 Raphaël Mélotte <raphael.melotte@mind.be> napisał(a):
> > > > If 4addr mode is already enabled, the call to enable it a second time
> > > > may fail. If this happens when roaming, it leads to deauthentication.
>
> What is the case where 4ddr mode would have already been enabled? Does
> this happen every time when there is roaming since
> multi_ap_set_4addr_mode() did not have this check before? Or are you
> considering cases where something external to wpa_supplicant is changing
> this parameter?
>
> > > > -       if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > > > -               wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > > > -               goto fail;
> > > > +       if (wpa_s->enabled_4addr_mode == 0) {
> > > > +               if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
> > > > +                       wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
> > > > +                       goto fail;
> > > > +               }
> > > > +               wpa_s->enabled_4addr_mode = 1;
> > > >         }
> > > > -       wpa_s->enabled_4addr_mode = 1;
>
> This looks safer to me if it is clear that no external entity is messing
> up with the kernel parameter.
>
> > > Today set_4addr_mode(1) fail with EBUSY if netdev is already in the bridge.
> > > So, maybe we should fix it in cfg80211?
>
> > > janusz@t2:~$ sudo ifconfig wlp1s0 up
> > > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > > janusz@t2:~$ sudo brctl addbr br0
> > > janusz@t2:~$ sudo brctl addif br0 wlp1s0
> > > janusz@t2:~$ sudo iw wlp1s0 set 4addr on
> > > command failed: Device or resource busy (-16)
>
> That specific EBUSY seems unnecessary for a no-change operation, but
> disabling 4addr mode in station interface that is in a bridge should
> still be prevented.
>
> > Did fast patch (didn't check code deeply yet - just check set 4addr
> > when busy issuey, seems works correctly):
>
> > diff --git a/net/wireless/util.c b/net/wireless/util.c
> > @@ -1027,6 +1027,7 @@ int cfg80211_change_iface(struct
> > cfg80211_registered_device *rdev,
> >
> >         /* if it's part of a bridge, reject changing type to station/ibss */
> >         if (netif_is_bridge_port(dev) &&
> > +           (ntype != otype) &&
> >             (ntype == NL80211_IFTYPE_ADHOC ||
> >              ntype == NL80211_IFTYPE_STATION ||
> >              ntype == NL80211_IFTYPE_P2P_CLIENT))
>
> How would that prevent disabling of 4addr mode when the netdev is in a
> bridge?
>
This what I have with cfg80211 patch included:
root@t2:~# brctl addif br0 wlp1s0
root@t2:~# iw wlp1s0 set 4addr on
root@t2:~# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.68942328a725 no wlp1s0

root@t2:~# iw wlp1s0 set 4addr off
command failed: Device or resource busy (-16)
root@t2:~# iw wlp1s0 set 4addr on
root@t2:~# iw wlp1s0 set 4addr on

Seems there is another check in cfg80211 that already handle this.

> > For old kernels/cfg80211 maybe we should introduce
> > wpa_drv_get_4addr_mode() and set only if required?
>
> Do we really need that complexity? Isn't the internal tracking with
> wpa_s->enabled_4addr_mode sufficient? In other words, simply apply this
> patch as-is..
>
Sounds good, not sure about case where:
- iw wlX set 4addr on
- brctl add br0 wlX
- supplicant (wlX)
- wpa_cli -i wlX wps_pbc multi_ap=1

This test case failed for me (or simply restart supplicant after wlX
in bridge) - because of 4addr on failed.

Will check this with original patch.

BR
Janusz
Raphaël Mélotte Feb. 10, 2021, 4:37 p.m. UTC | #5
On 2/10/21 10:38 AM, Jouni Malinen wrote:
> On Wed, Feb 10, 2021 at 10:07:42AM +0100, Janusz Dziedzic wrote:
>> wt., 9 lut 2021 o 10:26 Janusz Dziedzic <janusz.dziedzic@gmail.com> napisał(a):
>>> pon., 8 lut 2021 o 18:30 Raphaël Mélotte <raphael.melotte@mind.be> napisał(a):
>>>> If 4addr mode is already enabled, the call to enable it a second time
>>>> may fail. If this happens when roaming, it leads to deauthentication.
> 
> What is the case where 4ddr mode would have already been enabled? Does
> this happen every time when there is roaming since
> multi_ap_set_4addr_mode() did not have this check before? Or are you
> considering cases where something external to wpa_supplicant is changing
> this parameter?

In my specific case I think 4addr mode is always enabled (by the supplicant) 
before roaming, because I'm trying to switch from one backhaul AP to another one 
(which means the supplicant enables 4addr mode when connecting to the first bAP).

As Janusz pointed out, it seems the actual problem I'm facing is not the fact 
that 4addr mode is already enabled, but the fact that the interface is already 
part of the bridge (so my patch description is not accurate). No external entity 
changes this parameter in my case.


Thanks,

Raphaël
Johannes Berg Feb. 12, 2021, 9:22 a.m. UTC | #6
On Wed, 2021-02-10 at 12:37 +0100, Janusz Dziedzic wrote:

> > > Did fast patch (didn't check code deeply yet - just check set 4addr
> > > when busy issuey, seems works correctly):
> > > diff --git a/net/wireless/util.c b/net/wireless/util.c
> > > @@ -1027,6 +1027,7 @@ int cfg80211_change_iface(struct
> > > cfg80211_registered_device *rdev,
> > > 
> > >         /* if it's part of a bridge, reject changing type to station/ibss */
> > >         if (netif_is_bridge_port(dev) &&
> > > +           (ntype != otype) &&
> > >             (ntype == NL80211_IFTYPE_ADHOC ||
> > >              ntype == NL80211_IFTYPE_STATION ||
> > >              ntype == NL80211_IFTYPE_P2P_CLIENT))
> > 
> > How would that prevent disabling of 4addr mode when the netdev is in a
> > bridge?
> > 
> This what I have with cfg80211 patch included:
> root@t2:~# brctl addif br0 wlp1s0
> root@t2:~# iw wlp1s0 set 4addr on
> root@t2:~# brctl show
> bridge name bridge id STP enabled interfaces
> br0 8000.68942328a725 no wlp1s0
> 
> root@t2:~# iw wlp1s0 set 4addr off
> command failed: Device or resource busy (-16)
> root@t2:~# iw wlp1s0 set 4addr on
> root@t2:~# iw wlp1s0 set 4addr on
> 
> Seems there is another check in cfg80211 that already handle this.

It's a different code path to set 4addr on/off in this function, so this
is not really a surprise?

Anyway, from a code POV this seems OK, though I'd prefer moving the
bridge port check into the existing "ntype != otype" if there.

johannes
Jouni Malinen Feb. 13, 2021, 10:56 p.m. UTC | #7
On Mon, Feb 08, 2021 at 06:28:23PM +0100, Raphaël Mélotte wrote:
> If 4addr mode is already enabled, the call to enable it a second time
> may fail. If this happens when roaming, it leads to deauthentication.

Based on the discussion, I addressed this issue differently by ignoring
the nl80211 command failure when trying to enable the 4addr mode in case
it is already enabled.
Raphaël Mélotte Feb. 16, 2021, 8:55 a.m. UTC | #8
On 2/13/21 11:56 PM, Jouni Malinen wrote:
> On Mon, Feb 08, 2021 at 06:28:23PM +0100, Raphaël Mélotte wrote:
>> If 4addr mode is already enabled, the call to enable it a second time
>> may fail. If this happens when roaming, it leads to deauthentication.
> 
> Based on the discussion, I addressed this issue differently by ignoring
> the nl80211 command failure when trying to enable the 4addr mode in case
> it is already enabled.
>   

I confirm the issue which lead me to send this patch is fixed with 
'58bbbb5981440da508164cbd423ca5dd7c1b98d8'.

I was wondering if it was possible to use a bridge in the tests to reproduce the 
original issue with hwsim, but now I see that the test is already there :-)

Thanks!
diff mbox series

Patch

diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 199829bcf..7682dae76 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -2627,11 +2627,13 @@  static void multi_ap_set_4addr_mode(struct wpa_supplicant *wpa_s)
 		goto fail;
 	}
 
-	if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
-		wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
-		goto fail;
+	if (wpa_s->enabled_4addr_mode == 0) {
+		if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
+			wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
+			goto fail;
+		}
+		wpa_s->enabled_4addr_mode = 1;
 	}
-	wpa_s->enabled_4addr_mode = 1;
 	return;
 
 fail: