Ping命令是ICMP协议的一个使用范例。Ping命令主要是用于测试一台主机和另外一台主机之间的网络连通性。主要的过程就是Ping程序通过发送回显请求报文,然后接收远程主机的回显应答报文,通过分析回显应答报文就可知道两台主机的网络连通性。
ICMP数据报首部如下
| 类型(0或8) | 代码(0) | 校验和 |
| 标识符 | 序号 |
| 选项数据 |
实现ping命令主要就是自己构造ICMP回显请求报文,通过套接字发送到目的主机,然后接收目的主机的ICMP回显应答报文,通过解析回显应答报文就知道是否可以到达远程主机了!
下面来说一下分析一下ICMP报文首部各个字段的具体含义,只有知道了具体的含义才能知道怎么来构造自己的ICMP报文:
ICMP回显请求报文和回显应答报文都属于查询类报文的一种。类型字段指名了该报文是回显请求报文还是回显应答报文:0代表回显应答报文,8代表回显请求报文;代码字段在这两种报文的情况下都是0;校验和是重点,应该无效的报文是不能利用的,所以,通过校验和我们可以获取有效报文再进行报文分析;标识符字段一般的做法都是设置为进程的ID号,这样简洁明了,而且使得我们可以在一台主机上运行多个ping命令实例,不会发生冲突。序号字段从0开始,每发送一次新的会显请求就会增加1.数据选项我们只需注意,回显请求报文和回显应答报文该字段设置成一样即可。
下面看看ICMP在linux下有关数据结构的标示方式:定义可查看
http://freebsd.active-venture.com/FreeBSD-srctree/newsrc/netinet/ip_icmp.h.html
首先看看回显请求和回显应答的命令定义:
#define ICMP_ECHOREPLY 0 /* echo reply */
#define ICMP_ECHO 8 /* echo service */
再来看看icmp的数据结构:
/*
* Structure of an icmp header.
*/
struct icmp {
u_char icmp_type; /* type of message, see below */
u_char icmp_code; /* type sub code */
u_short icmp_cksum; /* ones complement cksum of struct */
union {
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
struct ih_idseq {
n_short icd_id;
n_short icd_seq;
} ih_idseq;
int ih_void;
/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
struct ih_pmtu {
n_short ipm_void;
n_short ipm_nextmtu;
} ih_pmtu;
struct ih_rtradv {
u_char irt_num_addrs;
u_char 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 id_ts {
n_time its_otime;
n_time its_rtime;
n_time its_ttime;
} id_ts;
struct id_ip {
struct ip idi_ip;
/* options and then 64 bits of data */
} id_ip;
struct icmp_ra_addr id_radv;
u_int32_t id_mask;
char 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
};
我们可以比较容易的看到类型、代码和校验和这些字段。接下来是一个联合的数据类型,里面的数据项比较多,可以看到序号之类的字段,这样看起来比较晕,型号内核为我们定义了下面的一些宏定义,使得我们只需要关注自己感兴趣的部分,例如:#define icmp_id icmp_hun.ih_idseq.icd_id 则是ICMP数据报首部的标识符;
#define icmp_seq icmp_hun.ih_idseq.icd_seq 则是我们前面提到的序号字段;
#define icmp_data icmp_dun.id_data 则是数据字段,可设置为发送数据报的时间。
总结:我们仅仅需要设置这几个字段就好。
icmp->icmp_type
icmp->icmp_code
icmp->icmp_cksum
icmp->icmp_id
icmp->icmp_seq
icmp ->icmp_data;