1.程序源代码 /******************************************************** * 功能:实现PING功能 * 环境: GCC-4.2.4 * 作者:YSQ-NJUST,yushengqiangyu@163.com * 备注:自由软件,主要用于学习、交流、共享。 *******************************************************/ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netdb.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/ip_icmp.h> #include <netinet/in.h> #include <arpa/inet.h> #define E_FAILD_FD -1 #define ICMP_DATA_LEN 20 /* ICMP负载长度 */ #define ICMP_ECHO_MAX 4 /* ECHO-REQUEST报文发送次数 */ #define ICMP_REQUEST_TIMEOUT 2 /* 超时时间间隔 */ /* ICMP报文发送与接收缓存 */ static unsigned char aucSendBuf[1024 * 1024] = {0}; static unsigned char aucRecvBuf[1024 * 1024] = {0}; /* 结构体定义 */ typedef struct tagIcmpStatic { unsigned int uiSendPktNum; unsigned int uiRcvPktNum; float fMinTime; float fMaxTime; float fArgTime; }ICMP_STATIC_S; /* 全局数据结构 */ ICMP_STATIC_S g_stPktStatic; /* ICMP报文统计 */ struct timeval stSendTime = {0}; /* ECHO-REQUEST报文发送时间 */ struct timeval stRcvTime ={0}; /* ECHO-REPLY报文接收时间 */ /* 输出报文统计信息 */ void showStatic(const ICMP_STATIC_S *pstStInfo) { unsigned int uiSend, uiRecv; uiSend = pstStInfo->uiSendPktNum; uiRecv = pstStInfo->uiRcvPktNum; printf("n***PING Statistics***"); printf("nPackets:Send = %u,Recveived = %u,Lost = %u", uiSend, uiRecv, uiSend - uiRecv); printf("nTime:Minimum = %.1fms,Maximum = %.1fms,Average=%.2fmsn", pstStInfo->fMinTime, pstStInfo->fMaxTime, pstStInfo->fArgTime); } /* 计算时间差,返回时间以毫秒为单位 */ unsigned int timeSub(const struct timeval *pstOut, const struct timeval *pstIn) { unsigned int uiSec = 0; int iUsec = 0; uiSec = pstOut->tv_sec - pstIn->tv_sec; iUsec = pstOut->tv_usec - pstIn->tv_usec; if (0 > iUsec) { iUsec += 1000000; uiSec--; } return uiSec * 1000 + (unsigned int)(iUsec / 1000); } /* 校验和计算 */ unsigned short calcIcmpChkSum(const void *pPacket, int iPktLen) { unsigned short usChkSum = 0; unsigned short *pusOffset = NULL; pusOffset = (unsigned short *)pPacket; while(1 < iPktLen) { usChkSum += *pusOffset++; iPktLen -= sizeof(unsigned short); } if (1 == iPktLen) { usChkSum += *((char *)pusOffset); } usChkSum = (usChkSum >> 16) + (usChkSum & 0xffff); usChkSum += (usChkSum >>16); return ~usChkSum; } /* ICMP报文填充 */ int newIcmpEcho(const int iPacketNum, const int iDataLen) { struct icmp *pstIcmp = NULL; memset(aucSendBuf, 0, sizeof(aucSendBuf)); pstIcmp = (struct icmp *)aucSendBuf; pstIcmp->icmp_type = ICMP_ECHO; pstIcmp->icmp_code = 0; pstIcmp->icmp_seq = htons((unsigned short)iPacketNum); pstIcmp->icmp_id = htons((unsigned short)getpid()); pstIcmp->icmp_cksum = 0; pstIcmp->icmp_cksum = calcIcmpChkSum(pstIcmp, iDataLen + 8); return iDataLen + 8; } /* 解析ECHO-REPLY响应报文 */ int parseIcmp(const struct sockaddr_in *pstFromAddr, char *pRecvBuf, const int iLen) { int iIpHeadLen = 0; int iIcmpLen = 0; struct ip *pstIp = NULL; struct icmp *pstIcmpReply = NULL; pstIp = (struct ip *)pRecvBuf; iIpHeadLen = pstIp->ip_hl << 2; pstIcmpReply = (struct icmp *)(pRecvBuf + iIpHeadLen); /* 报文长度非法 */ iIcmpLen = iLen - iIpHeadLen; if (8 > iIcmpLen) { printf("[Error]Bad ICMP Echo-replyn"); return -1; } /* 报文类型非法 */ if ((pstIcmpReply->icmp_type != ICMP_ECHOREPLY) || (pstIcmpReply->icmp_id != htons((unsigned short)getpid()))) { return -1; } sleep(1); printf("%d bytes reply from %s: icmp_seq=%u Time=%dms TTL=%dn", iIcmpLen, inet_ntoa(pstFromAddr->sin_addr), ntohs(pstIcmpReply->icmp_seq), timeSub(&stRcvTime, &stSendTime), pstIp->ip_ttl); return 1; } /* Echo响应报文接收 */ void recvIcmp(const int fd) { int iRet = 0; int iRecvLen = 0; unsigned int uiInterval = 0; socklen_t fromLen = sizeof(struct sockaddr_in); struct sockaddr_in stFromAddr = {0}; /* 清空接收缓存,并准备接收响应报文 */ memset(aucRecvBuf, 0, 1024 * 1024); iRecvLen = recvfrom(fd, (void *)aucRecvBuf, sizeof(aucRecvBuf), 0, (struct sockaddr *)&stFromAddr,&fromLen); gettimeofday(&stRcvTime, NULL); if (0 > iRecvLen) { if (EAGAIN == errno) { /* 请求超时 */ printf("Request time out.n"); g_stPktStatic.fMaxTime = ~0; } else { /* 接收数据包出错 */ perror("[Error]ICMP Receive"); } return; } /* 获取统计参数 */ g_stPktStatic.uiRcvPktNum++; uiInterval = timeSub(&stRcvTime, &stSendTime); g_stPktStatic.fArgTime = (g_stPktStatic.fArgTime * (g_stPktStatic.uiSendPktNum - 1) + uiInterval)/g_stPktStatic.uiSendPktNum; if (uiInterval < g_stPktStatic.fMinTime) { g_stPktStatic.fMinTime = uiInterval; } if (uiInterval > g_stPktStatic.fMaxTime) { g_stPktStatic.fMaxTime = uiInterval; } /* 解析ICMP响应报文 */ iRet = parseIcmp(&stFromAddr, (char *)aucRecvBuf, iRecvLen); if (0 > iRet) { return; } } /* 发送ICMP报文 */ void sendIcmp(const int fd, const struct sockaddr_in *pstDestAddr) { unsigned char ucEchoNum = 0; int iPktLen = 0; int iRet = 0; while(ICMP_ECHO_MAX > ucEchoNum) { iPktLen = newIcmpEcho(ucEchoNum, ICMP_DATA_LEN); /* 记录发送起始时间 */ g_stPktStatic.uiSendPktNum++; gettimeofday(&stSendTime, NULL); /* 发送ICMP-ECHO报文 */ iRet = sendto(fd, aucSendBuf, iPktLen, 0, (struct sockaddr *)pstDestAddr, sizeof(struct sockaddr_in)); if(0 > iRet) { perror("Send ICMP Error"); continue; } /* 等待接收响应报文 */ recvIcmp(fd); ucEchoNum++; } }
/*主函数*/ int main(int argc, char *argv[]) { int iRet = 0; int iRcvBufSize = 1024 * 1024; int fd = E_FAILD_FD; in_addr_t stHostAddr; struct timeval stRcvTimeOut = {0}; struct hostent *pHost = NULL; struct sockaddr_in stDestAddr = {0}; struct protoent *pProtoIcmp = NULL; g_stPktStatic.uiSendPktNum = 0; g_stPktStatic.uiRcvPktNum =0; g_stPktStatic.fMinTime = 1000000.0; g_stPktStatic.fMaxTime = -1.0; g_stPktStatic.fArgTime = 0.0; /* 参数判断 */ if (2 > argc) { printf("nUsage:%s hostname/IP addressn", argv[0]); return -1; } /* 获取ICMP协议类型 */ pProtoIcmp = getprotobyname("icmp"); if (NULL == pProtoIcmp) { perror("[Error]Get ICMP Protoent Structrue"); return -1; } /* 创建ICMP使用的SOCKET */ fd = socket(PF_INET, SOCK_RAW, pProtoIcmp->p_proto); if (0 > fd) { perror("[Error]Init Socket"); return -1; } /* ROOT权限回收 */ iRet = setuid(getuid()); if (0 > iRet) { perror("[Error]Setuid"); close(fd); return -1; } /* 设置SOCKET选项 */ stRcvTimeOut.tv_sec = ICMP_REQUEST_TIMEOUT; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &iRcvBufSize, sizeof(iRcvBufSize)); setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &stRcvTimeOut, sizeof(struct timeval)); /* 地址解析 */ stHostAddr = inet_addr(argv[1]); if (INADDR_NONE == stHostAddr) { /* 根据主机名称解析IP地址 */ pHost = gethostbyname(argv[1]); if (NULL == pHost) { perror("[Error]Host Name Error"); close(fd); return -1; } memcpy((char *)&stDestAddr.sin_addr, (char *)(pHost->h_addr), pHost->h_length); } else { memcpy((char *)&stDestAddr.sin_addr, (char *)&stHostAddr, sizeof(stHostAddr)); } printf("nPING %s(%s): %d bytes in ICMP packetsn", argv[1], inet_ntoa(stDestAddr.sin_addr), ICMP_DATA_LEN); /* 发送ICMP报文 */ sendIcmp(fd, &stDestAddr); /* 输出统计信息 */ showStatic(&g_stPktStatic); /* 关闭FD */ close(fd); return 0; }
|