2006-01-02 19:04:38 +01:00
/*
* net / tipc / eth_media . c : Ethernet bearer support for TIPC
2007-02-09 23:25:21 +09:00
*
2007-06-10 17:24:20 -07:00
* Copyright ( c ) 2001 - 2007 , Ericsson AB
2011-05-27 13:59:17 -04:00
* Copyright ( c ) 2005 - 2008 , 2011 , Wind River Systems
2006-01-02 19:04:38 +01:00
* All rights reserved .
*
2006-01-11 13:30:43 +01:00
* Redistribution and use in source and binary forms , with or without
2006-01-02 19:04:38 +01:00
* modification , are permitted provided that the following conditions are met :
*
2006-01-11 13:30:43 +01:00
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
2006-01-02 19:04:38 +01:00
*
2006-01-11 13:30:43 +01:00
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) version 2 as published by the Free
* Software Foundation .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
2006-01-02 19:04:38 +01:00
* POSSIBILITY OF SUCH DAMAGE .
*/
2010-11-30 12:00:53 +00:00
# include "core.h"
# include "bearer.h"
2011-05-27 15:09:40 -04:00
# define MAX_ETH_BEARERS MAX_BEARERS
2006-01-02 19:04:38 +01:00
2011-10-07 13:37:34 -04:00
# define ETH_ADDR_OFFSET 4 /* message header offset of MAC address */
2006-01-02 19:04:38 +01:00
/**
* struct eth_bearer - Ethernet bearer data structure
* @ bearer : ptr to associated " generic " bearer structure
* @ dev : ptr to associated Ethernet network device
* @ tipc_packet_type : used in binding TIPC to Ethernet driver
2011-10-19 15:18:11 -04:00
* @ cleanup : work item used when disabling bearer
2006-01-02 19:04:38 +01:00
*/
struct eth_bearer {
struct tipc_bearer * bearer ;
struct net_device * dev ;
struct packet_type tipc_packet_type ;
2011-10-19 15:18:11 -04:00
struct work_struct cleanup ;
2006-01-02 19:04:38 +01:00
} ;
2011-12-29 20:19:42 -05:00
static struct tipc_media eth_media_info ;
2006-01-02 19:04:38 +01:00
static struct eth_bearer eth_bearers [ MAX_ETH_BEARERS ] ;
2010-12-31 18:59:34 +00:00
static int eth_started ;
2006-01-02 19:04:38 +01:00
static struct notifier_block notifier ;
2011-10-07 13:37:34 -04:00
/**
* eth_media_addr_set - initialize Ethernet media address structure
2011-10-07 15:19:11 -04:00
*
* Media - dependent " value " field stores MAC address in first 6 bytes
* and zeroes out the remaining bytes .
2011-10-07 13:37:34 -04:00
*/
static void eth_media_addr_set ( struct tipc_media_addr * a , char * mac )
{
2011-10-07 15:19:11 -04:00
memcpy ( a - > value , mac , ETH_ALEN ) ;
memset ( a - > value + ETH_ALEN , 0 , sizeof ( a - > value ) - ETH_ALEN ) ;
a - > media_id = TIPC_MEDIA_TYPE_ETH ;
a - > broadcast = ! memcmp ( mac , eth_media_info . bcast_addr . value , ETH_ALEN ) ;
2011-10-07 13:37:34 -04:00
}
2006-01-02 19:04:38 +01:00
/**
2007-02-09 23:25:21 +09:00
* send_msg - send a TIPC message out over an Ethernet interface
2006-01-02 19:04:38 +01:00
*/
2007-02-09 23:25:21 +09:00
static int send_msg ( struct sk_buff * buf , struct tipc_bearer * tb_ptr ,
2006-01-02 19:04:38 +01:00
struct tipc_media_addr * dest )
{
struct sk_buff * clone ;
struct net_device * dev ;
2010-09-03 08:33:40 +00:00
int delta ;
2006-01-02 19:04:38 +01:00
clone = skb_clone ( buf , GFP_ATOMIC ) ;
2010-09-03 08:33:40 +00:00
if ( ! clone )
return 0 ;
dev = ( ( struct eth_bearer * ) ( tb_ptr - > usr_handle ) ) - > dev ;
delta = dev - > hard_header_len - skb_headroom ( buf ) ;
if ( ( delta > 0 ) & &
pskb_expand_head ( clone , SKB_DATA_ALIGN ( delta ) , 0 , GFP_ATOMIC ) ) {
kfree_skb ( clone ) ;
return 0 ;
2006-01-02 19:04:38 +01:00
}
2010-09-03 08:33:40 +00:00
skb_reset_network_header ( clone ) ;
clone - > dev = dev ;
2011-10-07 15:19:11 -04:00
dev_hard_header ( clone , dev , ETH_P_TIPC , dest - > value ,
2010-09-03 08:33:40 +00:00
dev - > dev_addr , clone - > len ) ;
dev_queue_xmit ( clone ) ;
2008-07-14 22:44:01 -07:00
return 0 ;
2006-01-02 19:04:38 +01:00
}
/**
* recv_msg - handle incoming TIPC message from an Ethernet interface
2007-02-09 23:25:21 +09:00
*
2007-06-10 17:24:20 -07:00
* Accept only packets explicitly sent to this node , or broadcast packets ;
* ignores packets sent using Ethernet multicast , and traffic sent to other
* nodes ( which can happen if interface is running in promiscuous mode ) .
2006-01-02 19:04:38 +01:00
*/
2007-02-09 23:25:21 +09:00
static int recv_msg ( struct sk_buff * buf , struct net_device * dev ,
2006-01-02 19:04:38 +01:00
struct packet_type * pt , struct net_device * orig_dev )
{
struct eth_bearer * eb_ptr = ( struct eth_bearer * ) pt - > af_packet_priv ;
2008-07-19 22:34:43 -07:00
if ( ! net_eq ( dev_net ( dev ) , & init_net ) ) {
2007-09-17 11:53:39 -07:00
kfree_skb ( buf ) ;
return 0 ;
}
2006-01-02 19:04:38 +01:00
if ( likely ( eb_ptr - > bearer ) ) {
2007-06-10 17:24:20 -07:00
if ( likely ( buf - > pkt_type < = PACKET_BROADCAST ) ) {
2010-09-08 13:31:24 +00:00
buf - > next = NULL ;
tipc_recv_msg ( buf , eb_ptr - > bearer ) ;
return 0 ;
2006-01-02 19:04:38 +01:00
}
}
2006-06-25 23:36:43 -07:00
kfree_skb ( buf ) ;
2008-07-14 22:44:01 -07:00
return 0 ;
2006-01-02 19:04:38 +01:00
}
/**
2007-02-09 23:25:21 +09:00
* enable_bearer - attach TIPC bearer to an Ethernet interface
2006-01-02 19:04:38 +01:00
*/
static int enable_bearer ( struct tipc_bearer * tb_ptr )
{
2007-05-23 15:11:15 -07:00
struct net_device * dev = NULL ;
struct net_device * pdev = NULL ;
2006-01-02 19:04:38 +01:00
struct eth_bearer * eb_ptr = & eth_bearers [ 0 ] ;
struct eth_bearer * stop = & eth_bearers [ MAX_ETH_BEARERS ] ;
char * driver_name = strchr ( ( const char * ) tb_ptr - > name , ' : ' ) + 1 ;
2010-09-03 08:33:42 +00:00
int pending_dev = 0 ;
/* Find unused Ethernet bearer structure */
while ( eb_ptr - > dev ) {
if ( ! eb_ptr - > bearer )
pending_dev + + ;
if ( + + eb_ptr = = stop )
return pending_dev ? - EAGAIN : - EDQUOT ;
}
2006-01-02 19:04:38 +01:00
/* Find device with specified name */
2011-05-27 13:59:17 -04:00
read_lock ( & dev_base_lock ) ;
2010-12-31 18:59:32 +00:00
for_each_netdev ( & init_net , pdev ) {
2007-05-23 15:11:15 -07:00
if ( ! strncmp ( pdev - > name , driver_name , IFNAMSIZ ) ) {
2007-05-03 15:13:45 -07:00
dev = pdev ;
2011-05-27 13:59:17 -04:00
dev_hold ( dev ) ;
2007-05-03 15:13:45 -07:00
break ;
}
2007-05-23 15:11:15 -07:00
}
2011-05-27 13:59:17 -04:00
read_unlock ( & dev_base_lock ) ;
2006-01-02 19:04:38 +01:00
if ( ! dev )
return - ENODEV ;
2011-05-27 14:02:48 -04:00
/* Create Ethernet bearer for device */
eb_ptr - > dev = dev ;
eb_ptr - > tipc_packet_type . type = htons ( ETH_P_TIPC ) ;
eb_ptr - > tipc_packet_type . dev = dev ;
eb_ptr - > tipc_packet_type . func = recv_msg ;
eb_ptr - > tipc_packet_type . af_packet_priv = eb_ptr ;
INIT_LIST_HEAD ( & ( eb_ptr - > tipc_packet_type . list ) ) ;
dev_add_pack ( & eb_ptr - > tipc_packet_type ) ;
2006-01-02 19:04:38 +01:00
/* Associate TIPC bearer with Ethernet bearer */
eb_ptr - > bearer = tb_ptr ;
tb_ptr - > usr_handle = ( void * ) eb_ptr ;
tb_ptr - > mtu = dev - > mtu ;
2007-02-09 23:25:21 +09:00
tb_ptr - > blocked = 0 ;
2011-10-07 13:37:34 -04:00
eth_media_addr_set ( & tb_ptr - > addr , ( char * ) dev - > dev_addr ) ;
2006-01-02 19:04:38 +01:00
return 0 ;
}
2011-10-19 15:18:11 -04:00
/**
* cleanup_bearer - break association between Ethernet bearer and interface
*
* This routine must be invoked from a work queue because it can sleep .
*/
static void cleanup_bearer ( struct work_struct * work )
{
struct eth_bearer * eb_ptr =
container_of ( work , struct eth_bearer , cleanup ) ;
dev_remove_pack ( & eb_ptr - > tipc_packet_type ) ;
dev_put ( eb_ptr - > dev ) ;
eb_ptr - > dev = NULL ;
}
2006-01-02 19:04:38 +01:00
/**
2007-02-09 23:25:21 +09:00
* disable_bearer - detach TIPC bearer from an Ethernet interface
2006-01-02 19:04:38 +01:00
*
2011-10-19 15:18:11 -04:00
* Mark Ethernet bearer as inactive so that incoming buffers are thrown away ,
* then get worker thread to complete bearer cleanup . ( Can ' t do cleanup
* here because cleanup code needs to sleep and caller holds spinlocks . )
2006-01-02 19:04:38 +01:00
*/
static void disable_bearer ( struct tipc_bearer * tb_ptr )
{
2011-10-19 15:18:11 -04:00
struct eth_bearer * eb_ptr = ( struct eth_bearer * ) tb_ptr - > usr_handle ;
eb_ptr - > bearer = NULL ;
INIT_WORK ( & eb_ptr - > cleanup , cleanup_bearer ) ;
schedule_work ( & eb_ptr - > cleanup ) ;
2006-01-02 19:04:38 +01:00
}
/**
* recv_notification - handle device updates from OS
*
2007-02-09 23:25:21 +09:00
* Change the state of the Ethernet bearer ( if any ) associated with the
2006-01-02 19:04:38 +01:00
* specified device .
*/
2007-02-09 23:25:21 +09:00
static int recv_notification ( struct notifier_block * nb , unsigned long evt ,
2006-01-02 19:04:38 +01:00
void * dv )
{
struct net_device * dev = ( struct net_device * ) dv ;
struct eth_bearer * eb_ptr = & eth_bearers [ 0 ] ;
struct eth_bearer * stop = & eth_bearers [ MAX_ETH_BEARERS ] ;
2008-07-19 22:34:43 -07:00
if ( ! net_eq ( dev_net ( dev ) , & init_net ) )
2007-09-12 13:02:17 +02:00
return NOTIFY_DONE ;
2006-01-02 19:04:38 +01:00
while ( ( eb_ptr - > dev ! = dev ) ) {
if ( + + eb_ptr = = stop )
return NOTIFY_DONE ; /* couldn't find device */
}
if ( ! eb_ptr - > bearer )
return NOTIFY_DONE ; /* bearer had been disabled */
2007-02-09 23:25:21 +09:00
eb_ptr - > bearer - > mtu = dev - > mtu ;
2006-01-02 19:04:38 +01:00
switch ( evt ) {
case NETDEV_CHANGE :
if ( netif_carrier_ok ( dev ) )
tipc_continue ( eb_ptr - > bearer ) ;
else
tipc_block_bearer ( eb_ptr - > bearer - > name ) ;
break ;
case NETDEV_UP :
tipc_continue ( eb_ptr - > bearer ) ;
break ;
case NETDEV_DOWN :
tipc_block_bearer ( eb_ptr - > bearer - > name ) ;
break ;
case NETDEV_CHANGEMTU :
2007-02-09 23:25:21 +09:00
case NETDEV_CHANGEADDR :
2006-01-02 19:04:38 +01:00
tipc_block_bearer ( eb_ptr - > bearer - > name ) ;
2007-02-09 23:25:21 +09:00
tipc_continue ( eb_ptr - > bearer ) ;
2006-01-02 19:04:38 +01:00
break ;
case NETDEV_UNREGISTER :
2007-02-09 23:25:21 +09:00
case NETDEV_CHANGENAME :
2006-01-02 19:04:38 +01:00
tipc_disable_bearer ( eb_ptr - > bearer - > name ) ;
break ;
}
return NOTIFY_OK ;
}
/**
* eth_addr2str - convert Ethernet address to string
*/
2011-10-07 11:31:49 -04:00
static int eth_addr2str ( struct tipc_media_addr * a , char * str_buf , int str_size )
2007-02-09 23:25:21 +09:00
{
2011-10-07 11:31:49 -04:00
if ( str_size < 18 ) /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
return 1 ;
2011-10-07 15:19:11 -04:00
sprintf ( str_buf , " %pM " , a - > value ) ;
2011-10-07 11:31:49 -04:00
return 0 ;
2006-01-02 19:04:38 +01:00
}
2011-10-07 13:37:34 -04:00
/**
* eth_str2addr - convert string to Ethernet address
*/
static int eth_str2addr ( struct tipc_media_addr * a , char * str_buf )
{
char mac [ ETH_ALEN ] ;
int r ;
r = sscanf ( str_buf , " %02x:%02x:%02x:%02x:%02x:%02x " ,
( u32 * ) & mac [ 0 ] , ( u32 * ) & mac [ 1 ] , ( u32 * ) & mac [ 2 ] ,
( u32 * ) & mac [ 3 ] , ( u32 * ) & mac [ 4 ] , ( u32 * ) & mac [ 5 ] ) ;
if ( r ! = ETH_ALEN )
return 1 ;
eth_media_addr_set ( a , mac ) ;
return 0 ;
}
/**
* eth_str2addr - convert Ethernet address format to message header format
*/
static int eth_addr2msg ( struct tipc_media_addr * a , char * msg_area )
{
memset ( msg_area , 0 , TIPC_MEDIA_ADDR_SIZE ) ;
msg_area [ TIPC_MEDIA_TYPE_OFFSET ] = TIPC_MEDIA_TYPE_ETH ;
2011-10-07 15:19:11 -04:00
memcpy ( msg_area + ETH_ADDR_OFFSET , a - > value , ETH_ALEN ) ;
2011-10-07 13:37:34 -04:00
return 0 ;
}
/**
* eth_str2addr - convert message header address format to Ethernet format
*/
static int eth_msg2addr ( struct tipc_media_addr * a , char * msg_area )
{
if ( msg_area [ TIPC_MEDIA_TYPE_OFFSET ] ! = TIPC_MEDIA_TYPE_ETH )
return 1 ;
eth_media_addr_set ( a , msg_area + ETH_ADDR_OFFSET ) ;
return 0 ;
}
2011-10-06 15:28:44 -04:00
/*
* Ethernet media registration info
*/
2011-12-29 20:19:42 -05:00
static struct tipc_media eth_media_info = {
2011-10-06 15:28:44 -04:00
. send_msg = send_msg ,
. enable_bearer = enable_bearer ,
. disable_bearer = disable_bearer ,
. addr2str = eth_addr2str ,
2011-10-07 13:37:34 -04:00
. str2addr = eth_str2addr ,
. addr2msg = eth_addr2msg ,
. msg2addr = eth_msg2addr ,
2011-10-07 15:19:11 -04:00
. bcast_addr = { { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ,
TIPC_MEDIA_TYPE_ETH , 1 } ,
2011-10-06 15:28:44 -04:00
. priority = TIPC_DEF_LINK_PRI ,
. tolerance = TIPC_DEF_LINK_TOL ,
. window = TIPC_DEF_LINK_WIN ,
. type_id = TIPC_MEDIA_TYPE_ETH ,
. name = " eth "
} ;
2006-01-02 19:04:38 +01:00
/**
2006-01-18 00:38:21 +01:00
* tipc_eth_media_start - activate Ethernet bearer support
2006-01-02 19:04:38 +01:00
*
* Register Ethernet media type with TIPC bearer code . Also register
* with OS for notifications about device state changes .
*/
2006-01-18 00:38:21 +01:00
int tipc_eth_media_start ( void )
2007-02-09 23:25:21 +09:00
{
2006-01-02 19:04:38 +01:00
int res ;
if ( eth_started )
return - EINVAL ;
2011-10-06 15:28:44 -04:00
res = tipc_register_media ( & eth_media_info ) ;
2006-01-02 19:04:38 +01:00
if ( res )
return res ;
notifier . notifier_call = & recv_notification ;
notifier . priority = 0 ;
res = register_netdevice_notifier ( & notifier ) ;
if ( ! res )
eth_started = 1 ;
return res ;
}
/**
2006-01-18 00:38:21 +01:00
* tipc_eth_media_stop - deactivate Ethernet bearer support
2006-01-02 19:04:38 +01:00
*/
2006-01-18 00:38:21 +01:00
void tipc_eth_media_stop ( void )
2006-01-02 19:04:38 +01:00
{
if ( ! eth_started )
return ;
2011-10-19 15:18:11 -04:00
flush_scheduled_work ( ) ;
2006-01-02 19:04:38 +01:00
unregister_netdevice_notifier ( & notifier ) ;
eth_started = 0 ;
}