对于IPv4报文,头部长这样:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
一般都不会附加特殊的选项信息,对于Header Length 就是5,ip头部的总长度也就是20个字节。
最近有需求需要附加一点额外的信息,先从ip的option入手看看能不能实现。
首先直接写代码测试这个头部分信息
有两种方式
当使用 setsockopt 设置 IP 选项时,将在该套接字上的所有 IP 数据报上发送指定的选项。这适用于 TCP、UDP 和原始 IP 套接字。要清除这些选项,请调用 setsockopt 并指定空指针作为第四个参数或值 0 作为第五个参数(长度)。
原始 IP 套接字设置 IP_HDRINCL 套接字选项,但是这个IP 选项好像某些情况下不适用。
刚开始不清楚这一点,折腾了好久,把原始套接字的代码贴上来
代码是网上修改别人的原始套接字发送ICMP的例子,这里面也有一些坑代码,找了好久没有找到。
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <time.h>
#include <iostream>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int IP_Header_min_lenght = 20;
/*
Ver 4
Hlen 4
1
Servicetype 8
1
Total length 16
2
Identifier 16
2
flag and frag offset 16
2
TimeToLive 8
1
Prorocol 8
CHECKSUM 16
SRC ADDR 32
DES ADDR 32
160/8=20
*/
#pragma pack(push,1)
typedef struct ip_hdr //定义IP首部
{
unsigned char h_verlen; //4位首部长度,4位IP版本号
unsigned char tos; //8位服务类型TOS
unsigned short total_len; //16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; //3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; //8位协议 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IPHEADER;
typedef struct tsd_hdr //定义TCP伪首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}PSDHEADER;
typedef struct tcp_hdr //定义TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列号
unsigned int th_ack; //32位确认号
unsigned char th_lenres; //4位首部长度/6位保留字
unsigned char th_flag; //6位标志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校验和
USHORT th_urp; //16位紧急数据偏移量
}TCPHEADER;
typedef struct icmp_hdr
{
unsigned char icmp_type; // 类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
unsigned short icmp_id; // ID号,设为PID
unsigned short icmp_sequence; // 序列号
unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR;
#pragma pack(pop)
unsigned short CheckSum(USHORT* buff, int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if (size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
bool SetTimeout(SOCKET sRaw, int nTime)
{
int ret = setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTime, sizeof(nTime));
return ret != SOCKET_ERROR;
}
double GetTickCountA()
{
__int64 Freq = 0;
__int64 Count = 0;
if (QueryPerformanceFrequency((LARGE_INTEGER*)&Freq) && Freq > 0 && QueryPerformanceCounter((LARGE_INTEGER*)&Count))
{
//乘以1000,把秒化为毫秒
return (double)Count / (double)Freq * 1000.0;
}
return 0.0;
}
int main()
{
char website_name[] = "10.19.11.221";// www.baidu.com www.google.com
char *DestIp;
double succ = 0;
double fail = 0;
int min_RTT = 999999999;
int max_RTT = -1;
int sum_RTT = 0;
DestIp = (char*)malloc(20 * sizeof(char));
//初始化WindowsSocketsAPI
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);//声明版本 2.2
WSAStartup(sockVersion, &wsaData);//启动
//获取IP地址
struct hostent *curr_hostent = gethostbyname(website_name);
DestIp = inet_ntoa(*(struct in_addr*)curr_hostent->h_addr_list[0]);
DestIp = (char *)"10.19.11.221";
printf("正在ping %s:[%s] \n", website_name, DestIp);
//printf("%s\n",curr_hostent->h_addr_list[0]);
//printf("%s\n",inet_ntoa(*(struct in_addr*)curr_hostent->h_addr_list[0]));
if (!curr_hostent) {
puts("Get IP address error!");
//system("pause");
// exit(0);
}
//创立套接字
SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//AF_INET=ipv4 SOCK_RAW ICMP协议
BOOL flag = true;
if (setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&flag, sizeof(flag)) == SOCKET_ERROR)
{
printf("setsockopt IP_HDRINCL error! ");
return false;
}
SetTimeout(sRaw, 1000);
// 设置目的地址
SOCKADDR_IN dest;
dest.sin_family = AF_INET;//ipv4地址家族
// dest.sin_port = htons(0);//将整型变量从主机字节顺序转变成网络字节顺序
dest.sin_addr.S_un.S_addr = inet_addr(DestIp);
// 创建ICMP数据包并填写
//ip 选项部分4字节
const int i_len_total = sizeof(IPHEADER) + 12 + sizeof(ICMP_HDR) + 32;//...
char buff[i_len_total];
IPHEADER* ipHeader = (IPHEADER*)buff;
//ipHeader->h_verlen = (4 << 4 | sizeof(ipHeader) / sizeof(unsigned long));
ipHeader->h_verlen = 0x40 + (sizeof(IPHEADER) / 4 + 3); //...
ipHeader->tos=0;
ipHeader->total_len = /*htons*/(i_len_total);
ipHeader->ident = 1;
ipHeader->frag_and_flags = 0;
ipHeader->ttl = 120;
ipHeader->proto = IPPROTO_ICMP;
ipHeader->checksum = 0;
ipHeader->sourceIP = inet_addr("10.18.11.223"); //src ip
ipHeader->destIP = inet_addr( "10.19.11.221" ); //dest ip
char optionPacket_2[] = {
'\x01', '\x01', '\x01', '\x01',
'\x83', '\x07', '\x08', '\xC0',
'\xA8', '\x7E', '\x80', '\x00'
};
int optionLength = 12;
memcpy(buff + sizeof(IPHEADER), optionPacket_2, optionLength);
//ipHeader->checksum = CheckSum((unsigned short*)ipHeader, sizeof(IPHEADER) /*+ 4*/);
ICMP_HDR* pIcmp = (ICMP_HDR*)(buff + sizeof(IPHEADER)+ 12);//...
pIcmp->icmp_type = 8; // 请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();
unsigned short nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
for (int o = 0; o < 9999999; o++)
{
int nRet;
pIcmp->icmp_checksum = 0;//为了后续checksum函数正常 需要先置位0
pIcmp->icmp_timestamp = GetTickCountA();
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_checksum = CheckSum((unsigned short*)pIcmp, sizeof(ICMP_HDR) + 32);
//发送包
nRet = sendto(sRaw, buff, i_len_total, 0, (SOCKADDR *)&dest, sizeof(dest));//发送
if (nRet == SOCKET_ERROR)
{
printf(" sendto() failed: %d \n", ::WSAGetLastError());
return -1;
}
//接收包
nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
if (nRet == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf(" Request timed out\n");
fail++;
continue;
}
fail++;
printf(" Failed. Error code: %d\n", WSAGetLastError());
return -1;
}
// 解析包
int nTick = GetTickCountA();
if (nRet < IP_Header_min_lenght + sizeof(ICMP_HDR))
{
printf(" Returned too few bytes from %s \n", inet_ntoa(from.sin_addr));
fail++;
}
// 接收包中包含IP头,所以加20得到ICMP头
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + IP_Header_min_lenght); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
if (pRecvIcmp->icmp_type != 0) // 回显
{
if (pRecvIcmp->icmp_type == 3) // 回显
{
printf(" Unreachable\n"); fail++;
}
else if (pRecvIcmp->icmp_type == 4) // 回显
{
printf(" Origin suppression \n"); fail++;
}
else if (pRecvIcmp->icmp_type == 5) // 回显
{
printf(" Redirect \n"); fail++;
}
else if (pRecvIcmp->icmp_type == 8) // 回显
{
printf(" Echo request\n"); fail++;
}
else if (pRecvIcmp->icmp_type == 9) // 回显
{
printf(" Router announcement\n"); fail++;
}
else if (pRecvIcmp->icmp_type == 10) // 回显
{
printf(" Router request \n"); fail++;
}
else if (pRecvIcmp->icmp_type == 11) // 回显
{
printf(" Time out \n"); fail++;
}
else if (pRecvIcmp->icmp_type == 17) // 回显
{
printf(" Address subnet request\n"); fail++;
}
else if (pRecvIcmp->icmp_type == 18) // 回显
{
printf(" Address subnet response\n"); fail++;
}
return -1;
}
succ++;
printf(" 来自 %s的回复: ", inet_ntoa(from.sin_addr));
printf(" 字节=%d bytes :", nRet, inet_ntoa(from.sin_addr));
printf(" 时间= %d ms", nTick - pRecvIcmp->icmp_timestamp);
printf(" TTL= %d \n", *(recvBuf + 8));
min_RTT = min(min_RTT, (int)(nTick - pRecvIcmp->icmp_timestamp));
max_RTT = max(max_RTT, (int)(nTick - pRecvIcmp->icmp_timestamp));
sum_RTT = sum_RTT + nTick - pRecvIcmp->icmp_timestamp;
Sleep(1000);//一秒
}
printf("数据包丢失率:%lf", fail * 100 / (succ + fail));
cout << "%" << endl;
printf("最小 RTT = %d ms \n", min_RTT);
printf("最大 RTT = %d ms\n", max_RTT);
printf("平均 RTT = %d ms\n", sum_RTT / 10);
return 0;
}
测试可以正常发送ip选项头
?
?但是这个头部能携带的信息有格式要求,并且信息有线,大部分软件都没有采用这个ip报文的option头部了,改为使用tcp、udp传输层协议的option字段了。所以这个ip option也就没有太大意义了。
以下是ip option的相关说明,翻译自RFC 791: Internet Protocol?第14页的内容
Options: variable
选项可能出现或不出现在数据报中。它们必须 ? ? 由所有 IP 模块(主机和网关)实现。可选 ? ? 的是它们在任何特定数据报中的传输,而不是它们的 ? ? 实现。
? ? 在某些环境中,所有数据报都可能需要安全选项 ? ? 。
? ? 选项字段的长度是可变的。可能有零个或多个 ? ? 选项。选项的格式有两种情况:
? ? ? 情况 1:选项类型的单个八位字节。
? ? ? 情况 2:一个选项类型八位字节、一个选项长度八位字节和 ? ? ? ? ? ? ? ?实际的选项数据八位字节。
? ? option-length octet 计算 option-type octet 和 ? ? option-length octet 以及 option-data octet。
? ? 选项类型八位字节被视为具有 3 个字段:
? ? ? 1 位复制标志、 ? ? ? 2 位选项类别、 ? ? ? 5 位选项编号。
? ? 复制标志表示该选项被复制到分片时 ? ? 的所有分片中。
? ? ? 0 = 未复制 ? ? ? 1 = 已复制
? ? 选项类别为:
? ? ? 0 = 控制 ? ? ? 1 = 保留供将来使用 ? ? ? 2 = 调试和测量 ? ? ? 3 = 保留供将来使用 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? 定义了以下 Internet 选项:
? ? ? 类别号长度说明 ? ? ? ----- ------ ------ -----------? ? ? ? ? 0 0 - 选项结束列表。 ? End of Option List 此选项仅占用1 个八位字节;它没有长度字节。 ? ? ? ? 0 1 - 无操作。Nop 此选项仅占用 1 个八位字节;它没有长度字节。 ? ? ? ? 0 2 11 安全。Security (S field) 用于携带与 DOD 兼容的安全、分隔、用户组 (TCC) 和处理限制代码要求。 ? ? ? ? 0 3 变量。松散源路由。 Loose Source and Record Route 用于根据源提供的信息路由 Internet 数据报 ? ? ? ? ? ? ? ? ? ? ? ? ? 。 ? ? ? ? 0 9 变量。严格的源路由。 ?Strict Source and Record Route 用于根据源提供的信息路由 Internet 数据报 ? ? ? ? ? ? ? ? ? ? ? ? ? 。 ? ? ? ? 0 7 变量。记录路由。Record Route 用于跟踪Internet 数据报采用的路由。 ? ? ? ? 0 8 4 流 ID。 Stream Identifier? 用于携带流标识符。 ? ? ? ? 2 4 变量。互联网时间戳。 Internet Timestamp
? ? 具体选项定义
? ? ? 选项列表结束
? ? ? ? +--------+? ? ? ? ? |00000000|? ? ? ? ? +--------+? ? ? ? ? ? Type=0
? ? ? ? 该选项表示选项列表的结束。 ? ? ? ? 根据Internet 标头长度,这可能 ? ? ? ? 与 Internet 标头的结尾不一致。这用于所有选项的结尾,而不是每个选项的结尾,并且仅 ? ? ? ? 在选项的结尾 ? ? ? ? 与 Internet 标头的结尾不一致 ? ? ? ? 时才需要使用。 ? ? ? ? 可能会因碎片或任何其他原因 ? ? ? ? 而被复制、引入或删除。
? ? ? 无操作
? ? ? ? +--------+? ? ? ? ? |00000001|? ? ? ? ? +--------+? ? ? ? ? ? Type=1
? ? ? ? 此选项可用于选项之间,例如,将 ? ? ? ? 后续选项的开头对齐在 32 位边界上。 ? ? ? ? 可能会因碎片或任何其他原因
? ? ? ? 而被复制、引入或删除。 ? ? ? 安全 ? ? ? ? 此选项为主机提供一种发送安全、 ? ? ? ? 隔离、处理限制和 TCC(封闭用户
? ? ? ? 组)参数。该选项的格式如下:
? ? ? ? ? +--------+--------+---//---+---//---+---/ /---+---//---+? ? ? ? ? ? |10000010|00001011|SSS SSS|CCC CCC|HHH HHH| 台积电 |? ? ? ? ? ? +--------+--------+---//---+---//---+---//---+---/ /---+ ? ? ? ? ? ?类型=130 长度=11
? ? ? ? 安全性(S 字段):16 位
? ? ? ? ? 指定 16 个安全级别之一(其中 8 个 ? ? ? ? ? 保留供将来使用)。
? ? ? ? ? ? 00000000 00000000 - 未分类 ? ? ? ? ? ? 11110001 00110101 - 机密 ? ? ? ? ? ? 01111000 10011010 - EFTO? ? ? ? ? ? ? 10111100 01001101 - MMMM? ? ? ? ? ? ? 01011110 00100110 - PROG ? ? ? ? ? ? 10101111 00010011 ?- 受限制 ? ? ? ? ? ? 11010111 10001000 ?- 秘密 ? ? ? ? ? ? 01101011 110001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010-? ? ? ? ? ? ? (保留供将来使用) ? ? ? ? ? ? 10011010 11110001 ?- (保留供将来使用) ? ? ? ? ? ? 01001101 01111101 ?- (保留供将来使用) ? ? ? ? ? ? 00100100 10111101 ?- (保留用于将来使用) ? ? ? ? ? ? 0010011 01011110 - (留作将来使用) ? ? ? ? ? ? 10001001 10101111 - (留作将来使用)? ? ? ? ? ? ? 11000100 11010110 - (留作将来使用) 11100010 01101011 - ( ? ? ? ? ? ? 留作将来使用) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? 隔间(C 字段):16 位当传输的信息 ? ? ? ? ? 没有隔开
? ? ? ? ? 时,使用全零值。隔间字段的其他值 ? ? ? ? ? 可以从国防情报局获得。
? ? ? ? 处理限制(H 字段):16 位
? ? ? ? ? 控制和释放标记的值是 ? ? ? ? ? 字母数字二合字母,并在国防 ? ? ? ? ? 情报局手册 DIAM 65-19,“标准安全 ? ? ? ? ? 标记”中定义。
? ? ? ? 传输控制码(TCC 字段):24 位
? ? ? ? ? 提供一种方法来隔离流量并定义 ? ? ? ? ? 订户之间的受控利益社区。TCC 值是 ? ? ? ? ? 三元组,可从 HQ DCA 代码 530
? ? ? ? 获得。必须在分段时复制。该选项 ? ? ? ? 在一个数据报中最多出现一次。
? ? ? 松源和记录路由
? ? ? ? +--------+--------+--------+---------//----- --+? ? ? ? ? |10000011| 长度 | 指针| 路由数据 |? ? ? ? ? +--------+--------+--------+---------//--------+ ? ? ? ? ?类型= 131
? ? ? ? 松散源和记录路由 (LSRR) 选项为 ? ? ? ? Internet 数据报的源提供路由 ? ? ? ? 网关在将 ? ? ? ? 数据报转发到目的地时使用的信息,并记录路由 ? ? ? ? 信息。
? ? ? ? 该选项以选项类型代码开头。第二个八位位组 ? ? ? ? 是选项长度,包括选项类型代码和 ? ? ? ? 长度八位位组、指针八位位组和长度为 3 位的路由 ? ? ? ? 数据。第三个八位字节是指向路由数据的指针, ? ? ? ? 指示开始下一个要 ? ? ? ? 处理的源地址的八位字节。指针是相对于这个选项的, ? ? ? ? 指针的最小合法值是4。
? ? ? ? 一个路由数据是由一系列互联网地址组成的。 ? ? ? ? 每个互联网地址是 32 位或 4 个八位字节。如果指针 ? ? ? ? 大于长度,则源路由为空(并且 ? ? ? ? 记录的路由已满),路由将基于 ? ? ? ? 目标地址字段。
? ? ? ? 如果已到达目的地址域中的地址且 ? ? ? ? 指针不大于长度, ? ? ? ? 则源路由中的下一个地址替换目的地址 ? ? ? ? 域中的地址,并且记录的路由地址替换源 ? ? ? ? 地址刚用过,指针加四。 ? ? ? ? 记录的路由地址是该数据报转发 ? ? ? ? 到的环境中已知
? ? ? ? 的互联网模块自己的互联网地址。
? ? ? ? 这个用记录的路由替换源路由的过程 ? ? ? ? (尽管它与 ? ? ? ? 用作源路由的顺序相反)意味着选项(以及整个 IP 标头 ? ? ? ? )保持恒定长度,如数据报 ? ? ? ? 通过互联网传输。
? ? ? ? 此选项是松散源路由,因为 ? ? ? ? 允许网关或主机 IP 使用任意数量的其他 ? ? ? ? 中间网关的任意路由到达路由中的下一个地址。
? ? ? ? 必须在碎片上复制。在一个数据报中最多出现一次 ? ? ? ? 。
? ? ? 严格的源和记录路由
? ? ? ? +--------+--------+--------+---------//------ --+ ? ? ? ? |10001001| 长度 | 指针| 路由数据 |? ? ? ? ? +--------+--------+--------+---------//--------+ ? ? ? ? ?类型= 137
? ? ? ? 严格的源和记录路由 (SSRR) 选项为 ? ? ? ? 互联网数据报的源提供了一种方法,以提供 ? ? ? ? 网关在将 ? ? ? ? 数据报转发到目的地时使用的路由信息??,并记录路由 ? ? ? ? 信息。
? ? ? ? 该选项以选项类型代码开头。第二个八位位组 ? ? ? ? 是选项长度,包括选项类型代码和 ? ? ? ? 长度八位位组、指针八位位组和长度为 3 位的路由 ? ? ? ? 数据。第三个八位字节是指向路由数据的指针 ? ? ? ? 指示开始下一个要 ? ? ? ? 处理的源地址的八位字节。指针是相对于这个选项的, ? ? ? ? 指针的最小合法值是4。
? ? ? ? 一个路由数据是由一系列互联网地址组成的。 ? ? ? ? 每个互联网地址是 32 位或 4 个八位字节。如果指针 ? ? ? ? 大于长度,则源路由为空(并且 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? 记录了完整的路由)并且路由将基于 ? ? ? ? 目标地址字段。
? ? ? ? 如果已经到达目的地址域中的地址并且 ? ? ? ? 指针不大于长度, ? ? ? ? 则源路由中的下一个地址替换目的地址 ? ? ? ? 域中的地址,记录的路由地址替换 ? ? ? ? 刚刚使用的源地址,指针增加了四。 ? ? ? ? 记录的路由地址是该数据报所在的环境中已知
? ? ? ? 的 Internet 模块自己的 Internet地址 ? ? ? ? 被转发。
? ? ? ? 这个用记录的路由替换源路由的过程 ? ? ? ? (尽管它与 ? ? ? ? 用作源路由的顺序相反)意味着选项(以及整个 IP 标头 ? ? ? ? )保持恒定长度,如数据报 ? ? ? ? 通过互联网传输。
? ? ? ? 此选项是严格源路由,因为网关或主机IP 必须 ? ? ? ? 仅通过下一个地址中指示的直连网络 ? ? ? ? 将数据报直接发送到源路由 ? ? ? ? 中的下一个地址,才能到达 ? ? ? ? 路由中指定的下一个网关或主机。
? ? ? ? 必须在碎片上复制。在一个数据报中最多出现一次 ? ? ? ? 。
? ? ? 记录路由
? ? ? ? +--------+--------+--------+---------//--------+? ? ? ? ? |00000111| 长度 | 指针| 路由数据 |? ? ? ? ? +--------+--------+--------+---------//--------+ ? ? ? ? ? 类型= 7
? ? ? ? 记录路由选项提供了一种记录 ? ? ? ? 互联网数据报路由的方法。
? ? ? ? 该选项以选项类型代码开头。第二个八位位组 ? ? ? ? 是选项长度,包括选项类型代码和 ? ? ? ? 长度八位位组、指针八位位组和长度为 3 位的路由 ? ? ? ? 数据。第三个八位字节是指向路由数据的指针 ? ? ? ? 指示开始存储路由 ? ? ? ? 地址的下一个区域的八位字节。指针是相对于这个选项的, ? ? ? ? 指针的最小合法值是4。
? ? ? ? 一条记录的路由是由一系列互联网地址组成的。 ? ? ? ? 每个互联网地址是 32 位或 4 个八位字节。如果指针为
? ? ? ? 长度大于长度,记录的路由数据区已满。 ? ? ? ? 始发主机必须将此选项与 ? ? ? ? 足够大的路由数据区域组成,以容纳所有预期的地址。选项的 ? ? ? ? 大小不会因添加地址而改变。路由数据区的 ? ? ? ? 初始内容必须为零。
? ? ? ? 当 Internet 模块路由数据报时,它会检查是否 ? ? ? ? 存在记录路由选项。如果是,则插入其 ? ? ? ? 在该 ? ? ? ? 数据报被转发到的环境中已知的自己的互联网地址 ? ? ? ? ,从指针指示的八位字节开始记录的路由,并将指针增加 ? ? ? ? 四。
? ? ? ? 如果路由数据区已满(指针超过 ? ? ? ? 长度),则转发数据报而不将地址 ? ? ? ? 插入记录的路由中。如果有一些空间但没有足够的 ? ? ? ? 空间来插入完整地址,则原始数据报被 ? ? ? ? 认为是错误的并被丢弃。在任何一种情况下, ? ? ? ? 都可以将 ICMP 参数问题消息发送到源 ? ? ? ? 主机 [ 3 ]。
? ? ? ? 不复制碎片,只进入第一个碎片。 ? ? ? ? 在一个数据报中最多出现一次。
? ? ? 流标识符
? ? ? ? +--------+--------+--------+--------+? ? ? ? ? |10001000|00000010| 流 ID |? ? ? ? ? +--------+--------+--------+--------+? ? ? ? ? ?Type=136 Length=4
? ? ? ? 该选项为 ? ? ? ? 通过不支持 ? ? ? ? 流概念的网络携带的 16 位 SATNET 流标识符。
? ? ? ? 必须在碎片上复制。在一个数据报中最多出现一次 ? ? ? ? 。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? Internet 时间戳
? ? ? ? +--------+--------+--------+--------+? ? ? ? ? |01000100| 长度 | 指针|oflw|flg|? ? ? ? ? +--------+--------+--------+--------+? ? ? ? ? | 网址 |? ? ? ? ? +--------+--------+--------+--------+? ? ? ? ? | 时间戳 |? ? ? ? ? +--------+--------+--------+--------+? ? ? ? ? | . |? ? ? ? ? ? ? ? ? ? ? ? ? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? .? ? ? ? ? Type = 68
? ? ? ? 选项长度是选项计数中的八位字节数 ? ? ? ? 类型、长度、指针和溢出/标志八位字节(最大 ? ? ? ? 长度 40)。
? ? ? ? 指针是从该 ? ? ? ? 选项开始到时间戳结束的八位字节数加一(即,它指向 ? ? ? ? 开始下一个时间戳空间的八位字节)。最小的合法值为 5。当指针 ? ? ? ? 大于长度 ? ? ? ? 时,时间戳区域已满。 ? ? ? ? 溢出 (oflw) [4 位] 是 ? ? ? ? 由于空间不足而无法注册时间戳的 IP 模块的数量。 ? ? ? ? 标志 (flg) [4 位] 值为 ? ? ? ? ? 0 - 仅时间戳,存储在连续的 32 位字中,
? ? ? ? ? 1 -- 每个时间戳都以 ? ? ? ? ? ? ? ?注册实体的互联网地址开头,
? ? ? ? ? 3 -- 互联网地址字段是预先指定的。IP模块只有在它自己的 ? ? ? ? ? ? ? ?地址与下一个指定的互联网地址 ? ? ? ? ? ? ? ?匹配时才注册它的时间戳。 ? ? ? ? Timestamp 是一个右对齐的 32 位时间戳, ? ? ? ? 从 UT 午夜开始以毫秒为单位。如果时间不能以 ? ? ? ? 毫秒为单位或不能提供相对于午夜 UT ? ? ? ? 的时间,则可以插入任何时间作为时间戳,前提是 ? ? ? ? 时间戳字段的高位设置为 1 以指示 ? ? ? ? 使用非标准值。
? ? ? ? 始发主机必须将此选项与 ? ? ? ? 足够大的时间戳数据区域组成,以保存所有 ? ? ? ? 预期的时间戳信息。选项的大小不会因为添加
? ? ? ? 时间戳。时间戳数据区的初始内容 ? ? ? ? 必须为零或互联网地址/零对。
? ? ? ? 如果时间戳数据区已满(指针超出 ? ? ? ? 长度),则转发数据报而不插入 ? ? ? ? 时间戳,但溢出计数加一。
? ? ? ? 如果有一些空间但没有足够的空间 ? ? ? ? 来插入完整的时间戳,或者溢出计数本身溢出,则 ? ? ? ? 原始数据报被认为是错误的并被丢弃。 ? ? ? ? 在任何一种情况下,都可以将 ICMP 参数问题消息发送到 ? ? ? ? 源主机 [ 3 ]。
? ? ? ? 分片时不会复制时间戳选项。它 ? ? ? ? 在第一个片段中携带。在一个数据报中最多出现一次 ? ? ? ? 。
? 填充:变量
? ? 互联网报头填充用于确保互联网 ? ? 报头以 32 位边界结束。填充为零。3.2 . 讨论 ? 协议的实施必须是稳健的。每个实现都 ? 必须期望与不同 ? 个人创建的其他实现互操作。虽然本规范的目标是明确的
? 关于协议,可能会有不同的 ? 解释。一般来说,一个实现必须 ? 在其发送行为上是保守的,而在其接收行为上是自由的。也就是说 ? ,它必须小心发送格式良好的数据报,但必须接受 ? 它可以解释的任何数据报(例如,不反对 ? 含义仍然清楚的技术错误)。
? 基本的互联网服务是面向数据报的,并在网关处提供数据报的分段,并 ? 在目标主机中的目标互联网协议模块 ? 处进行重组。 ? 当然,网络内数据报的分段和重组 ? 或者通过网络网关之间的私有协议也是 ? 允许的,因为这对互联网协议和 ? 更高级别的协议是透明的。这种透明类型的分片和 ? 重组被称为“网络相关”(或内联网)分片 ? ,这里不再进一步讨论。
? Internet 地址将源和目标区分为主机 ? 级别,并提供协议字段。假设每个 ? 协议将提供 ? 主机内所需的任何多路复用。 ?
--
我需求需要用到的安全性相关的要求目前已经于新的rfc文档中说明已经弃用了,不确定现在的某些交换机路由器等网络设备会不会丢弃这些带有option字段的数据;
对于ipv6更是没有这个选项字段了,取而代之的是
所以也不会采用这种方案。
其他更多得信息可以参考
RFC 791: Internet Protocol https://www.rfc-editor.org/rfc/rfc791IPv4 Options_安静呆一会儿的博客-CSDN博客_ipv4 optionIPv4 allows up to 40 bytes of options to follow the fixed 20-byte header. Although 10 different options are defined, the most commonly used is the source route option. Accessto these options is throhttps://blog.csdn.net/u014211079/article/details/35987367?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7.pc_relevant_paycolumn_v3&utm_relevant_index=11
这个是一个另外的例子代码https://github.com/50u1w4y/50u1w4y.github.io/blob/d3ade0b17a0be3b7df84be2d96a4bd15b5c6c0dd/site/recurrence/code/CVE-2021-24074/test.cpp
|