2019-08-25 12:49:18 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
/*
2007-10-16 12:27:29 +04:00
* Copyright ( C ) 2001 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2007-05-07 01:51:33 +04:00
* Copyright ( C ) 2001 Lennert Buytenhek ( buytenh @ gnu . org ) and
2005-04-17 02:20:36 +04:00
* James Leu ( jleu @ mindspring . net ) .
* Copyright ( C ) 2001 by various other people who didn ' t put their name here .
*/
# include <stdio.h>
# include <unistd.h>
2007-10-16 12:27:29 +04:00
# include <errno.h>
# include <string.h>
2005-04-17 02:20:36 +04:00
# include <sys/socket.h>
# include <sys/wait.h>
# include "etap.h"
2012-10-08 06:27:32 +04:00
# include <os.h>
# include <net_user.h>
# include <um_malloc.h>
2005-04-17 02:20:36 +04:00
# define MAX_PACKET ETH_MAX_PACKET
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-07 01:51:04 +04:00
static int etap_user_init ( void * data , void * dev )
2005-04-17 02:20:36 +04:00
{
struct ethertap_data * pri = data ;
pri - > dev = dev ;
uml: network interface hotplug error handling
This fixes a number of problems associated with network interface hotplug.
The userspace initialization function can fail in some cases, but the
failure was never passed back to eth_configure, which proceeded with the
configuration. This results in a zombie device that is present, but can't
work. This is fixed by allowing the initialization routines to return an
error, which is checked, and the configuration aborted on failure.
eth_configure failed to check for many failures. Even when it did check,
it didn't undo whatever initializations has already happened, so a present,
but partially initialized and non-working device could result. It now
checks everything that can fail, and bails out, undoing whatever had been
done.
The return value of eth_configure was always ignored, so it is now just
void.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-07 01:51:04 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
struct addr_change {
enum { ADD_ADDR , DEL_ADDR } what ;
unsigned char addr [ 4 ] ;
unsigned char netmask [ 4 ] ;
} ;
static void etap_change ( int op , unsigned char * addr , unsigned char * netmask ,
int fd )
{
struct addr_change change ;
2006-04-11 09:53:32 +04:00
char * output ;
2005-04-17 02:20:36 +04:00
int n ;
change . what = op ;
memcpy ( change . addr , addr , sizeof ( change . addr ) ) ;
memcpy ( change . netmask , netmask , sizeof ( change . netmask ) ) ;
2007-05-07 01:51:35 +04:00
CATCH_EINTR ( n = write ( fd , & change , sizeof ( change ) ) ) ;
2007-10-16 12:27:29 +04:00
if ( n ! = sizeof ( change ) ) {
printk ( UM_KERN_ERR " etap_change - request failed, err = %d \n " ,
errno ) ;
2007-05-07 01:51:33 +04:00
return ;
}
2008-05-13 01:01:52 +04:00
output = uml_kmalloc ( UM_KERN_PAGE_SIZE , UM_GFP_KERNEL ) ;
2007-10-16 12:27:29 +04:00
if ( output = = NULL )
printk ( UM_KERN_ERR " etap_change : Failed to allocate output "
" buffer \n " ) ;
2007-05-07 01:51:22 +04:00
read_output ( fd , output , UM_KERN_PAGE_SIZE ) ;
2007-10-16 12:27:29 +04:00
if ( output ! = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( " %s " , output ) ;
kfree ( output ) ;
}
}
static void etap_open_addr ( unsigned char * addr , unsigned char * netmask ,
void * arg )
{
etap_change ( ADD_ADDR , addr , netmask , * ( ( int * ) arg ) ) ;
}
static void etap_close_addr ( unsigned char * addr , unsigned char * netmask ,
void * arg )
{
etap_change ( DEL_ADDR , addr , netmask , * ( ( int * ) arg ) ) ;
}
struct etap_pre_exec_data {
int control_remote ;
int control_me ;
int data_me ;
} ;
static void etap_pre_exec ( void * arg )
{
struct etap_pre_exec_data * data = arg ;
dup2 ( data - > control_remote , 1 ) ;
2007-10-16 12:27:11 +04:00
close ( data - > data_me ) ;
close ( data - > control_me ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:11 +04:00
static int etap_tramp ( char * dev , char * gate , int control_me ,
2005-04-17 02:20:36 +04:00
int control_remote , int data_me , int data_remote )
{
struct etap_pre_exec_data pe_data ;
2007-12-18 03:19:46 +03:00
int pid , err , n ;
2005-04-17 02:20:36 +04:00
char version_buf [ sizeof ( " nnnnn \0 " ) ] ;
char data_fd_buf [ sizeof ( " nnnnnn \0 " ) ] ;
char gate_buf [ sizeof ( " nnn.nnn.nnn.nnn \0 " ) ] ;
char * setup_args [ ] = { " uml_net " , version_buf , " ethertap " , dev ,
data_fd_buf , gate_buf , NULL } ;
2007-10-16 12:27:11 +04:00
char * nosetup_args [ ] = { " uml_net " , version_buf , " ethertap " ,
2005-04-17 02:20:36 +04:00
dev , data_fd_buf , NULL } ;
char * * args , c ;
sprintf ( data_fd_buf , " %d " , data_remote ) ;
sprintf ( version_buf , " %d " , UML_NET_VERSION ) ;
2007-10-16 12:27:29 +04:00
if ( gate ! = NULL ) {
2023-09-11 20:52:44 +03:00
strscpy ( gate_buf , gate , sizeof ( gate_buf ) ) ;
2005-04-17 02:20:36 +04:00
args = setup_args ;
}
else args = nosetup_args ;
err = 0 ;
pe_data . control_remote = control_remote ;
pe_data . control_me = control_me ;
pe_data . data_me = data_me ;
2007-07-16 10:38:56 +04:00
pid = run_helper ( etap_pre_exec , & pe_data , args ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:29 +04:00
if ( pid < 0 )
2007-05-07 01:51:33 +04:00
err = pid ;
2007-10-16 12:27:11 +04:00
close ( data_remote ) ;
close ( control_remote ) ;
2007-05-07 01:51:35 +04:00
CATCH_EINTR ( n = read ( control_me , & c , sizeof ( c ) ) ) ;
2007-10-16 12:27:29 +04:00
if ( n ! = sizeof ( c ) ) {
2007-05-07 01:51:35 +04:00
err = - errno ;
2007-10-16 12:27:29 +04:00
printk ( UM_KERN_ERR " etap_tramp : read of status failed, "
" err = %d \n " , - err ) ;
2007-05-07 01:51:35 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:29 +04:00
if ( c ! = 1 ) {
printk ( UM_KERN_ERR " etap_tramp : uml_net failed \n " ) ;
2008-02-05 09:31:10 +03:00
err = helper_wait ( pid ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:02 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int etap_open ( void * data )
{
struct ethertap_data * pri = data ;
char * output ;
int data_fds [ 2 ] , control_fds [ 2 ] , err , output_len ;
err = tap_open_common ( pri - > dev , pri - > gate_addr ) ;
2007-10-16 12:27:29 +04:00
if ( err )
2007-05-07 01:51:02 +04:00
return err ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:11 +04:00
err = socketpair ( AF_UNIX , SOCK_DGRAM , 0 , data_fds ) ;
2007-10-16 12:27:29 +04:00
if ( err ) {
2007-10-16 12:27:11 +04:00
err = - errno ;
2007-10-16 12:27:29 +04:00
printk ( UM_KERN_ERR " etap_open - data socketpair failed - "
" err = %d \n " , errno ) ;
2007-05-07 01:51:02 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:11 +04:00
err = socketpair ( AF_UNIX , SOCK_STREAM , 0 , control_fds ) ;
2007-10-16 12:27:29 +04:00
if ( err ) {
2007-10-16 12:27:11 +04:00
err = - errno ;
2007-10-16 12:27:29 +04:00
printk ( UM_KERN_ERR " etap_open - control socketpair failed - "
" err = %d \n " , errno ) ;
2007-10-16 12:27:11 +04:00
goto out_close_data ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:02 +04:00
2007-10-16 12:27:11 +04:00
err = etap_tramp ( pri - > dev_name , pri - > gate_addr , control_fds [ 0 ] ,
2005-04-17 02:20:36 +04:00
control_fds [ 1 ] , data_fds [ 0 ] , data_fds [ 1 ] ) ;
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 ) ;
2005-04-17 02:20:36 +04:00
read_output ( control_fds [ 0 ] , output , output_len ) ;
2007-10-16 12:27:29 +04:00
if ( output = = NULL )
printk ( UM_KERN_ERR " etap_open : failed to allocate output "
" buffer \n " ) ;
2005-04-17 02:20:36 +04:00
else {
printk ( " %s " , output ) ;
kfree ( output ) ;
}
2007-10-16 12:27:29 +04:00
if ( err < 0 ) {
printk ( UM_KERN_ERR " etap_tramp failed - err = %d \n " , - err ) ;
2007-10-16 12:27:11 +04:00
goto out_close_control ;
2005-04-17 02:20:36 +04:00
}
pri - > data_fd = data_fds [ 0 ] ;
pri - > control_fd = control_fds [ 0 ] ;
iter_addresses ( pri - > dev , etap_open_addr , & pri - > control_fd ) ;
2007-05-07 01:51:02 +04:00
return data_fds [ 0 ] ;
2007-10-16 12:27:11 +04:00
out_close_control :
close ( control_fds [ 0 ] ) ;
close ( control_fds [ 1 ] ) ;
out_close_data :
close ( data_fds [ 0 ] ) ;
close ( data_fds [ 1 ] ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
static void etap_close ( int fd , void * data )
{
struct ethertap_data * pri = data ;
iter_addresses ( pri - > dev , etap_close_addr , & pri - > control_fd ) ;
2007-10-16 12:27:11 +04:00
close ( fd ) ;
2007-10-16 12:27:29 +04:00
if ( shutdown ( pri - > data_fd , SHUT_RDWR ) < 0 )
printk ( UM_KERN_ERR " etap_close - shutdown data socket failed, "
2007-10-16 12:27:11 +04:00
" errno = %d \n " , errno ) ;
2007-10-16 12:27:29 +04:00
if ( shutdown ( pri - > control_fd , SHUT_RDWR ) < 0 )
printk ( UM_KERN_ERR " etap_close - shutdown control socket "
" failed, errno = %d \n " , errno ) ;
2007-10-16 12:27:11 +04:00
close ( pri - > data_fd ) ;
2005-04-17 02:20:36 +04:00
pri - > data_fd = - 1 ;
2007-10-16 12:27:11 +04:00
close ( pri - > control_fd ) ;
2005-04-17 02:20:36 +04:00
pri - > control_fd = - 1 ;
}
static void etap_add_addr ( unsigned char * addr , unsigned char * netmask ,
void * data )
{
struct ethertap_data * pri = data ;
tap_check_ips ( pri - > gate_addr , addr ) ;
2007-10-16 12:27:29 +04:00
if ( pri - > control_fd = = - 1 )
2007-05-07 01:51:02 +04:00
return ;
2005-04-17 02:20:36 +04:00
etap_open_addr ( addr , netmask , & pri - > control_fd ) ;
}
2007-10-16 12:27:11 +04:00
static void etap_del_addr ( unsigned char * addr , unsigned char * netmask ,
2005-04-17 02:20:36 +04:00
void * data )
{
struct ethertap_data * pri = data ;
2007-10-16 12:27:29 +04:00
if ( pri - > control_fd = = - 1 )
2007-05-07 01:51:02 +04:00
return ;
2007-10-16 12:27:11 +04:00
2005-04-17 02:20:36 +04:00
etap_close_addr ( addr , netmask , & pri - > control_fd ) ;
}
2006-09-27 12:50:33 +04:00
const struct net_user_info ethertap_user_info = {
2005-04-17 02:20:36 +04:00
. init = etap_user_init ,
. open = etap_open ,
. close = etap_close ,
. remove = NULL ,
. add_address = etap_add_addr ,
. delete_address = etap_del_addr ,
2007-10-16 12:27:31 +04:00
. mtu = ETH_MAX_PACKET ,
. max_packet = ETH_MAX_PACKET + ETH_HEADER_ETHERTAP ,
2005-04-17 02:20:36 +04:00
} ;