diff mbox

[RFC] net/bridge: port based vlan filtering for bridges

Message ID 20120411151002.GA17739@kvack.org
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Benjamin LaHaise April 11, 2012, 3:10 p.m. UTC
Hello folks,

Attached is the first stab at a patch to make it possible to filter packets 
received from other bridge ports based on the port number.  This can be used 
to emulate port based VLANs that some switches support.

The justification for this is a bit interesting.  Initially, I had been 
filtering packets using firewall rules.  Unfortunately, the number of 
filter rules becomes impossible to manage when trying to filter traffic 
between 100 different ports.  CPU overhead of the filters is also a major 
problem.

The particular use-case I'm dealing with is simulating wireless networks 
on a system using LXC containers.  Each guest has a veth device that is a 
member of the bridge, but the topology of which nodes can "hear" each other 
changes at runtime.

Comments/thoughts?

		-ben
---
 br_forward.c  |    3 +++
 br_if.c       |    3 +--
 br_private.h  |    4 ++++
 br_sysfs_if.c |   20 ++++++++++++++++++++
 4 files changed, 28 insertions(+), 2 deletions(-)

Comments

stephen hemminger April 11, 2012, 3:30 p.m. UTC | #1
On Wed, 11 Apr 2012 11:10:02 -0400
Benjamin LaHaise <bcrl@kvack.org> wrote:

> Hello folks,
> 
> Attached is the first stab at a patch to make it possible to filter packets 
> received from other bridge ports based on the port number.  This can be used 
> to emulate port based VLANs that some switches support.
> 
> The justification for this is a bit interesting.  Initially, I had been 
> filtering packets using firewall rules.  Unfortunately, the number of 
> filter rules becomes impossible to manage when trying to filter traffic 
> between 100 different ports.  CPU overhead of the filters is also a major 
> problem.
> 
> The particular use-case I'm dealing with is simulating wireless networks 
> on a system using LXC containers.  Each guest has a veth device that is a 
> member of the bridge, but the topology of which nodes can "hear" each other 
> changes at runtime.
> 
> Comments/thoughts?
> 

Nak. If firewall doesn't work then implement a better netfilter
module.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Benjamin LaHaise April 11, 2012, 3:36 p.m. UTC | #2
On Wed, Apr 11, 2012 at 08:30:52AM -0700, Stephen Hemminger wrote:
> On Wed, 11 Apr 2012 11:10:02 -0400
> Benjamin LaHaise <bcrl@kvack.org> wrote:
> 
> > Hello folks,
> > 
> > Attached is the first stab at a patch to make it possible to filter packets 
> > received from other bridge ports based on the port number.  This can be used 
> > to emulate port based VLANs that some switches support.
> > 
> > The justification for this is a bit interesting.  Initially, I had been 
> > filtering packets using firewall rules.  Unfortunately, the number of 
> > filter rules becomes impossible to manage when trying to filter traffic 
> > between 100 different ports.  CPU overhead of the filters is also a major 
> > problem.
> > 
> > The particular use-case I'm dealing with is simulating wireless networks 
> > on a system using LXC containers.  Each guest has a veth device that is a 
> > member of the bridge, but the topology of which nodes can "hear" each other 
> > changes at runtime.
> > 
> > Comments/thoughts?
> > 
> 
> Nak. If firewall doesn't work then implement a better netfilter
> module.

That still results in the CPU overhead of packet duplication for each and 
every bridge port regardless of the port receiving the packet or not.  
Hmmmm, would a NF_HOOK in should_deliver be okay?

		-ben
stephen hemminger April 11, 2012, 3:38 p.m. UTC | #3
On Wed, 11 Apr 2012 11:36:29 -0400
Benjamin LaHaise <bcrl@kvack.org> wrote:

> On Wed, Apr 11, 2012 at 08:30:52AM -0700, Stephen Hemminger wrote:
> > On Wed, 11 Apr 2012 11:10:02 -0400
> > Benjamin LaHaise <bcrl@kvack.org> wrote:
> > 
> > > Hello folks,
> > > 
> > > Attached is the first stab at a patch to make it possible to filter packets 
> > > received from other bridge ports based on the port number.  This can be used 
> > > to emulate port based VLANs that some switches support.
> > > 
> > > The justification for this is a bit interesting.  Initially, I had been 
> > > filtering packets using firewall rules.  Unfortunately, the number of 
> > > filter rules becomes impossible to manage when trying to filter traffic 
> > > between 100 different ports.  CPU overhead of the filters is also a major 
> > > problem.
> > > 
> > > The particular use-case I'm dealing with is simulating wireless networks 
> > > on a system using LXC containers.  Each guest has a veth device that is a 
> > > member of the bridge, but the topology of which nodes can "hear" each other 
> > > changes at runtime.
> > > 
> > > Comments/thoughts?
> > > 
> > 
> > Nak. If firewall doesn't work then implement a better netfilter
> > module.
> 
> That still results in the CPU overhead of packet duplication for each and 
> every bridge port regardless of the port receiving the packet or not.  
> Hmmmm, would a NF_HOOK in should_deliver be okay?

Sure. Having better way to do policy would be great. Just don't want
to have implementations of specific policies in generic code.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index ee64287..9b106f8 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -30,6 +30,9 @@  static int deliver_clone(const struct net_bridge_port *prev,
 static inline int should_deliver(const struct net_bridge_port *p,
 				 const struct sk_buff *skb)
 {
+	struct net_bridge_port *from = br_port_get_rcu(skb->dev);
+	if (from && test_bit(from->port_no, p->filter_ports))
+		return 0;
 	return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
 		p->state == BR_STATE_FORWARDING);
 }
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f603e5b..2f2e595 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -183,8 +183,7 @@  static int find_portno(struct net_bridge *br)
 	struct net_bridge_port *p;
 	unsigned long *inuse;
 
-	inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long),
-			GFP_KERNEL);
+	inuse = kcalloc(BR_PORT_LONGS, sizeof(unsigned long), GFP_KERNEL);
 	if (!inuse)
 		return -ENOMEM;
 
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d7d6fb0..c6fbab0 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -17,6 +17,7 @@ 
 #include <linux/if_bridge.h>
 #include <linux/netpoll.h>
 #include <linux/u64_stats_sync.h>
+#include <linux/bitops.h>
 #include <net/route.h>
 
 #define BR_HASH_BITS 8
@@ -26,6 +27,7 @@ 
 
 #define BR_PORT_BITS	10
 #define BR_MAX_PORTS	(1<<BR_PORT_BITS)
+#define BR_PORT_LONGS	BITS_TO_LONGS(BR_MAX_PORTS)
 
 #define BR_VERSION	"2.3"
 
@@ -156,6 +158,8 @@  struct net_bridge_port
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	struct netpoll			*np;
 #endif
+
+	unsigned long			filter_ports[BR_PORT_LONGS];
 };
 
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 6229b62..9d95f6a 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -164,6 +164,24 @@  static BRPORT_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router,
 		   store_multicast_router);
 #endif
 
+static int store_add_filter_port(struct net_bridge_port *p, unsigned long v)
+{
+	if (v >= BR_MAX_PORTS)
+		return -EINVAL;
+	set_bit(v, p->filter_ports);
+	return 0;
+}
+static BRPORT_ATTR(add_filter_port, S_IWUSR, NULL, store_add_filter_port);
+
+static int store_remove_filter_port(struct net_bridge_port *p, unsigned long v)
+{
+	if (v >= BR_MAX_PORTS)
+		return -EINVAL;
+	clear_bit(v, p->filter_ports);
+	return 0;
+}
+static BRPORT_ATTR(remove_filter_port, S_IWUSR, NULL, store_remove_filter_port);
+
 static struct brport_attribute *brport_attrs[] = {
 	&brport_attr_path_cost,
 	&brport_attr_priority,
@@ -184,6 +202,8 @@  static struct brport_attribute *brport_attrs[] = {
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 	&brport_attr_multicast_router,
 #endif
+	&brport_attr_add_filter_port,
+	&brport_attr_remove_filter_port,
 	NULL
 };