Commit 54c31984 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: mscc: ocelot: enforce FDB isolation when VLAN-unaware

Currently ocelot uses a pvid of 0 for standalone ports and ports under a
VLAN-unaware bridge, and the pvid of the bridge for ports under a
VLAN-aware bridge. Standalone ports do not perform learning, but packets
received on them are still subject to FDB lookups. So if the MAC DA that
a standalone port receives has been also learned on a VLAN-unaware
bridge port, ocelot will attempt to forward to that port, even though it
can't, so it will drop packets.

So there is a desire to avoid that, and isolate the FDBs of different
bridges from one another, and from standalone ports.

The ocelot switch library has two distinct entry points: the felix DSA
driver and the ocelot switchdev driver.

We need to code up a minimal bridge_num allocation in the ocelot
switchdev driver too, this is copied from DSA with the exception that
ocelot does not care about DSA trees, cross-chip bridging etc. So it
only looks at its own ports that are already in the same bridge.

The ocelot switchdev driver uses the bridge_num it has allocated itself,
while the felix driver uses the bridge_num allocated by DSA. They are
both stored inside ocelot_port->bridge_num by the common function
ocelot_port_bridge_join() which receives the bridge_num passed by value.

Once we have a bridge_num, we can only use it to enforce isolation
between VLAN-unaware bridges. As far as I can see, ocelot does not have
anything like a FID that further makes VLAN 100 from a port be different
to VLAN 100 from another port with regard to FDB lookup. So we simply
deny multiple VLAN-aware bridges.

For VLAN-unaware bridges, we crop the 4000-4095 VLAN region and we
allocate a VLAN for each bridge_num. This will be used as the pvid of
each port that is under that VLAN-unaware bridge, for as long as that
bridge is VLAN-unaware.

VID 0 remains only for standalone ports. It is okay if all standalone
ports use the same VID 0, since they perform no address learning, the
FDB will contain no entry in VLAN 0, so the packets will always be
flooded to the only possible destination, the CPU port.

The CPU port module doesn't need to be member of the VLANs to receive
packets, but if we use the DSA tag_8021q protocol, those packets are
part of the data plane as far as ocelot is concerned, so there it needs
to. Just ensure that the DSA tag_8021q CPU port is a member of all
reserved VLANs when it is created, and is removed when it is deleted.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 219827ef
......@@ -234,7 +234,7 @@ static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port)
{
mutex_lock(&ocelot->fwd_domain_lock);
ocelot->ports[port]->is_dsa_8021q_cpu = true;
ocelot_port_set_dsa_8021q_cpu(ocelot, port);
ocelot->npi = -1;
/* Overwrite PGID_CPU with the non-tagging port */
......@@ -250,6 +250,7 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
mutex_lock(&ocelot->fwd_domain_lock);
ocelot->ports[port]->is_dsa_8021q_cpu = false;
ocelot_port_unset_dsa_8021q_cpu(ocelot, port);
/* Restore PGID_CPU */
ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,
......@@ -591,58 +592,99 @@ static int felix_fdb_dump(struct dsa_switch *ds, int port,
return ocelot_fdb_dump(ocelot, port, cb, data);
}
/* Translate the DSA database API into the ocelot switch library API,
* which uses VID 0 for all ports that aren't part of a bridge,
* and expects the bridge_dev to be NULL in that case.
*/
static struct net_device *felix_classify_db(struct dsa_db db)
{
switch (db.type) {
case DSA_DB_PORT:
case DSA_DB_LAG:
return NULL;
case DSA_DB_BRIDGE:
return db.bridge.dev;
default:
return ERR_PTR(-EOPNOTSUPP);
}
}
static int felix_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_fdb_add(ocelot, port, addr, vid);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev);
}
static int felix_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_fdb_del(ocelot, port, addr, vid);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev);
}
static int felix_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid, bridge_dev);
}
static int felix_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid, bridge_dev);
}
static int felix_mdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_port_mdb_add(ocelot, port, mdb);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev);
}
static int felix_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db)
{
struct net_device *bridge_dev = felix_classify_db(db);
struct ocelot *ocelot = ds->priv;
return ocelot_port_mdb_del(ocelot, port, mdb);
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev);
}
static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
......@@ -679,9 +721,8 @@ static int felix_bridge_join(struct dsa_switch *ds, int port,
{
struct ocelot *ocelot = ds->priv;
ocelot_port_bridge_join(ocelot, port, bridge.dev);
return 0;
return ocelot_port_bridge_join(ocelot, port, bridge.dev, bridge.num,
extack);
}
static void felix_bridge_leave(struct dsa_switch *ds, int port,
......@@ -1208,6 +1249,8 @@ static int felix_setup(struct dsa_switch *ds)
ds->mtu_enforcement_ingress = true;
ds->assisted_learning_on_cpu_port = true;
ds->fdb_isolation = true;
ds->max_num_bridges = ds->num_ports;
return 0;
......
This diff is collapsed.
......@@ -26,7 +26,7 @@
#include "ocelot_rew.h"
#include "ocelot_qs.h"
#define OCELOT_VLAN_UNAWARE_PVID 0
#define OCELOT_STANDALONE_PVID 0
#define OCELOT_BUFFER_CELL_SZ 60
#define OCELOT_STATS_CHECK_DELAY (2 * HZ)
......@@ -81,6 +81,9 @@ struct ocelot_multicast {
struct ocelot_pgid *pgid;
};
int ocelot_bridge_num_find(struct ocelot *ocelot,
const struct net_device *bridge);
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data);
int ocelot_mact_learn(struct ocelot *ocelot, int port,
......
......@@ -107,16 +107,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot,
struct ocelot_port *port)
{
ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac,
OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac,
OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
}
static void ocelot_mrp_del_mac(struct ocelot *ocelot,
struct ocelot_port *port)
{
ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID);
ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID);
ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_STANDALONE_PVID);
ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_STANDALONE_PVID);
}
int ocelot_mrp_add(struct ocelot *ocelot, int port,
......
......@@ -419,7 +419,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
* with VLAN filtering feature. We need to keep it to receive
* untagged traffic.
*/
if (vid == OCELOT_VLAN_UNAWARE_PVID)
if (vid == OCELOT_STANDALONE_PVID)
return 0;
ret = ocelot_vlan_del(ocelot, port, vid);
......@@ -559,7 +559,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
struct ocelot_mact_work_ctx w;
ether_addr_copy(w.forget.addr, addr);
w.forget.vid = OCELOT_VLAN_UNAWARE_PVID;
w.forget.vid = OCELOT_STANDALONE_PVID;
w.type = OCELOT_MACT_FORGET;
return ocelot_enqueue_mact_action(ocelot, &w);
......@@ -573,7 +573,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
struct ocelot_mact_work_ctx w;
ether_addr_copy(w.learn.addr, addr);
w.learn.vid = OCELOT_VLAN_UNAWARE_PVID;
w.learn.vid = OCELOT_STANDALONE_PVID;
w.learn.pgid = PGID_CPU;
w.learn.entry_type = ENTRYTYPE_LOCKED;
w.type = OCELOT_MACT_LEARN;
......@@ -608,9 +608,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
/* Learn the new net device MAC address in the mac table. */
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data,
OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
/* Then forget the previous one. */
ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID);
ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_STANDALONE_PVID);
eth_hw_addr_set(dev, addr->sa_data);
return 0;
......@@ -662,10 +662,11 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
return ocelot_fdb_add(ocelot, port, addr, vid);
return ocelot_fdb_add(ocelot, port, addr, vid, ocelot_port->bridge);
}
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
......@@ -673,10 +674,11 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
const unsigned char *addr, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
return ocelot_fdb_del(ocelot, port, addr, vid);
return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge);
}
static int ocelot_port_fdb_dump(struct sk_buff *skb,
......@@ -988,7 +990,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
return ocelot_port_mdb_add(ocelot, port, mdb);
return ocelot_port_mdb_add(ocelot, port, mdb, ocelot_port->bridge);
}
static int ocelot_port_obj_del_mdb(struct net_device *dev,
......@@ -999,7 +1001,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
return ocelot_port_mdb_del(ocelot, port, mdb);
return ocelot_port_mdb_del(ocelot, port, mdb, ocelot_port->bridge);
}
static int ocelot_port_obj_mrp_add(struct net_device *dev,
......@@ -1173,6 +1175,33 @@ static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port)
return 0;
}
static int ocelot_bridge_num_get(struct ocelot *ocelot,
const struct net_device *bridge_dev)
{
int bridge_num = ocelot_bridge_num_find(ocelot, bridge_dev);
if (bridge_num < 0) {
/* First port that offloads this bridge */
bridge_num = find_first_zero_bit(&ocelot->bridges,
ocelot->num_phys_ports);
set_bit(bridge_num, &ocelot->bridges);
}
return bridge_num;
}
static void ocelot_bridge_num_put(struct ocelot *ocelot,
const struct net_device *bridge_dev,
int bridge_num)
{
/* Check if the bridge is still in use, otherwise it is time
* to clean it up so we can reuse this bridge_num later.
*/
if (!ocelot_bridge_num_find(ocelot, bridge_dev))
clear_bit(bridge_num, &ocelot->bridges);
}
static int ocelot_netdevice_bridge_join(struct net_device *dev,
struct net_device *brport_dev,
struct net_device *bridge,
......@@ -1182,9 +1211,14 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev,
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err;
int bridge_num, err;
bridge_num = ocelot_bridge_num_get(ocelot, bridge);
ocelot_port_bridge_join(ocelot, port, bridge);
err = ocelot_port_bridge_join(ocelot, port, bridge, bridge_num,
extack);
if (err)
goto err_join;
err = switchdev_bridge_port_offload(brport_dev, dev, priv,
&ocelot_switchdev_nb,
......@@ -1205,6 +1239,8 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev,
&ocelot_switchdev_blocking_nb);
err_switchdev_offload:
ocelot_port_bridge_leave(ocelot, port, bridge);
err_join:
ocelot_bridge_num_put(ocelot, bridge, bridge_num);
return err;
}
......@@ -1225,6 +1261,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev,
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int bridge_num = ocelot_port->bridge_num;
int port = priv->chip_port;
int err;
......@@ -1233,6 +1270,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev,
return err;
ocelot_port_bridge_leave(ocelot, port, bridge);
ocelot_bridge_num_put(ocelot, bridge, bridge_num);
return 0;
}
......@@ -1700,7 +1738,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
eth_hw_addr_gen(dev, ocelot->base_mac, port);
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr,
OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
ocelot_init_port(ocelot, port);
......
......@@ -668,6 +668,7 @@ struct ocelot_port {
u16 mrp_ring_id;
struct net_device *bridge;
int bridge_num;
u8 stp_state;
int speed;
......@@ -713,6 +714,8 @@ struct ocelot {
enum ocelot_tag_prefix npi_inj_prefix;
enum ocelot_tag_prefix npi_xtr_prefix;
unsigned long bridges;
struct list_head multicast;
struct list_head pgids;
......@@ -846,6 +849,9 @@ void ocelot_deinit(struct ocelot *ocelot);
void ocelot_init_port(struct ocelot *ocelot, int port);
void ocelot_deinit_port(struct ocelot *ocelot, int port);
void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port);
void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port);
/* DSA callbacks */
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
......@@ -863,21 +869,24 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val);
void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val);
void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge, int bridge_num,
struct netlink_ext_ack *extack);
void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_mact_flush(struct ocelot *ocelot, int port);
int ocelot_fdb_dump(struct ocelot *ocelot, int port,
dsa_fdb_dump_cb_t *cb, void *data);
int ocelot_fdb_add(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid);
int ocelot_fdb_del(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid);
int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr,
u16 vid, const struct net_device *bridge);
int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
u16 vid, const struct net_device *bridge);
int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
const unsigned char *addr, u16 vid);
const unsigned char *addr, u16 vid,
const struct net_device *bridge);
int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond,
const unsigned char *addr, u16 vid);
const unsigned char *addr, u16 vid,
const struct net_device *bridge);
int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
bool untagged, struct netlink_ext_ack *extack);
int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
......@@ -901,9 +910,11 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress);
int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_port_mdb *mdb);
const struct switchdev_obj_port_mdb *mdb,
const struct net_device *bridge);
int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
const struct switchdev_obj_port_mdb *mdb);
const struct switchdev_obj_port_mdb *mdb,
const struct net_device *bridge);
int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond,
struct netdev_lag_upper_info *info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment