2013-02-13 16:00:09 +04:00
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/rtnetlink.h>
# include <linux/slab.h>
# include "br_private.h"
2013-02-13 16:00:15 +04:00
static void __vlan_add_pvid ( struct net_port_vlans * v , u16 vid )
{
if ( v - > pvid = = vid )
return ;
smp_wmb ( ) ;
v - > pvid = vid ;
}
static void __vlan_delete_pvid ( struct net_port_vlans * v , u16 vid )
{
if ( v - > pvid ! = vid )
return ;
smp_wmb ( ) ;
v - > pvid = 0 ;
}
static int __vlan_add ( struct net_port_vlans * v , u16 vid , u16 flags )
2013-02-13 16:00:09 +04:00
{
2013-02-13 16:00:19 +04:00
struct net_bridge_port * p = NULL ;
struct net_bridge * br ;
struct net_device * dev ;
2013-02-13 16:00:09 +04:00
int err ;
2013-02-13 16:00:15 +04:00
if ( test_bit ( vid , v - > vlan_bitmap ) ) {
if ( flags & BRIDGE_VLAN_INFO_PVID )
__vlan_add_pvid ( v , vid ) ;
return 0 ;
}
2013-02-13 16:00:09 +04:00
2013-02-13 16:00:19 +04:00
if ( vid ) {
if ( v - > port_idx ) {
p = v - > parent . port ;
br = p - > br ;
dev = p - > dev ;
} else {
br = v - > parent . br ;
dev = br - > dev ;
}
2013-02-13 16:00:09 +04:00
2013-02-13 16:00:19 +04:00
if ( p & & ( dev - > features & NETIF_F_HW_VLAN_FILTER ) ) {
/* 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 .
*/
2013-02-13 16:00:09 +04:00
err = dev - > netdev_ops - > ndo_vlan_rx_add_vid ( dev , vid ) ;
if ( err )
return err ;
}
2013-02-13 16:00:19 +04:00
err = br_fdb_insert ( br , p , dev - > dev_addr , vid ) ;
if ( err ) {
br_err ( br , " failed insert local address into bridge "
" forwarding table \n " ) ;
goto out_filt ;
}
2013-02-13 16:00:09 +04:00
}
set_bit ( vid , v - > vlan_bitmap ) ;
2013-02-13 16:00:13 +04:00
v - > num_vlans + + ;
2013-02-13 16:00:15 +04:00
if ( flags & BRIDGE_VLAN_INFO_PVID )
__vlan_add_pvid ( v , vid ) ;
2013-02-13 16:00:09 +04:00
return 0 ;
2013-02-13 16:00:19 +04:00
out_filt :
if ( p & & ( dev - > features & NETIF_F_HW_VLAN_FILTER ) )
dev - > netdev_ops - > ndo_vlan_rx_kill_vid ( dev , vid ) ;
return err ;
2013-02-13 16:00:09 +04:00
}
static int __vlan_del ( struct net_port_vlans * v , u16 vid )
{
if ( ! test_bit ( vid , v - > vlan_bitmap ) )
return - EINVAL ;
2013-02-13 16:00:15 +04:00
__vlan_delete_pvid ( v , vid ) ;
2013-02-13 16:00:09 +04:00
if ( v - > port_idx & & vid ) {
struct net_device * dev = v - > parent . port - > 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 ) ;
2013-02-13 16:00:13 +04:00
v - > num_vlans - - ;
2013-02-13 16:00:09 +04:00
if ( bitmap_empty ( v - > vlan_bitmap , BR_VLAN_BITMAP_LEN ) ) {
if ( v - > port_idx )
rcu_assign_pointer ( v - > parent . port - > vlan_info , NULL ) ;
else
rcu_assign_pointer ( v - > parent . br - > vlan_info , NULL ) ;
kfree_rcu ( v , rcu ) ;
}
return 0 ;
}
static void __vlan_flush ( struct net_port_vlans * v )
{
2013-02-13 16:00:15 +04:00
smp_wmb ( ) ;
v - > pvid = 0 ;
2013-02-13 16:00:09 +04:00
bitmap_zero ( v - > vlan_bitmap , BR_VLAN_BITMAP_LEN ) ;
if ( v - > port_idx )
rcu_assign_pointer ( v - > parent . port - > vlan_info , NULL ) ;
else
rcu_assign_pointer ( v - > parent . br - > vlan_info , NULL ) ;
kfree_rcu ( v , rcu ) ;
}
2013-02-13 16:00:14 +04:00
/* Strip the tag from the packet. Will return skb with tci set 0. */
static struct sk_buff * br_vlan_untag ( struct sk_buff * skb )
{
if ( skb - > protocol ! = htons ( ETH_P_8021Q ) ) {
skb - > vlan_tci = 0 ;
return skb ;
}
skb - > vlan_tci = 0 ;
skb = vlan_untag ( skb ) ;
if ( skb )
skb - > vlan_tci = 0 ;
return skb ;
}
struct sk_buff * br_handle_vlan ( struct net_bridge * br ,
const struct net_port_vlans * pv ,
struct sk_buff * skb )
2013-02-13 16:00:10 +04:00
{
u16 vid ;
2013-02-13 16:00:14 +04:00
if ( ! br - > vlan_enabled )
goto out ;
/* At this point, we know that the frame was filtered and contains
* a valid vlan id . If the vlan id matches the pvid of current port
* send untagged ; otherwise , send taged .
*/
br_vlan_get_tag ( skb , & vid ) ;
if ( vid = = br_get_pvid ( pv ) )
skb = br_vlan_untag ( skb ) ;
else {
/* Egress policy says "send tagged". If output device
* is the bridge , we need to add the VLAN header
* ourselves since we ' ll be going through the RX path .
* Sending to ports puts the frame on the TX path and
* we let dev_hard_start_xmit ( ) add the header .
*/
if ( skb - > protocol ! = htons ( ETH_P_8021Q ) & &
pv - > port_idx = = 0 ) {
/* vlan_put_tag expects skb->data to point to
* mac header .
*/
skb_push ( skb , ETH_HLEN ) ;
skb = __vlan_put_tag ( skb , skb - > vlan_tci ) ;
if ( ! skb )
goto out ;
/* put skb->data back to where it was */
skb_pull ( skb , ETH_HLEN ) ;
skb - > vlan_tci = 0 ;
}
}
out :
return skb ;
}
/* Called under RCU */
bool br_allowed_ingress ( struct net_bridge * br , struct net_port_vlans * v ,
struct sk_buff * skb , u16 * vid )
{
2013-02-13 16:00:10 +04:00
/* If VLAN filtering is disabled on the bridge, all packets are
* permitted .
*/
if ( ! br - > vlan_enabled )
return true ;
/* If there are no vlan in the permitted list, all packets are
* rejected .
*/
if ( ! v )
return false ;
2013-02-13 16:00:14 +04:00
if ( br_vlan_get_tag ( skb , vid ) ) {
u16 pvid = br_get_pvid ( v ) ;
/* Frame did not have a tag. See if pvid is set
* on this port . That tells us which vlan untagged
* traffic belongs to .
*/
if ( pvid = = VLAN_N_VID )
return false ;
/* PVID is set on this port. Any untagged ingress
* frame is considered to belong to this vlan .
*/
__vlan_hwaccel_put_tag ( skb , pvid ) ;
return true ;
}
/* Frame had a valid vlan tag. See if vlan is allowed */
if ( test_bit ( * vid , v - > vlan_bitmap ) )
2013-02-13 16:00:10 +04:00
return true ;
return false ;
}
2013-02-13 16:00:11 +04:00
/* Called under RCU. */
bool br_allowed_egress ( struct net_bridge * br ,
const struct net_port_vlans * v ,
const struct sk_buff * skb )
{
u16 vid ;
if ( ! br - > vlan_enabled )
return true ;
if ( ! v )
return false ;
br_vlan_get_tag ( skb , & vid ) ;
if ( test_bit ( vid , v - > vlan_bitmap ) )
return true ;
return false ;
}
2013-02-13 16:00:09 +04:00
/* Must be protected by RTNL */
2013-02-13 16:00:15 +04:00
int br_vlan_add ( struct net_bridge * br , u16 vid , u16 flags )
2013-02-13 16:00:09 +04:00
{
struct net_port_vlans * pv = NULL ;
int err ;
ASSERT_RTNL ( ) ;
pv = rtnl_dereference ( br - > vlan_info ) ;
if ( pv )
2013-02-13 16:00:15 +04:00
return __vlan_add ( pv , vid , flags ) ;
2013-02-13 16:00:09 +04:00
/* Create port vlan infomration
*/
pv = kzalloc ( sizeof ( * pv ) , GFP_KERNEL ) ;
if ( ! pv )
return - ENOMEM ;
pv - > parent . br = br ;
2013-02-13 16:00:15 +04:00
err = __vlan_add ( pv , vid , flags ) ;
2013-02-13 16:00:09 +04:00
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 ;
2013-02-13 16:00:19 +04:00
if ( vid ) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it ' s the default and is always there in the fdb .
*/
spin_lock_bh ( & br - > hash_lock ) ;
fdb_delete_by_addr ( br , br - > dev - > dev_addr , vid ) ;
spin_unlock_bh ( & br - > hash_lock ) ;
}
2013-02-13 16:00:09 +04:00
__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 */
2013-02-13 16:00:15 +04:00
int nbp_vlan_add ( struct net_bridge_port * port , u16 vid , u16 flags )
2013-02-13 16:00:09 +04:00
{
struct net_port_vlans * pv = NULL ;
int err ;
ASSERT_RTNL ( ) ;
pv = rtnl_dereference ( port - > vlan_info ) ;
if ( pv )
2013-02-13 16:00:15 +04:00
return __vlan_add ( pv , vid , flags ) ;
2013-02-13 16:00:09 +04:00
/* 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 = port ;
2013-02-13 16:00:15 +04:00
err = __vlan_add ( pv , vid , flags ) ;
2013-02-13 16:00:09 +04:00
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 ;
2013-02-13 16:00:19 +04:00
if ( vid ) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it ' s the default and is always there in the fdb .
*/
spin_lock_bh ( & port - > br - > hash_lock ) ;
fdb_delete_by_addr ( port - > br , port - > dev - > dev_addr , vid ) ;
spin_unlock_bh ( & port - > br - > hash_lock ) ;
}
2013-02-13 16:00:09 +04:00
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 ) ;
}
2013-02-13 16:00:19 +04:00
bool nbp_vlan_find ( struct net_bridge_port * port , u16 vid )
{
struct net_port_vlans * pv ;
bool found = false ;
rcu_read_lock ( ) ;
pv = rcu_dereference ( port - > vlan_info ) ;
if ( ! pv )
goto out ;
if ( test_bit ( vid , pv - > vlan_bitmap ) )
found = true ;
out :
rcu_read_unlock ( ) ;
return found ;
}