红联Linux门户
Linux帮助

Linux C实现最简单的ICMP_ECHO请求报文发送

发布时间:2016-11-15 10:38:16来源:linux网站作者:_nMaple_
弄了两天,终于搞定了!把最简单的icmp报文发送实现了。本程序在Linux环境编写,使用原始套接字。
 
实现步骤:
1、得到protocol实体(protoent,声明于<netdb.h>);
2、初始化地址结构(sockaddr_in,声明于<netinet/in.h>);
3、创建套接字(socket(),声明于<sys/socket.h>,参数声明于<sys/types.h>);
4、更改socket选项,更改发送缓冲区大小(setsockopt(),声明于<sys/socket.h><sys/types.h>);
5、发送报文(sendto(),声明于<sys/types.h><sys/socket.h>);
6、关闭socket描述符(close(),声明于<unistd.h>);
 
代码:
#include <unistd.h>  
#include <sys/types.h>  
#include <stdio.h>  
#include <string.h>  
#include <stdlib.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <netdb.h>  
#include <netinet/ip_icmp.h>  
#include <netinet/ip.h>  
#include <arpa/inet.h>  
typedef unsigned char byte;  
typedef unsigned long ulong;  
typedef unsigned short ushort; 
const char* proto_name  
="icmp"; 
const char* ip_str  
="10.14.4.167";
#   define SEND_BUFF1024
#   define CK_NULL(___ent,___str)   ({  \  
if(___ent==NULL)printf(___str); \  
})
#   define PRT_ADDR_TEST(___addr)   ({  \  
printf("direction ip is:%d.%d.%d.%d\r\n",   \  
(___addr&0x000000ff),   \  
(___addr&0x0000ff00)>>8,  \  
(___addr&0x00ff0000)>>16, \  
(___addr&0xff000000)>>24  \  
);  \  
})
byte  
send_buffer[SEND_BUFF];
// declare function  
ushort  
icmp_cal_cksum(byte*,int);
void  
icmp_create(){  
struct icmp* icmph=  
(struct icmp*)send_buffer;  
int pid,  
i,  
data_len;  
pid=getuid();  
data_len=SEND_BUFF-8;   // send_length sub icmp header_length  
icmph->icmp_type=ICMP_ECHO;  
icmph->icmp_code=0;  
icmph->icmp_cksum=0;  
icmph->icmp_seq=0;  
icmph->icmp_id=pid;  
for(i=0;i<data_len;i++)  
icmph->icmp_data[i]=i;  
icmph->icmp_cksum=icmp_cal_cksum((byte*)icmph,data_len);  
}
ushort  
icmp_cal_cksum(byte* _data,int _data_len){  
int sum=0;  
int odd=_data_len&0x01;  
ushort* value=(ushort*)_data;  
while(_data_len & 0xfffe){  
sum+=*(ushort*)_data;  
_data+=2;  
_data_len-=2;  
}  
if(odd){  
ushort tmp=((*_data)<<8)&0xff00;  
sum+=tmp;  
}  
sum=(sum>>16)+(sum&0xffff);  
sum+=(sum>>16);
return ~sum;  
}
int  
main(int argc,char** argv){  
// socket fd  
int sockfd;  
// return err code  
int err_code;  
// send data length  
int snd_len;  
// long addr  
ulong addr_l;  
// protocol entity  
struct protoent* protocol;  
// socket send buffer length  
int send_buff=SEND_BUFF;  
// protocol socket address   
struct sockaddr_in dest_addr;  
// get protocol entity   
protocol=getprotobyname(proto_name);  
CK_NULL(protocol,"error! cannot get protocol entity!!!\r\n");  
// convert ip str to u_long  
addr_l=inet_addr(ip_str);  
if(addr_l==INADDR_NONE){  
printf("error! cannot convert ip str to long addr!!!\r\n");  
return -1;  
}  
addr_l=htonl(addr_l);  
PRT_ADDR_TEST(addr_l);  
// set memory 0  
bzero((char*)&dest_addr,sizeof(dest_addr));  
// full sockaddr_in struct  
memcpy((char*)&dest_addr.sin_addr,&addr_l,sizeof(addr_l));  
sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto);  
if(sockfd<0){  
printf("error!cannot create sockfd!\r\n");  
return -1;  
}  
err_code=setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&send_buff,sizeof(send_buff));  
icmp_create();  
snd_len=sendto(sockfd,send_buffer,send_buff,0,(struct sockaddr*)&dest_addr,sizeof(dest_addr));  
if(snd_len<send_buff){  
printf("error!send data length is not enougth!\r\n");  
return -1;  
}  
printf("data has sended length=%d\r\n",snd_len);  
close(sockfd);  
return 0; 
}
 
注:创建原始套接字时应使用管理员权限,否则会创建失败!好像也有代码中获取权限的方法,但我没有去查相关资料。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/26017.html