2005-04-17 02:20:36 +04:00
/*
* 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 .
*
* Copyright ( C ) Alan Cox GW4PTS ( alan @ lxorguk . ukuu . org . uk )
* Copyright ( C ) Jonathan Naylor G4KLX ( g4klx @ g4klx . demon . co . uk )
* Copyright ( C ) Darryl Miles G7LED ( dlm @ g7led . demon . co . uk )
* Copyright ( C ) Steven Whitehouse GW7RRM ( stevew @ acm . org )
* Copyright ( C ) Joerg Reuter DL1BKE ( jreuter @ yaina . de )
* Copyright ( C ) Hans - Joachim Hetscher DD8NE ( dd8ne @ bnv - bamberg . de )
* Copyright ( C ) Hans Alblas PE1AYX ( hans @ esrac . ele . tue . nl )
* Copyright ( C ) Frederic Rible F1OAT ( frible @ teaser . fr )
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/smp_lock.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <net/ax25.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <linux/skbuff.h>
# include <net/sock.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include <linux/fcntl.h>
# include <linux/termios.h> /* For TIOCINQ/OUTQ */
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/notifier.h>
# include <linux/proc_fs.h>
# include <linux/stat.h>
# include <linux/netfilter.h>
# include <linux/sysctl.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <net/tcp.h>
# include <net/ip.h>
# include <net/arp.h>
HLIST_HEAD ( ax25_list ) ;
DEFINE_SPINLOCK ( ax25_list_lock ) ;
static struct proto_ops ax25_proto_ops ;
static void ax25_free_sock ( struct sock * sk )
{
ax25_cb_put ( ax25_sk ( sk ) ) ;
}
/*
* Socket removal during an interrupt is now safe .
*/
static void ax25_cb_del ( ax25_cb * ax25 )
{
if ( ! hlist_unhashed ( & ax25 - > ax25_node ) ) {
spin_lock_bh ( & ax25_list_lock ) ;
hlist_del_init ( & ax25 - > ax25_node ) ;
spin_unlock_bh ( & ax25_list_lock ) ;
ax25_cb_put ( ax25 ) ;
}
}
/*
* Kill all bound sockets on a dropped device .
*/
static void ax25_kill_by_device ( struct net_device * dev )
{
ax25_dev * ax25_dev ;
ax25_cb * s ;
struct hlist_node * node ;
if ( ( ax25_dev = ax25_dev_ax25dev ( dev ) ) = = NULL )
return ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( s , node , & ax25_list ) {
if ( s - > ax25_dev = = ax25_dev ) {
s - > ax25_dev = NULL ;
ax25_disconnect ( s , ENETUNREACH ) ;
}
}
spin_unlock_bh ( & ax25_list_lock ) ;
}
/*
* Handle device status changes .
*/
static int ax25_device_event ( struct notifier_block * this , unsigned long event ,
void * ptr )
{
struct net_device * dev = ( struct net_device * ) ptr ;
/* Reject non AX.25 devices */
if ( dev - > type ! = ARPHRD_AX25 )
return NOTIFY_DONE ;
switch ( event ) {
case NETDEV_UP :
ax25_dev_device_up ( dev ) ;
break ;
case NETDEV_DOWN :
ax25_kill_by_device ( dev ) ;
ax25_rt_device_down ( dev ) ;
ax25_dev_device_down ( dev ) ;
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
/*
* Add a socket to the bound sockets list .
*/
void ax25_cb_add ( ax25_cb * ax25 )
{
spin_lock_bh ( & ax25_list_lock ) ;
ax25_cb_hold ( ax25 ) ;
hlist_add_head ( & ax25 - > ax25_node , & ax25_list ) ;
spin_unlock_bh ( & ax25_list_lock ) ;
}
/*
* Find a socket that wants to accept the SABM we have just
* received .
*/
struct sock * ax25_find_listener ( ax25_address * addr , int digi ,
struct net_device * dev , int type )
{
ax25_cb * s ;
struct hlist_node * node ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( s , node , & ax25_list ) {
if ( ( s - > iamdigi & & ! digi ) | | ( ! s - > iamdigi & & digi ) )
continue ;
if ( s - > sk & & ! ax25cmp ( & s - > source_addr , addr ) & &
s - > sk - > sk_type = = type & & s - > sk - > sk_state = = TCP_LISTEN ) {
/* If device is null we match any device */
if ( s - > ax25_dev = = NULL | | s - > ax25_dev - > dev = = dev ) {
sock_hold ( s - > sk ) ;
spin_unlock_bh ( & ax25_list_lock ) ;
return s - > sk ;
}
}
}
spin_unlock_bh ( & ax25_list_lock ) ;
return NULL ;
}
/*
* Find an AX .25 socket given both ends .
*/
struct sock * ax25_get_socket ( ax25_address * my_addr , ax25_address * dest_addr ,
int type )
{
struct sock * sk = NULL ;
ax25_cb * s ;
struct hlist_node * node ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( s , node , & ax25_list ) {
if ( s - > sk & & ! ax25cmp ( & s - > source_addr , my_addr ) & &
! ax25cmp ( & s - > dest_addr , dest_addr ) & &
s - > sk - > sk_type = = type ) {
sk = s - > sk ;
sock_hold ( sk ) ;
break ;
}
}
spin_unlock_bh ( & ax25_list_lock ) ;
return sk ;
}
/*
* Find an AX .25 control block given both ends . It will only pick up
* floating AX .25 control blocks or non Raw socket bound control blocks .
*/
ax25_cb * ax25_find_cb ( ax25_address * src_addr , ax25_address * dest_addr ,
ax25_digi * digi , struct net_device * dev )
{
ax25_cb * s ;
struct hlist_node * node ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( s , node , & ax25_list ) {
if ( s - > sk & & s - > sk - > sk_type ! = SOCK_SEQPACKET )
continue ;
if ( s - > ax25_dev = = NULL )
continue ;
if ( ax25cmp ( & s - > source_addr , src_addr ) = = 0 & & ax25cmp ( & s - > dest_addr , dest_addr ) = = 0 & & s - > ax25_dev - > dev = = dev ) {
if ( digi ! = NULL & & digi - > ndigi ! = 0 ) {
if ( s - > digipeat = = NULL )
continue ;
if ( ax25digicmp ( s - > digipeat , digi ) ! = 0 )
continue ;
} else {
if ( s - > digipeat ! = NULL & & s - > digipeat - > ndigi ! = 0 )
continue ;
}
ax25_cb_hold ( s ) ;
spin_unlock_bh ( & ax25_list_lock ) ;
return s ;
}
}
spin_unlock_bh ( & ax25_list_lock ) ;
return NULL ;
}
void ax25_send_to_raw ( ax25_address * addr , struct sk_buff * skb , int proto )
{
ax25_cb * s ;
struct sk_buff * copy ;
struct hlist_node * node ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( s , node , & ax25_list ) {
if ( s - > sk ! = NULL & & ax25cmp ( & s - > source_addr , addr ) = = 0 & &
s - > sk - > sk_type = = SOCK_RAW & &
s - > sk - > sk_protocol = = proto & &
s - > ax25_dev - > dev = = skb - > dev & &
atomic_read ( & s - > sk - > sk_rmem_alloc ) < = s - > sk - > sk_rcvbuf ) {
if ( ( copy = skb_clone ( skb , GFP_ATOMIC ) ) = = NULL )
continue ;
if ( sock_queue_rcv_skb ( s - > sk , copy ) ! = 0 )
kfree_skb ( copy ) ;
}
}
spin_unlock_bh ( & ax25_list_lock ) ;
}
/*
* Deferred destroy .
*/
void ax25_destroy_socket ( ax25_cb * ) ;
/*
* Handler for deferred kills .
*/
static void ax25_destroy_timer ( unsigned long data )
{
ax25_cb * ax25 = ( ax25_cb * ) data ;
struct sock * sk ;
sk = ax25 - > sk ;
bh_lock_sock ( sk ) ;
sock_hold ( sk ) ;
ax25_destroy_socket ( ax25 ) ;
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
}
/*
* This is called from user mode and the timers . Thus it protects itself
* against interrupt users but doesn ' t worry about being called during
* work . Once it is removed from the queue no interrupt or bottom half
* will touch it and we are ( fairly 8 - ) ) safe .
*/
void ax25_destroy_socket ( ax25_cb * ax25 )
{
struct sk_buff * skb ;
ax25_cb_del ( ax25 ) ;
ax25_stop_heartbeat ( ax25 ) ;
ax25_stop_t1timer ( ax25 ) ;
ax25_stop_t2timer ( ax25 ) ;
ax25_stop_t3timer ( ax25 ) ;
ax25_stop_idletimer ( ax25 ) ;
ax25_clear_queues ( ax25 ) ; /* Flush the queues */
if ( ax25 - > sk ! = NULL ) {
while ( ( skb = skb_dequeue ( & ax25 - > sk - > sk_receive_queue ) ) ! = NULL ) {
if ( skb - > sk ! = ax25 - > sk ) {
/* A pending connection */
ax25_cb * sax25 = ax25_sk ( skb - > sk ) ;
/* Queue the unaccepted socket for death */
sock_orphan ( skb - > sk ) ;
ax25_start_heartbeat ( sax25 ) ;
sax25 - > state = AX25_STATE_0 ;
}
kfree_skb ( skb ) ;
}
skb_queue_purge ( & ax25 - > sk - > sk_write_queue ) ;
}
if ( ax25 - > sk ! = NULL ) {
if ( atomic_read ( & ax25 - > sk - > sk_wmem_alloc ) | |
atomic_read ( & ax25 - > sk - > sk_rmem_alloc ) ) {
/* Defer: outstanding buffers */
init_timer ( & ax25 - > dtimer ) ;
ax25 - > dtimer . expires = jiffies + 2 * HZ ;
ax25 - > dtimer . function = ax25_destroy_timer ;
ax25 - > dtimer . data = ( unsigned long ) ax25 ;
add_timer ( & ax25 - > dtimer ) ;
} else {
struct sock * sk = ax25 - > sk ;
ax25 - > sk = NULL ;
sock_put ( sk ) ;
}
} else {
ax25_cb_put ( ax25 ) ;
}
}
/*
* dl1bke 960311 : set parameters for existing AX .25 connections ,
* includes a KILL command to abort any connection .
* VERY useful for debugging ; - )
*/
static int ax25_ctl_ioctl ( const unsigned int cmd , void __user * arg )
{
struct ax25_ctl_struct ax25_ctl ;
ax25_digi digi ;
ax25_dev * ax25_dev ;
ax25_cb * ax25 ;
unsigned int k ;
if ( copy_from_user ( & ax25_ctl , arg , sizeof ( ax25_ctl ) ) )
return - EFAULT ;
if ( ( ax25_dev = ax25_addr_ax25dev ( & ax25_ctl . port_addr ) ) = = NULL )
return - ENODEV ;
if ( ax25_ctl . digi_count > AX25_MAX_DIGIS )
return - EINVAL ;
digi . ndigi = ax25_ctl . digi_count ;
for ( k = 0 ; k < digi . ndigi ; k + + )
digi . calls [ k ] = ax25_ctl . digi_addr [ k ] ;
if ( ( ax25 = ax25_find_cb ( & ax25_ctl . source_addr , & ax25_ctl . dest_addr , & digi , ax25_dev - > dev ) ) = = NULL )
return - ENOTCONN ;
switch ( ax25_ctl . cmd ) {
case AX25_KILL :
ax25_send_control ( ax25 , AX25_DISC , AX25_POLLON , AX25_COMMAND ) ;
# ifdef CONFIG_AX25_DAMA_SLAVE
if ( ax25_dev - > dama . slave & & ax25 - > ax25_dev - > values [ AX25_VALUES_PROTOCOL ] = = AX25_PROTO_DAMA_SLAVE )
ax25_dama_off ( ax25 ) ;
# endif
ax25_disconnect ( ax25 , ENETRESET ) ;
break ;
case AX25_WINDOW :
if ( ax25 - > modulus = = AX25_MODULUS ) {
if ( ax25_ctl . arg < 1 | | ax25_ctl . arg > 7 )
return - EINVAL ;
} else {
if ( ax25_ctl . arg < 1 | | ax25_ctl . arg > 63 )
return - EINVAL ;
}
ax25 - > window = ax25_ctl . arg ;
break ;
case AX25_T1 :
if ( ax25_ctl . arg < 1 )
return - EINVAL ;
ax25 - > rtt = ( ax25_ctl . arg * HZ ) / 2 ;
ax25 - > t1 = ax25_ctl . arg * HZ ;
break ;
case AX25_T2 :
if ( ax25_ctl . arg < 1 )
return - EINVAL ;
ax25 - > t2 = ax25_ctl . arg * HZ ;
break ;
case AX25_N2 :
if ( ax25_ctl . arg < 1 | | ax25_ctl . arg > 31 )
return - EINVAL ;
ax25 - > n2count = 0 ;
ax25 - > n2 = ax25_ctl . arg ;
break ;
case AX25_T3 :
if ( ax25_ctl . arg < 0 )
return - EINVAL ;
ax25 - > t3 = ax25_ctl . arg * HZ ;
break ;
case AX25_IDLE :
if ( ax25_ctl . arg < 0 )
return - EINVAL ;
ax25 - > idle = ax25_ctl . arg * 60 * HZ ;
break ;
case AX25_PACLEN :
if ( ax25_ctl . arg < 16 | | ax25_ctl . arg > 65535 )
return - EINVAL ;
ax25 - > paclen = ax25_ctl . arg ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*
* Fill in a created AX .25 created control block with the default
* values for a particular device .
*/
void ax25_fillin_cb ( ax25_cb * ax25 , ax25_dev * ax25_dev )
{
ax25 - > ax25_dev = ax25_dev ;
if ( ax25 - > ax25_dev ! = NULL ) {
ax25 - > rtt = ax25_dev - > values [ AX25_VALUES_T1 ] / 2 ;
ax25 - > t1 = ax25_dev - > values [ AX25_VALUES_T1 ] ;
ax25 - > t2 = ax25_dev - > values [ AX25_VALUES_T2 ] ;
ax25 - > t3 = ax25_dev - > values [ AX25_VALUES_T3 ] ;
ax25 - > n2 = ax25_dev - > values [ AX25_VALUES_N2 ] ;
ax25 - > paclen = ax25_dev - > values [ AX25_VALUES_PACLEN ] ;
ax25 - > idle = ax25_dev - > values [ AX25_VALUES_IDLE ] ;
ax25 - > backoff = ax25_dev - > values [ AX25_VALUES_BACKOFF ] ;
if ( ax25_dev - > values [ AX25_VALUES_AXDEFMODE ] ) {
ax25 - > modulus = AX25_EMODULUS ;
ax25 - > window = ax25_dev - > values [ AX25_VALUES_EWINDOW ] ;
} else {
ax25 - > modulus = AX25_MODULUS ;
ax25 - > window = ax25_dev - > values [ AX25_VALUES_WINDOW ] ;
}
} else {
ax25 - > rtt = AX25_DEF_T1 / 2 ;
ax25 - > t1 = AX25_DEF_T1 ;
ax25 - > t2 = AX25_DEF_T2 ;
ax25 - > t3 = AX25_DEF_T3 ;
ax25 - > n2 = AX25_DEF_N2 ;
ax25 - > paclen = AX25_DEF_PACLEN ;
ax25 - > idle = AX25_DEF_IDLE ;
ax25 - > backoff = AX25_DEF_BACKOFF ;
if ( AX25_DEF_AXDEFMODE ) {
ax25 - > modulus = AX25_EMODULUS ;
ax25 - > window = AX25_DEF_EWINDOW ;
} else {
ax25 - > modulus = AX25_MODULUS ;
ax25 - > window = AX25_DEF_WINDOW ;
}
}
}
/*
* Create an empty AX .25 control block .
*/
ax25_cb * ax25_create_cb ( void )
{
ax25_cb * ax25 ;
if ( ( ax25 = kmalloc ( sizeof ( * ax25 ) , GFP_ATOMIC ) ) = = NULL )
return NULL ;
memset ( ax25 , 0x00 , sizeof ( * ax25 ) ) ;
atomic_set ( & ax25 - > refcount , 1 ) ;
skb_queue_head_init ( & ax25 - > write_queue ) ;
skb_queue_head_init ( & ax25 - > frag_queue ) ;
skb_queue_head_init ( & ax25 - > ack_queue ) ;
skb_queue_head_init ( & ax25 - > reseq_queue ) ;
init_timer ( & ax25 - > timer ) ;
init_timer ( & ax25 - > t1timer ) ;
init_timer ( & ax25 - > t2timer ) ;
init_timer ( & ax25 - > t3timer ) ;
init_timer ( & ax25 - > idletimer ) ;
ax25_fillin_cb ( ax25 , NULL ) ;
ax25 - > state = AX25_STATE_0 ;
return ax25 ;
}
/*
* Handling for system calls applied via the various interfaces to an
* AX25 socket object
*/
static int ax25_setsockopt ( struct socket * sock , int level , int optname ,
char __user * optval , int optlen )
{
struct sock * sk = sock - > sk ;
ax25_cb * ax25 ;
struct net_device * dev ;
char devname [ IFNAMSIZ ] ;
int opt , res = 0 ;
if ( level ! = SOL_AX25 )
return - ENOPROTOOPT ;
if ( optlen < sizeof ( int ) )
return - EINVAL ;
if ( get_user ( opt , ( int __user * ) optval ) )
return - EFAULT ;
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
switch ( optname ) {
case AX25_WINDOW :
if ( ax25 - > modulus = = AX25_MODULUS ) {
if ( opt < 1 | | opt > 7 ) {
res = - EINVAL ;
break ;
}
} else {
if ( opt < 1 | | opt > 63 ) {
res = - EINVAL ;
break ;
}
}
ax25 - > window = opt ;
break ;
case AX25_T1 :
if ( opt < 1 ) {
res = - EINVAL ;
break ;
}
ax25 - > rtt = ( opt * HZ ) / 2 ;
ax25 - > t1 = opt * HZ ;
break ;
case AX25_T2 :
if ( opt < 1 ) {
res = - EINVAL ;
break ;
}
ax25 - > t2 = opt * HZ ;
break ;
case AX25_N2 :
if ( opt < 1 | | opt > 31 ) {
res = - EINVAL ;
break ;
}
ax25 - > n2 = opt ;
break ;
case AX25_T3 :
if ( opt < 1 ) {
res = - EINVAL ;
break ;
}
ax25 - > t3 = opt * HZ ;
break ;
case AX25_IDLE :
if ( opt < 0 ) {
res = - EINVAL ;
break ;
}
ax25 - > idle = opt * 60 * HZ ;
break ;
case AX25_BACKOFF :
if ( opt < 0 | | opt > 2 ) {
res = - EINVAL ;
break ;
}
ax25 - > backoff = opt ;
break ;
case AX25_EXTSEQ :
ax25 - > modulus = opt ? AX25_EMODULUS : AX25_MODULUS ;
break ;
case AX25_PIDINCL :
ax25 - > pidincl = opt ? 1 : 0 ;
break ;
case AX25_IAMDIGI :
ax25 - > iamdigi = opt ? 1 : 0 ;
break ;
case AX25_PACLEN :
if ( opt < 16 | | opt > 65535 ) {
res = - EINVAL ;
break ;
}
ax25 - > paclen = opt ;
break ;
case SO_BINDTODEVICE :
if ( optlen > IFNAMSIZ )
optlen = IFNAMSIZ ;
if ( copy_from_user ( devname , optval , optlen ) ) {
res = - EFAULT ;
break ;
}
dev = dev_get_by_name ( devname ) ;
if ( dev = = NULL ) {
res = - ENODEV ;
break ;
}
if ( sk - > sk_type = = SOCK_SEQPACKET & &
( sock - > state ! = SS_UNCONNECTED | |
sk - > sk_state = = TCP_LISTEN ) ) {
res = - EADDRNOTAVAIL ;
dev_put ( dev ) ;
break ;
}
ax25 - > ax25_dev = ax25_dev_ax25dev ( dev ) ;
ax25_fillin_cb ( ax25 , ax25 - > ax25_dev ) ;
break ;
default :
res = - ENOPROTOOPT ;
}
release_sock ( sk ) ;
return res ;
}
static int ax25_getsockopt ( struct socket * sock , int level , int optname ,
char __user * optval , int __user * optlen )
{
struct sock * sk = sock - > sk ;
ax25_cb * ax25 ;
struct ax25_dev * ax25_dev ;
char devname [ IFNAMSIZ ] ;
void * valptr ;
int val = 0 ;
int maxlen , length ;
if ( level ! = SOL_AX25 )
return - ENOPROTOOPT ;
if ( get_user ( maxlen , optlen ) )
return - EFAULT ;
if ( maxlen < 1 )
return - EFAULT ;
valptr = ( void * ) & val ;
length = min_t ( unsigned int , maxlen , sizeof ( int ) ) ;
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
switch ( optname ) {
case AX25_WINDOW :
val = ax25 - > window ;
break ;
case AX25_T1 :
val = ax25 - > t1 / HZ ;
break ;
case AX25_T2 :
val = ax25 - > t2 / HZ ;
break ;
case AX25_N2 :
val = ax25 - > n2 ;
break ;
case AX25_T3 :
val = ax25 - > t3 / HZ ;
break ;
case AX25_IDLE :
val = ax25 - > idle / ( 60 * HZ ) ;
break ;
case AX25_BACKOFF :
val = ax25 - > backoff ;
break ;
case AX25_EXTSEQ :
val = ( ax25 - > modulus = = AX25_EMODULUS ) ;
break ;
case AX25_PIDINCL :
val = ax25 - > pidincl ;
break ;
case AX25_IAMDIGI :
val = ax25 - > iamdigi ;
break ;
case AX25_PACLEN :
val = ax25 - > paclen ;
break ;
case SO_BINDTODEVICE :
ax25_dev = ax25 - > ax25_dev ;
if ( ax25_dev ! = NULL & & ax25_dev - > dev ! = NULL ) {
strlcpy ( devname , ax25_dev - > dev - > name , sizeof ( devname ) ) ;
length = strlen ( devname ) + 1 ;
} else {
* devname = ' \0 ' ;
length = 1 ;
}
valptr = ( void * ) devname ;
break ;
default :
release_sock ( sk ) ;
return - ENOPROTOOPT ;
}
release_sock ( sk ) ;
if ( put_user ( length , optlen ) )
return - EFAULT ;
return copy_to_user ( optval , valptr , length ) ? - EFAULT : 0 ;
}
static int ax25_listen ( struct socket * sock , int backlog )
{
struct sock * sk = sock - > sk ;
int res = 0 ;
lock_sock ( sk ) ;
if ( sk - > sk_type = = SOCK_SEQPACKET & & sk - > sk_state ! = TCP_LISTEN ) {
sk - > sk_max_ack_backlog = backlog ;
sk - > sk_state = TCP_LISTEN ;
goto out ;
}
res = - EOPNOTSUPP ;
out :
release_sock ( sk ) ;
return res ;
}
/*
* XXX : when creating ax25_sock we should update the . obj_size setting
* below .
*/
static struct proto ax25_proto = {
. name = " AX25 " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct sock ) ,
} ;
static int ax25_create ( struct socket * sock , int protocol )
{
struct sock * sk ;
ax25_cb * ax25 ;
switch ( sock - > type ) {
case SOCK_DGRAM :
if ( protocol = = 0 | | protocol = = PF_AX25 )
protocol = AX25_P_TEXT ;
break ;
case SOCK_SEQPACKET :
switch ( protocol ) {
case 0 :
case PF_AX25 : /* For CLX */
protocol = AX25_P_TEXT ;
break ;
case AX25_P_SEGMENT :
# ifdef CONFIG_INET
case AX25_P_ARP :
case AX25_P_IP :
# endif
# ifdef CONFIG_NETROM
case AX25_P_NETROM :
# endif
# ifdef CONFIG_ROSE
case AX25_P_ROSE :
# endif
return - ESOCKTNOSUPPORT ;
# ifdef CONFIG_NETROM_MODULE
case AX25_P_NETROM :
if ( ax25_protocol_is_registered ( AX25_P_NETROM ) )
return - ESOCKTNOSUPPORT ;
# endif
# ifdef CONFIG_ROSE_MODULE
case AX25_P_ROSE :
if ( ax25_protocol_is_registered ( AX25_P_ROSE ) )
return - ESOCKTNOSUPPORT ;
# endif
default :
break ;
}
break ;
case SOCK_RAW :
break ;
default :
return - ESOCKTNOSUPPORT ;
}
if ( ( sk = sk_alloc ( PF_AX25 , GFP_ATOMIC , & ax25_proto , 1 ) ) = = NULL )
return - ENOMEM ;
ax25 = sk - > sk_protinfo = ax25_create_cb ( ) ;
if ( ! ax25 ) {
sk_free ( sk ) ;
return - ENOMEM ;
}
sock_init_data ( sock , sk ) ;
sk - > sk_destruct = ax25_free_sock ;
sock - > ops = & ax25_proto_ops ;
sk - > sk_protocol = protocol ;
ax25 - > sk = sk ;
return 0 ;
}
struct sock * ax25_make_new ( struct sock * osk , struct ax25_dev * ax25_dev )
{
struct sock * sk ;
ax25_cb * ax25 , * oax25 ;
if ( ( sk = sk_alloc ( PF_AX25 , GFP_ATOMIC , osk - > sk_prot , 1 ) ) = = NULL )
return NULL ;
if ( ( ax25 = ax25_create_cb ( ) ) = = NULL ) {
sk_free ( sk ) ;
return NULL ;
}
switch ( osk - > sk_type ) {
case SOCK_DGRAM :
break ;
case SOCK_SEQPACKET :
break ;
default :
sk_free ( sk ) ;
ax25_cb_put ( ax25 ) ;
return NULL ;
}
sock_init_data ( NULL , sk ) ;
sk - > sk_destruct = ax25_free_sock ;
sk - > sk_type = osk - > sk_type ;
sk - > sk_socket = osk - > sk_socket ;
sk - > sk_priority = osk - > sk_priority ;
sk - > sk_protocol = osk - > sk_protocol ;
sk - > sk_rcvbuf = osk - > sk_rcvbuf ;
sk - > sk_sndbuf = osk - > sk_sndbuf ;
sk - > sk_state = TCP_ESTABLISHED ;
sk - > sk_sleep = osk - > sk_sleep ;
if ( sock_flag ( osk , SOCK_DBG ) )
sock_set_flag ( sk , SOCK_DBG ) ;
if ( sock_flag ( osk , SOCK_ZAPPED ) )
sock_set_flag ( sk , SOCK_ZAPPED ) ;
oax25 = ax25_sk ( osk ) ;
ax25 - > modulus = oax25 - > modulus ;
ax25 - > backoff = oax25 - > backoff ;
ax25 - > pidincl = oax25 - > pidincl ;
ax25 - > iamdigi = oax25 - > iamdigi ;
ax25 - > rtt = oax25 - > rtt ;
ax25 - > t1 = oax25 - > t1 ;
ax25 - > t2 = oax25 - > t2 ;
ax25 - > t3 = oax25 - > t3 ;
ax25 - > n2 = oax25 - > n2 ;
ax25 - > idle = oax25 - > idle ;
ax25 - > paclen = oax25 - > paclen ;
ax25 - > window = oax25 - > window ;
ax25 - > ax25_dev = ax25_dev ;
ax25 - > source_addr = oax25 - > source_addr ;
if ( oax25 - > digipeat ! = NULL ) {
if ( ( ax25 - > digipeat = kmalloc ( sizeof ( ax25_digi ) , GFP_ATOMIC ) ) = = NULL ) {
sk_free ( sk ) ;
ax25_cb_put ( ax25 ) ;
return NULL ;
}
memcpy ( ax25 - > digipeat , oax25 - > digipeat , sizeof ( ax25_digi ) ) ;
}
sk - > sk_protinfo = ax25 ;
ax25 - > sk = sk ;
return sk ;
}
static int ax25_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
ax25_cb * ax25 ;
if ( sk = = NULL )
return 0 ;
sock_hold ( sk ) ;
sock_orphan ( sk ) ;
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
if ( sk - > sk_type = = SOCK_SEQPACKET ) {
switch ( ax25 - > state ) {
case AX25_STATE_0 :
release_sock ( sk ) ;
ax25_disconnect ( ax25 , 0 ) ;
lock_sock ( sk ) ;
ax25_destroy_socket ( ax25 ) ;
break ;
case AX25_STATE_1 :
case AX25_STATE_2 :
ax25_send_control ( ax25 , AX25_DISC , AX25_POLLON , AX25_COMMAND ) ;
release_sock ( sk ) ;
ax25_disconnect ( ax25 , 0 ) ;
lock_sock ( sk ) ;
ax25_destroy_socket ( ax25 ) ;
break ;
case AX25_STATE_3 :
case AX25_STATE_4 :
ax25_clear_queues ( ax25 ) ;
ax25 - > n2count = 0 ;
switch ( ax25 - > ax25_dev - > values [ AX25_VALUES_PROTOCOL ] ) {
case AX25_PROTO_STD_SIMPLEX :
case AX25_PROTO_STD_DUPLEX :
ax25_send_control ( ax25 ,
AX25_DISC ,
AX25_POLLON ,
AX25_COMMAND ) ;
ax25_stop_t2timer ( ax25 ) ;
ax25_stop_t3timer ( ax25 ) ;
ax25_stop_idletimer ( ax25 ) ;
break ;
# ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE :
ax25_stop_t3timer ( ax25 ) ;
ax25_stop_idletimer ( ax25 ) ;
break ;
# endif
}
ax25_calculate_t1 ( ax25 ) ;
ax25_start_t1timer ( ax25 ) ;
ax25 - > state = AX25_STATE_2 ;
sk - > sk_state = TCP_CLOSE ;
sk - > sk_shutdown | = SEND_SHUTDOWN ;
sk - > sk_state_change ( sk ) ;
sock_set_flag ( sk , SOCK_DESTROY ) ;
break ;
default :
break ;
}
} else {
sk - > sk_state = TCP_CLOSE ;
sk - > sk_shutdown | = SEND_SHUTDOWN ;
sk - > sk_state_change ( sk ) ;
ax25_destroy_socket ( ax25 ) ;
}
sock - > sk = NULL ;
release_sock ( sk ) ;
sock_put ( sk ) ;
return 0 ;
}
/*
* We support a funny extension here so you can ( as root ) give any callsign
* digipeated via a local address as source . This hack is obsolete now
* that we ' ve implemented support for SO_BINDTODEVICE . It is however small
* and trivially backward compatible .
*/
static int ax25_bind ( struct socket * sock , struct sockaddr * uaddr , int addr_len )
{
struct sock * sk = sock - > sk ;
struct full_sockaddr_ax25 * addr = ( struct full_sockaddr_ax25 * ) uaddr ;
ax25_dev * ax25_dev = NULL ;
ax25_address * call ;
ax25_cb * ax25 ;
int err = 0 ;
if ( addr_len ! = sizeof ( struct sockaddr_ax25 ) & &
addr_len ! = sizeof ( struct full_sockaddr_ax25 ) ) {
/* support for old structure may go away some time */
if ( ( addr_len < sizeof ( struct sockaddr_ax25 ) + sizeof ( ax25_address ) * 6 ) | |
( addr_len > sizeof ( struct full_sockaddr_ax25 ) ) ) {
return - EINVAL ;
}
printk ( KERN_WARNING " ax25_bind(): %s uses old (6 digipeater) socket structure. \n " ,
current - > comm ) ;
}
if ( addr - > fsa_ax25 . sax25_family ! = AF_AX25 )
return - EINVAL ;
call = ax25_findbyuid ( current - > euid ) ;
if ( call = = NULL & & ax25_uid_policy & & ! capable ( CAP_NET_ADMIN ) ) {
return - EACCES ;
}
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
if ( ! sock_flag ( sk , SOCK_ZAPPED ) ) {
err = - EINVAL ;
goto out ;
}
if ( call = = NULL )
ax25 - > source_addr = addr - > fsa_ax25 . sax25_call ;
else
ax25 - > source_addr = * call ;
/*
* User already set interface with SO_BINDTODEVICE
*/
if ( ax25 - > ax25_dev ! = NULL )
goto done ;
if ( addr_len > sizeof ( struct sockaddr_ax25 ) & & addr - > fsa_ax25 . sax25_ndigis = = 1 ) {
if ( ax25cmp ( & addr - > fsa_digipeater [ 0 ] , & null_ax25_address ) ! = 0 & &
( ax25_dev = ax25_addr_ax25dev ( & addr - > fsa_digipeater [ 0 ] ) ) = = NULL ) {
err = - EADDRNOTAVAIL ;
goto out ;
}
} else {
if ( ( ax25_dev = ax25_addr_ax25dev ( & addr - > fsa_ax25 . sax25_call ) ) = = NULL ) {
err = - EADDRNOTAVAIL ;
goto out ;
}
}
if ( ax25_dev ! = NULL )
ax25_fillin_cb ( ax25 , ax25_dev ) ;
done :
ax25_cb_add ( ax25 ) ;
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
out :
release_sock ( sk ) ;
return 0 ;
}
/*
* FIXME : nonblock behaviour looks like it may have a bug .
*/
static int ax25_connect ( struct socket * sock , struct sockaddr * uaddr ,
int addr_len , int flags )
{
struct sock * sk = sock - > sk ;
ax25_cb * ax25 = ax25_sk ( sk ) , * ax25t ;
struct full_sockaddr_ax25 * fsa = ( struct full_sockaddr_ax25 * ) uaddr ;
ax25_digi * digi = NULL ;
int ct = 0 , err = 0 ;
/*
* some sanity checks . code further down depends on this
*/
if ( addr_len = = sizeof ( struct sockaddr_ax25 ) ) {
/* support for this will go away in early 2.5.x */
printk ( KERN_WARNING " ax25_connect(): %s uses obsolete socket structure \n " ,
current - > comm ) ;
}
else if ( addr_len ! = sizeof ( struct full_sockaddr_ax25 ) ) {
/* support for old structure may go away some time */
if ( ( addr_len < sizeof ( struct sockaddr_ax25 ) + sizeof ( ax25_address ) * 6 ) | |
( addr_len > sizeof ( struct full_sockaddr_ax25 ) ) ) {
return - EINVAL ;
}
printk ( KERN_WARNING " ax25_connect(): %s uses old (6 digipeater) socket structure. \n " ,
current - > comm ) ;
}
if ( fsa - > fsa_ax25 . sax25_family ! = AF_AX25 )
return - EINVAL ;
lock_sock ( sk ) ;
/* deal with restarts */
if ( sock - > state = = SS_CONNECTING ) {
switch ( sk - > sk_state ) {
case TCP_SYN_SENT : /* still trying */
err = - EINPROGRESS ;
goto out ;
case TCP_ESTABLISHED : /* connection established */
sock - > state = SS_CONNECTED ;
goto out ;
case TCP_CLOSE : /* connection refused */
sock - > state = SS_UNCONNECTED ;
err = - ECONNREFUSED ;
goto out ;
}
}
if ( sk - > sk_state = = TCP_ESTABLISHED & & sk - > sk_type = = SOCK_SEQPACKET ) {
err = - EISCONN ; /* No reconnect on a seqpacket socket */
goto out ;
}
sk - > sk_state = TCP_CLOSE ;
sock - > state = SS_UNCONNECTED ;
if ( ax25 - > digipeat ! = NULL ) {
kfree ( ax25 - > digipeat ) ;
ax25 - > digipeat = NULL ;
}
/*
* Handle digi - peaters to be used .
*/
if ( addr_len > sizeof ( struct sockaddr_ax25 ) & &
fsa - > fsa_ax25 . sax25_ndigis ! = 0 ) {
/* Valid number of digipeaters ? */
if ( fsa - > fsa_ax25 . sax25_ndigis < 1 | | fsa - > fsa_ax25 . sax25_ndigis > AX25_MAX_DIGIS ) {
err = - EINVAL ;
goto out ;
}
if ( ( digi = kmalloc ( sizeof ( ax25_digi ) , GFP_KERNEL ) ) = = NULL ) {
err = - ENOBUFS ;
goto out ;
}
digi - > ndigi = fsa - > fsa_ax25 . sax25_ndigis ;
digi - > lastrepeat = - 1 ;
while ( ct < fsa - > fsa_ax25 . sax25_ndigis ) {
if ( ( fsa - > fsa_digipeater [ ct ] . ax25_call [ 6 ] &
AX25_HBIT ) & & ax25 - > iamdigi ) {
digi - > repeated [ ct ] = 1 ;
digi - > lastrepeat = ct ;
} else {
digi - > repeated [ ct ] = 0 ;
}
digi - > calls [ ct ] = fsa - > fsa_digipeater [ ct ] ;
ct + + ;
}
}
/*
* Must bind first - autobinding in this may or may not work . If
* the socket is already bound , check to see if the device has
* been filled in , error if it hasn ' t .
*/
if ( sock_flag ( sk , SOCK_ZAPPED ) ) {
/* check if we can remove this feature. It is broken. */
printk ( KERN_WARNING " ax25_connect(): %s uses autobind, please contact jreuter@yaina.de \n " ,
current - > comm ) ;
if ( ( err = ax25_rt_autobind ( ax25 , & fsa - > fsa_ax25 . sax25_call ) ) < 0 ) {
kfree ( digi ) ;
goto out ;
}
ax25_fillin_cb ( ax25 , ax25 - > ax25_dev ) ;
ax25_cb_add ( ax25 ) ;
} else {
if ( ax25 - > ax25_dev = = NULL ) {
kfree ( digi ) ;
err = - EHOSTUNREACH ;
goto out ;
}
}
if ( sk - > sk_type = = SOCK_SEQPACKET & &
( ax25t = ax25_find_cb ( & ax25 - > source_addr , & fsa - > fsa_ax25 . sax25_call , digi ,
ax25 - > ax25_dev - > dev ) ) ) {
kfree ( digi ) ;
err = - EADDRINUSE ; /* Already such a connection */
ax25_cb_put ( ax25t ) ;
goto out ;
}
ax25 - > dest_addr = fsa - > fsa_ax25 . sax25_call ;
ax25 - > digipeat = digi ;
/* First the easy one */
if ( sk - > sk_type ! = SOCK_SEQPACKET ) {
sock - > state = SS_CONNECTED ;
sk - > sk_state = TCP_ESTABLISHED ;
goto out ;
}
/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
sock - > state = SS_CONNECTING ;
sk - > sk_state = TCP_SYN_SENT ;
switch ( ax25 - > ax25_dev - > values [ AX25_VALUES_PROTOCOL ] ) {
case AX25_PROTO_STD_SIMPLEX :
case AX25_PROTO_STD_DUPLEX :
ax25_std_establish_data_link ( ax25 ) ;
break ;
# ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE :
ax25 - > modulus = AX25_MODULUS ;
ax25 - > window = ax25 - > ax25_dev - > values [ AX25_VALUES_WINDOW ] ;
if ( ax25 - > ax25_dev - > dama . slave )
ax25_ds_establish_data_link ( ax25 ) ;
else
ax25_std_establish_data_link ( ax25 ) ;
break ;
# endif
}
ax25 - > state = AX25_STATE_1 ;
ax25_start_heartbeat ( ax25 ) ;
/* Now the loop */
if ( sk - > sk_state ! = TCP_ESTABLISHED & & ( flags & O_NONBLOCK ) ) {
err = - EINPROGRESS ;
goto out ;
}
if ( sk - > sk_state = = TCP_SYN_SENT ) {
struct task_struct * tsk = current ;
DECLARE_WAITQUEUE ( wait , tsk ) ;
add_wait_queue ( sk - > sk_sleep , & wait ) ;
for ( ; ; ) {
if ( sk - > sk_state ! = TCP_SYN_SENT )
break ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
release_sock ( sk ) ;
if ( ! signal_pending ( tsk ) ) {
schedule ( ) ;
lock_sock ( sk ) ;
continue ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( sk - > sk_sleep , & wait ) ;
return - ERESTARTSYS ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( sk - > sk_sleep , & wait ) ;
}
if ( sk - > sk_state ! = TCP_ESTABLISHED ) {
/* Not in ABM, not in WAIT_UA -> failed */
sock - > state = SS_UNCONNECTED ;
err = sock_error ( sk ) ; /* Always set at this point */
goto out ;
}
sock - > state = SS_CONNECTED ;
err = 0 ;
out :
release_sock ( sk ) ;
return err ;
}
static int ax25_accept ( struct socket * sock , struct socket * newsock , int flags )
{
struct task_struct * tsk = current ;
DECLARE_WAITQUEUE ( wait , tsk ) ;
struct sk_buff * skb ;
struct sock * newsk ;
struct sock * sk ;
int err = 0 ;
if ( sock - > state ! = SS_UNCONNECTED )
return - EINVAL ;
if ( ( sk = sock - > sk ) = = NULL )
return - EINVAL ;
lock_sock ( sk ) ;
if ( sk - > sk_type ! = SOCK_SEQPACKET ) {
err = - EOPNOTSUPP ;
goto out ;
}
if ( sk - > sk_state ! = TCP_LISTEN ) {
err = - EINVAL ;
goto out ;
}
/*
* The read queue this time is holding sockets ready to use
* hooked into the SABM we saved
*/
add_wait_queue ( sk - > sk_sleep , & wait ) ;
for ( ; ; ) {
skb = skb_dequeue ( & sk - > sk_receive_queue ) ;
if ( skb )
break ;
release_sock ( sk ) ;
current - > state = TASK_INTERRUPTIBLE ;
if ( flags & O_NONBLOCK ) {
current - > state = TASK_RUNNING ;
remove_wait_queue ( sk - > sk_sleep , & wait ) ;
return - EWOULDBLOCK ;
}
if ( ! signal_pending ( tsk ) ) {
schedule ( ) ;
lock_sock ( sk ) ;
continue ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( sk - > sk_sleep , & wait ) ;
return - ERESTARTSYS ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( sk - > sk_sleep , & wait ) ;
newsk = skb - > sk ;
newsk - > sk_socket = newsock ;
newsk - > sk_sleep = & newsock - > wait ;
/* Now attach up the new socket */
kfree_skb ( skb ) ;
sk - > sk_ack_backlog - - ;
newsock - > sk = newsk ;
newsock - > state = SS_CONNECTED ;
out :
release_sock ( sk ) ;
return err ;
}
static int ax25_getname ( struct socket * sock , struct sockaddr * uaddr ,
int * uaddr_len , int peer )
{
struct full_sockaddr_ax25 * fsa = ( struct full_sockaddr_ax25 * ) uaddr ;
struct sock * sk = sock - > sk ;
unsigned char ndigi , i ;
ax25_cb * ax25 ;
int err = 0 ;
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
if ( peer ! = 0 ) {
if ( sk - > sk_state ! = TCP_ESTABLISHED ) {
err = - ENOTCONN ;
goto out ;
}
fsa - > fsa_ax25 . sax25_family = AF_AX25 ;
fsa - > fsa_ax25 . sax25_call = ax25 - > dest_addr ;
fsa - > fsa_ax25 . sax25_ndigis = 0 ;
if ( ax25 - > digipeat ! = NULL ) {
ndigi = ax25 - > digipeat - > ndigi ;
fsa - > fsa_ax25 . sax25_ndigis = ndigi ;
for ( i = 0 ; i < ndigi ; i + + )
fsa - > fsa_digipeater [ i ] =
ax25 - > digipeat - > calls [ i ] ;
}
} else {
fsa - > fsa_ax25 . sax25_family = AF_AX25 ;
fsa - > fsa_ax25 . sax25_call = ax25 - > source_addr ;
fsa - > fsa_ax25 . sax25_ndigis = 1 ;
if ( ax25 - > ax25_dev ! = NULL ) {
memcpy ( & fsa - > fsa_digipeater [ 0 ] ,
ax25 - > ax25_dev - > dev - > dev_addr , AX25_ADDR_LEN ) ;
} else {
fsa - > fsa_digipeater [ 0 ] = null_ax25_address ;
}
}
* uaddr_len = sizeof ( struct full_sockaddr_ax25 ) ;
out :
release_sock ( sk ) ;
return err ;
}
static int ax25_sendmsg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * msg , size_t len )
{
struct sockaddr_ax25 * usax = ( struct sockaddr_ax25 * ) msg - > msg_name ;
struct sock * sk = sock - > sk ;
struct sockaddr_ax25 sax ;
struct sk_buff * skb ;
ax25_digi dtmp , * dp ;
unsigned char * asmptr ;
ax25_cb * ax25 ;
size_t size ;
int lv , err , addr_len = msg - > msg_namelen ;
if ( msg - > msg_flags & ~ ( MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT ) )
return - EINVAL ;
lock_sock ( sk ) ;
ax25 = ax25_sk ( sk ) ;
if ( sock_flag ( sk , SOCK_ZAPPED ) ) {
err = - EADDRNOTAVAIL ;
goto out ;
}
if ( sk - > sk_shutdown & SEND_SHUTDOWN ) {
send_sig ( SIGPIPE , current , 0 ) ;
err = - EPIPE ;
goto out ;
}
if ( ax25 - > ax25_dev = = NULL ) {
err = - ENETUNREACH ;
goto out ;
}
if ( len > ax25 - > ax25_dev - > dev - > mtu ) {
err = - EMSGSIZE ;
goto out ;
}
if ( usax ! = NULL ) {
if ( usax - > sax25_family ! = AF_AX25 ) {
err = - EINVAL ;
goto out ;
}
if ( addr_len = = sizeof ( struct sockaddr_ax25 ) ) {
printk ( KERN_WARNING " ax25_sendmsg(): %s uses obsolete socket structure \n " ,
current - > comm ) ;
}
else if ( addr_len ! = sizeof ( struct full_sockaddr_ax25 ) ) {
/* support for old structure may go away some time */
if ( ( addr_len < sizeof ( struct sockaddr_ax25 ) + sizeof ( ax25_address ) * 6 ) | |
( addr_len > sizeof ( struct full_sockaddr_ax25 ) ) ) {
err = - EINVAL ;
goto out ;
}
printk ( KERN_WARNING " ax25_sendmsg(): %s uses old (6 digipeater) socket structure. \n " ,
current - > comm ) ;
}
if ( addr_len > sizeof ( struct sockaddr_ax25 ) & & usax - > sax25_ndigis ! = 0 ) {
int ct = 0 ;
struct full_sockaddr_ax25 * fsa = ( struct full_sockaddr_ax25 * ) usax ;
/* Valid number of digipeaters ? */
if ( usax - > sax25_ndigis < 1 | | usax - > sax25_ndigis > AX25_MAX_DIGIS ) {
err = - EINVAL ;
goto out ;
}
dtmp . ndigi = usax - > sax25_ndigis ;
while ( ct < usax - > sax25_ndigis ) {
dtmp . repeated [ ct ] = 0 ;
dtmp . calls [ ct ] = fsa - > fsa_digipeater [ ct ] ;
ct + + ;
}
dtmp . lastrepeat = 0 ;
}
sax = * usax ;
if ( sk - > sk_type = = SOCK_SEQPACKET & &
ax25cmp ( & ax25 - > dest_addr , & sax . sax25_call ) ) {
err = - EISCONN ;
goto out ;
}
if ( usax - > sax25_ndigis = = 0 )
dp = NULL ;
else
dp = & dtmp ;
} else {
/*
* FIXME : 1003.1 g - if the socket is like this because
* it has become closed ( not started closed ) and is VC
* we ought to SIGPIPE , EPIPE
*/
if ( sk - > sk_state ! = TCP_ESTABLISHED ) {
err = - ENOTCONN ;
goto out ;
}
sax . sax25_family = AF_AX25 ;
sax . sax25_call = ax25 - > dest_addr ;
dp = ax25 - > digipeat ;
}
SOCK_DEBUG ( sk , " AX.25: sendto: Addresses built. \n " ) ;
/* Build a packet */
SOCK_DEBUG ( sk , " AX.25: sendto: building packet. \n " ) ;
/* Assume the worst case */
size = len + ax25 - > ax25_dev - > dev - > hard_header_len ;
skb = sock_alloc_send_skb ( sk , size , msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( skb = = NULL )
goto out ;
skb_reserve ( skb , size - len ) ;
SOCK_DEBUG ( sk , " AX.25: Appending user data \n " ) ;
/* User data follows immediately after the AX.25 data */
if ( memcpy_fromiovec ( skb_put ( skb , len ) , msg - > msg_iov , len ) ) {
err = - EFAULT ;
kfree_skb ( skb ) ;
goto out ;
}
skb - > nh . raw = skb - > data ;
/* Add the PID if one is not supplied by the user in the skb */
if ( ! ax25 - > pidincl ) {
asmptr = skb_push ( skb , 1 ) ;
* asmptr = sk - > sk_protocol ;
}
SOCK_DEBUG ( sk , " AX.25: Transmitting buffer \n " ) ;
if ( sk - > sk_type = = SOCK_SEQPACKET ) {
/* Connected mode sockets go via the LAPB machine */
if ( sk - > sk_state ! = TCP_ESTABLISHED ) {
kfree_skb ( skb ) ;
err = - ENOTCONN ;
goto out ;
}
/* Shove it onto the queue and kick */
ax25_output ( ax25 , ax25 - > paclen , skb ) ;
err = len ;
goto out ;
}
asmptr = skb_push ( skb , 1 + ax25_addr_size ( dp ) ) ;
SOCK_DEBUG ( sk , " Building AX.25 Header (dp=%p). \n " , dp ) ;
if ( dp ! = NULL )
SOCK_DEBUG ( sk , " Num digipeaters=%d \n " , dp - > ndigi ) ;
/* Build an AX.25 header */
asmptr + = ( lv = ax25_addr_build ( asmptr , & ax25 - > source_addr ,
& sax . sax25_call , dp ,
AX25_COMMAND , AX25_MODULUS ) ) ;
SOCK_DEBUG ( sk , " Built header (%d bytes) \n " , lv ) ;
skb - > h . raw = asmptr ;
SOCK_DEBUG ( sk , " base=%p pos=%p \n " , skb - > data , asmptr ) ;
* asmptr = AX25_UI ;
/* Datagram frames go straight out of the door as UI */
2005-04-22 03:46:56 +04:00
ax25_queue_xmit ( skb , ax25 - > ax25_dev - > dev ) ;
2005-04-17 02:20:36 +04:00
err = len ;
out :
release_sock ( sk ) ;
return err ;
}
static int ax25_recvmsg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * msg , size_t size , int flags )
{
struct sock * sk = sock - > sk ;
struct sk_buff * skb ;
int copied ;
int err = 0 ;
lock_sock ( sk ) ;
/*
* This works for seqpacket too . The receiver has ordered the
* queue for us ! We do one quick check first though
*/
if ( sk - > sk_type = = SOCK_SEQPACKET & & sk - > sk_state ! = TCP_ESTABLISHED ) {
err = - ENOTCONN ;
goto out ;
}
/* Now we can treat all alike */
skb = skb_recv_datagram ( sk , flags & ~ MSG_DONTWAIT ,
flags & MSG_DONTWAIT , & err ) ;
if ( skb = = NULL )
goto out ;
if ( ! ax25_sk ( sk ) - > pidincl )
skb_pull ( skb , 1 ) ; /* Remove PID */
skb - > h . raw = skb - > data ;
copied = skb - > len ;
if ( copied > size ) {
copied = size ;
msg - > msg_flags | = MSG_TRUNC ;
}
skb_copy_datagram_iovec ( skb , 0 , msg - > msg_iov , copied ) ;
if ( msg - > msg_namelen ! = 0 ) {
struct sockaddr_ax25 * sax = ( struct sockaddr_ax25 * ) msg - > msg_name ;
ax25_digi digi ;
ax25_address src ;
ax25_addr_parse ( skb - > mac . raw + 1 , skb - > data - skb - > mac . raw - 1 , & src , NULL , & digi , NULL , NULL ) ;
sax - > sax25_family = AF_AX25 ;
/* We set this correctly, even though we may not let the
application know the digi calls further down ( because it
did NOT ask to know them ) . This could get political . . . * */
sax - > sax25_ndigis = digi . ndigi ;
sax - > sax25_call = src ;
if ( sax - > sax25_ndigis ! = 0 ) {
int ct ;
struct full_sockaddr_ax25 * fsa = ( struct full_sockaddr_ax25 * ) sax ;
for ( ct = 0 ; ct < digi . ndigi ; ct + + )
fsa - > fsa_digipeater [ ct ] = digi . calls [ ct ] ;
}
msg - > msg_namelen = sizeof ( struct full_sockaddr_ax25 ) ;
}
skb_free_datagram ( sk , skb ) ;
err = copied ;
out :
release_sock ( sk ) ;
return err ;
}
static int ax25_shutdown ( struct socket * sk , int how )
{
/* FIXME - generate DM and RNR states */
return - EOPNOTSUPP ;
}
static int ax25_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
struct sock * sk = sock - > sk ;
void __user * argp = ( void __user * ) arg ;
int res = 0 ;
lock_sock ( sk ) ;
switch ( cmd ) {
case TIOCOUTQ : {
long amount ;
amount = sk - > sk_sndbuf - atomic_read ( & sk - > sk_wmem_alloc ) ;
if ( amount < 0 )
amount = 0 ;
res = put_user ( amount , ( int __user * ) argp ) ;
break ;
}
case TIOCINQ : {
struct sk_buff * skb ;
long amount = 0L ;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ( ( skb = skb_peek ( & sk - > sk_receive_queue ) ) ! = NULL )
amount = skb - > len ;
res = put_user ( amount , ( int __user * ) argp ) ;
break ;
}
case SIOCGSTAMP :
if ( sk ! = NULL ) {
res = sock_get_timestamp ( sk , argp ) ;
break ;
}
res = - EINVAL ;
break ;
case SIOCAX25ADDUID : /* Add a uid to the uid/call map table */
case SIOCAX25DELUID : /* Delete a uid from the uid/call map table */
case SIOCAX25GETUID : {
struct sockaddr_ax25 sax25 ;
if ( copy_from_user ( & sax25 , argp , sizeof ( sax25 ) ) ) {
res = - EFAULT ;
break ;
}
res = ax25_uid_ioctl ( cmd , & sax25 ) ;
break ;
}
case SIOCAX25NOUID : { /* Set the default policy (default/bar) */
long amount ;
if ( ! capable ( CAP_NET_ADMIN ) ) {
res = - EPERM ;
break ;
}
if ( get_user ( amount , ( long __user * ) argp ) ) {
res = - EFAULT ;
break ;
}
if ( amount > AX25_NOUID_BLOCK ) {
res = - EINVAL ;
break ;
}
ax25_uid_policy = amount ;
res = 0 ;
break ;
}
case SIOCADDRT :
case SIOCDELRT :
case SIOCAX25OPTRT :
if ( ! capable ( CAP_NET_ADMIN ) ) {
res = - EPERM ;
break ;
}
res = ax25_rt_ioctl ( cmd , argp ) ;
break ;
case SIOCAX25CTLCON :
if ( ! capable ( CAP_NET_ADMIN ) ) {
res = - EPERM ;
break ;
}
res = ax25_ctl_ioctl ( cmd , argp ) ;
break ;
case SIOCAX25GETINFO :
case SIOCAX25GETINFOOLD : {
ax25_cb * ax25 = ax25_sk ( sk ) ;
struct ax25_info_struct ax25_info ;
ax25_info . t1 = ax25 - > t1 / HZ ;
ax25_info . t2 = ax25 - > t2 / HZ ;
ax25_info . t3 = ax25 - > t3 / HZ ;
ax25_info . idle = ax25 - > idle / ( 60 * HZ ) ;
ax25_info . n2 = ax25 - > n2 ;
ax25_info . t1timer = ax25_display_timer ( & ax25 - > t1timer ) / HZ ;
ax25_info . t2timer = ax25_display_timer ( & ax25 - > t2timer ) / HZ ;
ax25_info . t3timer = ax25_display_timer ( & ax25 - > t3timer ) / HZ ;
ax25_info . idletimer = ax25_display_timer ( & ax25 - > idletimer ) / ( 60 * HZ ) ;
ax25_info . n2count = ax25 - > n2count ;
ax25_info . state = ax25 - > state ;
ax25_info . rcv_q = atomic_read ( & sk - > sk_rmem_alloc ) ;
ax25_info . snd_q = atomic_read ( & sk - > sk_wmem_alloc ) ;
ax25_info . vs = ax25 - > vs ;
ax25_info . vr = ax25 - > vr ;
ax25_info . va = ax25 - > va ;
ax25_info . vs_max = ax25 - > vs ; /* reserved */
ax25_info . paclen = ax25 - > paclen ;
ax25_info . window = ax25 - > window ;
/* old structure? */
if ( cmd = = SIOCAX25GETINFOOLD ) {
static int warned = 0 ;
if ( ! warned ) {
printk ( KERN_INFO " %s uses old SIOCAX25GETINFO \n " ,
current - > comm ) ;
warned = 1 ;
}
if ( copy_to_user ( argp , & ax25_info , sizeof ( struct ax25_info_struct_deprecated ) ) ) {
res = - EFAULT ;
break ;
}
} else {
if ( copy_to_user ( argp , & ax25_info , sizeof ( struct ax25_info_struct ) ) ) {
res = - EINVAL ;
break ;
}
}
res = 0 ;
break ;
}
case SIOCAX25ADDFWD :
case SIOCAX25DELFWD : {
struct ax25_fwd_struct ax25_fwd ;
if ( ! capable ( CAP_NET_ADMIN ) ) {
res = - EPERM ;
break ;
}
if ( copy_from_user ( & ax25_fwd , argp , sizeof ( ax25_fwd ) ) ) {
res = - EFAULT ;
break ;
}
res = ax25_fwd_ioctl ( cmd , & ax25_fwd ) ;
break ;
}
case SIOCGIFADDR :
case SIOCSIFADDR :
case SIOCGIFDSTADDR :
case SIOCSIFDSTADDR :
case SIOCGIFBRDADDR :
case SIOCSIFBRDADDR :
case SIOCGIFNETMASK :
case SIOCSIFNETMASK :
case SIOCGIFMETRIC :
case SIOCSIFMETRIC :
res = - EINVAL ;
break ;
default :
res = dev_ioctl ( cmd , argp ) ;
break ;
}
release_sock ( sk ) ;
return res ;
}
# ifdef CONFIG_PROC_FS
static void * ax25_info_start ( struct seq_file * seq , loff_t * pos )
{
struct ax25_cb * ax25 ;
struct hlist_node * node ;
int i = 0 ;
spin_lock_bh ( & ax25_list_lock ) ;
ax25_for_each ( ax25 , node , & ax25_list ) {
if ( i = = * pos )
return ax25 ;
+ + i ;
}
return NULL ;
}
static void * ax25_info_next ( struct seq_file * seq , void * v , loff_t * pos )
{
+ + * pos ;
return hlist_entry ( ( ( struct ax25_cb * ) v ) - > ax25_node . next ,
struct ax25_cb , ax25_node ) ;
}
static void ax25_info_stop ( struct seq_file * seq , void * v )
{
spin_unlock_bh ( & ax25_list_lock ) ;
}
static int ax25_info_show ( struct seq_file * seq , void * v )
{
ax25_cb * ax25 = v ;
int k ;
/*
* New format :
* magic dev src_addr dest_addr , digi1 , digi2 , . . st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd - Q Rcv - Q inode
*/
seq_printf ( seq , " %8.8lx %s %s%s " ,
( long ) ax25 ,
ax25 - > ax25_dev = = NULL ? " ??? " : ax25 - > ax25_dev - > dev - > name ,
ax2asc ( & ax25 - > source_addr ) ,
ax25 - > iamdigi ? " * " : " " ) ;
seq_printf ( seq , " %s " , ax2asc ( & ax25 - > dest_addr ) ) ;
for ( k = 0 ; ( ax25 - > digipeat ! = NULL ) & & ( k < ax25 - > digipeat - > ndigi ) ; k + + ) {
seq_printf ( seq , " ,%s%s " ,
ax2asc ( & ax25 - > digipeat - > calls [ k ] ) ,
ax25 - > digipeat - > repeated [ k ] ? " * " : " " ) ;
}
seq_printf ( seq , " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d " ,
ax25 - > state ,
ax25 - > vs , ax25 - > vr , ax25 - > va ,
ax25_display_timer ( & ax25 - > t1timer ) / HZ , ax25 - > t1 / HZ ,
ax25_display_timer ( & ax25 - > t2timer ) / HZ , ax25 - > t2 / HZ ,
ax25_display_timer ( & ax25 - > t3timer ) / HZ , ax25 - > t3 / HZ ,
ax25_display_timer ( & ax25 - > idletimer ) / ( 60 * HZ ) ,
ax25 - > idle / ( 60 * HZ ) ,
ax25 - > n2count , ax25 - > n2 ,
ax25 - > rtt / HZ ,
ax25 - > window ,
ax25 - > paclen ) ;
if ( ax25 - > sk ! = NULL ) {
bh_lock_sock ( ax25 - > sk ) ;
seq_printf ( seq , " %d %d %ld \n " ,
atomic_read ( & ax25 - > sk - > sk_wmem_alloc ) ,
atomic_read ( & ax25 - > sk - > sk_rmem_alloc ) ,
ax25 - > sk - > sk_socket ! = NULL ? SOCK_INODE ( ax25 - > sk - > sk_socket ) - > i_ino : 0L ) ;
bh_unlock_sock ( ax25 - > sk ) ;
} else {
seq_puts ( seq , " * * * \n " ) ;
}
return 0 ;
}
static struct seq_operations ax25_info_seqops = {
. start = ax25_info_start ,
. next = ax25_info_next ,
. stop = ax25_info_stop ,
. show = ax25_info_show ,
} ;
static int ax25_info_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & ax25_info_seqops ) ;
}
static struct file_operations ax25_info_fops = {
. owner = THIS_MODULE ,
. open = ax25_info_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
# endif
static struct net_proto_family ax25_family_ops = {
. family = PF_AX25 ,
. create = ax25_create ,
. owner = THIS_MODULE ,
} ;
static struct proto_ops ax25_proto_ops = {
. family = PF_AX25 ,
. owner = THIS_MODULE ,
. release = ax25_release ,
. bind = ax25_bind ,
. connect = ax25_connect ,
. socketpair = sock_no_socketpair ,
. accept = ax25_accept ,
. getname = ax25_getname ,
. poll = datagram_poll ,
. ioctl = ax25_ioctl ,
. listen = ax25_listen ,
. shutdown = ax25_shutdown ,
. setsockopt = ax25_setsockopt ,
. getsockopt = ax25_getsockopt ,
. sendmsg = ax25_sendmsg ,
. recvmsg = ax25_recvmsg ,
. mmap = sock_no_mmap ,
. sendpage = sock_no_sendpage ,
} ;
/*
* Called by socket . c on kernel start up
*/
static struct packet_type ax25_packet_type = {
. type = __constant_htons ( ETH_P_AX25 ) ,
. dev = NULL , /* All devices */
. func = ax25_kiss_rcv ,
} ;
static struct notifier_block ax25_dev_notifier = {
. notifier_call = ax25_device_event ,
} ;
EXPORT_SYMBOL ( ax25_encapsulate ) ;
EXPORT_SYMBOL ( ax25_rebuild_header ) ;
EXPORT_SYMBOL ( ax25_findbyuid ) ;
EXPORT_SYMBOL ( ax25_find_cb ) ;
EXPORT_SYMBOL ( ax25_linkfail_register ) ;
EXPORT_SYMBOL ( ax25_linkfail_release ) ;
EXPORT_SYMBOL ( ax25_listen_register ) ;
EXPORT_SYMBOL ( ax25_listen_release ) ;
EXPORT_SYMBOL ( ax25_protocol_register ) ;
EXPORT_SYMBOL ( ax25_protocol_release ) ;
EXPORT_SYMBOL ( ax25_send_frame ) ;
EXPORT_SYMBOL ( ax25_uid_policy ) ;
EXPORT_SYMBOL ( ax25cmp ) ;
EXPORT_SYMBOL ( ax2asc ) ;
EXPORT_SYMBOL ( asc2ax ) ;
EXPORT_SYMBOL ( null_ax25_address ) ;
EXPORT_SYMBOL ( ax25_display_timer ) ;
static int __init ax25_init ( void )
{
int rc = proto_register ( & ax25_proto , 0 ) ;
if ( rc ! = 0 )
goto out ;
sock_register ( & ax25_family_ops ) ;
dev_add_pack ( & ax25_packet_type ) ;
register_netdevice_notifier ( & ax25_dev_notifier ) ;
ax25_register_sysctl ( ) ;
proc_net_fops_create ( " ax25_route " , S_IRUGO , & ax25_route_fops ) ;
proc_net_fops_create ( " ax25 " , S_IRUGO , & ax25_info_fops ) ;
proc_net_fops_create ( " ax25_calls " , S_IRUGO , & ax25_uid_fops ) ;
out :
return rc ;
}
module_init ( ax25_init ) ;
MODULE_AUTHOR ( " Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk> " ) ;
MODULE_DESCRIPTION ( " The amateur radio AX.25 link layer protocol " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_NETPROTO ( PF_AX25 ) ;
static void __exit ax25_exit ( void )
{
proc_net_remove ( " ax25_route " ) ;
proc_net_remove ( " ax25 " ) ;
proc_net_remove ( " ax25_calls " ) ;
ax25_rt_free ( ) ;
ax25_uid_free ( ) ;
ax25_dev_free ( ) ;
ax25_unregister_sysctl ( ) ;
unregister_netdevice_notifier ( & ax25_dev_notifier ) ;
dev_remove_pack ( & ax25_packet_type ) ;
sock_unregister ( PF_AX25 ) ;
proto_unregister ( & ax25_proto ) ;
}
module_exit ( ax25_exit ) ;