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 .
*/
2010-02-27 22:41:43 +03:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# 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"
2010-03-17 00:37:47 +03:00
static int deliver_clone ( const struct net_bridge_port * prev ,
struct sk_buff * skb ,
2010-03-16 10:26:22 +03:00
void ( * __packet_hook ) ( const struct net_bridge_port * p ,
struct sk_buff * skb ) ) ;
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 )
{
2009-08-13 10:55:16 +04:00
return ( ( ( p - > flags & BR_HAIRPIN_MODE ) | | 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 )
{
2010-03-23 06:07:21 +03:00
return NF_HOOK ( NFPROTO_BRIDGE , NF_BR_POST_ROUTING , skb , NULL , skb - > dev ,
2006-05-26 02:58:54 +04:00
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 ;
2010-03-23 06:07:21 +03:00
NF_HOOK ( NFPROTO_BRIDGE , NF_BR_LOCAL_OUT , skb , NULL , skb - > dev ,
br_forward_finish ) ;
2005-04-17 02:20:36 +04:00
}
static void __br_forward ( const struct net_bridge_port * to , struct sk_buff * skb )
{
struct net_device * indev ;
2009-02-10 02:07:18 +03:00
if ( skb_warn_if_lro ( skb ) ) {
kfree_skb ( skb ) ;
return ;
}
2005-04-17 02:20:36 +04:00
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
2010-03-23 06:07:21 +03:00
NF_HOOK ( NFPROTO_BRIDGE , NF_BR_FORWARD , skb , indev , skb - > dev ,
br_forward_finish ) ;
2005-04-17 02:20:36 +04:00
}
/* 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 */
2010-03-16 10:26:22 +03:00
void br_forward ( const struct net_bridge_port * to , struct sk_buff * skb , struct sk_buff * skb0 )
2005-04-17 02:20:36 +04:00
{
2009-02-10 02:07:18 +03:00
if ( should_deliver ( to , skb ) ) {
2010-03-16 10:26:22 +03:00
if ( skb0 )
deliver_clone ( to , skb , __br_forward ) ;
else
__br_forward ( to , skb ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2010-03-16 10:26:22 +03:00
if ( ! skb0 )
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2010-03-17 00:37:47 +03:00
static int deliver_clone ( const struct net_bridge_port * prev ,
struct sk_buff * skb ,
2010-02-27 22:41:43 +03:00
void ( * __packet_hook ) ( const struct net_bridge_port * p ,
struct sk_buff * skb ) )
{
skb = skb_clone ( skb , GFP_ATOMIC ) ;
if ( ! skb ) {
struct net_device * dev = BR_INPUT_SKB_CB ( skb ) - > brdev ;
dev - > stats . tx_dropped + + ;
return - ENOMEM ;
}
__packet_hook ( prev , skb ) ;
return 0 ;
}
static struct net_bridge_port * maybe_deliver (
struct net_bridge_port * prev , struct net_bridge_port * p ,
struct sk_buff * skb ,
void ( * __packet_hook ) ( const struct net_bridge_port * p ,
struct sk_buff * skb ) )
{
int err ;
if ( ! should_deliver ( p , skb ) )
return prev ;
if ( ! prev )
goto out ;
err = deliver_clone ( prev , skb , __packet_hook ) ;
if ( err )
return ERR_PTR ( err ) ;
out :
return p ;
}
2005-04-17 02:20:36 +04:00
/* called under bridge lock */
2007-09-17 03:20:48 +04:00
static void br_flood ( struct net_bridge * br , struct sk_buff * skb ,
2010-02-27 22:41:41 +03:00
struct sk_buff * skb0 ,
void ( * __packet_hook ) ( const struct net_bridge_port * p ,
struct sk_buff * skb ) )
2005-04-17 02:20:36 +04:00
{
struct net_bridge_port * p ;
struct net_bridge_port * prev ;
prev = NULL ;
list_for_each_entry_rcu ( p , & br - > port_list , list ) {
2010-02-27 22:41:43 +03:00
prev = maybe_deliver ( prev , p , skb , __packet_hook ) ;
if ( IS_ERR ( prev ) )
goto out ;
2005-04-17 02:20:36 +04:00
}
2010-02-27 22:41:41 +03:00
if ( ! prev )
goto out ;
2010-02-27 22:41:43 +03:00
if ( skb0 )
deliver_clone ( prev , skb , __packet_hook ) ;
else
__packet_hook ( prev , skb ) ;
2010-02-27 22:41:41 +03:00
return ;
2005-04-17 02:20:36 +04:00
2010-02-27 22:41:41 +03:00
out :
if ( ! skb0 )
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
/* 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
{
2010-02-27 22:41:41 +03:00
br_flood ( br , skb , NULL , __br_deliver ) ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
2010-02-27 22:41:41 +03:00
void br_flood_forward ( struct net_bridge * br , struct sk_buff * skb ,
struct sk_buff * skb2 )
2005-04-17 02:20:36 +04:00
{
2010-02-27 22:41:41 +03:00
br_flood ( br , skb , skb2 , __br_forward ) ;
2005-04-17 02:20:36 +04:00
}
2010-02-27 22:41:46 +03:00
# ifdef CONFIG_BRIDGE_IGMP_SNOOPING
/* called with rcu_read_lock */
static void br_multicast_flood ( struct net_bridge_mdb_entry * mdst ,
struct sk_buff * skb , struct sk_buff * skb0 ,
void ( * __packet_hook ) (
const struct net_bridge_port * p ,
struct sk_buff * skb ) )
{
struct net_device * dev = BR_INPUT_SKB_CB ( skb ) - > brdev ;
struct net_bridge * br = netdev_priv ( dev ) ;
struct net_bridge_port * port ;
struct net_bridge_port * lport , * rport ;
struct net_bridge_port * prev ;
struct net_bridge_port_group * p ;
struct hlist_node * rp ;
prev = NULL ;
rp = br - > router_list . first ;
p = mdst ? mdst - > ports : NULL ;
while ( p | | rp ) {
lport = p ? p - > port : NULL ;
rport = rp ? hlist_entry ( rp , struct net_bridge_port , rlist ) :
NULL ;
port = ( unsigned long ) lport > ( unsigned long ) rport ?
lport : rport ;
prev = maybe_deliver ( prev , port , skb , __packet_hook ) ;
if ( IS_ERR ( prev ) )
goto out ;
if ( ( unsigned long ) lport > = ( unsigned long ) port )
p = p - > next ;
if ( ( unsigned long ) rport > = ( unsigned long ) port )
rp = rp - > next ;
}
if ( ! prev )
goto out ;
if ( skb0 )
deliver_clone ( prev , skb , __packet_hook ) ;
else
__packet_hook ( prev , skb ) ;
return ;
out :
if ( ! skb0 )
kfree_skb ( skb ) ;
}
/* called with rcu_read_lock */
void br_multicast_deliver ( struct net_bridge_mdb_entry * mdst ,
struct sk_buff * skb )
{
br_multicast_flood ( mdst , skb , NULL , __br_deliver ) ;
}
/* called with rcu_read_lock */
void br_multicast_forward ( struct net_bridge_mdb_entry * mdst ,
struct sk_buff * skb , struct sk_buff * skb2 )
{
br_multicast_flood ( mdst , skb , skb2 , __br_forward ) ;
}
# endif