您的位置:首页 > 运维架构 > Linux

一步一步实现Linux下Ping命令

2012-03-14 10:28 369 查看
ICMP协议是Internet控制报文协议,通常被认为是IP层的组成部分。它传递差错报文以及其它需要注意的信息。ICMP协议通常被IP层或更高层协议使用。

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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: