2005-04-17 02:20:36 +04:00
/*
* " LAPB via ethernet " driver release 001
*
* This code REQUIRES 2.1 .15 or higher / NET3 .038
*
* This module :
* This module 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 .
*
* This is a " pseudo " network driver to allow LAPB over Ethernet .
*
* This driver can use any ethernet destination address , and can be
* limited to accept frames from one dedicated ethernet card only .
*
* History
* LAPBETH 001 Jonathan Naylor Cloned from bpqether . c
* 2000 - 10 - 29 Henner Eisen lapb_data_indication ( ) return status .
* 2000 - 11 - 14 Henner Eisen dev_hold / put , NETDEV_GOING_DOWN support
*/
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/net.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <linux/skbuff.h>
# include <net/sock.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/notifier.h>
# include <linux/stat.h>
# include <linux/netfilter.h>
# include <linux/module.h>
# include <linux/lapb.h>
# include <linux/init.h>
# include <net/x25device.h>
static char bcast_addr [ 6 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
/* If this number is made larger, check that the temporary string buffer
* in lapbeth_new_device is large enough to store the probe device name . */
# define MAXLAPBDEV 100
struct lapbethdev {
struct list_head node ;
struct net_device * ethdev ; /* link to ethernet device */
struct net_device * axdev ; /* lapbeth device (lapb#) */
struct net_device_stats stats ; /* some statistics */
} ;
2008-03-29 02:16:39 +03:00
static LIST_HEAD ( lapbeth_devices ) ;
2005-04-17 02:20:36 +04:00
/* ------------------------------------------------------------------------ */
/*
* Get the LAPB device for the ethernet device
*/
static struct lapbethdev * lapbeth_get_x25_dev ( struct net_device * dev )
{
struct lapbethdev * lapbeth ;
list_for_each_entry_rcu ( lapbeth , & lapbeth_devices , node ) {
if ( lapbeth - > ethdev = = dev )
return lapbeth ;
}
return NULL ;
}
static __inline__ int dev_is_ethdev ( struct net_device * dev )
{
return dev - > type = = ARPHRD_ETHER & & strncmp ( dev - > name , " dummy " , 5 ) ;
}
/* ------------------------------------------------------------------------ */
/*
* Receive a LAPB frame via an ethernet interface .
*/
2005-08-10 06:34:12 +04:00
static int lapbeth_rcv ( struct sk_buff * skb , struct net_device * dev , struct packet_type * ptype , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
int len , err ;
struct lapbethdev * lapbeth ;
2007-09-17 22:53:39 +04:00
if ( dev - > nd_net ! = & init_net )
goto drop ;
2005-04-17 02:20:36 +04:00
if ( ( skb = skb_share_check ( skb , GFP_ATOMIC ) ) = = NULL )
return NET_RX_DROP ;
if ( ! pskb_may_pull ( skb , 2 ) )
goto drop ;
rcu_read_lock ( ) ;
lapbeth = lapbeth_get_x25_dev ( dev ) ;
if ( ! lapbeth )
goto drop_unlock ;
if ( ! netif_running ( lapbeth - > axdev ) )
goto drop_unlock ;
lapbeth - > stats . rx_packets + + ;
len = skb - > data [ 0 ] + skb - > data [ 1 ] * 256 ;
lapbeth - > stats . rx_bytes + = len ;
skb_pull ( skb , 2 ) ; /* Remove the length bytes */
skb_trim ( skb , len ) ; /* Set the length of the data */
if ( ( err = lapb_data_received ( lapbeth - > axdev , skb ) ) ! = LAPB_OK ) {
printk ( KERN_DEBUG " lapbether: lapb_data_received err - %d \n " , err ) ;
goto drop_unlock ;
}
out :
rcu_read_unlock ( ) ;
return 0 ;
drop_unlock :
kfree_skb ( skb ) ;
goto out ;
drop :
kfree_skb ( skb ) ;
return 0 ;
}
static int lapbeth_data_indication ( struct net_device * dev , struct sk_buff * skb )
{
unsigned char * ptr ;
skb_push ( skb , 1 ) ;
if ( skb_cow ( skb , 1 ) )
return NET_RX_DROP ;
ptr = skb - > data ;
* ptr = 0x00 ;
skb - > protocol = x25_type_trans ( skb , dev ) ;
skb - > dev - > last_rx = jiffies ;
return netif_rx ( skb ) ;
}
/*
* Send a LAPB frame via an ethernet interface
*/
static int lapbeth_xmit ( struct sk_buff * skb , struct net_device * dev )
{
int err = - ENODEV ;
/*
* Just to be * really * sure not to send anything if the interface
* is down , the ethernet device may have gone .
*/
if ( ! netif_running ( dev ) ) {
goto drop ;
}
switch ( skb - > data [ 0 ] ) {
case 0x00 :
err = 0 ;
break ;
case 0x01 :
if ( ( err = lapb_connect_request ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " lapbeth: lapb_connect_request "
" error: %d \n " , err ) ;
goto drop_ok ;
case 0x02 :
if ( ( err = lapb_disconnect_request ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " lapbeth: lapb_disconnect_request "
" err: %d \n " , err ) ;
/* Fall thru */
default :
goto drop_ok ;
}
skb_pull ( skb , 1 ) ;
if ( ( err = lapb_data_request ( dev , skb ) ) ! = LAPB_OK ) {
printk ( KERN_ERR " lapbeth: lapb_data_request error - %d \n " , err ) ;
err = - ENOMEM ;
goto drop ;
}
err = 0 ;
out :
return err ;
drop_ok :
err = 0 ;
drop :
kfree_skb ( skb ) ;
goto out ;
}
static void lapbeth_data_transmit ( struct net_device * ndev , struct sk_buff * skb )
{
struct lapbethdev * lapbeth = netdev_priv ( ndev ) ;
unsigned char * ptr ;
struct net_device * dev ;
int size = skb - > len ;
skb - > protocol = htons ( ETH_P_X25 ) ;
ptr = skb_push ( skb , 2 ) ;
* ptr + + = size % 256 ;
* ptr + + = size / 256 ;
lapbeth - > stats . tx_packets + + ;
lapbeth - > stats . tx_bytes + = size ;
skb - > dev = dev = lapbeth - > ethdev ;
2007-10-09 12:36:32 +04:00
dev_hard_header ( skb , dev , ETH_P_DEC , bcast_addr , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
}
static void lapbeth_connected ( struct net_device * dev , int reason )
{
unsigned char * ptr ;
struct sk_buff * skb = dev_alloc_skb ( 1 ) ;
if ( ! skb ) {
printk ( KERN_ERR " lapbeth: out of memory \n " ) ;
return ;
}
ptr = skb_put ( skb , 1 ) ;
* ptr = 0x01 ;
skb - > protocol = x25_type_trans ( skb , dev ) ;
skb - > dev - > last_rx = jiffies ;
netif_rx ( skb ) ;
}
static void lapbeth_disconnected ( struct net_device * dev , int reason )
{
unsigned char * ptr ;
struct sk_buff * skb = dev_alloc_skb ( 1 ) ;
if ( ! skb ) {
printk ( KERN_ERR " lapbeth: out of memory \n " ) ;
return ;
}
ptr = skb_put ( skb , 1 ) ;
* ptr = 0x02 ;
skb - > protocol = x25_type_trans ( skb , dev ) ;
skb - > dev - > last_rx = jiffies ;
netif_rx ( skb ) ;
}
/*
* Statistics
*/
static struct net_device_stats * lapbeth_get_stats ( struct net_device * dev )
{
struct lapbethdev * lapbeth = netdev_priv ( dev ) ;
return & lapbeth - > stats ;
}
/*
* Set AX .25 callsign
*/
static int lapbeth_set_mac_address ( struct net_device * dev , void * addr )
{
struct sockaddr * sa = addr ;
memcpy ( dev - > dev_addr , sa - > sa_data , dev - > addr_len ) ;
return 0 ;
}
static struct lapb_register_struct lapbeth_callbacks = {
. connect_confirmation = lapbeth_connected ,
. connect_indication = lapbeth_connected ,
. disconnect_confirmation = lapbeth_disconnected ,
. disconnect_indication = lapbeth_disconnected ,
. data_indication = lapbeth_data_indication ,
. data_transmit = lapbeth_data_transmit ,
} ;
/*
* open / close a device
*/
static int lapbeth_open ( struct net_device * dev )
{
int err ;
if ( ( err = lapb_register ( dev , & lapbeth_callbacks ) ) ! = LAPB_OK ) {
printk ( KERN_ERR " lapbeth: lapb_register error - %d \n " , err ) ;
return - ENODEV ;
}
netif_start_queue ( dev ) ;
return 0 ;
}
static int lapbeth_close ( struct net_device * dev )
{
int err ;
netif_stop_queue ( dev ) ;
if ( ( err = lapb_unregister ( dev ) ) ! = LAPB_OK )
printk ( KERN_ERR " lapbeth: lapb_unregister error - %d \n " , err ) ;
return 0 ;
}
/* ------------------------------------------------------------------------ */
static void lapbeth_setup ( struct net_device * dev )
{
dev - > hard_start_xmit = lapbeth_xmit ;
dev - > open = lapbeth_open ;
dev - > stop = lapbeth_close ;
dev - > destructor = free_netdev ;
dev - > set_mac_address = lapbeth_set_mac_address ;
dev - > get_stats = lapbeth_get_stats ;
dev - > type = ARPHRD_X25 ;
dev - > hard_header_len = 3 ;
dev - > mtu = 1000 ;
dev - > addr_len = 0 ;
}
/*
* Setup a new device .
*/
static int lapbeth_new_device ( struct net_device * dev )
{
struct net_device * ndev ;
struct lapbethdev * lapbeth ;
int rc = - ENOMEM ;
ASSERT_RTNL ( ) ;
ndev = alloc_netdev ( sizeof ( * lapbeth ) , " lapb%d " ,
lapbeth_setup ) ;
if ( ! ndev )
goto out ;
lapbeth = netdev_priv ( ndev ) ;
lapbeth - > axdev = ndev ;
dev_hold ( dev ) ;
lapbeth - > ethdev = dev ;
rc = dev_alloc_name ( ndev , ndev - > name ) ;
if ( rc < 0 )
goto fail ;
rc = - EIO ;
if ( register_netdevice ( ndev ) )
goto fail ;
list_add_rcu ( & lapbeth - > node , & lapbeth_devices ) ;
rc = 0 ;
out :
return rc ;
fail :
dev_put ( dev ) ;
free_netdev ( ndev ) ;
kfree ( lapbeth ) ;
goto out ;
}
/*
* Free a lapb network device .
*/
static void lapbeth_free_device ( struct lapbethdev * lapbeth )
{
dev_put ( lapbeth - > ethdev ) ;
list_del_rcu ( & lapbeth - > node ) ;
unregister_netdevice ( lapbeth - > axdev ) ;
}
/*
* Handle device status changes .
*
* Called from notifier with RTNL held .
*/
static int lapbeth_device_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
struct lapbethdev * lapbeth ;
struct net_device * dev = ptr ;
2007-09-12 15:02:17 +04:00
if ( dev - > nd_net ! = & init_net )
return NOTIFY_DONE ;
2005-04-17 02:20:36 +04:00
if ( ! dev_is_ethdev ( dev ) )
return NOTIFY_DONE ;
switch ( event ) {
case NETDEV_UP :
/* New ethernet device -> new LAPB interface */
if ( lapbeth_get_x25_dev ( dev ) = = NULL )
lapbeth_new_device ( dev ) ;
break ;
case NETDEV_DOWN :
/* ethernet device closed -> close LAPB interface */
lapbeth = lapbeth_get_x25_dev ( dev ) ;
if ( lapbeth )
dev_close ( lapbeth - > axdev ) ;
break ;
case NETDEV_UNREGISTER :
/* ethernet device disappears -> remove LAPB interface */
lapbeth = lapbeth_get_x25_dev ( dev ) ;
if ( lapbeth )
lapbeth_free_device ( lapbeth ) ;
break ;
}
return NOTIFY_DONE ;
}
/* ------------------------------------------------------------------------ */
static struct packet_type lapbeth_packet_type = {
. type = __constant_htons ( ETH_P_DEC ) ,
. func = lapbeth_rcv ,
} ;
static struct notifier_block lapbeth_dev_notifier = {
. notifier_call = lapbeth_device_event ,
} ;
static char banner [ ] __initdata = KERN_INFO " LAPB Ethernet driver version 0.02 \n " ;
static int __init lapbeth_init_driver ( void )
{
dev_add_pack ( & lapbeth_packet_type ) ;
register_netdevice_notifier ( & lapbeth_dev_notifier ) ;
printk ( banner ) ;
return 0 ;
}
module_init ( lapbeth_init_driver ) ;
static void __exit lapbeth_cleanup_driver ( void )
{
struct lapbethdev * lapbeth ;
struct list_head * entry , * tmp ;
dev_remove_pack ( & lapbeth_packet_type ) ;
unregister_netdevice_notifier ( & lapbeth_dev_notifier ) ;
rtnl_lock ( ) ;
list_for_each_safe ( entry , tmp , & lapbeth_devices ) {
lapbeth = list_entry ( entry , struct lapbethdev , node ) ;
unregister_netdevice ( lapbeth - > axdev ) ;
}
rtnl_unlock ( ) ;
}
module_exit ( lapbeth_cleanup_driver ) ;
MODULE_AUTHOR ( " Jonathan Naylor <g4klx@g4klx.demon.co.uk> " ) ;
MODULE_DESCRIPTION ( " The unofficial LAPB over Ethernet driver " ) ;
MODULE_LICENSE ( " GPL " ) ;