X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Froms%2Fipxe%2Fsrc%2Fcore%2Fpinger.c;fp=qemu%2Froms%2Fipxe%2Fsrc%2Fcore%2Fpinger.c;h=31ea2ce1c5d03bf38fca105241d32597d3a5dd8f;hb=e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb;hp=0000000000000000000000000000000000000000;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/qemu/roms/ipxe/src/core/pinger.c b/qemu/roms/ipxe/src/core/pinger.c new file mode 100644 index 000000000..31ea2ce1c --- /dev/null +++ b/qemu/roms/ipxe/src/core/pinger.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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 of the + * License, or 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * ICMP ping sender + * + */ + +/* Disambiguate the various error causes */ +#define EPROTO_LEN __einfo_error ( EINFO_EPROTO_LEN ) +#define EINFO_EPROTO_LEN __einfo_uniqify ( EINFO_EPROTO, 0x01, \ + "Incorrect reply length" ) +#define EPROTO_DATA __einfo_error ( EINFO_EPROTO_DATA ) +#define EINFO_EPROTO_DATA __einfo_uniqify ( EINFO_EPROTO, 0x02, \ + "Incorrect reply data" ) +#define EPROTO_SEQ __einfo_error ( EINFO_EPROTO_SEQ ) +#define EINFO_EPROTO_SEQ __einfo_uniqify ( EINFO_EPROTO, 0x03, \ + "Delayed or out-of-sequence reply" ) + +/** A pinger */ +struct pinger { + /** Reference count */ + struct refcnt refcnt; + + /** Job control interface */ + struct interface job; + /** Data transfer interface */ + struct interface xfer; + + /** Timer */ + struct retry_timer timer; + /** Timeout */ + unsigned long timeout; + + /** Payload length */ + size_t len; + /** Current sequence number */ + 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 + * + * @v src Source socket address, or NULL + * @v sequence Sequence number + * @v len Payload length + * @v rc Status code + */ + void ( * callback ) ( struct sockaddr *src, unsigned int sequence, + size_t len, int rc ); +}; + +/** + * Generate payload + * + * @v pinger Pinger + * @v data Data buffer + */ +static void pinger_generate ( struct pinger *pinger, void *data ) { + uint8_t *bytes = data; + unsigned int i; + + /* Generate byte sequence */ + for ( i = 0 ; i < pinger->len ; i++ ) + bytes[i] = ( i & 0xff ); +} + +/** + * Verify payload + * + * @v pinger Pinger + * @v data Data buffer + * @ret rc Return status code + */ +static int pinger_verify ( struct pinger *pinger, const void *data ) { + const uint8_t *bytes = data; + unsigned int i; + + /* Check byte sequence */ + for ( i = 0 ; i < pinger->len ; i++ ) { + if ( bytes[i] != ( i & 0xff ) ) + return -EPROTO_DATA; + } + + return 0; +} + +/** + * Close pinger + * + * @v pinger Pinger + * @v rc Reason for close + */ +static void pinger_close ( struct pinger *pinger, int rc ) { + + /* Stop timer */ + stop_timer ( &pinger->timer ); + + /* Shut down interfaces */ + intf_shutdown ( &pinger->xfer, rc ); + intf_shutdown ( &pinger->job, rc ); +} + +/** + * Handle data transfer window change + * + * @v pinger Pinger + */ +static void pinger_window_changed ( struct pinger *pinger ) { + + /* Do nothing if timer is already running */ + if ( timer_running ( &pinger->timer ) ) + return; + + /* Start timer when window opens for the first time */ + if ( xfer_window ( &pinger->xfer ) ) + start_timer_nodelay ( &pinger->timer ); +} + +/** + * Handle timer expiry + * + * @v timer Timer + * @v over Failure indicator + */ +static void pinger_expired ( struct retry_timer *timer, int over __unused ) { + struct pinger *pinger = container_of ( timer, struct pinger, timer ); + struct xfer_metadata meta; + struct io_buffer *iobuf; + int rc; + + /* If no response has been received, notify the callback function */ + if ( pinger->pending && pinger->callback ) + pinger->callback ( NULL, pinger->sequence, 0, -ETIMEDOUT ); + + /* Check for termination */ + if ( pinger->remaining && ( --pinger->remaining == 0 ) ) { + pinger_close ( pinger, pinger->rc ); + return; + } + + /* Increase sequence number */ + pinger->sequence++; + + /* Restart timer. Do this before attempting to transmit, in + * 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 ); + if ( ! iobuf ) { + DBGC ( pinger, "PINGER %p could not allocate I/O buffer\n", + pinger ); + return; + } + + /* Generate payload */ + pinger_generate ( pinger, iob_put ( iobuf, pinger->len ) ); + + /* Generate metadata */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.flags = XFER_FL_ABS_OFFSET; + meta.offset = pinger->sequence; + + /* Transmit packet */ + if ( ( rc = xfer_deliver ( &pinger->xfer, iobuf, &meta ) ) != 0 ) { + DBGC ( pinger, "PINGER %p could not transmit: %s\n", + pinger, strerror ( rc ) ); + return; + } +} + +/** + * Handle received data + * + * @v pinger Pinger + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +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 */ + if ( sequence == pinger->sequence ) + pinger->pending = 0; + + /* 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 */ + free_iob ( iobuf ); + + /* Notify callback function, if applicable */ + if ( pinger->callback ) + pinger->callback ( meta->src, sequence, len, rc ); + + /* Terminate if applicable */ + if ( terminate ) + pinger_close ( pinger, rc ); + + return rc; +} + +/** Pinger data transfer interface operations */ +static struct interface_operation pinger_xfer_op[] = { + INTF_OP ( xfer_deliver, struct pinger *, pinger_deliver ), + INTF_OP ( xfer_window_changed, struct pinger *, pinger_window_changed ), + INTF_OP ( intf_close, struct pinger *, pinger_close ), +}; + +/** Pinger data transfer interface descriptor */ +static struct interface_descriptor pinger_xfer_desc = + INTF_DESC ( struct pinger, xfer, pinger_xfer_op ); + +/** Pinger job control interface operations */ +static struct interface_operation pinger_job_op[] = { + INTF_OP ( intf_close, struct pinger *, pinger_close ), +}; + +/** Pinger job control interface descriptor */ +static struct interface_descriptor pinger_job_desc = + INTF_DESC ( struct pinger, job, pinger_job_op ); + +/** + * Create pinger + * + * @v job Job control interface + * @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) + * @v callback Callback function (or NULL) + * @ret rc Return status code + */ +int create_pinger ( struct interface *job, const char *hostname, + unsigned long timeout, size_t len, unsigned int count, + void ( * callback ) ( struct sockaddr *src, + unsigned int sequence, size_t len, + int rc ) ) { + struct pinger *pinger; + int rc; + + /* Sanity check */ + if ( ! timeout ) + return -EINVAL; + + /* Allocate and initialise structure */ + pinger = zalloc ( sizeof ( *pinger ) ); + if ( ! pinger ) + return -ENOMEM; + ref_init ( &pinger->refcnt, NULL ); + intf_init ( &pinger->job, &pinger_job_desc, &pinger->refcnt ); + intf_init ( &pinger->xfer, &pinger_xfer_desc, &pinger->refcnt ); + 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, + hostname, NULL ) ) != 0 ) { + DBGC ( pinger, "PINGER %p could not open socket: %s\n", + pinger, strerror ( rc ) ); + goto err; + } + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &pinger->job, job ); + ref_put ( &pinger->refcnt ); + return 0; + + err: + pinger_close ( pinger, rc ); + ref_put ( &pinger->refcnt ); + return rc; +}