2005-04-17 02:20:36 +04:00
/*
* X .25 Packet Layer release 002
*
* This is ALPHA test software . This code may break your machine ,
* randomly fail to work with new releases , misbehave and / or generally
2007-02-09 17:25:27 +03:00
* screw up . It might even work .
2005-04-17 02:20:36 +04:00
*
* This code REQUIRES 2.1 .15 or higher
*
* 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 .
*
* History
* X .25 001 Jonathan Naylor Started coding .
*/
# include <linux/if_arp.h>
# include <linux/init.h>
# include <net/x25.h>
struct list_head x25_route_list = LIST_HEAD_INIT ( x25_route_list ) ;
DEFINE_RWLOCK ( x25_route_list_lock ) ;
/*
* Add a new route .
*/
static int x25_add_route ( struct x25_address * address , unsigned int sigdigits ,
struct net_device * dev )
{
struct x25_route * rt ;
struct list_head * entry ;
int rc = - EINVAL ;
write_lock_bh ( & x25_route_list_lock ) ;
list_for_each ( entry , & x25_route_list ) {
rt = list_entry ( entry , struct x25_route , node ) ;
if ( ! memcmp ( & rt - > address , address , sigdigits ) & &
rt - > sigdigits = = sigdigits )
goto out ;
}
rt = kmalloc ( sizeof ( * rt ) , GFP_ATOMIC ) ;
rc = - ENOMEM ;
if ( ! rt )
goto out ;
strcpy ( rt - > address . x25_addr , " 000000000000000 " ) ;
memcpy ( rt - > address . x25_addr , address - > x25_addr , sigdigits ) ;
rt - > sigdigits = sigdigits ;
rt - > dev = dev ;
atomic_set ( & rt - > refcnt , 1 ) ;
list_add ( & rt - > node , & x25_route_list ) ;
rc = 0 ;
out :
write_unlock_bh ( & x25_route_list_lock ) ;
return rc ;
}
/**
* __x25_remove_route - remove route from x25_route_list
* @ rt - route to remove
*
* Remove route from x25_route_list . If it was there .
* Caller must hold x25_route_list_lock .
*/
static void __x25_remove_route ( struct x25_route * rt )
{
if ( rt - > node . next ) {
list_del ( & rt - > node ) ;
x25_route_put ( rt ) ;
}
}
static int x25_del_route ( struct x25_address * address , unsigned int sigdigits ,
struct net_device * dev )
{
struct x25_route * rt ;
struct list_head * entry ;
int rc = - EINVAL ;
write_lock_bh ( & x25_route_list_lock ) ;
list_for_each ( entry , & x25_route_list ) {
rt = list_entry ( entry , struct x25_route , node ) ;
if ( ! memcmp ( & rt - > address , address , sigdigits ) & &
rt - > sigdigits = = sigdigits & & rt - > dev = = dev ) {
__x25_remove_route ( rt ) ;
rc = 0 ;
break ;
}
}
write_unlock_bh ( & x25_route_list_lock ) ;
return rc ;
}
/*
* A device has been removed , remove its routes .
*/
void x25_route_device_down ( struct net_device * dev )
{
struct x25_route * rt ;
struct list_head * entry , * tmp ;
write_lock_bh ( & x25_route_list_lock ) ;
list_for_each_safe ( entry , tmp , & x25_route_list ) {
rt = list_entry ( entry , struct x25_route , node ) ;
if ( rt - > dev = = dev )
__x25_remove_route ( rt ) ;
}
write_unlock_bh ( & x25_route_list_lock ) ;
2007-02-09 00:34:02 +03:00
/* Remove any related forwarding */
x25_clear_forward_by_dev ( dev ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Check that the device given is a valid X .25 interface that is " up " .
*/
struct net_device * x25_dev_get ( char * devname )
{
2007-09-17 22:56:21 +04:00
struct net_device * dev = dev_get_by_name ( & init_net , devname ) ;
2005-04-17 02:20:36 +04:00
if ( dev & &
( ! ( dev - > flags & IFF_UP ) | | ( dev - > type ! = ARPHRD_X25
# if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
& & dev - > type ! = ARPHRD_ETHER
# endif
) ) )
dev_put ( dev ) ;
return dev ;
}
/**
* x25_get_route - Find a route given an X .25 address .
* @ addr - address to find a route for
*
* Find a route given an X .25 address .
*/
struct x25_route * x25_get_route ( struct x25_address * addr )
{
struct x25_route * rt , * use = NULL ;
struct list_head * entry ;
read_lock_bh ( & x25_route_list_lock ) ;
list_for_each ( entry , & x25_route_list ) {
rt = list_entry ( entry , struct x25_route , node ) ;
if ( ! memcmp ( & rt - > address , addr , rt - > sigdigits ) ) {
if ( ! use )
use = rt ;
else if ( rt - > sigdigits > use - > sigdigits )
use = rt ;
}
}
if ( use )
x25_route_hold ( use ) ;
read_unlock_bh ( & x25_route_list_lock ) ;
return use ;
}
/*
* Handle the ioctls that control the routing functions .
*/
int x25_route_ioctl ( unsigned int cmd , void __user * arg )
{
struct x25_route_struct rt ;
struct net_device * dev ;
int rc = - EINVAL ;
if ( cmd ! = SIOCADDRT & & cmd ! = SIOCDELRT )
goto out ;
rc = - EFAULT ;
if ( copy_from_user ( & rt , arg , sizeof ( rt ) ) )
goto out ;
rc = - EINVAL ;
if ( rt . sigdigits < 0 | | rt . sigdigits > 15 )
goto out ;
dev = x25_dev_get ( rt . device ) ;
if ( ! dev )
goto out ;
if ( cmd = = SIOCADDRT )
rc = x25_add_route ( & rt . address , rt . sigdigits , dev ) ;
else
rc = x25_del_route ( & rt . address , rt . sigdigits , dev ) ;
dev_put ( dev ) ;
out :
return rc ;
}
/*
* Release all memory associated with X .25 routing structures .
*/
void __exit x25_route_free ( void )
{
struct x25_route * rt ;
struct list_head * entry , * tmp ;
write_lock_bh ( & x25_route_list_lock ) ;
list_for_each_safe ( entry , tmp , & x25_route_list ) {
rt = list_entry ( entry , struct x25_route , node ) ;
__x25_remove_route ( rt ) ;
}
write_unlock_bh ( & x25_route_list_lock ) ;
}