/* * my P I N G . C * * NetBSD, FreeBSD, OpenBSD, Solaris, Linuxで動作確認をしたよんヽ(´ー`)ノ * * compile: * NetBSD,OpenBSD,FreeBSD,Linux: % gcc ping.c -Wall * Solaris: * % gcc ping.c -Wall -lsocket -lnsl * * by ---- */ #include #include #include #include #include #include #undef __USE_BSD #define __USE_BSD #undef __FAVOR_BSD #define __FAVOR_BSD #include #include #include #include #include #include #include #include #include #include #include #include #define SMAX(a,b) (((int)(a) > (int)(b)) ? (a) : (b)) #define SMIN(a,b) (((int)(a) < (int)(b)) ? (a) : (b)) #define PHDR_LEN ((int)sizeof(struct timeval)) #define DEFDATALEN (64 - PHDR_LEN) #define MAXALARM 60 #define MAXPACKETLEN (IP_MAXPACKET - (0xf << 2)) #define MAXDATALEN (MAXPACKETLEN - ICMP_MINLEN) #define PTNSET(a,b) memset((char *)((char *)(a)+(sizeof(struct timeval))+ICMP_MINLEN), (b), MAXDATALEN) #define ST(a) (gettimeofday((struct timeval *)&outpack[ICMP_MINLEN], NULL)) #define A(bit) rcvd_tbl[(bit)>>3] #define B(bit) (1 << ((bit) & 0x07)) #define SET(bit) (A(bit) |= B(bit)) #define CLR(bit) (A(bit) &= (~B(bit))) #define TST(bit) (A(bit) & B(bit)) #define MAX_DUP_CHK (8 * 128) int mx_dup_ck = MAX_DUP_CHK; char rcvd_tbl[MAX_DUP_CHK / 8]; int sd; int ident; int finish_up; int preload; int datalen = DEFDATALEN; long nreports; long ntransmitted; long nreceived; long npackets; char *hostname; char pattern = 0xff; char outpack[MAXPACKETLEN]; char packet[IP_MAXPACKET]; double tmin = 999999999.0; double tmax = 0.0; double tsum = 0.0; static u_short in_cksum(u_short *addr, int len); static void usage(const char *p); static int wait_socket_readable(int sd, long sec, long usec); static void tvsub(struct timeval *out, struct timeval *in); static void mk_packet(int cc); static void pinger(register struct sockaddr_in *to); static int resolve(const char *host, struct in_addr *addr); static void print_icmph(struct ip *ip, struct icmp *icp, int cc); static void print_packet(char *p, int cc, struct sockaddr_in *from, struct timeval *tv); static void stopit(int sig); static void finish(void); int main(int argc, char *argv[]) { struct timeval now; struct iovec iov; struct msghdr msg; struct sockaddr_in from; struct sockaddr_in to; unsigned long ultmp; int ttl = 0; int c; int timeout_sec = 1; int packetlen; int almost_done = 0; char *ep; if (argc == 1) usage(argv[0]); while ((c = getopt(argc, argv, "c:i:l:m:p:t:h")) != -1) { switch (c) { case 'c': ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp > LONG_MAX || ultmp == 0) { fprintf(stderr, "Invalid count of packets to transmit: %s\n", optarg); exit(EXIT_FAILURE); } npackets = ultmp; break; case 'i': ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp > MAXALARM) { fprintf(stderr, "Invalid interval value: %s\n", optarg); exit(EXIT_FAILURE); } timeout_sec = ultmp; break; case 'l': ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp > INT_MAX) { fprintf(stderr, "Invalid preload value: %s\n", optarg); exit(EXIT_FAILURE); } preload = ultmp; break; case 'm': ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp > 0xff) { fprintf(stderr, "Invalid TTL: %s\n", optarg); exit(EXIT_FAILURE); } ttl = ultmp; break; case 'p': ultmp = strtol(optarg, NULL, 0); pattern = ultmp & 0xff; break; case 't': ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp == 0 || ultmp > MAXALARM) { fprintf(stderr, "Invalid timeout: %s\n", optarg); exit(EXIT_FAILURE); } timeout_sec = ultmp; break; case '?': case ':': case 'h': default: usage(argv[0]); } } argc -= optind; argv += optind; if (argc != 1) usage(argv[0]); memset(&to, 0x00, sizeof(to)); to.sin_family = AF_INET; if ((hostname = *argv) == NULL) usage(argv[0]); if (resolve(hostname, &to.sin_addr) < 0) { fprintf(stderr, "%s: hostname lookup failure\n", hostname); exit(EXIT_FAILURE); } if ((sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(EXIT_FAILURE); } if (ttl) { if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { perror("setsockopt IP_TTL"); exit(EXIT_FAILURE); } } packetlen = (0xf << 2) + ICMP_MINLEN + datalen; packetlen = SMIN(packetlen, IP_MAXPACKET); memset(&msg, 0x00, sizeof(msg)); msg.msg_name = (caddr_t)&from; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_namelen = sizeof(from); iov.iov_base = packet; iov.iov_len = packetlen; signal(SIGINT, stopit); ident = getpid() & 0xFFFF; printf("PING %s (%s)\n", hostname, inet_ntoa(to.sin_addr)); if (preload == 0) pinger(&to); else { if (npackets != 0 && preload > npackets) preload = npackets; while (preload--) pinger(&to); } while (!finish_up) { int res; int cc; res = wait_socket_readable(sd, (int)timeout_sec, 0); if (res < 0) continue; if (res == 1) { cc = recvmsg(sd, &msg, 0); if (cc < 0) { if (errno == EINTR) continue; perror("recvmsg"); exit(EXIT_FAILURE); } gettimeofday(&now, NULL); print_packet(packet, cc, &from, &now); if (npackets && nreceived >= npackets) break; } if (res == 0) { if (npackets == 0 || ntransmitted < npackets) pinger(&to); else { if (almost_done) break; almost_done = 1; timeout_sec = 0; } } } finish(); /* NOTREACHED */ return 0; } /* * in_cksum -- * Checksum routine for Internet Protocol family headers (C Version) */ static u_short in_cksum(u_short *addr, int len) { int nleft, sum; u_short *w; union { u_short us; u_char uc[2]; } last; u_short answer; nleft = len; sum = 0; w = addr; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { last.uc[0] = *(u_char *)w; last.uc[1] = 0; sum += last.us; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } static void usage(const char *p) { fprintf(stderr, "Usage: %s [option(s)] \n" " -c \n" " -m \n" " -p \n" " -t \n", p); exit(EXIT_FAILURE); } static int wait_socket_readable(int sd, long sec, long usec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(sd, &rset); tv.tv_sec = sec; tv.tv_usec = usec; return (select(sd + 1, &rset, NULL, NULL, &tv)); } static void tvsub(struct timeval *out, struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } static void mk_packet(int cc) { struct icmp *icp; icp = (struct icmp *)outpack; icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; icp->icmp_seq = ntransmitted; icp->icmp_id = ident; PTNSET(icp, pattern); ST(outpack); CLR(icp->icmp_seq % mx_dup_ck); icp->icmp_cksum = in_cksum((u_short *)icp, cc); } static void pinger(register struct sockaddr_in *to) { int cc; int len; cc = ICMP_MINLEN + datalen; mk_packet(cc); len = sendto(sd, outpack, cc, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in)); if (len < 0 || len != cc) { perror("sendto"); exit(EXIT_FAILURE); } ntransmitted++; } static int resolve(const char *host, struct in_addr *addr) { struct hostent *hoste; if (inet_pton(AF_INET, host, addr) == 1) return 0; if ((hoste = gethostbyname(host))) { memcpy(addr, hoste->h_addr_list[0], sizeof(struct in_addr)); return 1; } return -1; } static void print_icmph(struct ip *ip, struct icmp *icp, int cc) { struct ip *errip; struct udphdr *errudp; printf("%d bytes from %s: ", cc, inet_ntoa(ip->ip_src)); switch (icp->icmp_type) { case ICMP_TIMXCEED: printf("time exceeded"); if (icp->icmp_code == ICMP_TIMXCEED_INTRANS) printf(" in-transit"); else if (icp->icmp_code == ICMP_TIMXCEED_REASS) printf(" in-reass"); break; case ICMP_SOURCEQUENCH: /* packet lost, slow down */ printf("packet lost, slow down"); break; case ICMP_ROUTERADVERT: /* router advertisement */ printf("router advertisement"); break; case ICMP_ROUTERSOLICIT: /* router solicitation */ printf("router solicitation"); break; case ICMP_PARAMPROB: /* ip header bad */ printf("ip header bad"); break; case ICMP_UNREACH: errip = (struct ip *)((char *)icp + 8); switch (icp->icmp_code) { case ICMP_UNREACH_NET: /* bad net */ printf("%s network unreachable for ", inet_ntoa(ip->ip_src)); printf("%s > ", inet_ntoa(errip->ip_src)); printf("%s:", inet_ntoa(errip->ip_dst)); break; case ICMP_UNREACH_HOST: /* bad host */ printf("%s host ", inet_ntoa(ip->ip_src)); printf("%s unreachable for ", inet_ntoa(errip->ip_dst)); printf("%s > ", inet_ntoa(errip->ip_src)); printf("%s:", inet_ntoa(errip->ip_dst)); break; case ICMP_UNREACH_PROTOCOL: /* bad protocol */ printf("bad protocol"); break; case ICMP_UNREACH_PORT: /* bad port */ if (errip->ip_p == IPPROTO_UDP) { errudp = (struct udphdr *)((char *)errip + (errip->ip_hl << 2)); printf("%s udp port %d unreachable for ", inet_ntoa(ip->ip_src), ntohs(errudp->uh_dport)); printf("%s.%d > ", inet_ntoa(errip->ip_src), ntohs(errudp->uh_sport)); printf("%s.%d:", inet_ntoa(errip->ip_dst), ntohs(errudp->uh_dport)); } break; case ICMP_UNREACH_NEEDFRAG: /* IP_DF caused drop */ printf("IP_DF"); break; case ICMP_UNREACH_SRCFAIL: /* src route failed */ printf("src route failed"); break; case ICMP_UNREACH_NET_UNKNOWN: /* unknown net */ printf("unknown net"); break; case ICMP_UNREACH_HOST_UNKNOWN: /* unknown host */ printf("unknown host"); break; case ICMP_UNREACH_ISOLATED: /* src host isolated */ printf("src host isolated"); break; case ICMP_UNREACH_NET_PROHIB: /* prohibited access */ printf("prohibited access"); break; case ICMP_UNREACH_HOST_PROHIB: /* ditto */ printf("ditto"); break; case ICMP_UNREACH_TOSNET: /* bad tos for net */ printf("bad tos for net"); break; case ICMP_UNREACH_TOSHOST: /* bad tos for host */ printf("bad tos for host"); break; #ifdef ICMP_UNREACH_FILTER_PROHIB case ICMP_UNREACH_FILTER_PROHIB: /* admin prohib */ printf("admin prohib"); break; #endif #ifdef ICMP_UNREACH_HOST_PRECEDENCE case ICMP_UNREACH_HOST_PRECEDENCE: /* host prec vio. */ printf("host prec vio"); break; #endif #ifdef ICMP_UNREACH_PRECEDENCE_CUTOFF case ICMP_UNREACH_PRECEDENCE_CUTOFF: /* prec cutoff */ printf("prec cutoff"); break; #endif default: break; } break; default: printf("unknown"); break; } return; } static void print_packet(char *p, int cc, struct sockaddr_in *from, struct timeval *tv) { struct ip *ip; struct icmp *icp; struct timeval *tp, tv1; double triptime; int iphlen; int dupflag; ip = (struct ip *)p; iphlen = ip->ip_hl << 2; if (cc < iphlen + ICMP_MINLEN) return; cc -= iphlen; icp = (struct icmp *)(p + iphlen); if (icp->icmp_type == ICMP_ECHOREPLY) { if (icp->icmp_id != ident) return; nreceived++; triptime = 0.0; #ifndef icmp_data tp = (struct timeval *)&icp->icmp_ip; #else tp = (struct timeval *)icp->icmp_data; #endif memcpy(&tv1, tp, sizeof(tv1)); tvsub(tv, &tv1); triptime = ((double)tv->tv_sec) * 1000.0 + ((double)tv->tv_usec) / 1000.0; tsum += triptime; if (triptime < tmin) tmin = triptime; if (triptime > tmax) tmax = triptime; if (TST(icp->icmp_seq % mx_dup_ck)) { nreports++; nreceived--; dupflag = 1; } else { SET(icp->icmp_seq % mx_dup_ck); dupflag = 0; } printf("%d bytes from %s: icmp_seq=%u", cc, inet_ntoa(from->sin_addr), icp->icmp_seq); printf(" ttl=%d", ip->ip_ttl); printf(" time=%.3f ms", triptime); if (dupflag) printf(" (DUP!)"); } else print_icmph(ip, icp, cc); putchar('\n'); } static void stopit(int sig) { finish_up = 1; } static void finish(void) { putchar('\n'); printf("--- %s ping statistics ---\n", hostname); printf("%ld packets transmitted", ntransmitted); printf(", %ld packets received, ", nreceived); if (nreports) printf("+%ld duplicates, ", nreports); if (ntransmitted) { if (nreceived > ntransmitted) printf("-- somebody's printing up packets!"); else printf("%d%% packet loss", (int)(((ntransmitted - nreceived) * 100) / ntransmitted)); } putchar('\n'); if (nreceived) { double n = nreceived; double avg = tsum / n; printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", tmin, avg, tmax); } exit(EXIT_SUCCESS); }