ICMP->linux c PING功能實現
阿新 • • 發佈:2018-12-09
報文格式:
\
程式碼
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/ip_icmp.h> //struct icmp #include <netinet/in.h> //sockaddr_in #include <netinet/ip.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #include <string.h> //校驗和計算 unsigned short calc_cksum(char *buff,int len) { int blen = len; unsigned short *mid = (unsigned short*)buff; unsigned short te = 0; unsigned int sum = 0; while(blen > 1) { sum += *mid++; blen -= 2; } //資料長度為奇數比如65 上面的while是按16計算的 最後就會剩下一位元組不能計算 if(blen == 1) { //將多出的一位元組放入short型別的高位 低8位置0 加入到sum中 te = *(unsigned char*)mid; te = (te << 8) & 0xff; sum += te; } sum = (sum >> 16) + (sum&0xffff); sum += sum >>16; return (unsigned short)(~sum); } static void icmp_packet(char *buff,int len,int id,int seq) { struct timeval *tval = NULL; struct icmp *icmp = (struct icmp*)buff; icmp->icmp_type = 8; //ECHO REQUEST icmp->icmp_code = 0; icmp->icmp_cksum = 0; //first set zero icmp->icmp_id = id & 0xffff; icmp->icmp_seq = seq; tval = (struct timeval *)icmp->icmp_data; gettimeofday(tval,NULL);//獲得傳輸時間作為資料 //計算校驗和 icmp->icmp_cksum = calc_cksum(buff,len); return; } void parse_packet(char *buff,int len) { struct timeval *val; struct timeval nv; struct icmp *icmp; struct iphdr *iphead = (struct iphdr *)buff; struct in_addr addr; addr.s_addr = iphead->saddr; printf("comefrom ip=%s ",inet_ntoa(addr)); //跳過ip頭 icmp = (struct icmp *)(buff+sizeof(struct iphdr)); //看傳輸回的包校驗和是否正確 if(calc_cksum((char *)icmp,len-sizeof(sizeof(struct iphdr))) > 1) { printf("receiver error\n"); return; } gettimeofday(&nv,NULL); val = (struct timeval *)icmp->icmp_data; printf("type=%d seq=%d id=%d pid=%d usec=%d \n",icmp->icmp_type,icmp->icmp_seq,icmp->icmp_id,(getpid()&0xffff),nv.tv_usec - val->tv_usec); } int main(int argc,char *argv[]) { int skfd; struct sockaddr_in addr={0}; struct sockaddr_in saddr={0}; char buff[64]={0}; char recvbuff[512]={0}; int ret; int addrlen = 0; int count = 5; int i = 1; skfd = socket(PF_INET,SOCK_RAW,IPPROTO_ICMP); if(skfd < 0) { printf("socket error\n"); return -1; } addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("192.168.13.148"); //每一秒傳送一次 共傳送count次 while(count > 0) { //序列號seq 從1 開始傳輸 buff的大小為64 memset(buff,0,sizeof(buff)); icmp_packet(buff,64,getpid(),i); i++; count --; //將資料傳送出去 ret = sendto(skfd,buff,64,0,(struct sockaddr *)&addr,sizeof(addr)); if(ret <= 0) { printf("send error\n"); goto out; } else printf("send success ret=%d\n",ret); //接收echo replay memset(recvbuff,0,sizeof(recvbuff)); memset(&saddr,0,sizeof(saddr)); addrlen = sizeof(saddr); ret = recvfrom(skfd,recvbuff,sizeof(recvbuff),0,(struct sockaddr *)&saddr,&addrlen); if(ret <= 0) { printf("recv error\n"); goto out; } parse_packet(recvbuff,ret); sleep(1); } out: close(skfd); return 0; }