2005-04-17 02:20:36 +04:00
/*
* Userspace interface
* Linux ethernet bridge
*
* Authors :
* Lennert Buytenhek < buytenh @ gnu . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/ethtool.h>
# include <linux/if_arp.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/rtnetlink.h>
2006-01-06 03:35:42 +03:00
# include <linux/if_ether.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include "br_private.h"
/*
* Determine initial path cost based on speed .
* using recommendations from 802.1 d standard
*
2007-08-01 01:00:02 +04:00
* Since driver might sleep need to not be holding any locks .
2005-04-17 02:20:36 +04:00
*/
2005-12-21 02:19:51 +03:00
static int port_cost ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-08-31 09:16:22 +04:00
if ( dev - > ethtool_ops & & dev - > ethtool_ops - > get_settings ) {
struct ethtool_cmd ecmd = { . cmd = ETHTOOL_GSET , } ;
if ( ! dev - > ethtool_ops - > get_settings ( dev , & ecmd ) ) {
2007-08-01 01:00:02 +04:00
switch ( ecmd . speed ) {
case SPEED_10000 :
return 2 ;
2007-08-31 09:16:22 +04:00
case SPEED_1000 :
return 4 ;
case SPEED_100 :
return 19 ;
2007-08-01 01:00:02 +04:00
case SPEED_10 :
return 100 ;
}
2005-04-17 02:20:36 +04:00
}
}
/* Old silly heuristics based on name */
if ( ! strncmp ( dev - > name , " lec " , 3 ) )
return 7 ;
if ( ! strncmp ( dev - > name , " plip " , 4 ) )
return 2500 ;
return 100 ; /* assume old 10Mbps */
}
2005-12-21 02:19:51 +03:00
/*
* Check for port carrier transistions .
* Called from work queue to allow for calling functions that
* might sleep ( such as speed check ) , and to debounce .
*/
2007-02-22 12:10:18 +03:00
void br_port_carrier_check ( struct net_bridge_port * p )
2005-12-21 02:19:51 +03:00
{
2007-02-22 12:10:18 +03:00
struct net_device * dev = p - > dev ;
struct net_bridge * br = p - > br ;
2006-03-04 04:14:51 +03:00
if ( netif_carrier_ok ( dev ) )
p - > path_cost = port_cost ( dev ) ;
2007-02-22 12:10:18 +03:00
if ( netif_running ( br - > dev ) ) {
2006-03-04 04:14:51 +03:00
spin_lock_bh ( & br - > lock ) ;
if ( netif_carrier_ok ( dev ) ) {
if ( p - > state = = BR_STATE_DISABLED )
br_stp_enable_port ( p ) ;
} else {
if ( p - > state ! = BR_STATE_DISABLED )
br_stp_disable_port ( p ) ;
2005-12-21 02:19:51 +03:00
}
2006-03-04 04:14:51 +03:00
spin_unlock_bh ( & br - > lock ) ;
2005-12-21 02:19:51 +03:00
}
}
2006-02-10 04:10:12 +03:00
static void release_nbp ( struct kobject * kobj )
{
struct net_bridge_port * p
= container_of ( kobj , struct net_bridge_port , kobj ) ;
kfree ( p ) ;
}
static struct kobj_type brport_ktype = {
# ifdef CONFIG_SYSFS
. sysfs_ops = & brport_sysfs_ops ,
# endif
. release = release_nbp ,
} ;
2005-04-17 02:20:36 +04:00
static void destroy_nbp ( struct net_bridge_port * p )
{
struct net_device * dev = p - > dev ;
p - > br = NULL ;
p - > dev = NULL ;
dev_put ( dev ) ;
2006-02-10 04:10:12 +03:00
kobject_put ( & p - > kobj ) ;
2005-04-17 02:20:36 +04:00
}
static void destroy_nbp_rcu ( struct rcu_head * head )
{
struct net_bridge_port * p =
container_of ( head , struct net_bridge_port , rcu ) ;
destroy_nbp ( p ) ;
}
2006-02-01 04:44:07 +03:00
/* Delete port(interface) from bridge is done in two steps.
* via RCU . First step , marks device as down . That deletes
* all the timers and stops new packets from flowing through .
*
* Final cleanup doesn ' t occur until after all CPU ' s finished
* processing packets .
*
* Protected from multiple admin operations by RTNL mutex
*/
2005-04-17 02:20:36 +04:00
static void del_nbp ( struct net_bridge_port * p )
{
struct net_bridge * br = p - > br ;
struct net_device * dev = p - > dev ;
2007-12-17 22:54:39 +03:00
sysfs_remove_link ( br - > ifobj , dev - > name ) ;
2006-02-10 04:10:12 +03:00
2005-04-17 02:20:36 +04:00
dev_set_promiscuity ( dev , - 1 ) ;
spin_lock_bh ( & br - > lock ) ;
br_stp_disable_port ( p ) ;
spin_unlock_bh ( & br - > lock ) ;
2007-03-23 00:08:46 +03:00
br_ifinfo_notify ( RTM_DELLINK , p ) ;
2006-10-13 01:45:38 +04:00
br_fdb_delete_by_port ( br , p , 1 ) ;
2005-04-17 02:20:36 +04:00
list_del_rcu ( & p - > list ) ;
2006-02-10 04:08:52 +03:00
rcu_assign_pointer ( dev - > br_port , NULL ) ;
2006-03-04 04:16:15 +03:00
kobject_uevent ( & p - > kobj , KOBJ_REMOVE ) ;
2006-02-10 04:10:12 +03:00
kobject_del ( & p - > kobj ) ;
2005-04-17 02:20:36 +04:00
call_rcu ( & p - > rcu , destroy_nbp_rcu ) ;
}
/* called with RTNL */
static void del_br ( struct net_bridge * br )
{
struct net_bridge_port * p , * n ;
list_for_each_entry_safe ( p , n , & br - > port_list , list ) {
del_nbp ( p ) ;
}
del_timer_sync ( & br - > gc_timer ) ;
br_sysfs_delbr ( br - > dev ) ;
2007-02-09 17:24:35 +03:00
unregister_netdevice ( br - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2008-09-09 03:19:58 +04:00
static struct net_device * new_bridge_dev ( struct net * net , const char * name )
2005-04-17 02:20:36 +04:00
{
struct net_bridge * br ;
struct net_device * dev ;
dev = alloc_netdev ( sizeof ( struct net_bridge ) , name ,
br_dev_setup ) ;
2007-02-09 17:24:35 +03:00
2005-04-17 02:20:36 +04:00
if ( ! dev )
return NULL ;
2008-09-09 03:19:58 +04:00
dev_net_set ( dev , net ) ;
2005-04-17 02:20:36 +04:00
br = netdev_priv ( dev ) ;
br - > dev = dev ;
spin_lock_init ( & br - > lock ) ;
INIT_LIST_HEAD ( & br - > port_list ) ;
spin_lock_init ( & br - > hash_lock ) ;
br - > bridge_id . prio [ 0 ] = 0x80 ;
br - > bridge_id . prio [ 1 ] = 0x00 ;
2006-03-21 09:57:03 +03:00
memcpy ( br - > group_addr , br_group_address , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
2005-12-22 06:00:58 +03:00
br - > feature_mask = dev - > features ;
2007-03-22 00:22:44 +03:00
br - > stp_enabled = BR_NO_STP ;
2005-04-17 02:20:36 +04:00
br - > designated_root = br - > bridge_id ;
br - > root_path_cost = 0 ;
br - > root_port = 0 ;
br - > bridge_max_age = br - > max_age = 20 * HZ ;
br - > bridge_hello_time = br - > hello_time = 2 * HZ ;
br - > bridge_forward_delay = br - > forward_delay = 15 * HZ ;
br - > topology_change = 0 ;
br - > topology_change_detected = 0 ;
br - > ageing_time = 300 * HZ ;
2008-07-31 03:27:55 +04:00
br_netfilter_rtable_init ( br ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & br - > age_list ) ;
br_stp_timer_init ( br ) ;
return dev ;
}
/* find an available port number */
static int find_portno ( struct net_bridge * br )
{
int index ;
struct net_bridge_port * p ;
unsigned long * inuse ;
2006-03-21 09:56:50 +03:00
inuse = kcalloc ( BITS_TO_LONGS ( BR_MAX_PORTS ) , sizeof ( unsigned long ) ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! inuse )
return - ENOMEM ;
set_bit ( 0 , inuse ) ; /* zero is reserved */
list_for_each_entry ( p , & br - > port_list , list ) {
set_bit ( p - > port_no , inuse ) ;
}
index = find_first_zero_bit ( inuse , BR_MAX_PORTS ) ;
kfree ( inuse ) ;
return ( index > = BR_MAX_PORTS ) ? - EXFULL : index ;
}
2005-12-21 02:19:51 +03:00
/* called with RTNL but without bridge lock */
2007-02-09 17:24:35 +03:00
static struct net_bridge_port * new_nbp ( struct net_bridge * br ,
2005-12-21 02:19:51 +03:00
struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
int index ;
struct net_bridge_port * p ;
2007-02-09 17:24:35 +03:00
2005-04-17 02:20:36 +04:00
index = find_portno ( br ) ;
if ( index < 0 )
return ERR_PTR ( index ) ;
2006-03-21 09:57:03 +03:00
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( p = = NULL )
return ERR_PTR ( - ENOMEM ) ;
p - > br = br ;
dev_hold ( dev ) ;
p - > dev = dev ;
2005-12-21 02:19:51 +03:00
p - > path_cost = port_cost ( dev ) ;
2007-02-09 17:24:35 +03:00
p - > priority = 0x8000 > > BR_PORT_BITS ;
2005-04-17 02:20:36 +04:00
p - > port_no = index ;
br_init_port ( p ) ;
p - > state = BR_STATE_DISABLED ;
2006-03-04 04:15:34 +03:00
br_stp_port_timer_init ( p ) ;
2005-04-17 02:20:36 +04:00
return p ;
}
2008-09-09 03:19:58 +04:00
int br_add_bridge ( struct net * net , const char * name )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev ;
int ret ;
2008-09-09 03:19:58 +04:00
dev = new_bridge_dev ( net , name ) ;
2007-02-09 17:24:35 +03:00
if ( ! dev )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
rtnl_lock ( ) ;
if ( strchr ( dev - > name , ' % ' ) ) {
ret = dev_alloc_name ( dev , dev - > name ) ;
2008-05-05 04:58:07 +04:00
if ( ret < 0 )
goto out_free ;
2005-04-17 02:20:36 +04:00
}
ret = register_netdevice ( dev ) ;
2008-05-05 04:58:07 +04:00
if ( ret )
goto out_free ;
2005-04-17 02:20:36 +04:00
ret = br_sysfs_addbr ( dev ) ;
2006-05-11 00:21:53 +04:00
if ( ret )
2006-06-06 03:39:34 +04:00
unregister_netdevice ( dev ) ;
out :
2005-04-17 02:20:36 +04:00
rtnl_unlock ( ) ;
2006-05-11 00:21:53 +04:00
return ret ;
2008-05-05 04:58:07 +04:00
out_free :
free_netdev ( dev ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-09-09 03:19:58 +04:00
int br_del_bridge ( struct net * net , const char * name )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev ;
int ret = 0 ;
rtnl_lock ( ) ;
2008-09-09 03:19:58 +04:00
dev = __dev_get_by_name ( net , name ) ;
2007-02-09 17:24:35 +03:00
if ( dev = = NULL )
2005-04-17 02:20:36 +04:00
ret = - ENXIO ; /* Could not find device */
else if ( ! ( dev - > priv_flags & IFF_EBRIDGE ) ) {
/* Attempt to delete non bridge device! */
ret = - EPERM ;
}
else if ( dev - > flags & IFF_UP ) {
/* Not shutdown yet. */
ret = - EBUSY ;
2007-02-09 17:24:35 +03:00
}
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:35 +03:00
else
2005-04-17 02:20:36 +04:00
del_br ( netdev_priv ( dev ) ) ;
rtnl_unlock ( ) ;
return ret ;
}
2006-01-06 03:35:42 +03:00
/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
2005-04-17 02:20:36 +04:00
int br_min_mtu ( const struct net_bridge * br )
{
const struct net_bridge_port * p ;
int mtu = 0 ;
ASSERT_RTNL ( ) ;
if ( list_empty ( & br - > port_list ) )
2006-01-06 03:35:42 +03:00
mtu = ETH_DATA_LEN ;
2005-04-17 02:20:36 +04:00
else {
list_for_each_entry ( p , & br - > port_list , list ) {
if ( ! mtu | | p - > dev - > mtu < mtu )
mtu = p - > dev - > mtu ;
}
}
return mtu ;
}
2005-05-30 01:15:17 +04:00
/*
* Recomputes features using slave ' s features
*/
void br_features_recompute ( struct net_bridge * br )
{
struct net_bridge_port * p ;
2007-08-11 02:47:58 +04:00
unsigned long features ;
2005-05-30 01:15:17 +04:00
2007-08-11 02:47:58 +04:00
features = br - > feature_mask ;
2005-05-30 01:15:17 +04:00
list_for_each_entry ( p , & br - > port_list , list ) {
2007-08-11 02:47:58 +04:00
features = netdev_compute_features ( features , p - > dev - > features ) ;
2005-05-30 01:15:17 +04:00
}
2007-08-11 02:47:58 +04:00
br - > dev - > features = features ;
2005-05-30 01:15:17 +04:00
}
2005-04-17 02:20:36 +04:00
/* called with RTNL */
int br_add_if ( struct net_bridge * br , struct net_device * dev )
{
struct net_bridge_port * p ;
int err = 0 ;
if ( dev - > flags & IFF_LOOPBACK | | dev - > type ! = ARPHRD_ETHER )
return - EINVAL ;
if ( dev - > hard_start_xmit = = br_dev_xmit )
return - ELOOP ;
if ( dev - > br_port ! = NULL )
return - EBUSY ;
2006-02-10 04:10:12 +03:00
p = new_nbp ( br , dev ) ;
if ( IS_ERR ( p ) )
2005-04-17 02:20:36 +04:00
return PTR_ERR ( p ) ;
2008-07-15 07:53:13 +04:00
err = dev_set_promiscuity ( dev , 1 ) ;
if ( err )
goto put_back ;
2007-12-18 09:05:35 +03:00
err = kobject_init_and_add ( & p - > kobj , & brport_ktype , & ( dev - > dev . kobj ) ,
SYSFS_BRIDGE_PORT_ATTR ) ;
2006-02-10 04:10:12 +03:00
if ( err )
goto err0 ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:35 +03:00
err = br_fdb_insert ( br , p , dev - > dev_addr ) ;
2006-02-10 04:10:12 +03:00
if ( err )
goto err1 ;
2005-04-17 02:20:36 +04:00
2006-02-10 04:10:12 +03:00
err = br_sysfs_addif ( p ) ;
if ( err )
goto err2 ;
2005-04-17 02:20:36 +04:00
2006-02-10 04:10:12 +03:00
rcu_assign_pointer ( dev - > br_port , p ) ;
2008-06-20 03:15:47 +04:00
dev_disable_lro ( dev ) ;
2006-02-10 04:10:12 +03:00
list_add_rcu ( & p - > list , & br - > port_list ) ;
spin_lock_bh ( & br - > lock ) ;
br_stp_recalculate_bridge_id ( br ) ;
br_features_recompute ( br ) ;
2007-03-08 03:10:53 +03:00
if ( ( dev - > flags & IFF_UP ) & & netif_carrier_ok ( dev ) & &
( br - > dev - > flags & IFF_UP ) )
br_stp_enable_port ( p ) ;
2006-02-10 04:10:12 +03:00
spin_unlock_bh ( & br - > lock ) ;
2007-03-23 00:08:46 +03:00
br_ifinfo_notify ( RTM_NEWLINK , p ) ;
2006-02-10 04:10:12 +03:00
dev_set_mtu ( br - > dev , br_min_mtu ( br ) ) ;
2007-02-22 12:10:18 +03:00
2006-02-10 04:10:12 +03:00
kobject_uevent ( & p - > kobj , KOBJ_ADD ) ;
2005-04-17 02:20:36 +04:00
2006-02-10 04:10:12 +03:00
return 0 ;
err2 :
2006-10-13 01:45:38 +04:00
br_fdb_delete_by_port ( br , p , 1 ) ;
2006-02-10 04:10:12 +03:00
err1 :
kobject_del ( & p - > kobj ) ;
err0 :
kobject_put ( & p - > kobj ) ;
2008-07-15 07:53:13 +04:00
dev_set_promiscuity ( dev , - 1 ) ;
2008-04-29 14:17:42 +04:00
put_back :
dev_put ( dev ) ;
2008-07-15 07:53:13 +04:00
kfree ( p ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
/* called with RTNL */
int br_del_if ( struct net_bridge * br , struct net_device * dev )
{
struct net_bridge_port * p = dev - > br_port ;
2007-02-09 17:24:35 +03:00
if ( ! p | | p - > br ! = br )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
del_nbp ( p ) ;
spin_lock_bh ( & br - > lock ) ;
br_stp_recalculate_bridge_id ( br ) ;
2005-05-30 01:15:17 +04:00
br_features_recompute ( br ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_bh ( & br - > lock ) ;
return 0 ;
}
2008-09-09 03:20:18 +04:00
void br_net_exit ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2008-07-03 14:53:42 +04:00
struct net_device * dev ;
2005-04-17 02:20:36 +04:00
rtnl_lock ( ) ;
2008-07-03 14:53:42 +04:00
restart :
2008-09-09 03:20:18 +04:00
for_each_netdev ( net , dev ) {
2008-07-03 14:53:42 +04:00
if ( dev - > priv_flags & IFF_EBRIDGE ) {
2005-04-17 02:20:36 +04:00
del_br ( dev - > priv ) ;
2008-07-03 14:53:42 +04:00
goto restart ;
}
}
2005-04-17 02:20:36 +04:00
rtnl_unlock ( ) ;
}