]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ping.c
snoopy(8): avoid extra spaces in dhcp filter output
[plan9front.git] / sys / src / cmd / ip / ping.c
1 /* ping for ip v4 and v6 */
2 #include <u.h>
3 #include <libc.h>
4 #include <ctype.h>
5 #include <ip.h>
6 #include <bio.h>
7 #include <ndb.h>
8 #include "icmp.h"
9
10 enum {
11         MAXMSG          = 32,
12         SLEEPMS         = 1000,
13
14         SECOND          = 1000000000LL,
15         MINUTE          = 60*SECOND,
16 };
17
18 typedef struct Req Req;
19 struct Req
20 {
21         ushort  seq;    /* sequence number */
22         vlong   time;   /* time sent */
23         vlong   rtt;
24         int     ttl;
25         int     replied;
26         Req      *next;
27 };
28
29 typedef struct {
30         int     version;
31         char    *net;
32         int     echocmd;
33         int     echoreply;
34         unsigned iphdrsz;
35
36         void    (*prreply)(Req *r, void *v);
37         void    (*prlost)(ushort seq, void *v);
38 } Proto;
39
40
41 Req     *first;         /* request list */
42 Req     *last;          /* ... */
43 Lock    listlock;
44
45 char *argv0;
46
47 int addresses;
48 int debug;
49 int done;
50 int flood;
51 int lostmsgs;
52 int lostonly;
53 int quiet;
54 int rcvdmsgs;
55 int rint;
56 ushort firstseq;
57 vlong sum;
58 int waittime = 5000;
59
60 void lost(Req*, void*);
61 void reply(Req*, void*);
62
63 static void
64 usage(void)
65 {
66         fprint(2,
67             "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",
68                 argv0);
69         exits("usage");
70 }
71
72 static void
73 catch(void *a, char *msg)
74 {
75         USED(a);
76         if(strstr(msg, "alarm"))
77                 noted(NCONT);
78         else if(strstr(msg, "die"))
79                 exits("errors");
80         else
81                 noted(NDFLT);
82 }
83
84 static void
85 prlost4(ushort seq, void *v)
86 {
87         Ip4hdr *ip4 = v;
88
89         print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst);
90 }
91
92 static void
93 prlost6(ushort seq, void *v)
94 {
95         Ip6hdr *ip6 = v;
96
97         print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst);
98 }
99
100 static void
101 prreply4(Req *r, void *v)
102 {
103         Ip4hdr *ip4 = v;
104
105         print("%ud: %V -> %V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
106                 r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,
107                 r->ttl);
108 }
109
110 static void
111 prreply6(Req *r, void *v)
112 {
113         Ip6hdr *ip6 = v;
114
115         print("%ud: %I -> %I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
116                 r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,
117                 r->ttl);
118 }
119
120 static Proto v4pr = {
121         4,              "icmp",
122         EchoRequest,    EchoReply,
123         IPV4HDR_LEN,
124         prreply4,       prlost4,
125 };
126 static Proto v6pr = {
127         6,              "icmpv6",
128         EchoRequestV6,  EchoReplyV6,
129         IPV6HDR_LEN,
130         prreply6,       prlost6,
131 };
132
133 static Proto *proto = &v4pr;
134
135
136 Icmphdr *
137 geticmp(void *v)
138 {
139         char *p = v;
140
141         return (Icmphdr *)(p + proto->iphdrsz);
142 }
143
144 void
145 clean(ushort seq, vlong now, void *v)
146 {
147         int ttl;
148         Req **l, *r;
149
150         ttl = 0;
151         if (v) {
152                 if (proto->version == 4)
153                         ttl = ((Ip4hdr *)v)->ttl;
154                 else
155                         ttl = ((Ip6hdr *)v)->ttl;
156         }
157         lock(&listlock);
158         last = nil;
159         for(l = &first; *l; ){
160                 r = *l;
161
162                 if(v && r->seq == seq){
163                         r->rtt = now-r->time;
164                         r->ttl = ttl;
165                         reply(r, v);
166                 }
167
168                 if(now-r->time > MINUTE){
169                         *l = r->next;
170                         r->rtt = now-r->time;
171                         if(v)
172                                 r->ttl = ttl;
173                         if(r->replied == 0)
174                                 lost(r, v);
175                         free(r);
176                 }else{
177                         last = r;
178                         l = &r->next;
179                 }
180         }
181         unlock(&listlock);
182 }
183
184 void
185 sender(int fd, int msglen, int interval, int n)
186 {
187         int i, extra;
188         ushort seq;
189         char buf[64*1024+512];
190         Icmphdr *icmp;
191         Req *r;
192
193         srand(time(0));
194         firstseq = seq = rand();
195
196         icmp = geticmp(buf);
197         memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE);
198         for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
199                 buf[i] = i;
200         icmp->type = proto->echocmd;
201         icmp->code = 0;
202
203         if(rint != 0 && interval <= 0)
204                 rint = 0;
205         extra = 0;
206         for(i = 0; i < n; i++){
207                 if(i != 0){
208                         if(rint != 0)
209                                 extra = nrand(interval);
210                         sleep(interval + extra);
211                 }
212                 r = malloc(sizeof *r);
213                 if (r == nil)
214                         continue;
215                 hnputs(icmp->seq, seq);
216                 r->seq = seq;
217                 r->next = nil;
218                 r->replied = 0;
219                 r->time = nsec();       /* avoid early free in reply! */
220                 lock(&listlock);
221                 if(first == nil)
222                         first = r;
223                 else
224                         last->next = r;
225                 last = r;
226                 unlock(&listlock);
227                 r->time = nsec();
228                 if(write(fd, buf, msglen) < msglen){
229                         fprint(2, "%s: write failed: %r\n", argv0);
230                         return;
231                 }
232                 seq++;
233         }
234         done = 1;
235 }
236
237 void
238 rcvr(int fd, int msglen, int interval, int nmsg)
239 {
240         int i, n, munged;
241         ushort x;
242         vlong now;
243         char err[ERRMAX];
244         uchar buf[64*1024+512];
245         Icmphdr *icmp;
246         Req *r;
247
248         sum = 0;
249         while(lostmsgs+rcvdmsgs < nmsg){
250                 alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);
251                 n = read(fd, buf, sizeof buf);
252                 alarm(0);
253                 if(n == 0)
254                         strcpy(err, "got eof");
255                 else if(n < 0)
256                         rerrstr(err, sizeof(err));
257                 now = nsec();
258                 if(n <= 0){
259                         print("%s\n", err);
260                         clean(0, now+MINUTE, nil);
261                         if(strstr(err, "interrupted") == nil)
262                                 sleep(waittime);
263                         continue;
264                 }
265                 if(n < msglen){
266                         print("bad len %d/%d\n", n, msglen);
267                         continue;
268                 }
269                 icmp = geticmp(buf);
270                 munged = 0;
271                 for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
272                         if(buf[i] != (uchar)i)
273                                 munged++;
274                 if(munged)
275                         print("corrupted reply\n");
276                 x = nhgets(icmp->seq);
277                 if(icmp->type != proto->echoreply || icmp->code != 0) {
278                         print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",
279                                 icmp->type, icmp->code, x,
280                                 proto->echoreply, 0, x);
281                         continue;
282                 }
283                 clean(x, now, buf);
284         }
285
286         lock(&listlock);
287         for(r = first; r; r = r->next)
288                 if(r->replied == 0)
289                         lostmsgs++;
290         unlock(&listlock);
291
292         if(!quiet && lostmsgs)
293                 print("%d out of %d messages lost\n", lostmsgs,
294                         lostmsgs+rcvdmsgs);
295 }
296
297 enum
298 {
299         Maxstring       = 128,
300         Maxpath         = 256,
301 };
302
303 void
304 main(int argc, char **argv)
305 {
306         int fd, msglen, interval, nmsg;
307
308         nsec();         /* make sure time file is already open */
309
310         fmtinstall('V', eipfmt);
311         fmtinstall('I', eipfmt);
312
313         msglen = interval = 0;
314         nmsg = MAXMSG;
315         ARGBEGIN {
316         case '6':
317                 proto = &v6pr;
318                 break;
319         case 'a':
320                 addresses = 1;
321                 break;
322         case 'd':
323                 debug++;
324                 break;
325         case 'f':
326                 flood = 1;
327                 break;
328         case 'i':
329                 interval = atoi(EARGF(usage()));
330                 if(interval < 0)
331                         usage();
332                 break;
333         case 'l':
334                 lostonly++;
335                 break;
336         case 'n':
337                 nmsg = atoi(EARGF(usage()));
338                 if(nmsg < 0)
339                         usage();
340                 break;
341         case 'q':
342                 quiet = 1;
343                 break;
344         case 'r':
345                 rint = 1;
346                 break;
347         case 's':
348                 msglen = atoi(EARGF(usage()));
349                 break;
350         case 'w':
351                 waittime = atoi(EARGF(usage()));
352                 if(waittime < 0)
353                         usage();
354                 break;
355         default:
356                 usage();
357                 break;
358         } ARGEND;
359
360         if(msglen < proto->iphdrsz + ICMP_HDRSIZE)
361                 msglen = proto->iphdrsz + ICMP_HDRSIZE;
362         if(msglen < 64)
363                 msglen = 64;
364         if(msglen >= 64*1024)
365                 msglen = 64*1024-1;
366         if(interval <= 0 && !flood)
367                 interval = SLEEPMS;
368
369         if(argc < 1)
370                 usage();
371
372         notify(catch);
373
374         if(strstr(argv[0], "icmpv6!") != nil)
375                 proto = &v6pr;
376 again:
377         fd = dial(netmkaddr(argv[0], proto->net, "1"), nil, nil, nil);
378         if(fd < 0){
379                 if(proto == &v4pr){
380                         proto = &v6pr;
381                         goto again;
382                 }
383                 fprint(2, "%s: couldn't dial: %r\n", argv0);
384                 exits("dialing");
385         }
386         if (!quiet){
387                 NetConnInfo *nci = getnetconninfo(nil, fd);
388                 print("sending %d %d byte messages %d ms apart to %s\n",
389                         nmsg, msglen, interval, nci != nil? nci->raddr: argv[0]);
390         }
391
392         switch(rfork(RFPROC|RFMEM|RFFDG)){
393         case -1:
394                 fprint(2, "%s: can't fork: %r\n", argv0);
395                 exits("forking");
396         case 0:
397                 rcvr(fd, msglen, interval, nmsg);
398                 exits(0);
399         default:
400                 sender(fd, msglen, interval, nmsg);
401                 wait();
402                 exits(lostmsgs ? "lost messages" : "");
403         }
404 }
405
406 void
407 reply(Req *r, void *v)
408 {
409         r->rtt /= 1000LL;
410         sum += r->rtt;
411         if(!r->replied)
412                 rcvdmsgs++;
413         if(!quiet && !lostonly)
414                 if(addresses)
415                         (*proto->prreply)(r, v);
416                 else
417                         print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
418                                 r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
419         r->replied = 1;
420 }
421
422 void
423 lost(Req *r, void *v)
424 {
425         if(!quiet)
426                 if(addresses && v != nil)
427                         (*proto->prlost)(r->seq - firstseq, v);
428                 else
429                         print("lost %ud\n", r->seq - firstseq);
430         lostmsgs++;
431 }