Show ping.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.c: This a "ping" client, which broadcasts a 'ping' packet, and
* then waits for replies from the network. This program, and the
* corresponding 'pingd' server, serve as a useful example for various
* tasks:
*
* - How to create an application-layer protocol format
* - How to send packets to the network
* - How to wait for and react to packets that come from the network
* - How to filter incoming packets
*
* $Id: ping.c,v 1.37 2005/09/29 19:43:35 karenyc Exp $
*/
char ping_c_cvsid[] = "$Id: ping.c,v 1.37 2005/09/29 19:43:35 karenyc Exp $";
#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"
/*******************************************************************/
#define MAX_PINGS 1000
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 last_seqno_rcv; /* last seqno received (used to detect
* duplicates when using hbh and acks are
* lost) */
if_id_t last_replier; /* src address of last reply */
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 */
int request_receipts; /* command line arg to request receipts */
lu_context_t *link; /* link we're using to ping on */
if_id_t dest; /* node id destination of our ping */
} ping_state_t;
/*******************************************************************/
/*
* 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)
{
ping_state_t *p = (ping_state_t *) data;
struct timeval min, max, sum;
int i;
if (!p->num_replies) {
elog_g(LOG_NOTICE, "%d pings sent, no replies received", p->total_sent);
goto done;
}
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_g(LOG_NOTICE, "%d pings sent, %d replies received, "
"min/avg/max rtt %.1f/%.1f/%.1f ms",
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));
/* Now shut down */
done:
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)
{
/* 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->src.id = my_node_id;
pkt->dst.id = p->dest;
pkt->type = PKT_TYPE_PING;
/* and our application-layer (ping) protocol data */
ping_pkt->cmd = PING_REQUEST;
ping_pkt->random_id = p->random_id;
ping_pkt->seqno = this_seq;
ping_pkt->node_id = my_node_id;
/* request a receipt.. */
/* if you request a receipt, you will receive back a MAC_CTRL message that tells
* what happened to your packet. */
if (p->request_receipts)
link_receipt_request(pkt, NULL);
/* 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, "send ping seqno %d to dest %s", ping_pkt->seqno,
print_if_id(pkt->dst.id));
elog(LOG_DEBUG(5), "command: %d, random id: %d, data len: %d",
ping_pkt->cmd, ping_pkt->random_id, sizeof(ping_pkt_t));
/* 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_periodic_timer(void *data, int interval, g_event_t *ev)
{
ping_state_t *p = (ping_state_t *) data;
ping_send(p);
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)
{
/* flag if duplicate */
int duplicate=0;
/* 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);
/*
* Make sure the packet has the right packet type. Since we
* specified the desired packet type when we registered to receive
* packets, this test should never fail.
*/
if (link_pkt->type != PKT_TYPE_PING) {
elog(LOG_WARNING, "got a packet not meant for us - filter failed!");
goto done;
}
/*
* 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;
}
/* Flag (some) duplicates that arrive due to the lost of an ack, when using
* a hop by hop reliable mechanism like hbh. Will usually work using
* unicast pings, will sometimes work with broadcasts.
*
* NOTE: when using multihop routing *and* if the links in the path
* are not very reliable, it would be possible that a later ping reply
* could get to us before a previous one (out of order delivery).
* The chances of out of order delivery increase with smaller ping
* intervals and with a low reliability network. If this is the case,
* you may want to maintain not just the last packet recieved but a
* list (buffer) of the last X packet received, such that you can fill
* the holes when you have out of order delivery. The current simple
* implementation detects duplicates but it does not deal with out of
* order delivery.
*/
if (ping_pkt->seqno < p->last_seqno_rcv ||
(ping_pkt->seqno == p->last_seqno_rcv &&
link_pkt->src.id == p->last_replier)) {
duplicate = 1;
} else { /* update state */
p->last_seqno_rcv = ping_pkt->seqno;
p->last_replier = link_pkt->src.id;
}
/*
* 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 %s",
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,
duplicate ? "(DUP!)" : "");
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, "-U <device> [-f] [-i <interval>] [-p <pot value>] [-d <destination>]",
" --uses <device>: Specify device to use\n"
" --flood: send pings as fast as possible\n"
" --interval: send pings as specified interval (ms)\n"
" --pot <value>: TX potentiomenter value\n"
" --dest <value>: node id destination of the pings\n"
" --request_receipts: turn on receipt requests\n"
);
exit(1);
}
int main(int argc, char *argv[])
{
ping_state_t ping_state;
uint period = 1000;
int pot = 0;
char *uses = NULL;
emrun_opts_t emrun_opts = {
shutdown: ping_shutdown,
data: (void *) &ping_state
};
/* Generic initialization common to most software */
misc_init(&argc, argv, CVSTAG);
/* clean up our state variable */
memset(&ping_state, 0, sizeof(ping_state));
/* get the --uses arg */
uses = link_parse_uses(&argc, argv, NULL);
if (uses == NULL) {
elog(LOG_CRIT, "Please specify a link to use!");
usage(argv[0]);
}
/* ping flood option */
if (misc_parse_out_switch(&argc, argv, "flood", 'f'))
period = 10;
/* ping interval option */
misc_parse_option_as_uint(&argc, argv, "interval", 'i', &period);
/* ping pot option */
misc_parse_option_as_int(&argc, argv, "pot", 'p', &pot);
/* request receipt option */
ping_state.request_receipts = misc_parse_out_switch(&argc, argv, "request_receipts", 0);
/* set default destination to broadcast */
ping_state.dest = LINK_BROADCAST;
/* ping destination option */
char *dest_str = misc_parse_out_option(&argc, argv, "dest", 'd');
if (dest_str && parse_if_id(dest_str, &(ping_state.dest)) < 0) {
elog(LOG_CRIT, "invalid destination address: '%s'.. use 0xXX, DD, or dotted-quad",
dest_str);
exit(1);
}
/* 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
*/
ping_state.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 ping_state.link)
*/
{
lu_opts_t lu_opts = {
opts: {
name: uses,
data: &ping_state,
pkt_type: PKT_TYPE_PING /* only give us ping-type packets */
},
receive: ping_receiver /* call this func when packets arrive */
};
if (lu_open(&lu_opts, &ping_state.link) < 0) {
elog(LOG_CRIT, "can't open %s: %m", link_name(&lu_opts.opts, NULL));
exit(1);
}
/* Try to set the potentiometer of the radio to 'pot' */
// ONLY if the pot has in fact been set...no more 70ies
if (pot > 0) {
if (lu_ioctl(ping_state.link, LINK_SET_POT, &pot) < 0)
elog(LOG_CRIT, "warning: can't set potentiometer: %m");
}
elog_g(LOG_NOTICE, "running, using %s", lu_name(ping_state.link, NULL));
}
/* Send an initial ping */
ping_send(&ping_state);
/* And set a timer to send one every 'period' miliseconds */
g_timer_add(period, ping_periodic_timer, &ping_state, NULL, NULL);
/*
* 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;
}
See more files for this project here