Ditch the sysvinit stuff
[profile/ivi/iputils.git] / ping.c
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Muuss.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
40  All rights reserved.\n";
41 #endif /* not lint */
42
43 /*
44  *                      P I N G . C
45  *
46  * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
47  * measure round-trip-delays and packet loss across network paths.
48  *
49  * Author -
50  *      Mike Muuss
51  *      U. S. Army Ballistic Research Laboratory
52  *      December, 1983
53  *
54  * Status -
55  *      Public Domain.  Distribution Unlimited.
56  * Bugs -
57  *      More statistics could always be gathered.
58  *      This program has to run SUID to ROOT to access the ICMP socket.
59  */
60
61 #include "ping_common.h"
62
63 #include <netinet/ip.h>
64 #include <netinet/ip_icmp.h>
65
66 #ifndef ICMP_FILTER
67 #define ICMP_FILTER     1
68 struct icmp_filter {
69         __u32   data;
70 };
71 #endif
72
73
74 #define MAXIPLEN        60
75 #define MAXICMPLEN      76
76 #define NROUTES         9               /* number of record route slots */
77 #define TOS_MAX         255             /* 8-bit TOS field */
78 #define MAX_HOSTNAMELEN NI_MAXHOST
79
80
81 static int ts_type;
82 static int nroute = 0;
83 static __u32 route[10];
84
85
86
87 struct sockaddr_in whereto;     /* who to ping */
88 int optlen = 0;
89 int settos = 0;                 /* Set TOS, Precendence or other QOS options */
90 int icmp_sock;                  /* socket file descriptor */
91 u_char outpack[0x10000];
92 int maxpacket = sizeof(outpack);
93
94 static int broadcast_pings = 0;
95
96 static char *pr_addr(__u32);
97 static void pr_options(unsigned char * cp, int hlen);
98 static void pr_iph(struct iphdr *ip);
99 static void usage(void) __attribute__((noreturn));
100 static u_short in_cksum(const u_short *addr, int len, u_short salt);
101 static void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp);
102 static int parsetos(char *str);
103
104 static struct {
105         struct cmsghdr cm;
106         struct in_pktinfo ipi;
107 } cmsg = { {sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), SOL_IP, IP_PKTINFO},
108            {0, }};
109 int cmsg_len;
110
111 struct sockaddr_in source;
112 char *device;
113 int pmtudisc = -1;
114
115
116 int
117 main(int argc, char **argv)
118 {
119         struct hostent *hp;
120         int ch, hold, packlen;
121         int socket_errno;
122         u_char *packet;
123         char *target, hnamebuf[MAX_HOSTNAMELEN];
124         char rspace[3 + 4 * NROUTES + 1];       /* record route space */
125
126         icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
127         socket_errno = errno;
128
129         uid = getuid();
130         if (setuid(uid)) {
131                 perror("ping: setuid");
132                 exit(-1);
133         }
134
135         source.sin_family = AF_INET;
136
137         preload = 1;
138         while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:")) != EOF) {
139                 switch(ch) {
140                 case 'b':
141                         broadcast_pings = 1;
142                         break;
143                 case 'Q':
144                         settos = parsetos(optarg);
145                         if (settos &&
146                             (setsockopt(icmp_sock, IPPROTO_IP, IP_TOS,
147                                         (char *)&settos, sizeof(int)) < 0)) {
148                                 perror("ping: error setting QOS sockopts");
149                                 exit(2);
150                         }
151                         break;
152                 case 'R':
153                         if (options & F_TIMESTAMP) {
154                                 fprintf(stderr, "Only one of -T or -R may be used\n");
155                                 exit(2);
156                         }
157                         options |= F_RROUTE;
158                         break;
159                 case 'T':
160                         if (options & F_RROUTE) {
161                                 fprintf(stderr, "Only one of -T or -R may be used\n");
162                                 exit(2);
163                         }
164                         options |= F_TIMESTAMP;
165                         if (strcmp(optarg, "tsonly") == 0)
166                                 ts_type = IPOPT_TS_TSONLY;
167                         else if (strcmp(optarg, "tsandaddr") == 0)
168                                 ts_type = IPOPT_TS_TSANDADDR;
169                         else if (strcmp(optarg, "tsprespec") == 0)
170                                 ts_type = IPOPT_TS_PRESPEC;
171                         else {
172                                 fprintf(stderr, "Invalid timestamp type\n");
173                                 exit(2);
174                         }
175                         break;
176                 case 'I':
177                 {
178 #if 0
179                         char dummy;
180                         int i1, i2, i3, i4;
181
182                         if (sscanf(optarg, "%u.%u.%u.%u%c",
183                                    &i1, &i2, &i3, &i4, &dummy) == 4) {
184                                 __u8 *ptr;
185                                 ptr = (__u8*)&source.sin_addr;
186                                 ptr[0] = i1;
187                                 ptr[1] = i2;
188                                 ptr[2] = i3;
189                                 ptr[3] = i4;
190                                 options |= F_STRICTSOURCE;
191                         } else {
192                                 device = optarg;
193                         }
194 #else
195                         if (inet_pton(AF_INET, optarg, &source.sin_addr) > 0)
196                                 options |= F_STRICTSOURCE;
197                         else
198                                 device = optarg;
199 #endif
200                         break;
201                 }
202                 case 'M':
203                         if (strcmp(optarg, "do") == 0)
204                                 pmtudisc = IP_PMTUDISC_DO;
205                         else if (strcmp(optarg, "dont") == 0)
206                                 pmtudisc = IP_PMTUDISC_DONT;
207                         else if (strcmp(optarg, "want") == 0)
208                                 pmtudisc = IP_PMTUDISC_WANT;
209                         else {
210                                 fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
211                                 exit(2);
212                         }
213                         break;
214                 case 'V':
215                         printf("ping utility, iputils-ss%s\n", SNAPSHOT);
216                         exit(0);
217                 COMMON_OPTIONS
218                         common_options(ch);
219                         break;
220                 default:
221                         usage();
222                 }
223         }
224         argc -= optind;
225         argv += optind;
226
227         if (argc == 0)
228                 usage();
229         if (argc > 1) {
230                 if (options & F_RROUTE)
231                         usage();
232                 else if (options & F_TIMESTAMP) {
233                         if (ts_type != IPOPT_TS_PRESPEC)
234                                 usage();
235                         if (argc > 5)
236                                 usage();
237                 } else {
238                         if (argc > 10)
239                                 usage();
240                         options |= F_SOURCEROUTE;
241                 }
242         }
243         while (argc > 0) {
244                 target = *argv;
245
246                 memset((char *)&whereto, 0, sizeof(whereto));
247                 whereto.sin_family = AF_INET;
248                 if (inet_aton(target, &whereto.sin_addr) == 1) {
249                         hostname = target;
250                         if (argc == 1)
251                                 options |= F_NUMERIC;
252                 } else {
253                         hp = gethostbyname(target);
254                         if (!hp) {
255                                 fprintf(stderr, "ping: unknown host %s\n", target);
256                                 exit(2);
257                         }
258                         memcpy(&whereto.sin_addr, hp->h_addr, 4);
259                         strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
260                         hnamebuf[sizeof(hnamebuf) - 1] = 0;
261                         hostname = hnamebuf;
262                 }
263                 if (argc > 1)
264                         route[nroute++] = whereto.sin_addr.s_addr;
265                 argc--;
266                 argv++;
267         }
268
269         if (source.sin_addr.s_addr == 0) {
270                 socklen_t alen;
271                 struct sockaddr_in dst = whereto;
272                 int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
273
274                 if (probe_fd < 0) {
275                         perror("socket");
276                         exit(2);
277                 }
278                 if (device) {
279                         struct ifreq ifr;
280                         memset(&ifr, 0, sizeof(ifr));
281                         strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
282                         if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
283                                 if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) {
284                                         struct ip_mreqn imr;
285                                         if (ioctl(probe_fd, SIOCGIFINDEX, &ifr) < 0) {
286                                                 fprintf(stderr, "ping: unknown iface %s\n", device);
287                                                 exit(2);
288                                         }
289                                         memset(&imr, 0, sizeof(imr));
290                                         imr.imr_ifindex = ifr.ifr_ifindex;
291                                         if (setsockopt(probe_fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) {
292                                                 perror("ping: IP_MULTICAST_IF");
293                                                 exit(2);
294                                         }
295                                 }
296                         }
297                 }
298
299                 if (settos &&
300                     setsockopt(probe_fd, IPPROTO_IP, IP_TOS, (char *)&settos, sizeof(int)) < 0)
301                         perror("Warning: error setting QOS sockopts");
302
303                 dst.sin_port = htons(1025);
304                 if (nroute)
305                         dst.sin_addr.s_addr = route[0];
306                 if (connect(probe_fd, (struct sockaddr*)&dst, sizeof(dst)) == -1) {
307                         if (errno == EACCES) {
308                                 if (broadcast_pings == 0) {
309                                         fprintf(stderr, "Do you want to ping broadcast? Then -b\n");
310                                         exit(2);
311                                 }
312                                 fprintf(stderr, "WARNING: pinging broadcast address\n");
313                                 if (setsockopt(probe_fd, SOL_SOCKET, SO_BROADCAST,
314                                                &broadcast_pings, sizeof(broadcast_pings)) < 0) {
315                                         perror ("can't set broadcasting");
316                                         exit(2);
317                                 }
318                                 if (connect(probe_fd, (struct sockaddr*)&dst, sizeof(dst)) == -1) {
319                                         perror("connect");
320                                         exit(2);
321                                 }
322                         } else {
323                                 perror("connect");
324                                 exit(2);
325                         }
326                 }
327                 alen = sizeof(source);
328                 if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
329                         perror("getsockname");
330                         exit(2);
331                 }
332                 source.sin_port = 0;
333                 close(probe_fd);
334         } while (0);
335
336         if (whereto.sin_addr.s_addr == 0)
337                 whereto.sin_addr.s_addr = source.sin_addr.s_addr;
338
339         if (icmp_sock < 0) {
340                 errno = socket_errno;
341                 perror("ping: icmp open socket");
342                 exit(2);
343         }
344
345         if (device) {
346                 struct ifreq ifr;
347
348                 memset(&ifr, 0, sizeof(ifr));
349                 strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
350                 if (ioctl(icmp_sock, SIOCGIFINDEX, &ifr) < 0) {
351                         fprintf(stderr, "ping: unknown iface %s\n", device);
352                         exit(2);
353                 }
354                 cmsg.ipi.ipi_ifindex = ifr.ifr_ifindex;
355                 cmsg_len = sizeof(cmsg);
356         }
357
358         if (broadcast_pings || IN_MULTICAST(ntohl(whereto.sin_addr.s_addr))) {
359                 if (uid) {
360                         if (interval < 1000) {
361                                 fprintf(stderr, "ping: broadcast ping with too short interval.\n");
362                                 exit(2);
363                         }
364                         if (pmtudisc >= 0 && pmtudisc != IP_PMTUDISC_DO) {
365                                 fprintf(stderr, "ping: broadcast ping does not fragment.\n");
366                                 exit(2);
367                         }
368                 }
369                 if (pmtudisc < 0)
370                         pmtudisc = IP_PMTUDISC_DO;
371         }
372
373         if (pmtudisc >= 0) {
374                 if (setsockopt(icmp_sock, SOL_IP, IP_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
375                         perror("ping: IP_MTU_DISCOVER");
376                         exit(2);
377                 }
378         }
379
380         if ((options&F_STRICTSOURCE) &&
381             bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
382                 perror("bind");
383                 exit(2);
384         }
385
386         if (1) {
387                 struct icmp_filter filt;
388                 filt.data = ~((1<<ICMP_SOURCE_QUENCH)|
389                               (1<<ICMP_DEST_UNREACH)|
390                               (1<<ICMP_TIME_EXCEEDED)|
391                               (1<<ICMP_PARAMETERPROB)|
392                               (1<<ICMP_REDIRECT)|
393                               (1<<ICMP_ECHOREPLY));
394                 if (setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1)
395                         perror("WARNING: setsockopt(ICMP_FILTER)");
396         }
397
398         hold = 1;
399         if (setsockopt(icmp_sock, SOL_IP, IP_RECVERR, (char *)&hold, sizeof(hold)))
400                 fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
401
402         /* record route option */
403         if (options & F_RROUTE) {
404                 memset(rspace, 0, sizeof(rspace));
405                 rspace[0] = IPOPT_NOP;
406                 rspace[1+IPOPT_OPTVAL] = IPOPT_RR;
407                 rspace[1+IPOPT_OLEN] = sizeof(rspace)-1;
408                 rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF;
409                 optlen = 40;
410                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, sizeof(rspace)) < 0) {
411                         perror("ping: record route");
412                         exit(2);
413                 }
414         }
415         if (options & F_TIMESTAMP) {
416                 memset(rspace, 0, sizeof(rspace));
417                 rspace[0] = IPOPT_TIMESTAMP;
418                 rspace[1] = (ts_type==IPOPT_TS_TSONLY ? 40 : 36);
419                 rspace[2] = 5;
420                 rspace[3] = ts_type;
421                 if (ts_type == IPOPT_TS_PRESPEC) {
422                         int i;
423                         rspace[1] = 4+nroute*8;
424                         for (i=0; i<nroute; i++)
425                                 *(__u32*)&rspace[4+i*8] = route[i];
426                 }
427                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) {
428                         rspace[3] = 2;
429                         if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) {
430                                 perror("ping: ts option");
431                                 exit(2);
432                         }
433                 }
434                 optlen = 40;
435         }
436         if (options & F_SOURCEROUTE) {
437                 int i;
438                 memset(rspace, 0, sizeof(rspace));
439                 rspace[0] = IPOPT_NOOP;
440                 rspace[1+IPOPT_OPTVAL] = (options & F_SO_DONTROUTE) ? IPOPT_SSRR
441                         : IPOPT_LSRR;
442                 rspace[1+IPOPT_OLEN] = 3 + nroute*4;
443                 rspace[1+IPOPT_OFFSET] = IPOPT_MINOFF;
444                 for (i=0; i<nroute; i++)
445                         *(__u32*)&rspace[4+i*4] = route[i];
446
447                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_OPTIONS, rspace, 4 + nroute*4) < 0) {
448                         perror("ping: record route");
449                         exit(2);
450                 }
451                 optlen = 40;
452         }
453
454         /* Estimate memory eaten by single packet. It is rough estimate.
455          * Actually, for small datalen's it depends on kernel side a lot. */
456         hold = datalen + 8;
457         hold += ((hold+511)/512)*(optlen + 20 + 16 + 64 + 160);
458         sock_setbufs(icmp_sock, hold);
459
460         if (broadcast_pings) {
461                 if (setsockopt(icmp_sock, SOL_SOCKET, SO_BROADCAST,
462                                &broadcast_pings, sizeof(broadcast_pings)) < 0) {
463                         perror ("ping: can't set broadcasting");
464                         exit(2);
465                 }
466         }
467
468         if (options & F_NOLOOP) {
469                 int loop = 0;
470                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
471                                                         &loop, 1) == -1) {
472                         perror ("ping: can't disable multicast loopback");
473                         exit(2);
474                 }
475         }
476         if (options & F_TTL) {
477                 int ittl = ttl;
478                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_MULTICAST_TTL,
479                                                         &ttl, 1) == -1) {
480                         perror ("ping: can't set multicast time-to-live");
481                         exit(2);
482                 }
483                 if (setsockopt(icmp_sock, IPPROTO_IP, IP_TTL,
484                                                         &ittl, sizeof(ittl)) == -1) {
485                         perror ("ping: can't set unicast time-to-live");
486                         exit(2);
487                 }
488         }
489
490         if (datalen > 0xFFFF - 8 - optlen - 20) {
491                 if (uid || datalen > sizeof(outpack)-8) {
492                         fprintf(stderr, "Error: packet size %d is too large. Maximum is %d\n", datalen, 0xFFFF-8-20-optlen);
493                         exit(2);
494                 }
495                 /* Allow small oversize to root yet. It will cause EMSGSIZE. */
496                 fprintf(stderr, "WARNING: packet size %d is too large. Maximum is %d\n", datalen, 0xFFFF-8-20-optlen);
497         }
498
499         if (datalen >= sizeof(struct timeval))  /* can we time transfer */
500                 timing = 1;
501         packlen = datalen + MAXIPLEN + MAXICMPLEN;
502         if (!(packet = (u_char *)malloc((u_int)packlen))) {
503                 fprintf(stderr, "ping: out of memory.\n");
504                 exit(2);
505         }
506
507         printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr));
508         if (device || (options&F_STRICTSOURCE))
509                 printf("from %s %s: ", inet_ntoa(source.sin_addr), device ?: "");
510         printf("%d(%d) bytes of data.\n", datalen, datalen+8+optlen+20);
511
512         setup(icmp_sock);
513
514         main_loop(icmp_sock, packet, packlen);
515 }
516
517
518 int receive_error_msg()
519 {
520         int res;
521         char cbuf[512];
522         struct iovec  iov;
523         struct msghdr msg;
524         struct cmsghdr *cmsg;
525         struct sock_extended_err *e;
526         struct icmphdr icmph;
527         struct sockaddr_in target;
528         int net_errors = 0;
529         int local_errors = 0;
530         int saved_errno = errno;
531
532         iov.iov_base = &icmph;
533         iov.iov_len = sizeof(icmph);
534         msg.msg_name = (void*)&target;
535         msg.msg_namelen = sizeof(target);
536         msg.msg_iov = &iov;
537         msg.msg_iovlen = 1;
538         msg.msg_flags = 0;
539         msg.msg_control = cbuf;
540         msg.msg_controllen = sizeof(cbuf);
541
542         res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
543         if (res < 0)
544                 goto out;
545
546         e = NULL;
547         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
548                 if (cmsg->cmsg_level == SOL_IP) {
549                         if (cmsg->cmsg_type == IP_RECVERR)
550                                 e = (struct sock_extended_err *)CMSG_DATA(cmsg);
551                 }
552         }
553         if (e == NULL)
554                 abort();
555
556         if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
557                 local_errors++;
558                 if (options & F_QUIET)
559                         goto out;
560                 if (options & F_FLOOD)
561                         write(STDOUT_FILENO, "E", 1);
562                 else if (e->ee_errno != EMSGSIZE)
563                         fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
564                 else
565                         fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info);
566                 nerrors++;
567         } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
568                 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
569
570                 if (res < sizeof(icmph) ||
571                     target.sin_addr.s_addr != whereto.sin_addr.s_addr ||
572                     icmph.type != ICMP_ECHO ||
573                     icmph.un.echo.id != ident) {
574                         /* Not our error, not an error at all. Clear. */
575                         saved_errno = 0;
576                         goto out;
577                 }
578
579                 acknowledge(ntohs(icmph.un.echo.sequence));
580
581                 if (!working_recverr) {
582                         struct icmp_filter filt;
583                         working_recverr = 1;
584                         /* OK, it works. Add stronger filter. */
585                         filt.data = ~((1<<ICMP_SOURCE_QUENCH)|
586                                       (1<<ICMP_REDIRECT)|
587                                       (1<<ICMP_ECHOREPLY));
588                         if (setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1)
589                                 perror("\rWARNING: setsockopt(ICMP_FILTER)");
590                 }
591
592                 net_errors++;
593                 nerrors++;
594                 if (options & F_QUIET)
595                         goto out;
596                 if (options & F_FLOOD) {
597                         write(STDOUT_FILENO, "\bE", 2);
598                 } else {
599                         print_timestamp();
600                         printf("From %s icmp_seq=%u ", pr_addr(sin->sin_addr.s_addr), ntohs(icmph.un.echo.sequence));
601                         pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL);
602                         fflush(stdout);
603                 }
604         }
605
606 out:
607         errno = saved_errno;
608         return net_errors ? : -local_errors;
609 }
610
611 /*
612  * pinger --
613  *      Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
614  * will be added on by the kernel.  The ID field is our UNIX process ID,
615  * and the sequence number is an ascending integer.  The first 8 bytes
616  * of the data portion are used to hold a UNIX "timeval" struct in VAX
617  * byte-order, to compute the round-trip time.
618  */
619 int send_probe()
620 {
621         struct icmphdr *icp;
622         int cc;
623         int i;
624
625         icp = (struct icmphdr *)outpack;
626         icp->type = ICMP_ECHO;
627         icp->code = 0;
628         icp->checksum = 0;
629         icp->un.echo.sequence = htons(ntransmitted+1);
630         icp->un.echo.id = ident;                        /* ID */
631
632         CLR((ntransmitted+1) % mx_dup_ck);
633
634         if (timing) {
635                 if (options&F_LATENCY) {
636                         static volatile int fake_fucked_egcs = sizeof(struct timeval);
637                         struct timeval tmp_tv;
638                         gettimeofday(&tmp_tv, NULL);
639                         /* egcs is crap or glibc is crap, but memcpy
640                            does not copy anything, if len is constant! */
641                         memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
642                 } else {
643                         memset(icp+1, 0, sizeof(struct timeval));
644                 }
645         }
646
647         cc = datalen + 8;                       /* skips ICMP portion */
648
649         /* compute ICMP checksum here */
650         icp->checksum = in_cksum((u_short *)icp, cc, 0);
651
652         if (timing && !(options&F_LATENCY)) {
653                 static volatile int fake_fucked_egcs = sizeof(struct timeval);
654                 struct timeval tmp_tv;
655                 gettimeofday(&tmp_tv, NULL);
656                 /* egcs is crap or glibc is crap, but memcpy
657                    does not copy anything, if len is constant! */
658                 memcpy(icp+1, &tmp_tv, fake_fucked_egcs);
659                 icp->checksum = in_cksum((u_short *)(icp+1), fake_fucked_egcs, ~icp->checksum);
660         }
661
662         do {
663                 static struct iovec iov = {outpack, 0};
664                 static struct msghdr m = { &whereto, sizeof(whereto),
665                                                    &iov, 1, &cmsg, 0, 0 };
666                 m.msg_controllen = cmsg_len;
667                 iov.iov_len = cc;
668
669                 i = sendmsg(icmp_sock, &m, confirm);
670                 confirm = 0;
671         } while (0);
672
673         return (cc == i ? 0 : i);
674 }
675
676 /*
677  * parse_reply --
678  *      Print out the packet, if it came from us.  This logic is necessary
679  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
680  * which arrive ('tis only fair).  This permits multiple copies of this
681  * program to be run without having intermingled output (or statistics!).
682  */
683 void pr_echo_reply(__u8 *_icp, int len)
684 {
685         struct icmphdr *icp = (struct icmphdr *)_icp;
686         printf(" icmp_req=%u", ntohs(icp->un.echo.sequence));
687 }
688
689 int
690 parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
691 {
692         struct sockaddr_in *from = addr;
693         __u8 *buf = msg->msg_iov->iov_base;
694         struct icmphdr *icp;
695         struct iphdr *ip;
696         int hlen;
697         int csfailed;
698
699         /* Check the IP header */
700         ip = (struct iphdr *)buf;
701         hlen = ip->ihl*4;
702         if (cc < hlen + 8 || ip->ihl < 5) {
703                 if (options & F_VERBOSE)
704                         fprintf(stderr, "ping: packet too short (%d bytes) from %s\n", cc,
705                                 pr_addr(from->sin_addr.s_addr));
706                 return 1;
707         }
708
709         /* Now the ICMP part */
710         cc -= hlen;
711         icp = (struct icmphdr *)(buf + hlen);
712         csfailed = in_cksum((u_short *)icp, cc, 0);
713
714         if (icp->type == ICMP_ECHOREPLY) {
715                 if (icp->un.echo.id != ident)
716                         return 1;                       /* 'Twas not our ECHO */
717                 if (gather_statistics((__u8*)icp, sizeof(*icp), cc,
718                                       ntohs(icp->un.echo.sequence),
719                                       ip->ttl, 0, tv, pr_addr(from->sin_addr.s_addr),
720                                       pr_echo_reply))
721                         return 0;
722         } else {
723                 /* We fall here when a redirect or source quench arrived.
724                  * Also this branch processes icmp errors, when IP_RECVERR
725                  * is broken. */
726
727                 switch (icp->type) {
728                 case ICMP_ECHO:
729                         /* MUST NOT */
730                         return 1;
731                 case ICMP_SOURCE_QUENCH:
732                 case ICMP_REDIRECT:
733                 case ICMP_DEST_UNREACH:
734                 case ICMP_TIME_EXCEEDED:
735                 case ICMP_PARAMETERPROB:
736                         {
737                                 struct iphdr * iph = (struct  iphdr *)(&icp[1]);
738                                 struct icmphdr *icp1 = (struct icmphdr*)((unsigned char *)iph + iph->ihl*4);
739                                 int error_pkt;
740                                 if (cc < 8+sizeof(struct iphdr)+8 ||
741                                     cc < 8+iph->ihl*4+8)
742                                         return 1;
743                                 if (icp1->type != ICMP_ECHO ||
744                                     iph->daddr != whereto.sin_addr.s_addr ||
745                                     icp1->un.echo.id != ident)
746                                         return 1;
747                                 error_pkt = (icp->type != ICMP_REDIRECT &&
748                                              icp->type != ICMP_SOURCE_QUENCH);
749                                 if (error_pkt) {
750                                         acknowledge(ntohs(icp1->un.echo.sequence));
751                                         if (working_recverr) {
752                                                 return 0;
753                                         } else {
754                                                 static int once;
755                                                 /* Sigh, IP_RECVERR for raw socket
756                                                  * was broken until 2.4.9. So, we ignore
757                                                  * the first error and warn on the second.
758                                                  */
759                                                 if (once++ == 1)
760                                                         fprintf(stderr, "\rWARNING: kernel is not very fresh, upgrade is recommended.\n");
761                                                 if (once == 1)
762                                                         return 0;
763                                         }
764                                 }
765                                 nerrors+=error_pkt;
766                                 if (options&F_QUIET)
767                                         return !error_pkt;
768                                 if (options & F_FLOOD) {
769                                         if (error_pkt)
770                                                 write(STDOUT_FILENO, "\bE", 2);
771                                         return !error_pkt;
772                                 }
773                                 print_timestamp();
774                                 printf("From %s: icmp_seq=%u ",
775                                        pr_addr(from->sin_addr.s_addr),
776                                        ntohs(icp1->un.echo.sequence));
777                                 if (csfailed)
778                                         printf("(BAD CHECKSUM)");
779                                 pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp);
780                                 return !error_pkt;
781                         }
782                 default:
783                         /* MUST NOT */
784                         break;
785                 }
786                 if ((options & F_FLOOD) && !(options & (F_VERBOSE|F_QUIET))) {
787                         if (!csfailed)
788                                 write(STDOUT_FILENO, "!E", 2);
789                         else
790                                 write(STDOUT_FILENO, "!EC", 3);
791                         return 0;
792                 }
793                 if (!(options & F_VERBOSE) || uid)
794                         return 0;
795                 if (options & F_PTIMEOFDAY) {
796                         struct timeval recv_time;
797                         gettimeofday(&recv_time, NULL);
798                         printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec);
799                 }
800                 printf("From %s: ", pr_addr(from->sin_addr.s_addr));
801                 if (csfailed) {
802                         printf("(BAD CHECKSUM)\n");
803                         return 0;
804                 }
805                 pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp);
806                 return 0;
807         }
808
809         if (!(options & F_FLOOD)) {
810                 pr_options(buf + sizeof(struct iphdr), hlen);
811
812                 if (options & F_AUDIBLE)
813                         putchar('\a');
814                 putchar('\n');
815                 fflush(stdout);
816         } else {
817                 putchar('\a');
818                 fflush(stdout);
819         }
820         return 0;
821 }
822
823 u_short
824 in_cksum(const u_short *addr, register int len, u_short csum)
825 {
826         register int nleft = len;
827         const u_short *w = addr;
828         register u_short answer;
829         register int sum = csum;
830
831         /*
832          *  Our algorithm is simple, using a 32 bit accumulator (sum),
833          *  we add sequential 16 bit words to it, and at the end, fold
834          *  back all the carry bits from the top 16 bits into the lower
835          *  16 bits.
836          */
837         while (nleft > 1)  {
838                 sum += *w++;
839                 nleft -= 2;
840         }
841
842         /* mop up an odd byte, if necessary */
843         if (nleft == 1)
844                 sum += htons(*(u_char *)w << 8);
845
846         /*
847          * add back carry outs from top 16 bits to low 16 bits
848          */
849         sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
850         sum += (sum >> 16);                     /* add carry */
851         answer = ~sum;                          /* truncate to 16 bits */
852         return (answer);
853 }
854
855 /*
856  * pr_icmph --
857  *      Print a descriptive string about an ICMP header.
858  */
859 void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp)
860 {
861         switch(type) {
862         case ICMP_ECHOREPLY:
863                 printf("Echo Reply\n");
864                 /* XXX ID + Seq + Data */
865                 break;
866         case ICMP_DEST_UNREACH:
867                 switch(code) {
868                 case ICMP_NET_UNREACH:
869                         printf("Destination Net Unreachable\n");
870                         break;
871                 case ICMP_HOST_UNREACH:
872                         printf("Destination Host Unreachable\n");
873                         break;
874                 case ICMP_PROT_UNREACH:
875                         printf("Destination Protocol Unreachable\n");
876                         break;
877                 case ICMP_PORT_UNREACH:
878                         printf("Destination Port Unreachable\n");
879                         break;
880                 case ICMP_FRAG_NEEDED:
881                         printf("Frag needed and DF set (mtu = %u)\n", info);
882                         break;
883                 case ICMP_SR_FAILED:
884                         printf("Source Route Failed\n");
885                         break;
886                 case ICMP_PKT_FILTERED:
887                         printf("Packet filtered\n");
888                         break;
889                 default:
890                         printf("Dest Unreachable, Bad Code: %d\n", code);
891                         break;
892                 }
893                 if (icp && (options & F_VERBOSE))
894                         pr_iph((struct iphdr*)(icp + 1));
895                 break;
896         case ICMP_SOURCE_QUENCH:
897                 printf("Source Quench\n");
898                 if (icp && (options & F_VERBOSE))
899                         pr_iph((struct iphdr*)(icp + 1));
900                 break;
901         case ICMP_REDIRECT:
902                 switch(code) {
903                 case ICMP_REDIR_NET:
904                         printf("Redirect Network");
905                         break;
906                 case ICMP_REDIR_HOST:
907                         printf("Redirect Host");
908                         break;
909                 case ICMP_REDIR_NETTOS:
910                         printf("Redirect Type of Service and Network");
911                         break;
912                 case ICMP_REDIR_HOSTTOS:
913                         printf("Redirect Type of Service and Host");
914                         break;
915                 default:
916                         printf("Redirect, Bad Code: %d", code);
917                         break;
918                 }
919                 if (icp)
920                         printf("(New nexthop: %s)\n", pr_addr(icp->un.gateway));
921                 if (icp && (options & F_VERBOSE))
922                         pr_iph((struct iphdr*)(icp + 1));
923                 break;
924         case ICMP_ECHO:
925                 printf("Echo Request\n");
926                 /* XXX ID + Seq + Data */
927                 break;
928         case ICMP_TIME_EXCEEDED:
929                 switch(code) {
930                 case ICMP_EXC_TTL:
931                         printf("Time to live exceeded\n");
932                         break;
933                 case ICMP_EXC_FRAGTIME:
934                         printf("Frag reassembly time exceeded\n");
935                         break;
936                 default:
937                         printf("Time exceeded, Bad Code: %d\n", code);
938                         break;
939                 }
940                 if (icp && (options & F_VERBOSE))
941                         pr_iph((struct iphdr*)(icp + 1));
942                 break;
943         case ICMP_PARAMETERPROB:
944                 printf("Parameter problem: pointer = %u\n", icp ? (ntohl(icp->un.gateway)>>24) : info);
945                 if (icp && (options & F_VERBOSE))
946                         pr_iph((struct iphdr*)(icp + 1));
947                 break;
948         case ICMP_TIMESTAMP:
949                 printf("Timestamp\n");
950                 /* XXX ID + Seq + 3 timestamps */
951                 break;
952         case ICMP_TIMESTAMPREPLY:
953                 printf("Timestamp Reply\n");
954                 /* XXX ID + Seq + 3 timestamps */
955                 break;
956         case ICMP_INFO_REQUEST:
957                 printf("Information Request\n");
958                 /* XXX ID + Seq */
959                 break;
960         case ICMP_INFO_REPLY:
961                 printf("Information Reply\n");
962                 /* XXX ID + Seq */
963                 break;
964 #ifdef ICMP_MASKREQ
965         case ICMP_MASKREQ:
966                 printf("Address Mask Request\n");
967                 break;
968 #endif
969 #ifdef ICMP_MASKREPLY
970         case ICMP_MASKREPLY:
971                 printf("Address Mask Reply\n");
972                 break;
973 #endif
974         default:
975                 printf("Bad ICMP type: %d\n", type);
976         }
977 }
978
979 void pr_options(unsigned char * cp, int hlen)
980 {
981         int i, j;
982         int optlen, totlen;
983         unsigned char * optptr;
984         static int old_rrlen;
985         static char old_rr[MAX_IPOPTLEN];
986
987         totlen = hlen-sizeof(struct iphdr);
988         optptr = cp;
989
990         while (totlen > 0) {
991                 if (*optptr == IPOPT_EOL)
992                         break;
993                 if (*optptr == IPOPT_NOP) {
994                         totlen--;
995                         optptr++;
996                         printf("\nNOP");
997                         continue;
998                 }
999                 cp = optptr;
1000                 optlen = optptr[1];
1001                 if (optlen < 2 || optlen > totlen)
1002                         break;
1003
1004                 switch (*cp) {
1005                 case IPOPT_SSRR:
1006                 case IPOPT_LSRR:
1007                         printf("\n%cSRR: ", *cp==IPOPT_SSRR ? 'S' : 'L');
1008                         j = *++cp;
1009                         i = *++cp;
1010                         i -= 4;
1011                         cp++;
1012                         if (j > IPOPT_MINOFF) {
1013                                 for (;;) {
1014                                         __u32 address;
1015                                         memcpy(&address, cp, 4);
1016                                         cp += 4;
1017                                         if (address == 0)
1018                                                 printf("\t0.0.0.0");
1019                                         else
1020                                                 printf("\t%s", pr_addr(address));
1021                                         j -= 4;
1022                                         putchar('\n');
1023                                         if (j <= IPOPT_MINOFF)
1024                                                 break;
1025                                 }
1026                         }
1027                         break;
1028                 case IPOPT_RR:
1029                         j = *++cp;              /* get length */
1030                         i = *++cp;              /* and pointer */
1031                         if (i > j)
1032                                 i = j;
1033                         i -= IPOPT_MINOFF;
1034                         if (i <= 0)
1035                                 break;
1036                         if (i == old_rrlen
1037                             && !strncmp((char *)cp, old_rr, i)
1038                             && !(options & F_FLOOD)) {
1039                                 printf("\t(same route)");
1040                                 i = ((i + 3) / 4) * 4;
1041                                 cp += i;
1042                                 break;
1043                         }
1044                         old_rrlen = i;
1045                         memcpy(old_rr, (char *)cp, i);
1046                         printf("\nRR: ");
1047                         cp++;
1048                         for (;;) {
1049                                 __u32 address;
1050                                 memcpy(&address, cp, 4);
1051                                 cp += 4;
1052                                 if (address == 0)
1053                                         printf("\t0.0.0.0");
1054                                 else
1055                                         printf("\t%s", pr_addr(address));
1056                                 i -= 4;
1057                                 putchar('\n');
1058                                 if (i <= 0)
1059                                         break;
1060                         }
1061                         break;
1062                 case IPOPT_TS:
1063                 {
1064                         int stdtime = 0, nonstdtime = 0;
1065                         __u8 flags;
1066                         j = *++cp;              /* get length */
1067                         i = *++cp;              /* and pointer */
1068                         if (i > j)
1069                                 i = j;
1070                         i -= 5;
1071                         if (i <= 0)
1072                                 break;
1073                         flags = *++cp;
1074                         printf("\nTS: ");
1075                         cp++;
1076                         for (;;) {
1077                                 long l;
1078
1079                                 if ((flags&0xF) != IPOPT_TS_TSONLY) {
1080                                         __u32 address;
1081                                         memcpy(&address, cp, 4);
1082                                         cp += 4;
1083                                         if (address == 0)
1084                                                 printf("\t0.0.0.0");
1085                                         else
1086                                                 printf("\t%s", pr_addr(address));
1087                                         i -= 4;
1088                                         if (i <= 0)
1089                                                 break;
1090                                 }
1091                                 l = *cp++;
1092                                 l = (l<<8) + *cp++;
1093                                 l = (l<<8) + *cp++;
1094                                 l = (l<<8) + *cp++;
1095
1096                                 if  (l & 0x80000000) {
1097                                         if (nonstdtime==0)
1098                                                 printf("\t%ld absolute not-standard", l&0x7fffffff);
1099                                         else
1100                                                 printf("\t%ld not-standard", (l&0x7fffffff) - nonstdtime);
1101                                         nonstdtime = l&0x7fffffff;
1102                                 } else {
1103                                         if (stdtime==0)
1104                                                 printf("\t%ld absolute", l);
1105                                         else
1106                                                 printf("\t%ld", l - stdtime);
1107                                         stdtime = l;
1108                                 }
1109                                 i -= 4;
1110                                 putchar('\n');
1111                                 if (i <= 0)
1112                                         break;
1113                         }
1114                         if (flags>>4)
1115                                 printf("Unrecorded hops: %d\n", flags>>4);
1116                         break;
1117                 }
1118                 default:
1119                         printf("\nunknown option %x", *cp);
1120                         break;
1121                 }
1122                 totlen -= optlen;
1123                 optptr += optlen;
1124         }
1125 }
1126
1127
1128 /*
1129  * pr_iph --
1130  *      Print an IP header with options.
1131  */
1132 void pr_iph(struct iphdr *ip)
1133 {
1134         int hlen;
1135         u_char *cp;
1136
1137         hlen = ip->ihl << 2;
1138         cp = (u_char *)ip + 20;         /* point to options */
1139
1140         printf("Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst Data\n");
1141         printf(" %1x  %1x  %02x %04x %04x",
1142                ip->version, ip->ihl, ip->tos, ip->tot_len, ip->id);
1143         printf("   %1x %04x", ((ip->frag_off) & 0xe000) >> 13,
1144                (ip->frag_off) & 0x1fff);
1145         printf("  %02x  %02x %04x", ip->ttl, ip->protocol, ip->check);
1146         printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->saddr));
1147         printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->daddr));
1148         printf("\n");
1149         pr_options(cp, hlen);
1150 }
1151
1152 /*
1153  * pr_addr --
1154  *      Return an ascii host address as a dotted quad and optionally with
1155  * a hostname.
1156  */
1157 char *
1158 pr_addr(__u32 addr)
1159 {
1160         struct hostent *hp;
1161         static char buf[4096];
1162
1163         if ((options & F_NUMERIC) ||
1164             !(hp = gethostbyaddr((char *)&addr, 4, AF_INET)))
1165                 sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
1166         else
1167                 snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
1168                          inet_ntoa(*(struct in_addr *)&addr));
1169         return(buf);
1170 }
1171
1172
1173 /* Set Type of Service (TOS) and other Quality of Service relating bits */
1174 int parsetos(char *str)
1175 {
1176         const char *cp;
1177         int tos;
1178         char *ep;
1179
1180         /* handle both hex and decimal values */
1181         if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
1182                 cp = str + 2;
1183                 tos = (int)strtol(cp, &ep, 16);
1184         } else
1185                 tos = (int)strtol(str, &ep, 10);
1186
1187         /* doesn't look like decimal or hex, eh? */
1188         if (*ep != '\0') {
1189                 fprintf(stderr, "ping: \"%s\" bad value for TOS\n", str);
1190                 exit(2);
1191         }
1192
1193         if (tos > TOS_MAX) {
1194                 fprintf(stderr, "ping: the decimal value of TOS bits must be 0-254 (or zero)\n");
1195                 exit(2);
1196         }
1197         return(tos);
1198 }
1199
1200 #include <linux/filter.h>
1201
1202 void install_filter(void)
1203 {
1204         static int once;
1205         static struct sock_filter insns[] = {
1206                 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping6. */
1207                 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 4), /* Load icmp echo ident */
1208                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */
1209                 BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */
1210                 BPF_STMT(BPF_LD|BPF_B|BPF_IND, 0), /* Load icmp type */
1211                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
1212                 BPF_STMT(BPF_RET|BPF_K, 0xFFFFFFF), /* No. It passes. */
1213                 BPF_STMT(BPF_RET|BPF_K, 0) /* Echo with wrong ident. Reject. */
1214         };
1215         static struct sock_fprog filter = {
1216                 sizeof insns / sizeof(insns[0]),
1217                 insns
1218         };
1219
1220         if (once)
1221                 return;
1222         once = 1;
1223
1224         /* Patch bpflet for current identifier. */
1225         insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1);
1226
1227         if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
1228                 perror("WARNING: failed to install socket filter\n");
1229 }
1230
1231
1232 void usage(void)
1233 {
1234         fprintf(stderr,
1235 "Usage: ping [-LRUbdfnqrvVaAD] [-c count] [-i interval] [-w deadline]\n"
1236 "            [-p pattern] [-s packetsize] [-t ttl] [-I interface]\n"
1237 "            [-M pmtudisc-hint] [-m mark] [-S sndbuf]\n"
1238 "            [-T tstamp-options] [-Q tos] [hop1 ...] destination\n");
1239         exit(2);
1240 }