metal/mtce-common/src/common/pingUtil.cpp

833 lines
28 KiB
C++

/*
* Copyright (c) 2015-2017 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/**
* @file
* Wind River Titanium Cloud Maintenance Ping Utility Implementation
*/
#include "daemon_common.h" /* for ... MEMSET_ZERO */
#include "nodeBase.h"
#include "nodeUtil.h"
#include "hostUtil.h" /* for ... hostUtil_is_valid_ip_addr */
#include "pingUtil.h" /* for ... this module header */
#ifdef __AREA__
#undef __AREA__
#endif
#define __AREA__ "acc"
typedef struct
{
struct icmphdr hdr;
char msg[PING_MESSAGE_LEN];
} ping4_tx_message_type ;
typedef struct
{
struct iphdr ip_hdr ;
struct icmphdr hdr ;
char msg[PING_MESSAGE_LEN];
} ping4_rx_message_type ;
typedef struct
{
// struct ip6_hdr iphdr;
struct icmp6_hdr icmphdr;
char msg[PING_MESSAGE_LEN] ; // MSG_HEADER_SIZE];
} ping6_tx_message_type ;
typedef struct
{
// struct ip6_hdr ip_hdr;
struct icmp6_hdr hdr;
char msg[PING_MESSAGE_LEN];
} ping6_rx_message_type ;
/*******************************************************************************
*
* Name : pingUtil_init
*
* Purpose : Setup a ping socket
*
* Assumptions: caller initializes and installs timer handler outside of init
* before the monitor is called.
*
* Returns : PASS : non-blocking ping socket towards specified ip address setup ok
* FAIL__xxx : init failed
*
******************************************************************************/
int pingUtil_init ( string hostname, ping_info_type & ping_info, const char * ip_address )
{
int rc = PASS ;
if ( hostUtil_is_valid_ip_addr ( ip_address ) == false )
{
wlog ("%s refusing to setup ping socket for invalid IP address\n", hostname.c_str());
return (FAIL_NULL_POINTER);
}
string identity_string = program_invocation_short_name ;
identity_string.append ("_");
identity_string.append(ip_address) ;
ping_info.identity =
checksum ((void*)identity_string.data(), identity_string.length());
dlog1 ("%s ping identity string: %s (0x%04x)\n",
hostname.c_str(),
identity_string.c_str(),
ping_info.identity);
/* init the ping_info struct */
ping_info.hostname = hostname ;
ping_info.ip = ip_address ;
ping_info.sequence = getpid() ;
ping_info.recv_retries = 0 ;
ping_info.send_retries = 0 ;
ping_info.requested= false ;
ping_info.received = false ;
ping_info.recv_flush_highwater=2;
/* added for ping monitor */
ping_info.ok = false ;
ping_info.monitoring = false ;
ping_info.stage = PINGUTIL_MONITOR_STAGE__OPEN ;
ping_info.sock = new msgClassTx(ip_address, 0, IPPROTO_RAW, NULL );
/* Validate the socket setup */
if ( ping_info.sock == NULL )
{
rc = FAIL_SOCKET_CREATE ;
elog ("%s failed to create ping socket ; null socket\n",
ping_info.hostname.c_str());
}
else
{
rc = ping_info.sock->return_status;
if ( rc != PASS )
{
elog ("%s failed to create ping socket ; error status:%d\n",
ping_info.hostname.c_str(), rc );
rc = FAIL_SOCKET_CREATE ;
delete (ping_info.sock );
return rc;
}
else
{
if ( ( rc = ping_info.sock->setSocketNonBlocking () ) == PASS )
{
MEMSET_ZERO(ping_info.message);
switch ( ping_info.sock->get_dst_addr()->getIPVersion() )
{
case AF_INET:
{
ping_info.ipv6_mode = false ;
snprintf (&ping_info.message[0], PING_MESSAGE_LEN,
"%s ipv4 ping message from %s daemon",
ping_info.hostname.data(),
program_invocation_short_name);
break ;
}
case AF_INET6:
{
ping_info.ipv6_mode = true ;
snprintf (&ping_info.message[0], PING_MESSAGE_LEN,
"%s ipv6 ping message from %s daemon",
ping_info.hostname.data(),
program_invocation_short_name);
break ;
}
default:
{
elog ("Unsupported IP protocol version\n");
return (FAIL);
}
}
dlog3 ("%s (fd:%d)\n",
ping_info.message,
ping_info.sock->getFD());
}
else
{
elog ("%s failed to set ping socket to non-blocking:%d\n",
ping_info.hostname.c_str(), rc );
}
}
}
return rc ;
}
int pingUtil_recv_flush ( ping_info_type & ping_info, bool loud );
/*******************************************************************************
*
* Name : pingUtil_send
*
* Purpose : Send an ICMP ECHO ping request to the specified socket
*
* Returns : PASS : send was ok
* FAIL : send failed
*
******************************************************************************/
int pingUtil_send ( ping_info_type & ping_info )
{
ping4_tx_message_type ping4_tx;
ping6_tx_message_type ping6_tx;
int bytes = 0 ;
pingUtil_recv_flush ( ping_info, false );
if (( ping_info.sock == NULL ) || ( ping_info.sock->return_status != PASS ))
{
wlog ("%s refusing to send ping on %s socket\n",
ping_info.hostname.c_str(),
ping_info.sock ? "faulty" : "null" );
return (FAIL_NULL_POINTER);
}
ping_info.sequence++ ;
ping_info.recv_retries = 0;
if ( ping_info.ipv6_mode == false )
{
MEMSET_ZERO (ping4_tx);
ping4_tx.hdr.type = ICMP_ECHO;
ping4_tx.hdr.un.echo.id = htons(ping_info.identity) ;
ping4_tx.hdr.un.echo.sequence = htons(ping_info.sequence) ;
snprintf ( &ping4_tx.msg[0], PING_MESSAGE_LEN, ping_info.message );
/* checksum should not be converted to htons
* - will get (wrong icmp cksum ) */
ping4_tx.hdr.checksum = checksum(&ping4_tx, sizeof(ping4_tx));
dlog3 ("%s ping4 checksum: %04x\n",
ping_info.hostname.c_str(),
ping4_tx.hdr.checksum );
bytes = ping_info.sock->write((const char*)&ping4_tx, sizeof(ping4_tx));
}
else
{
MEMSET_ZERO (ping6_tx);
ping6_tx.icmphdr.icmp6_type = ICMP6_ECHO_REQUEST;
ping6_tx.icmphdr.icmp6_code = 0;
ping6_tx.icmphdr.icmp6_id = htons(ping_info.identity) ;
ping6_tx.icmphdr.icmp6_seq = htons(ping_info.sequence) ;
snprintf ( &ping6_tx.msg[0], PING_MESSAGE_LEN, ping_info.message );
ping6_tx.icmphdr.icmp6_cksum = htons(checksum(&ping6_tx, sizeof(ping6_tx)));
dlog3 ("%s ping6 checksum: %04x\n",
ping_info.hostname.c_str(),
ping6_tx.icmphdr.icmp6_cksum );
bytes = ping_info.sock->write( (const char*)&ping6_tx, sizeof(ping6_tx_message_type));
}
ping_info.recv_retries = 0;
if ( bytes <= 0 )
{
wlog ("%s ping %s send failed (rc:%d) (%d:%m)\n", ping_info.hostname.c_str(), ping_info.ip.c_str(), bytes, errno );
return FAIL ;
}
if ( ping_info.monitoring == false )
{
ilog ("%s ping send %s ok ; identity:%04x sequence:%04x (try %d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.send_retries);
}
else
{
mlog ("%s ping send %s ok ; identity:%04x sequence:%04x (try %d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.send_retries);
}
ping_info.received = false ;
ping_info.requested = true ;
return PASS ;
}
/*******************************************************************************
*
* Name : pingUtil_recv_flush
*
* Purpose : Empty the ping receiver in preparation for a fresh ping request.
*
* Returns : PASS : empty
* RETRY: not empty
*
******************************************************************************/
int pingUtil_recv_flush ( ping_info_type & ping_info, bool loud )
{
int empty_count = 0 ;
int flush_count = 0 ;
bool exit_pass = false ;
if ( ping_info.sock == NULL )
return (FAIL_NULL_POINTER);
for ( int i = 0 , bytes = 0 ; i < PING_MAX_FLUSH_RETRIES ; i++ )
{
if ( ping_info.ipv6_mode == true )
{
ping6_rx_message_type ping6_rx ;
MEMSET_ZERO(ping6_rx);
bytes = ping_info.sock->readReply( (char *)&ping6_rx, sizeof(ping6_rx_message_type));
if ( bytes > 0 )
{
unsigned short id = htons(ping6_rx.hdr.icmp6_id) ;
unsigned short seq = htons(ping6_rx.hdr.icmp6_seq) ;
flush_count++ ;
empty_count = 0 ;
if ( id == ping_info.identity )
{
wlog ("%s flushed out-of-sequence ping response for my identity:%04x ; sequence:%04x (%d)\n",
ping_info.hostname.c_str(), ping_info.identity, seq, flush_count );
}
else if ( loud == true )
{
wlog ("%s flushed %d byte message identity:%04x sequence:%04x\n",
ping_info.hostname.c_str(), bytes, id , seq );
}
}
}
else
{
ping4_rx_message_type ping4_rx ;
MEMSET_ZERO(ping4_rx);
bytes = ping_info.sock->readReply( (char *)&ping4_rx, sizeof(ping4_rx_message_type));
if (( bytes > 0 ) && ( ping4_rx.hdr.un.echo.id != 0 ))
{
flush_count++ ;
empty_count = 0 ;
unsigned short id = htons(ping4_rx.hdr.un.echo.id);
unsigned short seq = htons(ping4_rx.hdr.un.echo.sequence);
if ( id == ping_info.identity )
{
wlog ("%s flushed out-of-sequence ping response for my identity:%04x ; sequence:%04x (%d)\n",
ping_info.hostname.c_str(),
ping_info.identity,
seq,
flush_count );
}
else if ( loud == true )
{
wlog ("%s flushed %d byte message identity:%04x sequence:%04x\n",
ping_info.hostname.c_str(), bytes, id, seq );
}
}
}
if ( bytes <= 0 )
{
if ( empty_count++ == 3 )
{
exit_pass = true ;
break ;
}
}
}
if ( flush_count > ping_info.recv_flush_highwater )
{
ping_info.recv_flush_highwater = flush_count ;
dlog ("%s ping flush peak at %d\n",
ping_info.hostname.c_str(),
ping_info.recv_flush_highwater );
}
else if ( flush_count )
{
dlog2 ("%s ping flushed %d messages\n",
ping_info.hostname.c_str(), flush_count );
}
if ( exit_pass == true )
return (PASS);
return (RETRY);
}
/*******************************************************************************
*
* Name : pingUtil_recv
*
* Purpose : Receive an ICMP ping response and compare the suggested sequence
* and identifier numbers.
*
* Returns : PASS : got the response with the correct id and seq codes
* RETRY: got response but with one or mode bad codes
* FAIL : got no ping reply
*
******************************************************************************/
/* handle a reasonable ping flood without failing local pings */
#define MAX_PING_FLUSH (512)
int pingUtil_recv ( ping_info_type & ping_info,
bool loud ) /* print log if no data received */
{
int rc = FAIL ;
int bytes = 0 ;
if (( ping_info.requested == true ) && ( ping_info.received == true ))
{
ping_info.requested = false ;
return (PASS);
}
if ( ping_info.sock == NULL )
return (FAIL_NULL_POINTER);
for ( int i = 0 ; i < MAX_PING_FLUSH ; i++ )
{
if ( ping_info.ipv6_mode == true )
{
ping6_rx_message_type ping6_rx ;
MEMSET_ZERO(ping6_rx);
bytes = ping_info.sock->readReply( (char *)&ping6_rx, sizeof(ping6_rx_message_type));
if ( bytes > 0 )
{
unsigned short id = htons(ping6_rx.hdr.icmp6_id);
unsigned short seq = htons(ping6_rx.hdr.icmp6_seq);
if ( loud == true )
{
ilog ("%s %s search ; bytes:%d ; identity:%04x (got %04x) sequence:%04x (got %04x)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(), bytes,
ping_info.identity, ping6_rx.hdr.icmp6_id,
ping_info.sequence, ping6_rx.hdr.icmp6_seq );
}
if (( ping6_rx.hdr.icmp6_type == ICMP6_ECHO_REPLY ) &&
( id == ping_info.identity ) &&
( seq == ping_info.sequence ))
{
/* Don't print this log once we have established ping and
* are in monitoring mode. */
if ( ping_info.monitoring == false )
{
/* ... only want the log when we ar first connecting */
ilog ("%s ping recv %s ok ; identity:%04x sequence:%04x (try %d) (%d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.recv_retries+1,
i);
}
else
{
/* ... only want the log when we ar first connecting */
mlog ("%s ping recv %s ok ; identity:%04x sequence:%04x (try %d) (%d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.recv_retries+1,
i);
}
ping_info.requested = false ;
ping_info.received = true ;
rc = PASS ;
break ;
}
else if ( ping6_rx.hdr.icmp6_id == ping_info.identity )
{
ilog ("%s received-out-of-sequence ping response for this identity:%04x ; sequence:%04x\n",
ping_info.hostname.c_str(), id, seq);
rc = RETRY ;
}
else
{
; /* identity is 0 or does not match this host */
}
}
else
{
/* no data */
rc = RETRY ;
break ;
}
}
else
{
ping4_rx_message_type ping4_rx ;
MEMSET_ZERO(ping4_rx);
bytes = ping_info.sock->readReply ( (char*)&ping4_rx, sizeof(ping4_rx)) ;
if ( bytes > 0 )
{
unsigned short id = htons(ping4_rx.hdr.un.echo.id);
unsigned short seq = htons(ping4_rx.hdr.un.echo.sequence);
// dump_memory ( &ping4_rx, 16, sizeof(ping4_rx_message_type));
if ( loud == true )
{
ilog ("%s %s search ; bytes:%d ; identity:%04x (got %04x) sequence:%04x (got %04x)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(), bytes,
ping_info.identity, id,
ping_info.sequence, seq );
}
if (( ping4_rx.hdr.type == ICMP_ECHOREPLY ) &&
( id == ping_info.identity ) &&
( seq == ping_info.sequence ))
{
/* Don't print this log once we have established ping and
* are in monitoring mode. */
if ( ping_info.monitoring == false )
{
/* ... only want the log when we ar first connecting */
ilog ("%s ping recv %s ok ; identity:%04x sequence:%04x (try %d) (%d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.recv_retries+1,
i);
}
else
{
/* ... only want the log when we ar first connecting */
mlog ("%s ping recv %s ok ; identity:%04x sequence:%04x (try %d) (%d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.recv_retries+1,
i);
}
ping_info.requested = false ;
ping_info.received = true ;
rc = PASS ;
break ;
}
else if ( id == ping_info.identity )
{
ilog ("%s received-out-of-sequence ping response for this identity:%04x ; sequence:%04x\n",
ping_info.hostname.c_str(), id, seq );
rc = RETRY ;
}
else
{
; /* identity is 0 or does not match this host */
}
}
else
{
/* no data */
rc = RETRY ;
break ;
}
}
}
return rc ;
}
/*******************************************************************************
*
* Name : pingUtil_fini
*
* Purpose : Close an ping socket
*
*******************************************************************************/
void pingUtil_fini ( ping_info_type & ping_info )
{
if ( ping_info.sock )
{
dlog1 ("%s ping socket close ok (fd:%d)\n",
ping_info.hostname.c_str(),
ping_info.sock->getFD());
delete ( ping_info.sock );
ping_info.sock = NULL ;
}
ping_info.recv_retries = 0;
ping_info.send_retries = 0;
ping_info.sequence = 0;
ping_info.identity = 0;
/* Support for ping monitor */
mtcTimer_reset ( ping_info.timer );
ping_info.stage = PINGUTIL_MONITOR_STAGE__IDLE ;
}
/********************************************************************************
*
* Name : pingUtil_acc_monitor
*
* Purpose : FSM used to monitor ping access to specific ip address
*
*******************************************************************************/
int pingUtil_acc_monitor ( ping_info_type & ping_info )
{
switch ( ping_info.stage )
{
/* do nothing stage */
case PINGUTIL_MONITOR_STAGE__IDLE:
{
break ;
}
case PINGUTIL_MONITOR_STAGE__WAIT:
{
if ( mtcTimer_expired ( ping_info.timer ) )
{
ping_info.stage = PINGUTIL_MONITOR_STAGE__SEND ;
}
/* Don't let the buffer fill up with pings ;
* keep the socket empty till we want to ping */
pingUtil_recv_flush ( ping_info , false );
break ;
}
case PINGUTIL_MONITOR_STAGE__OPEN:
{
if ( pingUtil_init ( ping_info.hostname,
ping_info,
ping_info.ip.data()) != PASS )
{
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
}
else
{
ping_info.stage = PINGUTIL_MONITOR_STAGE__SEND ;
}
break ;
}
case PINGUTIL_MONITOR_STAGE__SEND:
{
if ( ping_info.sock == NULL )
{
if (( ping_info.ip.empty()) || !ping_info.ip.compare(NONE))
{
elog ("%s no address to ping\n", ping_info.hostname.c_str());
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
break ;
}
int rc = pingUtil_init ( ping_info.hostname,
ping_info,
ping_info.ip.data());
if ( rc )
{
elog ("%s failed to setup bmc ping socket to '%s'\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str());
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
break ;
}
}
if ( ++ping_info.send_retries > PING_MAX_SEND_RETRIES )
{
elog ("%s ping to %s failed\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str());
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
}
else if ( pingUtil_send ( ping_info ) )
{
elog ("%s failed to send bmc ping\n", ping_info.hostname.c_str());
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
}
else
{
if ( ping_info.timer_handler == NULL )
{
elog ("%s no timer handler installed\n", ping_info.hostname.c_str());
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
}
else
{
if ( ping_info.timer.tid )
{
ilog ("%s unexpected active timer\n", ping_info.hostname.c_str());
mtcTimer_reset ( ping_info.timer );
}
mtcTimer_start_msec ( ping_info.timer, ping_info.timer_handler, PING_WAIT_TIMER_MSEC );
ping_info.stage = PINGUTIL_MONITOR_STAGE__RECV ;
}
}
break ;
}
case PINGUTIL_MONITOR_STAGE__RECV:
{
if ( mtcTimer_expired ( ping_info.timer ))
{
bool loud = false ;
if ( daemon_get_cfg_ptr()->debug_bmgmt )
loud = true ;
if ( pingUtil_recv ( ping_info , loud ) )
{
if ( ++ping_info.recv_retries > (PING_MAX_RECV_RETRIES) )
{
/* only print this log once on the the resend attempt */
if ( ping_info.send_retries >= PING_MAX_SEND_RETRIES )
{
mlog ("%s ping recv from %s missed ; identity:%04x sequence:%04x (try %d of %d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.identity,
ping_info.sequence,
ping_info.recv_retries-1,
PING_MAX_RECV_RETRIES);
}
ping_info.stage = PINGUTIL_MONITOR_STAGE__SEND ;
break ;
}
else
{
blog1 ("%s retrying ping\n", ping_info.hostname.c_str());
}
mtcTimer_start_msec ( ping_info.timer, ping_info.timer_handler, PING_RETRY_DELAY_MSECS );
}
else
{
int interval = PING_MONITOR_INTERVAL ;
ping_info.ok = true ;
ping_info.monitoring = true ;
dlog ("%s ping %s ok (send:%d:recv:%d) (%d)\n",
ping_info.hostname.c_str(),
ping_info.ip.c_str(),
ping_info.send_retries,
ping_info.recv_retries+1,
ping_info.ok );
ping_info.send_retries = 0 ;
ping_info.recv_retries = 0 ;
#ifdef WANT_FIT_TESTING
if ( daemon_want_fit ( FIT_CODE__FAST_PING_AUDIT_HOST, ping_info.hostname ) == true )
interval = 3 ;
if ( daemon_want_fit ( FIT_CODE__FAST_PING_AUDIT_ALL ) == true )
interval = 3 ;
#endif
mtcTimer_start ( ping_info.timer, ping_info.timer_handler, interval );
ping_info.stage = PINGUTIL_MONITOR_STAGE__WAIT ;
}
}
break ;
}
case PINGUTIL_MONITOR_STAGE__CLOSE:
{
pingUtil_fini (ping_info);
break ;
}
case PINGUTIL_MONITOR_STAGE__FAIL:
{
ping_info.ok = false ;
ping_info.send_retries = 0 ;
ping_info.monitoring = false ;
pingUtil_fini (ping_info);
pingUtil_init (ping_info.hostname, ping_info, ping_info.ip.data());
mtcTimer_reset ( ping_info.timer );
mtcTimer_start ( ping_info.timer, ping_info.timer_handler, PING_MONITOR_INTERVAL );
ping_info.stage = PINGUTIL_MONITOR_STAGE__WAIT;
break ;
}
default:
{
slog ("%s default case (%d)\n", ping_info.hostname.c_str(), ping_info.stage );
/* Default to check the connection.
* Failure case is handled there */
mtcTimer_reset ( ping_info.timer );
ping_info.stage = PINGUTIL_MONITOR_STAGE__FAIL ;
}
}
return(PASS);
}
#ifdef WANT_MAIN
/*--------------------------------------------------------------------*/
/*--- main - look up host and start ping processes. ---*/
/*--------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
int rc ;
int ping_socket = 0;
int sequence = 1 ;
struct sockaddr_in addr_ping ;
if ( argc > 1 )
{
int identity = getpid() ;
printf ( "\npinging %s\n", argv[1]);
if ( ( rc = pingUtil_init ( argv[1], ping_socket , addr_ping )) == 0 )
{
pingUtil_recv ( ping_socket, identity, sequence, false );
if ( ( rc = pingUtil_send ( ping_socket, &addr_ping, identity, sequence )) == 0 )
{
for ( int loop=0;loop < 10; loop++)
{
usleep(300000);
if ( ( rc = pingUtil_recv ( ping_socket, identity, sequence, true ) ) == 0 )
{
printf("Ping OK.\n");
return 0;
}
else
{
printf ("receive failed (%d)\n", rc );
}
}
printf("Ping FAILED !!\n");
}
else
{
printf ("ping send Failed (%d)\n", rc );
}
}
else
{
printf ("ping init failed (%d)\n", rc );
}
}
pingUtil_close ( ping_socket );
return 0;
}
#endif