要模拟实现ping命令,就需要对ICMP协议有所了解:
ICMP:Internet控制报文协议,它是TCP/IP协议族中的一个子协议,用于在IP主机,路由之间传递信息的协议。
传输的信息包括:
1.目的不可达消息
2.超时消息
3.重定向消息
4.时间戳请求和时间戳响应消息
5.回显请求和回显响应消息。
ping命令 的机制就是回显请求和回显应答消息,具体是向网络上另一个主机上发送ICMP报文,如果指定的主机得到了这个报文,就将报文原封不动的发送回发送者。
ICMP报文格式:
类型:回显请求报文其中类型为0,代码为0
代码:回显应答报文其中类型为8,代码为0
校验和:包括数据在内整个ICMP协议数据包校验和
标识符:用于文艺标识ICMP报文,Linux中使用进程ID
序列号:报文的序列号
数据:ping中将发送报文的时间戳放入数据字段
通过下面的代码可以打印出IP协议头部的格式:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>
int main() {
int sfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if(sfd < 0)
{
perror("socket");
return 1;
}
int opt = 1;
setsockopt(sfd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt));
char buf[1500];
while(1)
{
memset(buf, 0x00, sizeof(buf));
int ret = read(sfd, buf, 1500);
if(ret <= 0)
{
break;
}
struct iphdr* pip = (struct iphdr*)(buf);
struct in_addr ad;
ad.s_addr = pip->saddr;
//printf("protocol: %hhd, %s <-----> ", pip->protocol, inet_ntoa(ad));
printf("数据报为%s <===>",inet_ntoa(ad));
ad.s_addr = pip->daddr;
printf("%s\n", inet_ntoa(ad));
}
return 0;
}
打印出信息如下:
准备工作就绪,先看看系统的ping长得样子:
首先介绍几个函数:
1.得到主机的时间,精确到毫秒
int gettimeofday(struct timeval *tv, struct timezone *tz);//获取的时间为微秒级的,big存放在tv中。
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};这是一个线程安全的函数。
2.类似DNS的域名解析,将输入的域名解析成ip地址
struct hostent *gethostbyname(const char *name); //函数返回给定主机名的hostent类型的结构。
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
3.校验和:
校验算法可以分成两步来实现。
首先在发送端,有以下三步:
1.把校验和字段置为 0。
2.对需要校验的数据看成以 16bit 为单位的数字组成,依次进行二进制求和。
3.将上一步的求和结果取反,存入校验和字段。
其次在接收端,也有相应的三步:
1.对需要校验的数据看成以 16bit 为单位的数字组成,依次进行二进制求和,包括校验和字段。
2.将上一步的求和结果取反
3.判断最终结果是否为 0。如果为 0,说明校验和正确。如果不为 0,则协议栈会丢掉接收到的数据。
unsigned short chksum(unsigned short* addr, int len) //校验和
{
unsigned int ret = 0;
while(len > 1)
{
ret += *addr++;
len -= 2;
}
if(len == 1)
{
ret += *(unsigned char*)addr;
}
ret = (ret >> 16) + (ret & 0xffff);
ret += (ret >> 16);
return (unsigned short)~ret;
}
万事具备:完整代码链接
代码执行结果:
至此,整个ping命令就全部模仿出来了,ping命令基本的结构都有了。
来源:CSDN
作者:CZF_csdn
链接:https://blog.csdn.net/bian_cheng_ru_men/article/details/81476998