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 ) Jonathan Naylor G4KLX ( g4klx @ g4klx . demon . co . uk )
*/
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/jiffies.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <net/ax25.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <net/sock.h>
# include <asm/system.h>
# include <linux/fcntl.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/netfilter.h>
# include <net/rose.h>
static void rose_ftimer_expiry ( unsigned long ) ;
static void rose_t0timer_expiry ( unsigned long ) ;
static void rose_transmit_restart_confirmation ( struct rose_neigh * neigh ) ;
static void rose_transmit_restart_request ( struct rose_neigh * neigh ) ;
void rose_start_ftimer ( struct rose_neigh * neigh )
{
del_timer ( & neigh - > ftimer ) ;
neigh - > ftimer . data = ( unsigned long ) neigh ;
neigh - > ftimer . function = & rose_ftimer_expiry ;
2006-05-04 10:28:20 +04:00
neigh - > ftimer . expires =
jiffies + msecs_to_jiffies ( sysctl_rose_link_fail_timeout ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & neigh - > ftimer ) ;
}
static void rose_start_t0timer ( struct rose_neigh * neigh )
{
del_timer ( & neigh - > t0timer ) ;
neigh - > t0timer . data = ( unsigned long ) neigh ;
neigh - > t0timer . function = & rose_t0timer_expiry ;
2006-05-04 10:28:20 +04:00
neigh - > t0timer . expires =
jiffies + msecs_to_jiffies ( sysctl_rose_restart_request_timeout ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & neigh - > t0timer ) ;
}
void rose_stop_ftimer ( struct rose_neigh * neigh )
{
del_timer ( & neigh - > ftimer ) ;
}
void rose_stop_t0timer ( struct rose_neigh * neigh )
{
del_timer ( & neigh - > t0timer ) ;
}
int rose_ftimer_running ( struct rose_neigh * neigh )
{
return timer_pending ( & neigh - > ftimer ) ;
}
static int rose_t0timer_running ( struct rose_neigh * neigh )
{
return timer_pending ( & neigh - > t0timer ) ;
}
static void rose_ftimer_expiry ( unsigned long param )
{
}
static void rose_t0timer_expiry ( unsigned long param )
{
struct rose_neigh * neigh = ( struct rose_neigh * ) param ;
rose_transmit_restart_request ( neigh ) ;
neigh - > dce_mode = 0 ;
rose_start_t0timer ( neigh ) ;
}
/*
* Interface to ax25_send_frame . Changes my level 2 callsign depending
* on whether we have a global ROSE callsign or use the default port
* callsign .
*/
static int rose_send_frame ( struct sk_buff * skb , struct rose_neigh * neigh )
{
ax25_address * rose_call ;
if ( ax25cmp ( & rose_callsign , & null_ax25_address ) = = 0 )
rose_call = ( ax25_address * ) neigh - > dev - > dev_addr ;
else
rose_call = & rose_callsign ;
neigh - > ax25 = ax25_send_frame ( skb , 260 , rose_call , & neigh - > callsign , neigh - > digipeat , neigh - > dev ) ;
return ( neigh - > ax25 ! = NULL ) ;
}
/*
* Interface to ax25_link_up . Changes my level 2 callsign depending
* on whether we have a global ROSE callsign or use the default port
* callsign .
*/
static int rose_link_up ( struct rose_neigh * neigh )
{
ax25_address * rose_call ;
if ( ax25cmp ( & rose_callsign , & null_ax25_address ) = = 0 )
rose_call = ( ax25_address * ) neigh - > dev - > dev_addr ;
else
rose_call = & rose_callsign ;
neigh - > ax25 = ax25_find_cb ( rose_call , & neigh - > callsign , neigh - > digipeat , neigh - > dev ) ;
return ( neigh - > ax25 ! = NULL ) ;
}
/*
* This handles all restart and diagnostic frames .
*/
void rose_link_rx_restart ( struct sk_buff * skb , struct rose_neigh * neigh , unsigned short frametype )
{
struct sk_buff * skbn ;
switch ( frametype ) {
case ROSE_RESTART_REQUEST :
rose_stop_t0timer ( neigh ) ;
neigh - > restarted = 1 ;
neigh - > dce_mode = ( skb - > data [ 3 ] = = ROSE_DTE_ORIGINATED ) ;
rose_transmit_restart_confirmation ( neigh ) ;
break ;
case ROSE_RESTART_CONFIRMATION :
rose_stop_t0timer ( neigh ) ;
neigh - > restarted = 1 ;
break ;
case ROSE_DIAGNOSTIC :
printk ( KERN_WARNING " ROSE: received diagnostic #%d - %02X %02X %02X \n " , skb - > data [ 3 ] , skb - > data [ 4 ] , skb - > data [ 5 ] , skb - > data [ 6 ] ) ;
break ;
default :
printk ( KERN_WARNING " ROSE: received unknown %02X with LCI 000 \n " , frametype ) ;
break ;
}
if ( neigh - > restarted ) {
while ( ( skbn = skb_dequeue ( & neigh - > queue ) ) ! = NULL )
if ( ! rose_send_frame ( skbn , neigh ) )
kfree_skb ( skbn ) ;
}
}
/*
* This routine is called when a Restart Request is needed
*/
static void rose_transmit_restart_request ( struct rose_neigh * neigh )
{
struct sk_buff * skb ;
unsigned char * dptr ;
int len ;
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3 ;
if ( ( skb = alloc_skb ( len , GFP_ATOMIC ) ) = = NULL )
return ;
skb_reserve ( skb , AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN ) ;
dptr = skb_put ( skb , ROSE_MIN_LEN + 3 ) ;
* dptr + + = AX25_P_ROSE ;
* dptr + + = ROSE_GFI ;
* dptr + + = 0x00 ;
* dptr + + = ROSE_RESTART_REQUEST ;
* dptr + + = ROSE_DTE_ORIGINATED ;
* dptr + + = 0 ;
if ( ! rose_send_frame ( skb , neigh ) )
kfree_skb ( skb ) ;
}
/*
* This routine is called when a Restart Confirmation is needed
*/
static void rose_transmit_restart_confirmation ( struct rose_neigh * neigh )
{
struct sk_buff * skb ;
unsigned char * dptr ;
int len ;
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1 ;
if ( ( skb = alloc_skb ( len , GFP_ATOMIC ) ) = = NULL )
return ;
skb_reserve ( skb , AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN ) ;
dptr = skb_put ( skb , ROSE_MIN_LEN + 1 ) ;
* dptr + + = AX25_P_ROSE ;
* dptr + + = ROSE_GFI ;
* dptr + + = 0x00 ;
* dptr + + = ROSE_RESTART_CONFIRMATION ;
if ( ! rose_send_frame ( skb , neigh ) )
kfree_skb ( skb ) ;
}
/*
* This routine is called when a Clear Request is needed outside of the context
* of a connected socket .
*/
void rose_transmit_clear_request ( struct rose_neigh * neigh , unsigned int lci , unsigned char cause , unsigned char diagnostic )
{
struct sk_buff * skb ;
unsigned char * dptr ;
int len ;
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3 ;
if ( ( skb = alloc_skb ( len , GFP_ATOMIC ) ) = = NULL )
return ;
skb_reserve ( skb , AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN ) ;
dptr = skb_put ( skb , ROSE_MIN_LEN + 3 ) ;
* dptr + + = AX25_P_ROSE ;
* dptr + + = ( ( lci > > 8 ) & 0x0F ) | ROSE_GFI ;
* dptr + + = ( ( lci > > 0 ) & 0xFF ) ;
* dptr + + = ROSE_CLEAR_REQUEST ;
* dptr + + = cause ;
* dptr + + = diagnostic ;
if ( ! rose_send_frame ( skb , neigh ) )
kfree_skb ( skb ) ;
}
void rose_transmit_link ( struct sk_buff * skb , struct rose_neigh * neigh )
{
unsigned char * dptr ;
#if 0
if ( call_fw_firewall ( PF_ROSE , skb - > dev , skb - > data , NULL , & skb ) ! = FW_ACCEPT ) {
kfree_skb ( skb ) ;
return ;
}
# endif
if ( neigh - > loopback ) {
rose_loopback_queue ( skb , neigh ) ;
return ;
}
if ( ! rose_link_up ( neigh ) )
neigh - > restarted = 0 ;
dptr = skb_push ( skb , 1 ) ;
* dptr + + = AX25_P_ROSE ;
if ( neigh - > restarted ) {
if ( ! rose_send_frame ( skb , neigh ) )
kfree_skb ( skb ) ;
} else {
skb_queue_tail ( & neigh - > queue , skb ) ;
if ( ! rose_t0timer_running ( neigh ) ) {
rose_transmit_restart_request ( neigh ) ;
neigh - > dce_mode = 0 ;
rose_start_t0timer ( neigh ) ;
}
}
}