该code中icmp报文结构为自组装,适合轻量级嵌入式系统中调用
#include #include #include #include #include #include #include #include #include #include #include #ifndef ICMP_ECHOREPLY #define ICMP_ECHOREPLY 0 #endif #ifndef ICMP_ECHO #define ICMP_ECHO 8 #endif #ifndef bool #define bool int #endif #ifndef true #define true 1 #endif #ifndef false #define false 0 #endif #define XM_ICMP_LEN (56 + 8) //ICMP默认数据长度 + ICMP默认头部长度 #define SEND_BUFFER_SIZE 128 //发送缓冲区大小 #define RECV_BUFFER_SIZE 128 //接收缓冲区大小 //IP报头 小端结构 第一个字节,低4位是 version 高四位是 length //IP报头 大端结构 第一个字节,低4位是 length 高四位是 version //接收报文失败,可尝试交换 ip_hl ip_v位置,或者取icmp报文时候直接跳过20个byte typedef struct xm_ip { unsigned int ip_hl:4; //header length(报头长度) unsigned int ip_v:4; //version(版本) unsigned char ip_tos; unsigned short ip_len; unsigned short ip_id; unsigned short ip_off; unsigned char ip_ttl; unsigned char ip_p; unsigned short ip_sum; struct in_addr ip_src; struct in_addr ip_dst; }IP_HEADER; struct xm_icmp { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_cksum; union { struct { unsigned short icmp_id; unsigned short icmp_sequence; }echo; unsigned int gateway; struct { unsigned short icmp_unused; unsigned short icmp_mtu; }frag; //pmtu发现 }un; unsigned char icmp_data[0]; #define icmp_id un.echo.icmp_id #define icmp_seq un.echo.icmp_sequence }; static bool XM_SendPacket(int sock_icmp, struct sockaddr_in *dest_addr, int nSend) { char SendBuffer[SEND_BUFFER_SIZE] = {0}; struct xm_icmp *pIcmp = (struct xm_icmp*)SendBuffer; struct timeval *pTime; unsigned short *data = (unsigned short *)pIcmp; int len = XM_ICMP_LEN; unsigned int sum = 0; /* 类型和代码分别为ICMP_ECHO,0代表请求回送 */ pIcmp->icmp_type = ICMP_ECHO; pIcmp->icmp_code = 0; pIcmp->icmp_cksum = 0; //校验和 pIcmp->icmp_seq = nSend; //序号 pIcmp->icmp_id = sock_icmp; //取描述符作为标志 pTime = (struct timeval *)pIcmp->icmp_data; gettimeofday(pTime, NULL); //数据段存放发送时间 while (len > 1) { sum += *data++; len -= 2; } if (1 == len) { unsigned short tmp = *data; tmp &= 0xff00; sum += tmp; } //ICMP校验和带进位 while (sum >> 16) sum = (sum >> 16) + (sum & 0x0000ffff); sum = ~sum; pIcmp->icmp_cksum =sum; if (sendto(sock_icmp, SendBuffer, XM_ICMP_LEN, 0,(struct sockaddr *)dest_addr, sizeof(struct sockaddr_in)) < 0) { printf("%s:%d sendto failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } return true; } static bool XM_RecvePacket(int sock_icmp, struct sockaddr_in *dest_addr) { int RecvBytes = 0; char RecvBuffer[RECV_BUFFER_SIZE] = {0}; int addrlen = sizeof(struct sockaddr_in); struct xm_ip *Ip = (struct xm_ip *)RecvBuffer; struct xm_icmp *Icmp; struct timeval RecvTime; int ipHeadLen = 0; double rtt; if ((RecvBytes = recvfrom(sock_icmp, RecvBuffer, RECV_BUFFER_SIZE,0, (struct sockaddr *)dest_addr, &addrlen)) < 0) { printf("%s:%d recvfrom failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } ipHeadLen = (Ip->ip_hl << 2); gettimeofday(&RecvTime, NULL); Icmp = (struct xm_icmp *)(RecvBuffer + ipHeadLen); //判断接收到的报文是否是自己所发报文的响应 if ((Icmp->icmp_type == ICMP_ECHOREPLY) && Icmp->icmp_id == sock_icmp) { struct timeval *SendTime = (struct timeval *)Icmp->icmp_data; if ((RecvTime.tv_usec -= SendTime->tv_usec) < 0) { --(RecvTime.tv_sec); RecvTime.tv_usec += 1000000; } RecvTime.tv_sec -= SendTime->tv_sec; rtt = RecvTime.tv_sec * 1000.0 + RecvTime.tv_usec / 1000.0; printf("%u bytes from %s:icmp_seq=%u ttl=%u time=%f ms\n",(ntohs(Ip->ip_len) - ipHeadLen),inet_ntoa(Ip->ip_src),Icmp->icmp_seq,Ip->ip_ttl,rtt); return true; } return true; } bool XM_Ping(char *pszHost, int nTimes,int nTransTimeout, int *pnSussessCount) { int nSend = 0; int sock_icmp = 0; //icmp套接字 struct sockaddr_in dest_addr; //IPv4专用socket地址,保存目的地址 struct timeval timeout = {nTransTimeout / 1000, nTransTimeout % 1000 * 1000}; struct addrinfo stHints, *p_res,*p_cur; *pnSussessCount = 0; memset(&stHints, 0, sizeof(struct addrinfo)); stHints.ai_family = PF_INET; stHints.ai_flags = AI_PASSIVE; stHints.ai_protocol = IPPROTO_ICMP; stHints.ai_socktype = SOCK_RAW; if (-1 == getaddrinfo(pszHost, NULL, &stHints, &p_res)) { printf("%s:%d getaddrinfo failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } if(!p_res) { printf("%s:%d getaddrinfo failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } for (p_cur = p_res; p_cur != NULL; p_cur = p_cur->ai_next) { int nCount = 0; if((p_cur->ai_family != PF_INET)||(p_cur->ai_socktype != SOCK_RAW)||(p_cur->ai_protocol != IPPROTO_ICMP)) { printf("%s:%d [%d %d %d %d %d %d]\n",__FUNCTION__,__LINE__,p_cur->ai_family,PF_INET,p_cur->ai_socktype,SOCK_RAW,p_cur->ai_protocol, IPPROTO_ICMP); continue; } for(nCount = 0; nCount < 10; nCount++) { sock_icmp = socket(p_cur->ai_family, p_cur->ai_socktype, p_cur->ai_protocol); if(sock_icmp > 0) { memcpy(&dest_addr,p_cur->ai_addr,sizeof(struct sockaddr_in)); break; } sleep(1); } if(nCount < 10) { break; } printf("%s:%d socket failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); } freeaddrinfo(p_res); if(sock_icmp <= 0) { printf("%s:%d socket failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } if(-1 == setsockopt(sock_icmp, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(struct timeval))) { printf("%s:%d setsockopt failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } if(-1 == setsockopt(sock_icmp, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(struct timeval))) { printf("%s:%d setsockopt failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno)); return false; } printf("(%s) %d bytes of data\n",inet_ntoa(dest_addr.sin_addr), XM_ICMP_LEN); while (nSend < nTimes) { if(!XM_SendPacket(sock_icmp, &dest_addr, nSend)) { nSend++; continue; } nSend++; if(!XM_RecvePacket(sock_icmp, &dest_addr)) { continue; } (*pnSussessCount)++; } return true; } bool main(int argc, char *argv[]) { int nSussessCount = 0; if(argc < 4) { printf("usage: ./a.out url[www.baidu.com] count[10] timeout[100]ms\n"); return false; } XM_Ping(argv[1],atoi(argv[2]),atoi(argv[3]),&nSussessCount); printf("ping %s nTotalCount:%d nSussessCount :%d\n",argv[1],atoi(argv[2]),nSussessCount); return true; } |