2007-10-16 12:27:29 +04:00
/*
* Copyright ( C ) 2001 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2005-04-17 02:20:36 +04:00
* Licensed under the GPL
*/
# include <stdio.h>
2007-10-16 12:27:29 +04:00
# include <unistd.h>
# include <stdarg.h>
2005-04-17 02:20:36 +04:00
# include <errno.h>
2007-10-16 12:27:29 +04:00
# include <stddef.h>
2005-04-17 02:20:36 +04:00
# include <string.h>
# include <sys/socket.h>
# include <sys/wait.h>
2012-10-08 06:27:32 +04:00
# include <net_user.h>
# include <os.h>
# include <um_malloc.h>
2005-04-17 02:20:36 +04:00
int tap_open_common ( void * dev , char * gate_addr )
{
int tap_addr [ 4 ] ;
2007-10-16 12:27:29 +04:00
if ( gate_addr = = NULL )
2006-07-10 15:45:14 +04:00
return 0 ;
2007-10-16 12:27:29 +04:00
if ( sscanf ( gate_addr , " %d.%d.%d.%d " , & tap_addr [ 0 ] ,
& tap_addr [ 1 ] , & tap_addr [ 2 ] , & tap_addr [ 3 ] ) ! = 4 ) {
printk ( UM_KERN_ERR " Invalid tap IP address - '%s' \n " ,
gate_addr ) ;
2006-07-10 15:45:14 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-07-10 15:45:14 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-06-09 02:48:01 +04:00
void tap_check_ips ( char * gate_addr , unsigned char * eth_addr )
2005-04-17 02:20:36 +04:00
{
int tap_addr [ 4 ] ;
2007-10-16 12:27:29 +04:00
if ( ( gate_addr ! = NULL ) & &
( sscanf ( gate_addr , " %d.%d.%d.%d " , & tap_addr [ 0 ] ,
& tap_addr [ 1 ] , & tap_addr [ 2 ] , & tap_addr [ 3 ] ) = = 4 ) & &
( eth_addr [ 0 ] = = tap_addr [ 0 ] ) & &
( eth_addr [ 1 ] = = tap_addr [ 1 ] ) & &
( eth_addr [ 2 ] = = tap_addr [ 2 ] ) & &
( eth_addr [ 3 ] = = tap_addr [ 3 ] ) ) {
printk ( UM_KERN_ERR " The tap IP address and the UML eth IP "
" address must be different \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-02-25 00:03:57 +03:00
/* Do reliable error handling as this fails frequently enough. */
2005-04-17 02:20:36 +04:00
void read_output ( int fd , char * output , int len )
{
2006-02-25 00:03:57 +03:00
int remain , ret , expected ;
2005-04-17 02:20:36 +04:00
char c ;
2006-02-25 00:03:57 +03:00
char * str ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:29 +04:00
if ( output = = NULL ) {
2005-04-17 02:20:36 +04:00
output = & c ;
len = sizeof ( c ) ;
}
2007-10-16 12:27:29 +04:00
2005-04-17 02:20:36 +04:00
* output = ' \0 ' ;
2007-10-16 12:27:29 +04:00
ret = read ( fd , & remain , sizeof ( remain ) ) ;
2006-02-25 00:03:57 +03:00
if ( ret ! = sizeof ( remain ) ) {
2007-10-16 12:27:29 +04:00
if ( ret < 0 )
ret = - errno ;
2006-02-25 00:03:57 +03:00
expected = sizeof ( remain ) ;
str = " length " ;
goto err ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:29 +04:00
while ( remain ! = 0 ) {
2006-02-25 00:03:57 +03:00
expected = ( remain < len ) ? remain : len ;
2007-10-16 12:27:29 +04:00
ret = read ( fd , output , expected ) ;
2006-02-25 00:03:57 +03:00
if ( ret ! = expected ) {
2007-10-16 12:27:29 +04:00
if ( ret < 0 )
ret = - errno ;
2006-02-25 00:03:57 +03:00
str = " data " ;
goto err ;
2005-04-17 02:20:36 +04:00
}
2006-02-25 00:03:57 +03:00
remain - = ret ;
2005-04-17 02:20:36 +04:00
}
2006-02-25 00:03:57 +03:00
2005-04-17 02:20:36 +04:00
return ;
2006-02-25 00:03:57 +03:00
err :
if ( ret < 0 )
2007-10-16 12:27:29 +04:00
printk ( UM_KERN_ERR " read_output - read of %s failed, "
" errno = %d \n " , str , - ret ) ;
2006-02-25 00:03:57 +03:00
else
2007-10-16 12:27:29 +04:00
printk ( UM_KERN_ERR " read_output - read of %s failed, read only "
" %d of %d bytes \n " , str , ret , expected ) ;
2005-04-17 02:20:36 +04:00
}
int net_read ( int fd , void * buf , int len )
{
int n ;
2007-10-16 12:27:29 +04:00
n = read ( fd , buf , len ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:29 +04:00
if ( ( n < 0 ) & & ( errno = = EAGAIN ) )
2006-07-10 15:45:14 +04:00
return 0 ;
2007-10-16 12:27:29 +04:00
else if ( n = = 0 )
2006-07-10 15:45:14 +04:00
return - ENOTCONN ;
return n ;
2005-04-17 02:20:36 +04:00
}
int net_recvfrom ( int fd , void * buf , int len )
{
int n ;
2006-07-10 15:45:15 +04:00
CATCH_EINTR ( n = recvfrom ( fd , buf , len , 0 , NULL , NULL ) ) ;
2007-10-16 12:27:29 +04:00
if ( n < 0 ) {
if ( errno = = EAGAIN )
2006-07-10 15:45:14 +04:00
return 0 ;
return - errno ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:29 +04:00
else if ( n = = 0 )
2006-07-10 15:45:14 +04:00
return - ENOTCONN ;
return n ;
2005-04-17 02:20:36 +04:00
}
int net_write ( int fd , void * buf , int len )
{
int n ;
2007-10-16 12:27:29 +04:00
n = write ( fd , buf , len ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:29 +04:00
if ( ( n < 0 ) & & ( errno = = EAGAIN ) )
2006-07-10 15:45:14 +04:00
return 0 ;
2007-10-16 12:27:29 +04:00
else if ( n = = 0 )
2006-07-10 15:45:14 +04:00
return - ENOTCONN ;
return n ;
2005-04-17 02:20:36 +04:00
}
int net_send ( int fd , void * buf , int len )
{
int n ;
2006-07-10 15:45:15 +04:00
CATCH_EINTR ( n = send ( fd , buf , len , 0 ) ) ;
2007-10-16 12:27:29 +04:00
if ( n < 0 ) {
if ( errno = = EAGAIN )
2006-07-10 15:45:14 +04:00
return 0 ;
return - errno ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:29 +04:00
else if ( n = = 0 )
2006-07-10 15:45:14 +04:00
return - ENOTCONN ;
return n ;
2005-04-17 02:20:36 +04:00
}
int net_sendto ( int fd , void * buf , int len , void * to , int sock_len )
{
int n ;
2006-07-10 15:45:15 +04:00
CATCH_EINTR ( n = sendto ( fd , buf , len , 0 , ( struct sockaddr * ) to ,
sock_len ) ) ;
2007-10-16 12:27:29 +04:00
if ( n < 0 ) {
if ( errno = = EAGAIN )
2006-07-10 15:45:14 +04:00
return 0 ;
return - errno ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:29 +04:00
else if ( n = = 0 )
2006-07-10 15:45:14 +04:00
return - ENOTCONN ;
return n ;
2005-04-17 02:20:36 +04:00
}
struct change_pre_exec_data {
int close_me ;
int stdout ;
} ;
static void change_pre_exec ( void * arg )
{
struct change_pre_exec_data * data = arg ;
2007-10-16 12:27:29 +04:00
close ( data - > close_me ) ;
2005-04-17 02:20:36 +04:00
dup2 ( data - > stdout , 1 ) ;
}
static int change_tramp ( char * * argv , char * output , int output_len )
{
int pid , fds [ 2 ] , err ;
struct change_pre_exec_data pe_data ;
err = os_pipe ( fds , 1 , 0 ) ;
2007-10-16 12:27:29 +04:00
if ( err < 0 ) {
printk ( UM_KERN_ERR " change_tramp - pipe failed, err = %d \n " ,
- err ) ;
2006-07-10 15:45:14 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
pe_data . close_me = fds [ 0 ] ;
pe_data . stdout = fds [ 1 ] ;
2007-07-16 10:38:56 +04:00
pid = run_helper ( change_pre_exec , & pe_data , argv ) ;
2005-04-17 02:20:36 +04:00
2006-04-11 09:53:37 +04:00
if ( pid > 0 ) /* Avoid hang as we won't get data in failure case. */
read_output ( fds [ 0 ] , output , output_len ) ;
2007-10-16 12:27:29 +04:00
close ( fds [ 0 ] ) ;
close ( fds [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
if ( pid > 0 )
2008-02-05 09:31:10 +03:00
helper_wait ( pid ) ;
2006-07-10 15:45:14 +04:00
return pid ;
2005-04-17 02:20:36 +04:00
}
static void change ( char * dev , char * what , unsigned char * addr ,
unsigned char * netmask )
{
char addr_buf [ sizeof ( " 255.255.255.255 \0 " ) ] ;
char netmask_buf [ sizeof ( " 255.255.255.255 \0 " ) ] ;
char version [ sizeof ( " nnnnn \0 " ) ] ;
2007-10-16 12:27:29 +04:00
char * argv [ ] = { " uml_net " , version , what , dev , addr_buf ,
2005-04-17 02:20:36 +04:00
netmask_buf , NULL } ;
char * output ;
int output_len , pid ;
sprintf ( version , " %d " , UML_NET_VERSION ) ;
sprintf ( addr_buf , " %d.%d.%d.%d " , addr [ 0 ] , addr [ 1 ] , addr [ 2 ] , addr [ 3 ] ) ;
2007-10-16 12:27:29 +04:00
sprintf ( netmask_buf , " %d.%d.%d.%d " , netmask [ 0 ] , netmask [ 1 ] ,
2005-04-17 02:20:36 +04:00
netmask [ 2 ] , netmask [ 3 ] ) ;
2007-05-07 01:51:22 +04:00
output_len = UM_KERN_PAGE_SIZE ;
2008-05-13 01:01:52 +04:00
output = uml_kmalloc ( output_len , UM_GFP_KERNEL ) ;
2007-10-16 12:27:29 +04:00
if ( output = = NULL )
printk ( UM_KERN_ERR " change : failed to allocate output "
" buffer \n " ) ;
2005-04-17 02:20:36 +04:00
pid = change_tramp ( argv , output , output_len ) ;
2011-07-26 04:12:49 +04:00
if ( pid < 0 ) {
kfree ( output ) ;
return ;
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:29 +04:00
if ( output ! = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( " %s " , output ) ;
kfree ( output ) ;
}
}
void open_addr ( unsigned char * addr , unsigned char * netmask , void * arg )
{
change ( arg , " add " , addr , netmask ) ;
}
void close_addr ( unsigned char * addr , unsigned char * netmask , void * arg )
{
change ( arg , " del " , addr , netmask ) ;
}
char * split_if_spec ( char * str , . . . )
{
char * * arg , * end ;
va_list ap ;
va_start ( ap , str ) ;
2007-10-16 12:27:29 +04:00
while ( ( arg = va_arg ( ap , char * * ) ) ! = NULL ) {
if ( * str = = ' \0 ' )
2006-07-10 15:45:14 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
end = strchr ( str , ' , ' ) ;
2007-10-16 12:27:29 +04:00
if ( end ! = str )
2005-04-17 02:20:36 +04:00
* arg = str ;
2007-10-16 12:27:29 +04:00
if ( end = = NULL )
2006-07-10 15:45:14 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
* end + + = ' \0 ' ;
str = end ;
}
va_end ( ap ) ;
2006-07-10 15:45:14 +04:00
return str ;
2005-04-17 02:20:36 +04:00
}