2005-04-16 15:20:36 -07:00
/* net/atm/addr.c - Local ATM address registry */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
# include <linux/atm.h>
# include <linux/atmdev.h>
# include <asm/uaccess.h>
# include "signaling.h"
# include "addr.h"
2008-06-17 16:20:06 -07:00
static int check_addr ( const struct sockaddr_atmsvc * addr )
2005-04-16 15:20:36 -07:00
{
int i ;
if ( addr - > sas_family ! = AF_ATMSVC )
return - EAFNOSUPPORT ;
if ( ! * addr - > sas_addr . pub )
return * addr - > sas_addr . prv ? 0 : - EINVAL ;
for ( i = 1 ; i < ATM_E164_LEN + 1 ; i + + ) /* make sure it's \0-terminated */
if ( ! addr - > sas_addr . pub [ i ] )
return 0 ;
return - EINVAL ;
}
2008-06-17 16:20:06 -07:00
static int identical ( const struct sockaddr_atmsvc * a , const struct sockaddr_atmsvc * b )
2005-04-16 15:20:36 -07:00
{
if ( * a - > sas_addr . prv )
if ( memcmp ( a - > sas_addr . prv , b - > sas_addr . prv , ATM_ESA_LEN ) )
return 0 ;
if ( ! * a - > sas_addr . pub )
return ! * b - > sas_addr . pub ;
if ( ! * b - > sas_addr . pub )
return 0 ;
return ! strcmp ( a - > sas_addr . pub , b - > sas_addr . pub ) ;
}
2008-06-17 16:20:06 -07:00
static void notify_sigd ( const struct atm_dev * dev )
2005-04-16 15:20:36 -07:00
{
struct sockaddr_atmpvc pvc ;
pvc . sap_addr . itf = dev - > number ;
sigd_enq ( NULL , as_itf_notify , NULL , & pvc , NULL ) ;
}
2005-10-06 22:19:28 -07:00
void atm_reset_addr ( struct atm_dev * dev , enum atm_addr_type_t atype )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
struct atm_dev_addr * this , * p ;
2005-10-06 22:19:28 -07:00
struct list_head * head ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( atype = = ATM_ADDR_LECS )
head = & dev - > lecs ;
else
head = & dev - > local ;
list_for_each_entry_safe ( this , p , head , entry ) {
2005-09-28 16:35:22 -07:00
list_del ( & this - > entry ) ;
kfree ( this ) ;
}
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( head = = & dev - > local )
notify_sigd ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2008-06-17 16:20:06 -07:00
int atm_add_addr ( struct atm_dev * dev , const struct sockaddr_atmsvc * addr ,
2005-10-06 22:19:28 -07:00
enum atm_addr_type_t atype )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
struct atm_dev_addr * this ;
2005-10-06 22:19:28 -07:00
struct list_head * head ;
2005-04-16 15:20:36 -07:00
int error ;
error = check_addr ( addr ) ;
if ( error )
return error ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( atype = = ATM_ADDR_LECS )
head = & dev - > lecs ;
else
head = & dev - > local ;
list_for_each_entry ( this , head , entry ) {
2005-04-16 15:20:36 -07:00
if ( identical ( & this - > addr , addr ) ) {
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
return - EEXIST ;
}
}
this = kmalloc ( sizeof ( struct atm_dev_addr ) , GFP_ATOMIC ) ;
if ( ! this ) {
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
return - ENOMEM ;
}
this - > addr = * addr ;
2005-10-06 22:19:28 -07:00
list_add ( & this - > entry , head ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( head = = & dev - > local )
notify_sigd ( dev ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-06-17 16:20:06 -07:00
int atm_del_addr ( struct atm_dev * dev , const struct sockaddr_atmsvc * addr ,
2005-10-06 22:19:28 -07:00
enum atm_addr_type_t atype )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
struct atm_dev_addr * this ;
2005-10-06 22:19:28 -07:00
struct list_head * head ;
2005-04-16 15:20:36 -07:00
int error ;
error = check_addr ( addr ) ;
if ( error )
return error ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( atype = = ATM_ADDR_LECS )
head = & dev - > lecs ;
else
head = & dev - > local ;
list_for_each_entry ( this , head , entry ) {
2005-04-16 15:20:36 -07:00
if ( identical ( & this - > addr , addr ) ) {
list_del ( & this - > entry ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
kfree ( this ) ;
2005-10-06 22:19:28 -07:00
if ( head = = & dev - > local )
notify_sigd ( dev ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
}
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
return - ENOENT ;
}
int atm_get_addr ( struct atm_dev * dev , struct sockaddr_atmsvc __user * buf ,
2005-10-06 22:19:28 -07:00
size_t size , enum atm_addr_type_t atype )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
struct atm_dev_addr * this ;
2005-10-06 22:19:28 -07:00
struct list_head * head ;
2005-04-16 15:20:36 -07:00
int total = 0 , error ;
struct sockaddr_atmsvc * tmp_buf , * tmp_bufp ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2005-10-06 22:19:28 -07:00
if ( atype = = ATM_ADDR_LECS )
head = & dev - > lecs ;
else
head = & dev - > local ;
list_for_each_entry ( this , head , entry )
2005-04-16 15:20:36 -07:00
total + = sizeof ( struct sockaddr_atmsvc ) ;
tmp_buf = tmp_bufp = kmalloc ( total , GFP_ATOMIC ) ;
if ( ! tmp_buf ) {
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
return - ENOMEM ;
}
2005-10-06 22:19:28 -07:00
list_for_each_entry ( this , head , entry )
2005-04-16 15:20:36 -07:00
memcpy ( tmp_bufp + + , & this - > addr , sizeof ( struct sockaddr_atmsvc ) ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
error = total > size ? - E2BIG : total ;
if ( copy_to_user ( buf , tmp_buf , total < size ? total : size ) )
error = - EFAULT ;
kfree ( tmp_buf ) ;
return error ;
}