2005-04-17 02:20:36 +04:00
/*
* Handle incoming frames
* Linux ethernet bridge
*
* Authors :
* Lennert Buytenhek < buytenh @ gnu . org >
*
* $ Id : br_input . c , v 1.10 2001 / 12 / 24 04 : 50 : 20 davem Exp $
*
* 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/etherdevice.h>
# include <linux/netfilter_bridge.h>
# include "br_private.h"
const unsigned char bridge_ula [ 6 ] = { 0x01 , 0x80 , 0xc2 , 0x00 , 0x00 , 0x00 } ;
static void br_pass_frame_up ( struct net_bridge * br , struct sk_buff * skb )
{
struct net_device * indev ;
br - > statistics . rx_packets + + ;
br - > statistics . rx_bytes + = skb - > len ;
indev = skb - > dev ;
skb - > dev = br - > dev ;
NF_HOOK ( PF_BRIDGE , NF_BR_LOCAL_IN , skb , indev , NULL ,
2006-03-21 09:57:18 +03:00
netif_receive_skb ) ;
2005-04-17 02:20:36 +04:00
}
/* note: already called with rcu_read_lock (preempt_disabled) */
int br_handle_frame_finish ( struct sk_buff * skb )
{
const unsigned char * dest = eth_hdr ( skb ) - > h_dest ;
2006-02-10 04:08:52 +03:00
struct net_bridge_port * p = rcu_dereference ( skb - > dev - > br_port ) ;
struct net_bridge * br ;
2005-04-17 02:20:36 +04:00
struct net_bridge_fdb_entry * dst ;
int passedup = 0 ;
2006-02-10 04:08:52 +03:00
if ( ! p | | p - > state = = BR_STATE_DISABLED )
goto drop ;
2005-05-30 01:15:55 +04:00
/* insert into forwarding database after filtering to avoid spoofing */
2006-02-10 04:08:52 +03:00
br = p - > br ;
br_fdb_update ( br , p , eth_hdr ( skb ) - > h_source ) ;
2005-05-30 01:15:55 +04:00
2006-02-10 04:08:52 +03:00
if ( p - > state = = BR_STATE_LEARNING )
goto drop ;
2005-12-22 06:00:18 +03:00
2005-04-17 02:20:36 +04:00
if ( br - > dev - > flags & IFF_PROMISC ) {
struct sk_buff * skb2 ;
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
if ( skb2 ! = NULL ) {
passedup = 1 ;
br_pass_frame_up ( br , skb2 ) ;
}
}
2006-01-07 00:05:58 +03:00
if ( is_multicast_ether_addr ( dest ) ) {
2005-04-17 02:20:36 +04:00
br_flood_forward ( br , skb , ! passedup ) ;
if ( ! passedup )
br_pass_frame_up ( br , skb ) ;
goto out ;
}
dst = __br_fdb_get ( br , dest ) ;
if ( dst ! = NULL & & dst - > is_local ) {
if ( ! passedup )
br_pass_frame_up ( br , skb ) ;
else
kfree_skb ( skb ) ;
goto out ;
}
if ( dst ! = NULL ) {
br_forward ( dst - > dst , skb ) ;
goto out ;
}
br_flood_forward ( br , skb , 0 ) ;
out :
return 0 ;
2006-02-10 04:08:52 +03:00
drop :
kfree_skb ( skb ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
/*
* Called via br_handle_frame_hook .
* Return 0 if * pskb should be processed furthur
* 1 if * pskb is handled
* note : already called with rcu_read_lock ( preempt_disabled )
*/
int br_handle_frame ( struct net_bridge_port * p , struct sk_buff * * pskb )
{
struct sk_buff * skb = * pskb ;
const unsigned char * dest = eth_hdr ( skb ) - > h_dest ;
if ( p - > state = = BR_STATE_DISABLED )
goto err ;
if ( ! is_valid_ether_addr ( eth_hdr ( skb ) - > h_source ) )
goto err ;
if ( p - > br - > stp_enabled & &
! memcmp ( dest , bridge_ula , 5 ) & &
! ( dest [ 5 ] & 0xF0 ) ) {
if ( ! dest [ 5 ] ) {
NF_HOOK ( PF_BRIDGE , NF_BR_LOCAL_IN , skb , skb - > dev ,
NULL , br_stp_handle_bpdu ) ;
return 1 ;
}
2005-12-22 06:00:18 +03:00
goto err ;
2005-04-17 02:20:36 +04:00
}
2005-12-22 06:00:18 +03:00
if ( p - > state = = BR_STATE_FORWARDING | | p - > state = = BR_STATE_LEARNING ) {
2005-04-17 02:20:36 +04:00
if ( br_should_route_hook ) {
if ( br_should_route_hook ( pskb ) )
return 0 ;
skb = * pskb ;
dest = eth_hdr ( skb ) - > h_dest ;
}
2005-10-26 02:04:59 +04:00
if ( ! compare_ether_addr ( p - > br - > dev - > dev_addr , dest ) )
2005-04-17 02:20:36 +04:00
skb - > pkt_type = PACKET_HOST ;
NF_HOOK ( PF_BRIDGE , NF_BR_PRE_ROUTING , skb , skb - > dev , NULL ,
br_handle_frame_finish ) ;
return 1 ;
}
err :
kfree_skb ( skb ) ;
return 1 ;
}