Skip to content

Commit

Permalink
net: dsa: mv88e6xxx: mac-auth/MAB implementation
Browse files Browse the repository at this point in the history
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 will be added to the ATU with a zero-DPV,
and is then communicated through switchdev to the bridge module,
which adds a fdb entry with the fdb locked flag set. The entry
is kept according to the bridges ageing time, thus simulating a
dynamic entry.

As this is essentially a form of CPU based learning, the amount
of locked entries will be limited by a hardcoded value for now,
so as to prevent DOS attacks.

Signed-off-by: Hans Schultz <netdev@kapio-technology.com>
  • Loading branch information
Hans Schultz authored and intel-lab-lkp committed Jul 7, 2022
1 parent 804e3a7 commit 74f76ae
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 17 deletions.
1 change: 1 addition & 0 deletions drivers/net/dsa/mv88e6xxx/Makefile
Expand Up @@ -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 += switchdev.o
49 changes: 39 additions & 10 deletions drivers/net/dsa/mv88e6xxx/chip.c
Expand Up @@ -42,6 +42,7 @@
#include "ptp.h"
#include "serdes.h"
#include "smi.h"
#include "switchdev.h"

static void assert_reg_lock(struct mv88e6xxx_chip *chip)
{
Expand Down Expand Up @@ -919,6 +920,13 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
if (err)
dev_err(chip->dev,
"p%d: failed to force MAC link down\n", port);
else
if (mv88e6xxx_port_is_locked(chip, port)) {
err = mv88e6xxx_atu_locked_entry_flush(ds, port);
if (err)
dev_err(chip->dev,
"p%d: failed to clear locked entries\n", port);
}
}

static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
Expand Down Expand Up @@ -1685,6 +1693,12 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
struct mv88e6xxx_chip *chip = ds->priv;
int err;

if (mv88e6xxx_port_is_locked(chip, port))
err = mv88e6xxx_atu_locked_entry_flush(ds, port);
if (err)
dev_err(chip->ds->dev, "p%d: failed to clear locked entries: %d\n",
port, err);

mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_fast_age_fid(chip, port, 0);
mv88e6xxx_reg_unlock(chip);
Expand Down Expand Up @@ -1721,11 +1735,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),
Expand Down Expand Up @@ -2727,6 +2741,9 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
if (is_locked)
return 0;

if (mv88e6xxx_port_is_locked(chip, port))
mv88e6xxx_atu_locked_entry_find_purge(ds, port, addr, vid);

mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
Expand All @@ -2740,12 +2757,17 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
struct dsa_db db)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
bool locked_found = false;
int err = 0;

mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0);
mv88e6xxx_reg_unlock(chip);
if (mv88e6xxx_port_is_locked(chip, port))
locked_found = mv88e6xxx_atu_locked_entry_find_purge(ds, port, addr, vid);

if (!locked_found) {
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0);
mv88e6xxx_reg_unlock(chip);
}
return err;
}

Expand Down Expand Up @@ -3814,11 +3836,18 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)

static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port)
{
return mv88e6xxx_setup_devlink_regions_port(ds, port);
int err;

err = mv88e6xxx_setup_devlink_regions_port(ds, port);
if (!err)
return mv88e6xxx_init_violation_handler(ds, port);

return err;
}

static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port)
{
mv88e6xxx_teardown_violation_handler(ds, port);
mv88e6xxx_teardown_devlink_regions_port(ds, port);
}

Expand Down
15 changes: 15 additions & 0 deletions drivers/net/dsa/mv88e6xxx/chip.h
Expand Up @@ -280,6 +280,12 @@ struct mv88e6xxx_port {
unsigned int serdes_irq;
char serdes_irq_name[64];
struct devlink_region *region;

/* List and maintenance of ATU locked entries */
struct mutex ale_list_lock;
struct list_head ale_list;
struct delayed_work ale_work;
int ale_cnt;
};

enum mv88e6xxx_region_id {
Expand Down Expand Up @@ -399,6 +405,9 @@ struct mv88e6xxx_chip {
int egress_dest_port;
int ingress_dest_port;

/* Keep the register written age time for easy access */
u8 age_time;

/* Per-port timestamping resources. */
struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];

Expand Down Expand Up @@ -803,6 +812,12 @@ 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 */
1 change: 1 addition & 0 deletions drivers/net/dsa/mv88e6xxx/global1.h
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion drivers/net/dsa/mv88e6xxx/global1_atu.c
Expand Up @@ -12,6 +12,8 @@

#include "chip.h"
#include "global1.h"
#include "port.h"
#include "switchdev.h"

/* Offset 0x01: ATU FID Register */

Expand Down Expand Up @@ -54,6 +56,7 @@ int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,

/* Round to nearest multiple of coeff */
age_time = (msecs + coeff / 2) / coeff;
chip->age_time = age_time;

err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
if (err)
Expand Down Expand Up @@ -369,6 +372,7 @@ 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);

Expand All @@ -380,6 +384,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;
Expand All @@ -388,6 +396,8 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
if (err)
goto out;

mv88e6xxx_reg_unlock(chip);

spid = entry.state;

if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) {
Expand All @@ -408,6 +418,11 @@ 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 (fid && mv88e6xxx_port_is_locked(chip, spid))
err = mv88e6xxx_handle_violation(chip, spid, &entry, fid,
MV88E6XXX_G1_ATU_OP_MISS_VIOLATION);
if (err)
goto out;
}

if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
Expand All @@ -416,7 +431,6 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
entry.mac, entry.portvec, spid);
chip->ports[spid].atu_full_violation++;
}
mv88e6xxx_reg_unlock(chip);

return IRQ_HANDLED;

Expand Down
30 changes: 24 additions & 6 deletions drivers/net/dsa/mv88e6xxx/port.c
Expand Up @@ -14,9 +14,11 @@
#include <linux/phylink.h>

#include "chip.h"
#include "global1.h"
#include "global2.h"
#include "port.h"
#include "serdes.h"
#include "switchdev.h"

int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
u16 *val)
Expand Down Expand Up @@ -1239,6 +1241,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;

mv88e6xxx_reg_lock(chip);
mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
mv88e6xxx_reg_unlock(chip);

return reg & MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK;
}

int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
bool locked)
{
Expand All @@ -1257,13 +1270,18 @@ int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
if (err)
return err;

err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, &reg);
if (err)
return err;
if (!locked) {
err = mv88e6xxx_atu_locked_entry_flush(chip->ds, port);
if (err)
return err;
}

reg &= ~MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
if (locked)
reg |= MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
reg = 0;
if (locked) {
reg = (1 << port);
reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG |
MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
}

return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, reg);
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/dsa/mv88e6xxx/port.h
Expand Up @@ -231,6 +231,7 @@
#define MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT 0x2000
#define MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG 0x1000
#define MV88E6XXX_PORT_ASSOC_VECTOR_REFRESH_LOCKED 0x0800
#define MV88E6XXX_PORT_ASSOC_VECTOR_PAV_MASK 0x07ff

/* Offset 0x0C: Port ATU Control */
#define MV88E6XXX_PORT_ATU_CTL 0x0c
Expand Down Expand Up @@ -374,6 +375,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);

Expand Down

0 comments on commit 74f76ae

Please sign in to comment.