Code Search for Developers
 
 
  

ping.c from EmStar at Krugle


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

EmStar

EmStar is a software system for developing and deploying wireless sensor networks involving Linux-based platforms. As the wireless sensor network community has attempted to deploy more complex designs---large-scale, long-lived systems that need self-organization and adaptivity---a number of difficult software design issues have arisen. Advances in software design have not kept pace with the capabilities of hardware. This is because designing for an adaptive, efficient, and useful sensor network has turned out to be surprisingly complex and difficult. EmStar is a Linux-based software framework, whose goal is to dramatically reduce this complexity, enabling work to be shared and reused, and simplifying and speeding the design of new sensor network applications.

Project homepage: http://cvs.cens.ucla.edu/emstar/
Programming language(s): C,Shell Script
License: other

  link_quality
  pd.run
  ping-flood.run
  ping-flood.sim
  ping-ls-udp.run
  ping.c
  ping.h
  ping_device.sim
  ping_device_rcvr.run
  ping_device_sndr.run
  pingd.c
  pingd.run
  pingd.sim
  receive_ping_device.c
  send_ping_device.c
  test.run
  test.sim
  test_mote.run
  test_mote.sim