This patch set extends the locked port feature for devices
that are behind a locked port, but do not have the ability to
authorize themselves as a supplicant using IEEE 802.1X.
Such devices can be printers, meters or anything related to
fixed installations. Instead of 802.1X authorization, devices
can get access based on their MAC addresses being whitelisted.
For an authorization daemon to detect that a device is trying
to get access through a locked port, the bridge will add the
MAC address of the device to the FDB with a locked flag to it.
Thus the authorization daemon can catch the FDB add event and
check if the MAC address is in the whitelist and if so replace
the FDB entry without the locked flag enabled, and thus open
the port for the device.
This feature is known as MAC-Auth or MAC Authentication Bypass
(MAB) in Cisco terminology, where the full MAB concept involves
additional Cisco infrastructure for authorization. There is no
real authentication process, as the MAC address of the device
is the only input the authorization daemon, in the general
case, has to base the decision if to unlock the port or not.
With this patch set, an implementation of the offloaded case is
supplied for the mv88e6xxx driver. When a packet ingresses on
a locked port, an ATU miss violation event will occur. When
handling such ATU miss violation interrupts, the MAC address of
the device is added to the FDB with a zero destination port
vector (DPV) and the MAC address is communicated through the
switchdev layer to the bridge, so that a FDB entry with the
locked flag enabled can be added.
Hans Schultz (3):
net: bridge: add fdb flag to extent locked port feature
net: switchdev: add support for offloading of fdb locked flag
net: dsa: mv88e6xxx: mac-auth/MAB implementation
drivers/net/dsa/mv88e6xxx/Makefile | 1 +
drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
drivers/net/dsa/mv88e6xxx/global1.h | 1 +
drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
.../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
.../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
drivers/net/dsa/mv88e6xxx/port.c | 11 +++
drivers/net/dsa/mv88e6xxx/port.h | 1 +
include/net/switchdev.h | 3 +-
include/uapi/linux/neighbour.h | 1 +
net/bridge/br.c | 3 +-
net/bridge/br_fdb.c | 13 +++-
net/bridge/br_input.c | 11 ++-
net/bridge/br_private.h | 5 +-
15 files changed, 167 insertions(+), 14 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
--
2.30.2
This implementation for the Marvell mv88e6xxx chip series, is
based on handling ATU miss violations occurring when packets
ingress on a port that is locked. The mac address triggering
the ATU miss violation is communicated through switchdev to
the bridge module, which adds a fdb entry with the fdb locked
flag set.
Note: The locked port must have learning enabled for the ATU
miss violation to occur.
Signed-off-by: Hans Schultz <[email protected]>
---
drivers/net/dsa/mv88e6xxx/Makefile | 1 +
drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
drivers/net/dsa/mv88e6xxx/global1.h | 1 +
drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
.../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
.../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
drivers/net/dsa/mv88e6xxx/port.c | 11 +++
drivers/net/dsa/mv88e6xxx/port.h | 1 +
9 files changed, 138 insertions(+), 7 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index c8eca2b6f959..3ca57709730d 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -15,3 +15,4 @@ mv88e6xxx-objs += port_hidden.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
mv88e6xxx-objs += serdes.o
mv88e6xxx-objs += smi.o
+mv88e6xxx-objs += mv88e6xxx_switchdev.o
\ No newline at end of file
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 84b90fc36c58..e1b6bd738085 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1714,11 +1714,11 @@ static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
return err;
}
-static int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
- int (*cb)(struct mv88e6xxx_chip *chip,
- const struct mv88e6xxx_vtu_entry *entry,
- void *priv),
- void *priv)
+int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
+ int (*cb)(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_vtu_entry *entry,
+ void *priv),
+ void *priv)
{
struct mv88e6xxx_vtu_entry entry = {
.vid = mv88e6xxx_max_vid(chip),
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 30b92a265613..64e8fc470fdf 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -763,6 +763,11 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)
mutex_unlock(&chip->reg_lock);
}
+int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
+ int (*cb)(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_vtu_entry *entry,
+ void *priv),
+ void *priv);
int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap);
#endif /* _MV88E6XXX_CHIP_H */
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index 2c1607c858a1..729cc0610d9a 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -136,6 +136,7 @@
#define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000
#define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0
#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
+#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS 0x0000
#define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED 0x0000
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST 0x0001
diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
index 40bd67a5c8e9..afa54fe8667e 100644
--- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
+++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
@@ -12,6 +12,8 @@
#include "chip.h"
#include "global1.h"
+#include "port.h"
+#include "mv88e6xxx_switchdev.h"
/* Offset 0x01: ATU FID Register */
@@ -114,6 +116,18 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0);
}
+static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
+ MV88E6XXX_G1_ATU_OP_BUSY | MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g1_atu_op_wait(chip);
+}
+
static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
{
u16 val;
@@ -356,11 +370,11 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
int spid;
int err;
u16 val;
+ u16 fid;
mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_g1_atu_op(chip, 0,
- MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
+ err = mv88e6xxx_g1_read_atu_violation(chip);
if (err)
goto out;
@@ -368,6 +382,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
if (err)
goto out;
+ err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &fid);
+ if (err)
+ goto out;
+
err = mv88e6xxx_g1_atu_data_read(chip, &entry);
if (err)
goto out;
@@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
"ATU miss violation for %pM portvec %x spid %d\n",
entry.mac, entry.portvec, spid);
chip->ports[spid].atu_miss_violation++;
+ if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
+ err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
+ chip->ports[spid].port,
+ &entry,
+ fid);
+ if (err)
+ goto out;
}
if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
new file mode 100644
index 000000000000..e0ca452b6f86
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mv88e6xxx_switchdev.c
+ *
+ * Authors:
+ * Hans J. Schultz <[email protected]>
+ *
+ */
+
+#include <net/switchdev.h>
+#include "chip.h"
+#include "global1.h"
+
+struct mv88e6xxx_fid_search_ctx {
+ u16 fid_search;
+ u16 vid_found;
+};
+
+static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_vtu_entry *entry,
+ void *priv)
+{
+ struct mv88e6xxx_fid_search_ctx *ctx = priv;
+
+ if (ctx->fid_search == entry->fid) {
+ ctx->vid_found = entry->vid;
+ return 1;
+ }
+ return 0;
+}
+
+int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
+ int port,
+ struct mv88e6xxx_atu_entry *entry,
+ u16 fid)
+{
+ struct switchdev_notifier_fdb_info info = {
+ .addr = entry->mac,
+ .vid = 0,
+ .added_by_user = false,
+ .is_local = false,
+ .offloaded = true,
+ .locked = true,
+ };
+ struct mv88e6xxx_fid_search_ctx ctx;
+ struct netlink_ext_ack *extack;
+ struct net_device *brport;
+ struct dsa_port *dp;
+ int err;
+
+ ctx.fid_search = fid;
+ err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
+ if (err < 0)
+ return err;
+ if (err == 1)
+ info.vid = ctx.vid_found;
+ else
+ return -ENODATA;
+
+ dp = dsa_to_port(chip->ds, port);
+ brport = dsa_port_to_bridge_port(dp);
+ err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, extack);
+ if (err)
+ return err;
+ entry->portvec = MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS;
+ return mv88e6xxx_g1_atu_loadpurge(chip, fid, entry);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
new file mode 100644
index 000000000000..127f3098f745
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * mv88e6xxx_switchdev.h
+ *
+ * Authors:
+ * Hans J. Schultz <[email protected]>
+ *
+ */
+
+#ifndef DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
+#define DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
+
+#include <net/switchdev.h>
+
+int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
+ int port,
+ struct mv88e6xxx_atu_entry *entry,
+ u16 fid);
+
+#endif /* DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_ */
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 795b3128768f..6b375b0caa2c 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -1239,6 +1239,17 @@ int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
return err;
}
+bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port)
+{
+ u16 reg;
+
+ if (mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®))
+ return false;
+ if (!(reg & MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK))
+ return false;
+ return true;
+}
+
int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
bool locked)
{
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index e0a705d82019..09ea8f1615bb 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -374,6 +374,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
+bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port);
int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
bool locked);
--
2.30.2
Used for Mac-auth/MAB feature in the offloaded case.
Signed-off-by: Hans Schultz <[email protected]>
---
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 396dcf3084cf..91387aa7e400 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);
--
2.30.2
> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> + chip->ports[spid].port,
> + &entry,
> + fid);
> +static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
> + const struct mv88e6xxx_vtu_entry *entry,
> + void *priv)
> +{
> + struct mv88e6xxx_fid_search_ctx *ctx = priv;
> +
> + if (ctx->fid_search == entry->fid) {
> + ctx->vid_found = entry->vid;
> + return 1;
> + }
> + return 0;
> +}
> +
> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
> + int port,
> + struct mv88e6xxx_atu_entry *entry,
> + u16 fid)
> +{
> + struct switchdev_notifier_fdb_info info = {
> + .addr = entry->mac,
> + .vid = 0,
> + .added_by_user = false,
> + .is_local = false,
> + .offloaded = true,
> + .locked = true,
> + };
> + struct mv88e6xxx_fid_search_ctx ctx;
> + struct netlink_ext_ack *extack;
> + struct net_device *brport;
> + struct dsa_port *dp;
> + int err;
> +
> + ctx.fid_search = fid;
> + err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
I could be reading this code wrong, but it looks like you assume there
is a single new entry in the ATU. But interrupts on these devices are
slow. It would be easy for two or more devices to pop into existence
at the same time. Don't you need to walk the whole ATU to find all the
new entries? Have you tried this with a traffic generating populating
the ATU with new entries at different rates, up to line rate? Do you
get notifications for them all?
Andrew
On Thu, Mar 10, 2022 at 03:23:20PM +0100, Hans Schultz wrote:
> This implementation for the Marvell mv88e6xxx chip series, is
> based on handling ATU miss violations occurring when packets
> ingress on a port that is locked. The mac address triggering
> the ATU miss violation is communicated through switchdev to
> the bridge module, which adds a fdb entry with the fdb locked
> flag set.
> Note: The locked port must have learning enabled for the ATU
> miss violation to occur.
>
> Signed-off-by: Hans Schultz <[email protected]>
> ---
> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
> drivers/net/dsa/mv88e6xxx/port.h | 1 +
> 9 files changed, 138 insertions(+), 7 deletions(-)
> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>
> diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
> index c8eca2b6f959..3ca57709730d 100644
> --- a/drivers/net/dsa/mv88e6xxx/Makefile
> +++ b/drivers/net/dsa/mv88e6xxx/Makefile
> @@ -15,3 +15,4 @@ mv88e6xxx-objs += port_hidden.o
> mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
> mv88e6xxx-objs += serdes.o
> mv88e6xxx-objs += smi.o
> +mv88e6xxx-objs += mv88e6xxx_switchdev.o
> \ No newline at end of file
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
> index 84b90fc36c58..e1b6bd738085 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.c
> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
> @@ -1714,11 +1714,11 @@ static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
> return err;
> }
>
> -static int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
> - int (*cb)(struct mv88e6xxx_chip *chip,
> - const struct mv88e6xxx_vtu_entry *entry,
> - void *priv),
> - void *priv)
> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
> + int (*cb)(struct mv88e6xxx_chip *chip,
> + const struct mv88e6xxx_vtu_entry *entry,
> + void *priv),
> + void *priv)
> {
> struct mv88e6xxx_vtu_entry entry = {
> .vid = mv88e6xxx_max_vid(chip),
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
> index 30b92a265613..64e8fc470fdf 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.h
> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
> @@ -763,6 +763,11 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)
> mutex_unlock(&chip->reg_lock);
> }
>
> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
> + int (*cb)(struct mv88e6xxx_chip *chip,
> + const struct mv88e6xxx_vtu_entry *entry,
> + void *priv),
> + void *priv);
> int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap);
>
> #endif /* _MV88E6XXX_CHIP_H */
> diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
> index 2c1607c858a1..729cc0610d9a 100644
> --- a/drivers/net/dsa/mv88e6xxx/global1.h
> +++ b/drivers/net/dsa/mv88e6xxx/global1.h
> @@ -136,6 +136,7 @@
> #define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000
> #define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0
> #define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
> +#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS 0x0000
> #define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f
> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED 0x0000
> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST 0x0001
> diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
> index 40bd67a5c8e9..afa54fe8667e 100644
> --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
> +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
> @@ -12,6 +12,8 @@
>
> #include "chip.h"
> #include "global1.h"
> +#include "port.h"
> +#include "mv88e6xxx_switchdev.h"
>
> /* Offset 0x01: ATU FID Register */
>
> @@ -114,6 +116,18 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
> return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0);
> }
>
> +static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip)
> +{
> + int err;
> +
> + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
> + MV88E6XXX_G1_ATU_OP_BUSY | MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
> + if (err)
> + return err;
> +
> + return mv88e6xxx_g1_atu_op_wait(chip);
> +}
> +
> static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
> {
> u16 val;
> @@ -356,11 +370,11 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> int spid;
> int err;
> u16 val;
> + u16 fid;
>
> mv88e6xxx_reg_lock(chip);
>
> - err = mv88e6xxx_g1_atu_op(chip, 0,
> - MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
> + err = mv88e6xxx_g1_read_atu_violation(chip);
> if (err)
> goto out;
>
> @@ -368,6 +382,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> if (err)
> goto out;
>
> + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &fid);
> + if (err)
> + goto out;
> +
> err = mv88e6xxx_g1_atu_data_read(chip, &entry);
> if (err)
> goto out;
> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> "ATU miss violation for %pM portvec %x spid %d\n",
> entry.mac, entry.portvec, spid);
> chip->ports[spid].atu_miss_violation++;
> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> + chip->ports[spid].port,
> + &entry,
> + fid);
Do we want to suppress the ATU miss violation warnings if we're going to
notify the bridge, or is it better to keep them for some reason?
My logic is that they're part of normal operation, so suppressing makes
sense.
> + if (err)
> + goto out;
> }
>
> if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
> new file mode 100644
> index 000000000000..e0ca452b6f86
> --- /dev/null
> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
> @@ -0,0 +1,67 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * mv88e6xxx_switchdev.c
> + *
> + * Authors:
> + * Hans J. Schultz <[email protected]>
> + *
> + */
> +
> +#include <net/switchdev.h>
> +#include "chip.h"
> +#include "global1.h"
> +
> +struct mv88e6xxx_fid_search_ctx {
> + u16 fid_search;
> + u16 vid_found;
> +};
> +
> +static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
> + const struct mv88e6xxx_vtu_entry *entry,
> + void *priv)
> +{
> + struct mv88e6xxx_fid_search_ctx *ctx = priv;
> +
> + if (ctx->fid_search == entry->fid) {
> + ctx->vid_found = entry->vid;
> + return 1;
> + }
> + return 0;
> +}
> +
> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
> + int port,
> + struct mv88e6xxx_atu_entry *entry,
> + u16 fid)
> +{
> + struct switchdev_notifier_fdb_info info = {
> + .addr = entry->mac,
> + .vid = 0,
> + .added_by_user = false,
> + .is_local = false,
> + .offloaded = true,
> + .locked = true,
> + };
> + struct mv88e6xxx_fid_search_ctx ctx;
> + struct netlink_ext_ack *extack;
> + struct net_device *brport;
> + struct dsa_port *dp;
> + int err;
> +
> + ctx.fid_search = fid;
> + err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
> + if (err < 0)
> + return err;
> + if (err == 1)
> + info.vid = ctx.vid_found;
> + else
> + return -ENODATA;
> +
> + dp = dsa_to_port(chip->ds, port);
> + brport = dsa_port_to_bridge_port(dp);
Since this is threaded interrupt context, I suppose it could race with
dsa_port_bridge_leave(). So it is best to check whether "brport" is NULL
or not.
Speaking of races with dsa_port_bridge_leave().. does SWITCHDEV_FDB_ADD_TO_BRIDGE
not require rtnl_lock?
> + err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, extack);
It is buggy to pass an uninitialized on-stack extack, just pass NULL if
there's no one to consume it.
Alternatively, if the bridge produces a valid extack message for errors
in this case (I haven't checked), it may be more useful to manually
print the extack._msg to the kernel log - see dsa_switch_sync_vlan_filtering()
for an example.
I am a bit uncomfortable having every driver implement this ad-hoc and
potentially have a gazillion subtle bugs like these, could we have a
common function exported by DSA that deals with SWITCHDEV_FDB_ADD_TO_BRIDGE?
> + if (err)
> + return err;
> + entry->portvec = MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS;
> + return mv88e6xxx_g1_atu_loadpurge(chip, fid, entry);
> +}
> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
> new file mode 100644
> index 000000000000..127f3098f745
> --- /dev/null
> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * mv88e6xxx_switchdev.h
> + *
> + * Authors:
> + * Hans J. Schultz <[email protected]>
> + *
> + */
> +
> +#ifndef DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
> +#define DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
> +
> +#include <net/switchdev.h>
> +
> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
> + int port,
> + struct mv88e6xxx_atu_entry *entry,
> + u16 fid);
> +
> +#endif /* DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_ */
> diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
> index 795b3128768f..6b375b0caa2c 100644
> --- a/drivers/net/dsa/mv88e6xxx/port.c
> +++ b/drivers/net/dsa/mv88e6xxx/port.c
> @@ -1239,6 +1239,17 @@ int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
> return err;
> }
>
> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port)
> +{
> + u16 reg;
> +
> + if (mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®))
> + return false;
> + if (!(reg & MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK))
> + return false;
> + return true;
> +}
> +
> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
> bool locked)
> {
> diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
> index e0a705d82019..09ea8f1615bb 100644
> --- a/drivers/net/dsa/mv88e6xxx/port.h
> +++ b/drivers/net/dsa/mv88e6xxx/port.h
> @@ -374,6 +374,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
> int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
> int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
>
> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port);
> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
> bool locked);
>
> --
> 2.30.2
>
On tor, mar 10, 2022 at 16:28, Vladimir Oltean <[email protected]> wrote:
> On Thu, Mar 10, 2022 at 03:23:20PM +0100, Hans Schultz wrote:
>> This implementation for the Marvell mv88e6xxx chip series, is
>> based on handling ATU miss violations occurring when packets
>> ingress on a port that is locked. The mac address triggering
>> the ATU miss violation is communicated through switchdev to
>> the bridge module, which adds a fdb entry with the fdb locked
>> flag set.
>> Note: The locked port must have learning enabled for the ATU
>> miss violation to occur.
>>
>> Signed-off-by: Hans Schultz <[email protected]>
>> ---
>> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
>> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
>> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
>> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
>> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
>> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
>> drivers/net/dsa/mv88e6xxx/port.h | 1 +
>> 9 files changed, 138 insertions(+), 7 deletions(-)
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>>
>> diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
>> index c8eca2b6f959..3ca57709730d 100644
>> --- a/drivers/net/dsa/mv88e6xxx/Makefile
>> +++ b/drivers/net/dsa/mv88e6xxx/Makefile
>> @@ -15,3 +15,4 @@ mv88e6xxx-objs += port_hidden.o
>> mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
>> mv88e6xxx-objs += serdes.o
>> mv88e6xxx-objs += smi.o
>> +mv88e6xxx-objs += mv88e6xxx_switchdev.o
>> \ No newline at end of file
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
>> index 84b90fc36c58..e1b6bd738085 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.c
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
>> @@ -1714,11 +1714,11 @@ static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
>> return err;
>> }
>>
>> -static int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> - int (*cb)(struct mv88e6xxx_chip *chip,
>> - const struct mv88e6xxx_vtu_entry *entry,
>> - void *priv),
>> - void *priv)
>> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> + int (*cb)(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv),
>> + void *priv)
>> {
>> struct mv88e6xxx_vtu_entry entry = {
>> .vid = mv88e6xxx_max_vid(chip),
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
>> index 30b92a265613..64e8fc470fdf 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.h
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
>> @@ -763,6 +763,11 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)
>> mutex_unlock(&chip->reg_lock);
>> }
>>
>> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> + int (*cb)(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv),
>> + void *priv);
>> int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap);
>>
>> #endif /* _MV88E6XXX_CHIP_H */
>> diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
>> index 2c1607c858a1..729cc0610d9a 100644
>> --- a/drivers/net/dsa/mv88e6xxx/global1.h
>> +++ b/drivers/net/dsa/mv88e6xxx/global1.h
>> @@ -136,6 +136,7 @@
>> #define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000
>> #define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0
>> #define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
>> +#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS 0x0000
>> #define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f
>> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED 0x0000
>> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST 0x0001
>> diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> index 40bd67a5c8e9..afa54fe8667e 100644
>> --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> @@ -12,6 +12,8 @@
>>
>> #include "chip.h"
>> #include "global1.h"
>> +#include "port.h"
>> +#include "mv88e6xxx_switchdev.h"
>>
>> /* Offset 0x01: ATU FID Register */
>>
>> @@ -114,6 +116,18 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
>> return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0);
>> }
>>
>> +static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip)
>> +{
>> + int err;
>> +
>> + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
>> + MV88E6XXX_G1_ATU_OP_BUSY | MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
>> + if (err)
>> + return err;
>> +
>> + return mv88e6xxx_g1_atu_op_wait(chip);
>> +}
>> +
>> static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
>> {
>> u16 val;
>> @@ -356,11 +370,11 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> int spid;
>> int err;
>> u16 val;
>> + u16 fid;
>>
>> mv88e6xxx_reg_lock(chip);
>>
>> - err = mv88e6xxx_g1_atu_op(chip, 0,
>> - MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
>> + err = mv88e6xxx_g1_read_atu_violation(chip);
>> if (err)
>> goto out;
>>
>> @@ -368,6 +382,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> if (err)
>> goto out;
>>
>> + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &fid);
>> + if (err)
>> + goto out;
>> +
>> err = mv88e6xxx_g1_atu_data_read(chip, &entry);
>> if (err)
>> goto out;
>> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> "ATU miss violation for %pM portvec %x spid %d\n",
>> entry.mac, entry.portvec, spid);
>> chip->ports[spid].atu_miss_violation++;
>> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> + chip->ports[spid].port,
>> + &entry,
>> + fid);
>
> Do we want to suppress the ATU miss violation warnings if we're going to
> notify the bridge, or is it better to keep them for some reason?
> My logic is that they're part of normal operation, so suppressing makes
> sense.
>
Only one issue is that the ATU miss violations would not be reported on
ports that are not locked, while the bridge will not be notified either.
>> + if (err)
>> + goto out;
>> }
>>
>> if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
>> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> new file mode 100644
>> index 000000000000..e0ca452b6f86
>> --- /dev/null
>> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> @@ -0,0 +1,67 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * mv88e6xxx_switchdev.c
>> + *
>> + * Authors:
>> + * Hans J. Schultz <[email protected]>
>> + *
>> + */
>> +
>> +#include <net/switchdev.h>
>> +#include "chip.h"
>> +#include "global1.h"
>> +
>> +struct mv88e6xxx_fid_search_ctx {
>> + u16 fid_search;
>> + u16 vid_found;
>> +};
>> +
>> +static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv)
>> +{
>> + struct mv88e6xxx_fid_search_ctx *ctx = priv;
>> +
>> + if (ctx->fid_search == entry->fid) {
>> + ctx->vid_found = entry->vid;
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
>> + int port,
>> + struct mv88e6xxx_atu_entry *entry,
>> + u16 fid)
>> +{
>> + struct switchdev_notifier_fdb_info info = {
>> + .addr = entry->mac,
>> + .vid = 0,
>> + .added_by_user = false,
>> + .is_local = false,
>> + .offloaded = true,
>> + .locked = true,
>> + };
>> + struct mv88e6xxx_fid_search_ctx ctx;
>> + struct netlink_ext_ack *extack;
>> + struct net_device *brport;
>> + struct dsa_port *dp;
>> + int err;
>> +
>> + ctx.fid_search = fid;
>> + err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
>> + if (err < 0)
>> + return err;
>> + if (err == 1)
>> + info.vid = ctx.vid_found;
>> + else
>> + return -ENODATA;
>> +
>> + dp = dsa_to_port(chip->ds, port);
>> + brport = dsa_port_to_bridge_port(dp);
>
> Since this is threaded interrupt context, I suppose it could race with
> dsa_port_bridge_leave(). So it is best to check whether "brport" is NULL
> or not.
>
> Speaking of races with dsa_port_bridge_leave().. does SWITCHDEV_FDB_ADD_TO_BRIDGE
> not require rtnl_lock?
>
>> + err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, extack);
>
> It is buggy to pass an uninitialized on-stack extack, just pass NULL if
> there's no one to consume it.
>
> Alternatively, if the bridge produces a valid extack message for errors
> in this case (I haven't checked), it may be more useful to manually
> print the extack._msg to the kernel log - see dsa_switch_sync_vlan_filtering()
> for an example.
>
> I am a bit uncomfortable having every driver implement this ad-hoc and
> potentially have a gazillion subtle bugs like these, could we have a
> common function exported by DSA that deals with SWITCHDEV_FDB_ADD_TO_BRIDGE?
>
>> + if (err)
>> + return err;
>> + entry->portvec = MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS;
>> + return mv88e6xxx_g1_atu_loadpurge(chip, fid, entry);
>> +}
>> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>> new file mode 100644
>> index 000000000000..127f3098f745
>> --- /dev/null
>> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>> @@ -0,0 +1,20 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later
>> + *
>> + * mv88e6xxx_switchdev.h
>> + *
>> + * Authors:
>> + * Hans J. Schultz <[email protected]>
>> + *
>> + */
>> +
>> +#ifndef DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
>> +#define DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
>> +
>> +#include <net/switchdev.h>
>> +
>> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
>> + int port,
>> + struct mv88e6xxx_atu_entry *entry,
>> + u16 fid);
>> +
>> +#endif /* DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_ */
>> diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
>> index 795b3128768f..6b375b0caa2c 100644
>> --- a/drivers/net/dsa/mv88e6xxx/port.c
>> +++ b/drivers/net/dsa/mv88e6xxx/port.c
>> @@ -1239,6 +1239,17 @@ int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
>> return err;
>> }
>>
>> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port)
>> +{
>> + u16 reg;
>> +
>> + if (mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®))
>> + return false;
>> + if (!(reg & MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK))
>> + return false;
>> + return true;
>> +}
>> +
>> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
>> bool locked)
>> {
>> diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
>> index e0a705d82019..09ea8f1615bb 100644
>> --- a/drivers/net/dsa/mv88e6xxx/port.h
>> +++ b/drivers/net/dsa/mv88e6xxx/port.h
>> @@ -374,6 +374,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
>> int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
>> int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
>>
>> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port);
>> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
>> bool locked);
>>
>> --
>> 2.30.2
>>
On tor, mar 10, 2022 at 15:54, Andrew Lunn <[email protected]> wrote:
>> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> + chip->ports[spid].port,
>> + &entry,
>> + fid);
>
>> +static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv)
>> +{
>> + struct mv88e6xxx_fid_search_ctx *ctx = priv;
>> +
>> + if (ctx->fid_search == entry->fid) {
>> + ctx->vid_found = entry->vid;
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
>> + int port,
>> + struct mv88e6xxx_atu_entry *entry,
>> + u16 fid)
>> +{
>> + struct switchdev_notifier_fdb_info info = {
>> + .addr = entry->mac,
>> + .vid = 0,
>> + .added_by_user = false,
>> + .is_local = false,
>> + .offloaded = true,
>> + .locked = true,
>> + };
>> + struct mv88e6xxx_fid_search_ctx ctx;
>> + struct netlink_ext_ack *extack;
>> + struct net_device *brport;
>> + struct dsa_port *dp;
>> + int err;
>> +
>> + ctx.fid_search = fid;
>> + err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
>
> I could be reading this code wrong, but it looks like you assume there
> is a single new entry in the ATU. But interrupts on these devices are
> slow. It would be easy for two or more devices to pop into existence
> at the same time. Don't you need to walk the whole ATU to find all the
> new entries? Have you tried this with a traffic generating populating
> the ATU with new entries at different rates, up to line rate? Do you
> get notifications for them all?
>
> Andrew
We have not tried your said test, but if a packet doesn't manage to
trigger a ATU miss violation interrupt, not much will happen as far as I
see. The device sending the packet will not get access, but if it sends
again (maybe after a short while), it can still trigger the ATU miss
violation interrupt and get access.
I think that the normal behaviour for a device would be to try and
connect, and if that is not successfull inside a short time, it will wait
for a timeout before trying again.
On mån, mar 14, 2022 at 17:50, Ido Schimmel <[email protected]> wrote:
> On Thu, Mar 10, 2022 at 03:23:17PM +0100, Hans Schultz wrote:
>> This patch set extends the locked port feature for devices
>> that are behind a locked port, but do not have the ability to
>> authorize themselves as a supplicant using IEEE 802.1X.
>> Such devices can be printers, meters or anything related to
>> fixed installations. Instead of 802.1X authorization, devices
>> can get access based on their MAC addresses being whitelisted.
>>
>> For an authorization daemon to detect that a device is trying
>> to get access through a locked port, the bridge will add the
>> MAC address of the device to the FDB with a locked flag to it.
>> Thus the authorization daemon can catch the FDB add event and
>> check if the MAC address is in the whitelist and if so replace
>> the FDB entry without the locked flag enabled, and thus open
>> the port for the device.
>>
>> This feature is known as MAC-Auth or MAC Authentication Bypass
>> (MAB) in Cisco terminology, where the full MAB concept involves
>> additional Cisco infrastructure for authorization. There is no
>> real authentication process, as the MAC address of the device
>> is the only input the authorization daemon, in the general
>> case, has to base the decision if to unlock the port or not.
>>
>> With this patch set, an implementation of the offloaded case is
>> supplied for the mv88e6xxx driver. When a packet ingresses on
>> a locked port, an ATU miss violation event will occur. When
>
> When do you get an ATU miss violation? In case there is no FDB entry for
> the SA or also when there is an FDB entry, but it points to a different
> port? I see that the bridge will only create a "locked" FDB entry in
> case there is no existing entry, but it will not transition an existing
> entry to "locked" state. I guess ATU miss refers to an actual miss and
> not mismatch.
>
On a locked port, I get ATU miss violations when there is no FDB entry
for the SA, while if there is an entry but it is not assigned to the
port, then I get an ATU member violation (which I have now masked on
locked ports to limit unwanted interrupts).
So it seems to me that my 'ATU miss' corresponds to your MISS and my
'ATU member' corresponds to your MISMATCH. Since I inject an entry with
destination port vector (DPV) zero I get member violations after the
first miss violation.
> The HW I work with doesn't have the ability to generate such
> notifications, but it can trap packets on MISS (no entry) or MISMATCH
> (exists, but with different port). I believe that in order to support
> this feature we need to inject MISS-ed packets to the Rx path so that
> eventually the bridge itself will create the "locked" entry as opposed
> to notifying the bridge about the entry as in your case.
>
This seems to me to be the way forward in your case. What kind or family
of chips is your HW based on?
>> handling such ATU miss violation interrupts, the MAC address of
>> the device is added to the FDB with a zero destination port
>> vector (DPV) and the MAC address is communicated through the
>> switchdev layer to the bridge, so that a FDB entry with the
>> locked flag enabled can be added.
>>
>> Hans Schultz (3):
>> net: bridge: add fdb flag to extent locked port feature
>> net: switchdev: add support for offloading of fdb locked flag
>> net: dsa: mv88e6xxx: mac-auth/MAB implementation
>
> Please extend tools/testing/selftests/net/forwarding/bridge_locked_port.sh
> with new test cases for this code.
>
Shall do.
>>
>> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
>> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
>> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
>> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
>> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
>> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
>> drivers/net/dsa/mv88e6xxx/port.h | 1 +
>> include/net/switchdev.h | 3 +-
>> include/uapi/linux/neighbour.h | 1 +
>> net/bridge/br.c | 3 +-
>> net/bridge/br_fdb.c | 13 +++-
>> net/bridge/br_input.c | 11 ++-
>> net/bridge/br_private.h | 5 +-
>> 15 files changed, 167 insertions(+), 14 deletions(-)
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>>
>> --
>> 2.30.2
>>
On tor, mar 10, 2022 at 16:28, Vladimir Oltean <[email protected]> wrote:
> On Thu, Mar 10, 2022 at 03:23:20PM +0100, Hans Schultz wrote:
>> This implementation for the Marvell mv88e6xxx chip series, is
>> based on handling ATU miss violations occurring when packets
>> ingress on a port that is locked. The mac address triggering
>> the ATU miss violation is communicated through switchdev to
>> the bridge module, which adds a fdb entry with the fdb locked
>> flag set.
>> Note: The locked port must have learning enabled for the ATU
>> miss violation to occur.
>>
>> Signed-off-by: Hans Schultz <[email protected]>
>> ---
>> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
>> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
>> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
>> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
>> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
>> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
>> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
>> drivers/net/dsa/mv88e6xxx/port.h | 1 +
>> 9 files changed, 138 insertions(+), 7 deletions(-)
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>>
>> diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
>> index c8eca2b6f959..3ca57709730d 100644
>> --- a/drivers/net/dsa/mv88e6xxx/Makefile
>> +++ b/drivers/net/dsa/mv88e6xxx/Makefile
>> @@ -15,3 +15,4 @@ mv88e6xxx-objs += port_hidden.o
>> mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
>> mv88e6xxx-objs += serdes.o
>> mv88e6xxx-objs += smi.o
>> +mv88e6xxx-objs += mv88e6xxx_switchdev.o
>> \ No newline at end of file
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
>> index 84b90fc36c58..e1b6bd738085 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.c
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
>> @@ -1714,11 +1714,11 @@ static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
>> return err;
>> }
>>
>> -static int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> - int (*cb)(struct mv88e6xxx_chip *chip,
>> - const struct mv88e6xxx_vtu_entry *entry,
>> - void *priv),
>> - void *priv)
>> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> + int (*cb)(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv),
>> + void *priv)
>> {
>> struct mv88e6xxx_vtu_entry entry = {
>> .vid = mv88e6xxx_max_vid(chip),
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
>> index 30b92a265613..64e8fc470fdf 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.h
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
>> @@ -763,6 +763,11 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)
>> mutex_unlock(&chip->reg_lock);
>> }
>>
>> +int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
>> + int (*cb)(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv),
>> + void *priv);
>> int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap);
>>
>> #endif /* _MV88E6XXX_CHIP_H */
>> diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
>> index 2c1607c858a1..729cc0610d9a 100644
>> --- a/drivers/net/dsa/mv88e6xxx/global1.h
>> +++ b/drivers/net/dsa/mv88e6xxx/global1.h
>> @@ -136,6 +136,7 @@
>> #define MV88E6XXX_G1_ATU_DATA_TRUNK 0x8000
>> #define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK 0x00f0
>> #define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
>> +#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS 0x0000
>> #define MV88E6XXX_G1_ATU_DATA_STATE_MASK 0x000f
>> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED 0x0000
>> #define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST 0x0001
>> diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> index 40bd67a5c8e9..afa54fe8667e 100644
>> --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
>> @@ -12,6 +12,8 @@
>>
>> #include "chip.h"
>> #include "global1.h"
>> +#include "port.h"
>> +#include "mv88e6xxx_switchdev.h"
>>
>> /* Offset 0x01: ATU FID Register */
>>
>> @@ -114,6 +116,18 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
>> return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0);
>> }
>>
>> +static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip)
>> +{
>> + int err;
>> +
>> + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
>> + MV88E6XXX_G1_ATU_OP_BUSY | MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
>> + if (err)
>> + return err;
>> +
>> + return mv88e6xxx_g1_atu_op_wait(chip);
>> +}
>> +
>> static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
>> {
>> u16 val;
>> @@ -356,11 +370,11 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> int spid;
>> int err;
>> u16 val;
>> + u16 fid;
>>
>> mv88e6xxx_reg_lock(chip);
>>
>> - err = mv88e6xxx_g1_atu_op(chip, 0,
>> - MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
>> + err = mv88e6xxx_g1_read_atu_violation(chip);
>> if (err)
>> goto out;
>>
>> @@ -368,6 +382,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> if (err)
>> goto out;
>>
>> + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &fid);
>> + if (err)
>> + goto out;
>> +
>> err = mv88e6xxx_g1_atu_data_read(chip, &entry);
>> if (err)
>> goto out;
>> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> "ATU miss violation for %pM portvec %x spid %d\n",
>> entry.mac, entry.portvec, spid);
>> chip->ports[spid].atu_miss_violation++;
>> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> + chip->ports[spid].port,
>> + &entry,
>> + fid);
>
> Do we want to suppress the ATU miss violation warnings if we're going to
> notify the bridge, or is it better to keep them for some reason?
> My logic is that they're part of normal operation, so suppressing makes
> sense.
>
I have been seeing many ATU member violations after the miss violation is
handled (using ping), and I think it could be considered to suppress the ATU member
violations interrupts by setting the IgnoreWrongData bit for the
port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> + if (err)
>> + goto out;
>> }
>>
>> if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
>> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> new file mode 100644
>> index 000000000000..e0ca452b6f86
>> --- /dev/null
>> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
>> @@ -0,0 +1,67 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * mv88e6xxx_switchdev.c
>> + *
>> + * Authors:
>> + * Hans J. Schultz <[email protected]>
>> + *
>> + */
>> +
>> +#include <net/switchdev.h>
>> +#include "chip.h"
>> +#include "global1.h"
>> +
>> +struct mv88e6xxx_fid_search_ctx {
>> + u16 fid_search;
>> + u16 vid_found;
>> +};
>> +
>> +static int mv88e6xxx_find_vid_on_matching_fid(struct mv88e6xxx_chip *chip,
>> + const struct mv88e6xxx_vtu_entry *entry,
>> + void *priv)
>> +{
>> + struct mv88e6xxx_fid_search_ctx *ctx = priv;
>> +
>> + if (ctx->fid_search == entry->fid) {
>> + ctx->vid_found = entry->vid;
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
>> + int port,
>> + struct mv88e6xxx_atu_entry *entry,
>> + u16 fid)
>> +{
>> + struct switchdev_notifier_fdb_info info = {
>> + .addr = entry->mac,
>> + .vid = 0,
>> + .added_by_user = false,
>> + .is_local = false,
>> + .offloaded = true,
>> + .locked = true,
>> + };
>> + struct mv88e6xxx_fid_search_ctx ctx;
>> + struct netlink_ext_ack *extack;
>> + struct net_device *brport;
>> + struct dsa_port *dp;
>> + int err;
>> +
>> + ctx.fid_search = fid;
>> + err = mv88e6xxx_vtu_walk(chip, mv88e6xxx_find_vid_on_matching_fid, &ctx);
>> + if (err < 0)
>> + return err;
>> + if (err == 1)
>> + info.vid = ctx.vid_found;
>> + else
>> + return -ENODATA;
>> +
>> + dp = dsa_to_port(chip->ds, port);
>> + brport = dsa_port_to_bridge_port(dp);
>
> Since this is threaded interrupt context, I suppose it could race with
> dsa_port_bridge_leave(). So it is best to check whether "brport" is NULL
> or not.
>
> Speaking of races with dsa_port_bridge_leave().. does SWITCHDEV_FDB_ADD_TO_BRIDGE
> not require rtnl_lock?
>
>> + err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, extack);
>
> It is buggy to pass an uninitialized on-stack extack, just pass NULL if
> there's no one to consume it.
>
> Alternatively, if the bridge produces a valid extack message for errors
> in this case (I haven't checked), it may be more useful to manually
> print the extack._msg to the kernel log - see dsa_switch_sync_vlan_filtering()
> for an example.
>
> I am a bit uncomfortable having every driver implement this ad-hoc and
> potentially have a gazillion subtle bugs like these, could we have a
> common function exported by DSA that deals with SWITCHDEV_FDB_ADD_TO_BRIDGE?
>
>> + if (err)
>> + return err;
>> + entry->portvec = MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_NO_EGRESS;
>> + return mv88e6xxx_g1_atu_loadpurge(chip, fid, entry);
>> +}
>> diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>> new file mode 100644
>> index 000000000000..127f3098f745
>> --- /dev/null
>> +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>> @@ -0,0 +1,20 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later
>> + *
>> + * mv88e6xxx_switchdev.h
>> + *
>> + * Authors:
>> + * Hans J. Schultz <[email protected]>
>> + *
>> + */
>> +
>> +#ifndef DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
>> +#define DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_
>> +
>> +#include <net/switchdev.h>
>> +
>> +int mv88e6xxx_switchdev_handle_atu_miss_violation(struct mv88e6xxx_chip *chip,
>> + int port,
>> + struct mv88e6xxx_atu_entry *entry,
>> + u16 fid);
>> +
>> +#endif /* DRIVERS_NET_DSA_MV88E6XXX_MV88E6XXX_SWITCHDEV_H_ */
>> diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
>> index 795b3128768f..6b375b0caa2c 100644
>> --- a/drivers/net/dsa/mv88e6xxx/port.c
>> +++ b/drivers/net/dsa/mv88e6xxx/port.c
>> @@ -1239,6 +1239,17 @@ int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
>> return err;
>> }
>>
>> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port)
>> +{
>> + u16 reg;
>> +
>> + if (mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®))
>> + return false;
>> + if (!(reg & MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK))
>> + return false;
>> + return true;
>> +}
>> +
>> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
>> bool locked)
>> {
>> diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
>> index e0a705d82019..09ea8f1615bb 100644
>> --- a/drivers/net/dsa/mv88e6xxx/port.h
>> +++ b/drivers/net/dsa/mv88e6xxx/port.h
>> @@ -374,6 +374,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
>> int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
>> int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
>>
>> +bool mv88e6xxx_port_is_locked(struct mv88e6xxx_chip *chip, int port);
>> int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
>> bool locked);
>>
>> --
>> 2.30.2
>>
On 3/10/2022 6:23 AM, Hans Schultz wrote:
> This patch set extends the locked port feature for devices
> that are behind a locked port, but do not have the ability to
> authorize themselves as a supplicant using IEEE 802.1X.
> Such devices can be printers, meters or anything related to
> fixed installations. Instead of 802.1X authorization, devices
> can get access based on their MAC addresses being whitelisted.
>
> For an authorization daemon to detect that a device is trying
> to get access through a locked port, the bridge will add the
> MAC address of the device to the FDB with a locked flag to it.
> Thus the authorization daemon can catch the FDB add event and
> check if the MAC address is in the whitelist and if so replace
> the FDB entry without the locked flag enabled, and thus open
> the port for the device.
>
> This feature is known as MAC-Auth or MAC Authentication Bypass
> (MAB) in Cisco terminology, where the full MAB concept involves
> additional Cisco infrastructure for authorization. There is no
> real authentication process, as the MAC address of the device
> is the only input the authorization daemon, in the general
> case, has to base the decision if to unlock the port or not.
>
> With this patch set, an implementation of the offloaded case is
> supplied for the mv88e6xxx driver. When a packet ingresses on
> a locked port, an ATU miss violation event will occur. When
> handling such ATU miss violation interrupts, the MAC address of
> the device is added to the FDB with a zero destination port
> vector (DPV) and the MAC address is communicated through the
> switchdev layer to the bridge, so that a FDB entry with the
> locked flag enabled can be added.
FWIW, we may have about a 30% - 70% split between switches that will
signal ATU violations over a side band interrupt, like mv88e6xxx will,
and the rest will likely signal such events via the proprietary tag format.
--
Florian
On Tue, Mar 15, 2022 at 09:59:49AM +0100, Hans Schultz wrote:
> On m?n, mar 14, 2022 at 17:50, Ido Schimmel <[email protected]> wrote:
> > On Thu, Mar 10, 2022 at 03:23:17PM +0100, Hans Schultz wrote:
> >> This patch set extends the locked port feature for devices
> >> that are behind a locked port, but do not have the ability to
> >> authorize themselves as a supplicant using IEEE 802.1X.
> >> Such devices can be printers, meters or anything related to
> >> fixed installations. Instead of 802.1X authorization, devices
> >> can get access based on their MAC addresses being whitelisted.
> >>
> >> For an authorization daemon to detect that a device is trying
> >> to get access through a locked port, the bridge will add the
> >> MAC address of the device to the FDB with a locked flag to it.
> >> Thus the authorization daemon can catch the FDB add event and
> >> check if the MAC address is in the whitelist and if so replace
> >> the FDB entry without the locked flag enabled, and thus open
> >> the port for the device.
> >>
> >> This feature is known as MAC-Auth or MAC Authentication Bypass
> >> (MAB) in Cisco terminology, where the full MAB concept involves
> >> additional Cisco infrastructure for authorization. There is no
> >> real authentication process, as the MAC address of the device
> >> is the only input the authorization daemon, in the general
> >> case, has to base the decision if to unlock the port or not.
> >>
> >> With this patch set, an implementation of the offloaded case is
> >> supplied for the mv88e6xxx driver. When a packet ingresses on
> >> a locked port, an ATU miss violation event will occur. When
> >
> > When do you get an ATU miss violation? In case there is no FDB entry for
> > the SA or also when there is an FDB entry, but it points to a different
> > port? I see that the bridge will only create a "locked" FDB entry in
> > case there is no existing entry, but it will not transition an existing
> > entry to "locked" state. I guess ATU miss refers to an actual miss and
> > not mismatch.
> >
>
> On a locked port, I get ATU miss violations when there is no FDB entry
> for the SA, while if there is an entry but it is not assigned to the
> port, then I get an ATU member violation (which I have now masked on
> locked ports to limit unwanted interrupts).
>
> So it seems to me that my 'ATU miss' corresponds to your MISS and my
> 'ATU member' corresponds to your MISMATCH. Since I inject an entry with
> destination port vector (DPV) zero I get member violations after the
> first miss violation.
Which causes packets to be silently dropped by the device? Sounds OK, I
just want to verify I understand the behavior.
>
> > The HW I work with doesn't have the ability to generate such
> > notifications, but it can trap packets on MISS (no entry) or MISMATCH
> > (exists, but with different port). I believe that in order to support
> > this feature we need to inject MISS-ed packets to the Rx path so that
> > eventually the bridge itself will create the "locked" entry as opposed
> > to notifying the bridge about the entry as in your case.
> >
>
> This seems to me to be the way forward in your case. What kind or family
> of chips is your HW based on?
Nvidia Spectrum ASICs. Some users mentioned 802.1X support, but a
requirement never materialized so we didn't work on it.
>
> >> handling such ATU miss violation interrupts, the MAC address of
> >> the device is added to the FDB with a zero destination port
> >> vector (DPV) and the MAC address is communicated through the
> >> switchdev layer to the bridge, so that a FDB entry with the
> >> locked flag enabled can be added.
> >>
> >> Hans Schultz (3):
> >> net: bridge: add fdb flag to extent locked port feature
> >> net: switchdev: add support for offloading of fdb locked flag
> >> net: dsa: mv88e6xxx: mac-auth/MAB implementation
> >
> > Please extend tools/testing/selftests/net/forwarding/bridge_locked_port.sh
> > with new test cases for this code.
> >
>
> Shall do.
Thanks!
>
> >>
> >> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
> >> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
> >> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
> >> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
> >> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
> >> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
> >> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
> >> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
> >> drivers/net/dsa/mv88e6xxx/port.h | 1 +
> >> include/net/switchdev.h | 3 +-
> >> include/uapi/linux/neighbour.h | 1 +
> >> net/bridge/br.c | 3 +-
> >> net/bridge/br_fdb.c | 13 +++-
> >> net/bridge/br_input.c | 11 ++-
> >> net/bridge/br_private.h | 5 +-
> >> 15 files changed, 167 insertions(+), 14 deletions(-)
> >> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
> >> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
> >>
> >> --
> >> 2.30.2
> >>
On Thu, Mar 10, 2022 at 03:23:17PM +0100, Hans Schultz wrote:
> This patch set extends the locked port feature for devices
> that are behind a locked port, but do not have the ability to
> authorize themselves as a supplicant using IEEE 802.1X.
> Such devices can be printers, meters or anything related to
> fixed installations. Instead of 802.1X authorization, devices
> can get access based on their MAC addresses being whitelisted.
>
> For an authorization daemon to detect that a device is trying
> to get access through a locked port, the bridge will add the
> MAC address of the device to the FDB with a locked flag to it.
> Thus the authorization daemon can catch the FDB add event and
> check if the MAC address is in the whitelist and if so replace
> the FDB entry without the locked flag enabled, and thus open
> the port for the device.
>
> This feature is known as MAC-Auth or MAC Authentication Bypass
> (MAB) in Cisco terminology, where the full MAB concept involves
> additional Cisco infrastructure for authorization. There is no
> real authentication process, as the MAC address of the device
> is the only input the authorization daemon, in the general
> case, has to base the decision if to unlock the port or not.
>
> With this patch set, an implementation of the offloaded case is
> supplied for the mv88e6xxx driver. When a packet ingresses on
> a locked port, an ATU miss violation event will occur. When
When do you get an ATU miss violation? In case there is no FDB entry for
the SA or also when there is an FDB entry, but it points to a different
port? I see that the bridge will only create a "locked" FDB entry in
case there is no existing entry, but it will not transition an existing
entry to "locked" state. I guess ATU miss refers to an actual miss and
not mismatch.
The HW I work with doesn't have the ability to generate such
notifications, but it can trap packets on MISS (no entry) or MISMATCH
(exists, but with different port). I believe that in order to support
this feature we need to inject MISS-ed packets to the Rx path so that
eventually the bridge itself will create the "locked" entry as opposed
to notifying the bridge about the entry as in your case.
> handling such ATU miss violation interrupts, the MAC address of
> the device is added to the FDB with a zero destination port
> vector (DPV) and the MAC address is communicated through the
> switchdev layer to the bridge, so that a FDB entry with the
> locked flag enabled can be added.
>
> Hans Schultz (3):
> net: bridge: add fdb flag to extent locked port feature
> net: switchdev: add support for offloading of fdb locked flag
> net: dsa: mv88e6xxx: mac-auth/MAB implementation
Please extend tools/testing/selftests/net/forwarding/bridge_locked_port.sh
with new test cases for this code.
>
> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
> drivers/net/dsa/mv88e6xxx/chip.c | 10 +--
> drivers/net/dsa/mv88e6xxx/chip.h | 5 ++
> drivers/net/dsa/mv88e6xxx/global1.h | 1 +
> drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++-
> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c | 67 +++++++++++++++++++
> .../net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h | 20 ++++++
> drivers/net/dsa/mv88e6xxx/port.c | 11 +++
> drivers/net/dsa/mv88e6xxx/port.h | 1 +
> include/net/switchdev.h | 3 +-
> include/uapi/linux/neighbour.h | 1 +
> net/bridge/br.c | 3 +-
> net/bridge/br_fdb.c | 13 +++-
> net/bridge/br_input.c | 11 ++-
> net/bridge/br_private.h | 5 +-
> 15 files changed, 167 insertions(+), 14 deletions(-)
> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.c
> create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_switchdev.h
>
> --
> 2.30.2
>
On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> >> "ATU miss violation for %pM portvec %x spid %d\n",
> >> entry.mac, entry.portvec, spid);
> >> chip->ports[spid].atu_miss_violation++;
> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> >> + chip->ports[spid].port,
> >> + &entry,
> >> + fid);
> >
> > Do we want to suppress the ATU miss violation warnings if we're going to
> > notify the bridge, or is it better to keep them for some reason?
> > My logic is that they're part of normal operation, so suppressing makes
> > sense.
> >
>
> I have been seeing many ATU member violations after the miss violation is
> handled (using ping), and I think it could be considered to suppress the ATU member
> violations interrupts by setting the IgnoreWrongData bit for the
> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
So the first packet with a given MAC SA triggers an ATU miss violation
interrupt.
You program that MAC SA into the ATU with a destination port mask of all
zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
now generates ATU member violations, because the MAC SA _is_ present in
the ATU, but not towards the expected port (in fact, towards _no_ port).
Especially if user space decides it doesn't want to authorize this MAC
SA, it really becomes a problem because this is now a vector for denial
of service, with every packet triggering an ATU member violation
interrupt.
So your suggestion is to set the IgnoreWrongData bit on locked ports,
and this will suppress the actual member violation interrupts for
traffic coming from these ports.
So if the user decides to unplug a previously authorized printer from
switch port 1 and move it to port 2, how is this handled? If there isn't
a mechanism in place to delete the locked FDB entry when the printer
goes away, then by setting IgnoreWrongData you're effectively also
suppressing migration notifications.
Oh, btw, my question was: could you consider suppressing the _prints_ on
an ATU miss violation on a locked port?
On ons, mar 16, 2022 at 17:18, Florian Fainelli <[email protected]> wrote:
> On 3/10/2022 6:23 AM, Hans Schultz wrote:
>> This patch set extends the locked port feature for devices
>> that are behind a locked port, but do not have the ability to
>> authorize themselves as a supplicant using IEEE 802.1X.
>> Such devices can be printers, meters or anything related to
>> fixed installations. Instead of 802.1X authorization, devices
>> can get access based on their MAC addresses being whitelisted.
>>
>> For an authorization daemon to detect that a device is trying
>> to get access through a locked port, the bridge will add the
>> MAC address of the device to the FDB with a locked flag to it.
>> Thus the authorization daemon can catch the FDB add event and
>> check if the MAC address is in the whitelist and if so replace
>> the FDB entry without the locked flag enabled, and thus open
>> the port for the device.
>>
>> This feature is known as MAC-Auth or MAC Authentication Bypass
>> (MAB) in Cisco terminology, where the full MAB concept involves
>> additional Cisco infrastructure for authorization. There is no
>> real authentication process, as the MAC address of the device
>> is the only input the authorization daemon, in the general
>> case, has to base the decision if to unlock the port or not.
>>
>> With this patch set, an implementation of the offloaded case is
>> supplied for the mv88e6xxx driver. When a packet ingresses on
>> a locked port, an ATU miss violation event will occur. When
>> handling such ATU miss violation interrupts, the MAC address of
>> the device is added to the FDB with a zero destination port
>> vector (DPV) and the MAC address is communicated through the
>> switchdev layer to the bridge, so that a FDB entry with the
>> locked flag enabled can be added.
>
> FWIW, we may have about a 30% - 70% split between switches that will
> signal ATU violations over a side band interrupt, like mv88e6xxx will,
> and the rest will likely signal such events via the proprietary tag
> format.
I guess that the proprietary tag scheme a scenario where the packet can
be forwarded to the bridge module's ingress queue on the respective
port?
> --
> Florian
On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> >> entry.mac, entry.portvec, spid);
>> >> chip->ports[spid].atu_miss_violation++;
>> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> >> + chip->ports[spid].port,
>> >> + &entry,
>> >> + fid);
>> >
>> > Do we want to suppress the ATU miss violation warnings if we're going to
>> > notify the bridge, or is it better to keep them for some reason?
>> > My logic is that they're part of normal operation, so suppressing makes
>> > sense.
>> >
>>
>> I have been seeing many ATU member violations after the miss violation is
>> handled (using ping), and I think it could be considered to suppress the ATU member
>> violations interrupts by setting the IgnoreWrongData bit for the
>> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>
> So the first packet with a given MAC SA triggers an ATU miss violation
> interrupt.
>
> You program that MAC SA into the ATU with a destination port mask of all
> zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> now generates ATU member violations, because the MAC SA _is_ present in
> the ATU, but not towards the expected port (in fact, towards _no_ port).
>
> Especially if user space decides it doesn't want to authorize this MAC
> SA, it really becomes a problem because this is now a vector for denial
> of service, with every packet triggering an ATU member violation
> interrupt.
>
> So your suggestion is to set the IgnoreWrongData bit on locked ports,
> and this will suppress the actual member violation interrupts for
> traffic coming from these ports.
>
> So if the user decides to unplug a previously authorized printer from
> switch port 1 and move it to port 2, how is this handled? If there isn't
> a mechanism in place to delete the locked FDB entry when the printer
> goes away, then by setting IgnoreWrongData you're effectively also
> suppressing migration notifications.
I don't think such a scenario is so realistic, as changing port is not
just something done casually, besides port 2 then must also be a locked
port to have the same policy.
The other aspect is that the user space daemon that authorizes catches
the fdb add entry events and checks if it is a locked entry. So it will
be up to said daemon to decide the policy, like remove the fdb entry
after a timeout.
>
> Oh, btw, my question was: could you consider suppressing the _prints_ on
> an ATU miss violation on a locked port?
As there will only be such on the first packet, I think it should be
logged and those prints serve that purpose, so I think it is best to
keep the print.
If in the future some tests or other can argue for suppressing the
prints, it is an easy thing to do.
On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
> >> > >> >> entry.mac, entry.portvec, spid);
> >> > >> >> chip->ports[spid].atu_miss_violation++;
> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> >> > >> >> + chip->ports[spid].port,
> >> > >> >> + &entry,
> >> > >> >> + fid);
> >> > >> >
> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
> >> > >> > notify the bridge, or is it better to keep them for some reason?
> >> > >> > My logic is that they're part of normal operation, so suppressing makes
> >> > >> > sense.
> >> > >> >
> >> > >>
> >> > >> I have been seeing many ATU member violations after the miss violation is
> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
> >> > >
> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
> >> > > interrupt.
> >> > >
> >> > > You program that MAC SA into the ATU with a destination port mask of all
> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> >> > > now generates ATU member violations, because the MAC SA _is_ present in
> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
> >> > >
> >> > > Especially if user space decides it doesn't want to authorize this MAC
> >> > > SA, it really becomes a problem because this is now a vector for denial
> >> > > of service, with every packet triggering an ATU member violation
> >> > > interrupt.
> >> > >
> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
> >> > > and this will suppress the actual member violation interrupts for
> >> > > traffic coming from these ports.
> >> > >
> >> > > So if the user decides to unplug a previously authorized printer from
> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
> >> > > a mechanism in place to delete the locked FDB entry when the printer
> >> > > goes away, then by setting IgnoreWrongData you're effectively also
> >> > > suppressing migration notifications.
> >> >
> >> > I don't think such a scenario is so realistic, as changing port is not
> >> > just something done casually, besides port 2 then must also be a locked
> >> > port to have the same policy.
> >>
> >> I think it is very realistic. It is also something which does not work
> >> is going to cause a lot of confusion. People will blame the printer,
> >> when in fact they should be blaming the switch. They will be rebooting
> >> the printer, when in fact, they need to reboot the switch etc.
> >>
> >> I expect there is a way to cleanly support this, you just need to
> >> figure it out.
> >
> > Hans, why must port 2 also be a locked port? The FDB entry with no
> > destinations is present in the ATU, and static, why would just locked
> > ports match it?
> >
> You are right of course, but it was more from a policy standpoint as I
> pointed out. If the FDB entry is removed after some timeout and the
> device in the meantime somehow is on another port that is not locked
> with full access, the device will of course get full access.
> But since it was not given access in the first instance, the policy is
> not consistent.
>
> >> > The other aspect is that the user space daemon that authorizes catches
> >> > the fdb add entry events and checks if it is a locked entry. So it will
> >> > be up to said daemon to decide the policy, like remove the fdb entry
> >> > after a timeout.
> >
> > When you say 'timeout', what is the moment when the timer starts counting?
> > The last reception of the user space daemon of a packet with this MAC SA,
> > or the moment when the FDB entry originally became unlocked?
>
> I think that if the device is not given access, a timer should be
> started at that moment. No further FDB add events with the same MAC
> address will come of course until the FDB entry is removed, which I
> think would be done based on the said timer.
> >
> > I expect that once a device is authorized, and forwarding towards the
> > devices that it wants to talk to is handled in hardware, that the CPU no
> > longer receives packets from this device. In other words, are you saying
> > that you're going to break networking for the printer every 5 minutes,
> > as a keepalive measure?
>
> No, I don't think that would be a good idea, but as we are in userspace,
> that is a policy decision of those creating the daemon. The kernel just
> facilitates, it does not make those decisions as far as I think.
> >
> > I still think there should be a functional fast path for authorized
> > station migrations.
> >
> I am not sure in what way you are suggesting that should be, if the
> kernel should actively do something there? If a station is authorized,
> and somehow is transferred to another port, if that port is not locked it
> will get access, if the port is locked a miss violation will occur etc...
Wait, if the new port is locked and the device was previously
authorized, why will the new port trigger a miss violation? This is the
part I'm not following. The authorization is still present in the form
of an ATU entry on the old locked port, is it not?
> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
> >> > > an ATU miss violation on a locked port?
> >> >
> >> > As there will only be such on the first packet, I think it should be
> >> > logged and those prints serve that purpose, so I think it is best to
> >> > keep the print.
> >> > If in the future some tests or other can argue for suppressing the
> >> > prints, it is an easy thing to do.
> >>
> >> Please use a traffic generator and try to DOS one of your own
> >> switches. Can you?
> >>
> >> Andrew
On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
> On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> >> >> "ATU miss violation for %pM portvec %x spid %d\n",
> >> >> entry.mac, entry.portvec, spid);
> >> >> chip->ports[spid].atu_miss_violation++;
> >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> >> >> + chip->ports[spid].port,
> >> >> + &entry,
> >> >> + fid);
> >> >
> >> > Do we want to suppress the ATU miss violation warnings if we're going to
> >> > notify the bridge, or is it better to keep them for some reason?
> >> > My logic is that they're part of normal operation, so suppressing makes
> >> > sense.
> >> >
> >>
> >> I have been seeing many ATU member violations after the miss violation is
> >> handled (using ping), and I think it could be considered to suppress the ATU member
> >> violations interrupts by setting the IgnoreWrongData bit for the
> >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
> >
> > So the first packet with a given MAC SA triggers an ATU miss violation
> > interrupt.
> >
> > You program that MAC SA into the ATU with a destination port mask of all
> > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> > now generates ATU member violations, because the MAC SA _is_ present in
> > the ATU, but not towards the expected port (in fact, towards _no_ port).
> >
> > Especially if user space decides it doesn't want to authorize this MAC
> > SA, it really becomes a problem because this is now a vector for denial
> > of service, with every packet triggering an ATU member violation
> > interrupt.
> >
> > So your suggestion is to set the IgnoreWrongData bit on locked ports,
> > and this will suppress the actual member violation interrupts for
> > traffic coming from these ports.
> >
> > So if the user decides to unplug a previously authorized printer from
> > switch port 1 and move it to port 2, how is this handled? If there isn't
> > a mechanism in place to delete the locked FDB entry when the printer
> > goes away, then by setting IgnoreWrongData you're effectively also
> > suppressing migration notifications.
>
> I don't think such a scenario is so realistic, as changing port is not
> just something done casually, besides port 2 then must also be a locked
> port to have the same policy.
I think it is very realistic. It is also something which does not work
is going to cause a lot of confusion. People will blame the printer,
when in fact they should be blaming the switch. They will be rebooting
the printer, when in fact, they need to reboot the switch etc.
I expect there is a way to cleanly support this, you just need to
figure it out.
> The other aspect is that the user space daemon that authorizes catches
> the fdb add entry events and checks if it is a locked entry. So it will
> be up to said daemon to decide the policy, like remove the fdb entry
> after a timeout.
>
> >
> > Oh, btw, my question was: could you consider suppressing the _prints_ on
> > an ATU miss violation on a locked port?
>
> As there will only be such on the first packet, I think it should be
> logged and those prints serve that purpose, so I think it is best to
> keep the print.
> If in the future some tests or other can argue for suppressing the
> prints, it is an easy thing to do.
Please use a traffic generator and try to DOS one of your own
switches. Can you?
Andrew
On tor, mar 17, 2022 at 18:18, Vladimir Oltean <[email protected]> wrote:
> On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
>> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
>> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
>> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
>> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> >> > >> >> entry.mac, entry.portvec, spid);
>> >> > >> >> chip->ports[spid].atu_miss_violation++;
>> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> >> > >> >> + chip->ports[spid].port,
>> >> > >> >> + &entry,
>> >> > >> >> + fid);
>> >> > >> >
>> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
>> >> > >> > notify the bridge, or is it better to keep them for some reason?
>> >> > >> > My logic is that they're part of normal operation, so suppressing makes
>> >> > >> > sense.
>> >> > >> >
>> >> > >>
>> >> > >> I have been seeing many ATU member violations after the miss violation is
>> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
>> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
>> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> >> > >
>> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
>> >> > > interrupt.
>> >> > >
>> >> > > You program that MAC SA into the ATU with a destination port mask of all
>> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
>> >> > > now generates ATU member violations, because the MAC SA _is_ present in
>> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
>> >> > >
>> >> > > Especially if user space decides it doesn't want to authorize this MAC
>> >> > > SA, it really becomes a problem because this is now a vector for denial
>> >> > > of service, with every packet triggering an ATU member violation
>> >> > > interrupt.
>> >> > >
>> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
>> >> > > and this will suppress the actual member violation interrupts for
>> >> > > traffic coming from these ports.
>> >> > >
>> >> > > So if the user decides to unplug a previously authorized printer from
>> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
>> >> > > a mechanism in place to delete the locked FDB entry when the printer
>> >> > > goes away, then by setting IgnoreWrongData you're effectively also
>> >> > > suppressing migration notifications.
>> >> >
>> >> > I don't think such a scenario is so realistic, as changing port is not
>> >> > just something done casually, besides port 2 then must also be a locked
>> >> > port to have the same policy.
>> >>
>> >> I think it is very realistic. It is also something which does not work
>> >> is going to cause a lot of confusion. People will blame the printer,
>> >> when in fact they should be blaming the switch. They will be rebooting
>> >> the printer, when in fact, they need to reboot the switch etc.
>> >>
>> >> I expect there is a way to cleanly support this, you just need to
>> >> figure it out.
>> >
>> > Hans, why must port 2 also be a locked port? The FDB entry with no
>> > destinations is present in the ATU, and static, why would just locked
>> > ports match it?
>> >
>> You are right of course, but it was more from a policy standpoint as I
>> pointed out. If the FDB entry is removed after some timeout and the
>> device in the meantime somehow is on another port that is not locked
>> with full access, the device will of course get full access.
>> But since it was not given access in the first instance, the policy is
>> not consistent.
>>
>> >> > The other aspect is that the user space daemon that authorizes catches
>> >> > the fdb add entry events and checks if it is a locked entry. So it will
>> >> > be up to said daemon to decide the policy, like remove the fdb entry
>> >> > after a timeout.
>> >
>> > When you say 'timeout', what is the moment when the timer starts counting?
>> > The last reception of the user space daemon of a packet with this MAC SA,
>> > or the moment when the FDB entry originally became unlocked?
>>
>> I think that if the device is not given access, a timer should be
>> started at that moment. No further FDB add events with the same MAC
>> address will come of course until the FDB entry is removed, which I
>> think would be done based on the said timer.
>> >
>> > I expect that once a device is authorized, and forwarding towards the
>> > devices that it wants to talk to is handled in hardware, that the CPU no
>> > longer receives packets from this device. In other words, are you saying
>> > that you're going to break networking for the printer every 5 minutes,
>> > as a keepalive measure?
>>
>> No, I don't think that would be a good idea, but as we are in userspace,
>> that is a policy decision of those creating the daemon. The kernel just
>> facilitates, it does not make those decisions as far as I think.
>> >
>> > I still think there should be a functional fast path for authorized
>> > station migrations.
>> >
>> I am not sure in what way you are suggesting that should be, if the
>> kernel should actively do something there? If a station is authorized,
>> and somehow is transferred to another port, if that port is not locked it
>> will get access, if the port is locked a miss violation will occur etc...
>
> Wait, if the new port is locked and the device was previously
> authorized, why will the new port trigger a miss violation? This is the
> part I'm not following. The authorization is still present in the form
> of an ATU entry on the old locked port, is it not?
>
I am sure (have not tested) that a miss violation will occur. It might
be a member violation in this instance though.
When thinking of it, afaik there is no way today of having fine control
over the DPV when adding a FDB entry.
If the DPV could be finer controlled the entry could cover several
possible ports and the fast (immediate migration) will be accomplished?
>> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
>> >> > > an ATU miss violation on a locked port?
>> >> >
>> >> > As there will only be such on the first packet, I think it should be
>> >> > logged and those prints serve that purpose, so I think it is best to
>> >> > keep the print.
>> >> > If in the future some tests or other can argue for suppressing the
>> >> > prints, it is an easy thing to do.
>> >>
>> >> Please use a traffic generator and try to DOS one of your own
>> >> switches. Can you?
>> >>
>> >> Andrew
On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
> > >> >> entry.mac, entry.portvec, spid);
> > >> >> chip->ports[spid].atu_miss_violation++;
> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> > >> >> + chip->ports[spid].port,
> > >> >> + &entry,
> > >> >> + fid);
> > >> >
> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
> > >> > notify the bridge, or is it better to keep them for some reason?
> > >> > My logic is that they're part of normal operation, so suppressing makes
> > >> > sense.
> > >> >
> > >>
> > >> I have been seeing many ATU member violations after the miss violation is
> > >> handled (using ping), and I think it could be considered to suppress the ATU member
> > >> violations interrupts by setting the IgnoreWrongData bit for the
> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
> > >
> > > So the first packet with a given MAC SA triggers an ATU miss violation
> > > interrupt.
> > >
> > > You program that MAC SA into the ATU with a destination port mask of all
> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> > > now generates ATU member violations, because the MAC SA _is_ present in
> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
> > >
> > > Especially if user space decides it doesn't want to authorize this MAC
> > > SA, it really becomes a problem because this is now a vector for denial
> > > of service, with every packet triggering an ATU member violation
> > > interrupt.
> > >
> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
> > > and this will suppress the actual member violation interrupts for
> > > traffic coming from these ports.
> > >
> > > So if the user decides to unplug a previously authorized printer from
> > > switch port 1 and move it to port 2, how is this handled? If there isn't
> > > a mechanism in place to delete the locked FDB entry when the printer
> > > goes away, then by setting IgnoreWrongData you're effectively also
> > > suppressing migration notifications.
> >
> > I don't think such a scenario is so realistic, as changing port is not
> > just something done casually, besides port 2 then must also be a locked
> > port to have the same policy.
>
> I think it is very realistic. It is also something which does not work
> is going to cause a lot of confusion. People will blame the printer,
> when in fact they should be blaming the switch. They will be rebooting
> the printer, when in fact, they need to reboot the switch etc.
>
> I expect there is a way to cleanly support this, you just need to
> figure it out.
Hans, why must port 2 also be a locked port? The FDB entry with no
destinations is present in the ATU, and static, why would just locked
ports match it?
> > The other aspect is that the user space daemon that authorizes catches
> > the fdb add entry events and checks if it is a locked entry. So it will
> > be up to said daemon to decide the policy, like remove the fdb entry
> > after a timeout.
When you say 'timeout', what is the moment when the timer starts counting?
The last reception of the user space daemon of a packet with this MAC SA,
or the moment when the FDB entry originally became unlocked?
I expect that once a device is authorized, and forwarding towards the
devices that it wants to talk to is handled in hardware, that the CPU no
longer receives packets from this device. In other words, are you saying
that you're going to break networking for the printer every 5 minutes,
as a keepalive measure?
I still think there should be a functional fast path for authorized
station migrations.
> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
> > > an ATU miss violation on a locked port?
> >
> > As there will only be such on the first packet, I think it should be
> > logged and those prints serve that purpose, so I think it is best to
> > keep the print.
> > If in the future some tests or other can argue for suppressing the
> > prints, it is an easy thing to do.
>
> Please use a traffic generator and try to DOS one of your own
> switches. Can you?
>
> Andrew
On Thu, Mar 17, 2022 at 05:58:26PM +0100, Hans Schultz wrote:
> On tor, mar 17, 2022 at 18:18, Vladimir Oltean <[email protected]> wrote:
> > On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
> >> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
> >> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
> >> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
> >> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> >> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> >> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> >> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
> >> >> > >> >> entry.mac, entry.portvec, spid);
> >> >> > >> >> chip->ports[spid].atu_miss_violation++;
> >> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> >> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> >> >> > >> >> + chip->ports[spid].port,
> >> >> > >> >> + &entry,
> >> >> > >> >> + fid);
> >> >> > >> >
> >> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
> >> >> > >> > notify the bridge, or is it better to keep them for some reason?
> >> >> > >> > My logic is that they're part of normal operation, so suppressing makes
> >> >> > >> > sense.
> >> >> > >> >
> >> >> > >>
> >> >> > >> I have been seeing many ATU member violations after the miss violation is
> >> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
> >> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
> >> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
> >> >> > >
> >> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
> >> >> > > interrupt.
> >> >> > >
> >> >> > > You program that MAC SA into the ATU with a destination port mask of all
> >> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> >> >> > > now generates ATU member violations, because the MAC SA _is_ present in
> >> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
> >> >> > >
> >> >> > > Especially if user space decides it doesn't want to authorize this MAC
> >> >> > > SA, it really becomes a problem because this is now a vector for denial
> >> >> > > of service, with every packet triggering an ATU member violation
> >> >> > > interrupt.
> >> >> > >
> >> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
> >> >> > > and this will suppress the actual member violation interrupts for
> >> >> > > traffic coming from these ports.
> >> >> > >
> >> >> > > So if the user decides to unplug a previously authorized printer from
> >> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
> >> >> > > a mechanism in place to delete the locked FDB entry when the printer
> >> >> > > goes away, then by setting IgnoreWrongData you're effectively also
> >> >> > > suppressing migration notifications.
> >> >> >
> >> >> > I don't think such a scenario is so realistic, as changing port is not
> >> >> > just something done casually, besides port 2 then must also be a locked
> >> >> > port to have the same policy.
> >> >>
> >> >> I think it is very realistic. It is also something which does not work
> >> >> is going to cause a lot of confusion. People will blame the printer,
> >> >> when in fact they should be blaming the switch. They will be rebooting
> >> >> the printer, when in fact, they need to reboot the switch etc.
> >> >>
> >> >> I expect there is a way to cleanly support this, you just need to
> >> >> figure it out.
> >> >
> >> > Hans, why must port 2 also be a locked port? The FDB entry with no
> >> > destinations is present in the ATU, and static, why would just locked
> >> > ports match it?
> >> >
> >> You are right of course, but it was more from a policy standpoint as I
> >> pointed out. If the FDB entry is removed after some timeout and the
> >> device in the meantime somehow is on another port that is not locked
> >> with full access, the device will of course get full access.
> >> But since it was not given access in the first instance, the policy is
> >> not consistent.
> >>
> >> >> > The other aspect is that the user space daemon that authorizes catches
> >> >> > the fdb add entry events and checks if it is a locked entry. So it will
> >> >> > be up to said daemon to decide the policy, like remove the fdb entry
> >> >> > after a timeout.
> >> >
> >> > When you say 'timeout', what is the moment when the timer starts counting?
> >> > The last reception of the user space daemon of a packet with this MAC SA,
> >> > or the moment when the FDB entry originally became unlocked?
> >>
> >> I think that if the device is not given access, a timer should be
> >> started at that moment. No further FDB add events with the same MAC
> >> address will come of course until the FDB entry is removed, which I
> >> think would be done based on the said timer.
> >> >
> >> > I expect that once a device is authorized, and forwarding towards the
> >> > devices that it wants to talk to is handled in hardware, that the CPU no
> >> > longer receives packets from this device. In other words, are you saying
> >> > that you're going to break networking for the printer every 5 minutes,
> >> > as a keepalive measure?
> >>
> >> No, I don't think that would be a good idea, but as we are in userspace,
> >> that is a policy decision of those creating the daemon. The kernel just
> >> facilitates, it does not make those decisions as far as I think.
> >> >
> >> > I still think there should be a functional fast path for authorized
> >> > station migrations.
> >> >
> >> I am not sure in what way you are suggesting that should be, if the
> >> kernel should actively do something there? If a station is authorized,
> >> and somehow is transferred to another port, if that port is not locked it
> >> will get access, if the port is locked a miss violation will occur etc...
> >
> > Wait, if the new port is locked and the device was previously
> > authorized, why will the new port trigger a miss violation? This is the
> > part I'm not following. The authorization is still present in the form
> > of an ATU entry on the old locked port, is it not?
> >
> I am sure (have not tested) that a miss violation will occur. It might
> be a member violation in this instance though.
> When thinking of it, afaik there is no way today of having fine control
> over the DPV when adding a FDB entry.
> If the DPV could be finer controlled the entry could cover several
> possible ports and the fast (immediate migration) will be accomplished?
I'm not sure I understand this, either.
You're saying we should configure the authorizations as de-facto
multicast ATU entries towards all locked ports, so that there wouldn't
be any violation when a station migrates, because the new port is still
in the destination port mask of the ATU entry?
Yes, but... this leaks traffic between ports to a significant degree.
Any packet that targets your printer now targets your colleague's printer too.
I was expecting you'd say that when the cable is unplugged from the
switch, the authorization daemon is notified through rtnetlink of the
link state change, and it flushes the port of addresses it has added
(because the kernel surely does not do this).
This could work to an extent, but it wouldn't handle the case where the
printer isn't connected directly to the 802.1X port, but through
another dumb switch. I don't know enough about 802.1X, but I don't see
why this isn't a valid configuration.
To explain what I'm thinking of. At office, IT gave one Ethernet port to
each desk, but I have multiple devices. I have a PC, a printer, and a
development board, each with a single Ethernet port, so I use a dumb
4-port switch to connect all these devices to the 802.1X port beneath my
desk. I talked to IT, brought my printer to them, they agreed to bypass
802.1X authorization for it based on the MAC address on its label.
I've been working from home for the past few years, but now I need to
return to office. But since years have passed, some colleagues left,
some new colleagues came, and I need to change my desk. The new one
belonged to a co-worker who also had a dumb switch on his desk, so I see
no reason to move mine too. I unplug the printer from my dumb switch,
plug it into the new one, but it doesn't work. What do I do, open a
ticket to IT asking for halp?
To be honest this is purely fictional and I haven't tried it, but it
sounds like I should when I get the chance, to get a better image of how
things are supposed to work.
> >> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
> >> >> > > an ATU miss violation on a locked port?
> >> >> >
> >> >> > As there will only be such on the first packet, I think it should be
> >> >> > logged and those prints serve that purpose, so I think it is best to
> >> >> > keep the print.
> >> >> > If in the future some tests or other can argue for suppressing the
> >> >> > prints, it is an easy thing to do.
> >> >>
> >> >> Please use a traffic generator and try to DOS one of your own
> >> >> switches. Can you?
> >> >>
> >> >> Andrew
On Thu, Mar 17, 2022 at 09:29:10AM +0100, Hans Schultz wrote:
> On ons, mar 16, 2022 at 17:18, Florian Fainelli <[email protected]> wrote:
> > On 3/10/2022 6:23 AM, Hans Schultz wrote:
> >> This patch set extends the locked port feature for devices
> >> that are behind a locked port, but do not have the ability to
> >> authorize themselves as a supplicant using IEEE 802.1X.
> >> Such devices can be printers, meters or anything related to
> >> fixed installations. Instead of 802.1X authorization, devices
> >> can get access based on their MAC addresses being whitelisted.
> >>
> >> For an authorization daemon to detect that a device is trying
> >> to get access through a locked port, the bridge will add the
> >> MAC address of the device to the FDB with a locked flag to it.
> >> Thus the authorization daemon can catch the FDB add event and
> >> check if the MAC address is in the whitelist and if so replace
> >> the FDB entry without the locked flag enabled, and thus open
> >> the port for the device.
> >>
> >> This feature is known as MAC-Auth or MAC Authentication Bypass
> >> (MAB) in Cisco terminology, where the full MAB concept involves
> >> additional Cisco infrastructure for authorization. There is no
> >> real authentication process, as the MAC address of the device
> >> is the only input the authorization daemon, in the general
> >> case, has to base the decision if to unlock the port or not.
> >>
> >> With this patch set, an implementation of the offloaded case is
> >> supplied for the mv88e6xxx driver. When a packet ingresses on
> >> a locked port, an ATU miss violation event will occur. When
> >> handling such ATU miss violation interrupts, the MAC address of
> >> the device is added to the FDB with a zero destination port
> >> vector (DPV) and the MAC address is communicated through the
> >> switchdev layer to the bridge, so that a FDB entry with the
> >> locked flag enabled can be added.
> >
> > FWIW, we may have about a 30% - 70% split between switches that will
> > signal ATU violations over a side band interrupt, like mv88e6xxx will,
> > and the rest will likely signal such events via the proprietary tag
> > format.
>
> I guess that the proprietary tag scheme a scenario where the packet can
> be forwarded to the bridge module's ingress queue on the respective
> port?
I'm not sure what you mean by forwarding to the bridge module's ingress
queue. I expect that both cases of drivers to interact with the bridge
in the exact same way, expect one of them calls call_switchdev_notifiers()
from an interrupt context, and the other from NET_RX softirq context,
from the tagging protocol driver (ok, maybe not directly, it depends
upon whether we need rtnl_lock which sleeps, things like that).
I might be just projecting based on what I know, but the way I interpret
what Florian has said is by thinking of "learn frames" as described here:
https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/#24734685
The advantage of signaling ATU misses or membership violations via learn
frames is that you have a much wider toolbox of mitigations for denial
of service. Instead of one ATU interrupt per packet, you have NAPI on
the DSA master, interrupt coalescing, policers on the DSA master, rate
limiting for learn frames in the switch...
On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
> On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
>> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
>> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
>> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> > >> >> entry.mac, entry.portvec, spid);
>> > >> >> chip->ports[spid].atu_miss_violation++;
>> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> > >> >> + chip->ports[spid].port,
>> > >> >> + &entry,
>> > >> >> + fid);
>> > >> >
>> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
>> > >> > notify the bridge, or is it better to keep them for some reason?
>> > >> > My logic is that they're part of normal operation, so suppressing makes
>> > >> > sense.
>> > >> >
>> > >>
>> > >> I have been seeing many ATU member violations after the miss violation is
>> > >> handled (using ping), and I think it could be considered to suppress the ATU member
>> > >> violations interrupts by setting the IgnoreWrongData bit for the
>> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> > >
>> > > So the first packet with a given MAC SA triggers an ATU miss violation
>> > > interrupt.
>> > >
>> > > You program that MAC SA into the ATU with a destination port mask of all
>> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
>> > > now generates ATU member violations, because the MAC SA _is_ present in
>> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
>> > >
>> > > Especially if user space decides it doesn't want to authorize this MAC
>> > > SA, it really becomes a problem because this is now a vector for denial
>> > > of service, with every packet triggering an ATU member violation
>> > > interrupt.
>> > >
>> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
>> > > and this will suppress the actual member violation interrupts for
>> > > traffic coming from these ports.
>> > >
>> > > So if the user decides to unplug a previously authorized printer from
>> > > switch port 1 and move it to port 2, how is this handled? If there isn't
>> > > a mechanism in place to delete the locked FDB entry when the printer
>> > > goes away, then by setting IgnoreWrongData you're effectively also
>> > > suppressing migration notifications.
>> >
>> > I don't think such a scenario is so realistic, as changing port is not
>> > just something done casually, besides port 2 then must also be a locked
>> > port to have the same policy.
>>
>> I think it is very realistic. It is also something which does not work
>> is going to cause a lot of confusion. People will blame the printer,
>> when in fact they should be blaming the switch. They will be rebooting
>> the printer, when in fact, they need to reboot the switch etc.
>>
>> I expect there is a way to cleanly support this, you just need to
>> figure it out.
>
> Hans, why must port 2 also be a locked port? The FDB entry with no
> destinations is present in the ATU, and static, why would just locked
> ports match it?
>
You are right of course, but it was more from a policy standpoint as I
pointed out. If the FDB entry is removed after some timeout and the
device in the meantime somehow is on another port that is not locked
with full access, the device will of course get full access.
But since it was not given access in the first instance, the policy is
not consistent.
>> > The other aspect is that the user space daemon that authorizes catches
>> > the fdb add entry events and checks if it is a locked entry. So it will
>> > be up to said daemon to decide the policy, like remove the fdb entry
>> > after a timeout.
>
> When you say 'timeout', what is the moment when the timer starts counting?
> The last reception of the user space daemon of a packet with this MAC SA,
> or the moment when the FDB entry originally became unlocked?
I think that if the device is not given access, a timer should be
started at that moment. No further FDB add events with the same MAC
address will come of course until the FDB entry is removed, which I
think would be done based on the said timer.
>
> I expect that once a device is authorized, and forwarding towards the
> devices that it wants to talk to is handled in hardware, that the CPU no
> longer receives packets from this device. In other words, are you saying
> that you're going to break networking for the printer every 5 minutes,
> as a keepalive measure?
No, I don't think that would be a good idea, but as we are in userspace,
that is a policy decision of those creating the daemon. The kernel just
facilitates, it does not make those decisions as far as I think.
>
> I still think there should be a functional fast path for authorized
> station migrations.
>
I am not sure in what way you are suggesting that should be, if the
kernel should actively do something there? If a station is authorized,
and somehow is transferred to another port, if that port is not locked it
will get access, if the port is locked a miss violation will occur etc...
>> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
>> > > an ATU miss violation on a locked port?
>> >
>> > As there will only be such on the first packet, I think it should be
>> > logged and those prints serve that purpose, so I think it is best to
>> > keep the print.
>> > If in the future some tests or other can argue for suppressing the
>> > prints, it is an easy thing to do.
>>
>> Please use a traffic generator and try to DOS one of your own
>> switches. Can you?
>>
>> Andrew
On tor, mar 17, 2022 at 19:20, Vladimir Oltean <[email protected]> wrote:
> On Thu, Mar 17, 2022 at 05:58:26PM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 18:18, Vladimir Oltean <[email protected]> wrote:
>> > On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
>> >> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
>> >> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
>> >> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
>> >> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
>> >> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> >> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> >> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> >> >> > >> >> entry.mac, entry.portvec, spid);
>> >> >> > >> >> chip->ports[spid].atu_miss_violation++;
>> >> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> >> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> >> >> > >> >> + chip->ports[spid].port,
>> >> >> > >> >> + &entry,
>> >> >> > >> >> + fid);
>> >> >> > >> >
>> >> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
>> >> >> > >> > notify the bridge, or is it better to keep them for some reason?
>> >> >> > >> > My logic is that they're part of normal operation, so suppressing makes
>> >> >> > >> > sense.
>> >> >> > >> >
>> >> >> > >>
>> >> >> > >> I have been seeing many ATU member violations after the miss violation is
>> >> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
>> >> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
>> >> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> >> >> > >
>> >> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
>> >> >> > > interrupt.
>> >> >> > >
>> >> >> > > You program that MAC SA into the ATU with a destination port mask of all
>> >> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
>> >> >> > > now generates ATU member violations, because the MAC SA _is_ present in
>> >> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
>> >> >> > >
>> >> >> > > Especially if user space decides it doesn't want to authorize this MAC
>> >> >> > > SA, it really becomes a problem because this is now a vector for denial
>> >> >> > > of service, with every packet triggering an ATU member violation
>> >> >> > > interrupt.
>> >> >> > >
>> >> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
>> >> >> > > and this will suppress the actual member violation interrupts for
>> >> >> > > traffic coming from these ports.
>> >> >> > >
>> >> >> > > So if the user decides to unplug a previously authorized printer from
>> >> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
>> >> >> > > a mechanism in place to delete the locked FDB entry when the printer
>> >> >> > > goes away, then by setting IgnoreWrongData you're effectively also
>> >> >> > > suppressing migration notifications.
>> >> >> >
>> >> >> > I don't think such a scenario is so realistic, as changing port is not
>> >> >> > just something done casually, besides port 2 then must also be a locked
>> >> >> > port to have the same policy.
>> >> >>
>> >> >> I think it is very realistic. It is also something which does not work
>> >> >> is going to cause a lot of confusion. People will blame the printer,
>> >> >> when in fact they should be blaming the switch. They will be rebooting
>> >> >> the printer, when in fact, they need to reboot the switch etc.
>> >> >>
>> >> >> I expect there is a way to cleanly support this, you just need to
>> >> >> figure it out.
>> >> >
>> >> > Hans, why must port 2 also be a locked port? The FDB entry with no
>> >> > destinations is present in the ATU, and static, why would just locked
>> >> > ports match it?
>> >> >
>> >> You are right of course, but it was more from a policy standpoint as I
>> >> pointed out. If the FDB entry is removed after some timeout and the
>> >> device in the meantime somehow is on another port that is not locked
>> >> with full access, the device will of course get full access.
>> >> But since it was not given access in the first instance, the policy is
>> >> not consistent.
>> >>
>> >> >> > The other aspect is that the user space daemon that authorizes catches
>> >> >> > the fdb add entry events and checks if it is a locked entry. So it will
>> >> >> > be up to said daemon to decide the policy, like remove the fdb entry
>> >> >> > after a timeout.
>> >> >
>> >> > When you say 'timeout', what is the moment when the timer starts counting?
>> >> > The last reception of the user space daemon of a packet with this MAC SA,
>> >> > or the moment when the FDB entry originally became unlocked?
>> >>
>> >> I think that if the device is not given access, a timer should be
>> >> started at that moment. No further FDB add events with the same MAC
>> >> address will come of course until the FDB entry is removed, which I
>> >> think would be done based on the said timer.
>> >> >
>> >> > I expect that once a device is authorized, and forwarding towards the
>> >> > devices that it wants to talk to is handled in hardware, that the CPU no
>> >> > longer receives packets from this device. In other words, are you saying
>> >> > that you're going to break networking for the printer every 5 minutes,
>> >> > as a keepalive measure?
>> >>
>> >> No, I don't think that would be a good idea, but as we are in userspace,
>> >> that is a policy decision of those creating the daemon. The kernel just
>> >> facilitates, it does not make those decisions as far as I think.
>> >> >
>> >> > I still think there should be a functional fast path for authorized
>> >> > station migrations.
>> >> >
>> >> I am not sure in what way you are suggesting that should be, if the
>> >> kernel should actively do something there? If a station is authorized,
>> >> and somehow is transferred to another port, if that port is not locked it
>> >> will get access, if the port is locked a miss violation will occur etc...
>> >
>> > Wait, if the new port is locked and the device was previously
>> > authorized, why will the new port trigger a miss violation? This is the
>> > part I'm not following. The authorization is still present in the form
>> > of an ATU entry on the old locked port, is it not?
>> >
>> I am sure (have not tested) that a miss violation will occur. It might
>> be a member violation in this instance though.
>> When thinking of it, afaik there is no way today of having fine control
>> over the DPV when adding a FDB entry.
>> If the DPV could be finer controlled the entry could cover several
>> possible ports and the fast (immediate migration) will be accomplished?
>
> I'm not sure I understand this, either.
>
> You're saying we should configure the authorizations as de-facto
> multicast ATU entries towards all locked ports, so that there wouldn't
> be any violation when a station migrates, because the new port is still
> in the destination port mask of the ATU entry?
>
> Yes, but... this leaks traffic between ports to a significant degree.
> Any packet that targets your printer now targets your colleague's printer too.
>
> I was expecting you'd say that when the cable is unplugged from the
> switch, the authorization daemon is notified through rtnetlink of the
> link state change, and it flushes the port of addresses it has added
> (because the kernel surely does not do this).
>
So, my HW tests show that when the link is removed, the FDB entries
related to the port are flushed automatically.
> This could work to an extent, but it wouldn't handle the case where the
> printer isn't connected directly to the 802.1X port, but through
> another dumb switch. I don't know enough about 802.1X, but I don't see
> why this isn't a valid configuration.
>
ATM, the dynamic flag (bridge fdb add MAC dev DEV master dynamic)
doesn't create an ageing FDB entry in the offloaded case. Maybe if that
was solved, it would be a good enough solution, as for a noisy device,
it would lose some packets every 5 minutes, which higher layers should
be able to handle?
> To explain what I'm thinking of. At office, IT gave one Ethernet port to
> each desk, but I have multiple devices. I have a PC, a printer, and a
> development board, each with a single Ethernet port, so I use a dumb
> 4-port switch to connect all these devices to the 802.1X port beneath my
> desk. I talked to IT, brought my printer to them, they agreed to bypass
> 802.1X authorization for it based on the MAC address on its label.
>
> I've been working from home for the past few years, but now I need to
> return to office. But since years have passed, some colleagues left,
> some new colleagues came, and I need to change my desk. The new one
> belonged to a co-worker who also had a dumb switch on his desk, so I see
> no reason to move mine too. I unplug the printer from my dumb switch,
> plug it into the new one, but it doesn't work. What do I do, open a
> ticket to IT asking for halp?
>
> To be honest this is purely fictional and I haven't tried it, but it
> sounds like I should when I get the chance, to get a better image of how
> things are supposed to work.
>
>> >> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
>> >> >> > > an ATU miss violation on a locked port?
>> >> >> >
>> >> >> > As there will only be such on the first packet, I think it should be
>> >> >> > logged and those prints serve that purpose, so I think it is best to
>> >> >> > keep the print.
>> >> >> > If in the future some tests or other can argue for suppressing the
>> >> >> > prints, it is an easy thing to do.
>> >> >>
>> >> >> Please use a traffic generator and try to DOS one of your own
>> >> >> switches. Can you?
>> >> >>
>> >> >> Andrew
On Fri, Mar 18, 2022 at 11:04:36AM +0100, Hans Schultz wrote:
> On tor, mar 17, 2022 at 19:20, Vladimir Oltean <[email protected]> wrote:
> > On Thu, Mar 17, 2022 at 05:58:26PM +0100, Hans Schultz wrote:
> >> On tor, mar 17, 2022 at 18:18, Vladimir Oltean <[email protected]> wrote:
> >> > On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
> >> >> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
> >> >> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
> >> >> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
> >> >> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
> >> >> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
> >> >> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
> >> >> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
> >> >> >> > >> >> entry.mac, entry.portvec, spid);
> >> >> >> > >> >> chip->ports[spid].atu_miss_violation++;
> >> >> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
> >> >> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
> >> >> >> > >> >> + chip->ports[spid].port,
> >> >> >> > >> >> + &entry,
> >> >> >> > >> >> + fid);
> >> >> >> > >> >
> >> >> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
> >> >> >> > >> > notify the bridge, or is it better to keep them for some reason?
> >> >> >> > >> > My logic is that they're part of normal operation, so suppressing makes
> >> >> >> > >> > sense.
> >> >> >> > >> >
> >> >> >> > >>
> >> >> >> > >> I have been seeing many ATU member violations after the miss violation is
> >> >> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
> >> >> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
> >> >> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
> >> >> >> > >
> >> >> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
> >> >> >> > > interrupt.
> >> >> >> > >
> >> >> >> > > You program that MAC SA into the ATU with a destination port mask of all
> >> >> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
> >> >> >> > > now generates ATU member violations, because the MAC SA _is_ present in
> >> >> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
> >> >> >> > >
> >> >> >> > > Especially if user space decides it doesn't want to authorize this MAC
> >> >> >> > > SA, it really becomes a problem because this is now a vector for denial
> >> >> >> > > of service, with every packet triggering an ATU member violation
> >> >> >> > > interrupt.
> >> >> >> > >
> >> >> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
> >> >> >> > > and this will suppress the actual member violation interrupts for
> >> >> >> > > traffic coming from these ports.
> >> >> >> > >
> >> >> >> > > So if the user decides to unplug a previously authorized printer from
> >> >> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
> >> >> >> > > a mechanism in place to delete the locked FDB entry when the printer
> >> >> >> > > goes away, then by setting IgnoreWrongData you're effectively also
> >> >> >> > > suppressing migration notifications.
> >> >> >> >
> >> >> >> > I don't think such a scenario is so realistic, as changing port is not
> >> >> >> > just something done casually, besides port 2 then must also be a locked
> >> >> >> > port to have the same policy.
> >> >> >>
> >> >> >> I think it is very realistic. It is also something which does not work
> >> >> >> is going to cause a lot of confusion. People will blame the printer,
> >> >> >> when in fact they should be blaming the switch. They will be rebooting
> >> >> >> the printer, when in fact, they need to reboot the switch etc.
> >> >> >>
> >> >> >> I expect there is a way to cleanly support this, you just need to
> >> >> >> figure it out.
> >> >> >
> >> >> > Hans, why must port 2 also be a locked port? The FDB entry with no
> >> >> > destinations is present in the ATU, and static, why would just locked
> >> >> > ports match it?
> >> >> >
> >> >> You are right of course, but it was more from a policy standpoint as I
> >> >> pointed out. If the FDB entry is removed after some timeout and the
> >> >> device in the meantime somehow is on another port that is not locked
> >> >> with full access, the device will of course get full access.
> >> >> But since it was not given access in the first instance, the policy is
> >> >> not consistent.
> >> >>
> >> >> >> > The other aspect is that the user space daemon that authorizes catches
> >> >> >> > the fdb add entry events and checks if it is a locked entry. So it will
> >> >> >> > be up to said daemon to decide the policy, like remove the fdb entry
> >> >> >> > after a timeout.
> >> >> >
> >> >> > When you say 'timeout', what is the moment when the timer starts counting?
> >> >> > The last reception of the user space daemon of a packet with this MAC SA,
> >> >> > or the moment when the FDB entry originally became unlocked?
> >> >>
> >> >> I think that if the device is not given access, a timer should be
> >> >> started at that moment. No further FDB add events with the same MAC
> >> >> address will come of course until the FDB entry is removed, which I
> >> >> think would be done based on the said timer.
> >> >> >
> >> >> > I expect that once a device is authorized, and forwarding towards the
> >> >> > devices that it wants to talk to is handled in hardware, that the CPU no
> >> >> > longer receives packets from this device. In other words, are you saying
> >> >> > that you're going to break networking for the printer every 5 minutes,
> >> >> > as a keepalive measure?
> >> >>
> >> >> No, I don't think that would be a good idea, but as we are in userspace,
> >> >> that is a policy decision of those creating the daemon. The kernel just
> >> >> facilitates, it does not make those decisions as far as I think.
> >> >> >
> >> >> > I still think there should be a functional fast path for authorized
> >> >> > station migrations.
> >> >> >
> >> >> I am not sure in what way you are suggesting that should be, if the
> >> >> kernel should actively do something there? If a station is authorized,
> >> >> and somehow is transferred to another port, if that port is not locked it
> >> >> will get access, if the port is locked a miss violation will occur etc...
> >> >
> >> > Wait, if the new port is locked and the device was previously
> >> > authorized, why will the new port trigger a miss violation? This is the
> >> > part I'm not following. The authorization is still present in the form
> >> > of an ATU entry on the old locked port, is it not?
> >> >
> >> I am sure (have not tested) that a miss violation will occur. It might
> >> be a member violation in this instance though.
> >> When thinking of it, afaik there is no way today of having fine control
> >> over the DPV when adding a FDB entry.
> >> If the DPV could be finer controlled the entry could cover several
> >> possible ports and the fast (immediate migration) will be accomplished?
> >
> > I'm not sure I understand this, either.
> >
> > You're saying we should configure the authorizations as de-facto
> > multicast ATU entries towards all locked ports, so that there wouldn't
> > be any violation when a station migrates, because the new port is still
> > in the destination port mask of the ATU entry?
> >
> > Yes, but... this leaks traffic between ports to a significant degree.
> > Any packet that targets your printer now targets your colleague's printer too.
> >
> > I was expecting you'd say that when the cable is unplugged from the
> > switch, the authorization daemon is notified through rtnetlink of the
> > link state change, and it flushes the port of addresses it has added
> > (because the kernel surely does not do this).
> >
> So, my HW tests show that when the link is removed, the FDB entries
> related to the port are flushed automatically.
Don't get me wrong, some addresses are flushed, this is handled by
dsa_port_fast_age() as a result of the fact that the bridge port
transitions to the DISABLED state when the link is lost. But those
should only be the dynamically learned addresses. I'm not sure what
flags your user space daemon uses to program ATU entries to hardware,
but if those entries are anything other than dynamic, I don't think that
dsa_port_fast_age() should flush them.
root@debian:~# ip link add br0 type bridge
root@debian:~# ip link set swp3 master br0
[46343.691692] br0: port 1(swp3) entered blocking state
[46343.696791] br0: port 1(swp3) entered disabled state
[46343.703962] device swp3 entered promiscuous mode
root@debian:~# bridge fdb add dev swp3 00:01:02:03:04:05 master static
root@debian:~# bridge fdb show dev swp3
00:01:02:03:04:05 vlan 1 offload master br0 static
00:01:02:03:04:05 offload master br0 static
00:04:9f:05:f6:28 vlan 1 master br0 permanent
00:04:9f:05:f6:28 master br0 permanent
00:01:02:03:04:05 vlan 1 self
00:01:02:03:04:05 self
root@debian:~# ip link set swp3 up
[46391.626362] fsl_enetc 0000:00:00.2 eno2: configuring for fixed/internal link mode
[46391.634110] fsl_enetc 0000:00:00.2 eno2: Link is Up - 2.5Gbps/Full - flow control rx/tx
[46391.635256] mscc_felix 0000:00:00.5 swp3: configuring for inband/qsgmii link mode
root@debian:~# [46395.751268] mscc_felix 0000:00:00.5 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
[46395.759613] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
root@debian:~# bridge fdb show dev swp3
00:01:02:03:04:05 vlan 1 offload master br0 static
00:01:02:03:04:05 offload master br0 static
00:04:9f:05:f6:28 vlan 1 master br0 permanent
00:04:9f:05:f6:28 master br0 permanent
00:01:02:03:04:05 vlan 1 self
00:01:02:03:04:05 self
root@debian:~# ip link set swp3 down
[46403.978832] mscc_felix 0000:00:00.5 swp3: Link is Down
root@debian:~# bridge fdb show dev swp3
00:01:02:03:04:05 vlan 1 offload master br0 static
00:01:02:03:04:05 offload master br0 static
00:04:9f:05:f6:28 vlan 1 master br0 permanent
00:04:9f:05:f6:28 master br0 permanent
00:01:02:03:04:05 vlan 1 self
00:01:02:03:04:05 self
root@debian:~# ip link set swp3 up
[46410.499445] mscc_felix 0000:00:00.5 swp3: configuring for inband/qsgmii link mode
root@debian:~# [46414.597381] mscc_felix 0000:00:00.5 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
[46414.605775] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
root@debian:~# bridge fdb show dev swp3
00:01:02:03:04:05 vlan 1 offload master br0 static
00:01:02:03:04:05 offload master br0 static
00:04:9f:05:f6:28 vlan 1 master br0 permanent
00:04:9f:05:f6:28 master br0 permanent
00:01:02:03:04:05 vlan 1 self
00:01:02:03:04:05 self
I've searched for the call paths of br_fdb_delete_by_port().
One caller is br_stp_disable_port(), but this passes 0 to "do_all", so
this can't be it. Also, both IFLA_BRPORT_FLUSH and the "flush" sysfs
pass 0 to "do_all".
If it's not the user space daemon who is deleting these entries, this is strange.
More details on my suspicion below.
> > This could work to an extent, but it wouldn't handle the case where the
> > printer isn't connected directly to the 802.1X port, but through
> > another dumb switch. I don't know enough about 802.1X, but I don't see
> > why this isn't a valid configuration.
> >
> ATM, the dynamic flag (bridge fdb add MAC dev DEV master dynamic)
> doesn't create an ageing FDB entry in the offloaded case. Maybe if that
> was solved, it would be a good enough solution, as for a noisy device,
> it would lose some packets every 5 minutes, which higher layers should
> be able to handle?
Before we explore this too deeply, can you first double-check whether
station migrations will trigger a miss or a member violation?
When I look at the documentation, I see that the switch triggers a member
violation, unless the ATU age interrupt is enabled and the entry's state
is less than 4 (which it should be if RefreshLocked is false). In this
latter case, the documentation says that a miss violation will be
triggered, not a membership one. So if you satisfy these requirements,
you should be able to safely set IgnoreWrongData, and not miss out on
migrations.
My suspicion is that, since you add ATU authorizations on locked ports
with RefreshLocked as false, they will eventually age out. But the
driver doesn't enable the ATU age interrupt through Global 2 register 5,
so software never finds out when these ATU entries expire. And the idea
of triggering an ATU age interrupt when the entry state becomes less
than 4 (i.e. they are aged half-way) is precisely done such that you
don't break networking for the printer every 5 minutes. The switch
notifies you in advance of the complete entry expiration. I think you
should service this interrupt in the driver. Note that my belief is that
such ATU entries on locked ports aren't truly 'dynamic' as far as user
space is concerned. Sure, they age, but this is just a way for software
to keep them in check. But user space doesn't have to know this. To the
application, the entry is static, and the driver just refreshes the
entry unless told by the application to delete it.
Basically what I'm saying is that more testing needs to be done to make
sure that the hardware is configured to do something reasonable in all
cases, and doesn't just work by coincidence.
Makes sense?
> > To explain what I'm thinking of. At office, IT gave one Ethernet port to
> > each desk, but I have multiple devices. I have a PC, a printer, and a
> > development board, each with a single Ethernet port, so I use a dumb
> > 4-port switch to connect all these devices to the 802.1X port beneath my
> > desk. I talked to IT, brought my printer to them, they agreed to bypass
> > 802.1X authorization for it based on the MAC address on its label.
> >
> > I've been working from home for the past few years, but now I need to
> > return to office. But since years have passed, some colleagues left,
> > some new colleagues came, and I need to change my desk. The new one
> > belonged to a co-worker who also had a dumb switch on his desk, so I see
> > no reason to move mine too. I unplug the printer from my dumb switch,
> > plug it into the new one, but it doesn't work. What do I do, open a
> > ticket to IT asking for halp?
> >
> > To be honest this is purely fictional and I haven't tried it, but it
> > sounds like I should when I get the chance, to get a better image of how
> > things are supposed to work.
> >
> >> >> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
> >> >> >> > > an ATU miss violation on a locked port?
> >> >> >> >
> >> >> >> > As there will only be such on the first packet, I think it should be
> >> >> >> > logged and those prints serve that purpose, so I think it is best to
> >> >> >> > keep the print.
> >> >> >> > If in the future some tests or other can argue for suppressing the
> >> >> >> > prints, it is an easy thing to do.
> >> >> >>
> >> >> >> Please use a traffic generator and try to DOS one of your own
> >> >> >> switches. Can you?
> >> >> >>
> >> >> >> Andrew
On fre, mar 18, 2022 at 14:14, Vladimir Oltean <[email protected]> wrote:
> On Fri, Mar 18, 2022 at 11:04:36AM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 19:20, Vladimir Oltean <[email protected]> wrote:
>> > On Thu, Mar 17, 2022 at 05:58:26PM +0100, Hans Schultz wrote:
>> >> On tor, mar 17, 2022 at 18:18, Vladimir Oltean <[email protected]> wrote:
>> >> > On Thu, Mar 17, 2022 at 05:07:15PM +0100, Hans Schultz wrote:
>> >> >> On tor, mar 17, 2022 at 17:36, Vladimir Oltean <[email protected]> wrote:
>> >> >> > On Thu, Mar 17, 2022 at 03:19:46PM +0100, Andrew Lunn wrote:
>> >> >> >> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
>> >> >> >> > On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
>> >> >> >> > > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> >> >> >> > >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> >> >> >> > >> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> >> >> >> > >> >> entry.mac, entry.portvec, spid);
>> >> >> >> > >> >> chip->ports[spid].atu_miss_violation++;
>> >> >> >> > >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> >> >> >> > >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> >> >> >> > >> >> + chip->ports[spid].port,
>> >> >> >> > >> >> + &entry,
>> >> >> >> > >> >> + fid);
>> >> >> >> > >> >
>> >> >> >> > >> > Do we want to suppress the ATU miss violation warnings if we're going to
>> >> >> >> > >> > notify the bridge, or is it better to keep them for some reason?
>> >> >> >> > >> > My logic is that they're part of normal operation, so suppressing makes
>> >> >> >> > >> > sense.
>> >> >> >> > >> >
>> >> >> >> > >>
>> >> >> >> > >> I have been seeing many ATU member violations after the miss violation is
>> >> >> >> > >> handled (using ping), and I think it could be considered to suppress the ATU member
>> >> >> >> > >> violations interrupts by setting the IgnoreWrongData bit for the
>> >> >> >> > >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> >> >> >> > >
>> >> >> >> > > So the first packet with a given MAC SA triggers an ATU miss violation
>> >> >> >> > > interrupt.
>> >> >> >> > >
>> >> >> >> > > You program that MAC SA into the ATU with a destination port mask of all
>> >> >> >> > > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
>> >> >> >> > > now generates ATU member violations, because the MAC SA _is_ present in
>> >> >> >> > > the ATU, but not towards the expected port (in fact, towards _no_ port).
>> >> >> >> > >
>> >> >> >> > > Especially if user space decides it doesn't want to authorize this MAC
>> >> >> >> > > SA, it really becomes a problem because this is now a vector for denial
>> >> >> >> > > of service, with every packet triggering an ATU member violation
>> >> >> >> > > interrupt.
>> >> >> >> > >
>> >> >> >> > > So your suggestion is to set the IgnoreWrongData bit on locked ports,
>> >> >> >> > > and this will suppress the actual member violation interrupts for
>> >> >> >> > > traffic coming from these ports.
>> >> >> >> > >
>> >> >> >> > > So if the user decides to unplug a previously authorized printer from
>> >> >> >> > > switch port 1 and move it to port 2, how is this handled? If there isn't
>> >> >> >> > > a mechanism in place to delete the locked FDB entry when the printer
>> >> >> >> > > goes away, then by setting IgnoreWrongData you're effectively also
>> >> >> >> > > suppressing migration notifications.
>> >> >> >> >
>> >> >> >> > I don't think such a scenario is so realistic, as changing port is not
>> >> >> >> > just something done casually, besides port 2 then must also be a locked
>> >> >> >> > port to have the same policy.
>> >> >> >>
>> >> >> >> I think it is very realistic. It is also something which does not work
>> >> >> >> is going to cause a lot of confusion. People will blame the printer,
>> >> >> >> when in fact they should be blaming the switch. They will be rebooting
>> >> >> >> the printer, when in fact, they need to reboot the switch etc.
>> >> >> >>
>> >> >> >> I expect there is a way to cleanly support this, you just need to
>> >> >> >> figure it out.
>> >> >> >
>> >> >> > Hans, why must port 2 also be a locked port? The FDB entry with no
>> >> >> > destinations is present in the ATU, and static, why would just locked
>> >> >> > ports match it?
>> >> >> >
>> >> >> You are right of course, but it was more from a policy standpoint as I
>> >> >> pointed out. If the FDB entry is removed after some timeout and the
>> >> >> device in the meantime somehow is on another port that is not locked
>> >> >> with full access, the device will of course get full access.
>> >> >> But since it was not given access in the first instance, the policy is
>> >> >> not consistent.
>> >> >>
>> >> >> >> > The other aspect is that the user space daemon that authorizes catches
>> >> >> >> > the fdb add entry events and checks if it is a locked entry. So it will
>> >> >> >> > be up to said daemon to decide the policy, like remove the fdb entry
>> >> >> >> > after a timeout.
>> >> >> >
>> >> >> > When you say 'timeout', what is the moment when the timer starts counting?
>> >> >> > The last reception of the user space daemon of a packet with this MAC SA,
>> >> >> > or the moment when the FDB entry originally became unlocked?
>> >> >>
>> >> >> I think that if the device is not given access, a timer should be
>> >> >> started at that moment. No further FDB add events with the same MAC
>> >> >> address will come of course until the FDB entry is removed, which I
>> >> >> think would be done based on the said timer.
>> >> >> >
>> >> >> > I expect that once a device is authorized, and forwarding towards the
>> >> >> > devices that it wants to talk to is handled in hardware, that the CPU no
>> >> >> > longer receives packets from this device. In other words, are you saying
>> >> >> > that you're going to break networking for the printer every 5 minutes,
>> >> >> > as a keepalive measure?
>> >> >>
>> >> >> No, I don't think that would be a good idea, but as we are in userspace,
>> >> >> that is a policy decision of those creating the daemon. The kernel just
>> >> >> facilitates, it does not make those decisions as far as I think.
>> >> >> >
>> >> >> > I still think there should be a functional fast path for authorized
>> >> >> > station migrations.
>> >> >> >
>> >> >> I am not sure in what way you are suggesting that should be, if the
>> >> >> kernel should actively do something there? If a station is authorized,
>> >> >> and somehow is transferred to another port, if that port is not locked it
>> >> >> will get access, if the port is locked a miss violation will occur etc...
>> >> >
>> >> > Wait, if the new port is locked and the device was previously
>> >> > authorized, why will the new port trigger a miss violation? This is the
>> >> > part I'm not following. The authorization is still present in the form
>> >> > of an ATU entry on the old locked port, is it not?
>> >> >
>> >> I am sure (have not tested) that a miss violation will occur. It might
>> >> be a member violation in this instance though.
>> >> When thinking of it, afaik there is no way today of having fine control
>> >> over the DPV when adding a FDB entry.
>> >> If the DPV could be finer controlled the entry could cover several
>> >> possible ports and the fast (immediate migration) will be accomplished?
>> >
>> > I'm not sure I understand this, either.
>> >
>> > You're saying we should configure the authorizations as de-facto
>> > multicast ATU entries towards all locked ports, so that there wouldn't
>> > be any violation when a station migrates, because the new port is still
>> > in the destination port mask of the ATU entry?
>> >
>> > Yes, but... this leaks traffic between ports to a significant degree.
>> > Any packet that targets your printer now targets your colleague's printer too.
>> >
>> > I was expecting you'd say that when the cable is unplugged from the
>> > switch, the authorization daemon is notified through rtnetlink of the
>> > link state change, and it flushes the port of addresses it has added
>> > (because the kernel surely does not do this).
>> >
>> So, my HW tests show that when the link is removed, the FDB entries
>> related to the port are flushed automatically.
>
> Don't get me wrong, some addresses are flushed, this is handled by
> dsa_port_fast_age() as a result of the fact that the bridge port
> transitions to the DISABLED state when the link is lost. But those
> should only be the dynamically learned addresses. I'm not sure what
> flags your user space daemon uses to program ATU entries to hardware,
> but if those entries are anything other than dynamic, I don't think that
> dsa_port_fast_age() should flush them.
In the offloaded case there is no difference between static and dynamic
flags, which I see as a general issue. (The resulting ATU entry is static
in either case.)
These FDB entries are removed when link goes down (soft or hard). The
zero DPV entries that the new code introduces age out after 5 minutes,
while the locked flagged FDB entries are removed by link down (thus the
FDB and the ATU are not in sync in this case).
I need to get hold of a couple hubs to test the migration issue when no
link goes down, which I can do in the weekend, and test on Monday. :-)
>
> root@debian:~# ip link add br0 type bridge
> root@debian:~# ip link set swp3 master br0
> [46343.691692] br0: port 1(swp3) entered blocking state
> [46343.696791] br0: port 1(swp3) entered disabled state
> [46343.703962] device swp3 entered promiscuous mode
> root@debian:~# bridge fdb add dev swp3 00:01:02:03:04:05 master static
> root@debian:~# bridge fdb show dev swp3
> 00:01:02:03:04:05 vlan 1 offload master br0 static
> 00:01:02:03:04:05 offload master br0 static
> 00:04:9f:05:f6:28 vlan 1 master br0 permanent
> 00:04:9f:05:f6:28 master br0 permanent
> 00:01:02:03:04:05 vlan 1 self
> 00:01:02:03:04:05 self
> root@debian:~# ip link set swp3 up
> [46391.626362] fsl_enetc 0000:00:00.2 eno2: configuring for fixed/internal link mode
> [46391.634110] fsl_enetc 0000:00:00.2 eno2: Link is Up - 2.5Gbps/Full - flow control rx/tx
> [46391.635256] mscc_felix 0000:00:00.5 swp3: configuring for inband/qsgmii link mode
> root@debian:~# [46395.751268] mscc_felix 0000:00:00.5 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
> [46395.759613] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
> root@debian:~# bridge fdb show dev swp3
> 00:01:02:03:04:05 vlan 1 offload master br0 static
> 00:01:02:03:04:05 offload master br0 static
> 00:04:9f:05:f6:28 vlan 1 master br0 permanent
> 00:04:9f:05:f6:28 master br0 permanent
> 00:01:02:03:04:05 vlan 1 self
> 00:01:02:03:04:05 self
> root@debian:~# ip link set swp3 down
> [46403.978832] mscc_felix 0000:00:00.5 swp3: Link is Down
> root@debian:~# bridge fdb show dev swp3
> 00:01:02:03:04:05 vlan 1 offload master br0 static
> 00:01:02:03:04:05 offload master br0 static
> 00:04:9f:05:f6:28 vlan 1 master br0 permanent
> 00:04:9f:05:f6:28 master br0 permanent
> 00:01:02:03:04:05 vlan 1 self
> 00:01:02:03:04:05 self
> root@debian:~# ip link set swp3 up
> [46410.499445] mscc_felix 0000:00:00.5 swp3: configuring for inband/qsgmii link mode
> root@debian:~# [46414.597381] mscc_felix 0000:00:00.5 swp3: Link is Up - 1Gbps/Full - flow control rx/tx
> [46414.605775] IPv6: ADDRCONF(NETDEV_CHANGE): swp3: link becomes ready
> root@debian:~# bridge fdb show dev swp3
> 00:01:02:03:04:05 vlan 1 offload master br0 static
> 00:01:02:03:04:05 offload master br0 static
> 00:04:9f:05:f6:28 vlan 1 master br0 permanent
> 00:04:9f:05:f6:28 master br0 permanent
> 00:01:02:03:04:05 vlan 1 self
> 00:01:02:03:04:05 self
>
> I've searched for the call paths of br_fdb_delete_by_port().
> One caller is br_stp_disable_port(), but this passes 0 to "do_all", so
> this can't be it. Also, both IFLA_BRPORT_FLUSH and the "flush" sysfs
> pass 0 to "do_all".
>
> If it's not the user space daemon who is deleting these entries, this is strange.
> More details on my suspicion below.
>
>> > This could work to an extent, but it wouldn't handle the case where the
>> > printer isn't connected directly to the 802.1X port, but through
>> > another dumb switch. I don't know enough about 802.1X, but I don't see
>> > why this isn't a valid configuration.
>> >
>> ATM, the dynamic flag (bridge fdb add MAC dev DEV master dynamic)
>> doesn't create an ageing FDB entry in the offloaded case. Maybe if that
>> was solved, it would be a good enough solution, as for a noisy device,
>> it would lose some packets every 5 minutes, which higher layers should
>> be able to handle?
>
> Before we explore this too deeply, can you first double-check whether
> station migrations will trigger a miss or a member violation?
>
> When I look at the documentation, I see that the switch triggers a member
> violation, unless the ATU age interrupt is enabled and the entry's state
> is less than 4 (which it should be if RefreshLocked is false). In this
> latter case, the documentation says that a miss violation will be
> triggered, not a membership one. So if you satisfy these requirements,
> you should be able to safely set IgnoreWrongData, and not miss out on
> migrations.
>
> My suspicion is that, since you add ATU authorizations on locked ports
> with RefreshLocked as false, they will eventually age out. But the
> driver doesn't enable the ATU age interrupt through Global 2 register 5,
> so software never finds out when these ATU entries expire. And the idea
> of triggering an ATU age interrupt when the entry state becomes less
> than 4 (i.e. they are aged half-way) is precisely done such that you
> don't break networking for the printer every 5 minutes. The switch
> notifies you in advance of the complete entry expiration. I think you
> should service this interrupt in the driver. Note that my belief is that
> such ATU entries on locked ports aren't truly 'dynamic' as far as user
> space is concerned. Sure, they age, but this is just a way for software
> to keep them in check. But user space doesn't have to know this. To the
> application, the entry is static, and the driver just refreshes the
> entry unless told by the application to delete it.
>
> Basically what I'm saying is that more testing needs to be done to make
> sure that the hardware is configured to do something reasonable in all
> cases, and doesn't just work by coincidence.
>
> Makes sense?
>
>> > To explain what I'm thinking of. At office, IT gave one Ethernet port to
>> > each desk, but I have multiple devices. I have a PC, a printer, and a
>> > development board, each with a single Ethernet port, so I use a dumb
>> > 4-port switch to connect all these devices to the 802.1X port beneath my
>> > desk. I talked to IT, brought my printer to them, they agreed to bypass
>> > 802.1X authorization for it based on the MAC address on its label.
>> >
>> > I've been working from home for the past few years, but now I need to
>> > return to office. But since years have passed, some colleagues left,
>> > some new colleagues came, and I need to change my desk. The new one
>> > belonged to a co-worker who also had a dumb switch on his desk, so I see
>> > no reason to move mine too. I unplug the printer from my dumb switch,
>> > plug it into the new one, but it doesn't work. What do I do, open a
>> > ticket to IT asking for halp?
>> >
>> > To be honest this is purely fictional and I haven't tried it, but it
>> > sounds like I should when I get the chance, to get a better image of how
>> > things are supposed to work.
>> >
>> >> >> >> > > Oh, btw, my question was: could you consider suppressing the _prints_ on
>> >> >> >> > > an ATU miss violation on a locked port?
>> >> >> >> >
>> >> >> >> > As there will only be such on the first packet, I think it should be
>> >> >> >> > logged and those prints serve that purpose, so I think it is best to
>> >> >> >> > keep the print.
>> >> >> >> > If in the future some tests or other can argue for suppressing the
>> >> >> >> > prints, it is an easy thing to do.
>> >> >> >>
>> >> >> >> Please use a traffic generator and try to DOS one of your own
>> >> >> >> switches. Can you?
>> >> >> >>
>> >> >> >> Andrew
On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
> In the offloaded case there is no difference between static and dynamic
> flags, which I see as a general issue. (The resulting ATU entry is static
> in either case.)
It _is_ a problem. We had the same problem with the is_local bit.
Independently of this series, you can add the dynamic bit to struct
switchdev_notifier_fdb_info and make drivers reject it.
> These FDB entries are removed when link goes down (soft or hard). The
> zero DPV entries that the new code introduces age out after 5 minutes,
> while the locked flagged FDB entries are removed by link down (thus the
> FDB and the ATU are not in sync in this case).
Ok, so don't let them disappear from hardware, refresh them from the
driver, since user space and the bridge driver expect that they are
still there.
On tor, mar 17, 2022 at 15:19, Andrew Lunn <[email protected]> wrote:
> On Thu, Mar 17, 2022 at 09:52:15AM +0100, Hans Schultz wrote:
>> On tor, mar 17, 2022 at 01:34, Vladimir Oltean <[email protected]> wrote:
>> > On Mon, Mar 14, 2022 at 11:46:51AM +0100, Hans Schultz wrote:
>> >> >> @@ -396,6 +414,13 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
>> >> >> "ATU miss violation for %pM portvec %x spid %d\n",
>> >> >> entry.mac, entry.portvec, spid);
>> >> >> chip->ports[spid].atu_miss_violation++;
>> >> >> + if (mv88e6xxx_port_is_locked(chip, chip->ports[spid].port))
>> >> >> + err = mv88e6xxx_switchdev_handle_atu_miss_violation(chip,
>> >> >> + chip->ports[spid].port,
>> >> >> + &entry,
>> >> >> + fid);
>> >> >
>> >> > Do we want to suppress the ATU miss violation warnings if we're going to
>> >> > notify the bridge, or is it better to keep them for some reason?
>> >> > My logic is that they're part of normal operation, so suppressing makes
>> >> > sense.
>> >> >
>> >>
>> >> I have been seeing many ATU member violations after the miss violation is
>> >> handled (using ping), and I think it could be considered to suppress the ATU member
>> >> violations interrupts by setting the IgnoreWrongData bit for the
>> >> port (sect 4.4.7). This would be something to do whenever a port is set in locked mode?
>> >
>> > So the first packet with a given MAC SA triggers an ATU miss violation
>> > interrupt.
>> >
>> > You program that MAC SA into the ATU with a destination port mask of all
>> > zeroes. This suppresses further ATU miss interrupts for this MAC SA, but
>> > now generates ATU member violations, because the MAC SA _is_ present in
>> > the ATU, but not towards the expected port (in fact, towards _no_ port).
>> >
>> > Especially if user space decides it doesn't want to authorize this MAC
>> > SA, it really becomes a problem because this is now a vector for denial
>> > of service, with every packet triggering an ATU member violation
>> > interrupt.
>> >
>> > So your suggestion is to set the IgnoreWrongData bit on locked ports,
>> > and this will suppress the actual member violation interrupts for
>> > traffic coming from these ports.
>> >
>> > So if the user decides to unplug a previously authorized printer from
>> > switch port 1 and move it to port 2, how is this handled? If there isn't
>> > a mechanism in place to delete the locked FDB entry when the printer
>> > goes away, then by setting IgnoreWrongData you're effectively also
>> > suppressing migration notifications.
>>
>> I don't think such a scenario is so realistic, as changing port is not
>> just something done casually, besides port 2 then must also be a locked
>> port to have the same policy.
>
> I think it is very realistic. It is also something which does not work
> is going to cause a lot of confusion. People will blame the printer,
> when in fact they should be blaming the switch. They will be rebooting
> the printer, when in fact, they need to reboot the switch etc.
>
> I expect there is a way to cleanly support this, you just need to
> figure it out.
>
>> The other aspect is that the user space daemon that authorizes catches
>> the fdb add entry events and checks if it is a locked entry. So it will
>> be up to said daemon to decide the policy, like remove the fdb entry
>> after a timeout.
>>
>> >
>> > Oh, btw, my question was: could you consider suppressing the _prints_ on
>> > an ATU miss violation on a locked port?
>>
>> As there will only be such on the first packet, I think it should be
>> logged and those prints serve that purpose, so I think it is best to
>> keep the print.
>> If in the future some tests or other can argue for suppressing the
>> prints, it is an easy thing to do.
>
> Please use a traffic generator and try to DOS one of your own
> switches. Can you?
>
> Andrew
Here is a trafgen report, where I sent packets to a locked port with random SAs:
42527020 packets outgoing
3104472460 bytes outgoing
329 sec, 989345 usec on CPU0 (5835746 packets)
329 sec, 985243 usec on CPU1 (2119061 packets)
329 sec, 997323 usec on CPU2 (5656546 packets)
329 sec, 989475 usec on CPU3 (5617322 packets)
330 sec, 5228 usec on CPU4 (6034671 packets)
330 sec, 1603 usec on CPU5 (5833505 packets)
329 sec, 989319 usec on CPU6 (5709841 packets)
329 sec, 989294 usec on CPU7 (5720328 packets)
I could do 'bridge fdb show' after stopping the traffic, printing out a
very long list (minutes to print). The ATU was normal, so there is an
issue of the soft FDB locked entries not ageing out.
I saw many reports of suppressed IRQs in the kernel log.
On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
> On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>> In the offloaded case there is no difference between static and dynamic
>> flags, which I see as a general issue. (The resulting ATU entry is static
>> in either case.)
>
> It _is_ a problem. We had the same problem with the is_local bit.
> Independently of this series, you can add the dynamic bit to struct
> switchdev_notifier_fdb_info and make drivers reject it.
>
>> These FDB entries are removed when link goes down (soft or hard). The
>> zero DPV entries that the new code introduces age out after 5 minutes,
>> while the locked flagged FDB entries are removed by link down (thus the
>> FDB and the ATU are not in sync in this case).
>
> Ok, so don't let them disappear from hardware, refresh them from the
> driver, since user space and the bridge driver expect that they are
> still there.
I have now tested with two extra unmanaged switches (each connected to a
seperate port on our managed switch, and when migrating from one port to
another, there is member violations, but as the initial entry ages out,
a new miss violation occurs and the new port adds the locked entry. In
this case I only see one locked entry, either on the initial port or
later on the port the host migrated to (via switch).
If I refresh the ATU entries indefinitly, then this migration will for
sure not work, and with the member violation suppressed, it will be
silent about it.
So I don't think it is a good idea to refresh the ATU entries
indefinitely.
Another issue I see, is that there is a deadlock or similar issue when
receiving violations and running 'bridge fdb show' (it seemed that
member violations also caused this, but not sure yet...), as the unit
freezes, not to return...
On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
> On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
>> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
>> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>> >> In the offloaded case there is no difference between static and dynamic
>> >> flags, which I see as a general issue. (The resulting ATU entry is static
>> >> in either case.)
>> >
>> > It _is_ a problem. We had the same problem with the is_local bit.
>> > Independently of this series, you can add the dynamic bit to struct
>> > switchdev_notifier_fdb_info and make drivers reject it.
>> >
>> >> These FDB entries are removed when link goes down (soft or hard). The
>> >> zero DPV entries that the new code introduces age out after 5 minutes,
>> >> while the locked flagged FDB entries are removed by link down (thus the
>> >> FDB and the ATU are not in sync in this case).
>> >
>> > Ok, so don't let them disappear from hardware, refresh them from the
>> > driver, since user space and the bridge driver expect that they are
>> > still there.
>>
>> I have now tested with two extra unmanaged switches (each connected to a
>> seperate port on our managed switch, and when migrating from one port to
>> another, there is member violations, but as the initial entry ages out,
>> a new miss violation occurs and the new port adds the locked entry. In
>> this case I only see one locked entry, either on the initial port or
>> later on the port the host migrated to (via switch).
>>
>> If I refresh the ATU entries indefinitly, then this migration will for
>> sure not work, and with the member violation suppressed, it will be
>> silent about it.
>
> Manual says that migrations should trigger miss violations if configured
> adequately, is this not the case?
>
Yes, but that depends on the ATU entries ageing out. As it is now, it works.
>> So I don't think it is a good idea to refresh the ATU entries
>> indefinitely.
>>
>> Another issue I see, is that there is a deadlock or similar issue when
>> receiving violations and running 'bridge fdb show' (it seemed that
>> member violations also caused this, but not sure yet...), as the unit
>> freezes, not to return...
>
> Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
> like that?
No, I haven't looked deeper into it yet. Maybe I was hoping someone had
an idea... but I guess it cannot be a netlink deadlock?
On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
> >> In the offloaded case there is no difference between static and dynamic
> >> flags, which I see as a general issue. (The resulting ATU entry is static
> >> in either case.)
> >
> > It _is_ a problem. We had the same problem with the is_local bit.
> > Independently of this series, you can add the dynamic bit to struct
> > switchdev_notifier_fdb_info and make drivers reject it.
> >
> >> These FDB entries are removed when link goes down (soft or hard). The
> >> zero DPV entries that the new code introduces age out after 5 minutes,
> >> while the locked flagged FDB entries are removed by link down (thus the
> >> FDB and the ATU are not in sync in this case).
> >
> > Ok, so don't let them disappear from hardware, refresh them from the
> > driver, since user space and the bridge driver expect that they are
> > still there.
>
> I have now tested with two extra unmanaged switches (each connected to a
> seperate port on our managed switch, and when migrating from one port to
> another, there is member violations, but as the initial entry ages out,
> a new miss violation occurs and the new port adds the locked entry. In
> this case I only see one locked entry, either on the initial port or
> later on the port the host migrated to (via switch).
>
> If I refresh the ATU entries indefinitly, then this migration will for
> sure not work, and with the member violation suppressed, it will be
> silent about it.
Manual says that migrations should trigger miss violations if configured
adequately, is this not the case?
> So I don't think it is a good idea to refresh the ATU entries
> indefinitely.
>
> Another issue I see, is that there is a deadlock or similar issue when
> receiving violations and running 'bridge fdb show' (it seemed that
> member violations also caused this, but not sure yet...), as the unit
> freezes, not to return...
Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
like that?
On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
> On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
>> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
>> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>> >> In the offloaded case there is no difference between static and dynamic
>> >> flags, which I see as a general issue. (The resulting ATU entry is static
>> >> in either case.)
>> >
>> > It _is_ a problem. We had the same problem with the is_local bit.
>> > Independently of this series, you can add the dynamic bit to struct
>> > switchdev_notifier_fdb_info and make drivers reject it.
>> >
>> >> These FDB entries are removed when link goes down (soft or hard). The
>> >> zero DPV entries that the new code introduces age out after 5 minutes,
>> >> while the locked flagged FDB entries are removed by link down (thus the
>> >> FDB and the ATU are not in sync in this case).
>> >
>> > Ok, so don't let them disappear from hardware, refresh them from the
>> > driver, since user space and the bridge driver expect that they are
>> > still there.
>>
>> I have now tested with two extra unmanaged switches (each connected to a
>> seperate port on our managed switch, and when migrating from one port to
>> another, there is member violations, but as the initial entry ages out,
>> a new miss violation occurs and the new port adds the locked entry. In
>> this case I only see one locked entry, either on the initial port or
>> later on the port the host migrated to (via switch).
>>
>> If I refresh the ATU entries indefinitly, then this migration will for
>> sure not work, and with the member violation suppressed, it will be
>> silent about it.
>
> Manual says that migrations should trigger miss violations if configured
> adequately, is this not the case?
>
>> So I don't think it is a good idea to refresh the ATU entries
>> indefinitely.
>>
>> Another issue I see, is that there is a deadlock or similar issue when
>> receiving violations and running 'bridge fdb show' (it seemed that
>> member violations also caused this, but not sure yet...), as the unit
>> freezes, not to return...
>
> Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
> like that?
I have now determined that it is the rtnl_lock() that causes the
"deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
takes care of getting the fdb entries when running 'bridge fdb show'. In
principle there should be no problem with this, but I don't know if some
interrupt queue is getting jammed as they are blocked from rtnetlink.c?
On Wed, Mar 23, 2022 at 11:13:51AM +0100, Hans Schultz wrote:
> On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
> > On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
> >> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
> >> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
> >> >> In the offloaded case there is no difference between static and dynamic
> >> >> flags, which I see as a general issue. (The resulting ATU entry is static
> >> >> in either case.)
> >> >
> >> > It _is_ a problem. We had the same problem with the is_local bit.
> >> > Independently of this series, you can add the dynamic bit to struct
> >> > switchdev_notifier_fdb_info and make drivers reject it.
> >> >
> >> >> These FDB entries are removed when link goes down (soft or hard). The
> >> >> zero DPV entries that the new code introduces age out after 5 minutes,
> >> >> while the locked flagged FDB entries are removed by link down (thus the
> >> >> FDB and the ATU are not in sync in this case).
> >> >
> >> > Ok, so don't let them disappear from hardware, refresh them from the
> >> > driver, since user space and the bridge driver expect that they are
> >> > still there.
> >>
> >> I have now tested with two extra unmanaged switches (each connected to a
> >> seperate port on our managed switch, and when migrating from one port to
> >> another, there is member violations, but as the initial entry ages out,
> >> a new miss violation occurs and the new port adds the locked entry. In
> >> this case I only see one locked entry, either on the initial port or
> >> later on the port the host migrated to (via switch).
> >>
> >> If I refresh the ATU entries indefinitly, then this migration will for
> >> sure not work, and with the member violation suppressed, it will be
> >> silent about it.
> >
> > Manual says that migrations should trigger miss violations if configured
> > adequately, is this not the case?
> >
> >> So I don't think it is a good idea to refresh the ATU entries
> >> indefinitely.
> >>
> >> Another issue I see, is that there is a deadlock or similar issue when
> >> receiving violations and running 'bridge fdb show' (it seemed that
> >> member violations also caused this, but not sure yet...), as the unit
> >> freezes, not to return...
> >
> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
> > like that?
>
> I have now determined that it is the rtnl_lock() that causes the
> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
> takes care of getting the fdb entries when running 'bridge fdb show'. In
> principle there should be no problem with this, but I don't know if some
> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
Sorry, I forgot to respond yesterday to this.
By any chance do you maybe have an AB/BA lock inversion, where from the
ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
On Wed, Mar 23, 2022 at 11:57:16AM +0100, Hans Schultz wrote:
> >> >> Another issue I see, is that there is a deadlock or similar issue when
> >> >> receiving violations and running 'bridge fdb show' (it seemed that
> >> >> member violations also caused this, but not sure yet...), as the unit
> >> >> freezes, not to return...
> >> >
> >> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
> >> > like that?
> >>
> >> I have now determined that it is the rtnl_lock() that causes the
> >> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
> >> takes care of getting the fdb entries when running 'bridge fdb show'. In
> >> principle there should be no problem with this, but I don't know if some
> >> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
> >
> > Sorry, I forgot to respond yesterday to this.
> > By any chance do you maybe have an AB/BA lock inversion, where from the
> > ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
> > from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
>
> If I release the mv88e6xxx_reg_lock() before calling the handler, I need
> to get it again for the mv88e6xxx_g1_atu_loadpurge() call at least. But
> maybe the vtu_walk also needs the mv88e6xxx_reg_lock()?
> I could also just release the mv88e6xxx_reg_lock() before the
> call_switchdev_notifiers() call and reacquire it immediately after?
The cleanest way to go about this would be to have the call_switchdev_notifiers()
portion of the ATU interrupt handling at the very end of mv88e6xxx_g1_atu_prob_irq_thread_fn(),
with no hardware access needed, and therefore no reg_lock() held.
On tis, mar 22, 2022 at 14:21, Hans Schultz <[email protected]> wrote:
> On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
>> On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
>>> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
>>> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>>> >> In the offloaded case there is no difference between static and dynamic
>>> >> flags, which I see as a general issue. (The resulting ATU entry is static
>>> >> in either case.)
>>> >
>>> > It _is_ a problem. We had the same problem with the is_local bit.
>>> > Independently of this series, you can add the dynamic bit to struct
>>> > switchdev_notifier_fdb_info and make drivers reject it.
>>> >
>>> >> These FDB entries are removed when link goes down (soft or hard). The
>>> >> zero DPV entries that the new code introduces age out after 5 minutes,
>>> >> while the locked flagged FDB entries are removed by link down (thus the
>>> >> FDB and the ATU are not in sync in this case).
>>> >
>>> > Ok, so don't let them disappear from hardware, refresh them from the
>>> > driver, since user space and the bridge driver expect that they are
>>> > still there.
>>>
>>> I have now tested with two extra unmanaged switches (each connected to a
>>> seperate port on our managed switch, and when migrating from one port to
>>> another, there is member violations, but as the initial entry ages out,
>>> a new miss violation occurs and the new port adds the locked entry. In
>>> this case I only see one locked entry, either on the initial port or
>>> later on the port the host migrated to (via switch).
>>>
>>> If I refresh the ATU entries indefinitly, then this migration will for
>>> sure not work, and with the member violation suppressed, it will be
>>> silent about it.
>>
>> Manual says that migrations should trigger miss violations if configured
>> adequately, is this not the case?
>>
> Yes, but that depends on the ATU entries ageing out. As it is now, it works.
>
>>> So I don't think it is a good idea to refresh the ATU entries
>>> indefinitely.
>>>
>>> Another issue I see, is that there is a deadlock or similar issue when
>>> receiving violations and running 'bridge fdb show' (it seemed that
>>> member violations also caused this, but not sure yet...), as the unit
>>> freezes, not to return...
I have now verified that it is only on miss violations that the problem
occurs, so it seems that there is a deadlock (with 'bridge fdb show')
somehow with the nl lock that the handling of ATU miss violations
acquires.
>>
>> Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
>> like that?
>
> No, I haven't looked deeper into it yet. Maybe I was hoping someone had
> an idea... but I guess it cannot be a netlink deadlock?
On ons, mar 23, 2022 at 12:16, Vladimir Oltean <[email protected]> wrote:
> On Wed, Mar 23, 2022 at 11:13:51AM +0100, Hans Schultz wrote:
>> On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
>> > On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
>> >> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
>> >> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>> >> >> In the offloaded case there is no difference between static and dynamic
>> >> >> flags, which I see as a general issue. (The resulting ATU entry is static
>> >> >> in either case.)
>> >> >
>> >> > It _is_ a problem. We had the same problem with the is_local bit.
>> >> > Independently of this series, you can add the dynamic bit to struct
>> >> > switchdev_notifier_fdb_info and make drivers reject it.
>> >> >
>> >> >> These FDB entries are removed when link goes down (soft or hard). The
>> >> >> zero DPV entries that the new code introduces age out after 5 minutes,
>> >> >> while the locked flagged FDB entries are removed by link down (thus the
>> >> >> FDB and the ATU are not in sync in this case).
>> >> >
>> >> > Ok, so don't let them disappear from hardware, refresh them from the
>> >> > driver, since user space and the bridge driver expect that they are
>> >> > still there.
>> >>
>> >> I have now tested with two extra unmanaged switches (each connected to a
>> >> seperate port on our managed switch, and when migrating from one port to
>> >> another, there is member violations, but as the initial entry ages out,
>> >> a new miss violation occurs and the new port adds the locked entry. In
>> >> this case I only see one locked entry, either on the initial port or
>> >> later on the port the host migrated to (via switch).
>> >>
>> >> If I refresh the ATU entries indefinitly, then this migration will for
>> >> sure not work, and with the member violation suppressed, it will be
>> >> silent about it.
>> >
>> > Manual says that migrations should trigger miss violations if configured
>> > adequately, is this not the case?
>> >
>> >> So I don't think it is a good idea to refresh the ATU entries
>> >> indefinitely.
>> >>
>> >> Another issue I see, is that there is a deadlock or similar issue when
>> >> receiving violations and running 'bridge fdb show' (it seemed that
>> >> member violations also caused this, but not sure yet...), as the unit
>> >> freezes, not to return...
>> >
>> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
>> > like that?
>>
>> I have now determined that it is the rtnl_lock() that causes the
>> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
>> takes care of getting the fdb entries when running 'bridge fdb show'. In
>> principle there should be no problem with this, but I don't know if some
>> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
>
> Sorry, I forgot to respond yesterday to this.
> By any chance do you maybe have an AB/BA lock inversion, where from the
> ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
> from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
If I release the mv88e6xxx_reg_lock() before calling the handler, I need
to get it again for the mv88e6xxx_g1_atu_loadpurge() call at least. But
maybe the vtu_walk also needs the mv88e6xxx_reg_lock()?
I could also just release the mv88e6xxx_reg_lock() before the
call_switchdev_notifiers() call and reacquire it immediately after?
On Wed, Mar 23, 2022 at 12:43:03PM +0100, Hans Schultz wrote:
> On ons, mar 23, 2022 at 13:21, Vladimir Oltean <[email protected]> wrote:
> > On Wed, Mar 23, 2022 at 11:57:16AM +0100, Hans Schultz wrote:
> >> >> >> Another issue I see, is that there is a deadlock or similar issue when
> >> >> >> receiving violations and running 'bridge fdb show' (it seemed that
> >> >> >> member violations also caused this, but not sure yet...), as the unit
> >> >> >> freezes, not to return...
> >> >> >
> >> >> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
> >> >> > like that?
> >> >>
> >> >> I have now determined that it is the rtnl_lock() that causes the
> >> >> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
> >> >> takes care of getting the fdb entries when running 'bridge fdb show'. In
> >> >> principle there should be no problem with this, but I don't know if some
> >> >> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
> >> >
> >> > Sorry, I forgot to respond yesterday to this.
> >> > By any chance do you maybe have an AB/BA lock inversion, where from the
> >> > ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
> >> > from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
> >>
> >> If I release the mv88e6xxx_reg_lock() before calling the handler, I need
> >> to get it again for the mv88e6xxx_g1_atu_loadpurge() call at least. But
> >> maybe the vtu_walk also needs the mv88e6xxx_reg_lock()?
> >> I could also just release the mv88e6xxx_reg_lock() before the
> >> call_switchdev_notifiers() call and reacquire it immediately after?
> >
> > The cleanest way to go about this would be to have the call_switchdev_notifiers()
> > portion of the ATU interrupt handling at the very end of mv88e6xxx_g1_atu_prob_irq_thread_fn(),
> > with no hardware access needed, and therefore no reg_lock() held.
>
> So something like?
> mv88e6xxx_reg_unlock(chip);
> rtnl_lock();
> err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, NULL);
> rtnl_unlock();
> mv88e6xxx_reg_lock(chip);
No, call_switchdev_notifiers() should be the very end, no reg_lock() afterwards.
Do all the hardware handling you need, populate some variables to denote
that you need to notify switchdev, and if you do, lock the rtnetlink
mutex and do it.
On ons, mar 23, 2022 at 12:16, Vladimir Oltean <[email protected]> wrote:
> On Wed, Mar 23, 2022 at 11:13:51AM +0100, Hans Schultz wrote:
>> On tis, mar 22, 2022 at 13:08, Vladimir Oltean <[email protected]> wrote:
>> > On Tue, Mar 22, 2022 at 12:01:13PM +0100, Hans Schultz wrote:
>> >> On fre, mar 18, 2022 at 15:19, Vladimir Oltean <[email protected]> wrote:
>> >> > On Fri, Mar 18, 2022 at 02:10:26PM +0100, Hans Schultz wrote:
>> >> >> In the offloaded case there is no difference between static and dynamic
>> >> >> flags, which I see as a general issue. (The resulting ATU entry is static
>> >> >> in either case.)
>> >> >
>> >> > It _is_ a problem. We had the same problem with the is_local bit.
>> >> > Independently of this series, you can add the dynamic bit to struct
>> >> > switchdev_notifier_fdb_info and make drivers reject it.
>> >> >
>> >> >> These FDB entries are removed when link goes down (soft or hard). The
>> >> >> zero DPV entries that the new code introduces age out after 5 minutes,
>> >> >> while the locked flagged FDB entries are removed by link down (thus the
>> >> >> FDB and the ATU are not in sync in this case).
>> >> >
>> >> > Ok, so don't let them disappear from hardware, refresh them from the
>> >> > driver, since user space and the bridge driver expect that they are
>> >> > still there.
>> >>
>> >> I have now tested with two extra unmanaged switches (each connected to a
>> >> seperate port on our managed switch, and when migrating from one port to
>> >> another, there is member violations, but as the initial entry ages out,
>> >> a new miss violation occurs and the new port adds the locked entry. In
>> >> this case I only see one locked entry, either on the initial port or
>> >> later on the port the host migrated to (via switch).
>> >>
>> >> If I refresh the ATU entries indefinitly, then this migration will for
>> >> sure not work, and with the member violation suppressed, it will be
>> >> silent about it.
>> >
>> > Manual says that migrations should trigger miss violations if configured
>> > adequately, is this not the case?
>> >
>> >> So I don't think it is a good idea to refresh the ATU entries
>> >> indefinitely.
>> >>
>> >> Another issue I see, is that there is a deadlock or similar issue when
>> >> receiving violations and running 'bridge fdb show' (it seemed that
>> >> member violations also caused this, but not sure yet...), as the unit
>> >> freezes, not to return...
>> >
>> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
>> > like that?
>>
>> I have now determined that it is the rtnl_lock() that causes the
>> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
>> takes care of getting the fdb entries when running 'bridge fdb show'. In
>> principle there should be no problem with this, but I don't know if some
>> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
>
> Sorry, I forgot to respond yesterday to this.
> By any chance do you maybe have an AB/BA lock inversion, where from the
> ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
> from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
Yes, I forgot that the whole handler is under mv88e6xxx_reg_lock(). I
hope then that I can release the mv88e6xxx_reg_lock() before calling the
handler function with issues?
On ons, mar 23, 2022 at 13:21, Vladimir Oltean <[email protected]> wrote:
> On Wed, Mar 23, 2022 at 11:57:16AM +0100, Hans Schultz wrote:
>> >> >> Another issue I see, is that there is a deadlock or similar issue when
>> >> >> receiving violations and running 'bridge fdb show' (it seemed that
>> >> >> member violations also caused this, but not sure yet...), as the unit
>> >> >> freezes, not to return...
>> >> >
>> >> > Have you enabled lockdep, debug atomic sleep, detect hung tasks, things
>> >> > like that?
>> >>
>> >> I have now determined that it is the rtnl_lock() that causes the
>> >> "deadlock". The doit() in rtnetlink.c is under rtnl_lock() and is what
>> >> takes care of getting the fdb entries when running 'bridge fdb show'. In
>> >> principle there should be no problem with this, but I don't know if some
>> >> interrupt queue is getting jammed as they are blocked from rtnetlink.c?
>> >
>> > Sorry, I forgot to respond yesterday to this.
>> > By any chance do you maybe have an AB/BA lock inversion, where from the
>> > ATU interrupt handler you do mv88e6xxx_reg_lock() -> rtnl_lock(), while
>> > from the port_fdb_dump() handler you do rtnl_lock() -> mv88e6xxx_reg_lock()?
>>
>> If I release the mv88e6xxx_reg_lock() before calling the handler, I need
>> to get it again for the mv88e6xxx_g1_atu_loadpurge() call at least. But
>> maybe the vtu_walk also needs the mv88e6xxx_reg_lock()?
>> I could also just release the mv88e6xxx_reg_lock() before the
>> call_switchdev_notifiers() call and reacquire it immediately after?
>
> The cleanest way to go about this would be to have the call_switchdev_notifiers()
> portion of the ATU interrupt handling at the very end of mv88e6xxx_g1_atu_prob_irq_thread_fn(),
> with no hardware access needed, and therefore no reg_lock() held.
So something like?
mv88e6xxx_reg_unlock(chip);
rtnl_lock();
err = call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, brport, &info.info, NULL);
rtnl_unlock();
mv88e6xxx_reg_lock(chip);