2005-04-17 02:20:36 +04:00
/*
* G8BPQ compatible " AX.25 via ethernet " driver release 004
*
* This code REQUIRES 2.0 .0 or higher / NET3 .029
*
* 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 AX .25 over Ethernet
* using G8BPQ encapsulation . It has been extracted from the protocol
* implementation because
*
* - things got unreadable within the protocol stack
* - to cure the protocol stack from " feature-ism "
* - a protocol implementation shouldn ' t need to know on
* which hardware it is running
* - user - level programs like the AX .25 utilities shouldn ' t
* need to know about the hardware .
* - IP over ethernet encapsulated AX .25 was impossible
* - rxecho . c did not work
* - to have room for extensions
* - it just deserves to " live " as an own driver
*
* This driver can use any ethernet destination address , and can be
* limited to accept frames from one dedicated ethernet card only .
*
* Note that the driver sets up the BPQ devices automagically on
* startup or ( if started before the " insmod " of an ethernet device )
* on " ifconfig up " . It hopefully will remove the BPQ on " rmmod " ing
* the ethernet device ( in fact : as soon as another ethernet or bpq
* device gets " ifconfig " ured ) .
*
* I have heard that several people are thinking of experiments
* with highspeed packet radio using existing ethernet cards .
* Well , this driver is prepared for this purpose , just add
* your tx key control and a txdelay / tailtime algorithm ,
* probably some buffering , and / voila / . . .
*
* History
* BPQ 001 Joerg ( DL1BKE ) Extracted BPQ code from AX .25
* protocol stack and added my own
* yet existing patches
* BPQ 002 Joerg ( DL1BKE ) Scan network device list on
* startup .
* BPQ 003 Joerg ( DL1BKE ) Ethernet destination address
* and accepted source address
* can be configured by an ioctl ( )
* call .
* Fixed to match Linux networking
* changes - 2.1 .15 .
* BPQ 004 Joerg ( DL1BKE ) Fixed to not lock up on ifconfig .
*/
# 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 <net/ax25.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
2007-10-04 04:59:30 +04:00
# include <linux/etherdevice.h>
2005-04-17 02:20:36 +04:00
# 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/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/stat.h>
# include <linux/netfilter.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/rtnetlink.h>
# include <net/ip.h>
# include <net/arp.h>
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2005-04-17 02:20:36 +04:00
# include <linux/bpqether.h>
2009-02-14 14:33:21 +03:00
static const char banner [ ] __initdata = KERN_INFO \
" AX.25: bpqether driver version 004 \n " ;
2005-04-17 02:20:36 +04:00
static char bcast_addr [ 6 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
static char bpq_eth_addr [ 6 ] ;
2005-08-10 06:34:12 +04:00
static int bpq_rcv ( struct sk_buff * , struct net_device * , struct packet_type * , struct net_device * ) ;
2005-04-17 02:20:36 +04:00
static int bpq_device_event ( struct notifier_block * , unsigned long , void * ) ;
2009-03-09 11:18:29 +03:00
static struct packet_type bpq_packet_type __read_mostly = {
2009-02-01 11:45:17 +03:00
. type = cpu_to_be16 ( ETH_P_BPQ ) ,
2005-04-17 02:20:36 +04:00
. func = bpq_rcv ,
} ;
static struct notifier_block bpq_dev_notifier = {
. notifier_call = bpq_device_event ,
} ;
struct bpqdev {
struct list_head bpq_list ; /* list of bpq devices chain */
struct net_device * ethdev ; /* link to ethernet device */
struct net_device * axdev ; /* bpq device (bpq#) */
char dest_addr [ 6 ] ; /* ether destination address */
char acpt_addr [ 6 ] ; /* accept ether frames from this address only */
} ;
static LIST_HEAD ( bpq_devices ) ;
2006-07-10 22:24:46 +04:00
/*
* bpqether network devices are paired with ethernet devices below them , so
* form a special " super class " of normal ethernet devices ; split their locks
* off into a separate class since they always nest .
*/
static struct lock_class_key bpq_netdev_xmit_lock_key ;
2008-07-23 01:16:42 +04:00
static struct lock_class_key bpq_netdev_addr_lock_key ;
2005-04-17 02:20:36 +04:00
2008-07-17 11:34:19 +04:00
static void bpq_set_lockdep_class_one ( struct net_device * dev ,
struct netdev_queue * txq ,
void * _unused )
2008-07-09 10:13:53 +04:00
{
lockdep_set_class ( & txq - > _xmit_lock , & bpq_netdev_xmit_lock_key ) ;
}
static void bpq_set_lockdep_class ( struct net_device * dev )
{
2008-07-23 01:16:42 +04:00
lockdep_set_class ( & dev - > addr_list_lock , & bpq_netdev_addr_lock_key ) ;
2008-07-17 11:34:19 +04:00
netdev_for_each_tx_queue ( dev , bpq_set_lockdep_class_one , NULL ) ;
2008-07-09 10:13:53 +04:00
}
2005-04-17 02:20:36 +04:00
/* ------------------------------------------------------------------------ */
/*
* Get the ethernet device for a BPQ device
*/
static inline struct net_device * bpq_get_ether_dev ( struct net_device * dev )
{
struct bpqdev * bpq = netdev_priv ( dev ) ;
return bpq ? bpq - > ethdev : NULL ;
}
/*
* Get the BPQ device for the ethernet device
*/
static inline struct net_device * bpq_get_ax25_dev ( struct net_device * dev )
{
struct bpqdev * bpq ;
2005-10-17 15:42:32 +04:00
list_for_each_entry_rcu ( bpq , & bpq_devices , bpq_list ) {
2005-04-17 02:20:36 +04:00
if ( bpq - > ethdev = = dev )
return bpq - > axdev ;
}
return NULL ;
}
static inline int dev_is_ethdev ( struct net_device * dev )
{
return (
dev - > type = = ARPHRD_ETHER
& & strncmp ( dev - > name , " dummy " , 5 )
) ;
}
/* ------------------------------------------------------------------------ */
/*
* Receive an AX .25 frame via an ethernet interface .
*/
2005-08-10 06:34:12 +04:00
static int bpq_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 ;
char * ptr ;
struct ethhdr * eth ;
struct bpqdev * bpq ;
2008-03-25 15:47:49 +03:00
if ( dev_net ( dev ) ! = & init_net )
2007-09-17 22:53:39 +04:00
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 , sizeof ( struct ethhdr ) ) )
goto drop ;
rcu_read_lock ( ) ;
dev = bpq_get_ax25_dev ( dev ) ;
if ( dev = = NULL | | ! netif_running ( dev ) )
goto drop_unlock ;
/*
* if we want to accept frames from just one ethernet device
* we check the source address of the sender .
*/
bpq = netdev_priv ( dev ) ;
eth = eth_hdr ( skb ) ;
if ( ! ( bpq - > acpt_addr [ 0 ] & 0x01 ) & &
memcmp ( eth - > h_source , bpq - > acpt_addr , ETH_ALEN ) )
goto drop_unlock ;
if ( skb_cow ( skb , sizeof ( struct ethhdr ) ) )
goto drop_unlock ;
len = skb - > data [ 0 ] + skb - > data [ 1 ] * 256 - 5 ;
skb_pull ( skb , 2 ) ; /* Remove the length bytes */
skb_trim ( skb , len ) ; /* Set the length of the data */
2009-01-09 16:01:31 +03:00
dev - > stats . rx_packets + + ;
dev - > stats . rx_bytes + = len ;
2005-04-17 02:20:36 +04:00
ptr = skb_push ( skb , 1 ) ;
* ptr = 0 ;
2005-04-25 05:53:06 +04:00
skb - > protocol = ax25_type_trans ( skb , dev ) ;
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
unlock :
rcu_read_unlock ( ) ;
return 0 ;
drop_unlock :
kfree_skb ( skb ) ;
goto unlock ;
drop :
kfree_skb ( skb ) ;
return 0 ;
}
/*
* Send an AX .25 frame via an ethernet interface
*/
2009-08-31 23:50:43 +04:00
static netdev_tx_t bpq_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
unsigned char * ptr ;
struct bpqdev * bpq ;
int size ;
/*
* Just to be * really * sure not to send anything if the interface
* is down , the ethernet device may have gone .
*/
if ( ! netif_running ( dev ) ) {
kfree_skb ( skb ) ;
2009-06-16 03:33:24 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 10:09:29 +04:00
skb_pull ( skb , 1 ) ; /* Drop KISS byte */
2005-04-17 02:20:36 +04:00
size = skb - > len ;
/*
2009-09-03 10:09:29 +04:00
* We ' re about to mess with the skb which may still shared with the
* generic networking code so unshare and ensure it ' s got enough
* space for the BPQ headers .
2005-04-17 02:20:36 +04:00
*/
2009-09-03 10:09:29 +04:00
if ( skb_cow ( skb , AX25_BPQ_HEADER_LEN ) ) {
if ( net_ratelimit ( ) )
pr_err ( " bpqether: out of memory \n " ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
2009-09-03 10:09:29 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2009-09-03 10:09:29 +04:00
ptr = skb_push ( skb , 2 ) ; /* Make space for length */
2005-04-17 02:20:36 +04:00
* ptr + + = ( size + 5 ) % 256 ;
* ptr + + = ( size + 5 ) / 256 ;
bpq = netdev_priv ( dev ) ;
if ( ( dev = bpq_get_ether_dev ( dev ) ) = = NULL ) {
2009-01-09 16:01:31 +03:00
dev - > stats . tx_dropped + + ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
2009-06-12 09:20:01 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2005-04-25 05:53:06 +04:00
skb - > protocol = ax25_type_trans ( skb , dev ) ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2007-10-09 12:36:32 +04:00
dev_hard_header ( skb , dev , ETH_P_BPQ , bpq - > dest_addr , NULL , 0 ) ;
2009-01-09 16:01:31 +03:00
dev - > stats . tx_packets + + ;
dev - > stats . tx_bytes + = skb - > len ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
netif_wake_queue ( dev ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
/*
* Set AX .25 callsign
*/
static int bpq_set_mac_address ( struct net_device * dev , void * addr )
{
struct sockaddr * sa = ( struct sockaddr * ) addr ;
memcpy ( dev - > dev_addr , sa - > sa_data , dev - > addr_len ) ;
return 0 ;
}
/* Ioctl commands
*
* SIOCSBPQETHOPT reserved for enhancements
* SIOCSBPQETHADDR set the destination and accepted
* source ethernet address ( broadcast
* or multicast : accept all )
*/
static int bpq_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
struct bpq_ethaddr __user * ethaddr = ifr - > ifr_data ;
struct bpqdev * bpq = netdev_priv ( dev ) ;
struct bpq_req req ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
switch ( cmd ) {
case SIOCSBPQETHOPT :
if ( copy_from_user ( & req , ifr - > ifr_data , sizeof ( struct bpq_req ) ) )
return - EFAULT ;
switch ( req . cmd ) {
case SIOCGBPQETHPARAM :
case SIOCSBPQETHPARAM :
default :
return - EINVAL ;
}
break ;
case SIOCSBPQETHADDR :
if ( copy_from_user ( bpq - > dest_addr , ethaddr - > destination , ETH_ALEN ) )
return - EFAULT ;
if ( copy_from_user ( bpq - > acpt_addr , ethaddr - > accept , ETH_ALEN ) )
return - EFAULT ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*
* open / close a device
*/
static int bpq_open ( struct net_device * dev )
{
netif_start_queue ( dev ) ;
return 0 ;
}
static int bpq_close ( struct net_device * dev )
{
netif_stop_queue ( dev ) ;
return 0 ;
}
/* ------------------------------------------------------------------------ */
/*
* Proc filesystem
*/
static void * bpq_seq_start ( struct seq_file * seq , loff_t * pos )
2009-02-14 14:34:52 +03:00
__acquires ( RCU )
2005-04-17 02:20:36 +04:00
{
int i = 1 ;
struct bpqdev * bpqdev ;
rcu_read_lock ( ) ;
if ( * pos = = 0 )
return SEQ_START_TOKEN ;
2005-10-17 15:42:32 +04:00
list_for_each_entry_rcu ( bpqdev , & bpq_devices , bpq_list ) {
2005-04-17 02:20:36 +04:00
if ( i = = * pos )
return bpqdev ;
}
return NULL ;
}
static void * bpq_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct list_head * p ;
+ + * pos ;
if ( v = = SEQ_START_TOKEN )
2007-07-31 11:38:48 +04:00
p = rcu_dereference ( bpq_devices . next ) ;
2005-04-17 02:20:36 +04:00
else
2007-07-31 11:38:48 +04:00
p = rcu_dereference ( ( ( struct bpqdev * ) v ) - > bpq_list . next ) ;
2005-04-17 02:20:36 +04:00
return ( p = = & bpq_devices ) ? NULL
2007-07-31 11:38:48 +04:00
: list_entry ( p , struct bpqdev , bpq_list ) ;
2005-04-17 02:20:36 +04:00
}
static void bpq_seq_stop ( struct seq_file * seq , void * v )
2009-02-14 14:34:52 +03:00
__releases ( RCU )
2005-04-17 02:20:36 +04:00
{
rcu_read_unlock ( ) ;
}
static int bpq_seq_show ( struct seq_file * seq , void * v )
{
if ( v = = SEQ_START_TOKEN )
seq_puts ( seq ,
" dev ether destination accept from \n " ) ;
else {
const struct bpqdev * bpqdev = v ;
2008-10-28 01:59:26 +03:00
seq_printf ( seq , " %-5s %-10s %pM " ,
2005-04-17 02:20:36 +04:00
bpqdev - > axdev - > name , bpqdev - > ethdev - > name ,
2008-10-28 01:59:26 +03:00
bpqdev - > dest_addr ) ;
2005-04-17 02:20:36 +04:00
2007-10-04 04:59:30 +04:00
if ( is_multicast_ether_addr ( bpqdev - > acpt_addr ) )
seq_printf ( seq , " * \n " ) ;
else
2008-10-28 01:59:26 +03:00
seq_printf ( seq , " %pM \n " , bpqdev - > acpt_addr ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2009-01-15 00:52:18 +03:00
static const struct seq_operations bpq_seqops = {
2005-04-17 02:20:36 +04:00
. start = bpq_seq_start ,
. next = bpq_seq_next ,
. stop = bpq_seq_stop ,
. show = bpq_seq_show ,
} ;
static int bpq_info_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & bpq_seqops ) ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations bpq_info_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = bpq_info_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
/* ------------------------------------------------------------------------ */
2009-01-09 16:01:32 +03:00
static const struct net_device_ops bpq_netdev_ops = {
. ndo_open = bpq_open ,
. ndo_stop = bpq_close ,
. ndo_start_xmit = bpq_xmit ,
. ndo_set_mac_address = bpq_set_mac_address ,
. ndo_do_ioctl = bpq_ioctl ,
} ;
2005-04-17 02:20:36 +04:00
static void bpq_setup ( struct net_device * dev )
{
2009-01-09 16:01:32 +03:00
dev - > netdev_ops = & bpq_netdev_ops ;
2005-04-17 02:20:36 +04:00
dev - > destructor = free_netdev ;
2006-12-08 02:47:08 +03:00
memcpy ( dev - > broadcast , & ax25_bcast , AX25_ADDR_LEN ) ;
memcpy ( dev - > dev_addr , & ax25_defaddr , AX25_ADDR_LEN ) ;
2005-04-17 02:20:36 +04:00
dev - > flags = 0 ;
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
2007-10-09 12:40:57 +04:00
dev - > header_ops = & ax25_header_ops ;
2005-04-17 02:20:36 +04:00
# endif
dev - > type = ARPHRD_AX25 ;
dev - > hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN ;
dev - > mtu = AX25_DEF_PACLEN ;
dev - > addr_len = AX25_ADDR_LEN ;
}
/*
* Setup a new device .
*/
static int bpq_new_device ( struct net_device * edev )
{
int err ;
struct net_device * ndev ;
struct bpqdev * bpq ;
ndev = alloc_netdev ( sizeof ( struct bpqdev ) , " bpq%d " ,
bpq_setup ) ;
if ( ! ndev )
return - ENOMEM ;
bpq = netdev_priv ( ndev ) ;
dev_hold ( edev ) ;
bpq - > ethdev = edev ;
bpq - > axdev = ndev ;
memcpy ( bpq - > dest_addr , bcast_addr , sizeof ( bpq_eth_addr ) ) ;
memcpy ( bpq - > acpt_addr , bcast_addr , sizeof ( bpq_eth_addr ) ) ;
err = dev_alloc_name ( ndev , ndev - > name ) ;
if ( err < 0 )
goto error ;
err = register_netdevice ( ndev ) ;
if ( err )
goto error ;
2008-07-09 10:13:53 +04:00
bpq_set_lockdep_class ( ndev ) ;
2005-04-17 02:20:36 +04:00
/* List protected by RTNL */
list_add_rcu ( & bpq - > bpq_list , & bpq_devices ) ;
return 0 ;
error :
dev_put ( edev ) ;
free_netdev ( ndev ) ;
return err ;
}
static void bpq_free_device ( struct net_device * ndev )
{
struct bpqdev * bpq = netdev_priv ( ndev ) ;
dev_put ( bpq - > ethdev ) ;
list_del_rcu ( & bpq - > bpq_list ) ;
unregister_netdevice ( ndev ) ;
}
/*
* Handle device status changes .
*/
static int bpq_device_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
struct net_device * dev = ( struct net_device * ) ptr ;
2008-03-25 15:47:49 +03:00
if ( dev_net ( dev ) ! = & init_net )
2007-09-12 15:02:17 +04:00
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 BPQ interface */
if ( bpq_get_ax25_dev ( dev ) = = NULL )
bpq_new_device ( dev ) ;
break ;
case NETDEV_DOWN : /* ethernet device closed -> close BPQ interface */
if ( ( dev = bpq_get_ax25_dev ( dev ) ) ! = NULL )
dev_close ( dev ) ;
break ;
case NETDEV_UNREGISTER : /* ethernet device removed -> free BPQ interface */
if ( ( dev = bpq_get_ax25_dev ( dev ) ) ! = NULL )
bpq_free_device ( dev ) ;
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
/* ------------------------------------------------------------------------ */
/*
* Initialize driver . To be called from af_ax25 if not compiled as a
* module
*/
static int __init bpq_init_driver ( void )
{
# ifdef CONFIG_PROC_FS
2007-09-12 14:01:34 +04:00
if ( ! proc_net_fops_create ( & init_net , " bpqether " , S_IRUGO , & bpq_info_fops ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR
" bpq: cannot create /proc/net/bpqether entry. \n " ) ;
return - ENOENT ;
}
# endif /* CONFIG_PROC_FS */
dev_add_pack ( & bpq_packet_type ) ;
register_netdevice_notifier ( & bpq_dev_notifier ) ;
printk ( banner ) ;
return 0 ;
}
static void __exit bpq_cleanup_driver ( void )
{
struct bpqdev * bpq ;
dev_remove_pack ( & bpq_packet_type ) ;
unregister_netdevice_notifier ( & bpq_dev_notifier ) ;
2007-09-12 14:01:34 +04:00
proc_net_remove ( & init_net , " bpqether " ) ;
2005-04-17 02:20:36 +04:00
rtnl_lock ( ) ;
while ( ! list_empty ( & bpq_devices ) ) {
bpq = list_entry ( bpq_devices . next , struct bpqdev , bpq_list ) ;
bpq_free_device ( bpq - > axdev ) ;
}
rtnl_unlock ( ) ;
}
MODULE_AUTHOR ( " Joerg Reuter DL1BKE <jreuter@yaina.de> " ) ;
MODULE_DESCRIPTION ( " Transmit and receive AX.25 packets over Ethernet " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( bpq_init_driver ) ;
module_exit ( bpq_cleanup_driver ) ;