如何开启原始套接字并发送自定义ping包(icmp)

大耗子 2020年04月02日 192次浏览

文章链接:https://codemouse.online/archives/2020-04-02182132

开启原始套接字

struct protoent *protocol;   
if( (protocol=getprotobyname("icmp"))==NULL)        
{       
    perror("getprotobyname");                
    exit(1);        
}        

if( (sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) )<0)       
{       
    perror("socket error");                
    exit(1);        
}  

提升权限

注意: linux中要开启套接字编程一定要使用root权限,否则会失败。因为我们是在替系统干事了。

setuid(getuid());  // 使用什么用户就得到什么用户的权限

获取主机信息

#include <netdb.h>
extern int h_errno;

struct hostent *gethostbyname(const char *name);

#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);

//type:  AF_INET,AF_INET6

struct hostent {
    char  *h_name;            /* official name of host */
    char **h_aliases;         /* alias list */
    int    h_addrtype;        /* host address type */
    int    h_length;          /* length of address */
    char **h_addr_list;       /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */

注:通过地址或者主机名字获取主机信息。

组织和解开icmp包

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/time.h>

int pack(int pack_no,int pid)
{       
	int i,packsize;        
	struct icmp *icmp;        
	struct timeval *tval;    
	
	icmp=(struct icmp*)sendpacket;        
	icmp->icmp_type=ICMP_ECHO;        
	icmp->icmp_code=0;        
	icmp->icmp_cksum=0;        
	icmp->icmp_seq=pack_no;        
	icmp->icmp_id=pid;        
	packsize=8+DATA_LEN;      
    // 在数据位可以放任意数据,此处放时间
	tval= (struct timeval *)icmp->icmp_data; 
	
	gettimeofday(tval,NULL);   
	// 获取校验和
	icmp->icmp_cksum=cal_chksum( (unsigned short *)icmp,packsize);         
	return packsize;
}

int unpack(char *buf,int len,int pid)
{       
	int i,iphdrlen;        
	struct ip *ip;        
	struct icmp *icmp;        
	struct timeval *tvsend;        
	double rtt;  
	
	ip=(struct ip *)buf;        
	iphdrlen=ip->ip_hl<<2;       
    
	icmp=(struct icmp *)(buf+iphdrlen);   
	
	len-=iphdrlen;               
	if( len<8)                     
	{       
		printf("ICMP packets\'s length is less than 8\n");                
		return -1;        
	}     
	
	if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )        
	{       
		tvsend=(struct timeval *)icmp->icmp_data;  
		// 获取的数据可以做任何操作,甚至可以在数据里面放暗号
        // 此处放的是时间

		tv_sub(&tvrecv,tvsend);             
		rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;                
		printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq, ip->ip_ttl,rtt);        
	}        
	else    
		return -1;
}

//crc32的校验计算方法,网上可以找得到
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;
}

发送数据

char sendpacket[PACKET_SIZE];

void send_packet(int sockfd,int pid, struct sockaddr_in dest_addr)
{       
	int packetsize;     
	           
    packetsize=pack(nsend,pid);                
    if( sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0  )                
    {       
        perror("sendto error");                        
        continue;                
    }                

}

接收数据

struct sockaddr_in from;

char recvpacket[PACKET_SIZE];
void recv_packet(int sockfd,int pid)
{       
	int n,fromlen;        
	extern int errno;           
	fromlen=sizeof(from);        
       
    if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen)) <0)                
    {       
        if(errno==EINTR)continue;                        
        perror("recvfrom error");                        
        continue;                
    }                          
    if(unpack(recvpacket,n,pid)==-1)
        continue;                
  

}