2005-04-17 02:20:36 +04:00
/*
* Forwarding decision
* 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/skbuff.h>
2006-04-26 13:39:19 +04:00
# include <linux/if_vlan.h>
2005-04-17 02:20:36 +04:00
# include <linux/netfilter_bridge.h>
# include "br_private.h"
2006-05-26 02:58:54 +04:00
/* Don't forward packets to originating port or forwarding diasabled */
2007-02-09 17:24:35 +03:00
static inline int should_deliver ( const struct net_bridge_port * p ,
2005-04-17 02:20:36 +04:00
const struct sk_buff * skb )
{
2006-05-26 02:58:54 +04:00
return ( skb - > dev ! = p - > dev & & p - > state = = BR_STATE_FORWARDING ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-26 13:39:19 +04:00
static inline unsigned packet_length ( const struct sk_buff * skb )
{
return skb - > len - ( skb - > protocol = = htons ( ETH_P_8021Q ) ? VLAN_HLEN : 0 ) ;
}
2005-04-17 02:20:36 +04:00
int br_dev_queue_push_xmit ( struct sk_buff * skb )
{
2006-06-22 13:40:14 +04:00
/* drop mtu oversized packets except gso */
2006-07-09 00:34:32 +04:00
if ( packet_length ( skb ) > skb - > dev - > mtu & & ! skb_is_gso ( skb ) )
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
else {
/* ip_refrag calls ip_fragment, doesn't copy the MAC header. */
2006-08-27 07:28:30 +04:00
if ( nf_bridge_maybe_copy_header ( skb ) )
kfree_skb ( skb ) ;
2006-08-30 04:48:17 +04:00
else {
2006-08-27 07:28:30 +04:00
skb_push ( skb , ETH_HLEN ) ;
2005-04-17 02:20:36 +04:00
2006-08-27 07:28:30 +04:00
dev_queue_xmit ( skb ) ;
}
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
int br_forward_finish ( struct sk_buff * skb )
{
2006-05-26 02:58:54 +04:00
return NF_HOOK ( PF_BRIDGE , NF_BR_POST_ROUTING , skb , NULL , skb - > dev ,
br_dev_queue_push_xmit ) ;
2005-04-17 02:20:36 +04:00
}
static void __br_deliver ( const struct net_bridge_port * to , struct sk_buff * skb )
{
skb - > dev = to - > dev ;
NF_HOOK ( PF_BRIDGE , NF_BR_LOCAL_OUT , skb , NULL , skb - > dev ,
br_forward_finish ) ;
}
static void __br_forward ( const struct net_bridge_port * to , struct sk_buff * skb )
{
struct net_device * indev ;
indev = skb - > dev ;
skb - > dev = to - > dev ;
2007-03-27 10:22:20 +04:00
skb_forward_csum ( skb ) ;
2005-04-17 02:20:36 +04:00
NF_HOOK ( PF_BRIDGE , NF_BR_FORWARD , skb , indev , skb - > dev ,
br_forward_finish ) ;
}
/* called with rcu_read_lock */
void br_deliver ( const struct net_bridge_port * to , struct sk_buff * skb )
{
if ( should_deliver ( to , skb ) ) {
__br_deliver ( to , skb ) ;
return ;
}
kfree_skb ( skb ) ;
}
/* called with rcu_read_lock */
void br_forward ( const struct net_bridge_port * to , struct sk_buff * skb )
{
2008-06-20 03:22:28 +04:00
if ( ! skb_warn_if_lro ( skb ) & & should_deliver ( to , skb ) ) {
2005-04-17 02:20:36 +04:00
__br_forward ( to , skb ) ;
return ;
}
kfree_skb ( skb ) ;
}
/* called under bridge lock */
2007-09-17 03:20:48 +04:00
static void br_flood ( struct net_bridge * br , struct sk_buff * skb ,
2007-02-09 17:24:35 +03:00
void ( * __packet_hook ) ( const struct net_bridge_port * p ,
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ) )
{
struct net_bridge_port * p ;
struct net_bridge_port * prev ;
prev = NULL ;
list_for_each_entry_rcu ( p , & br - > port_list , list ) {
if ( should_deliver ( p , skb ) ) {
if ( prev ! = NULL ) {
struct sk_buff * skb2 ;
if ( ( skb2 = skb_clone ( skb , GFP_ATOMIC ) ) = = NULL ) {
2008-05-22 01:13:47 +04:00
br - > dev - > stats . tx_dropped + + ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return ;
}
__packet_hook ( prev , skb2 ) ;
}
prev = p ;
}
}
if ( prev ! = NULL ) {
__packet_hook ( prev , skb ) ;
return ;
}
kfree_skb ( skb ) ;
}
/* called with rcu_read_lock */
2007-09-17 03:20:48 +04:00
void br_flood_deliver ( struct net_bridge * br , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
2007-09-17 03:20:48 +04:00
br_flood ( br , skb , __br_deliver ) ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
2007-09-17 03:20:48 +04:00
void br_flood_forward ( struct net_bridge * br , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
2007-09-17 03:20:48 +04:00
br_flood ( br , skb , __br_forward ) ;
2005-04-17 02:20:36 +04:00
}