ping 程序的实现
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#define PACKET_SIZE 4096
const int MAX_TIME_WAIT = 10;
const int maxpackage = 8;
struct sockaddr_in addr;
struct sockaddr_in from;
struct hostent *host;
int sendnum = 0, recvnum = 0;
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int datalen = 56;
struct timeval recvtime;
void subtime(struct timeval *in, struct timeval *out) ;
double sumtime;
int socketfd;
unsigned short check_sum(unsigned short *addr, int len) {
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *) (&answer) = *(unsigned char *) w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
int pack(int pack_no, int pid) {
struct icmp *icmp;
int packsize;
icmp = (struct icmp *) sendpacket;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no;
icmp->icmp_id = pid;
packsize = datalen + 8;
gettimeofday((struct timeval *) icmp->icmp_data, NULL);
icmp->icmp_cksum = check_sum((unsigned short *) icmp, packsize);
return packsize;
}
void statistics(int signo) {
printf("--- %s ping statistics ---\n", inet_ntoa(from.sin_addr));
printf("%d packets transmitted, %d received, %.2f packet loss, time %.2fms\n", sendnum, recvnum, (sendnum - recvnum) * 1.0 / sendnum * 100, sumtime);
close(socketfd);
return;
}
void send_packet(int pid) {
while (sendnum < maxpackage) {
sendnum++;
int packetsize = pack(sendnum, pid);
if (sendto(socketfd, sendpacket, (size_t)packetsize, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("sendto ERROR\n");
return;
}
}
}
int unpack(char *buf, int len, int pid) {
struct ip *ip;
struct icmp *icmp;
ip = (struct ip *) buf;
int iplen = ip->ip_hl << 2;
icmp = (struct icmp *) (buf + iplen);
len -= iplen;
if (len < 8) {
printf("ICMP packet's length is less than 8\n");
return -1;
}
if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) {
gettimeofday((struct timeval *) icmp->icmp_data, &recvtime);
double rtt = recvtime.tv_sec * 1000 + recvtime.tv_usec / 1000;
sumtime += rtt;
printf("%d bytes from %s: icmp_seq=%d ttl = %d time=%.3f ms\n", len, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
} else return -1;
}
void recv_packet(int pid) {
signal(SIGALRM, statistics);
socklen_t fromlen;
fromlen = sizeof(from);
while (recvnum < maxpackage) {
alarm(MAX_TIME_WAIT);
int n;
if ((n = recvfrom(socketfd, recvpacket, (size_t)sizeof(recvpacket), 0, (struct sockaddr *) &from, &fromlen)) < 0) {
if (errno == EINTR) continue;
perror("recvfrom error\n");
continue;
}
gettimeofday(&recvtime, NULL);
if (unpack(recvpacket, n, pid) == -1) continue;
recvnum++;
}
}
void subtime(struct timeval *in, struct timeval *out) {
if (out->tv_usec - in->tv_usec < 0) {
out->tv_sec--;
out->tv_usec = out->tv_usec - in->tv_usec + 1000000;
}
out->tv_sec = out->tv_sec - in->tv_sec;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Invalid IP Address\n");
return -1;
}
struct protoent *protoent1;
if ((protoent1 = getprotobyname("icmp")) == NULL) {
printf("protocol error\n");
return -1;
}
socketfd = socket(AF_INET, SOCK_RAW, protoent1->p_proto);
if (socketfd < 0) {
perror("socket error\n");
return -1;
}
int size = 50 * 1024;
setuid(getuid());
setsockopt(socketfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
long inaddr = inet_addr(argv[1]);
if (inaddr == INADDR_NONE) {
if ((host = gethostbyname(argv[1])) == NULL) {
perror("host error\n");
return -1;
}
memcpy((char *) &addr.sin_addr, host->h_addr, (size_t)host->h_length);
} else {
memcpy((char *) &addr.sin_addr, (char *) &addr, (size_t)host->h_length);
}
int pid = getpid();
printf("Ping %s(%s): %d bytes data in ICMP packets\n", argv[1], inet_ntoa(addr.sin_addr), datalen);
send_packet(pid);
recv_packet(pid);
statistics(SIGALRM);
return 0;
}