send_ping_device.c from EmStar at Krugle
Show send_ping_device.c syntax highlighted
/*
*
* Copyright (c) 2003 The Regents of the University of California. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* ping-device that will actively send pings when requested through a
* status-device. Will be used for experiments in testing the change of
* link-state over time.
* We want to see:
* - how long it takes for two different ping-frequencies to
* converge on the same link-quality measurement
* - how quickly different frequencies will react to changes in link-quality
* - how stable each frequency is given a stable link-quality
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include "emrun/emrun.h"
#include "link/link.h"
#include "ping.h"
#include "libmisc/misc_time.h"
/*******************************************************************/
#define MAX_PINGS 1000
#define LONG_INTERVAL 90000
#define SHORT_INTERVAL 11000
#define CMD_DEV_NAME "ping_device/command"
#define SHORT_STATE "Short"
#define LONG_STATE "Long"
#define PKT_TYPE_PING_SHORT 45
#define PKT_TYPE_PING_LONG 46
typedef struct ping_state {
int random_id; /* our randomly selected ID, to
* differentiate our packets from any
* other ping processes that might be
* happening at the same time. */
int last_seqno; /* last seqno we used */
int total_sent; /* total pings sent */
struct timeval time_sent; /* time we sent the most recent ping */
struct timeval rtt[MAX_PINGS]; /* rtts of responses we've received */
int num_replies; /* number of responses we've received */
lu_context_t *link; /* link we're using to ping on */
int pkt_type;
} ping_state_t;
typedef struct pd_ctx {
uint sint; /* Interval for short timer */
uint lint; /* Interval for long timer */
g_event_t* stimer;
g_event_t* ltimer;
ping_state_t ps_short;
ping_state_t ps_long;
} pd_ctx_t;
void compute_stats(ping_state_t* p, char* state);
/*******************************************************************/
/*
* Callback activated when we are asked to shut down by emrun.
* Compute and print summary statistics of all replies received
*/
void ping_shutdown(void *data)
{
pd_ctx_t *pd = (pd_ctx_t *) data;
ping_state_t* p;
int j;
char* state;
for (j = 0; j < 2; j++)
{
if (j == 0)
{
p = &pd->ps_short;
state = SHORT_STATE;
}
else
{
p = &pd->ps_long;
state = LONG_STATE;
}
if (!p->num_replies) {
elog_g(LOG_NOTICE, "%s: %d pings sent, no replies received",
state, p->total_sent);
}
else compute_stats(p, state);
}
/* Now shut down */
exit(0);
}
/*
* Construct a ping packet, and send it out over the wire, using the
* link context stored as part of our "ping" state.
*/
void ping_send(ping_state_t *p, int pkt_type)
{
/* Allocate a small buffer for the packet */
char buf[200];
link_pkt_t *pkt = (link_pkt_t *) buf;
ping_pkt_t *ping_pkt = (ping_pkt_t *) pkt->data;
int this_seq = p->last_seqno + 1;
/* initialize the outer header (the link_pkt header) */
memset(buf, 0, sizeof(buf));
pkt->dst.id = LINK_BROADCAST;
pkt->type = pkt_type;
/* and our application-layer (ping) protocol data */
ping_pkt->cmd = PING_REQUEST;
ping_pkt->random_id = p->random_id;
ping_pkt->seqno = this_seq;
/* now launch the packet! */
if (lu_send(p->link, pkt, sizeof(ping_pkt_t)) < 0) {
if (errno != EAGAIN)
elog(LOG_ERR, "can't send on %s: %m", lu_name(p->link, NULL));
}
else {
elog(LOG_NOTICE, "broadcast ping seqno %d", ping_pkt->seqno);
/* remember what time it is, so we can calculate RTT */
gettimeofday(&p->time_sent, NULL);
p->last_seqno = this_seq;
p->total_sent++;
}
}
/* Called every time our periodic ping timer expies. */
int ping_send_short(void *data, int interval, g_event_t *ev)
{
pd_ctx_t* pd = (pd_ctx_t *) data;
elog(LOG_DEBUG(1), "%s\n", SHORT_STATE);
compute_stats(&pd->ps_short, SHORT_STATE);
ping_send(&pd->ps_short, PKT_TYPE_PING_SHORT);
return EVENT_RENEW;
}
int ping_send_long(void *data, int interval, g_event_t *ev)
{
pd_ctx_t* pd = (pd_ctx_t *) data;
struct timeval tv;
gettimeofday(&tv, NULL);
elog(LOG_DEBUG(1), "%s: time called %s\n", LONG_STATE, misc_tv_to_str(&tv));
compute_stats(&pd->ps_long, LONG_STATE);
ping_send(&pd->ps_long, PKT_TYPE_PING_LONG);
return EVENT_RENEW;
}
/*
* This callback is called whenever a packet arrives on the link we
* opened. "pkt" is a pointer to a link_pkt_t header followed by
* data_len bytes of data. (data_len is the length of the data
* following the header; it doesn't include the size of the link_pkt_t
* header itself.) "link" is a pointer to the link that the packet
* was received on, and can be used for sending a reply packet (we
* don't send reply packets from the receiver callback here, but see
* pingd for an example where we do.)
*/
int ping_receiver(lu_context_t *link, link_pkt_t *link_pkt, ssize_t data_len)
{
/* Get a pointer to the data portion of the packet */
ping_pkt_t *ping_pkt = (ping_pkt_t *) link_pkt->data;
/* We stored a pointer to the 'ping_state' struct as the "data"
* field of link_opts in main(). Now, we retrieve that pointer. */
ping_state_t *p = (ping_state_t *) lu_data(link);
if (link_pkt->type != p->pkt_type) {
elog(LOG_ERR, "RECEIVED INCORRECT pkt-type %d, expecting %d\n",
link_pkt->type, p->pkt_type);
}
/*
* Make sure the data portion of the packet is exactly the right
* size (i.e., the size of our "ping" application-layer protocol
* frame.
*/
if (data_len != sizeof(ping_pkt_t)) {
elog(LOG_ERR, "got a short ping packet (%d bytes)!", data_len);
goto done;
}
/* We now know that the data pointed to by ping_pkt is valid */
/*
* Ignore everything but ping replies (i.e. ignore requests that
* other ping clients might be sending.)
*/
if (ping_pkt->cmd != PING_REPLY) {
elog(LOG_DEBUG(2), "got non-reply packet - dropping");
goto done;
}
/*
* Ignore replies not meant for us (based on the random ID), or that
* are arriving late for the previous request
*/
if (ping_pkt->random_id != p->random_id ||
ping_pkt->seqno != p->last_seqno) {
elog(LOG_DEBUG(2), "dropping old or not-for-us reply packet");
goto done;
}
/*
* compute the roundtrip time, by subtracting the time the reply
* arrived from the time the original ping was sent.
*/
misc_tv_sub(&link_pkt->rcv_time, &p->time_sent);
p->rtt[p->num_replies++] = link_pkt->rcv_time;
elog(LOG_NOTICE, "got reply %d from node %d, iface %s, rtt %.2f ms, %d hops away",
p->num_replies,
ping_pkt->node_id,
print_if_id(link_pkt->src.id),
misc_tv_msec_f(&link_pkt->rcv_time),
PING_MAX_HOPS - link_pkt->max_hops + 1);
if (p->num_replies >= MAX_PINGS) {
elog(LOG_ALERT, "max pings exceeded; shutting down");
ping_shutdown(p);
}
done:
/* note, packet must be freed! */
free(link_pkt);
return EVENT_RENEW;
}
void usage(char *name)
{
misc_print_usage
(name, "-l <long-device> -s <short-device> [-f] [-t <linterval>] [-r <sinterval>]",
" --l/suses <device>: Specify device for long/short-intervals to use\n"
" --flood: send pings as fast as possible\n"
" --l[t]/sint[r]: interval for long/short period (ms)\n"
);
exit(1);
}
int ping_command(char* cmd, size_t size, void* data)
{
pd_ctx_t* pd = (pd_ctx_t *)data;
uint sint = 0;
uint lint = 0;
parser_state_t ps = {
input: (void *) cmd,
input_len: size + 1
};
if (!pd) return EVENT_DONE;
elog(LOG_DEBUG(1),"string = %s\n", cmd);
/* Parse command string */
while(misc_parse_next_kvp(&ps) >= 0)
{
if (strcmp(ps.key, "sint") == 0)
{
sint = (uint) atoi(ps.value);
elog(LOG_DEBUG(1), "Changing sint val from %d to %d\n", pd->sint, sint);
pd->sint = sint;
}
if (strcmp(ps.key, "lint") == 0)
{
lint = (uint) atoi(ps.value);
elog(LOG_DEBUG(1), "Changing lint val from %d to %d\n", pd->lint, lint);
pd->lint = lint;
}
}
/* Set timer if we get correct command */
elog(LOG_DEBUG(1), "sint = %d, list = %d\n", sint, lint);
if (!pd->stimer) return EVENT_DONE;
if (!pd->ltimer) return EVENT_DONE;
if (sint > 0)
{
if (pd->stimer) g_timer_resched(pd->stimer, sint);
else g_timer_add(sint, ping_send_short, pd, NULL, &pd->stimer);
elog(LOG_DEBUG(1), "Reprogrammed short timer with int %d\n", sint);
}
if (lint > 0)
{
if (pd->ltimer) g_timer_resched(pd->ltimer, lint);
else g_timer_add(lint, ping_send_long, pd, NULL, &pd->ltimer);
elog(LOG_DEBUG(1), "Reprogrammed long timer with int %d\n", lint);
}
elog(LOG_DEBUG(1), "done with command\n");
return EVENT_RENEW;
}
char* ping_usage(void* data)
{
static char buf[200];
sprintf(buf, "usage: write lint=<int [ms]>:sint=<int [ms]> to specify interval for long/short pings\n");
return buf;
}
int main(int argc, char *argv[])
{
pd_ctx_t pd = {
lint:LONG_INTERVAL,
sint:SHORT_INTERVAL,
};
char *luses = NULL;
char *suses = NULL;
emrun_opts_t emrun_opts = {
shutdown: ping_shutdown,
data: (void *) &pd
};
cmd_dev_opts_t c_opts = {
device: {
devname: CMD_DEV_NAME,
device_info: &pd,
},
command: ping_command,
usage: ping_usage
};
/* Generic initialization common to most software */
misc_init(&argc, argv, CVSTAG);
if (!g_command_dev(&c_opts, NULL))
{
elog(LOG_ERR, "UNable to create command device: %s!\n", c_opts.device.devname);
}
/* get the --uses arg */
if (!(suses = misc_parse_out_option(&argc, argv, "suses", 's')))
{
elog(LOG_CRIT, "Please specify a link to use for the short-interval!\n");
usage(argv[0]);
}
if (!(luses = misc_parse_out_option(&argc, argv, "luses", 'l')))
{
elog(LOG_CRIT, "Please specify a link to use for the long-interval!\n");
usage(argv[0]);
}
if (misc_parse_option_as_uint(&argc, argv, "lint", 't', &pd.lint))
{
elog(LOG_DEBUG(1), "have long-int spec: %d\n", pd.lint);
elog(LOG_CRIT, "have long-int spec: %d\n", pd.lint);
}
if (misc_parse_option_as_uint(&argc, argv, "sint", 'r', &pd.sint))
{
elog(LOG_DEBUG(1), "have short-int spec: %d\n", pd.sint);
elog(LOG_CRIT, "have short-int spec: %d\n", pd.sint);
}
elog(LOG_DEBUG(1), "long-int = %d, short-int = %d\n", pd.lint, pd.sint);
/* add'l args? */
if (misc_args_remain(&argc, argv)) {
elog(LOG_CRIT, "Additional unparsed arguments!");
usage(argv[0]);
}
/*
* Pick our random ID (a 14 bit number, from the definition of the
* ping packet in ping.h
*/
memset(&pd, 0, sizeof(pd_ctx_t));
pd.ps_short.random_id = random_range(1, (1 << 13));
pd.ps_long.random_id = random_range(1, (1 << 13));
/*
* Open the link-layer device. The options struct tells it we want
* the ping_receiver function to be called every time a packet of
* type PKT_TYPE_PING arrives.
*
* If the link-opening succeeds, the link struct is returned to us
* using the 2nd argument (i.e., written to pd.ps_short.link)
*/
{
lu_opts_t lu_opts = {
opts: {
name: suses,
data: &pd.ps_short,
pkt_type: PKT_TYPE_PING_SHORT /* only give us ping-type packets */
},
receive: ping_receiver /* call this func when packets arrive */
};
/* Open state for short-interval ping-device */
pd.ps_short.pkt_type = PKT_TYPE_PING_SHORT;
if (lu_open(&lu_opts, &pd.ps_short.link) < 0) {
elog(LOG_CRIT, "can't open %s: %m", link_name(&lu_opts.opts, NULL));
exit(1);
}
/* Open state for long-interval ping-device */
lu_opts.opts.data = &pd.ps_long;
lu_opts.opts.name = luses;
lu_opts.opts.pkt_type = PKT_TYPE_PING_LONG;
pd.ps_long.pkt_type = PKT_TYPE_PING_LONG;
if (lu_open(&lu_opts, &pd.ps_long.link) < 0) {
elog(LOG_CRIT, "can't open %s: %m", link_name(&lu_opts.opts, NULL));
exit(1);
}
elog_g(LOG_NOTICE, "running, using short:%s and long:%s",
lu_name(pd.ps_short.link, NULL),
lu_name(pd.ps_long.link, NULL));
}
/* And set a timer to send one every second */
g_timer_add(SHORT_INTERVAL, ping_send_short, &pd, NULL, &pd.stimer);
g_timer_add(LONG_INTERVAL, ping_send_long, &pd, NULL, &pd.ltimer);
/*
* Start the event loop running - it should never exit (the shutdown
* handler is called when the program is supposed to stop)
*/
emrun_init(&emrun_opts); /* this should be the last initialization */
g_main();
elog_g(LOG_CRIT, "event loop terminated abnormally!");
return 0;
}
/*** LOCAL FUNCS ***/
void compute_stats(ping_state_t* p, char* state)
{
struct timeval min, max, sum;
int i;
min = max = sum = p->rtt[0];
/* This loop finds the min and max RTTs, sums all the RTTs for
* average, and counts how many replies we received. */
for (i = 1; i < p->num_replies; i++) {
/* misc_tv_* are convenience functions for doing computations on a
* struct timeval found in libmisc */
if (misc_tv_offset_neg(&p->rtt[i], &min) < 0)
min = p->rtt[i];
if (misc_tv_offset_neg(&p->rtt[i], &max) > 0)
max = p->rtt[i];
misc_tv_add(&sum, &p->rtt[i]);
}
/* compute average from the sum */
elog(LOG_NOTICE, "%s: %d pings sent, %d replies received"
"min/avg/max rtt %.1f/%.1f/%.1f ms", state,
p->total_sent, p->num_replies,
misc_tv_msec_f(&min),
((float) sum.tv_sec + (sum.tv_usec / MILLION_F)) * 1000.0 /
p->num_replies,
misc_tv_msec_f(&max));
if (p->total_sent > 0) elog(LOG_NOTICE, "%% received: %d%%\n",
100 * p->num_replies/p->total_sent);
}
See more files for this project here