2005-04-17 02:20:36 +04:00
/*
* Device handling code
* 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>
2010-05-06 11:48:24 +04:00
# include <linux/netpoll.h>
2005-12-22 05:51:49 +03:00
# include <linux/etherdevice.h>
2005-12-22 06:00:58 +03:00
# include <linux/ethtool.h>
2010-05-06 11:48:24 +04:00
# include <linux/list.h>
2010-04-15 14:14:51 +04:00
# include <linux/netfilter_bridge.h>
2005-12-22 05:51:49 +03:00
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include "br_private.h"
2010-07-27 12:26:30 +04:00
/* net device transmit always called with BH disabled */
2009-08-31 23:50:41 +04:00
netdev_tx_t br_dev_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
struct net_bridge * br = netdev_priv ( dev ) ;
const unsigned char * dest = skb - > data ;
struct net_bridge_fdb_entry * dst ;
2010-02-27 22:41:48 +03:00
struct net_bridge_mdb_entry * mdst ;
2010-03-02 16:32:09 +03:00
struct br_cpu_netstats * brstats = this_cpu_ptr ( br - > stats ) ;
2005-04-17 02:20:36 +04:00
2010-04-15 14:14:51 +04:00
# ifdef CONFIG_BRIDGE_NETFILTER
if ( skb - > nf_bridge & & ( skb - > nf_bridge - > mask & BRNF_BRIDGED_DNAT ) ) {
br_nf_pre_routing_finish_bridge_slow ( skb ) ;
return NETDEV_TX_OK ;
}
# endif
2010-03-02 16:32:09 +03:00
brstats - > tx_packets + + ;
brstats - > tx_bytes + = skb - > len ;
2010-02-27 22:41:42 +03:00
2010-03-02 16:32:09 +03:00
BR_INPUT_SKB_CB ( skb ) - > brdev = dev ;
2005-04-17 02:20:36 +04:00
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , ETH_HLEN ) ;
2010-07-27 12:26:30 +04:00
rcu_read_lock ( ) ;
2010-04-27 11:13:06 +04:00
if ( is_multicast_ether_addr ( dest ) ) {
2010-02-27 22:41:48 +03:00
if ( br_multicast_rcv ( br , NULL , skb ) )
goto out ;
mdst = br_mdb_get ( br , skb ) ;
2010-03-16 00:51:18 +03:00
if ( mdst | | BR_INPUT_SKB_CB_MROUTERS_ONLY ( skb ) )
2010-02-27 22:41:48 +03:00
br_multicast_deliver ( mdst , skb ) ;
else
br_flood_deliver ( br , skb ) ;
} else if ( ( dst = __br_fdb_get ( br , dest ) ) ! = NULL )
2005-04-17 02:20:36 +04:00
br_deliver ( dst - > dst , skb ) ;
else
2007-09-17 03:20:48 +04:00
br_flood_deliver ( br , skb ) ;
2005-04-17 02:20:36 +04:00
2010-02-27 22:41:48 +03:00
out :
2010-07-27 12:26:30 +04:00
rcu_read_unlock ( ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
static int br_dev_open ( struct net_device * dev )
{
2005-05-30 01:15:17 +04:00
struct net_bridge * br = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-05-30 01:15:17 +04:00
br_features_recompute ( br ) ;
netif_start_queue ( dev ) ;
br_stp_enable_bridge ( br ) ;
2010-02-28 11:49:38 +03:00
br_multicast_open ( br ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void br_dev_set_multicast_list ( struct net_device * dev )
{
}
static int br_dev_stop ( struct net_device * dev )
{
2010-02-28 11:49:38 +03:00
struct net_bridge * br = netdev_priv ( dev ) ;
br_stp_disable_bridge ( br ) ;
br_multicast_stop ( br ) ;
2005-04-17 02:20:36 +04:00
netif_stop_queue ( dev ) ;
return 0 ;
}
2010-03-02 16:32:09 +03:00
static struct net_device_stats * br_get_stats ( struct net_device * dev )
{
struct net_bridge * br = netdev_priv ( dev ) ;
struct net_device_stats * stats = & dev - > stats ;
struct br_cpu_netstats sum = { 0 } ;
unsigned int cpu ;
for_each_possible_cpu ( cpu ) {
const struct br_cpu_netstats * bstats
= per_cpu_ptr ( br - > stats , cpu ) ;
sum . tx_bytes + = bstats - > tx_bytes ;
sum . tx_packets + = bstats - > tx_packets ;
sum . rx_bytes + = bstats - > rx_bytes ;
sum . rx_packets + = bstats - > rx_packets ;
}
stats - > tx_bytes = sum . tx_bytes ;
stats - > tx_packets = sum . tx_packets ;
stats - > rx_bytes = sum . rx_bytes ;
stats - > rx_packets = sum . rx_packets ;
return stats ;
}
2005-04-17 02:20:36 +04:00
static int br_change_mtu ( struct net_device * dev , int new_mtu )
{
2008-07-31 03:27:55 +04:00
struct net_bridge * br = netdev_priv ( dev ) ;
if ( new_mtu < 68 | | new_mtu > br_min_mtu ( br ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
dev - > mtu = new_mtu ;
2008-07-31 03:27:55 +04:00
# ifdef CONFIG_BRIDGE_NETFILTER
/* remember the MTU in the rtable for PMTU */
br - > fake_rtable . u . dst . metrics [ RTAX_MTU - 1 ] = new_mtu ;
# endif
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-09 22:49:58 +04:00
/* Allow setting mac address to any valid ethernet address. */
2005-12-22 05:51:49 +03:00
static int br_set_mac_address ( struct net_device * dev , void * p )
{
struct net_bridge * br = netdev_priv ( dev ) ;
struct sockaddr * addr = p ;
2007-04-09 22:49:58 +04:00
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EINVAL ;
2005-12-22 05:51:49 +03:00
spin_lock_bh ( & br - > lock ) ;
2007-04-09 22:49:58 +04:00
memcpy ( dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
br_stp_change_bridge_id ( br , addr - > sa_data ) ;
2008-06-18 03:10:06 +04:00
br - > flags | = BR_SET_MAC_ADDR ;
2005-12-22 05:51:49 +03:00
spin_unlock_bh ( & br - > lock ) ;
2007-04-09 22:49:58 +04:00
return 0 ;
2005-12-22 05:51:49 +03:00
}
2005-12-22 06:00:58 +03:00
static void br_getinfo ( struct net_device * dev , struct ethtool_drvinfo * info )
{
strcpy ( info - > driver , " bridge " ) ;
strcpy ( info - > version , BR_VERSION ) ;
strcpy ( info - > fw_version , " N/A " ) ;
strcpy ( info - > bus_info , " N/A " ) ;
}
static int br_set_sg ( struct net_device * dev , u32 data )
{
struct net_bridge * br = netdev_priv ( dev ) ;
if ( data )
br - > feature_mask | = NETIF_F_SG ;
else
br - > feature_mask & = ~ NETIF_F_SG ;
br_features_recompute ( br ) ;
return 0 ;
}
static int br_set_tso ( struct net_device * dev , u32 data )
{
struct net_bridge * br = netdev_priv ( dev ) ;
if ( data )
br - > feature_mask | = NETIF_F_TSO ;
else
br - > feature_mask & = ~ NETIF_F_TSO ;
br_features_recompute ( br ) ;
return 0 ;
}
static int br_set_tx_csum ( struct net_device * dev , u32 data )
{
struct net_bridge * br = netdev_priv ( dev ) ;
if ( data )
2006-06-18 09:06:45 +04:00
br - > feature_mask | = NETIF_F_NO_CSUM ;
2005-12-22 06:00:58 +03:00
else
2006-06-18 09:06:45 +04:00
br - > feature_mask & = ~ NETIF_F_ALL_CSUM ;
2005-12-22 06:00:58 +03:00
br_features_recompute ( br ) ;
return 0 ;
}
2010-05-06 11:48:24 +04:00
# ifdef CONFIG_NET_POLL_CONTROLLER
2010-05-10 13:31:08 +04:00
static bool br_devices_support_netpoll ( struct net_bridge * br )
2010-05-06 11:48:24 +04:00
{
struct net_bridge_port * p ;
bool ret = true ;
int count = 0 ;
unsigned long flags ;
spin_lock_irqsave ( & br - > lock , flags ) ;
list_for_each_entry ( p , & br - > port_list , list ) {
count + + ;
if ( ( p - > dev - > priv_flags & IFF_DISABLE_NETPOLL ) | |
! p - > dev - > netdev_ops - > ndo_poll_controller )
ret = false ;
}
spin_unlock_irqrestore ( & br - > lock , flags ) ;
return count ! = 0 & & ret ;
}
2010-05-10 13:31:08 +04:00
void br_netpoll_cleanup ( struct net_device * dev )
2010-05-06 11:48:24 +04:00
{
2010-05-10 13:31:08 +04:00
struct net_bridge * br = netdev_priv ( dev ) ;
2010-05-06 11:48:24 +04:00
struct net_bridge_port * p , * n ;
const struct net_device_ops * ops ;
br - > dev - > npinfo = NULL ;
list_for_each_entry_safe ( p , n , & br - > port_list , list ) {
if ( p - > dev ) {
ops = p - > dev - > netdev_ops ;
if ( ops - > ndo_netpoll_cleanup )
ops - > ndo_netpoll_cleanup ( p - > dev ) ;
else
p - > dev - > npinfo = NULL ;
}
}
}
2010-05-10 13:31:08 +04:00
void br_netpoll_disable ( struct net_bridge * br ,
struct net_device * dev )
{
if ( br_devices_support_netpoll ( br ) )
br - > dev - > priv_flags & = ~ IFF_DISABLE_NETPOLL ;
if ( dev - > netdev_ops - > ndo_netpoll_cleanup )
dev - > netdev_ops - > ndo_netpoll_cleanup ( dev ) ;
else
dev - > npinfo = NULL ;
}
2010-05-06 11:48:24 +04:00
2010-05-10 13:31:08 +04:00
void br_netpoll_enable ( struct net_bridge * br ,
struct net_device * dev )
2010-05-06 11:48:24 +04:00
{
2010-05-10 13:31:08 +04:00
if ( br_devices_support_netpoll ( br ) ) {
br - > dev - > priv_flags & = ~ IFF_DISABLE_NETPOLL ;
if ( br - > dev - > npinfo )
dev - > npinfo = br - > dev - > npinfo ;
} else if ( ! ( br - > dev - > priv_flags & IFF_DISABLE_NETPOLL ) ) {
br - > dev - > priv_flags | = IFF_DISABLE_NETPOLL ;
2010-05-10 13:31:09 +04:00
br_info ( br , " new device %s does not support netpoll (disabling) " ,
dev - > name ) ;
2010-05-10 13:31:08 +04:00
}
2010-05-06 11:48:24 +04:00
}
# endif
2008-11-20 08:49:00 +03:00
static const struct ethtool_ops br_ethtool_ops = {
2008-08-16 06:51:07 +04:00
. get_drvinfo = br_getinfo ,
. get_link = ethtool_op_get_link ,
. get_tx_csum = ethtool_op_get_tx_csum ,
. set_tx_csum = br_set_tx_csum ,
. get_sg = ethtool_op_get_sg ,
. set_sg = br_set_sg ,
. get_tso = ethtool_op_get_tso ,
. set_tso = br_set_tso ,
. get_ufo = ethtool_op_get_ufo ,
2009-10-07 16:41:17 +04:00
. set_ufo = ethtool_op_set_ufo ,
2008-08-16 06:51:07 +04:00
. get_flags = ethtool_op_get_flags ,
2005-12-22 06:00:58 +03:00
} ;
2008-11-20 08:49:00 +03:00
static const struct net_device_ops br_netdev_ops = {
. ndo_open = br_dev_open ,
. ndo_stop = br_dev_stop ,
2008-11-21 07:14:53 +03:00
. ndo_start_xmit = br_dev_xmit ,
2010-03-02 16:32:09 +03:00
. ndo_get_stats = br_get_stats ,
2008-11-21 07:14:53 +03:00
. ndo_set_mac_address = br_set_mac_address ,
. ndo_set_multicast_list = br_dev_set_multicast_list ,
. ndo_change_mtu = br_change_mtu ,
. ndo_do_ioctl = br_dev_ioctl ,
2010-05-06 11:48:24 +04:00
# ifdef CONFIG_NET_POLL_CONTROLLER
. ndo_netpoll_cleanup = br_netpoll_cleanup ,
# endif
2008-11-20 08:49:00 +03:00
} ;
2010-03-02 16:32:09 +03:00
static void br_dev_free ( struct net_device * dev )
{
struct net_bridge * br = netdev_priv ( dev ) ;
free_percpu ( br - > stats ) ;
free_netdev ( dev ) ;
}
2005-04-17 02:20:36 +04:00
void br_dev_setup ( struct net_device * dev )
{
2007-12-17 00:35:51 +03:00
random_ether_addr ( dev - > dev_addr ) ;
2005-04-17 02:20:36 +04:00
ether_setup ( dev ) ;
2008-11-20 08:49:00 +03:00
dev - > netdev_ops = & br_netdev_ops ;
2010-03-02 16:32:09 +03:00
dev - > destructor = br_dev_free ;
2007-02-09 17:24:35 +03:00
SET_ETHTOOL_OPS ( dev , & br_ethtool_ops ) ;
2005-04-17 02:20:36 +04:00
dev - > tx_queue_len = 0 ;
dev - > priv_flags = IFF_EBRIDGE ;
2005-12-22 06:00:58 +03:00
2007-02-09 17:24:35 +03:00
dev - > features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
2008-09-09 03:19:58 +04:00
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
2008-10-23 12:11:29 +04:00
NETIF_F_NETNS_LOCAL | NETIF_F_GSO ;
2005-04-17 02:20:36 +04:00
}