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"
2006-03-21 09:59:21 +03:00
/* Bridge group multicast address 802.1d (pg 51). */
const u8 br_group_address [ ETH_ALEN ] = { 0x01 , 0x80 , 0xc2 , 0x00 , 0x00 , 0x00 } ;
2005-04-17 02:20:36 +04: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-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 ;
2007-09-17 03:20:48 +04:00
struct sk_buff * skb2 ;
2005-04-17 02:20:36 +04:00
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
2007-09-17 03:20:48 +04:00
/* The packet skb2 goes to the local host (NULL to skip). */
skb2 = NULL ;
2005-04-17 02:20:36 +04:00
2007-09-17 03:20:48 +04:00
if ( br - > dev - > flags & IFF_PROMISC )
skb2 = skb ;
dst = NULL ;
2005-04-17 02:20:36 +04:00
2006-01-07 00:05:58 +03:00
if ( is_multicast_ether_addr ( dest ) ) {
2006-05-06 04:07:13 +04:00
br - > statistics . multicast + + ;
2007-09-17 03:20:48 +04:00
skb2 = skb ;
} else if ( ( dst = __br_fdb_get ( br , dest ) ) & & dst - > is_local ) {
skb2 = skb ;
/* Do not forward the packet since it's local. */
skb = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-09-17 03:20:48 +04:00
if ( skb2 = = skb )
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
2007-09-17 03:20:48 +04:00
if ( skb2 )
br_pass_frame_up ( br , skb2 ) ;
2005-04-17 02:20:36 +04:00
2007-09-17 03:20:48 +04:00
if ( skb ) {
if ( dst )
br_forward ( dst - > dst , skb ) ;
else
br_flood_forward ( br , skb ) ;
}
2005-04-17 02:20:36 +04:00
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
}
2006-03-21 09:59:06 +03: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 ) ;
2007-08-31 09:15:35 +04:00
if ( p )
2006-03-21 09:59:06 +03:00
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-21 10:00:56 +03:00
static inline int is_link_local ( const unsigned char * dest )
2006-03-21 09:59:06 +03:00
{
2007-07-26 20:33:39 +04:00
__be16 * a = ( __be16 * ) dest ;
static const __be16 * b = ( const __be16 * ) br_group_address ;
static const __be16 m = __constant_cpu_to_be16 ( 0xfff0 ) ;
2007-03-13 02:25:32 +03:00
return ( ( a [ 0 ] ^ b [ 0 ] ) | ( a [ 1 ] ^ b [ 1 ] ) | ( ( a [ 2 ] ^ b [ 2 ] ) & m ) ) = = 0 ;
2006-03-21 09:59:06 +03:00
}
2005-04-17 02:20:36 +04:00
/*
* Called via br_handle_frame_hook .
2007-03-21 23:38:47 +03:00
* Return NULL if skb is handled
2007-02-09 17:24:35 +03:00
* note : already called with rcu_read_lock ( preempt_disabled )
2005-04-17 02:20:36 +04:00
*/
2007-03-21 23:38:47 +03:00
struct sk_buff * br_handle_frame ( struct net_bridge_port * p , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
const unsigned char * dest = eth_hdr ( skb ) - > h_dest ;
if ( ! is_valid_ether_addr ( eth_hdr ( skb ) - > h_source ) )
2007-03-21 23:42:06 +03:00
goto drop ;
2005-04-17 02:20:36 +04:00
2007-04-26 09:05:55 +04:00
if ( unlikely ( is_link_local ( dest ) ) ) {
/* Pause frames shouldn't be passed up by driver anyway */
if ( skb - > protocol = = htons ( ETH_P_PAUSE ) )
goto drop ;
2007-04-26 09:07:58 +04:00
/* Process STP BPDU's through normal netif_receive_skb() path */
if ( p - > br - > stp_enabled ! = BR_NO_STP ) {
if ( NF_HOOK ( PF_BRIDGE , NF_BR_LOCAL_IN , skb , skb - > dev ,
NULL , br_handle_local_finish ) )
return NULL ;
else
return skb ;
}
2007-04-26 09:05:55 +04:00
}
2005-04-17 02:20:36 +04:00
2007-03-21 23:42:06 +03:00
switch ( p - > state ) {
case BR_STATE_FORWARDING :
2005-04-17 02:20:36 +04:00
if ( br_should_route_hook ) {
2007-03-21 23:38:47 +03:00
if ( br_should_route_hook ( & skb ) )
return skb ;
2005-04-17 02:20:36 +04:00
dest = eth_hdr ( skb ) - > h_dest ;
}
2007-03-21 23:42:06 +03:00
/* fall through */
case BR_STATE_LEARNING :
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 ) ;
2007-03-21 23:42:06 +03:00
break ;
default :
drop :
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-21 23:38:47 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}