diff mbox

[v7,net-next,01/12] bridge: Add vlan filtering infrastructure

Message ID 1359601979-14942-2-git-send-email-vyasevic@redhat.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Vlad Yasevich Jan. 31, 2013, 3:12 a.m. UTC
Adds an optional infrustructure component to bridge that would allow
native vlan filtering in the bridge.  Each bridge port (as well
as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
is associated with a vlan id.  This way if the bit corresponding to
the vid is set in the bitmap that the packet with vid is allowed to
enter and exit the port.

Write access the bitmap is protected by RTNL and read access
protected by RCU.

Vlan functionality is disabled by default.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/bridge/Kconfig       |   14 +++
 net/bridge/Makefile      |    2 +
 net/bridge/br_if.c       |    1 +
 net/bridge/br_private.h  |   55 +++++++++++
 net/bridge/br_sysfs_br.c |   21 ++++
 net/bridge/br_vlan.c     |  234 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 327 insertions(+), 0 deletions(-)
 create mode 100644 net/bridge/br_vlan.c

Comments

=?ISO-8859-2?Q?Micha=B3_Miros=B3aw?= Jan. 31, 2013, 7:57 p.m. UTC | #1
2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
> Adds an optional infrustructure component to bridge that would allow
> native vlan filtering in the bridge.  Each bridge port (as well
> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
> is associated with a vlan id.  This way if the bit corresponding to
> the vid is set in the bitmap that the packet with vid is allowed to
> enter and exit the port.
>
> Write access the bitmap is protected by RTNL and read access
> protected by RCU.
[...]
> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
> +{
> +       unsigned long first_bit;
> +       unsigned long last_bit;
> +
> +       if (!test_bit(vid, v->vlan_bitmap))
> +               return -EINVAL;
> +
> +       /* Check to see if any other vlans are in this table.  If this
> +        * is the last vlan, delete the whole structure.  If this is not the
> +        * last vlan, just clear the bit.
> +        */
> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
> +
> +       if (v->port_idx && vid) {
> +               struct net_device *dev = vlans_to_port(v)->dev;
> +
> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
> +       }
> +
> +       clear_bit(vid, v->vlan_bitmap);
> +       if (first_bit == last_bit) {

if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))

> +               if (v->port_idx) {
> +                       struct net_bridge_port *p = vlans_to_port(v);
> +                       rcu_assign_pointer(p->vlan_info, NULL);
> +               } else {
> +                       struct net_bridge *br = vlans_to_bridge(v);
> +                       rcu_assign_pointer(br->vlan_info, NULL);
> +               }

You seem to use vlans_to_port/vlans_to_bridge only to get at
vlan_info. Maybe that could be abstracted to a single interface, or
even change v->parent to be a 'net_port_vlans **'?

Best Regards,
Michał Mirosław
--
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
Vlad Yasevich Jan. 31, 2013, 8:13 p.m. UTC | #2
On 01/31/2013 02:57 PM, Michał Mirosław wrote:
> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>> Adds an optional infrustructure component to bridge that would allow
>> native vlan filtering in the bridge.  Each bridge port (as well
>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>> is associated with a vlan id.  This way if the bit corresponding to
>> the vid is set in the bitmap that the packet with vid is allowed to
>> enter and exit the port.
>>
>> Write access the bitmap is protected by RTNL and read access
>> protected by RCU.
> [...]
>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>> +{
>> +       unsigned long first_bit;
>> +       unsigned long last_bit;
>> +
>> +       if (!test_bit(vid, v->vlan_bitmap))
>> +               return -EINVAL;
>> +
>> +       /* Check to see if any other vlans are in this table.  If this
>> +        * is the last vlan, delete the whole structure.  If this is not the
>> +        * last vlan, just clear the bit.
>> +        */
>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +
>> +       if (v->port_idx && vid) {
>> +               struct net_device *dev = vlans_to_port(v)->dev;
>> +
>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
>> +       }
>> +
>> +       clear_bit(vid, v->vlan_bitmap);
>> +       if (first_bit == last_bit) {
>
> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))

Yeah.  I didn't have the clear_bit about before, but with it 
bitmap_empty() is much better.

>
>> +               if (v->port_idx) {
>> +                       struct net_bridge_port *p = vlans_to_port(v);
>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>> +               } else {
>> +                       struct net_bridge *br = vlans_to_bridge(v);
>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>> +               }
>
> You seem to use vlans_to_port/vlans_to_bridge only to get at
> vlan_info. Maybe that could be abstracted to a single interface, or
> even change v->parent to be a 'net_port_vlans **'?

Hmm..  net_port_vlan** has appeal.  I'll see if I can make it work.

Thanks
-vlad
>
> Best Regards,
> Michał Mirosław
>

--
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
Simon Barber Jan. 31, 2013, 8:33 p.m. UTC | #3
I wrote a similar patch a few years ago:

https://lists.linux-foundation.org/pipermail/bridge/2006-September/005046.html

This patch also added the possibility to define a native vlan for each 
port and for the bridge port itself - is there any interest in this 
feature as well as the filtering?

Simon


On 01/31/2013 11:57 AM, Michał Mirosław wrote:
> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>> Adds an optional infrustructure component to bridge that would allow
>> native vlan filtering in the bridge.  Each bridge port (as well
>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>> is associated with a vlan id.  This way if the bit corresponding to
>> the vid is set in the bitmap that the packet with vid is allowed to
>> enter and exit the port.
>>
>> Write access the bitmap is protected by RTNL and read access
>> protected by RCU.
> [...]
>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>> +{
>> +       unsigned long first_bit;
>> +       unsigned long last_bit;
>> +
>> +       if (!test_bit(vid, v->vlan_bitmap))
>> +               return -EINVAL;
>> +
>> +       /* Check to see if any other vlans are in this table.  If this
>> +        * is the last vlan, delete the whole structure.  If this is not the
>> +        * last vlan, just clear the bit.
>> +        */
>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>> +
>> +       if (v->port_idx && vid) {
>> +               struct net_device *dev = vlans_to_port(v)->dev;
>> +
>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
>> +       }
>> +
>> +       clear_bit(vid, v->vlan_bitmap);
>> +       if (first_bit == last_bit) {
>
> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))
>
>> +               if (v->port_idx) {
>> +                       struct net_bridge_port *p = vlans_to_port(v);
>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>> +               } else {
>> +                       struct net_bridge *br = vlans_to_bridge(v);
>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>> +               }
>
> You seem to use vlans_to_port/vlans_to_bridge only to get at
> vlan_info. Maybe that could be abstracted to a single interface, or
> even change v->parent to be a 'net_port_vlans **'?
>
> Best Regards,
> Michał Mirosław
>
--
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
Vlad Yasevich Jan. 31, 2013, 8:34 p.m. UTC | #4
On 01/31/2013 03:33 PM, Simon Barber wrote:
> I wrote a similar patch a few years ago:
>
> https://lists.linux-foundation.org/pipermail/bridge/2006-September/005046.html
>
>
> This patch also added the possibility to define a native vlan for each
> port and for the bridge port itself - is there any interest in this
> feature as well as the filtering?
>

See patch 5 and 6 and the series :)

-vlad

> Simon
>
>
> On 01/31/2013 11:57 AM, Michał Mirosław wrote:
>> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>>> Adds an optional infrustructure component to bridge that would allow
>>> native vlan filtering in the bridge.  Each bridge port (as well
>>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>>> is associated with a vlan id.  This way if the bit corresponding to
>>> the vid is set in the bitmap that the packet with vid is allowed to
>>> enter and exit the port.
>>>
>>> Write access the bitmap is protected by RTNL and read access
>>> protected by RCU.
>> [...]
>>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>>> +{
>>> +       unsigned long first_bit;
>>> +       unsigned long last_bit;
>>> +
>>> +       if (!test_bit(vid, v->vlan_bitmap))
>>> +               return -EINVAL;
>>> +
>>> +       /* Check to see if any other vlans are in this table.  If this
>>> +        * is the last vlan, delete the whole structure.  If this is
>>> not the
>>> +        * last vlan, just clear the bit.
>>> +        */
>>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>> +
>>> +       if (v->port_idx && vid) {
>>> +               struct net_device *dev = vlans_to_port(v)->dev;
>>> +
>>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
>>> +       }
>>> +
>>> +       clear_bit(vid, v->vlan_bitmap);
>>> +       if (first_bit == last_bit) {
>>
>> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))
>>
>>> +               if (v->port_idx) {
>>> +                       struct net_bridge_port *p = vlans_to_port(v);
>>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>>> +               } else {
>>> +                       struct net_bridge *br = vlans_to_bridge(v);
>>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>>> +               }
>>
>> You seem to use vlans_to_port/vlans_to_bridge only to get at
>> vlan_info. Maybe that could be abstracted to a single interface, or
>> even change v->parent to be a 'net_port_vlans **'?
>>
>> Best Regards,
>> Michał Mirosław
>>

--
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
Simon Barber Jan. 31, 2013, 9:46 p.m. UTC | #5
Great - I look forward to seeing this functionality in the kernel - this 
will be very useful. Are you releasing brctl patches too?

Simon

On 01/31/2013 12:34 PM, Vlad Yasevich wrote:
> On 01/31/2013 03:33 PM, Simon Barber wrote:
>> I wrote a similar patch a few years ago:
>>
>> https://lists.linux-foundation.org/pipermail/bridge/2006-September/005046.html
>>
>>
>>
>> This patch also added the possibility to define a native vlan for each
>> port and for the bridge port itself - is there any interest in this
>> feature as well as the filtering?
>>
>
> See patch 5 and 6 and the series :)
>
> -vlad
>
>> Simon
>>
>>
>> On 01/31/2013 11:57 AM, Michał Mirosław wrote:
>>> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>>>> Adds an optional infrustructure component to bridge that would allow
>>>> native vlan filtering in the bridge.  Each bridge port (as well
>>>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>>>> is associated with a vlan id.  This way if the bit corresponding to
>>>> the vid is set in the bitmap that the packet with vid is allowed to
>>>> enter and exit the port.
>>>>
>>>> Write access the bitmap is protected by RTNL and read access
>>>> protected by RCU.
>>> [...]
>>>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>>>> +{
>>>> +       unsigned long first_bit;
>>>> +       unsigned long last_bit;
>>>> +
>>>> +       if (!test_bit(vid, v->vlan_bitmap))
>>>> +               return -EINVAL;
>>>> +
>>>> +       /* Check to see if any other vlans are in this table.  If this
>>>> +        * is the last vlan, delete the whole structure.  If this is
>>>> not the
>>>> +        * last vlan, just clear the bit.
>>>> +        */
>>>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>>> +
>>>> +       if (v->port_idx && vid) {
>>>> +               struct net_device *dev = vlans_to_port(v)->dev;
>>>> +
>>>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>>>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev,
>>>> vid);
>>>> +       }
>>>> +
>>>> +       clear_bit(vid, v->vlan_bitmap);
>>>> +       if (first_bit == last_bit) {
>>>
>>> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))
>>>
>>>> +               if (v->port_idx) {
>>>> +                       struct net_bridge_port *p = vlans_to_port(v);
>>>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>>>> +               } else {
>>>> +                       struct net_bridge *br = vlans_to_bridge(v);
>>>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>>>> +               }
>>>
>>> You seem to use vlans_to_port/vlans_to_bridge only to get at
>>> vlan_info. Maybe that could be abstracted to a single interface, or
>>> even change v->parent to be a 'net_port_vlans **'?
>>>
>>> Best Regards,
>>> Michał Mirosław
>>>
>
--
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
Vlad Yasevich Jan. 31, 2013, 9:54 p.m. UTC | #6
On 01/31/2013 04:46 PM, Simon Barber wrote:
> Great - I look forward to seeing this functionality in the kernel - this
> will be very useful. Are you releasing brctl patches too?

ioctl interface is deprecated so there will be no brctl patches.  These 
patches use a netlink interface and I will post patches to the iproute2 
bridge tool to configure this.

-vlad

>
> Simon
>
> On 01/31/2013 12:34 PM, Vlad Yasevich wrote:
>> On 01/31/2013 03:33 PM, Simon Barber wrote:
>>> I wrote a similar patch a few years ago:
>>>
>>> https://lists.linux-foundation.org/pipermail/bridge/2006-September/005046.html
>>>
>>>
>>>
>>>
>>> This patch also added the possibility to define a native vlan for each
>>> port and for the bridge port itself - is there any interest in this
>>> feature as well as the filtering?
>>>
>>
>> See patch 5 and 6 and the series :)
>>
>> -vlad
>>
>>> Simon
>>>
>>>
>>> On 01/31/2013 11:57 AM, Michał Mirosław wrote:
>>>> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>>>>> Adds an optional infrustructure component to bridge that would allow
>>>>> native vlan filtering in the bridge.  Each bridge port (as well
>>>>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>>>>> is associated with a vlan id.  This way if the bit corresponding to
>>>>> the vid is set in the bitmap that the packet with vid is allowed to
>>>>> enter and exit the port.
>>>>>
>>>>> Write access the bitmap is protected by RTNL and read access
>>>>> protected by RCU.
>>>> [...]
>>>>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>>>>> +{
>>>>> +       unsigned long first_bit;
>>>>> +       unsigned long last_bit;
>>>>> +
>>>>> +       if (!test_bit(vid, v->vlan_bitmap))
>>>>> +               return -EINVAL;
>>>>> +
>>>>> +       /* Check to see if any other vlans are in this table.  If this
>>>>> +        * is the last vlan, delete the whole structure.  If this is
>>>>> not the
>>>>> +        * last vlan, just clear the bit.
>>>>> +        */
>>>>> +       first_bit = find_first_bit(v->vlan_bitmap,
>>>>> BR_VLAN_BITMAP_LEN);
>>>>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>>>> +
>>>>> +       if (v->port_idx && vid) {
>>>>> +               struct net_device *dev = vlans_to_port(v)->dev;
>>>>> +
>>>>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>>>>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev,
>>>>> vid);
>>>>> +       }
>>>>> +
>>>>> +       clear_bit(vid, v->vlan_bitmap);
>>>>> +       if (first_bit == last_bit) {
>>>>
>>>> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))
>>>>
>>>>> +               if (v->port_idx) {
>>>>> +                       struct net_bridge_port *p = vlans_to_port(v);
>>>>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>>>>> +               } else {
>>>>> +                       struct net_bridge *br = vlans_to_bridge(v);
>>>>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>>>>> +               }
>>>>
>>>> You seem to use vlans_to_port/vlans_to_bridge only to get at
>>>> vlan_info. Maybe that could be abstracted to a single interface, or
>>>> even change v->parent to be a 'net_port_vlans **'?
>>>>
>>>> Best Regards,
>>>> Michał Mirosław
>>>>
>>

--
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
Vlad Yasevich Feb. 1, 2013, 2:50 a.m. UTC | #7
On 01/31/2013 03:13 PM, Vlad Yasevich wrote:
> On 01/31/2013 02:57 PM, Michał Mirosław wrote:
>> 2013/1/31 Vlad Yasevich <vyasevic@redhat.com>:
>>> Adds an optional infrustructure component to bridge that would allow
>>> native vlan filtering in the bridge.  Each bridge port (as well
>>> as the bridge device) now get a VLAN bitmap.  Each bit in the bitmap
>>> is associated with a vlan id.  This way if the bit corresponding to
>>> the vid is set in the bitmap that the packet with vid is allowed to
>>> enter and exit the port.
>>>
>>> Write access the bitmap is protected by RTNL and read access
>>> protected by RCU.
>> [...]
>>> +static int __vlan_del(struct net_port_vlans *v, u16 vid)
>>> +{
>>> +       unsigned long first_bit;
>>> +       unsigned long last_bit;
>>> +
>>> +       if (!test_bit(vid, v->vlan_bitmap))
>>> +               return -EINVAL;
>>> +
>>> +       /* Check to see if any other vlans are in this table.  If this
>>> +        * is the last vlan, delete the whole structure.  If this is
>>> not the
>>> +        * last vlan, just clear the bit.
>>> +        */
>>> +       first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>> +       last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
>>> +
>>> +       if (v->port_idx && vid) {
>>> +               struct net_device *dev = vlans_to_port(v)->dev;
>>> +
>>> +               if (dev->features & NETIF_F_HW_VLAN_FILTER)
>>> +                       dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
>>> +       }
>>> +
>>> +       clear_bit(vid, v->vlan_bitmap);
>>> +       if (first_bit == last_bit) {
>>
>> if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN))
>
> Yeah.  I didn't have the clear_bit about before, but with it
> bitmap_empty() is much better.
>
>>
>>> +               if (v->port_idx) {
>>> +                       struct net_bridge_port *p = vlans_to_port(v);
>>> +                       rcu_assign_pointer(p->vlan_info, NULL);
>>> +               } else {
>>> +                       struct net_bridge *br = vlans_to_bridge(v);
>>> +                       rcu_assign_pointer(br->vlan_info, NULL);
>>> +               }
>>
>> You seem to use vlans_to_port/vlans_to_bridge only to get at
>> vlan_info. Maybe that could be abstracted to a single interface, or
>> even change v->parent to be a 'net_port_vlans **'?
>
> Hmm..  net_port_vlan** has appeal.  I'll see if I can make it work.

So, I went about rewriting this only to realize that there is a bug in
patch 10 and I need the conversion functions to fix it.
I can't really abstract it to a single interface without adding the 
whole nbp/port layer on top of the bridge device and that's overkill. 
Changing to net_port_vlans** doesn't buy me anything other then obfuscation.

So I think I am going to keep the much simpler parent pointer and 
conversion functions since I will need them later.

Thanks
-vlad
>
> Thanks
> -vlad
>>
>> Best Regards,
>> Michał Mirosław
>>
>

--
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/Kconfig b/net/bridge/Kconfig
index 6dee7bf..aa0d3b2 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -46,3 +46,17 @@  config BRIDGE_IGMP_SNOOPING
 	  Say N to exclude this support and reduce the binary size.
 
 	  If unsure, say Y.
+
+config BRIDGE_VLAN_FILTERING
+	bool "VLAN filtering"
+	depends on BRIDGE
+	depends on VLAN_8021Q
+	default n
+	---help---
+	  If you say Y here, then the Ethernet bridge will be able selectively
+	  receive and forward traffic based on VLAN information in the packet
+	  any VLAN information configured on the bridge port or bridge device.
+
+	  Say N to exclude this support and reduce the binary size.
+
+	  If unsure, say Y.
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index e859098..e85498b2f 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -14,4 +14,6 @@  bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
 
 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
 
+bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
+
 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 2148d47..af9d65a 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -139,6 +139,7 @@  static void del_nbp(struct net_bridge_port *p)
 
 	br_ifinfo_notify(RTM_DELLINK, p);
 
+	nbp_vlan_flush(p);
 	br_fdb_delete_by_port(br, p, 1);
 
 	list_del_rcu(&p->list);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 711094a..1c1b2f1 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -18,6 +18,7 @@ 
 #include <linux/netpoll.h>
 #include <linux/u64_stats_sync.h>
 #include <net/route.h>
+#include <linux/if_vlan.h>
 
 #define BR_HASH_BITS 8
 #define BR_HASH_SIZE (1 << BR_HASH_BITS)
@@ -26,6 +27,8 @@ 
 
 #define BR_PORT_BITS	10
 #define BR_MAX_PORTS	(1<<BR_PORT_BITS)
+#define BR_VLAN_BITMAP_LEN	BITS_TO_LONGS(VLAN_N_VID)
+#define BR_INVALID_VID	(1<<15)
 
 #define BR_VERSION	"2.3"
 
@@ -63,6 +66,13 @@  struct br_ip
 	__be16		proto;
 };
 
+struct net_port_vlans {
+	u16				port_idx;
+	void				*parent;
+	struct rcu_head			rcu;
+	unsigned long			vlan_bitmap[BR_VLAN_BITMAP_LEN];
+};
+
 struct net_bridge_fdb_entry
 {
 	struct hlist_node		hlist;
@@ -156,6 +166,7 @@  struct net_bridge_port
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	struct netpoll			*np;
 #endif
+	struct net_port_vlans __rcu	*vlan_info;
 };
 
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
@@ -260,6 +271,10 @@  struct net_bridge
 	struct timer_list		topology_change_timer;
 	struct timer_list		gc_timer;
 	struct kobject			*ifobj;
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+	u8				vlan_enabled;
+	struct net_port_vlans __rcu	*vlan_info;
+#endif
 };
 
 struct br_input_skb_cb {
@@ -534,6 +549,46 @@  static inline void br_mdb_uninit(void)
 }
 #endif
 
+/* br_vlan.c */
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+extern int br_vlan_add(struct net_bridge *br, u16 vid);
+extern int br_vlan_delete(struct net_bridge *br, u16 vid);
+extern void br_vlan_flush(struct net_bridge *br);
+extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
+extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
+extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
+extern void nbp_vlan_flush(struct net_bridge_port *port);
+#else
+static inline int br_vlan_add(struct net_bridge *br, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int br_vlan_delete(struct net_bridge *br, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void br_vlan_flush(struct net_bridge *br)
+{
+}
+
+static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void nbp_vlan_flush(struct net_bridge_port *port)
+{
+}
+
+#endif
+
 /* br_netfilter.c */
 #ifdef CONFIG_BRIDGE_NETFILTER
 extern int br_netfilter_init(void);
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5913a3a..8baa9c0 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -691,6 +691,24 @@  static ssize_t store_nf_call_arptables(
 static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR,
 		   show_nf_call_arptables, store_nf_call_arptables);
 #endif
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+static ssize_t show_vlan_filtering(struct device *d,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct net_bridge *br = to_bridge(d);
+	return sprintf(buf, "%d\n", br->vlan_enabled);
+}
+
+static ssize_t store_vlan_filtering(struct device *d,
+				    struct device_attribute *attr,
+				    const char *buf, size_t len)
+{
+	return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
+}
+static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR,
+		   show_vlan_filtering, store_vlan_filtering);
+#endif
 
 static struct attribute *bridge_attrs[] = {
 	&dev_attr_forward_delay.attr,
@@ -732,6 +750,9 @@  static struct attribute *bridge_attrs[] = {
 	&dev_attr_nf_call_ip6tables.attr,
 	&dev_attr_nf_call_arptables.attr,
 #endif
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+	&dev_attr_vlan_filtering.attr,
+#endif
 	NULL
 };
 
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
new file mode 100644
index 0000000..7b6e0dd
--- /dev/null
+++ b/net/bridge/br_vlan.c
@@ -0,0 +1,234 @@ 
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+
+#include "br_private.h"
+
+static inline struct net_bridge_port *vlans_to_port(struct net_port_vlans *vlans)
+{
+	struct net_bridge_port *p = NULL;
+
+	if (vlans->port_idx)
+		p = (struct net_bridge_port *) vlans->parent;
+
+	return p;
+}
+
+static inline struct net_bridge *vlans_to_bridge(struct net_port_vlans *vlans)
+{
+	struct net_bridge *br;
+
+	if (!vlans->port_idx)
+		br = (struct net_bridge *)vlans->parent;
+	else
+		br = vlans_to_port(vlans)->br;
+
+	return br;
+}
+
+static int __vlan_add(struct net_port_vlans *v, u16 vid)
+{
+	int err;
+
+	if (test_bit(vid, v->vlan_bitmap))
+		return -EEXIST;
+
+	if (v->port_idx && vid) {
+		struct net_device *dev = vlans_to_port(v)->dev;
+
+		/* Add VLAN to the device filter if it is supported.
+		 * Stricly speaking, this is not necessary now, since devices
+		 * are made promiscuous by the bridge, but if that ever changes
+		 * this code will allow tagged traffic to enter the bridge.
+		 */
+		if (dev->features & NETIF_F_HW_VLAN_FILTER) {
+			err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
+			if (err)
+				return err;
+		}
+	}
+
+	set_bit(vid, v->vlan_bitmap);
+	return 0;
+}
+
+static int __vlan_del(struct net_port_vlans *v, u16 vid)
+{
+	unsigned long first_bit;
+	unsigned long last_bit;
+
+	if (!test_bit(vid, v->vlan_bitmap))
+		return -EINVAL;
+
+	/* Check to see if any other vlans are in this table.  If this
+	 * is the last vlan, delete the whole structure.  If this is not the
+	 * last vlan, just clear the bit.
+	 */
+	first_bit = find_first_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+	last_bit = find_last_bit(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+
+	if (v->port_idx && vid) {
+		struct net_device *dev = vlans_to_port(v)->dev;
+		
+		if (dev->features & NETIF_F_HW_VLAN_FILTER)
+			dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
+	}
+
+	clear_bit(vid, v->vlan_bitmap);
+	if (first_bit == last_bit) {
+		if (v->port_idx) {
+			struct net_bridge_port *p = vlans_to_port(v);
+			rcu_assign_pointer(p->vlan_info, NULL);
+		} else {
+			struct net_bridge *br = vlans_to_bridge(v);
+			rcu_assign_pointer(br->vlan_info, NULL);
+		}
+		kfree_rcu(v, rcu);
+	}
+	return 0;
+}
+
+static void __vlan_flush(struct net_port_vlans *v)
+{
+	bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+	if (v->port_idx)
+		rcu_assign_pointer(vlans_to_port(v)->vlan_info, NULL);
+	else
+		rcu_assign_pointer(vlans_to_bridge(v)->vlan_info, NULL);
+	kfree_rcu(v, rcu);
+}
+
+/* Must be protected by RTNL */
+int br_vlan_add(struct net_bridge *br, u16 vid)
+{
+	struct net_port_vlans *pv = NULL;
+	int err;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (pv)
+		return __vlan_add(pv, vid);
+
+	/* Create port vlan infomration
+	 */
+	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+	if (!pv)
+		return -ENOMEM;
+
+	pv->parent = br;
+	err = __vlan_add(pv, vid);
+	if (err)
+		goto out;
+
+	rcu_assign_pointer(br->vlan_info, pv);
+	return 0;
+out:
+	kfree(pv);
+	return err;
+}
+
+/* Must be protected by RTNL */
+int br_vlan_delete(struct net_bridge *br, u16 vid)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (!pv)
+		return -EINVAL;
+
+	__vlan_del(pv, vid);
+	return 0;
+}
+
+void br_vlan_flush(struct net_bridge *br)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(br->vlan_info);
+	if (!pv)
+		return;
+
+	__vlan_flush(pv);
+}
+
+int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
+{
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	if (br->vlan_enabled == val)
+		goto unlock;
+
+	br->vlan_enabled = val;
+
+unlock:
+	rtnl_unlock();
+	return 0;
+}
+
+/* Must be protected by RTNL */
+int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+{
+	struct net_port_vlans *pv = NULL;
+	int err;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (pv)
+		return __vlan_add(pv, vid);
+
+	/* Create port vlan infomration
+	 */
+	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+	if (!pv) {
+		err = -ENOMEM;
+		goto clean_up;
+	}
+
+	pv->port_idx = port->port_no;
+	pv->parent = port;
+	err = __vlan_add(pv, vid);
+	if (err)
+		goto clean_up;
+
+	rcu_assign_pointer(port->vlan_info, pv);
+	return 0;
+
+clean_up:
+	kfree(pv);
+	return err;
+}
+
+/* Must be protected by RTNL */
+int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (!pv)
+		return -EINVAL;
+
+	return __vlan_del(pv, vid);
+}
+
+void nbp_vlan_flush(struct net_bridge_port *port)
+{
+	struct net_port_vlans *pv;
+
+	ASSERT_RTNL();
+
+	pv = rtnl_dereference(port->vlan_info);
+	if (!pv)
+		return;
+
+	__vlan_flush(pv);
+}