diff mbox series

[v2,net-next,2/4] net: switchdev: add support for offloading of fdb locked flag

Message ID 20220317093902.1305816-3-schultz.hans+netdev@gmail.com (mailing list archive)
State New
Headers show
Series Extend locked port feature with FDB locked flag (MAC-Auth/MAB) | expand

Commit Message

Hans S March 17, 2022, 9:39 a.m. UTC
Used for Mac-auth/MAB feature in the offloaded case.

Signed-off-by: Hans Schultz <schultz.hans+netdev@gmail.com>
---
 include/net/switchdev.h | 3 ++-
 net/bridge/br.c         | 3 ++-
 net/bridge/br_fdb.c     | 7 +++++--
 net/bridge/br_private.h | 2 +-
 4 files changed, 10 insertions(+), 5 deletions(-)

Comments

Hans S March 23, 2022, 12:29 p.m. UTC | #1
On tor, mar 17, 2022 at 10:39, Hans Schultz <schultz.hans@gmail.com> wrote:
> Used for Mac-auth/MAB feature in the offloaded case.
>
> Signed-off-by: Hans Schultz <schultz.hans+netdev@gmail.com>
> ---
>  include/net/switchdev.h | 3 ++-
>  net/bridge/br.c         | 3 ++-
>  net/bridge/br_fdb.c     | 7 +++++--
>  net/bridge/br_private.h | 2 +-
>  4 files changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/switchdev.h b/include/net/switchdev.h
> index 3e424d40fae3..d5d923411f5e 100644
> --- a/include/net/switchdev.h
> +++ b/include/net/switchdev.h
> @@ -229,7 +229,8 @@ struct switchdev_notifier_fdb_info {
>  	u16 vid;
>  	u8 added_by_user:1,
>  	   is_local:1,
> -	   offloaded:1;
> +	   offloaded:1,
> +	   locked:1;
>  };
>  
>  struct switchdev_notifier_port_obj_info {
> diff --git a/net/bridge/br.c b/net/bridge/br.c
> index b1dea3febeea..adcdbecbc218 100644
> --- a/net/bridge/br.c
> +++ b/net/bridge/br.c
> @@ -166,7 +166,8 @@ static int br_switchdev_event(struct notifier_block *unused,
>  	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
>  		fdb_info = ptr;
>  		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
> -						fdb_info->vid, false);
> +						fdb_info->vid, false,
> +						fdb_info->locked);
>  		if (err) {
>  			err = notifier_from_errno(err);
>  			break;
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 57ec559a85a7..57aa1955d34d 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -987,7 +987,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
>  					   "FDB entry towards bridge must be permanent");
>  			return -EINVAL;
>  		}
> -		err = br_fdb_external_learn_add(br, p, addr, vid, true);
> +		err = br_fdb_external_learn_add(br, p, addr, vid, true,
>  false);

Does someone have an idea why there at this point is no option to add a
dynamic fdb entry?

The fdb added entries here do not age out, while the ATU entries do
(after 5 min), resulting in unsynced ATU vs fdb.

>  	} else {
>  		spin_lock_bh(&br->hash_lock);
>  		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
> @@ -1216,7 +1216,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
>  
>  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>  			      const unsigned char *addr, u16 vid,
> -			      bool swdev_notify)
> +			      bool swdev_notify, bool locked)
>  {
>  	struct net_bridge_fdb_entry *fdb;
>  	bool modified = false;
> @@ -1236,6 +1236,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>  		if (!p)
>  			flags |= BIT(BR_FDB_LOCAL);
>  
> +		if (locked)
> +			flags |= BIT(BR_FDB_ENTRY_LOCKED);
> +
>  		fdb = fdb_create(br, p, addr, vid, flags);
>  		if (!fdb) {
>  			err = -ENOMEM;
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index f5a0b68c4857..3275e33b112f 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -790,7 +790,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
>  void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
>  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>  			      const unsigned char *addr, u16 vid,
> -			      bool swdev_notify);
> +			      bool swdev_notify, bool locked);
>  int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
>  			      const unsigned char *addr, u16 vid,
>  			      bool swdev_notify);
> -- 
> 2.30.2
Vladimir Oltean March 23, 2022, 12:35 p.m. UTC | #2
On Wed, Mar 23, 2022 at 01:29:52PM +0100, Hans Schultz wrote:
> On tor, mar 17, 2022 at 10:39, Hans Schultz <schultz.hans@gmail.com> wrote:
> > Used for Mac-auth/MAB feature in the offloaded case.
> >
> > Signed-off-by: Hans Schultz <schultz.hans+netdev@gmail.com>
> > ---
> >  include/net/switchdev.h | 3 ++-
> >  net/bridge/br.c         | 3 ++-
> >  net/bridge/br_fdb.c     | 7 +++++--
> >  net/bridge/br_private.h | 2 +-
> >  4 files changed, 10 insertions(+), 5 deletions(-)
> >
> > diff --git a/include/net/switchdev.h b/include/net/switchdev.h
> > index 3e424d40fae3..d5d923411f5e 100644
> > --- a/include/net/switchdev.h
> > +++ b/include/net/switchdev.h
> > @@ -229,7 +229,8 @@ struct switchdev_notifier_fdb_info {
> >  	u16 vid;
> >  	u8 added_by_user:1,
> >  	   is_local:1,
> > -	   offloaded:1;
> > +	   offloaded:1,
> > +	   locked:1;
> >  };
> >  
> >  struct switchdev_notifier_port_obj_info {
> > diff --git a/net/bridge/br.c b/net/bridge/br.c
> > index b1dea3febeea..adcdbecbc218 100644
> > --- a/net/bridge/br.c
> > +++ b/net/bridge/br.c
> > @@ -166,7 +166,8 @@ static int br_switchdev_event(struct notifier_block *unused,
> >  	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
> >  		fdb_info = ptr;
> >  		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
> > -						fdb_info->vid, false);
> > +						fdb_info->vid, false,
> > +						fdb_info->locked);
> >  		if (err) {
> >  			err = notifier_from_errno(err);
> >  			break;
> > diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> > index 57ec559a85a7..57aa1955d34d 100644
> > --- a/net/bridge/br_fdb.c
> > +++ b/net/bridge/br_fdb.c
> > @@ -987,7 +987,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
> >  					   "FDB entry towards bridge must be permanent");
> >  			return -EINVAL;
> >  		}
> > -		err = br_fdb_external_learn_add(br, p, addr, vid, true);
> > +		err = br_fdb_external_learn_add(br, p, addr, vid, true,
> >  false);
> 
> Does someone have an idea why there at this point is no option to add a
> dynamic fdb entry?
> 
> The fdb added entries here do not age out, while the ATU entries do
> (after 5 min), resulting in unsynced ATU vs fdb.

I think the expectation is to use br_fdb_external_learn_del() if the
externally learned entry expires. The bridge should not age by itself
FDB entries learned externally.

> >  	} else {
> >  		spin_lock_bh(&br->hash_lock);
> >  		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
> > @@ -1216,7 +1216,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
> >  
> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
> >  			      const unsigned char *addr, u16 vid,
> > -			      bool swdev_notify)
> > +			      bool swdev_notify, bool locked)
> >  {
> >  	struct net_bridge_fdb_entry *fdb;
> >  	bool modified = false;
> > @@ -1236,6 +1236,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
> >  		if (!p)
> >  			flags |= BIT(BR_FDB_LOCAL);
> >  
> > +		if (locked)
> > +			flags |= BIT(BR_FDB_ENTRY_LOCKED);
> > +
> >  		fdb = fdb_create(br, p, addr, vid, flags);
> >  		if (!fdb) {
> >  			err = -ENOMEM;
> > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> > index f5a0b68c4857..3275e33b112f 100644
> > --- a/net/bridge/br_private.h
> > +++ b/net/bridge/br_private.h
> > @@ -790,7 +790,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
> >  void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
> >  			      const unsigned char *addr, u16 vid,
> > -			      bool swdev_notify);
> > +			      bool swdev_notify, bool locked);
> >  int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
> >  			      const unsigned char *addr, u16 vid,
> >  			      bool swdev_notify);
> > -- 
> > 2.30.2
Hans S March 23, 2022, 12:49 p.m. UTC | #3
On ons, mar 23, 2022 at 14:35, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Wed, Mar 23, 2022 at 01:29:52PM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 10:39, Hans Schultz <schultz.hans@gmail.com> wrote:
>> > Used for Mac-auth/MAB feature in the offloaded case.
>> >
>> > Signed-off-by: Hans Schultz <schultz.hans+netdev@gmail.com>
>> > ---
>> >  include/net/switchdev.h | 3 ++-
>> >  net/bridge/br.c         | 3 ++-
>> >  net/bridge/br_fdb.c     | 7 +++++--
>> >  net/bridge/br_private.h | 2 +-
>> >  4 files changed, 10 insertions(+), 5 deletions(-)
>> >
>> > diff --git a/include/net/switchdev.h b/include/net/switchdev.h
>> > index 3e424d40fae3..d5d923411f5e 100644
>> > --- a/include/net/switchdev.h
>> > +++ b/include/net/switchdev.h
>> > @@ -229,7 +229,8 @@ struct switchdev_notifier_fdb_info {
>> >  	u16 vid;
>> >  	u8 added_by_user:1,
>> >  	   is_local:1,
>> > -	   offloaded:1;
>> > +	   offloaded:1,
>> > +	   locked:1;
>> >  };
>> >  
>> >  struct switchdev_notifier_port_obj_info {
>> > diff --git a/net/bridge/br.c b/net/bridge/br.c
>> > index b1dea3febeea..adcdbecbc218 100644
>> > --- a/net/bridge/br.c
>> > +++ b/net/bridge/br.c
>> > @@ -166,7 +166,8 @@ static int br_switchdev_event(struct notifier_block *unused,
>> >  	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
>> >  		fdb_info = ptr;
>> >  		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
>> > -						fdb_info->vid, false);
>> > +						fdb_info->vid, false,
>> > +						fdb_info->locked);
>> >  		if (err) {
>> >  			err = notifier_from_errno(err);
>> >  			break;
>> > diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> > index 57ec559a85a7..57aa1955d34d 100644
>> > --- a/net/bridge/br_fdb.c
>> > +++ b/net/bridge/br_fdb.c
>> > @@ -987,7 +987,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
>> >  					   "FDB entry towards bridge must be permanent");
>> >  			return -EINVAL;
>> >  		}
>> > -		err = br_fdb_external_learn_add(br, p, addr, vid, true);
>> > +		err = br_fdb_external_learn_add(br, p, addr, vid, true,
>> >  false);
>> 
>> Does someone have an idea why there at this point is no option to add a
>> dynamic fdb entry?
>> 
>> The fdb added entries here do not age out, while the ATU entries do
>> (after 5 min), resulting in unsynced ATU vs fdb.
>
> I think the expectation is to use br_fdb_external_learn_del() if the
> externally learned entry expires. The bridge should not age by itself
> FDB entries learned externally.
>

It seems to me that something is missing then?
My tests using trafgen that I gave a report on to Lunn generated massive
amounts of fdb entries, but after a while the ATU was clean and the fdb
was still full of random entries...

>> >  	} else {
>> >  		spin_lock_bh(&br->hash_lock);
>> >  		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
>> > @@ -1216,7 +1216,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
>> >  
>> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> > -			      bool swdev_notify)
>> > +			      bool swdev_notify, bool locked)
>> >  {
>> >  	struct net_bridge_fdb_entry *fdb;
>> >  	bool modified = false;
>> > @@ -1236,6 +1236,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  		if (!p)
>> >  			flags |= BIT(BR_FDB_LOCAL);
>> >  
>> > +		if (locked)
>> > +			flags |= BIT(BR_FDB_ENTRY_LOCKED);
>> > +
>> >  		fdb = fdb_create(br, p, addr, vid, flags);
>> >  		if (!fdb) {
>> >  			err = -ENOMEM;
>> > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> > index f5a0b68c4857..3275e33b112f 100644
>> > --- a/net/bridge/br_private.h
>> > +++ b/net/bridge/br_private.h
>> > @@ -790,7 +790,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
>> >  void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
>> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> > -			      bool swdev_notify);
>> > +			      bool swdev_notify, bool locked);
>> >  int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> >  			      bool swdev_notify);
>> > -- 
>> > 2.30.2
Hans S March 23, 2022, 2:42 p.m. UTC | #4
On ons, mar 23, 2022 at 14:35, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Wed, Mar 23, 2022 at 01:29:52PM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 10:39, Hans Schultz <schultz.hans@gmail.com> wrote:
>> > Used for Mac-auth/MAB feature in the offloaded case.
>> >
>> > Signed-off-by: Hans Schultz <schultz.hans+netdev@gmail.com>
>> > ---
>> >  include/net/switchdev.h | 3 ++-
>> >  net/bridge/br.c         | 3 ++-
>> >  net/bridge/br_fdb.c     | 7 +++++--
>> >  net/bridge/br_private.h | 2 +-
>> >  4 files changed, 10 insertions(+), 5 deletions(-)
>> >
>> > diff --git a/include/net/switchdev.h b/include/net/switchdev.h
>> > index 3e424d40fae3..d5d923411f5e 100644
>> > --- a/include/net/switchdev.h
>> > +++ b/include/net/switchdev.h
>> > @@ -229,7 +229,8 @@ struct switchdev_notifier_fdb_info {
>> >  	u16 vid;
>> >  	u8 added_by_user:1,
>> >  	   is_local:1,
>> > -	   offloaded:1;
>> > +	   offloaded:1,
>> > +	   locked:1;
>> >  };
>> >  
>> >  struct switchdev_notifier_port_obj_info {
>> > diff --git a/net/bridge/br.c b/net/bridge/br.c
>> > index b1dea3febeea..adcdbecbc218 100644
>> > --- a/net/bridge/br.c
>> > +++ b/net/bridge/br.c
>> > @@ -166,7 +166,8 @@ static int br_switchdev_event(struct notifier_block *unused,
>> >  	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
>> >  		fdb_info = ptr;
>> >  		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
>> > -						fdb_info->vid, false);
>> > +						fdb_info->vid, false,
>> > +						fdb_info->locked);
>> >  		if (err) {
>> >  			err = notifier_from_errno(err);
>> >  			break;
>> > diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
>> > index 57ec559a85a7..57aa1955d34d 100644
>> > --- a/net/bridge/br_fdb.c
>> > +++ b/net/bridge/br_fdb.c
>> > @@ -987,7 +987,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
>> >  					   "FDB entry towards bridge must be permanent");
>> >  			return -EINVAL;
>> >  		}
>> > -		err = br_fdb_external_learn_add(br, p, addr, vid, true);
>> > +		err = br_fdb_external_learn_add(br, p, addr, vid, true,
>> >  false);
>> 
>> Does someone have an idea why there at this point is no option to add a
>> dynamic fdb entry?
>> 
>> The fdb added entries here do not age out, while the ATU entries do
>> (after 5 min), resulting in unsynced ATU vs fdb.
>
> I think the expectation is to use br_fdb_external_learn_del() if the
> externally learned entry expires. The bridge should not age by itself
> FDB entries learned externally.
>

How is the mechanism supposed to work to remove fdb entries when ATU
entries age out?

>> >  	} else {
>> >  		spin_lock_bh(&br->hash_lock);
>> >  		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
>> > @@ -1216,7 +1216,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
>> >  
>> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> > -			      bool swdev_notify)
>> > +			      bool swdev_notify, bool locked)
>> >  {
>> >  	struct net_bridge_fdb_entry *fdb;
>> >  	bool modified = false;
>> > @@ -1236,6 +1236,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  		if (!p)
>> >  			flags |= BIT(BR_FDB_LOCAL);
>> >  
>> > +		if (locked)
>> > +			flags |= BIT(BR_FDB_ENTRY_LOCKED);
>> > +
>> >  		fdb = fdb_create(br, p, addr, vid, flags);
>> >  		if (!fdb) {
>> >  			err = -ENOMEM;
>> > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
>> > index f5a0b68c4857..3275e33b112f 100644
>> > --- a/net/bridge/br_private.h
>> > +++ b/net/bridge/br_private.h
>> > @@ -790,7 +790,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
>> >  void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
>> >  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> > -			      bool swdev_notify);
>> > +			      bool swdev_notify, bool locked);
>> >  int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
>> >  			      const unsigned char *addr, u16 vid,
>> >  			      bool swdev_notify);
>> > -- 
>> > 2.30.2
Vladimir Oltean March 23, 2022, 2:43 p.m. UTC | #5
On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
> >> Does someone have an idea why there at this point is no option to add a
> >> dynamic fdb entry?
> >> 
> >> The fdb added entries here do not age out, while the ATU entries do
> >> (after 5 min), resulting in unsynced ATU vs fdb.
> >
> > I think the expectation is to use br_fdb_external_learn_del() if the
> > externally learned entry expires. The bridge should not age by itself
> > FDB entries learned externally.
> >
> 
> It seems to me that something is missing then?
> My tests using trafgen that I gave a report on to Lunn generated massive
> amounts of fdb entries, but after a while the ATU was clean and the fdb
> was still full of random entries...

I'm no longer sure where you are, sorry..
I think we discussed that you need to enable ATU age interrupts in order
to keep the ATU in sync with the bridge FDB? Which means either to
delete the locked FDB entries from the bridge when they age out in the
ATU, or to keep refreshing locked ATU entries.
So it seems that you're doing neither of those 2 things if you end up
with bridge FDB entries which are no longer in the ATU.
Hans S March 23, 2022, 3:03 p.m. UTC | #6
On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> Does someone have an idea why there at this point is no option to add a
>> >> dynamic fdb entry?
>> >> 
>> >> The fdb added entries here do not age out, while the ATU entries do
>> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >
>> > I think the expectation is to use br_fdb_external_learn_del() if the
>> > externally learned entry expires. The bridge should not age by itself
>> > FDB entries learned externally.
>> >
>> 
>> It seems to me that something is missing then?
>> My tests using trafgen that I gave a report on to Lunn generated massive
>> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> was still full of random entries...
>
> I'm no longer sure where you are, sorry..
> I think we discussed that you need to enable ATU age interrupts in order
> to keep the ATU in sync with the bridge FDB? Which means either to
> delete the locked FDB entries from the bridge when they age out in the
> ATU, or to keep refreshing locked ATU entries.
> So it seems that you're doing neither of those 2 things if you end up
> with bridge FDB entries which are no longer in the ATU.

Right, there was much that needed my attention, so after the other
issues are taken care of, I can focus on this. So I thought there was
some general machanism in place already, but I see that Ineed to enable
the IntOnAgeOut interrupt and handle ATU age out violations.
Hans S March 24, 2022, 10:32 a.m. UTC | #7
On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> Does someone have an idea why there at this point is no option to add a
>> >> dynamic fdb entry?
>> >> 
>> >> The fdb added entries here do not age out, while the ATU entries do
>> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >
>> > I think the expectation is to use br_fdb_external_learn_del() if the
>> > externally learned entry expires. The bridge should not age by itself
>> > FDB entries learned externally.
>> >
>> 
>> It seems to me that something is missing then?
>> My tests using trafgen that I gave a report on to Lunn generated massive
>> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> was still full of random entries...
>
> I'm no longer sure where you are, sorry..
> I think we discussed that you need to enable ATU age interrupts in order
> to keep the ATU in sync with the bridge FDB? Which means either to
> delete the locked FDB entries from the bridge when they age out in the
> ATU, or to keep refreshing locked ATU entries.
> So it seems that you're doing neither of those 2 things if you end up
> with bridge FDB entries which are no longer in the ATU.

Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
for it, so I assume it is something default?
Vladimir Oltean March 24, 2022, 11:09 a.m. UTC | #8
On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
> >> >> Does someone have an idea why there at this point is no option to add a
> >> >> dynamic fdb entry?
> >> >> 
> >> >> The fdb added entries here do not age out, while the ATU entries do
> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
> >> >
> >> > I think the expectation is to use br_fdb_external_learn_del() if the
> >> > externally learned entry expires. The bridge should not age by itself
> >> > FDB entries learned externally.
> >> >
> >> 
> >> It seems to me that something is missing then?
> >> My tests using trafgen that I gave a report on to Lunn generated massive
> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
> >> was still full of random entries...
> >
> > I'm no longer sure where you are, sorry..
> > I think we discussed that you need to enable ATU age interrupts in order
> > to keep the ATU in sync with the bridge FDB? Which means either to
> > delete the locked FDB entries from the bridge when they age out in the
> > ATU, or to keep refreshing locked ATU entries.
> > So it seems that you're doing neither of those 2 things if you end up
> > with bridge FDB entries which are no longer in the ATU.
> 
> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
> for it, so I assume it is something default?

No idea, but I can confirm that the out-of-reset value I see for
MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
rely on any reset defaults though.
Hans S March 24, 2022, 11:23 a.m. UTC | #9
On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
>> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> >> Does someone have an idea why there at this point is no option to add a
>> >> >> dynamic fdb entry?
>> >> >> 
>> >> >> The fdb added entries here do not age out, while the ATU entries do
>> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >> >
>> >> > I think the expectation is to use br_fdb_external_learn_del() if the
>> >> > externally learned entry expires. The bridge should not age by itself
>> >> > FDB entries learned externally.
>> >> >
>> >> 
>> >> It seems to me that something is missing then?
>> >> My tests using trafgen that I gave a report on to Lunn generated massive
>> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> >> was still full of random entries...
>> >
>> > I'm no longer sure where you are, sorry..
>> > I think we discussed that you need to enable ATU age interrupts in order
>> > to keep the ATU in sync with the bridge FDB? Which means either to
>> > delete the locked FDB entries from the bridge when they age out in the
>> > ATU, or to keep refreshing locked ATU entries.
>> > So it seems that you're doing neither of those 2 things if you end up
>> > with bridge FDB entries which are no longer in the ATU.
>> 
>> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
>> for it, so I assume it is something default?
>
> No idea, but I can confirm that the out-of-reset value I see for
> MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
> rely on any reset defaults though.

I see no age out interrupts, even though the ports Age Out Int is on
(PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
is off). Any idea why that can be?

I combination with this I think it would be nice to have an ability to
set the AgeOut time even though it is not per port but global.
Vladimir Oltean March 24, 2022, 2:27 p.m. UTC | #10
On Thu, Mar 24, 2022 at 12:23:39PM +0100, Hans Schultz wrote:
> On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
> >> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
> >> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
> >> >> >> Does someone have an idea why there at this point is no option to add a
> >> >> >> dynamic fdb entry?
> >> >> >> 
> >> >> >> The fdb added entries here do not age out, while the ATU entries do
> >> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
> >> >> >
> >> >> > I think the expectation is to use br_fdb_external_learn_del() if the
> >> >> > externally learned entry expires. The bridge should not age by itself
> >> >> > FDB entries learned externally.
> >> >> >
> >> >> 
> >> >> It seems to me that something is missing then?
> >> >> My tests using trafgen that I gave a report on to Lunn generated massive
> >> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
> >> >> was still full of random entries...
> >> >
> >> > I'm no longer sure where you are, sorry..
> >> > I think we discussed that you need to enable ATU age interrupts in order
> >> > to keep the ATU in sync with the bridge FDB? Which means either to
> >> > delete the locked FDB entries from the bridge when they age out in the
> >> > ATU, or to keep refreshing locked ATU entries.
> >> > So it seems that you're doing neither of those 2 things if you end up
> >> > with bridge FDB entries which are no longer in the ATU.
> >> 
> >> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
> >> for it, so I assume it is something default?
> >
> > No idea, but I can confirm that the out-of-reset value I see for
> > MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
> > rely on any reset defaults though.
> 
> I see no age out interrupts, even though the ports Age Out Int is on
> (PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
> is off). Any idea why that can be?
> 
> I combination with this I think it would be nice to have an ability to
> set the AgeOut time even though it is not per port but global.

Sorry, I just don't know. Looking at the documentation for IntOnAgeOut,
I see it says that for an ATU entry to trigger an age out interrupt, the
port it's associated with must have IntOnAgeOut set.
But your locked ATU entries aren't associated with any port, they have
DPV=0, right? So will they never trigger any age out interrupt according
to this? I'm not clear.
Hans S March 25, 2022, 7:50 a.m. UTC | #11
On tor, mar 24, 2022 at 16:27, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Thu, Mar 24, 2022 at 12:23:39PM +0100, Hans Schultz wrote:
>> On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
>> >> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
>> >> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> >> >> Does someone have an idea why there at this point is no option to add a
>> >> >> >> dynamic fdb entry?
>> >> >> >> 
>> >> >> >> The fdb added entries here do not age out, while the ATU entries do
>> >> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >> >> >
>> >> >> > I think the expectation is to use br_fdb_external_learn_del() if the
>> >> >> > externally learned entry expires. The bridge should not age by itself
>> >> >> > FDB entries learned externally.
>> >> >> >
>> >> >> 
>> >> >> It seems to me that something is missing then?
>> >> >> My tests using trafgen that I gave a report on to Lunn generated massive
>> >> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> >> >> was still full of random entries...
>> >> >
>> >> > I'm no longer sure where you are, sorry..
>> >> > I think we discussed that you need to enable ATU age interrupts in order
>> >> > to keep the ATU in sync with the bridge FDB? Which means either to
>> >> > delete the locked FDB entries from the bridge when they age out in the
>> >> > ATU, or to keep refreshing locked ATU entries.
>> >> > So it seems that you're doing neither of those 2 things if you end up
>> >> > with bridge FDB entries which are no longer in the ATU.
>> >> 
>> >> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
>> >> for it, so I assume it is something default?
>> >
>> > No idea, but I can confirm that the out-of-reset value I see for
>> > MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
>> > rely on any reset defaults though.
>> 
>> I see no age out interrupts, even though the ports Age Out Int is on
>> (PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
>> is off). Any idea why that can be?
>> 
>> I combination with this I think it would be nice to have an ability to
>> set the AgeOut time even though it is not per port but global.
>
> Sorry, I just don't know. Looking at the documentation for IntOnAgeOut,
> I see it says that for an ATU entry to trigger an age out interrupt, the
> port it's associated with must have IntOnAgeOut set.
> But your locked ATU entries aren't associated with any port, they have
> DPV=0, right? So will they never trigger any age out interrupt according
> to this? I'm not clear.

I think that's absolutely right. That leaves two options. Either "port
10" if it has IntOnAgeOut setting, or the reason why I wrote my comments
in this part of the code, that it should be able to add a dynamic entry
in the bridge module from the driver.
Hans S March 25, 2022, 9:24 a.m. UTC | #12
On tor, mar 24, 2022 at 16:27, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Thu, Mar 24, 2022 at 12:23:39PM +0100, Hans Schultz wrote:
>> On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
>> >> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
>> >> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> >> >> Does someone have an idea why there at this point is no option to add a
>> >> >> >> dynamic fdb entry?
>> >> >> >> 
>> >> >> >> The fdb added entries here do not age out, while the ATU entries do
>> >> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >> >> >
>> >> >> > I think the expectation is to use br_fdb_external_learn_del() if the
>> >> >> > externally learned entry expires. The bridge should not age by itself
>> >> >> > FDB entries learned externally.
>> >> >> >
>> >> >> 
>> >> >> It seems to me that something is missing then?
>> >> >> My tests using trafgen that I gave a report on to Lunn generated massive
>> >> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> >> >> was still full of random entries...
>> >> >
>> >> > I'm no longer sure where you are, sorry..
>> >> > I think we discussed that you need to enable ATU age interrupts in order
>> >> > to keep the ATU in sync with the bridge FDB? Which means either to
>> >> > delete the locked FDB entries from the bridge when they age out in the
>> >> > ATU, or to keep refreshing locked ATU entries.
>> >> > So it seems that you're doing neither of those 2 things if you end up
>> >> > with bridge FDB entries which are no longer in the ATU.
>> >> 
>> >> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
>> >> for it, so I assume it is something default?
>> >
>> > No idea, but I can confirm that the out-of-reset value I see for
>> > MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
>> > rely on any reset defaults though.
>> 
>> I see no age out interrupts, even though the ports Age Out Int is on
>> (PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
>> is off). Any idea why that can be?
>> 
>> I combination with this I think it would be nice to have an ability to
>> set the AgeOut time even though it is not per port but global.
>
> Sorry, I just don't know. Looking at the documentation for IntOnAgeOut,
> I see it says that for an ATU entry to trigger an age out interrupt, the
> port it's associated with must have IntOnAgeOut set.
> But your locked ATU entries aren't associated with any port, they have
> DPV=0, right? So will they never trigger any age out interrupt according
> to this? I'm not clear.

If it could be possible to add a dynamic entry to the bridge module from
the driver, that would be a solution, and since in the ATU in this case
ages out entries learned, I don't see why __br_fdb_add() insists that an
external learned entry must be permanent?
Vladimir Oltean March 25, 2022, 1:21 p.m. UTC | #13
On Fri, Mar 25, 2022 at 08:50:34AM +0100, Hans Schultz wrote:
> On tor, mar 24, 2022 at 16:27, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Thu, Mar 24, 2022 at 12:23:39PM +0100, Hans Schultz wrote:
> >> On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
> >> > On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
> >> >> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
> >> >> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
> >> >> >> >> Does someone have an idea why there at this point is no option to add a
> >> >> >> >> dynamic fdb entry?
> >> >> >> >> 
> >> >> >> >> The fdb added entries here do not age out, while the ATU entries do
> >> >> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
> >> >> >> >
> >> >> >> > I think the expectation is to use br_fdb_external_learn_del() if the
> >> >> >> > externally learned entry expires. The bridge should not age by itself
> >> >> >> > FDB entries learned externally.
> >> >> >> >
> >> >> >> 
> >> >> >> It seems to me that something is missing then?
> >> >> >> My tests using trafgen that I gave a report on to Lunn generated massive
> >> >> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
> >> >> >> was still full of random entries...
> >> >> >
> >> >> > I'm no longer sure where you are, sorry..
> >> >> > I think we discussed that you need to enable ATU age interrupts in order
> >> >> > to keep the ATU in sync with the bridge FDB? Which means either to
> >> >> > delete the locked FDB entries from the bridge when they age out in the
> >> >> > ATU, or to keep refreshing locked ATU entries.
> >> >> > So it seems that you're doing neither of those 2 things if you end up
> >> >> > with bridge FDB entries which are no longer in the ATU.
> >> >> 
> >> >> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
> >> >> for it, so I assume it is something default?
> >> >
> >> > No idea, but I can confirm that the out-of-reset value I see for
> >> > MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
> >> > rely on any reset defaults though.
> >> 
> >> I see no age out interrupts, even though the ports Age Out Int is on
> >> (PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
> >> is off). Any idea why that can be?
> >> 
> >> I combination with this I think it would be nice to have an ability to
> >> set the AgeOut time even though it is not per port but global.
> >
> > Sorry, I just don't know. Looking at the documentation for IntOnAgeOut,
> > I see it says that for an ATU entry to trigger an age out interrupt, the
> > port it's associated with must have IntOnAgeOut set.
> > But your locked ATU entries aren't associated with any port, they have
> > DPV=0, right? So will they never trigger any age out interrupt according
> > to this? I'm not clear.
> 
> I think that's absolutely right. That leaves two options. Either "port
> 10" if it has IntOnAgeOut setting, or the reason why I wrote my comments
> in this part of the code, that it should be able to add a dynamic entry
> in the bridge module from the driver.

I'm sorry, I wasn't fully aware of the implications of the fact that
your 'locked' FDB entries have a DPV of all zeroes in hardware.
Practically, this means that while the locked bridge FDB entry is
associated with a bridge port, the ATU entry is associated with no port.

In turn, the hardware cannot ever true detect station migrations,
because it doesn't know which port this station migrates _from_ (you're
not telling it that). Every packet with this MAC SA is a station
migration, in effect, which you (for good reason) choose to ignore to
avoid denial of service.

Mark the locked (DPV=0) ATU entry as static, and you'll keep your CPU
clean of any ATU miss or member violation of this MAC SA. Read this as
"you'll need to call IT to ask them to remove it". Undesirable IMHO.

Mark the locked entry as non-static, and the entry will eventually
expire, with no interrupt to signal that - because any ATU age interrupt,
as mentioned, is fundamentally linked to a port.

You see this as a negative, and you're looking for ways to inform the
bridge driver that the locked FDB entry went away. But you aren't
looking at this the right way, I think. Making the mv88e6xxx driver
remove the locked FDB entry from the bridge seems like a non-goal now.

If you'd cache the locked ATU entry in the mv88e6xxx driver, and you'd
notify switchdev only if the entry is new to the cache, then you'd
actually still achieve something major. Yes, the bridge FDB will contain
locked FDB entries that aren't in the ATU. But that's because your
printer has been silent for X seconds. The policy for the printer still
hasn't changed, as far as the mv88e6xxx, or bridge, software drivers are
concerned. If the unauthorized printer says something again after the
locked ATU entry expires, the mv88e6xxx driver will find its MAC SA
in the cache of denied addresses, and reload the ATU. What this achieves
is that the number of ATU violation interrupts isn't proportional to the
number of packets sent by the printer, but with the ageing time you
configure for this ATU entry. You should be able to play with an
entry->state in the range of 1 -> 7 and get a good compromise between
responsiveness on station migrations and number of ATU interrupts to
service once the locked ATU entry is invalidated. In my opinion even the
quickest-to-expire entry->state of 1 is way better than letting every
packet spam the CPU. And you can always keep your cached locked ATU
entry in sync with the port that triggered the violation interrupt, and
figure out station migrations in software this way.

I hope I understood the hardware behavior correctly, I don't have any
direct experience with 802.1X as I mentioned, and only limited and
non-expert experience with Marvell hardware. This is just my
interpretation of some random documentation I found online.
Hans S March 25, 2022, 1:48 p.m. UTC | #14
On fre, mar 25, 2022 at 15:21, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Fri, Mar 25, 2022 at 08:50:34AM +0100, Hans Schultz wrote:
>> On tor, mar 24, 2022 at 16:27, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Thu, Mar 24, 2022 at 12:23:39PM +0100, Hans Schultz wrote:
>> >> On tor, mar 24, 2022 at 13:09, Vladimir Oltean <olteanv@gmail.com> wrote:
>> >> > On Thu, Mar 24, 2022 at 11:32:08AM +0100, Hans Schultz wrote:
>> >> >> On ons, mar 23, 2022 at 16:43, Vladimir Oltean <olteanv@gmail.com> wrote:
>> >> >> > On Wed, Mar 23, 2022 at 01:49:32PM +0100, Hans Schultz wrote:
>> >> >> >> >> Does someone have an idea why there at this point is no option to add a
>> >> >> >> >> dynamic fdb entry?
>> >> >> >> >> 
>> >> >> >> >> The fdb added entries here do not age out, while the ATU entries do
>> >> >> >> >> (after 5 min), resulting in unsynced ATU vs fdb.
>> >> >> >> >
>> >> >> >> > I think the expectation is to use br_fdb_external_learn_del() if the
>> >> >> >> > externally learned entry expires. The bridge should not age by itself
>> >> >> >> > FDB entries learned externally.
>> >> >> >> >
>> >> >> >> 
>> >> >> >> It seems to me that something is missing then?
>> >> >> >> My tests using trafgen that I gave a report on to Lunn generated massive
>> >> >> >> amounts of fdb entries, but after a while the ATU was clean and the fdb
>> >> >> >> was still full of random entries...
>> >> >> >
>> >> >> > I'm no longer sure where you are, sorry..
>> >> >> > I think we discussed that you need to enable ATU age interrupts in order
>> >> >> > to keep the ATU in sync with the bridge FDB? Which means either to
>> >> >> > delete the locked FDB entries from the bridge when they age out in the
>> >> >> > ATU, or to keep refreshing locked ATU entries.
>> >> >> > So it seems that you're doing neither of those 2 things if you end up
>> >> >> > with bridge FDB entries which are no longer in the ATU.
>> >> >> 
>> >> >> Any idea why G2 offset 5 ATUAgeIntEn (bit 10) is set? There is no define
>> >> >> for it, so I assume it is something default?
>> >> >
>> >> > No idea, but I can confirm that the out-of-reset value I see for
>> >> > MV88E6XXX_G2_SWITCH_MGMT on 6190 and 6390 is 0x400. It's best not to
>> >> > rely on any reset defaults though.
>> >> 
>> >> I see no age out interrupts, even though the ports Age Out Int is on
>> >> (PAV bit 14) on the locked port, and the ATU entries do age out (HoldAt1
>> >> is off). Any idea why that can be?
>> >> 
>> >> I combination with this I think it would be nice to have an ability to
>> >> set the AgeOut time even though it is not per port but global.
>> >
>> > Sorry, I just don't know. Looking at the documentation for IntOnAgeOut,
>> > I see it says that for an ATU entry to trigger an age out interrupt, the
>> > port it's associated with must have IntOnAgeOut set.
>> > But your locked ATU entries aren't associated with any port, they have
>> > DPV=0, right? So will they never trigger any age out interrupt according
>> > to this? I'm not clear.
>> 
>> I think that's absolutely right. That leaves two options. Either "port
>> 10" if it has IntOnAgeOut setting, or the reason why I wrote my comments
>> in this part of the code, that it should be able to add a dynamic entry
>> in the bridge module from the driver.
>
> I'm sorry, I wasn't fully aware of the implications of the fact that
> your 'locked' FDB entries have a DPV of all zeroes in hardware.
> Practically, this means that while the locked bridge FDB entry is
> associated with a bridge port, the ATU entry is associated with no port.
>
> In turn, the hardware cannot ever true detect station migrations,
> because it doesn't know which port this station migrates _from_ (you're
> not telling it that). Every packet with this MAC SA is a station
> migration, in effect, which you (for good reason) choose to ignore to
> avoid denial of service.
>
> Mark the locked (DPV=0) ATU entry as static, and you'll keep your CPU
> clean of any ATU miss or member violation of this MAC SA. Read this as
> "you'll need to call IT to ask them to remove it". Undesirable IMHO.
>
> Mark the locked entry as non-static, and the entry will eventually
> expire, with no interrupt to signal that - because any ATU age interrupt,
> as mentioned, is fundamentally linked to a port.
>
> You see this as a negative, and you're looking for ways to inform the
> bridge driver that the locked FDB entry went away. But you aren't
> looking at this the right way, I think. Making the mv88e6xxx driver
> remove the locked FDB entry from the bridge seems like a non-goal now.
>
> If you'd cache the locked ATU entry in the mv88e6xxx driver, and you'd
> notify switchdev only if the entry is new to the cache, then you'd
> actually still achieve something major. Yes, the bridge FDB will contain
> locked FDB entries that aren't in the ATU. But that's because your
> printer has been silent for X seconds. The policy for the printer still
> hasn't changed, as far as the mv88e6xxx, or bridge, software drivers are
> concerned. If the unauthorized printer says something again after the
> locked ATU entry expires, the mv88e6xxx driver will find its MAC SA
> in the cache of denied addresses, and reload the ATU. What this
> achieves

The driver will in this case just trigger a new miss violation and add
the entry again I think.
The problem with all this is that a malicious attack that spams the
switch with random mac addresses will be able to DOS the device as any
handling of the fdb will be too resource demanding. That is why it is
needed to remove those fdb entries after a time out, which dynamic
entries would serve.

> is that the number of ATU violation interrupts isn't proportional to the
> number of packets sent by the printer, but with the ageing time you
> configure for this ATU entry. You should be able to play with an
> entry->state in the range of 1 -> 7 and get a good compromise between
> responsiveness on station migrations and number of ATU interrupts to
> service once the locked ATU entry is invalidated. In my opinion even the
> quickest-to-expire entry->state of 1 is way better than letting every
> packet spam the CPU. And you can always keep your cached locked ATU
> entry in sync with the port that triggered the violation interrupt, and
> figure out station migrations in software this way.
>
> I hope I understood the hardware behavior correctly, I don't have any
> direct experience with 802.1X as I mentioned, and only limited and
> non-expert experience with Marvell hardware. This is just my
> interpretation of some random documentation I found online.
Vladimir Oltean March 25, 2022, 2 p.m. UTC | #15
On Fri, Mar 25, 2022 at 02:48:36PM +0100, Hans Schultz wrote:
> > If you'd cache the locked ATU entry in the mv88e6xxx driver, and you'd
> > notify switchdev only if the entry is new to the cache, then you'd
> > actually still achieve something major. Yes, the bridge FDB will contain
> > locked FDB entries that aren't in the ATU. But that's because your
> > printer has been silent for X seconds. The policy for the printer still
> > hasn't changed, as far as the mv88e6xxx, or bridge, software drivers are
> > concerned. If the unauthorized printer says something again after the
> > locked ATU entry expires, the mv88e6xxx driver will find its MAC SA
> > in the cache of denied addresses, and reload the ATU. What this
> > achieves
> 
> The driver will in this case just trigger a new miss violation and add
> the entry again I think.
> The problem with all this is that a malicious attack that spams the
> switch with random mac addresses will be able to DOS the device as any
> handling of the fdb will be too resource demanding. That is why it is
> needed to remove those fdb entries after a time out, which dynamic
> entries would serve.

An attacker sweeping through the 2^47 source MAC address range is a
problem regardless of the implementations proposed so far, no?
If unlimited growth of the mv88e6xxx locked ATU entry cache is a
concern (which it is), we could limit its size, and when we purge a
cached entry in software is also when we could emit a
SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
Hans S March 25, 2022, 4:01 p.m. UTC | #16
On fre, mar 25, 2022 at 16:00, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Fri, Mar 25, 2022 at 02:48:36PM +0100, Hans Schultz wrote:
>> > If you'd cache the locked ATU entry in the mv88e6xxx driver, and you'd
>> > notify switchdev only if the entry is new to the cache, then you'd
>> > actually still achieve something major. Yes, the bridge FDB will contain
>> > locked FDB entries that aren't in the ATU. But that's because your
>> > printer has been silent for X seconds. The policy for the printer still
>> > hasn't changed, as far as the mv88e6xxx, or bridge, software drivers are
>> > concerned. If the unauthorized printer says something again after the
>> > locked ATU entry expires, the mv88e6xxx driver will find its MAC SA
>> > in the cache of denied addresses, and reload the ATU. What this
>> > achieves
>> 
>> The driver will in this case just trigger a new miss violation and add
>> the entry again I think.
>> The problem with all this is that a malicious attack that spams the
>> switch with random mac addresses will be able to DOS the device as any
>> handling of the fdb will be too resource demanding. That is why it is
>> needed to remove those fdb entries after a time out, which dynamic
>> entries would serve.
>
> An attacker sweeping through the 2^47 source MAC address range is a
> problem regardless of the implementations proposed so far, no?

The idea is to have a count on the number of locked entries in both the
ATU and the FDB, so that a limit on entries can be enforced.

> If unlimited growth of the mv88e6xxx locked ATU entry cache is a
> concern (which it is), we could limit its size, and when we purge a
> cached entry in software is also when we could emit a
> SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?

I think the best would be dynamic entries in both the ATU and the FDB
for locked entries. How the two are kept in sync is another question,
but if there is a switchcore, it will be the 'master', so I don't think
the bridge module will need to tell the switchcore to remove entries in
that case. Or?
Vladimir Oltean March 25, 2022, 8:30 p.m. UTC | #17
On Fri, Mar 25, 2022 at 05:01:59PM +0100, Hans Schultz wrote:
> > An attacker sweeping through the 2^47 source MAC address range is a
> > problem regardless of the implementations proposed so far, no?
> 
> The idea is to have a count on the number of locked entries in both the
> ATU and the FDB, so that a limit on entries can be enforced.

I can agree with that.

Note that as far as I understand regular 802.1X, these locked FDB
entries are just bloatware if you don't need MAC authentication bypass,
because the source port is already locked, so it drops all traffic from
an unknown MAC SA except for the link-local packets necessary to run
EAPOL, which are trapped to the CPU.

So maybe user space should opt into the MAC authentication bypass
process, really, since that requires secure CPU-assisted learning, and
regular 802.1X doesn't. It's a real additional burden that shouldn't be
ignored or enabled by default.

> > If unlimited growth of the mv88e6xxx locked ATU entry cache is a
> > concern (which it is), we could limit its size, and when we purge a
> > cached entry in software is also when we could emit a
> > SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
> 
> I think the best would be dynamic entries in both the ATU and the FDB
> for locked entries.

Making locked (DPV=0) ATU entries be dynamic (age out) makes sense.
Since you set the IgnoreWrongData for source ports, you suppress ATU
interrupts for this MAC SA, which in turn means that a station which is
unauthorized on port A can never redeem itself when it migrates to port B,
for which it does have an authorization, since software never receives
any notice that it has moved to a new port.

But making the locked bridge FDB entry be dynamic, why does it matter?
I'm not seeing this through. To denote that it can migrate, or to denote
that it can age out? These locked FDB entries are 'extern_learn', so
they aren't aged out by the bridge anyway, they are aged out by whomever
added them => in our case the SWITCHDEV_FDB_DEL_TO_BRIDGE that I mentioned.

> How the two are kept in sync is another question, but if there is a
> switchcore, it will be the 'master', so I don't think the bridge
> module will need to tell the switchcore to remove entries in that
> case. Or?

The bridge will certainly not *need* to tell the switch to delete a
locked FDB entry, but it certainly *can* (and this is in fact part of
the authorization process, replace an ATU entry with DPV=0 with an ATU
entry with DPV=BIT(port)).

I feel as if I'm missing the essence of your reply.
Hans S March 28, 2022, 7:38 a.m. UTC | #18
On fre, mar 25, 2022 at 22:30, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Fri, Mar 25, 2022 at 05:01:59PM +0100, Hans Schultz wrote:
>> > An attacker sweeping through the 2^47 source MAC address range is a
>> > problem regardless of the implementations proposed so far, no?
>> 
>> The idea is to have a count on the number of locked entries in both the
>> ATU and the FDB, so that a limit on entries can be enforced.
>
> I can agree with that.
>
> Note that as far as I understand regular 802.1X, these locked FDB
> entries are just bloatware if you don't need MAC authentication bypass,
> because the source port is already locked, so it drops all traffic from
> an unknown MAC SA except for the link-local packets necessary to run
> EAPOL, which are trapped to the CPU.

802.1X and MAC Auth can be completely seperated by hostapd listning
directly on the locked port interface before entering the bridge.

>
> So maybe user space should opt into the MAC authentication bypass
> process, really, since that requires secure CPU-assisted learning, and
> regular 802.1X doesn't. It's a real additional burden that shouldn't be
> ignored or enabled by default.
>
>> > If unlimited growth of the mv88e6xxx locked ATU entry cache is a
>> > concern (which it is), we could limit its size, and when we purge a
>> > cached entry in software is also when we could emit a
>> > SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
>> 
>> I think the best would be dynamic entries in both the ATU and the FDB
>> for locked entries.
>
> Making locked (DPV=0) ATU entries be dynamic (age out) makes sense.
> Since you set the IgnoreWrongData for source ports, you suppress ATU
> interrupts for this MAC SA, which in turn means that a station which is
> unauthorized on port A can never redeem itself when it migrates to port B,
> for which it does have an authorization, since software never receives
> any notice that it has moved to a new port.
>
> But making the locked bridge FDB entry be dynamic, why does it matter?
> I'm not seeing this through. To denote that it can migrate, or to denote
> that it can age out? These locked FDB entries are 'extern_learn', so
> they aren't aged out by the bridge anyway, they are aged out by whomever
> added them => in our case the SWITCHDEV_FDB_DEL_TO_BRIDGE that I mentioned.
>
I think the FDB and the ATU should be as much in sync as possible, and
the FDB definitely should not keep stale entries that only get removed
by link down. The SWITCHDEV_FDB_DEL_TO_BRIDGE route would requre an
interrupt when a entry ages out in the ATU, but we know that that cannot
happen with DPV=0. Thus the need to add dynamic entries with
SWITCHDEV_FDB_ADD_TO_BRIDGE. 

>> How the two are kept in sync is another question, but if there is a
>> switchcore, it will be the 'master', so I don't think the bridge
>> module will need to tell the switchcore to remove entries in that
>> case. Or?
>
> The bridge will certainly not *need* to tell the switch to delete a
> locked FDB entry, but it certainly *can* (and this is in fact part of
> the authorization process, replace an ATU entry with DPV=0 with an ATU
> entry with DPV=BIT(port)).

Yes you are right, but I was implicitly only regarding internal
mechanisms in the 'bridge + switchcore', and not userspace netlink
commands.
>
> I feel as if I'm missing the essence of your reply.
Vladimir Oltean March 28, 2022, 8:48 a.m. UTC | #19
On Mon, Mar 28, 2022 at 09:38:33AM +0200, Hans Schultz wrote:
> On fre, mar 25, 2022 at 22:30, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Fri, Mar 25, 2022 at 05:01:59PM +0100, Hans Schultz wrote:
> >> > An attacker sweeping through the 2^47 source MAC address range is a
> >> > problem regardless of the implementations proposed so far, no?
> >> 
> >> The idea is to have a count on the number of locked entries in both the
> >> ATU and the FDB, so that a limit on entries can be enforced.
> >
> > I can agree with that.
> >
> > Note that as far as I understand regular 802.1X, these locked FDB
> > entries are just bloatware if you don't need MAC authentication bypass,
> > because the source port is already locked, so it drops all traffic from
> > an unknown MAC SA except for the link-local packets necessary to run
> > EAPOL, which are trapped to the CPU.
> 
> 802.1X and MAC Auth can be completely seperated by hostapd listning
> directly on the locked port interface before entering the bridge.

I don't understand this, sorry. What do you mean "before entering the
bridge"?

> > So maybe user space should opt into the MAC authentication bypass
> > process, really, since that requires secure CPU-assisted learning, and
> > regular 802.1X doesn't. It's a real additional burden that shouldn't be
> > ignored or enabled by default.
> >
> >> > If unlimited growth of the mv88e6xxx locked ATU entry cache is a
> >> > concern (which it is), we could limit its size, and when we purge a
> >> > cached entry in software is also when we could emit a
> >> > SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
> >> 
> >> I think the best would be dynamic entries in both the ATU and the FDB
> >> for locked entries.
> >
> > Making locked (DPV=0) ATU entries be dynamic (age out) makes sense.
> > Since you set the IgnoreWrongData for source ports, you suppress ATU
> > interrupts for this MAC SA, which in turn means that a station which is
> > unauthorized on port A can never redeem itself when it migrates to port B,
> > for which it does have an authorization, since software never receives
> > any notice that it has moved to a new port.
> >
> > But making the locked bridge FDB entry be dynamic, why does it matter?
> > I'm not seeing this through. To denote that it can migrate, or to denote
> > that it can age out? These locked FDB entries are 'extern_learn', so
> > they aren't aged out by the bridge anyway, they are aged out by whomever
> > added them => in our case the SWITCHDEV_FDB_DEL_TO_BRIDGE that I mentioned.
> >
> I think the FDB and the ATU should be as much in sync as possible, and
> the FDB definitely should not keep stale entries that only get removed
> by link down. The SWITCHDEV_FDB_DEL_TO_BRIDGE route would requre an
> interrupt when a entry ages out in the ATU, but we know that that cannot
> happen with DPV=0. Thus the need to add dynamic entries with
> SWITCHDEV_FDB_ADD_TO_BRIDGE. 

So what is your suggestion exactly? You want the driver to notify the
locked FDB entry via FDB_ADD_TO_BRIDGE with the dynamic flag, and then
rely on the bridge's software ageing timer to delete it? How does that
deletion propagate back to the driver then? I'm unclear on the ownership
model you propose.

> >> How the two are kept in sync is another question, but if there is a
> >> switchcore, it will be the 'master', so I don't think the bridge
> >> module will need to tell the switchcore to remove entries in that
> >> case. Or?
> >
> > The bridge will certainly not *need* to tell the switch to delete a
> > locked FDB entry, but it certainly *can* (and this is in fact part of
> > the authorization process, replace an ATU entry with DPV=0 with an ATU
> > entry with DPV=BIT(port)).
> 
> Yes you are right, but I was implicitly only regarding internal
> mechanisms in the 'bridge + switchcore', and not userspace netlink
> commands.
> >
> > I feel as if I'm missing the essence of your reply.
Hans S March 28, 2022, 9:31 a.m. UTC | #20
On mån, mar 28, 2022 at 11:48, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Mon, Mar 28, 2022 at 09:38:33AM +0200, Hans Schultz wrote:
>> On fre, mar 25, 2022 at 22:30, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Fri, Mar 25, 2022 at 05:01:59PM +0100, Hans Schultz wrote:
>> >> > An attacker sweeping through the 2^47 source MAC address range is a
>> >> > problem regardless of the implementations proposed so far, no?
>> >> 
>> >> The idea is to have a count on the number of locked entries in both the
>> >> ATU and the FDB, so that a limit on entries can be enforced.
>> >
>> > I can agree with that.
>> >
>> > Note that as far as I understand regular 802.1X, these locked FDB
>> > entries are just bloatware if you don't need MAC authentication bypass,
>> > because the source port is already locked, so it drops all traffic from
>> > an unknown MAC SA except for the link-local packets necessary to run
>> > EAPOL, which are trapped to the CPU.
>> 
>> 802.1X and MAC Auth can be completely seperated by hostapd listning
>> directly on the locked port interface before entering the bridge.
>
> I don't understand this, sorry. What do you mean "before entering the
> bridge"?
>
RAW socket on network slave device.

>> > So maybe user space should opt into the MAC authentication bypass
>> > process, really, since that requires secure CPU-assisted learning, and
>> > regular 802.1X doesn't. It's a real additional burden that shouldn't be
>> > ignored or enabled by default.
>> >
>> >> > If unlimited growth of the mv88e6xxx locked ATU entry cache is a
>> >> > concern (which it is), we could limit its size, and when we purge a
>> >> > cached entry in software is also when we could emit a
>> >> > SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
>> >> 
>> >> I think the best would be dynamic entries in both the ATU and the FDB
>> >> for locked entries.
>> >
>> > Making locked (DPV=0) ATU entries be dynamic (age out) makes sense.
>> > Since you set the IgnoreWrongData for source ports, you suppress ATU
>> > interrupts for this MAC SA, which in turn means that a station which is
>> > unauthorized on port A can never redeem itself when it migrates to port B,
>> > for which it does have an authorization, since software never receives
>> > any notice that it has moved to a new port.
>> >
>> > But making the locked bridge FDB entry be dynamic, why does it matter?
>> > I'm not seeing this through. To denote that it can migrate, or to denote
>> > that it can age out? These locked FDB entries are 'extern_learn', so
>> > they aren't aged out by the bridge anyway, they are aged out by whomever
>> > added them => in our case the SWITCHDEV_FDB_DEL_TO_BRIDGE that I mentioned.
>> >
>> I think the FDB and the ATU should be as much in sync as possible, and
>> the FDB definitely should not keep stale entries that only get removed
>> by link down. The SWITCHDEV_FDB_DEL_TO_BRIDGE route would requre an
>> interrupt when a entry ages out in the ATU, but we know that that cannot
>> happen with DPV=0. Thus the need to add dynamic entries with
>> SWITCHDEV_FDB_ADD_TO_BRIDGE. 
>
> So what is your suggestion exactly? You want the driver to notify the
> locked FDB entry via FDB_ADD_TO_BRIDGE with the dynamic flag, and then
> rely on the bridge's software ageing timer to delete it? How does that
> deletion propagate back to the driver then? I'm unclear on the ownership
> model you propose.
>

As the FDB and the ATU will age out the entry with the same timeout,
they will stay relatively in sync compared to the situation where the
switchcore driver will not be able to notify the bridge that a zero DPV
entry has aged out as it has no port association.

>> >> How the two are kept in sync is another question, but if there is a
>> >> switchcore, it will be the 'master', so I don't think the bridge
>> >> module will need to tell the switchcore to remove entries in that
>> >> case. Or?
>> >
>> > The bridge will certainly not *need* to tell the switch to delete a
>> > locked FDB entry, but it certainly *can* (and this is in fact part of
>> > the authorization process, replace an ATU entry with DPV=0 with an ATU
>> > entry with DPV=BIT(port)).
>> 
>> Yes you are right, but I was implicitly only regarding internal
>> mechanisms in the 'bridge + switchcore', and not userspace netlink
>> commands.
>> >
>> > I feel as if I'm missing the essence of your reply.
Vladimir Oltean March 28, 2022, 3:12 p.m. UTC | #21
On Mon, Mar 28, 2022 at 11:31:43AM +0200, Hans Schultz wrote:
> On mån, mar 28, 2022 at 11:48, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Mon, Mar 28, 2022 at 09:38:33AM +0200, Hans Schultz wrote:
> >> On fre, mar 25, 2022 at 22:30, Vladimir Oltean <olteanv@gmail.com> wrote:
> >> > On Fri, Mar 25, 2022 at 05:01:59PM +0100, Hans Schultz wrote:
> >> >> > An attacker sweeping through the 2^47 source MAC address range is a
> >> >> > problem regardless of the implementations proposed so far, no?
> >> >> 
> >> >> The idea is to have a count on the number of locked entries in both the
> >> >> ATU and the FDB, so that a limit on entries can be enforced.
> >> >
> >> > I can agree with that.
> >> >
> >> > Note that as far as I understand regular 802.1X, these locked FDB
> >> > entries are just bloatware if you don't need MAC authentication bypass,
> >> > because the source port is already locked, so it drops all traffic from
> >> > an unknown MAC SA except for the link-local packets necessary to run
> >> > EAPOL, which are trapped to the CPU.
> >> 
> >> 802.1X and MAC Auth can be completely seperated by hostapd listning
> >> directly on the locked port interface before entering the bridge.
> >
> > I don't understand this, sorry. What do you mean "before entering the
> > bridge"?
> >
> RAW socket on network slave device.

But as far as the port and its driver are concerned, there is a lot of
unnecessary functionality going on in the background if you don't need
MAC authentication bypass. All non-EAPOL packets could be unauthorized
without CPU intervention by simply not enabling CPU-assisted secure
learning in the first place. You might consider cutting off some of that
overhead by making user space opt into secure learning.

> >> > So maybe user space should opt into the MAC authentication bypass
> >> > process, really, since that requires secure CPU-assisted learning, and
> >> > regular 802.1X doesn't. It's a real additional burden that shouldn't be
> >> > ignored or enabled by default.
> >> >
> >> >> > If unlimited growth of the mv88e6xxx locked ATU entry cache is a
> >> >> > concern (which it is), we could limit its size, and when we purge a
> >> >> > cached entry in software is also when we could emit a
> >> >> > SWITCHDEV_FDB_DEL_TO_BRIDGE for it, right?
> >> >> 
> >> >> I think the best would be dynamic entries in both the ATU and the FDB
> >> >> for locked entries.
> >> >
> >> > Making locked (DPV=0) ATU entries be dynamic (age out) makes sense.
> >> > Since you set the IgnoreWrongData for source ports, you suppress ATU
> >> > interrupts for this MAC SA, which in turn means that a station which is
> >> > unauthorized on port A can never redeem itself when it migrates to port B,
> >> > for which it does have an authorization, since software never receives
> >> > any notice that it has moved to a new port.
> >> >
> >> > But making the locked bridge FDB entry be dynamic, why does it matter?
> >> > I'm not seeing this through. To denote that it can migrate, or to denote
> >> > that it can age out? These locked FDB entries are 'extern_learn', so
> >> > they aren't aged out by the bridge anyway, they are aged out by whomever
> >> > added them => in our case the SWITCHDEV_FDB_DEL_TO_BRIDGE that I mentioned.
> >> >
> >> I think the FDB and the ATU should be as much in sync as possible, and
> >> the FDB definitely should not keep stale entries that only get removed
> >> by link down. The SWITCHDEV_FDB_DEL_TO_BRIDGE route would requre an
> >> interrupt when a entry ages out in the ATU, but we know that that cannot
> >> happen with DPV=0. Thus the need to add dynamic entries with
> >> SWITCHDEV_FDB_ADD_TO_BRIDGE. 
> >
> > So what is your suggestion exactly? You want the driver to notify the
> > locked FDB entry via FDB_ADD_TO_BRIDGE with the dynamic flag, and then
> > rely on the bridge's software ageing timer to delete it? How does that
> > deletion propagate back to the driver then? I'm unclear on the ownership
> > model you propose.
> >
> 
> As the FDB and the ATU will age out the entry with the same timeout,
> they will stay relatively in sync compared to the situation where the
> switchcore driver will not be able to notify the bridge that a zero DPV
> entry has aged out as it has no port association.

So if the DPV=0 ATU entry doesn't get refreshed when a packet hits it
(even to get dropped), then I suppose the drift between software and
hardware ageing timers could be kept more or less under control.

But you still need to change switchdev and the bridge driver to support
this pattern, and you need to make a compelling case for it, because the
lack of a FDB_DEL_TO_BRIDGE notifier _is_ a concern in the general case.

And if you say "well, you know, the reason why I don't need to emit the
FDB_DEL_TO_BRIDGE is because I lied about the FDB entry's port association
in the first place (during FDB_ADD_TO_BRIDGE), it really is associated
with no port rather than with the port I said, just go with it", well,
that might not be the strongest argument for a new kind of externally
learned FDB entry. Anyway I'll defer to bridge and switchdev maintainers.

> >> >> How the two are kept in sync is another question, but if there is a
> >> >> switchcore, it will be the 'master', so I don't think the bridge
> >> >> module will need to tell the switchcore to remove entries in that
> >> >> case. Or?
> >> >
> >> > The bridge will certainly not *need* to tell the switch to delete a
> >> > locked FDB entry, but it certainly *can* (and this is in fact part of
> >> > the authorization process, replace an ATU entry with DPV=0 with an ATU
> >> > entry with DPV=BIT(port)).
> >> 
> >> Yes you are right, but I was implicitly only regarding internal
> >> mechanisms in the 'bridge + switchcore', and not userspace netlink
> >> commands.
> >> >
> >> > I feel as if I'm missing the essence of your reply.
diff mbox series

Patch

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 3e424d40fae3..d5d923411f5e 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -229,7 +229,8 @@  struct switchdev_notifier_fdb_info {
 	u16 vid;
 	u8 added_by_user:1,
 	   is_local:1,
-	   offloaded:1;
+	   offloaded:1,
+	   locked:1;
 };
 
 struct switchdev_notifier_port_obj_info {
diff --git a/net/bridge/br.c b/net/bridge/br.c
index b1dea3febeea..adcdbecbc218 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -166,7 +166,8 @@  static int br_switchdev_event(struct notifier_block *unused,
 	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
 		fdb_info = ptr;
 		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
-						fdb_info->vid, false);
+						fdb_info->vid, false,
+						fdb_info->locked);
 		if (err) {
 			err = notifier_from_errno(err);
 			break;
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 57ec559a85a7..57aa1955d34d 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -987,7 +987,7 @@  static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
 					   "FDB entry towards bridge must be permanent");
 			return -EINVAL;
 		}
-		err = br_fdb_external_learn_add(br, p, addr, vid, true);
+		err = br_fdb_external_learn_add(br, p, addr, vid, true, false);
 	} else {
 		spin_lock_bh(&br->hash_lock);
 		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
@@ -1216,7 +1216,7 @@  void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
 
 int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
 			      const unsigned char *addr, u16 vid,
-			      bool swdev_notify)
+			      bool swdev_notify, bool locked)
 {
 	struct net_bridge_fdb_entry *fdb;
 	bool modified = false;
@@ -1236,6 +1236,9 @@  int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
 		if (!p)
 			flags |= BIT(BR_FDB_LOCAL);
 
+		if (locked)
+			flags |= BIT(BR_FDB_ENTRY_LOCKED);
+
 		fdb = fdb_create(br, p, addr, vid, flags);
 		if (!fdb) {
 			err = -ENOMEM;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index f5a0b68c4857..3275e33b112f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -790,7 +790,7 @@  int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
 void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
 int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
 			      const unsigned char *addr, u16 vid,
-			      bool swdev_notify);
+			      bool swdev_notify, bool locked);
 int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
 			      const unsigned char *addr, u16 vid,
 			      bool swdev_notify);