struct icmp
{
u_int8_t icmp_type;
u_int8_t icmp_code;
u_int16_t icmp_cksum;
union
{
u_char ih_pptr;
struct in_addr ih_gwaddr;
struct ih_idseq
{
u_int16_t icd_id;
u_int16_t icd_seq;
} ih_idseq;
u_int32_t ih_void;
struct ih_pmtu
{
u_int16_t ipm_void;
u_int16_t ipm_nextmtu;
} ih_pmtu;
struct ih_rtradv
{
u_int8_t irt_num_addrs;
u_int8_t irt_wpa;
u_int16_t irt_lifetime;
} ih_rtradv;
} icmp_hun;
#define icmp_pptr icmp_hun.ih_pptr
#define icmp_gwaddr icmp_hun.ih_gwaddr
#define icmp_id icmp_hun.ih_idseq.icd_id
#define icmp_seq icmp_hun.ih_idseq.icd_seq
#define icmp_void icmp_hun.ih_void
#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
union
{
struct
{
u_int32_t its_otime;
u_int32_t its_rtime;
u_int32_t its_ttime;
} id_ts;
struct
{
struct ip idi_ip;
} id_ip;
struct icmp_ra_addr id_radv;
u_int32_t id_mask;
u_int8_t id_data[1];
} icmp_dun;
#define icmp_otime icmp_dun.id_ts.its_otime
#define icmp_rtime icmp_dun.id_ts.its_rtime
#define icmp_ttime icmp_dun.id_ts.its_ttime
#define icmp_ip icmp_dun.id_ip.idi_ip
#define icmp_radv icmp_dun.id_radv
#define icmp_mask icmp_dun.id_mask
#define icmp_data icmp_dun.id_data
};
使用宏定義令表達更簡潔,其中ICMP報頭為8位元組,資料報長度最大為64K位元組。
校驗和算法――這一算法稱為網際校驗和算法,把被校驗的資料16位進行累加,然後取反碼,若資料位元組長度為奇數,則資料尾部補一個位元組的0以湊成偶數。此算法适用于IPv4、ICMPv4、IGMPV4、ICMPv6、UDP和TCP校驗和,更詳細的資訊請參考RFC1071,校驗和字段為上述ICMP資料結構的icmp_cksum變量。
辨別符――用于唯一辨別ICMP封包, 為上述ICMP資料結構的icmp_id宏所指的變量。
順序号――ping指令的icmp_seq便由這裡讀出,代表ICMP封包的發送順序,為上述ICMP資料結構的icmp_seq宏所指的變量。
ICMP資料報
Ping指令中需要顯示的資訊,包括icmp_seq和ttl都已有實作的辦法,但還缺rtt往返時間。為了實作這一功能,可利用ICMP資料報攜帶一個時間戳。使用以下函數生成時間戳:
#include
int gettimeofday(struct timeval *tp,void *tzp)
其中timeval結構如下:
struct timeval{
long tv_sec;
long tv_usec;
}
其中tv_sec為秒數,tv_usec微秒數。在發送和接收封包時由gettimeofday分别生成兩個timeval結構,兩者之差即為往返時間,即ICMP封包發送與接收的時間差,而timeval結構由ICMP資料報攜帶,tzp指針表示時區,一般都不使用,賦NULL值。
資料統計
系統自帶的ping指令當它接送完所有ICMP封包後,會對所有發送和所有接收的ICMP封包進行統計,進而計算ICMP封包丢失的比率。為達此目的,定義兩個全局變量:接收計數器和發送計數器,用于記錄ICMP封包接受和發送數目。丢失數目=發送總數-接收總數,丢失比率=丢失數目/發送總數。
現給出模拟Ping程式功能的代碼如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int sockfd,datalen=56;
int nsend=0,nreceived=0;
struct sockaddr_in dest_addr;
pid_t pid;
struct sockaddr_in from;
struct timeval tvrecv;
void statistics(int signo);
unsigned short cal_chksum(unsigned short *addr,int len);
int pack(int pack_no);
void send_packet(void);
void recv_packet(void);
int unpack(char *buf,int len);
void tv_sub(struct timeval *out,struct timeval *in);
void statistics(int signo)
{ printf("\n--------------------PING statistics-------------------\n");
printf("%d packets transmitted, %d received , %%%d lost\n",nsend,nreceived,
(nsend-nreceived)/nsend*100);
close(sockfd);
exit(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;
}