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

文章链接: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;                
  

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Python 的原始套接字(raw sockets)对目标主机进行 ping 测试。具体实现可以使用 Python 内置的 socket 模块进行操作。以下是一个简单的示例代码: ```python import socket import struct import time def ping(host): # 构造 ICMP 报文 seq_num = 1 icmp_type = 8 # ping 请求 icmp_code = 0 icmp_checksum = 0 icmp_id = socket.getpid() & 0xFFFF # 可以自定义 # 构造 ICMP 报文 packet = struct.pack("!BBHHH", icmp_type, icmp_code, icmp_checksum, icmp_id, seq_num) cksum = calculate_checksum(packet) # 重新计算校验和 packet = struct.pack("!BBHHH", icmp_type, icmp_code, cksum, icmp_id, seq_num) # 建立原始套接字发送 ICMP 报文 sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) sock.sendto(packet, (host, 0)) # 接收并解析响应报文 sock.settimeout(5.0) try: data, addr = sock.recvfrom(1024) icmp_type, icmp_code, icmp_checksum, icmp_id, icmp_seq = struct.unpack("!BBHHH", data[20:28]) return True except socket.timeout: return False finally: sock.close() def calculate_checksum(data): n = len(data) % 2 sum = 0 for i in range(0, len(data)-n, 2): sum += (data[i] << 8) + data[i+1] if n: sum += (data[-1] << 8) sum = (sum >> 16) + (sum & 0xffff) sum += (sum >> 16) return ~sum & 0xffff if __name__ == "__main__": host = "www.google.com" # 目标主机 if ping(host): print(f"{host} is reachable") else: print(f"{host} is unreachable") ``` 注意,在 Windows 平台下,原始套接字需要以管理员权限运行 Python 解释器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值