mirror of https://github.com/ipxe/ipxe.git
[ping] Allow termination after a specified number of packets
Add the "-c <count>" option to the "ping" command, allowing for automatic termination after a specified number of packets. When a number of packets is specified: - if a serious error (i.e. length mismatch or content mismatch) occurs, then the ping will be immediately terminated with the relevant status code; - if at least one response is received successfully, and all errors are non-serious (i.e. timeouts or out-of-sequence responses), then the ping will be terminated after the final response (or timeout) with a success status; - if no responses are received successfully, then the ping will be terminated after the final timeout with ETIMEDOUT. If no number of packets is specified, then the ping will continue until manually interrupted. Originally-implemented-by: Cedric Levasseur <cyr-ius@ipocus.net> Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/32/head
parent
d1afe731ea
commit
1c34ca70d1
|
@ -70,6 +70,10 @@ struct pinger {
|
||||||
uint16_t sequence;
|
uint16_t sequence;
|
||||||
/** Response for current sequence number is still pending */
|
/** Response for current sequence number is still pending */
|
||||||
int pending;
|
int pending;
|
||||||
|
/** Number of remaining expiry events (zero to continue indefinitely) */
|
||||||
|
unsigned int remaining;
|
||||||
|
/** Return status */
|
||||||
|
int rc;
|
||||||
|
|
||||||
/** Callback function
|
/** Callback function
|
||||||
*
|
*
|
||||||
|
@ -164,7 +168,12 @@ static void pinger_expired ( struct retry_timer *timer, int over __unused ) {
|
||||||
/* If no response has been received, notify the callback function */
|
/* If no response has been received, notify the callback function */
|
||||||
if ( pinger->pending )
|
if ( pinger->pending )
|
||||||
pinger->callback ( NULL, pinger->sequence, 0, -ETIMEDOUT );
|
pinger->callback ( NULL, pinger->sequence, 0, -ETIMEDOUT );
|
||||||
pinger->pending = 1;
|
|
||||||
|
/* Check for termination */
|
||||||
|
if ( pinger->remaining && ( --pinger->remaining == 0 ) ) {
|
||||||
|
pinger_close ( pinger, pinger->rc );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Increase sequence number */
|
/* Increase sequence number */
|
||||||
pinger->sequence++;
|
pinger->sequence++;
|
||||||
|
@ -173,6 +182,7 @@ static void pinger_expired ( struct retry_timer *timer, int over __unused ) {
|
||||||
* case the transmission attempt fails.
|
* case the transmission attempt fails.
|
||||||
*/
|
*/
|
||||||
start_timer_fixed ( &pinger->timer, pinger->timeout );
|
start_timer_fixed ( &pinger->timer, pinger->timeout );
|
||||||
|
pinger->pending = 1;
|
||||||
|
|
||||||
/* Allocate I/O buffer */
|
/* Allocate I/O buffer */
|
||||||
iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len );
|
iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len );
|
||||||
|
@ -210,6 +220,7 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
|
||||||
struct xfer_metadata *meta ) {
|
struct xfer_metadata *meta ) {
|
||||||
size_t len = iob_len ( iobuf );
|
size_t len = iob_len ( iobuf );
|
||||||
uint16_t sequence = meta->offset;
|
uint16_t sequence = meta->offset;
|
||||||
|
int terminate = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Clear response pending flag, if applicable */
|
/* Clear response pending flag, if applicable */
|
||||||
|
@ -218,18 +229,35 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
|
||||||
|
|
||||||
/* Check for errors */
|
/* Check for errors */
|
||||||
if ( len != pinger->len ) {
|
if ( len != pinger->len ) {
|
||||||
|
/* Incorrect length: terminate immediately if we are
|
||||||
|
* not pinging indefinitely.
|
||||||
|
*/
|
||||||
DBGC ( pinger, "PINGER %p received incorrect length %zd "
|
DBGC ( pinger, "PINGER %p received incorrect length %zd "
|
||||||
"(expected %zd)\n", pinger, len, pinger->len );
|
"(expected %zd)\n", pinger, len, pinger->len );
|
||||||
rc = -EPROTO_LEN;
|
rc = -EPROTO_LEN;
|
||||||
|
terminate = ( pinger->remaining != 0 );
|
||||||
} else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) {
|
} else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) {
|
||||||
|
/* Incorrect data: terminate immediately if we are not
|
||||||
|
* pinging indefinitely.
|
||||||
|
*/
|
||||||
DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger );
|
DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger );
|
||||||
DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) );
|
DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) );
|
||||||
|
terminate = ( pinger->remaining != 0 );
|
||||||
} else if ( sequence != pinger->sequence ) {
|
} else if ( sequence != pinger->sequence ) {
|
||||||
|
/* Incorrect sequence number (probably a delayed response):
|
||||||
|
* report via callback but otherwise ignore.
|
||||||
|
*/
|
||||||
DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n",
|
DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n",
|
||||||
pinger, sequence, pinger->sequence );
|
pinger, sequence, pinger->sequence );
|
||||||
rc = -EPROTO_SEQ;
|
rc = -EPROTO_SEQ;
|
||||||
|
terminate = 0;
|
||||||
} else {
|
} else {
|
||||||
|
/* Success: record that a packet was successfully received,
|
||||||
|
* and terminate if we expect to send no further packets.
|
||||||
|
*/
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
pinger->rc = 0;
|
||||||
|
terminate = ( pinger->remaining == 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Discard I/O buffer */
|
/* Discard I/O buffer */
|
||||||
|
@ -238,6 +266,10 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
|
||||||
/* Notify callback function */
|
/* Notify callback function */
|
||||||
pinger->callback ( meta->src, sequence, len, rc );
|
pinger->callback ( meta->src, sequence, len, rc );
|
||||||
|
|
||||||
|
/* Terminate if applicable */
|
||||||
|
if ( terminate )
|
||||||
|
pinger_close ( pinger, rc );
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,10 +300,11 @@ static struct interface_descriptor pinger_job_desc =
|
||||||
* @v hostname Hostname to ping
|
* @v hostname Hostname to ping
|
||||||
* @v timeout Timeout (in ticks)
|
* @v timeout Timeout (in ticks)
|
||||||
* @v len Payload length
|
* @v len Payload length
|
||||||
|
* @v count Number of packets to send (or zero for no limit)
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
int create_pinger ( struct interface *job, const char *hostname,
|
int create_pinger ( struct interface *job, const char *hostname,
|
||||||
unsigned long timeout, size_t len,
|
unsigned long timeout, size_t len, unsigned int count,
|
||||||
void ( * callback ) ( struct sockaddr *src,
|
void ( * callback ) ( struct sockaddr *src,
|
||||||
unsigned int sequence, size_t len,
|
unsigned int sequence, size_t len,
|
||||||
int rc ) ) {
|
int rc ) ) {
|
||||||
|
@ -292,7 +325,9 @@ int create_pinger ( struct interface *job, const char *hostname,
|
||||||
timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt );
|
timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt );
|
||||||
pinger->timeout = timeout;
|
pinger->timeout = timeout;
|
||||||
pinger->len = len;
|
pinger->len = len;
|
||||||
|
pinger->remaining = ( count ? ( count + 1 /* Initial packet */ ) : 0 );
|
||||||
pinger->callback = callback;
|
pinger->callback = callback;
|
||||||
|
pinger->rc = -ETIMEDOUT;
|
||||||
|
|
||||||
/* Open socket */
|
/* Open socket */
|
||||||
if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL,
|
if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL,
|
||||||
|
|
|
@ -48,6 +48,8 @@ struct ping_options {
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
/** Timeout (in ms) */
|
/** Timeout (in ms) */
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
/** Number of packets to send (or zero for no limit) */
|
||||||
|
unsigned int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** "ping" option list */
|
/** "ping" option list */
|
||||||
|
@ -56,6 +58,8 @@ static struct option_descriptor ping_opts[] = {
|
||||||
struct ping_options, size, parse_integer ),
|
struct ping_options, size, parse_integer ),
|
||||||
OPTION_DESC ( "timeout", 't', required_argument,
|
OPTION_DESC ( "timeout", 't', required_argument,
|
||||||
struct ping_options, timeout, parse_timeout ),
|
struct ping_options, timeout, parse_timeout ),
|
||||||
|
OPTION_DESC ( "count", 'c', required_argument,
|
||||||
|
struct ping_options, count, parse_integer ),
|
||||||
};
|
};
|
||||||
|
|
||||||
/** "ping" command descriptor */
|
/** "ping" command descriptor */
|
||||||
|
@ -87,7 +91,8 @@ static int ping_exec ( int argc, char **argv ) {
|
||||||
hostname = argv[optind];
|
hostname = argv[optind];
|
||||||
|
|
||||||
/* Ping */
|
/* Ping */
|
||||||
if ( ( rc = ping ( hostname, opts.timeout, opts.size ) ) != 0 )
|
if ( ( rc = ping ( hostname, opts.timeout, opts.size,
|
||||||
|
opts.count ) ) != 0 )
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
extern int create_pinger ( struct interface *job, const char *hostname,
|
extern int create_pinger ( struct interface *job, const char *hostname,
|
||||||
unsigned long timeout, size_t len,
|
unsigned long timeout, size_t len,
|
||||||
|
unsigned int count,
|
||||||
void ( * callback ) ( struct sockaddr *peer,
|
void ( * callback ) ( struct sockaddr *peer,
|
||||||
unsigned int sequence,
|
unsigned int sequence,
|
||||||
size_t len,
|
size_t len,
|
||||||
|
|
|
@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern int ping ( const char *hostname, unsigned long timeout, size_t len );
|
extern int ping ( const char *hostname, unsigned long timeout, size_t len,
|
||||||
|
unsigned int count );
|
||||||
|
|
||||||
#endif /* _USR_PINGMGMT_H */
|
#endif /* _USR_PINGMGMT_H */
|
||||||
|
|
|
@ -58,14 +58,16 @@ static void ping_callback ( struct sockaddr *peer, unsigned int sequence,
|
||||||
* @v hostname Hostname
|
* @v hostname Hostname
|
||||||
* @v timeout Timeout between pings, in ticks
|
* @v timeout Timeout between pings, in ticks
|
||||||
* @v len Payload length
|
* @v len Payload length
|
||||||
|
* @v count Number of packets to send (or zero for no limit)
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
int ping ( const char *hostname, unsigned long timeout, size_t len ) {
|
int ping ( const char *hostname, unsigned long timeout, size_t len,
|
||||||
|
unsigned int count ) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Create pinger */
|
/* Create pinger */
|
||||||
if ( ( rc = create_pinger ( &monojob, hostname, timeout,
|
if ( ( rc = create_pinger ( &monojob, hostname, timeout, len,
|
||||||
len, ping_callback ) ) != 0 ) {
|
count, ping_callback ) ) != 0 ) {
|
||||||
printf ( "Could not start ping: %s\n", strerror ( rc ) );
|
printf ( "Could not start ping: %s\n", strerror ( rc ) );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue