Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/networking/ping.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/networking/ping.c
r821 r1770 1 1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $4 3 * Mini ping implementation for busybox 5 4 * … … 8 7 * Adapted from the ping in netkit-base 0.10: 9 8 * Copyright (c) 1989 The Regents of the University of California. 10 * Derived from software contributed to Berkeley by Mike Muuss. 9 * All rights reserved. 10 * 11 * This code is derived from software contributed to Berkeley by 12 * Mike Muuss. 11 13 * 12 14 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 13 15 */ 14 15 #include <sys/param.h> 16 #include <sys/socket.h> 17 #include <sys/file.h> 18 #include <sys/times.h> 19 #include <signal.h> 20 21 #include <netinet/in.h> 22 #include <netinet/ip.h> 16 /* from ping6.c: 17 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> 18 * 19 * This version of ping is adapted from the ping in netkit-base 0.10, 20 * which is: 21 * 22 * Original copyright notice is retained at the end of this file. 23 * 24 * This version is an adaptation of ping.c from busybox. 25 * The code was modified by Bart Visscher <magick@linux-fan.com> 26 */ 27 28 #include <net/if.h> 23 29 #include <netinet/ip_icmp.h> 24 #include <arpa/inet.h> 25 #include <netdb.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <errno.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include "busybox.h" 33 30 #include "libbb.h" 31 32 #if ENABLE_PING6 33 #include <netinet/icmp6.h> 34 /* I see RENUMBERED constants in bits/in.h - !!? 35 * What a fuck is going on with libc? Is it a glibc joke? */ 36 #ifdef IPV6_2292HOPLIMIT 37 #undef IPV6_HOPLIMIT 38 #define IPV6_HOPLIMIT IPV6_2292HOPLIMIT 39 #endif 40 #endif 34 41 35 42 enum { … … 40 47 MAX_DUP_CHK = (8 * 128), 41 48 MAXWAIT = 10, 42 PINGINTERVAL = 1 /*second */49 PINGINTERVAL = 1, /* 1 second */ 43 50 }; 44 51 45 #define O_QUIET (1 << 0) 52 /* common routines */ 53 54 static int in_cksum(unsigned short *buf, int sz) 55 { 56 int nleft = sz; 57 int sum = 0; 58 unsigned short *w = buf; 59 unsigned short ans = 0; 60 61 while (nleft > 1) { 62 sum += *w++; 63 nleft -= 2; 64 } 65 66 if (nleft == 1) { 67 *(unsigned char *) (&ans) = *(unsigned char *) w; 68 sum += ans; 69 } 70 71 sum = (sum >> 16) + (sum & 0xFFFF); 72 sum += (sum >> 16); 73 ans = ~sum; 74 return ans; 75 } 76 77 #if !ENABLE_FEATURE_FANCY_PING 78 79 /* simple version */ 80 81 static char *hostname; 82 83 static void noresp(int ign ATTRIBUTE_UNUSED) 84 { 85 printf("No response from %s\n", hostname); 86 exit(EXIT_FAILURE); 87 } 88 89 static void ping4(len_and_sockaddr *lsa) 90 { 91 struct sockaddr_in pingaddr; 92 struct icmp *pkt; 93 int pingsock, c; 94 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; 95 96 pingsock = create_icmp_socket(); 97 pingaddr = lsa->sin; 98 99 pkt = (struct icmp *) packet; 100 memset(pkt, 0, sizeof(packet)); 101 pkt->icmp_type = ICMP_ECHO; 102 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); 103 104 c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 105 (struct sockaddr *) &pingaddr, sizeof(pingaddr)); 106 107 /* listen for replies */ 108 while (1) { 109 struct sockaddr_in from; 110 socklen_t fromlen = sizeof(from); 111 112 c = recvfrom(pingsock, packet, sizeof(packet), 0, 113 (struct sockaddr *) &from, &fromlen); 114 if (c < 0) { 115 if (errno != EINTR) 116 bb_perror_msg("recvfrom"); 117 continue; 118 } 119 if (c >= 76) { /* ip + icmp */ 120 struct iphdr *iphdr = (struct iphdr *) packet; 121 122 pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */ 123 if (pkt->icmp_type == ICMP_ECHOREPLY) 124 break; 125 } 126 } 127 if (ENABLE_FEATURE_CLEAN_UP) 128 close(pingsock); 129 } 130 131 #if ENABLE_PING6 132 static void ping6(len_and_sockaddr *lsa) 133 { 134 struct sockaddr_in6 pingaddr; 135 struct icmp6_hdr *pkt; 136 int pingsock, c; 137 int sockopt; 138 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; 139 140 pingsock = create_icmp6_socket(); 141 pingaddr = lsa->sin6; 142 143 pkt = (struct icmp6_hdr *) packet; 144 memset(pkt, 0, sizeof(packet)); 145 pkt->icmp6_type = ICMP6_ECHO_REQUEST; 146 147 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); 148 setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt)); 149 150 c = xsendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), 151 (struct sockaddr *) &pingaddr, sizeof(pingaddr)); 152 153 /* listen for replies */ 154 while (1) { 155 struct sockaddr_in6 from; 156 socklen_t fromlen = sizeof(from); 157 158 c = recvfrom(pingsock, packet, sizeof(packet), 0, 159 (struct sockaddr *) &from, &fromlen); 160 if (c < 0) { 161 if (errno != EINTR) 162 bb_perror_msg("recvfrom"); 163 continue; 164 } 165 if (c >= 8) { /* icmp6_hdr */ 166 pkt = (struct icmp6_hdr *) packet; 167 if (pkt->icmp6_type == ICMP6_ECHO_REPLY) 168 break; 169 } 170 } 171 if (ENABLE_FEATURE_CLEAN_UP) 172 close(pingsock); 173 } 174 #endif 175 176 int ping_main(int argc, char **argv); 177 int ping_main(int argc, char **argv) 178 { 179 len_and_sockaddr *lsa; 180 #if ENABLE_PING6 181 sa_family_t af = AF_UNSPEC; 182 183 while ((++argv)[0] && argv[0][0] == '-') { 184 if (argv[0][1] == '4') { 185 af = AF_INET; 186 continue; 187 } 188 if (argv[0][1] == '6') { 189 af = AF_INET6; 190 continue; 191 } 192 bb_show_usage(); 193 } 194 #else 195 argv++; 196 #endif 197 198 hostname = *argv; 199 if (!hostname) 200 bb_show_usage(); 201 202 #if ENABLE_PING6 203 lsa = xhost_and_af2sockaddr(hostname, 0, af); 204 #else 205 lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET); 206 #endif 207 /* Set timer _after_ DNS resolution */ 208 signal(SIGALRM, noresp); 209 alarm(5); /* give the host 5000ms to respond */ 210 211 #if ENABLE_PING6 212 if (lsa->sa.sa_family == AF_INET6) 213 ping6(lsa); 214 else 215 #endif 216 ping4(lsa); 217 printf("%s is alive!\n", hostname); 218 return EXIT_SUCCESS; 219 } 220 221 222 #else /* FEATURE_FANCY_PING */ 223 224 225 /* full(er) version */ 226 227 #define OPT_STRING ("qvc:s:I:4" USE_PING6("6")) 228 enum { 229 OPT_QUIET = 1 << 0, 230 OPT_VERBOSE = 1 << 1, 231 OPT_c = 1 << 2, 232 OPT_s = 1 << 3, 233 OPT_I = 1 << 4, 234 OPT_IPV4 = 1 << 5, 235 OPT_IPV6 = (1 << 6) * ENABLE_PING6, 236 }; 237 238 239 struct globals { 240 int pingsock; 241 len_and_sockaddr *source_lsa; 242 unsigned datalen; 243 int if_index; 244 unsigned long ntransmitted, nreceived, nrepeats, pingcount; 245 uint16_t myid; 246 unsigned tmin, tmax; /* in us */ 247 unsigned long long tsum; /* in us, sum of all times */ 248 const char *hostname; 249 const char *dotted; 250 union { 251 struct sockaddr sa; 252 struct sockaddr_in sin; 253 #if ENABLE_PING6 254 struct sockaddr_in6 sin6; 255 #endif 256 } pingaddr; 257 char rcvd_tbl[MAX_DUP_CHK / 8]; 258 }; 259 #define G (*(struct globals*)&bb_common_bufsiz1) 260 #define pingsock (G.pingsock ) 261 #define source_lsa (G.source_lsa ) 262 #define datalen (G.datalen ) 263 #define if_index (G.if_index ) 264 #define ntransmitted (G.ntransmitted) 265 #define nreceived (G.nreceived ) 266 #define nrepeats (G.nrepeats ) 267 #define pingcount (G.pingcount ) 268 #define myid (G.myid ) 269 #define tmin (G.tmin ) 270 #define tmax (G.tmax ) 271 #define tsum (G.tsum ) 272 #define hostname (G.hostname ) 273 #define dotted (G.dotted ) 274 #define pingaddr (G.pingaddr ) 275 #define rcvd_tbl (G.rcvd_tbl ) 276 void BUG_ping_globals_too_big(void); 277 #define INIT_G() do { \ 278 if (sizeof(G) > COMMON_BUFSIZE) \ 279 BUG_ping_globals_too_big(); \ 280 pingsock = -1; \ 281 tmin = UINT_MAX; \ 282 } while (0) 283 46 284 47 285 #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ … … 51 289 #define TST(bit) (A(bit) & B(bit)) 52 290 53 static void ping(const char *host);54 55 /* common routines */56 static int in_cksum(unsigned short *buf, int sz)57 {58 int nleft = sz;59 int sum = 0;60 unsigned short *w = buf;61 unsigned short ans = 0;62 63 while (nleft > 1) {64 sum += *w++;65 nleft -= 2;66 }67 68 if (nleft == 1) {69 *(unsigned char *) (&ans) = *(unsigned char *) w;70 sum += ans;71 }72 73 sum = (sum >> 16) + (sum & 0xFFFF);74 sum += (sum >> 16);75 ans = ~sum;76 return (ans);77 }78 79 /* simple version */80 #ifndef CONFIG_FEATURE_FANCY_PING81 static char *hostname = NULL;82 static void noresp(int ign)83 {84 printf("No response from %s\n", hostname);85 exit(EXIT_FAILURE);86 }87 88 static void ping(const char *host)89 {90 struct hostent *h;91 struct sockaddr_in pingaddr;92 struct icmp *pkt;93 int pingsock, c;94 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];95 96 pingsock = create_icmp_socket();97 98 memset(&pingaddr, 0, sizeof(struct sockaddr_in));99 100 pingaddr.sin_family = AF_INET;101 h = xgethostbyname(host);102 memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));103 hostname = h->h_name;104 105 pkt = (struct icmp *) packet;106 memset(pkt, 0, sizeof(packet));107 pkt->icmp_type = ICMP_ECHO;108 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));109 110 c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,111 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));112 113 if (c < 0) {114 if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);115 bb_perror_msg_and_die("sendto");116 }117 118 signal(SIGALRM, noresp);119 alarm(5); /* give the host 5000ms to respond */120 /* listen for replies */121 while (1) {122 struct sockaddr_in from;123 socklen_t fromlen = sizeof(from);124 125 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,126 (struct sockaddr *) &from, &fromlen)) < 0) {127 if (errno == EINTR)128 continue;129 bb_perror_msg("recvfrom");130 continue;131 }132 if (c >= 76) { /* ip + icmp */133 struct iphdr *iphdr = (struct iphdr *) packet;134 135 pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */136 if (pkt->icmp_type == ICMP_ECHOREPLY)137 break;138 }139 }140 if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);141 printf("%s is alive!\n", hostname);142 return;143 }144 145 int ping_main(int argc, char **argv)146 {147 argc--;148 argv++;149 if (argc < 1)150 bb_show_usage();151 ping(*argv);152 return EXIT_SUCCESS;153 }154 155 #else /* ! CONFIG_FEATURE_FANCY_PING */156 /* full(er) version */157 static struct sockaddr_in pingaddr;158 static int pingsock = -1;159 static int datalen; /* intentionally uninitialized to work around gcc bug */160 161 static long ntransmitted, nreceived, nrepeats, pingcount;162 static int myid, options;163 static unsigned long tmin = ULONG_MAX, tmax, tsum;164 static char rcvd_tbl[MAX_DUP_CHK / 8];165 166 #ifndef CONFIG_FEATURE_FANCY_PING6167 static168 #endif169 struct hostent *hostent;170 171 static void sendping(int);172 static void pingstats(int);173 static void unpack(char *, int, struct sockaddr_in *);174 175 291 /**************************************************************************/ 176 292 177 static void pingstats(int junk) 178 { 179 int status; 180 293 static void pingstats(int junk ATTRIBUTE_UNUSED) 294 { 181 295 signal(SIGINT, SIG_IGN); 182 296 183 printf("\n--- %s ping statistics ---\n", host ent->h_name);184 printf("%l dpackets transmitted, ", ntransmitted);185 printf("%l dpackets received, ", nreceived);297 printf("\n--- %s ping statistics ---\n", hostname); 298 printf("%lu packets transmitted, ", ntransmitted); 299 printf("%lu packets received, ", nreceived); 186 300 if (nrepeats) 187 printf("%l dduplicates, ", nrepeats);301 printf("%lu duplicates, ", nrepeats); 188 302 if (ntransmitted) 189 printf("%ld%% packet loss\n", 190 (ntransmitted - nreceived) * 100 / ntransmitted); 191 if (nreceived) 192 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", 193 tmin / 10, tmin % 10, 194 (tsum / (nreceived + nrepeats)) / 10, 195 (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); 196 if (nreceived != 0) 197 status = EXIT_SUCCESS; 198 else 199 status = EXIT_FAILURE; 200 exit(status); 201 } 202 203 static void sendping(int junk) 204 { 205 struct icmp *pkt; 206 int i; 207 char packet[datalen + ICMP_MINLEN]; 208 209 pkt = (struct icmp *) packet; 303 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted; 304 printf("%lu%% packet loss\n", ntransmitted); 305 if (tmin != UINT_MAX) { 306 unsigned tavg = tsum / (nreceived + nrepeats); 307 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n", 308 tmin / 1000, tmin % 1000, 309 tavg / 1000, tavg % 1000, 310 tmax / 1000, tmax % 1000); 311 } 312 exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */ 313 } 314 315 static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) 316 { 317 int sz; 318 319 CLR((uint16_t)ntransmitted % MAX_DUP_CHK); 320 ntransmitted++; 321 322 /* sizeof(pingaddr) can be larger than real sa size, but I think 323 * it doesn't matter */ 324 sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr)); 325 if (sz != size_pkt) 326 bb_error_msg_and_die(bb_msg_write_error); 327 328 signal(SIGALRM, sp); 329 if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ 330 alarm(PINGINTERVAL); 331 } else { /* done, wait for the last ping to come back */ 332 /* todo, don't necessarily need to wait so long... */ 333 signal(SIGALRM, pingstats); 334 alarm(MAXWAIT); 335 } 336 } 337 338 static void sendping4(int junk ATTRIBUTE_UNUSED) 339 { 340 /* +4 reserves a place for timestamp, which may end up sitting 341 * *after* packet. Saves one if() */ 342 struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4); 210 343 211 344 pkt->icmp_type = ICMP_ECHO; 212 345 pkt->icmp_code = 0; 213 346 pkt->icmp_cksum = 0; 214 pkt->icmp_seq = htons(ntransmitted ++);347 pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ 215 348 pkt->icmp_id = myid; 216 CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK); 217 218 gettimeofday((struct timeval *) &pkt->icmp_dun, NULL); 219 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); 220 221 i = sendto(pingsock, packet, sizeof(packet), 0, 222 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); 223 224 if (i < 0) 225 bb_perror_msg_and_die("sendto"); 226 else if ((size_t)i != sizeof(packet)) 227 bb_error_msg_and_die("ping wrote %d chars; %d expected", i, 228 (int)sizeof(packet)); 229 230 signal(SIGALRM, sendping); 231 if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ 232 alarm(PINGINTERVAL); 233 } else { /* done, wait for the last ping to come back */ 234 /* todo, don't necessarily need to wait so long... */ 235 signal(SIGALRM, pingstats); 236 alarm(MAXWAIT); 237 } 238 } 239 240 static char *icmp_type_name (int id) 349 350 /* We don't do hton, because we will read it back on the same machine */ 351 /*if (datalen >= 4)*/ 352 *(uint32_t*)&pkt->icmp_dun = monotonic_us(); 353 354 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN); 355 356 sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN); 357 } 358 #if ENABLE_PING6 359 static void sendping6(int junk ATTRIBUTE_UNUSED) 360 { 361 struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4); 362 363 pkt->icmp6_type = ICMP6_ECHO_REQUEST; 364 pkt->icmp6_code = 0; 365 pkt->icmp6_cksum = 0; 366 pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ 367 pkt->icmp6_id = myid; 368 369 /*if (datalen >= 4)*/ 370 *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us(); 371 372 sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr)); 373 } 374 #endif 375 376 static const char *icmp_type_name(int id) 241 377 { 242 378 switch (id) { 243 case ICMP_ECHOREPLY: return "Echo Reply"; 244 case ICMP_DEST_UNREACH: return "Destination Unreachable"; 245 case ICMP_SOURCE_QUENCH: return "Source Quench"; 246 case ICMP_REDIRECT: return "Redirect (change route)"; 247 case ICMP_ECHO: return "Echo Request"; 248 case ICMP_TIME_EXCEEDED: return "Time Exceeded"; 249 case ICMP_PARAMETERPROB: return "Parameter Problem"; 250 case ICMP_TIMESTAMP: return "Timestamp Request"; 251 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; 252 case ICMP_INFO_REQUEST: return "Information Request"; 253 case ICMP_INFO_REPLY: return "Information Reply"; 254 case ICMP_ADDRESS: return "Address Mask Request"; 255 case ICMP_ADDRESSREPLY: return "Address Mask Reply"; 256 default: return "unknown ICMP type"; 257 } 258 } 259 260 static void unpack(char *buf, int sz, struct sockaddr_in *from) 261 { 262 struct icmp *icmppkt; 263 struct iphdr *iphdr; 264 struct timeval tv, *tp; 265 int hlen, dupflag; 266 unsigned long triptime; 267 268 gettimeofday(&tv, NULL); 269 270 /* check IP header */ 271 iphdr = (struct iphdr *) buf; 272 hlen = iphdr->ihl << 2; 273 /* discard if too short */ 274 if (sz < (datalen + ICMP_MINLEN)) 275 return; 276 277 sz -= hlen; 278 icmppkt = (struct icmp *) (buf + hlen); 279 280 if (icmppkt->icmp_id != myid) 281 return; /* not our ping */ 282 283 if (icmppkt->icmp_type == ICMP_ECHOREPLY) { 284 u_int16_t recv_seq = ntohs(icmppkt->icmp_seq); 285 ++nreceived; 286 tp = (struct timeval *) icmppkt->icmp_data; 287 288 if ((tv.tv_usec -= tp->tv_usec) < 0) { 289 --tv.tv_sec; 290 tv.tv_usec += 1000000; 291 } 292 tv.tv_sec -= tp->tv_sec; 293 294 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); 379 case ICMP_ECHOREPLY: return "Echo Reply"; 380 case ICMP_DEST_UNREACH: return "Destination Unreachable"; 381 case ICMP_SOURCE_QUENCH: return "Source Quench"; 382 case ICMP_REDIRECT: return "Redirect (change route)"; 383 case ICMP_ECHO: return "Echo Request"; 384 case ICMP_TIME_EXCEEDED: return "Time Exceeded"; 385 case ICMP_PARAMETERPROB: return "Parameter Problem"; 386 case ICMP_TIMESTAMP: return "Timestamp Request"; 387 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; 388 case ICMP_INFO_REQUEST: return "Information Request"; 389 case ICMP_INFO_REPLY: return "Information Reply"; 390 case ICMP_ADDRESS: return "Address Mask Request"; 391 case ICMP_ADDRESSREPLY: return "Address Mask Reply"; 392 default: return "unknown ICMP type"; 393 } 394 } 395 #if ENABLE_PING6 396 /* RFC3542 changed some definitions from RFC2292 for no good reason, whee! 397 * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */ 398 #ifndef MLD_LISTENER_QUERY 399 # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY 400 #endif 401 #ifndef MLD_LISTENER_REPORT 402 # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT 403 #endif 404 #ifndef MLD_LISTENER_REDUCTION 405 # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION 406 #endif 407 static const char *icmp6_type_name(int id) 408 { 409 switch (id) { 410 case ICMP6_DST_UNREACH: return "Destination Unreachable"; 411 case ICMP6_PACKET_TOO_BIG: return "Packet too big"; 412 case ICMP6_TIME_EXCEEDED: return "Time Exceeded"; 413 case ICMP6_PARAM_PROB: return "Parameter Problem"; 414 case ICMP6_ECHO_REPLY: return "Echo Reply"; 415 case ICMP6_ECHO_REQUEST: return "Echo Request"; 416 case MLD_LISTENER_QUERY: return "Listener Query"; 417 case MLD_LISTENER_REPORT: return "Listener Report"; 418 case MLD_LISTENER_REDUCTION: return "Listener Reduction"; 419 default: return "unknown ICMP type"; 420 } 421 } 422 #endif 423 424 static void unpack_tail(int sz, uint32_t *tp, 425 const char *from_str, 426 uint16_t recv_seq, int ttl) 427 { 428 const char *dupmsg = " (DUP!)"; 429 unsigned triptime = triptime; /* for gcc */ 430 431 ++nreceived; 432 433 if (tp) { 434 /* (int32_t) cast is for hypothetical 64-bit unsigned */ 435 /* (doesn't hurt 32-bit real-world anyway) */ 436 triptime = (int32_t) ((uint32_t)monotonic_us() - *tp); 295 437 tsum += triptime; 296 438 if (triptime < tmin) … … 298 440 if (triptime > tmax) 299 441 tmax = triptime; 300 301 if (TST(recv_seq % MAX_DUP_CHK)) { 302 ++nrepeats; 303 --nreceived; 304 dupflag = 1; 305 } else { 306 SET(recv_seq % MAX_DUP_CHK); 307 dupflag = 0; 308 } 309 310 if (options & O_QUIET) 311 return; 312 313 printf("%d bytes from %s: icmp_seq=%u", sz, 314 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), 315 recv_seq); 316 printf(" ttl=%d", iphdr->ttl); 317 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10); 318 if (dupflag) 319 printf(" (DUP!)"); 320 printf("\n"); 321 } else 322 if (icmppkt->icmp_type != ICMP_ECHO) 323 bb_error_msg("Warning: Got ICMP %d (%s)", 324 icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type)); 442 } 443 444 if (TST(recv_seq % MAX_DUP_CHK)) { 445 ++nrepeats; 446 --nreceived; 447 } else { 448 SET(recv_seq % MAX_DUP_CHK); 449 dupmsg += 7; 450 } 451 452 if (option_mask32 & OPT_QUIET) 453 return; 454 455 printf("%d bytes from %s: seq=%u ttl=%d", sz, 456 from_str, recv_seq, ttl); 457 if (tp) 458 printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000); 459 puts(dupmsg); 325 460 fflush(stdout); 326 461 } 327 328 static void ping(const char *host) 462 static void unpack4(char *buf, int sz, struct sockaddr_in *from) 463 { 464 struct icmp *icmppkt; 465 struct iphdr *iphdr; 466 int hlen; 467 468 /* discard if too short */ 469 if (sz < (datalen + ICMP_MINLEN)) 470 return; 471 472 /* check IP header */ 473 iphdr = (struct iphdr *) buf; 474 hlen = iphdr->ihl << 2; 475 sz -= hlen; 476 icmppkt = (struct icmp *) (buf + hlen); 477 if (icmppkt->icmp_id != myid) 478 return; /* not our ping */ 479 480 if (icmppkt->icmp_type == ICMP_ECHOREPLY) { 481 uint16_t recv_seq = ntohs(icmppkt->icmp_seq); 482 uint32_t *tp = NULL; 483 484 if (sz >= ICMP_MINLEN + sizeof(uint32_t)) 485 tp = (uint32_t *) icmppkt->icmp_data; 486 unpack_tail(sz, tp, 487 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), 488 recv_seq, iphdr->ttl); 489 } else if (icmppkt->icmp_type != ICMP_ECHO) { 490 bb_error_msg("warning: got ICMP %d (%s)", 491 icmppkt->icmp_type, 492 icmp_type_name(icmppkt->icmp_type)); 493 } 494 } 495 #if ENABLE_PING6 496 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit) 497 { 498 struct icmp6_hdr *icmppkt; 499 char buf[INET6_ADDRSTRLEN]; 500 501 /* discard if too short */ 502 if (sz < (datalen + sizeof(struct icmp6_hdr))) 503 return; 504 505 icmppkt = (struct icmp6_hdr *) packet; 506 if (icmppkt->icmp6_id != myid) 507 return; /* not our ping */ 508 509 if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { 510 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq); 511 uint32_t *tp = NULL; 512 513 if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t)) 514 tp = (uint32_t *) &icmppkt->icmp6_data8[4]; 515 unpack_tail(sz, tp, 516 inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr, 517 buf, sizeof(buf)), 518 recv_seq, hoplimit); 519 } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) { 520 bb_error_msg("warning: got ICMP %d (%s)", 521 icmppkt->icmp6_type, 522 icmp6_type_name(icmppkt->icmp6_type)); 523 } 524 } 525 #endif 526 527 static void ping4(len_and_sockaddr *lsa) 329 528 { 330 529 char packet[datalen + MAXIPLEN + MAXICMPLEN]; … … 332 531 333 532 pingsock = create_icmp_socket(); 334 335 memset(&pingaddr, 0, sizeof(struct sockaddr_in)); 336 337 pingaddr.sin_family = AF_INET; 338 hostent = xgethostbyname(host); 339 if (hostent->h_addrtype != AF_INET) 340 bb_error_msg_and_die("unknown address type; only AF_INET is currently supported."); 341 342 memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr)); 533 pingaddr.sin = lsa->sin; 534 if (source_lsa) { 535 if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF, 536 &source_lsa->sa, source_lsa->len)) 537 bb_error_msg_and_die("can't set multicast source interface"); 538 xbind(pingsock, &source_lsa->sa, source_lsa->len); 539 } 343 540 344 541 /* enable broadcast pings */ 345 sockopt = 1; 346 setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt, 347 sizeof(sockopt)); 542 setsockopt_broadcast(pingsock); 348 543 349 544 /* set recv buf for broadcast pings */ 350 sockopt = 48 * 1024; 351 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, 352 sizeof(sockopt)); 353 354 printf("PING %s (%s): %d data bytes\n", 355 hostent->h_name, 356 inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr), 357 datalen); 545 sockopt = 48 * 1024; /* explain why 48k? */ 546 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt)); 358 547 359 548 signal(SIGINT, pingstats); 360 549 361 550 /* start the ping's going ... */ 362 sendping (0);551 sendping4(0); 363 552 364 553 /* listen for replies */ … … 368 557 int c; 369 558 370 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,371 (struct sockaddr *) &from, &fromlen)) < 0) {372 if (errno == EINTR)373 continue;374 bb_perror_msg("recvfrom");559 c = recvfrom(pingsock, packet, sizeof(packet), 0, 560 (struct sockaddr *) &from, &fromlen); 561 if (c < 0) { 562 if (errno != EINTR) 563 bb_perror_msg("recvfrom"); 375 564 continue; 376 565 } 377 unpack (packet, c, &from);566 unpack4(packet, c, &from); 378 567 if (pingcount > 0 && nreceived >= pingcount) 379 568 break; 380 569 } 570 } 571 #if ENABLE_PING6 572 extern int BUG_bad_offsetof_icmp6_cksum(void); 573 static void ping6(len_and_sockaddr *lsa) 574 { 575 char packet[datalen + MAXIPLEN + MAXICMPLEN]; 576 int sockopt; 577 struct msghdr msg; 578 struct sockaddr_in6 from; 579 struct iovec iov; 580 char control_buf[CMSG_SPACE(36)]; 581 582 pingsock = create_icmp6_socket(); 583 pingaddr.sin6 = lsa->sin6; 584 /* untested whether "-I addr" really works for IPv6: */ 585 if (source_lsa) 586 xbind(pingsock, &source_lsa->sa, source_lsa->len); 587 588 #ifdef ICMP6_FILTER 589 { 590 struct icmp6_filter filt; 591 if (!(option_mask32 & OPT_VERBOSE)) { 592 ICMP6_FILTER_SETBLOCKALL(&filt); 593 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); 594 } else { 595 ICMP6_FILTER_SETPASSALL(&filt); 596 } 597 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 598 sizeof(filt)) < 0) 599 bb_error_msg_and_die("setsockopt(ICMP6_FILTER)"); 600 } 601 #endif /*ICMP6_FILTER*/ 602 603 /* enable broadcast pings */ 604 setsockopt_broadcast(pingsock); 605 606 /* set recv buf for broadcast pings */ 607 sockopt = 48 * 1024; /* explain why 48k? */ 608 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt)); 609 610 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); 611 if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2) 612 BUG_bad_offsetof_icmp6_cksum(); 613 setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt)); 614 615 /* request ttl info to be returned in ancillary data */ 616 setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1)); 617 618 if (if_index) 619 pingaddr.sin6.sin6_scope_id = if_index; 620 621 signal(SIGINT, pingstats); 622 623 /* start the ping's going ... */ 624 sendping6(0); 625 626 /* listen for replies */ 627 msg.msg_name = &from; 628 msg.msg_namelen = sizeof(from); 629 msg.msg_iov = &iov; 630 msg.msg_iovlen = 1; 631 msg.msg_control = control_buf; 632 iov.iov_base = packet; 633 iov.iov_len = sizeof(packet); 634 while (1) { 635 int c; 636 struct cmsghdr *mp; 637 int hoplimit = -1; 638 msg.msg_controllen = sizeof(control_buf); 639 640 c = recvmsg(pingsock, &msg, 0); 641 if (c < 0) { 642 if (errno != EINTR) 643 bb_perror_msg("recvfrom"); 644 continue; 645 } 646 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) { 647 if (mp->cmsg_level == SOL_IPV6 648 && mp->cmsg_type == IPV6_HOPLIMIT 649 /* don't check len - we trust the kernel: */ 650 /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */ 651 ) { 652 hoplimit = *(int*)CMSG_DATA(mp); 653 } 654 } 655 unpack6(packet, c, &from, hoplimit); 656 if (pingcount > 0 && nreceived >= pingcount) 657 break; 658 } 659 } 660 #endif 661 662 static void ping(len_and_sockaddr *lsa) 663 { 664 printf("PING %s (%s)", hostname, dotted); 665 if (source_lsa) { 666 printf(" from %s", 667 xmalloc_sockaddr2dotted_noport(&source_lsa->sa)); 668 } 669 printf(": %d data bytes\n", datalen); 670 671 #if ENABLE_PING6 672 if (lsa->sa.sa_family == AF_INET6) 673 ping6(lsa); 674 else 675 #endif 676 ping4(lsa); 677 } 678 679 int ping_main(int argc, char **argv); 680 int ping_main(int argc, char **argv) 681 { 682 len_and_sockaddr *lsa; 683 char *opt_c, *opt_s, *opt_I; 684 USE_PING6(sa_family_t af = AF_UNSPEC;) 685 686 INIT_G(); 687 688 datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ 689 690 /* exactly one argument needed, -v and -q don't mix */ 691 opt_complementary = "=1:q--v:v--q"; 692 getopt32(argv, OPT_STRING, &opt_c, &opt_s, &opt_I); 693 if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c 694 if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s 695 if (option_mask32 & OPT_I) { // -I 696 if_index = if_nametoindex(opt_I); 697 if (!if_index) { 698 /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */ 699 source_lsa = xdotted2sockaddr(opt_I, 0); 700 } 701 } 702 myid = (uint16_t) getpid(); 703 hostname = argv[optind]; 704 #if ENABLE_PING6 705 if (option_mask32 & OPT_IPV4) 706 af = AF_INET; 707 if (option_mask32 & OPT_IPV6) 708 af = AF_INET6; 709 lsa = xhost_and_af2sockaddr(hostname, 0, af); 710 #else 711 lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET); 712 #endif 713 714 if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family) 715 /* leaking it here... */ 716 source_lsa = NULL; 717 718 dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa); 719 ping(lsa); 381 720 pingstats(0); 382 }383 384 int ping_main(int argc, char **argv)385 {386 char *thisarg;387 388 datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */389 390 argc--;391 argv++;392 options = 0;393 /* Parse any options */394 while (argc >= 1 && **argv == '-') {395 thisarg = *argv;396 thisarg++;397 switch (*thisarg) {398 case 'q':399 options |= O_QUIET;400 break;401 case 'c':402 if (--argc <= 0)403 bb_show_usage();404 argv++;405 pingcount = atoi(*argv);406 break;407 case 's':408 if (--argc <= 0)409 bb_show_usage();410 argv++;411 datalen = atoi(*argv);412 break;413 default:414 bb_show_usage();415 }416 argc--;417 argv++;418 }419 if (argc < 1)420 bb_show_usage();421 422 myid = getpid() & 0xFFFF;423 ping(*argv);424 721 return EXIT_SUCCESS; 425 722 } 426 #endif /* ! CONFIG_FEATURE_FANCY_PING */ 723 #endif /* FEATURE_FANCY_PING */ 724 725 726 #if ENABLE_PING6 727 int ping6_main(int argc, char **argv); 728 int ping6_main(int argc, char **argv) 729 { 730 argv[0] = (char*)"-6"; 731 return ping_main(argc + 1, argv - 1); 732 } 733 #endif 734 735 /* from ping6.c: 736 * Copyright (c) 1989 The Regents of the University of California. 737 * All rights reserved. 738 * 739 * This code is derived from software contributed to Berkeley by 740 * Mike Muuss. 741 * 742 * Redistribution and use in source and binary forms, with or without 743 * modification, are permitted provided that the following conditions 744 * are met: 745 * 1. Redistributions of source code must retain the above copyright 746 * notice, this list of conditions and the following disclaimer. 747 * 2. Redistributions in binary form must reproduce the above copyright 748 * notice, this list of conditions and the following disclaimer in the 749 * documentation and/or other materials provided with the distribution. 750 * 751 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change 752 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> 753 * 754 * 4. Neither the name of the University nor the names of its contributors 755 * may be used to endorse or promote products derived from this software 756 * without specific prior written permission. 757 * 758 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 759 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 760 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 761 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 762 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 763 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 764 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 765 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 766 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 767 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 768 * SUCH DAMAGE. 769 */
Note:
See TracChangeset
for help on using the changeset viewer.