Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bridge: switchdev: Allow device drivers to install locked FDB entries

When the bridge is offloaded to hardware, FDB entries are learned and
aged-out by the hardware. Some device drivers synchronize the hardware
and software FDBs by generating switchdev events towards the bridge.

When a port is locked, the hardware must not learn autonomously, as
otherwise any host will blindly gain authorization. Instead, the
hardware should generate events regarding hosts that are trying to gain
authorization and their MAC addresses should be notified by the device
driver as locked FDB entries towards the bridge driver.

Allow device drivers to notify the bridge driver about such entries by
extending the 'switchdev_notifier_fdb_info' structure with the 'locked'
bit. The bit can only be set by device drivers and not by the bridge
driver.

Prevent a locked entry from being installed if MAB is not enabled on the
bridge port.

If an entry already exists in the bridge driver, reject the locked entry
if the current entry does not have the "locked" flag set or if it points
to a different port. The same semantics are implemented in the software
data path.

Signed-off-by: Hans J. Schultz <netdev@kapio-technology.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Hans J. Schultz and committed by
Jakub Kicinski
27fabd02 9baedc3c

+28 -4
+1
include/net/switchdev.h
··· 248 248 u16 vid; 249 249 u8 added_by_user:1, 250 250 is_local:1, 251 + locked:1, 251 252 offloaded:1; 252 253 }; 253 254
+2 -1
net/bridge/br.c
··· 166 166 case SWITCHDEV_FDB_ADD_TO_BRIDGE: 167 167 fdb_info = ptr; 168 168 err = br_fdb_external_learn_add(br, p, fdb_info->addr, 169 - fdb_info->vid, false); 169 + fdb_info->vid, 170 + fdb_info->locked, false); 170 171 if (err) { 171 172 err = notifier_from_errno(err); 172 173 break;
+20 -2
net/bridge/br_fdb.c
··· 1139 1139 "FDB entry towards bridge must be permanent"); 1140 1140 return -EINVAL; 1141 1141 } 1142 - err = br_fdb_external_learn_add(br, p, addr, vid, true); 1142 + err = br_fdb_external_learn_add(br, p, addr, vid, false, true); 1143 1143 } else { 1144 1144 spin_lock_bh(&br->hash_lock); 1145 1145 err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb); ··· 1377 1377 } 1378 1378 1379 1379 int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, 1380 - const unsigned char *addr, u16 vid, 1380 + const unsigned char *addr, u16 vid, bool locked, 1381 1381 bool swdev_notify) 1382 1382 { 1383 1383 struct net_bridge_fdb_entry *fdb; ··· 1385 1385 int err = 0; 1386 1386 1387 1387 trace_br_fdb_external_learn_add(br, p, addr, vid); 1388 + 1389 + if (locked && (!p || !(p->flags & BR_PORT_MAB))) 1390 + return -EINVAL; 1388 1391 1389 1392 spin_lock_bh(&br->hash_lock); 1390 1393 ··· 1401 1398 if (!p) 1402 1399 flags |= BIT(BR_FDB_LOCAL); 1403 1400 1401 + if (locked) 1402 + flags |= BIT(BR_FDB_LOCKED); 1403 + 1404 1404 fdb = fdb_create(br, p, addr, vid, flags); 1405 1405 if (!fdb) { 1406 1406 err = -ENOMEM; ··· 1411 1405 } 1412 1406 fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); 1413 1407 } else { 1408 + if (locked && 1409 + (!test_bit(BR_FDB_LOCKED, &fdb->flags) || 1410 + READ_ONCE(fdb->dst) != p)) { 1411 + err = -EINVAL; 1412 + goto err_unlock; 1413 + } 1414 + 1414 1415 fdb->updated = jiffies; 1415 1416 1416 1417 if (READ_ONCE(fdb->dst) != p) { ··· 1431 1418 } else if (!test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags)) { 1432 1419 /* Take over SW learned entry */ 1433 1420 set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags); 1421 + modified = true; 1422 + } 1423 + 1424 + if (locked != test_bit(BR_FDB_LOCKED, &fdb->flags)) { 1425 + change_bit(BR_FDB_LOCKED, &fdb->flags); 1434 1426 modified = true; 1435 1427 } 1436 1428
+1 -1
net/bridge/br_private.h
··· 811 811 void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); 812 812 int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, 813 813 const unsigned char *addr, u16 vid, 814 - bool swdev_notify); 814 + bool locked, bool swdev_notify); 815 815 int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, 816 816 const unsigned char *addr, u16 vid, 817 817 bool swdev_notify);
+4
net/bridge/br_switchdev.c
··· 136 136 item->added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); 137 137 item->offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags); 138 138 item->is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); 139 + item->locked = false; 139 140 item->info.dev = (!p || item->is_local) ? br->dev : p->dev; 140 141 item->info.ctx = ctx; 141 142 } ··· 146 145 const struct net_bridge_fdb_entry *fdb, int type) 147 146 { 148 147 struct switchdev_notifier_fdb_info item; 148 + 149 + if (test_bit(BR_FDB_LOCKED, &fdb->flags)) 150 + return; 149 151 150 152 br_switchdev_fdb_populate(br, &item, fdb, NULL); 151 153