1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32 /*
33 * ping.c: This a "ping" client, which broadcasts a 'ping' packet, and
34 * then waits for replies from the network. This program, and the
35 * corresponding 'pingd' server, serve as a useful example for various
36 * tasks:
37 *
38 * - How to create an application-layer protocol format
39 * - How to send packets to the network
40 * - How to wait for and react to packets that come from the network
41 * - How to filter incoming packets
42 *
43 * $Id: glsping.c,v 1.3 2003-07-11 22:30:01 cerpa Exp $
44 */
45
46 char ping_c_cvsid[] = "$Id: glsping.c,v 1.3 2003-07-11 22:30:01 cerpa Exp $";
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <sys/time.h>
51 #include <arpa/inet.h>
52
53 #include "emrun/emrun.h"
54 #include "link/link.h"
55 #include "glsping.h"
56
57
58 /*******************************************************************/
59
60
61 #define MAX_PINGS 1000
62
63 typedef struct ping_state {
64 int random_id; /* our randomly selected ID, to
65 * differentiate our packets from any
66 * other ping processes that might be
67 * happening at the same time. */
68 int last_seqno; /* last seqno we used */
69 int total_sent; /* total pings sent */
70 struct timeval time_sent; /* time we sent the most recent ping */
71 struct timeval rtt[MAX_PINGS]; /* rtts of responses we've received */
72 int num_replies; /* number of responses we've received */
73
74 loc_t dst_loc; /* destination location */
75
76 link_context_t *link; /* link we're using to ping on */
77 } ping_state_t;
78
79
80 /*******************************************************************/
81
82 /*
83 * Callback activated when we are asked to shut down by emrun.
84 * Compute and print summary statistics of all replies received
85 */
86 void ping_shutdown(void *data)
87 {
88 ping_state_t *p = (ping_state_t *) data;
89 struct timeval min, max, sum;
90 int i;
91
92 if (!p->num_replies) {
93 elog_g(LOG_NOTICE, "%d pings sent, no replies received", p->total_sent);
94 goto done;
95 }
96
97 min = max = sum = p->rtt[0];
98
99 /* This loop finds the min and max RTTs, sums all the RTTs for
100 * average, and counts how many replies we received. */
101 for (i = 1; i < p->num_replies; i++) {
102
103 /* misc_tv_* are convenience functions for doing computations on a
104 * struct timeval found in libmisc */
105 if (misc_tv_offset_neg(&p->rtt[i], &min) < 0)
106 min = p->rtt[i];
107 if (misc_tv_offset_neg(&p->rtt[i], &max) > 0)
108 max = p->rtt[i];
109
110 misc_tv_add(&sum, &p->rtt[i]);
111 }
112
113 /* compute average from the sum */
114 elog_g(LOG_NOTICE, "%d pings sent, %d replies received, "
115 "min/avg/max rtt %.1f/%.1f/%.1f ms",
116 p->total_sent, p->num_replies,
117 misc_tv_msec_f(&min),
118
119 ((float) sum.tv_sec + (sum.tv_usec / MILLION_F)) * 1000.0 /
120 p->num_replies,
121
122 misc_tv_msec_f(&max));
123
124 /* Now shut down */
125 done:
126 exit(0);
127 }
128
129
130 /*
131 * Construct a ping packet, and send it out over the wire, using the
132 * link context stored as part of our "ping" state.
133 */
134 void ping_send(ping_state_t *p, loc_t *dst_loc)
135 {
136 /* Allocate a small buffer for the packet */
137 char buf[200];
138 link_pkt_t *pkt = (link_pkt_t *) buf;
139 ping_pkt_t *ping_pkt = (ping_pkt_t *) pkt->data;
140
141 /* initialize the outer header (the link_pkt header) */
142 memset(buf, 0, sizeof(buf));
143 pkt->dst.id = LINK_BROADCAST;
144 pkt->type = PKT_TYPE_PING;
145
146 /* and our application-layer (ping) protocol data */
147 ping_pkt->cmd = PING_REQUEST;
148 ping_pkt->random_id = p->random_id;
149 ping_pkt->seqno = ++(p->last_seqno);
150
151 /* remember what time it is, so we can calculate RTT */
152 p->total_sent++;
153 gettimeofday(&p->time_sent, NULL);
154
155 /* set the destination geo location */
156 pkt->dst.loc.x = dst_loc->x;
157 pkt->dst.loc.y = dst_loc->y;
158
159 /* now launch the packet! */
160 if (link_send(p->link, pkt, sizeof(ping_pkt_t)) < 0)
161 elog(LOG_ERR, "can't send on %s: %m", link_name(link_opts(p->link)));
162 else
163 elog(LOG_NOTICE, "broadcast ping seqno %d", ping_pkt->seqno);
164 }
165
166
167 /* Called every time our periodic ping timer expies. */
168 int ping_periodic_timer(void *data, int interval, g_event_t *ev)
169 {
170 ping_state_t *p = (ping_state_t *) data;
171
172 ping_send(p, &p->dst_loc);
173
174 return EVENT_RENEW;
175 }
176
177
178 /*
179 * This callback is called whenever a packet arrives on the link we
180 * opened. "pkt" is a pointer to a link_pkt_t header followed by
181 * data_len bytes of data. (data_len is the length of the data
182 * following the header; it doesn't include the size of the link_pkt_t
183 * header itself.) "link" is a pointer to the link that the packet
184 * was received on, and can be used for sending a reply packet (we
185 * don't send reply packets from the receiver callback here, but see
186 * pingd for an example where we do.)
187 */
188 int ping_receiver(link_pkt_t *link_pkt, ssize_t data_len, link_context_t *link)
189 {
190 /* Get a pointer to the data portion of the packet */
191 ping_pkt_t *ping_pkt = (ping_pkt_t *) link_pkt->data;
192
193 /* We stored a pointer to the 'ping_state' struct as the "data"
194 * field of link_opts in main(). Now, we retrieve that pointer. */
195 ping_state_t *p = (ping_state_t *) (link_opts(link))->data;
196
197
198 elog (LOG_NOTICE, "Ping received packet");
199
200 /*
201 * Make sure the packet has the right packet type. Since we
202 * specified the desired packet type when we registered to receive
203 * packets, this test should never fail.
204 */
205 if (link_pkt->type != PKT_TYPE_PING) {
206 elog(LOG_WARNING, "got a packet not meant for us - filter failed!");
207 goto done;
208 }
209
210 /*
211 * Make sure the data portion of the packet is exactly the right
212 * size (i.e., the size of our "ping" application-layer protocol
213 * frame.
214 */
215 if (data_len != sizeof(ping_pkt_t)) {
216 elog(LOG_ERR, "got a short ping packet (%d bytes)!", data_len);
217 goto done;
218 }
219
220 /* We now know that the data pointed to by ping_pkt is valid */
221
222 /*
223 * Ignore everything but ping replies (i.e. ignore requests that
224 * other ping clients might be sending.)
225 */
226 if (ping_pkt->cmd != PING_REPLY) {
227 elog(LOG_DEBUG(2), "got non-reply packet - dropping");
228 goto done;
229 }
230
231 /*
232 * Ignore replies not meant for us (based on the random ID), or that
233 * are arriving late for the previous request
234 */
235 if (ping_pkt->random_id != p->random_id ||
236 ping_pkt->seqno != p->last_seqno) {
237 elog(LOG_DEBUG(2), "dropping old or not-for-us reply packet");
238 goto done;
239 }
240
241 /*
242 * compute the roundtrip time, by subtracting the time the reply
243 * arrived from the time the original ping was sent.
244 */
245 misc_tv_sub(&link_pkt->rcv_time, &p->time_sent);
246 p->rtt[p->num_replies++] = link_pkt->rcv_time;
247
248 elog(LOG_NOTICE, "got reply %d from node %d, iface %s, rtt %.2f ms",
249 p->num_replies,
250 ping_pkt->node_id,
251 print_if_id(link_pkt->src.id),
252 misc_tv_msec_f(&link_pkt->rcv_time));
253
254 if (p->num_replies >= MAX_PINGS) {
255 elog(LOG_ALERT, "max pings exceeded; shutting down");
256 ping_shutdown(p);
257 }
258
259 done:
260 /* note, packet must be freed! */
261 free(link_pkt);
262 return EVENT_RENEW;
263 }
264
265
266
267 int main(int argc, char *argv[])
268 {
269 ping_state_t ping_state;
270 int period = 1000;
271
272 link_opts_t link_opts = {
273 link_index: LINK_INDEX_AUTO, /* use any available link */
274 pkt_type: PKT_TYPE_PING, /* only give us ping-type packets */
275 receive: ping_receiver, /* call this func when packets arrive */
276 data: (void *) &ping_state /* store ping_state so link callback
277 can use it */
278 };
279
280 emrun_opts_t emrun_opts = {
281 shutdown: ping_shutdown,
282 data: (void *) &ping_state
283 };
284
285 /* Generic initialization common to most software */
286 misc_init(&argc, argv, CVSTAG);
287
288 /*
289 * Pick our random ID (a 14 bit number, from the definition of the
290 * ping packet in ping.h
291 */
292 memset(&ping_state, 0, sizeof(ping_state));
293 ping_state.random_id = random_range(1, (1 << 13));
294
295 /*
296 * If the user provided a "gls" argument, open the flooding device
297 * instead of the normal data device. Also, send pings more slowly.
298 */
299 if (argc>1 && !strcmp(argv[1], "gls")) {
300 link_opts.dev_type = LINK_DEV_GLS;
301 ping_state.dst_loc.x = atof(argv[2]);
302 ping_state.dst_loc.y = atof(argv[3]);
303 period *= 10;
304 }
305
306 /*
307 * Open the link-layer device. The options struct tells it we want
308 * the ping_receiver function to be called every time a packet of
309 * type PKT_TYPE_PING arrives.
310 *
311 * If the link-opening succeeds, the link struct is returned to us
312 * using the 2nd argument (i.e., written to ping_state.link)
313 */
314 if (link_open(&link_opts, &ping_state.link) < 0) {
315 elog(LOG_CRIT, "can't open %s: %m", link_name(&link_opts));
316 exit(1);
317 }
318
319 elog_g(LOG_NOTICE, "running, using %s", link_name(&link_opts));
320
321 /* Send an initial ping */
322 ping_send(&ping_state, &ping_state.dst_loc);
323
324 /* And set a timer to send one every second */
325 g_timer_add(period, ping_periodic_timer, &ping_state, NULL, NULL);
326
327 /*
328 * Start the event loop running - it should never exit (the shutdown
329 * handler is called when the program is supposed to stop)
330 */
331 emrun_init(&emrun_opts); /* this should be the last initialization */
332 g_main();
333 elog_g(LOG_CRIT, "event loop terminated abnormally!");
334 return 0;
335 }
336
337
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.