2009-07-28 18:46:53 +04:00
/*
Copyright Red Hat , Inc . 2006
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 , or ( at your option ) any
later version .
This program is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; see the file COPYING . If not , write to the
Free Software Foundation , Inc . , 675 Mass Ave , Cambridge ,
MA 0213 9 , USA .
*/
/*
* @ file fence_virtd . c : Implementation of server daemon for Xen virtual
* machine fencing . This uses SA AIS CKPT b .1 .0 checkpointing API to
* store virtual machine states .
*
* Author : Lon Hohberger < lhh at redhat . com >
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <signal.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <sys/un.h>
# include <sys/socket.h>
# include <sys/select.h>
# include <sys/ioctl.h>
# include <arpa/inet.h>
# include <net/if.h>
# include <netinet/in.h>
# include <netdb.h>
# include <sys/time.h>
# include <fcntl.h>
# include <errno.h>
# include <pthread.h>
# include <libgen.h>
# include <nss.h>
/* Local includes */
# include "xvm.h"
# include "ip_lookup.h"
# include "simple_auth.h"
# include "options.h"
# include "tcp.h"
# include "mcast.h"
# include "debug.h"
2009-12-07 20:55:48 +03:00
# include "fdops.h"
2009-07-28 18:46:53 +04:00
static int
tcp_wait_connect ( int lfd , int retry_tenths )
{
int fd ;
fd_set rfds ;
int n ;
struct timeval tv ;
dbg_printf ( 3 , " Waiting for connection from XVM host daemon. \n " ) ;
FD_ZERO ( & rfds ) ;
FD_SET ( lfd , & rfds ) ;
tv . tv_sec = retry_tenths / 10 ;
tv . tv_usec = ( retry_tenths % 10 ) * 100000 ;
n = select ( lfd + 1 , & rfds , NULL , NULL , & tv ) ;
if ( n = = 0 ) {
errno = ETIMEDOUT ;
return - 1 ;
} else if ( n < 0 ) {
return - 1 ;
}
fd = accept ( lfd , NULL , 0 ) ;
if ( fd < 0 )
return - 1 ;
return fd ;
}
2009-12-07 20:55:48 +03:00
void
do_read_hostlist ( int fd , int timeout )
{
host_state_t hinfo ;
fd_set rfds ;
struct timeval tv ;
int ret ;
do {
FD_ZERO ( & rfds ) ;
FD_SET ( fd , & rfds ) ;
tv . tv_sec = timeout ;
tv . tv_usec = 0 ;
ret = _select_retry ( fd + 1 , & rfds , NULL , NULL , & tv ) ;
if ( ret = = 0 ) {
printf ( " Timed out! \n " ) ;
break ;
}
ret = _read_retry ( fd , & hinfo , sizeof ( hinfo ) , & tv ) ;
if ( ret < sizeof ( hinfo ) ) {
printf ( " Bad read! \n " ) ;
break ;
}
if ( strlen ( ( char * ) hinfo . uuid ) = = 0 & &
strlen ( ( char * ) hinfo . domain ) = = 0 )
break ;
printf ( " %-20.20s %s %s \n " , hinfo . domain , hinfo . uuid ,
( hinfo . state = = 0 ) ? " off " : " on " ) ;
} while ( 1 ) ;
}
2009-07-28 18:46:53 +04:00
static int
tcp_exchange ( int fd , fence_auth_type_t auth , void * key ,
size_t key_len , int timeout )
{
fd_set rfds ;
struct timeval tv ;
2009-09-17 22:11:08 +04:00
char ret = 1 ;
2009-07-28 18:46:53 +04:00
/* Ok, we're connected */
dbg_printf ( 3 , " Issuing TCP challenge \n " ) ;
if ( tcp_challenge ( fd , auth , key , key_len , timeout ) < = 0 ) {
/* Challenge failed */
printf ( " Invalid response to challenge \n " ) ;
return 0 ;
}
/* Now they'll send us one, so we need to respond here */
dbg_printf ( 3 , " Responding to TCP challenge \n " ) ;
if ( tcp_response ( fd , auth , key , key_len , timeout ) < = 0 ) {
printf ( " Invalid response to challenge \n " ) ;
return 0 ;
}
dbg_printf ( 2 , " TCP Exchange + Authentication done... \n " ) ;
FD_ZERO ( & rfds ) ;
FD_SET ( fd , & rfds ) ;
tv . tv_sec = timeout ;
tv . tv_usec = 0 ;
ret = 1 ;
dbg_printf ( 3 , " Waiting for return value from XVM host \n " ) ;
2009-12-07 20:55:48 +03:00
if ( _select_retry ( fd + 1 , & rfds , NULL , NULL , & tv ) < = 0 )
2009-07-28 18:46:53 +04:00
return - 1 ;
/* Read return code */
2009-12-07 20:55:48 +03:00
if ( _read_retry ( fd , & ret , 1 , & tv ) < 0 )
2009-09-17 22:11:08 +04:00
ret = 1 ;
2009-12-07 20:55:48 +03:00
2010-01-13 17:26:32 +03:00
if ( ret = = ( char ) RESP_HOSTLIST ) /* hostlist */ {
2009-12-07 20:55:48 +03:00
do_read_hostlist ( fd , timeout ) ;
ret = 0 ;
}
2009-07-28 18:46:53 +04:00
close ( fd ) ;
2009-09-17 22:11:08 +04:00
2009-07-28 18:46:53 +04:00
return ret ;
}
static int
2009-09-15 18:28:20 +04:00
send_multicast_packets ( ip_list_t * ipl , fence_virt_args_t * args ,
uint32_t seqno , void * key , size_t key_len )
2009-07-28 18:46:53 +04:00
{
fence_req_t freq ;
int mc_sock ;
ip_addr_t * ipa ;
struct sockaddr_in tgt4 ;
struct sockaddr_in6 tgt6 ;
struct sockaddr * tgt ;
socklen_t tgt_len ;
for ( ipa = ipl - > tqh_first ; ipa ; ipa = ipa - > ipa_entries . tqe_next ) {
if ( ipa - > ipa_family ! = args - > net . family ) {
dbg_printf ( 2 , " Ignoring %s: wrong family \n " , ipa - > ipa_address ) ;
continue ;
}
if ( args - > net . family = = PF_INET ) {
mc_sock = ipv4_send_sk ( ipa - > ipa_address , args - > net . addr ,
args - > net . port ,
( struct sockaddr * ) & tgt4 ,
sizeof ( struct sockaddr_in ) ) ;
tgt = ( struct sockaddr * ) & tgt4 ;
tgt_len = sizeof ( tgt4 ) ;
} else if ( args - > net . family = = PF_INET6 ) {
mc_sock = ipv6_send_sk ( ipa - > ipa_address , args - > net . addr ,
args - > net . port ,
( struct sockaddr * ) & tgt6 ,
sizeof ( struct sockaddr_in6 ) ) ;
tgt = ( struct sockaddr * ) & tgt6 ;
tgt_len = sizeof ( tgt6 ) ;
} else {
dbg_printf ( 2 , " Unsupported family %d \n " , args - > net . family ) ;
return - 1 ;
}
if ( mc_sock < 0 )
continue ;
/* Build our packet */
memset ( & freq , 0 , sizeof ( freq ) ) ;
2009-12-07 20:55:48 +03:00
if ( args - > domain & & strlen ( ( char * ) args - > domain ) ) {
strncpy ( ( char * ) freq . domain , args - > domain ,
sizeof ( freq . domain ) ) ;
}
2009-07-28 18:46:53 +04:00
freq . request = args - > op ;
freq . hashtype = args - > net . hash ;
2009-09-15 18:28:20 +04:00
freq . seqno = seqno ;
2009-07-28 18:46:53 +04:00
/* Store source address */
if ( ipa - > ipa_family = = PF_INET ) {
freq . addrlen = sizeof ( struct in_addr ) ;
/* XXX Swap order for in_addr ? XXX */
inet_pton ( PF_INET , ipa - > ipa_address , freq . address ) ;
} else if ( ipa - > ipa_family = = PF_INET6 ) {
freq . addrlen = sizeof ( struct in6_addr ) ;
inet_pton ( PF_INET6 , ipa - > ipa_address , freq . address ) ;
}
freq . flags = 0 ;
if ( args - > flags & F_USE_UUID )
freq . flags | = RF_UUID ;
freq . family = ipa - > ipa_family ;
freq . port = args - > net . port ;
sign_request ( & freq , key , key_len ) ;
dbg_printf ( 3 , " Sending to %s via %s \n " , args - > net . addr ,
ipa - > ipa_address ) ;
sendto ( mc_sock , & freq , sizeof ( freq ) , 0 ,
( struct sockaddr * ) tgt , tgt_len ) ;
close ( mc_sock ) ;
}
return 0 ;
}
/* TODO: Clean this up!!! */
int
mcast_fence_virt ( fence_virt_args_t * args )
{
ip_list_t ipl ;
char key [ MAX_KEY_LEN ] ;
2009-09-15 18:28:20 +04:00
struct timeval tv ;
2009-07-28 18:46:53 +04:00
int lfd , key_len = 0 , fd ;
int attempts = 0 ;
2009-09-15 18:28:20 +04:00
uint32_t seqno ;
2009-07-28 18:46:53 +04:00
/* Initialize NSS; required to do hashing, as silly as that
sounds . . . */
if ( NSS_NoDB_Init ( NULL ) ! = SECSuccess ) {
printf ( " Could not initialize NSS \n " ) ;
return 1 ;
}
if ( args - > net . auth ! = AUTH_NONE | | args - > net . hash ! = HASH_NONE ) {
key_len = read_key_file ( args - > net . key_file , key , sizeof ( key ) ) ;
if ( key_len < 0 ) {
printf ( " Could not read %s; trying without "
" authentication \n " , args - > net . key_file ) ;
args - > net . auth = AUTH_NONE ;
args - > net . hash = HASH_NONE ;
}
}
/* Do the real work */
if ( ip_build_list ( & ipl ) < 0 ) {
printf ( " Error building IP address list \n " ) ;
return 1 ;
}
switch ( args - > net . auth ) {
case AUTH_NONE :
case AUTH_SHA1 :
case AUTH_SHA256 :
case AUTH_SHA512 :
if ( args - > net . family = = PF_INET ) {
lfd = ipv4_listen ( args - > net . port , 10 ) ;
} else {
lfd = ipv6_listen ( args - > net . port , 10 ) ;
}
break ;
/*case AUTH_X509:*/
/* XXX Setup SSL listener socket here */
default :
return 1 ;
}
if ( lfd < 0 ) {
printf ( " Failed to listen: %s \n " , strerror ( errno ) ) ;
return 1 ;
}
attempts = args - > timeout * 10 / args - > retr_time ;
2009-09-15 18:28:20 +04:00
gettimeofday ( & tv , NULL ) ;
seqno = ( uint32_t ) tv . tv_usec ;
2009-07-28 18:46:53 +04:00
do {
2009-09-15 18:28:20 +04:00
if ( send_multicast_packets ( & ipl , args , seqno ,
key , key_len ) ) {
2009-07-28 18:46:53 +04:00
return - 1 ;
}
switch ( args - > net . auth ) {
case AUTH_NONE :
case AUTH_SHA1 :
case AUTH_SHA256 :
case AUTH_SHA512 :
fd = tcp_wait_connect ( lfd , args - > retr_time ) ;
if ( fd < 0 & & ( errno = = ETIMEDOUT | |
errno = = EINTR ) )
continue ;
break ;
/* case AUTH_X509:
. . . = ssl_wait_connect . . . */
break ;
default :
return 1 ;
}
break ;
} while ( - - attempts ) ;
if ( fd < 0 ) {
if ( attempts < = 0 ) {
printf ( " Timed out waiting for response \n " ) ;
return 1 ;
}
printf ( " Operation failed: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
switch ( args - > net . auth ) {
case AUTH_NONE :
case AUTH_SHA1 :
case AUTH_SHA256 :
case AUTH_SHA512 :
return tcp_exchange ( fd , args - > net . auth , key , key_len ,
args - > timeout ) ;
break ;
/* case AUTH_X509:
return ssl_exchange ( . . . ) ; */
default :
return 1 ;
}
return 1 ;
}