Browse Source

Merge branch 'bridge-fdb-dump-filter'

Jamal Hadi Salim says:

====================
bridge: fdb dumping takes a filter device

v7:
Vxlan driver was not updated with new API. Found by DaveM

v6:
Missed checkpatch > 80 chars lines found by Varka Bhadram

v5:
 Embarassing qlnic compile failure found by DaveM

v4:
 Request from DaveM to use proper comment tagging and remove if-stmnt braces

V3:
 Suggestion from Eric D. to use for_each_netdev
 Suggestion from Stephen H. to reduce level of indentation

V2:
Suggestions from Vlad
 Get rid of rcu read lock since rtnl_lock is being held
 simplify for readability
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 11 years ago
parent
commit
51b5bd199a

+ 2 - 1
drivers/net/ethernet/intel/i40e/i40e_main.c

@@ -7095,13 +7095,14 @@ static int i40e_ndo_fdb_del(struct ndmsg *ndm,
 static int i40e_ndo_fdb_dump(struct sk_buff *skb,
 static int i40e_ndo_fdb_dump(struct sk_buff *skb,
 			     struct netlink_callback *cb,
 			     struct netlink_callback *cb,
 			     struct net_device *dev,
 			     struct net_device *dev,
+			     struct net_device *filter_dev,
 			     int idx)
 			     int idx)
 {
 {
 	struct i40e_netdev_priv *np = netdev_priv(dev);
 	struct i40e_netdev_priv *np = netdev_priv(dev);
 	struct i40e_pf *pf = np->vsi->back;
 	struct i40e_pf *pf = np->vsi->back;
 
 
 	if (pf->flags & I40E_FLAG_SRIOV_ENABLED)
 	if (pf->flags & I40E_FLAG_SRIOV_ENABLED)
-		idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+		idx = ndo_dflt_fdb_dump(skb, cb, dev, filter_dev, idx);
 
 
 	return idx;
 	return idx;
 }
 }

+ 4 - 3
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c

@@ -427,16 +427,17 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 }
 }
 
 
 static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
 static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
-			struct net_device *netdev, int idx)
+			struct net_device *netdev,
+			struct net_device *filter_dev, int idx)
 {
 {
 	struct qlcnic_adapter *adapter = netdev_priv(netdev);
 	struct qlcnic_adapter *adapter = netdev_priv(netdev);
 
 
 	if (!adapter->fdb_mac_learn)
 	if (!adapter->fdb_mac_learn)
-		return ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
+		return ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx);
 
 
 	if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
 	if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
 	    qlcnic_sriov_check(adapter))
 	    qlcnic_sriov_check(adapter))
-		idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
+		idx = ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx);
 
 
 	return idx;
 	return idx;
 }
 }

+ 2 - 1
drivers/net/vxlan.c

@@ -933,7 +933,8 @@ out:
 
 
 /* Dump forwarding table */
 /* Dump forwarding table */
 static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
-			  struct net_device *dev, int idx)
+			  struct net_device *dev,
+			  struct net_device *filter_dev, int idx)
 {
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	unsigned int h;
 	unsigned int h;

+ 3 - 1
include/linux/netdevice.h

@@ -943,7 +943,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  *		      const unsigned char *addr)
  *		      const unsigned char *addr)
  *	Deletes the FDB entry from dev coresponding to addr.
  *	Deletes the FDB entry from dev coresponding to addr.
  * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
  * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
- *		       struct net_device *dev, int idx)
+ *		       struct net_device *dev, struct net_device *filter_dev,
+ *		       int idx)
  *	Used to add FDB entries to dump requests. Implementers should add
  *	Used to add FDB entries to dump requests. Implementers should add
  *	entries to skb and update idx with the number of entries.
  *	entries to skb and update idx with the number of entries.
  *
  *
@@ -1114,6 +1115,7 @@ struct net_device_ops {
 	int			(*ndo_fdb_dump)(struct sk_buff *skb,
 	int			(*ndo_fdb_dump)(struct sk_buff *skb,
 						struct netlink_callback *cb,
 						struct netlink_callback *cb,
 						struct net_device *dev,
 						struct net_device *dev,
+						struct net_device *filter_dev,
 						int idx);
 						int idx);
 
 
 	int			(*ndo_bridge_setlink)(struct net_device *dev,
 	int			(*ndo_bridge_setlink)(struct net_device *dev,

+ 1 - 0
include/linux/rtnetlink.h

@@ -78,6 +78,7 @@ extern void __rtnl_unlock(void);
 extern int ndo_dflt_fdb_dump(struct sk_buff *skb,
 extern int ndo_dflt_fdb_dump(struct sk_buff *skb,
 			     struct netlink_callback *cb,
 			     struct netlink_callback *cb,
 			     struct net_device *dev,
 			     struct net_device *dev,
+			     struct net_device *filter_dev,
 			     int idx);
 			     int idx);
 extern int ndo_dflt_fdb_add(struct ndmsg *ndm,
 extern int ndo_dflt_fdb_add(struct ndmsg *ndm,
 			    struct nlattr *tb[],
 			    struct nlattr *tb[],

+ 14 - 0
net/bridge/br_fdb.c

@@ -676,6 +676,7 @@ errout:
 int br_fdb_dump(struct sk_buff *skb,
 int br_fdb_dump(struct sk_buff *skb,
 		struct netlink_callback *cb,
 		struct netlink_callback *cb,
 		struct net_device *dev,
 		struct net_device *dev,
+		struct net_device *filter_dev,
 		int idx)
 		int idx)
 {
 {
 	struct net_bridge *br = netdev_priv(dev);
 	struct net_bridge *br = netdev_priv(dev);
@@ -691,6 +692,19 @@ int br_fdb_dump(struct sk_buff *skb,
 			if (idx < cb->args[0])
 			if (idx < cb->args[0])
 				goto skip;
 				goto skip;
 
 
+			if (filter_dev &&
+			    (!f->dst || f->dst->dev != filter_dev)) {
+				if (filter_dev != dev)
+					goto skip;
+				/* !f->dst is a speacial case for bridge
+				 * It means the MAC belongs to the bridge
+				 * Therefore need a little more filtering
+				 * we only want to dump the !f->dst case
+				 */
+				if (f->dst)
+					goto skip;
+			}
+
 			if (fdb_fill_info(skb, br, f,
 			if (fdb_fill_info(skb, br, f,
 					  NETLINK_CB(cb->skb).portid,
 					  NETLINK_CB(cb->skb).portid,
 					  cb->nlh->nlmsg_seq,
 					  cb->nlh->nlmsg_seq,

+ 1 - 1
net/bridge/br_private.h

@@ -399,7 +399,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
 	       const unsigned char *addr, u16 nlh_flags);
 	       const unsigned char *addr, u16 nlh_flags);
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
-		struct net_device *dev, int idx);
+		struct net_device *dev, struct net_device *fdev, int idx);
 int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
 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);
 void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
 
 

+ 60 - 15
net/core/rtnetlink.c

@@ -2517,6 +2517,7 @@ skip:
 int ndo_dflt_fdb_dump(struct sk_buff *skb,
 int ndo_dflt_fdb_dump(struct sk_buff *skb,
 		      struct netlink_callback *cb,
 		      struct netlink_callback *cb,
 		      struct net_device *dev,
 		      struct net_device *dev,
+		      struct net_device *filter_dev,
 		      int idx)
 		      int idx)
 {
 {
 	int err;
 	int err;
@@ -2534,28 +2535,72 @@ EXPORT_SYMBOL(ndo_dflt_fdb_dump);
 
 
 static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
 {
-	int idx = 0;
-	struct net *net = sock_net(skb->sk);
 	struct net_device *dev;
 	struct net_device *dev;
+	struct nlattr *tb[IFLA_MAX+1];
+	struct net_device *bdev = NULL;
+	struct net_device *br_dev = NULL;
+	const struct net_device_ops *ops = NULL;
+	const struct net_device_ops *cops = NULL;
+	struct ifinfomsg *ifm = nlmsg_data(cb->nlh);
+	struct net *net = sock_net(skb->sk);
+	int brport_idx = 0;
+	int br_idx = 0;
+	int idx = 0;
 
 
-	rcu_read_lock();
-	for_each_netdev_rcu(net, dev) {
-		if (dev->priv_flags & IFF_BRIDGE_PORT) {
-			struct net_device *br_dev;
-			const struct net_device_ops *ops;
+	if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
+			ifla_policy) == 0) {
+		if (tb[IFLA_MASTER])
+			br_idx = nla_get_u32(tb[IFLA_MASTER]);
+	}
+
+	brport_idx = ifm->ifi_index;
+
+	if (br_idx) {
+		br_dev = __dev_get_by_index(net, br_idx);
+		if (!br_dev)
+			return -ENODEV;
+
+		ops = br_dev->netdev_ops;
+		bdev = br_dev;
+	}
+
+	for_each_netdev(net, dev) {
+		if (brport_idx && (dev->ifindex != brport_idx))
+			continue;
+
+		if (!br_idx) { /* user did not specify a specific bridge */
+			if (dev->priv_flags & IFF_BRIDGE_PORT) {
+				br_dev = netdev_master_upper_dev_get(dev);
+				cops = br_dev->netdev_ops;
+			}
 
 
-			br_dev = netdev_master_upper_dev_get(dev);
-			ops = br_dev->netdev_ops;
-			if (ops->ndo_fdb_dump)
-				idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
+			bdev = dev;
+		} else {
+			if (dev != br_dev &&
+			    !(dev->priv_flags & IFF_BRIDGE_PORT))
+				continue;
+
+			if (br_dev != netdev_master_upper_dev_get(dev) &&
+			    !(dev->priv_flags & IFF_EBRIDGE))
+				continue;
+
+			bdev = br_dev;
+			cops = ops;
+		}
+
+		if (dev->priv_flags & IFF_BRIDGE_PORT) {
+			if (cops && cops->ndo_fdb_dump)
+				idx = cops->ndo_fdb_dump(skb, cb, br_dev, dev,
+							 idx);
 		}
 		}
 
 
+		idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
 		if (dev->netdev_ops->ndo_fdb_dump)
 		if (dev->netdev_ops->ndo_fdb_dump)
-			idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
-		else
-			idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+			idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, bdev, dev,
+							    idx);
+
+		cops = NULL;
 	}
 	}
-	rcu_read_unlock();
 
 
 	cb->args[0] = idx;
 	cb->args[0] = idx;
 	return skb->len;
 	return skb->len;