#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#include<signal.h>
#define PACKLEN 64
unsigned short Chksum(unsigned short *ptr,int size);
void Set_header(int seq,char *datapack);
int Unpack(char *pack,int size);
void statistics(int signo);
int sendseq = 0,recvseq = 0;
int sockfd;
struct sockaddr_in out;
struct sockaddr_in in;
int iplen,inlen;
char sendbuf[4096];
char recvbuf[4096];
int main(int argc,char *argv[])
{
//接收control-c信号
signal(SIGINT,statistics);
out.sin_family = AF_INET;
out.sin_addr.s_addr = inet_addr(argv[1]);
//创建ICMP原始套接字,需要root权限
if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < 0)
{
perror("socket error.");
exit(1);
}
printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1],inet_ntoa(out.sin_addr),PACKLEN - 8);
while(1)
{
bzero(sendbuf,sizeof(sendbuf));
//设置报头
Set_header(sendseq,sendbuf);
sendseq++;
//发送数据包
if(sendto(sockfd,sendbuf,PACKLEN,0,(struct sockaddr*)&out,sizeof(out)) < 0)
{
perror("sendto error.");
exit(1);
}
bzero(recvbuf,sizeof(recvbuf));
bzero(&in,sizeof(in));
inlen = sizeof(in);
//接收数据包
if((iplen = recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&in,&inlen)) <= 0)
{
perror("recvfrom error.");
exit(1);
}
//解包,recvfrom从SOCK_RAW类型套接字接收到的数据为IP包
if( Unpack(recvbuf,iplen) == -1)
{
printf("-------Error---------\n");
}
else
recvseq++;
sleep(1);
}
return 0;
}
void statistics(int signo)
{
printf("\n--------------PING statistics-------------\n");
printf("%d packets transmitted, %d received ,%%%.2f lost\n",sendseq,recvseq,(double)(sendseq-recvseq) / sendseq * 100);
close(sockfd);
exit(1);
}
//计算校验和
unsigned short Chksum(unsigned short *ptr,int size)
{
int sum = 0;
//以两个字节为单位累加
while(size > 1)
{
sum += *ptr;
ptr++;
size -= 2;
}
//剩余一个字节放在高位继续累加
if(size)
sum += (*(unsigned char*)ptr) << 8;
//把16位后的进位加在低位
while(sum >> 16)
{
sum = (sum >> 16) + (sum & 0xffff);
}
//取反码
return (unsigned short)(~sum);
}
//设置ICMP报头
void Set_header(int seq,char *datapack)
{
struct icmp *icmp;
struct timeval *time;
icmp = (struct icmp*)datapack;
//报文类型:请求回显
icmp -> icmp_type = ICMP_ECHO;
icmp -> icmp_code = 0;
//标识符设为当前进程号
icmp -> icmp_id = getpid();
icmp -> icmp_seq = seq;
icmp -> icmp_cksum = 0;
//把数据去保存为当前时间
time = (struct timeval*)icmp -> icmp_data;
gettimeofday(time,NULL);
//计算校验和
icmp -> icmp_cksum = Chksum((unsigned short*)icmp,PACKLEN);
}
//数据解包
int Unpack(char *pack,int size)
{
struct ip *ip;
struct icmp* icmp;
struct timeval now;
struct timeval *timeout;
int iplen,icmplen;
double rtt = 0;
ip = (struct ip*)pack;
//计算ip报头长度
iplen = ip -> ip_hl * 4;
icmp = (struct icmp*)(pack + iplen);
icmplen = size - iplen;
if(icmplen < 8)
{
return -1;
}
if((icmp -> icmp_type == ICMP_ECHOREPLY) && (icmp -> icmp_id == getpid()))
{
//计算时间 当前时间与存取icmp数据区的发送时间相减
timeout = (struct timeval*)icmp -> icmp_data;
gettimeofday(&now,NULL);
if((now.tv_usec = now.tv_usec - timeout -> tv_usec) < 0)
{
now.tv_sec--;
now.tv_usec + 1000000;
}
now.tv_sec -= timeout -> tv_sec;
rtt = now.tv_sec * 1000 + now.tv_usec / 1000.0;
printf("%d byte from %s: icmp_seq = %u ttl = %d time = %.3f ms\n",icmplen,inet_ntoa(in.sin_addr),icmp -> icmp_seq,ip -> ip_ttl,rtt);
return 1;
}
//ping 127.0.0.1
else if((icmp -> icmp_type == ICMP_ECHO) && (icmp -> icmp_id == getpid()))
{
bzero(recvbuf,sizeof(recvbuf));
inlen = sizeof(in);
//接收数据包
if((iplen = recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&in,&inlen)) <= 0)
{
perror("recvfrom error.");
exit(1);
}
if(Unpack(recvbuf,iplen) == -1)
return -1;
}
else
return -1;
}
ping程序---unix系统
最新推荐文章于 2024-02-22 16:38:14 发布