2005-04-16 15:20:36 -07: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"
2006-03-20 22:59:21 -08:00
/* Bridge group multicast address 802.1d (pg 51). */
const u8 br_group_address [ ETH_ALEN ] = { 0x01 , 0x80 , 0xc2 , 0x00 , 0x00 , 0x00 } ;
2005-04-16 15:20:36 -07:00
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-20 22:57:18 -08:00
netif_receive_skb ) ;
2005-04-16 15:20:36 -07: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-09 17:08:52 -08:00
struct net_bridge_port * p = rcu_dereference ( skb - > dev - > br_port ) ;
struct net_bridge * br ;
2005-04-16 15:20:36 -07:00
struct net_bridge_fdb_entry * dst ;
int passedup = 0 ;
2006-02-09 17:08:52 -08:00
if ( ! p | | p - > state = = BR_STATE_DISABLED )
goto drop ;
2005-05-29 14:15:55 -07:00
/* insert into forwarding database after filtering to avoid spoofing */
2006-02-09 17:08:52 -08:00
br = p - > br ;
br_fdb_update ( br , p , eth_hdr ( skb ) - > h_source ) ;
2005-05-29 14:15:55 -07:00
2006-02-09 17:08:52 -08:00
if ( p - > state = = BR_STATE_LEARNING )
goto drop ;
2005-12-21 19:00:18 -08:00
2005-04-16 15:20:36 -07: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-06 13:05:58 -08:00
if ( is_multicast_ether_addr ( dest ) ) {
2006-05-05 17:07:13 -07:00
br - > statistics . multicast + + ;
2005-04-16 15:20:36 -07: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-09 17:08:52 -08:00
drop :
kfree_skb ( skb ) ;
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-03-20 22:59:06 -08:00
/* note: already called with rcu_read_lock (preempt_disabled) */
static int br_handle_local_finish ( struct sk_buff * skb )
{
struct net_bridge_port * p = rcu_dereference ( skb - > dev - > br_port ) ;
if ( p & & p - > state ! = BR_STATE_DISABLED )
br_fdb_update ( p - > br , p , eth_hdr ( skb ) - > h_source ) ;
return 0 ; /* process further */
}
/* Does address match the link local multicast address.
* 01 : 80 : c2 : 00 : 00 : 0 X
*/
2006-03-20 23:00:56 -08:00
static inline int is_link_local ( const unsigned char * dest )
2006-03-20 22:59:06 -08:00
{
2007-03-12 16:25:32 -07:00
const u16 * a = ( const u16 * ) dest ;
static const u16 * const b = ( const u16 * const ) br_group_address ;
static const u16 m = __constant_cpu_to_be16 ( 0xfff0 ) ;
return ( ( a [ 0 ] ^ b [ 0 ] ) | ( a [ 1 ] ^ b [ 1 ] ) | ( ( a [ 2 ] ^ b [ 2 ] ) & m ) ) = = 0 ;
2006-03-20 22:59:06 -08:00
}
2005-04-16 15:20:36 -07:00
/*
* Called via br_handle_frame_hook .
2007-03-21 13:38:47 -07:00
* Return NULL if skb is handled
2007-02-09 23:24:35 +09:00
* note : already called with rcu_read_lock ( preempt_disabled )
2005-04-16 15:20:36 -07:00
*/
2007-03-21 13:38:47 -07:00
struct sk_buff * br_handle_frame ( struct net_bridge_port * p , struct sk_buff * skb )
2005-04-16 15:20:36 -07:00
{
const unsigned char * dest = eth_hdr ( skb ) - > h_dest ;
if ( ! is_valid_ether_addr ( eth_hdr ( skb ) - > h_source ) )
goto err ;
2006-03-20 22:59:06 -08:00
if ( unlikely ( is_link_local ( dest ) ) ) {
skb - > pkt_type = PACKET_HOST ;
2007-03-21 13:38:47 -07:00
return ( NF_HOOK ( PF_BRIDGE , NF_BR_LOCAL_IN , skb , skb - > dev ,
NULL , br_handle_local_finish ) = = 0 ) ? skb : NULL ;
2005-04-16 15:20:36 -07:00
}
2005-12-21 19:00:18 -08:00
if ( p - > state = = BR_STATE_FORWARDING | | p - > state = = BR_STATE_LEARNING ) {
2005-04-16 15:20:36 -07:00
if ( br_should_route_hook ) {
2007-03-21 13:38:47 -07:00
if ( br_should_route_hook ( & skb ) )
return skb ;
2005-04-16 15:20:36 -07:00
dest = eth_hdr ( skb ) - > h_dest ;
}
2005-10-25 15:04:59 -07:00
if ( ! compare_ether_addr ( p - > br - > dev - > dev_addr , dest ) )
2005-04-16 15:20:36 -07:00
skb - > pkt_type = PACKET_HOST ;
NF_HOOK ( PF_BRIDGE , NF_BR_PRE_ROUTING , skb , skb - > dev , NULL ,
br_handle_frame_finish ) ;
2007-03-21 13:38:47 -07:00
return NULL ;
2005-04-16 15:20:36 -07:00
}
err :
kfree_skb ( skb ) ;
2007-03-21 13:38:47 -07:00
return NULL ;
2005-04-16 15:20:36 -07:00
}