lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 26 Oct 2021 01:24:13 +0300
From:   Vladimir Oltean <vladimir.oltean@....com>
To:     netdev@...r.kernel.org, Roopa Prabhu <roopa@...dia.com>,
        Nikolay Aleksandrov <nikolay@...dia.com>,
        Ido Schimmel <idosch@...dia.com>
Cc:     Jakub Kicinski <kuba@...nel.org>,
        "David S. Miller" <davem@...emloft.net>,
        Andrew Lunn <andrew@...n.ch>,
        Florian Fainelli <f.fainelli@...il.com>,
        Vivien Didelot <vivien.didelot@...il.com>,
        Vladimir Oltean <olteanv@...il.com>,
        Jiri Pirko <jiri@...dia.com>
Subject: [RFC PATCH net-next 13/15] net: bridge: wait for errors from switchdev when deleting FDB entries

Similar to the logic added for RTM_NEWNEIGH, we can make the bridge
RTM_DELNEIGH handler let switchdev veto an FDB entry deletion.

The strategy is:

- get hold of the FDB entry under &br->hash_lock
- notify switchdev of its deletion via the atomic notifier chain
- release the &br->hash_lock, wait for the response
- switchdev vetoes => error out
- switchdev agrees => attempt to get hold again of the FDB entry under
  &br->hash_lock, this time delete it without notifying switchdev _or_
  rtnetlink (we've notified both already).

Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
 net/bridge/br_fdb.c | 82 ++++++++++++++++++++++++---------------------
 1 file changed, 44 insertions(+), 38 deletions(-)

diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index ce49e5f914b1..5f9bef6e4472 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -407,7 +407,7 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
 }
 
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
-		       bool swdev_notify)
+		       bool notify_rtnl, bool swdev_notify)
 {
 	trace_fdb_delete(br, f);
 
@@ -417,7 +417,8 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
 	hlist_del_init_rcu(&f->fdb_node);
 	rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode,
 			       br_fdb_rht_params);
-	br_fdb_notify(br, f, RTM_DELNEIGH, swdev_notify);
+	if (notify_rtnl)
+		br_fdb_notify(br, f, RTM_DELNEIGH, swdev_notify);
 	call_rcu(&f->rcu, fdb_rcu_free);
 }
 
@@ -453,7 +454,7 @@ static void fdb_delete_local(struct net_bridge *br,
 		return;
 	}
 
-	fdb_delete(br, f, true);
+	fdb_delete(br, f, true, true);
 }
 
 void br_fdb_find_delete_local(struct net_bridge *br,
@@ -514,7 +515,7 @@ static int fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
 			return 0;
 		br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
 		       source ? source->dev->name : br->dev->name, addr, vid);
-		fdb_delete(br, fdb, true);
+		fdb_delete(br, fdb, true, true);
 	}
 
 	fdb = fdb_create(br, source, addr, vid,
@@ -639,7 +640,7 @@ void br_fdb_cleanup(struct work_struct *work)
 		} else {
 			spin_lock_bh(&br->hash_lock);
 			if (!hlist_unhashed(&f->fdb_node))
-				fdb_delete(br, f, true);
+				fdb_delete(br, f, true, true);
 			spin_unlock_bh(&br->hash_lock);
 		}
 	}
@@ -659,7 +660,7 @@ void br_fdb_flush(struct net_bridge *br)
 	spin_lock_bh(&br->hash_lock);
 	hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
 		if (!test_bit(BR_FDB_STATIC, &f->flags))
-			fdb_delete(br, f, true);
+			fdb_delete(br, f, true, true);
 	}
 	spin_unlock_bh(&br->hash_lock);
 }
@@ -691,7 +692,7 @@ void br_fdb_delete_by_port(struct net_bridge *br,
 		if (test_bit(BR_FDB_LOCAL, &f->flags))
 			fdb_delete_local(br, p, f);
 		else
-			fdb_delete(br, f, true);
+			fdb_delete(br, f, true, true);
 	}
 	spin_unlock_bh(&br->hash_lock);
 }
@@ -931,9 +932,10 @@ int br_fdb_get(struct sk_buff *skb,
 }
 
 /* Delete an FDB entry and don't notify switchdev */
-static void fdb_delete_by_addr_and_port(struct net_bridge *br,
-					const struct net_bridge_port *p,
-					const u8 *addr, u16 vlan)
+static int fdb_delete_by_addr_and_port(struct net_bridge *br,
+				       const struct net_bridge_port *p,
+				       const u8 *addr, u16 vlan,
+				       bool notify_rtnl)
 {
 	struct net_bridge_fdb_entry *fdb;
 
@@ -942,12 +944,14 @@ static void fdb_delete_by_addr_and_port(struct net_bridge *br,
 	fdb = br_fdb_find(br, addr, vlan);
 	if (!fdb || READ_ONCE(fdb->dst) != p) {
 		spin_unlock_bh(&br->hash_lock);
-		return;
+		return -ENOENT;
 	}
 
-	fdb_delete(br, fdb, false);
+	fdb_delete(br, fdb, notify_rtnl, false);
 
 	spin_unlock_bh(&br->hash_lock);
+
+	return 0;
 }
 
 /* returns true if the fdb is modified */
@@ -1079,7 +1083,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
 
 	err = br_switchdev_fdb_wait(&wait_ctx);
 	if (err)
-		fdb_delete_by_addr_and_port(br, source, addr, vid);
+		fdb_delete_by_addr_and_port(br, source, addr, vid, true);
 
 	return err;
 }
@@ -1208,36 +1212,38 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	return err;
 }
 
-/* Delete an FDB entry and notify switchdev.
- * Caller must hold &br->hash_lock.
- */
-static int
-fdb_delete_by_addr_and_port_switchdev(struct net_bridge *br,
-				      const struct net_bridge_port *p,
-				      const u8 *addr, u16 vlan)
+/* Delete an FDB entry and notify switchdev. */
+static int __br_fdb_delete(struct net_bridge *br,
+			   const struct net_bridge_port *p,
+			   const u8 *addr, u16 vlan,
+			   struct netlink_ext_ack *extack)
 {
+	struct br_switchdev_fdb_wait_ctx wait_ctx;
 	struct net_bridge_fdb_entry *fdb;
+	int err;
 
-	fdb = br_fdb_find(br, addr, vlan);
-	if (!fdb || READ_ONCE(fdb->dst) != p)
-		return -ENOENT;
+	br_switchdev_fdb_wait_ctx_init(&wait_ctx);
 
-	fdb_delete(br, fdb, true);
+	spin_lock_bh(&br->hash_lock);
 
-	return 0;
-}
+	fdb = br_fdb_find(br, addr, vlan);
+	if (!fdb || READ_ONCE(fdb->dst) != p) {
+		spin_unlock_bh(&br->hash_lock);
+		return -ENOENT;
+	}
 
-static int __br_fdb_delete(struct net_bridge *br,
-			   const struct net_bridge_port *p,
-			   const unsigned char *addr, u16 vid)
-{
-	int err;
+	br_fdb_notify_async(br, fdb, RTM_DELNEIGH, extack, &wait_ctx);
 
-	spin_lock_bh(&br->hash_lock);
-	err = fdb_delete_by_addr_and_port_switchdev(br, p, addr, vid);
 	spin_unlock_bh(&br->hash_lock);
 
-	return err;
+	err = br_switchdev_fdb_wait(&wait_ctx);
+	if (err)
+		return err;
+
+	/* We've notified rtnl and switchdev once, don't do it again,
+	 * just delete.
+	 */
+	return fdb_delete_by_addr_and_port(br, p, addr, vlan, false);
 }
 
 /* Remove neighbor entry with RTM_DELNEIGH */
@@ -1273,17 +1279,17 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 			return -EINVAL;
 		}
 
-		err = __br_fdb_delete(br, p, addr, vid);
+		err = __br_fdb_delete(br, p, addr, vid, extack);
 	} else {
 		err = -ENOENT;
-		err &= __br_fdb_delete(br, p, addr, 0);
+		err &= __br_fdb_delete(br, p, addr, 0, extack);
 		if (!vg || !vg->num_vlans)
 			return err;
 
 		list_for_each_entry(v, &vg->vlan_list, vlist) {
 			if (!br_vlan_should_use(v))
 				continue;
-			err &= __br_fdb_delete(br, p, addr, v->vid);
+			err &= __br_fdb_delete(br, p, addr, v->vid, extack);
 		}
 	}
 
@@ -1414,7 +1420,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
 
 	fdb = br_fdb_find(br, addr, vid);
 	if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
-		fdb_delete(br, fdb, swdev_notify);
+		fdb_delete(br, fdb, true, swdev_notify);
 	else
 		err = -ENOENT;
 
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ