Linux用ICMP协议实现简单Ping网络监测功能

ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是 TCP/IP协议族 的一个子协议,用于在IP主机、 路由 器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP协议是一种面向无连接的协议,用于传输出错报告控制信息。它是一个非常重要的协议,它对于 网络 安全具有极其重要的意义。

折腾半天,原来ICMP也是TCP/IP其中一种协议.那么监测网络是否ping的通,就跟TCP协议差不多了。
步骤简单归纳为:1.绑定套接字,2.发送数据包 3.接收数据包 4.解析数据包
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#define PACKET_SIZE     4096
#define ERROR           0
#define SUCCESS         1

//效验算法(百度下有注释,但是还是看不太明白)
unsigned short cal_chksum(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;
}
// Ping函数
int ping( char *ips, int timeout)  
{      
    struct timeval *tval;        
    int maxfds = 0;  
    fd_set readfds;  
    
    struct sockaddr_in addr;  
    struct sockaddr_in from;  
    // 设定Ip信息  
    bzero(&addr,sizeof(addr));  
    addr.sin_family = AF_INET;  
    addr.sin_addr.s_addr = inet_addr(ips);  
    
    int sockfd;  
    // 取得socket  
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  
    if (sockfd < 0)  
    {  
        printf("ip:%s,socket error\n",ips);  
        return ERROR;  
    }  
    
    struct timeval timeo;  
    // 设定TimeOut时间  
    timeo.tv_sec = timeout / 1000;  
    timeo.tv_usec = timeout % 1000;  
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)) == -1)  
    {  
        printf("ip:%s,setsockopt error\n",ips);  
        return ERROR;  
    }  
    
    char sendpacket[PACKET_SIZE];  
    char recvpacket[PACKET_SIZE];  
    // 设定Ping包  
    memset(sendpacket, 0, sizeof(sendpacket));  
    
    pid_t pid;  
    // 取得PID,作为Ping的Sequence ID  
    pid=getpid();  
    
    struct ip *iph;  
    struct icmp *icmp;  
    
  
    icmp=(struct icmp*)sendpacket;  
    icmp->icmp_type=ICMP_ECHO;  //回显请求
    icmp->icmp_code=0;  
    icmp->icmp_cksum=0;  
    icmp->icmp_seq=0;  
    icmp->icmp_id=pid; 
    tval= (struct timeval *)icmp->icmp_data;  
    gettimeofday(tval,NULL);  
    icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,sizeof(struct icmp));  //校验
    
    int n;  
    // 发包  
    n = sendto(sockfd, (char *)&sendpacket, sizeof(struct icmp), 0, (struct sockaddr *)&addr, sizeof(addr));  
    if (n < 1)  
    {  
        printf("ip:%s,sendto error\n",ips);  
        return ERROR;  
    }  
    
    // 接受  
    // 由于可能接受到其他Ping的应答消息,所以这里要用循环  
    while(1)  
    {  
        // 设定TimeOut时间,这次才是真正起作用的  
        FD_ZERO(&readfds);  
        FD_SET(sockfd, &readfds);  
        maxfds = sockfd + 1;  
        n = select(maxfds, &readfds, NULL, NULL, &timeo);  
        if (n <= 0)  
        {  
            printf("ip:%s,Time out error\n",ips);  
            close(sockfd);  
            return ERROR;  
        }  
        
        // 接受  
        memset(recvpacket, 0, sizeof(recvpacket));  
        int fromlen = sizeof(from);  
        n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);  
        if (n < 1) {  
            break;  
        }  
        
     
        char *from_ip = (char *)inet_ntoa(from.sin_addr);  
            // 判断是否是自己Ping的回复  
        if (strcmp(from_ip,ips) != 0)  
        {  
            printf("NowPingip:%s Fromip:%s\nNowPingip is not same to Fromip,so ping wrong!\n",ips,from_ip);  
   			return ERROR;
        }  
        
        iph = (struct ip *)recvpacket;  
        
        icmp=(struct icmp *)(recvpacket + (iph->ip_hl<<2));  
        
        printf("ip:%s\n,icmp->icmp_type:%d\n,icmp->icmp_id:%d\n",ips,icmp->icmp_type,icmp->icmp_id);  
        // 判断Ping回复包的状态  
        if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid)   //ICMP_ECHOREPLY回显应答
        {  
            // 正常就退出循环  
            break;  
        }  
        else  
        {  
            // 否则继续等  
            continue;  
        }  
    }  
    
    int main()
{
	char cPing[16];
	printf("Please input ping IP:");
	scanf("%s",cPing);
	
	if(ping(cPing,10000))
	{
		printf("Ping succeed!\n");
	}
	else
	{
		printf("Ping wrong!\n");
	}
	
}

测试结果:
root@an-virtual-machine:~/wyz/test# ./testping
Please input ping IP:192.168.1.155
Nowip:192.168.1.155 Fromip:192.168.1.133
Nowip is not same to Fromip,so ping wrong!
Ping wrong!
root@an-virtual-machine:~/wyz/test# ./testping
Please input ping IP:192.168.1.188
ip:192.168.1.188
,icmp->icmp_type:0
,icmp->icmp_id:27865
Ping succeed!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值