icmp报文带时间戳怎么设置
时间: 2025-07-22 10:15:59 浏览: 4
<think>我们正在讨论如何在ICMP报文中添加或设置时间戳。根据引用[1]和引用[2],ICMP时间戳请求和应答报文的结构已经定义好了。在ICMP时间戳请求报文中,类型值为13,代码值为0;在应答报文中,类型值为14,代码值为0。时间戳报文包含三个时间戳字段:发起时间戳(OriginateTimestamp)、接收时间戳(ReceiveTimestamp)和发送时间戳(TransmitTimestamp)。要在ICMP报文中设置时间戳,需要构建一个ICMP时间戳请求报文。步骤如下:1.创建ICMP报文头部:设置类型为13(请求),代码为0,校验和初始为0。2.添加三个时间戳字段:每个时间戳占4字节(32位),表示自UTC午夜起计的毫秒数。-发起时间戳(OriginateTimestamp):由发送方在发送请求前填写当前时间。-接收时间戳(ReceiveTimestamp):由接收方在收到请求时填写,发送请求时设为0。-发送时间戳(Transmit Timestamp):由接收方在发送应答时填写,发送请求时设为0。3.计算校验和:覆盖整个ICMP报文(包括头部和数据部分,即三个时间戳)。4.发送该报文。注意:在请求报文中,只有发起时间戳是有效的,其他两个时间戳在请求报文中通常设为0。引用[1]中提到可以使用netwox工具的81号模块来构建时间戳请求。但如果我们自己编程实现,可以参考以下步骤(以C语言为例):步骤:a.创建原始套接字(SOCK_RAW)。b.构建ICMP报文(包括类型、代码、校验和以及三个时间戳)。c.计算校验和。d.发送到目标主机。下面是一个简单的示例代码框架(注意:需要root权限):```c#include<stdio.h>#include <stdlib.h>
#include<string.h>#include <sys/socket.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>#include <arpa/inet.h>#include <time.h>//计算校验和函数unsignedshort checksum(void*b, intlen){unsigned short*buf= b;unsigned intsum =0;unsigned shortresult;for(sum=0;len >1; len-=2)sum+= *buf++;if(len==1)sum+= *(unsigned char*)buf;sum =(sum>>16)+ (sum &0xFFFF);sum +=(sum>>16);result= ~sum;returnresult;}
intmain(intargc,char *argv[]){if (argc!=2){printf("Usage:%s<destinationIP>\n", argv[0]);return1;}int sockfd;struct sockaddr_indest_addr;char packet[sizeof(struct icmphdr) +3*sizeof(uint32_t)]; //ICMP头+三个时间戳//创建原始套接字sockfd =socket(AF_INET,SOCK_RAW, IPPROTO_ICMP);if(sockfd<0){perror("socket");return1;}//设置目标地址dest_addr.sin_family =AF_INET;dest_addr.sin_addr.s_addr =inet_addr(argv[1]);memset(&dest_addr.sin_zero,0,sizeof(dest_addr.sin_zero));//构建ICMP报文struct icmphdr*icmp =(structicmphdr *)packet;icmp->type=13;//时间戳请求icmp->code =0;icmp->checksum =0; //先置0,后面计算icmp->un.echo.id= getpid();//可以用进程ID作为标识符icmp->un.echo.sequence=0;//序列号//三个时间戳字段(紧跟在ICMP头部后面)uint32_t *timestamps= (uint32_t *)(packet +sizeof(structicmphdr));//获取当前时间(自UTC午夜起计的毫秒数)structtimevaltv;gettimeofday(&tv,NULL);uint32_tcurrent_time= (tv.tv_sec%86400) *1000 +tv.tv_usec/1000;//设置发起时间戳timestamps[0]= htonl(current_time); //网络字节序//接收和发送时间戳在请求中设为0timestamps[1]=0;timestamps[2]=0;//计算校验和(覆盖整个ICMP报文,包括时间戳)icmp->checksum= checksum(packet,sizeof(packet));//发送报文if(sendto(sockfd, packet, sizeof(packet),0,(structsockaddr*)&dest_addr, sizeof(dest_addr))<0){perror("sendto");close(sockfd);return1;}printf("ICMPTimestampRequest sentto %s\n", argv[1]);close(sockfd);return0;}
```注意:上述代码仅用于发送请求。接收应答需要另外编写代码,解析ICMP应答报文(类型14)。在应答报文中,三个时间戳都会被填充。另外,时间戳的计算:自UTC午夜起计的毫秒数。我们通过gettimeofday获取当前时间,然后计算从当天UTC午夜到现在的毫秒数(一天有86400秒)。引用[2]中提到,很多主机在应答时会把接收时间戳和发送时间戳设为相同的值。总结:在ICMP报文中设置时间戳,主要是构建一个类型为13的ICMP报文,并在报文数据部分填充三个时间戳字段(发起时间戳为当前时间,其他两个在请求中设为0)。注意:由于ICMP时间戳请求和应答在实际网络中并不常用,且很多主机和路由器可能不响应此类请求,因此实际使用中可能会遇到无响应的情况。如果需要更完整的实现,还需要编写接收应答的代码,并解析时间戳。相关问题:1.如何解析ICMP时间戳应答报文?2.为什么ICMP时间戳请求在实际网络中不常用?3.除了ICMP时间戳,还有哪些协议或方法可以获取网络时间同步?</think>### 在ICMP报文中设置时间戳的方法
要在ICMP报文中添加时间戳,需要构建**ICMP时间戳请求报文**(类型13)或**应答报文**(类型14)。以下是具体实现步骤和技术细节:
#### 1. **ICMP时间戳报文结构**
ICMP时间戳报文在标准ICMP头部后包含三个32位时间戳字段:
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Originate Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receive Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transmit Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
```
- **类型(Type)**:
- 13:时间戳请求
- 14:时间戳应答
- **时间戳单位**:自UTC午夜起计的毫秒数(32位无符号整数)
- **时间戳字段**:
- Originate Timestamp:请求方发送时间
- Receive Timestamp:接收方收到时间
- Transmit Timestamp:接收方发送应答时间
#### 2. **手动构建报文(C语言示例)**
```c
#include <netinet/ip_icmp.h>
// 构建时间戳请求报文
void build_icmp_timestamp_request(struct icmp *packet) {
packet->icmp_type = 13; // 时间戳请求类型
packet->icmp_code = 0;
packet->icmp_cksum = 0; // 先置0,后计算校验和
// 设置时间戳(单位:毫秒)
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t timestamp_ms = (tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000;
// 网络字节序转换
packet->icmp_otime = htonl(timestamp_ms); // 发起时间戳
packet->icmp_rtime = 0; // 接收时间戳(请求时设为0)
packet->icmp_ttime = 0; // 发送时间戳(请求时设为0)
// 计算校验和
packet->icmp_cksum = in_cksum((u_short*)packet, sizeof(struct icmp));
}
```
#### 3. **使用工具实现**
- **netwox工具(模块81)**:
```bash
# 向192.168.1.100发送时间戳请求
netwox 81 --ip4-dst 192.168.1.100
```
- **ping命令扩展**(需自定义实现):
```bash
# 自定义ping实现(需编程)
./custom_ping -T 192.168.1.100
```
#### 4. **时间戳处理要点**
1. **时间获取**:
```c
struct timeval tv;
gettimeofday(&tv, NULL); // 获取微秒级时间
uint32_t timestamp_ms = (tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000;
```
2. **字节序转换**:
```c
packet->icmp_otime = htonl(timestamp_ms); // 主机序转网络序
```
3. **应答处理**:
- 接收方需在`Receive Timestamp`记录接收时间
- 在`Transmit Timestamp`记录发送应答时间
- 保持`Originate Timestamp`不变[^2]
#### 5. **注意事项**
1. **系统权限**:
- 创建原始套接字需要`CAP_NET_RAW`能力(通常需root权限)
```c
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
```
2. **防火墙设置**:
- 确保目标主机允许ICMP类型13/14通过:
```bash
iptables -A INPUT -p icmp --icmp-type timestamp-request -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type timestamp-reply -j ACCEPT
```
3. **精度限制**:
- 理论精度:毫秒级(1ms)
- 实际精度受系统时钟抖动影响,通常误差在1-10ms[^1]
> **关键点**:时间戳计算基于UTC午夜(00:00)的毫秒数,需确保发送方和接收方的时钟同步(如使用NTP),否则时间差值无意义[^2]。
阅读全文
相关推荐

















