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