作为从事网络工作的人来说,了解ping的一些知识是非常有必要的,因此,趁着最近的功夫,借着网络上的一些前辈的博客,自己也动手写了一个简单的ping命令实现,主要参考的博主文章链接如下:
https://www.cnblogs.com/skyfsm/p/6348040.html?utm_source=itdadao&utm_medium=referral
由于我们仅仅只是写一个ping的应用程序,所以其实也没有那么困难,但是还是需要有一些基本知识的了解,例如,ping时候基于icmp协议实现的,那起码要了解基本的icmp这种协议吧,不然连包都不会组就尴尬了,除此之外,包的收发其实与其他收发都类似,无非就是send/recv的过程,具体细节具体分析。
为了是文章更丰满,这里还是简单说一下ICMP协议,ICMP是位于网络层的协议,是为网关和目标主机提供一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发送方,因此就存在差错报文从差错点发回源主机的流程,这就可能要经过多个子网,牵涉到路由选择的问题,因此ICMP报文基于IP协议来进行转发,所以ICMP数据包的发送需要进行两级封装,其报文构造大体如下图所示:
IP head一般是20个字节,其头部结构如下图所示:
ICMP head一般是8个字节,其头部结构如下图所示:
ICMP报文种类大体分为两类,询问报文和差错报文,我们通常ping的时候发送和回应的就是询问报文类,下面表格列出了几个常见的ICMP报文type
通过表格可以了解,当我们ping的时候,发送的icmp报文,type应该是8,而回复的icmp报文,type应该是0,这个在后面写ping的时候需要我们注意了。
下面就开始详细介绍ping代码了。
ping代码详解
(1)ICMP包的组装
/**
* para: @icmphdr --- icmp报文的头部字段信息
* @seq --- icmp头部字段中的seq序号,icmp报文的请求和应答这个字段要一致
* @length --- icmp报文长度,不包括ip头
* func:icmp报文头部信息填充,需要注意,icmp_cksum最开始一定要先赋值0
**/
void icmp_pack(struct icmp *icmphdr, int seq, int length)
{
struct timeval *tvstart;
icmphdr->icmp_type = ICMP_REQUEST;
icmphdr->icmp_code = 0;
icmphdr->icmp_cksum = 0;
icmphdr->icmp_seq = seq;
icmphdr->icmp_id = pid & 0xffff;
tvstart = (struct timeval *)icmphdr->icmp_data;
gettimeofday(tvstart, NULL);
icmphdr->icmp_cksum = cal_chksum((unsigned short *)icmphdr, length);
}
这里解释一下个字段的含义:
icmp_type: 就是上面介绍icmp报文时的type字段,指明该报文的类型。
icmp_code: code是子类型的意思,因为每个type下面可能还细分了不同的小类型,就由这个code标识。
icmp_cksum: icmp报文校验值,必须先赋值为0,后面再计算填充,关于校验的算法后面会介绍。
icmp_seq: