]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ping] Allow termination after a specified number of packets
authorMichael Brown <mcb30@ipxe.org>
Thu, 23 Oct 2014 15:30:58 +0000 (16:30 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 23 Oct 2014 15:39:42 +0000 (16:39 +0100)
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>
src/core/pinger.c
src/hci/commands/ping_cmd.c
src/include/ipxe/pinger.h
src/include/usr/pingmgmt.h
src/usr/pingmgmt.c

index 0312ff38e611f34956900e03b641ae514ae884aa..558cfb49abed8bee110022b2b189f64ce5b15ac0 100644 (file)
@@ -70,6 +70,10 @@ struct pinger {
        uint16_t sequence;
        /** Response for current sequence number is still pending */
        int pending;
+       /** Number of remaining expiry events (zero to continue indefinitely) */
+       unsigned int remaining;
+       /** Return status */
+       int rc;
 
        /** 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 ( pinger->pending )
                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 */
        pinger->sequence++;
@@ -173,6 +182,7 @@ static void pinger_expired ( struct retry_timer *timer, int over __unused ) {
         * case the transmission attempt fails.
         */
        start_timer_fixed ( &pinger->timer, pinger->timeout );
+       pinger->pending = 1;
 
        /* Allocate I/O buffer */
        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 ) {
        size_t len = iob_len ( iobuf );
        uint16_t sequence = meta->offset;
+       int terminate = 0;
        int rc;
 
        /* Clear response pending flag, if applicable */
@@ -218,18 +229,35 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
 
        /* Check for errors */
        if ( len != pinger->len ) {
+               /* Incorrect length: terminate immediately if we are
+                * not pinging indefinitely.
+                */
                DBGC ( pinger, "PINGER %p received incorrect length %zd "
                       "(expected %zd)\n", pinger, len, pinger->len );
                rc = -EPROTO_LEN;
+               terminate = ( pinger->remaining != 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_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) );
+               terminate = ( pinger->remaining != 0 );
        } 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",
                       pinger, sequence, pinger->sequence );
                rc = -EPROTO_SEQ;
+               terminate = 0;
        } else {
+               /* Success: record that a packet was successfully received,
+                * and terminate if we expect to send no further packets.
+                */
                rc = 0;
+               pinger->rc = 0;
+               terminate = ( pinger->remaining == 1 );
        }
 
        /* Discard I/O buffer */
@@ -238,6 +266,10 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
        /* Notify callback function */
        pinger->callback ( meta->src, sequence, len, rc );
 
+       /* Terminate if applicable */
+       if ( terminate )
+               pinger_close ( pinger, rc );
+
        return rc;
 }
 
@@ -268,10 +300,11 @@ static struct interface_descriptor pinger_job_desc =
  * @v hostname         Hostname to ping
  * @v timeout          Timeout (in ticks)
  * @v len              Payload length
+ * @v count            Number of packets to send (or zero for no limit)
  * @ret rc             Return status code
  */
 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,
                                          unsigned int sequence, size_t len,
                                          int rc ) ) {
@@ -292,7 +325,9 @@ int create_pinger ( struct interface *job, const char *hostname,
        timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt );
        pinger->timeout = timeout;
        pinger->len = len;
+       pinger->remaining = ( count ? ( count + 1 /* Initial packet */ ) : 0 );
        pinger->callback = callback;
+       pinger->rc = -ETIMEDOUT;
 
        /* Open socket */
        if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL,
index d514a2a27e29c79472bcf9be6988a1817116e00d..92c5443a914bddb13435d9be54f35c68e387e970 100644 (file)
@@ -48,6 +48,8 @@ struct ping_options {
        unsigned int size;
        /** Timeout (in ms) */
        unsigned long timeout;
+       /** Number of packets to send (or zero for no limit) */
+       unsigned int count;
 };
 
 /** "ping" option list */
@@ -56,6 +58,8 @@ static struct option_descriptor ping_opts[] = {
                      struct ping_options, size, parse_integer ),
        OPTION_DESC ( "timeout", 't', required_argument,
                      struct ping_options, timeout, parse_timeout ),
+       OPTION_DESC ( "count", 'c', required_argument,
+                     struct ping_options, count, parse_integer ),
 };
 
 /** "ping" command descriptor */
@@ -87,7 +91,8 @@ static int ping_exec ( int argc, char **argv ) {
        hostname = argv[optind];
 
        /* Ping */
-       if ( ( rc = ping ( hostname, opts.timeout, opts.size ) ) != 0 )
+       if ( ( rc = ping ( hostname, opts.timeout, opts.size,
+                          opts.count ) ) != 0 )
                return rc;
 
        return 0;
index 2d8403313e4a408b11248ddef45f972d88fa285b..9932df6b0867f892c848e87846d3287f96ed4468 100644 (file)
@@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 
 extern int create_pinger ( struct interface *job, const char *hostname,
                           unsigned long timeout, size_t len,
+                          unsigned int count,
                           void ( * callback ) ( struct sockaddr *peer,
                                                 unsigned int sequence,
                                                 size_t len,
index 45ad5d3947e5e595bfecf44b1b65fe8ff1fcfac9..8bded38491686693985ba52f1d620c5c05e17e89 100644 (file)
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 
 #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 */
index cf6a51113f870beb5e0237558b211647e5808f20..f8366f26fab5724a98204a4c7e61cc72e1cdb6ad 100644 (file)
@@ -58,14 +58,16 @@ static void ping_callback ( struct sockaddr *peer, unsigned int sequence,
  * @v hostname         Hostname
  * @v timeout          Timeout between pings, in ticks
  * @v len              Payload length
+ * @v count            Number of packets to send (or zero for no limit)
  * @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;
 
        /* Create pinger */
-       if ( ( rc = create_pinger ( &monojob, hostname, timeout,
-                                   len, ping_callback ) ) != 0 ) {
+       if ( ( rc = create_pinger ( &monojob, hostname, timeout, len,
+                                   count, ping_callback ) ) != 0 ) {
                printf ( "Could not start ping: %s\n", strerror ( rc ) );
                return rc;
        }