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"
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 = v - > parent . port - > 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 )
{
if ( ! test_bit ( vid , v - > vlan_bitmap ) )
return - EINVAL ;
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 ) ;
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 )
{
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:10 +04:00
/* Called under RCU */
bool br_allowed_ingress ( struct net_bridge * br , struct net_port_vlans * v ,
struct sk_buff * skb )
{
u16 vid ;
/* 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 ;
br_vlan_get_tag ( skb , & vid ) ;
if ( test_bit ( vid , v - > vlan_bitmap ) )
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 */
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 = 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 = 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 ) ;
}