您的位置:首页 > 理论基础 > 计算机网络

Linux 网络协议栈开发基础篇(一)—— TCP/IP协议报文格式解析

2018-01-25 20:57 417 查看
一、传输层报文

1、TCP数据包的头





typedef struct _TCP_HEADER {
USHORT nSourPort ;   // 源端口号16bit
USHORT nDestPort ;   // 目的端口号16bit
UINT nSequNum ;   // 序列号32bit
UINT nAcknowledgeNum ; // 确认号32bit
USHORT nHLenAndFlag ;  // 前4位:TCP头长度;中6位:保留;后6位:标志位16bit
USHORT nWindowSize ;  // 窗口大小16bit
USHORT nCheckSum ;   // 检验和16bit
USHORT nrgentPointer ;  // 紧急数据偏移量16bit
} TCP_HEADER, *PTCP_HEADER ;

2、UDP数据包的头





typedef struct _UDP_HEADER {
USHORT nSourPort ;   // 源端口号16bit
USHORT nDestPort ;   // 目的端口号16bit
USHORT nLength ;   // 数据包长度16bit
USHORT nCheckSum ;   // 校验和16bit
} UDP_HEADER, *PUDP_HEADER ;

进入协议栈的过程:(从协议栈出来刚好相反)

二、网络层报文

1、IP头

     IP数据包也叫IP报文分组,传输在ISO网络7层结构中的网络层,它由IP报文头和IP报文用户数据组成,IP报文头的长度一般在20到60个字节之间,而一个IP分组的最大长度则不能超过65535个字节。 

    下图为IP分组的报文头格式,报文头的前20个字节是固定的,后面的可变。

 


版本号:4个bit,用来标识IP版本号。这个4位字段的值设置为二进制的0100表示IPv4,设置为0110表示IPv6。目前使用的IP协议版本号是4。

首部长度:4个bit。标识包括选项在内的IP头部字段的长度。

服务类型:8个bit。服务类型字段被划分成两个子字段:3bit的优先级字段和4bit
TOS字段,最后一位置为0。4bit的TOS分别代表:最小时延,最大吞吐量,最高可靠性和最小花费。4bit中只能将其中一个bit位置1。如果4个bit均为0,则代表一般服务。



 



现在大多数的TCP/IP实现都不支持TOS特性,但自4.3BSD
Reno以后的新版系统都对它进行了设置。另外,OSPF和IS-IS都可以根据这些字段的值进行路由策略。而类似SLIP这样的协议虽然提供基于服务类型的排队方法,允许对交互型数据优先进行处理,但它的这种排队机制由SLIP自身来判断和处理。驱动程序会先查看协议字段(确定是否是TCP段),然后检查TCP信源和信宿的端口号来判断是否是一个交互服务。

 

最近,TOS字段已经作为区分服务(Diffserv)架构的一部分被重新定义了。Diffserv比TOS定义所允许的处理更加灵活。在Diffserv下,能够在一台路由器上定义服务分类(COS),将数据包归类到这些分类中去。路由器可以根据它们的分类使用不同的优先级对数据包进行转发。每一个排序和转发处理称为一个PHB。这个架构也被简称为COS。



 

利用开始的6个位构成DSCP位,可以使用任意数值或根据区分服务体系结构中预先定义的服务类别,最多可以定义64个不同的服务类别并整理到PHB中。ECN为显式拥塞通知位。当路由器支持该特性时,这些位可以用于拥塞信号(ECN=11)。

 

总长度字段:16个bit。接收者用IP数据报总长度减去IP报头长度就可以确定数据包数据有效负荷的大小。IP数据报最长可达65535字节。

 

标识字段:16个bit。唯一的标识主机发送的每一份数据报。接收方根据分片中的标识字段是否相同来判断这些分片是否是同一个数据报的分片,从而进行分片的重组。通常每发送一份报文它的值就会加1。

标志字段:3个bit。用于标识数据报是否分片。第1位没有使用,第2位是不分段(DF)位。当DF位被设置为1时,表示路由器不能对数据包进行分段处理。如果数据包由于不能分段而未能被转发,那么路由器将丢弃该数据包并向源发送ICMP不可达。第3位是分段(MF)位。当路由器对数据包进行分段时,除了最后一个分段的MF位被设置为0外,其他的分段的MF位均设置为1,以便接收者直到收到MF位为0的分片为止。

位偏移:13个bit。在接收方进行数据报重组时用来标识分片的顺序。用于指明分段起始点相对于报头起始点的偏移量。由于分段到达时可能错序,所以位偏移字段可以使接收者按照正确的顺序重组数据包。

当数据包的长度超过它所要去的那个数据链路的MTU时,路由器要将它分片。数据包中的数据将被分成小片,每一片被封装在独立的数据包中。接收端使用标识符,分段偏移以及标记域的MF位来进行重组。

 

生存时间:8个bit。TTL域防止丢失的数据包在无休止的传播。该域包含一个8位整数,此数由产生数据包的主机设定。TTL值设置了数据报可以经过的最多的路由器数。TTL的初始值由源主机设置(通常为32或64),每经过一个处理它的路由器,TTL值减1。如果一台路由器将TTL减至0,它将丢弃该数据包并发送一个ICMP超时消息给数据包的源地址。注意:TTL值经过PIX时不减1。

协议字段:8个bit。用来标识是哪个协议向IP传送数据。ICMP为1,IGMP为2,TCP为6,UDP为17,GRE为47,ESP为50。

首部校验和:根据IP首部计算的校验和码。
[align=left]源IP地址:32位(bit),4个字节,每一个字节为0~255之间的整数,及我们日常见到的IP地址格式。 
目的IP地址:32位(bit),4个字节,每一个字节为0~255之间的整数,及我们日常见到的IP地址格式。
[/align]

Option选项:是数据报中的一个可变长的可选信息。



选项字段以32bit为界,不足时插入值为0的填充字节。保证IP首部始终是32bit的整数倍。

由于Delphi里面没有位域这个概念,所以定义结构的时候只能整字节了,挺怀恋C++或者Erlang的,有位域定义出来和使用起来都很方便了 

//IP包
TIPHeader = packed record
iph_verlen: byte;           // 版本和长度
iph_tos: byte;              // 服务类型
iph_length: word;           // 总长度,2个无符号字节所以只能65535
iph_id: word;               // 标识
iph_offset: word;           // 标志和片偏移
iph_ttl: byte;              // 生存时间
iph_protocol: byte;         // 协议
iph_xsum: word;             // 头校验和
iph_src: longword;          // 源地址
iph_dest: longword;         // 目的地址
end;

这个结构体有什么用呢?其实在嗅探的时候就很有用了.

2、ICMP头和报文校验和的计算



 

//定义ICMP包头
typedef struct _ICMP_HEADER {
BYTE bType ;   // 类型8bit
BYTE bCode ;   // 代码8bit
USHORT nCheckSum ;  // 校验和16bit
USHORT nId ;   // 标识,本进程ID16bit
USHORT nSequence ;  // 序列号16bit
UINT nTimeStamp ; // 可选项,这里为时间,用于计算时间32bit
} ICMP_HEADER, *PICMP_HEADER ;

发送ICMP报文时,必须由程序自己计算校验和,将它填入ICMP头部对应的域中。校验和的计算方法是:

将数据以字(16位)为单位累加到一个双字中(强转换双字类型),如果数据长度为奇数(奇数个字节),最后一个字节将被扩展到字,累加的结果是一个双字,最后将这个双字的高16位和低16位相加后取反,便得到了校验和!

// 计算ICMP包校验值
// 参数1:ICMP包缓冲区
// 参数2:ICMP包长度
USHORT GetCheckSum ( LPBYTE lpBuf, DWORD dwSize )
{
DWORD dwCheckSum = 0 ;
USHORT* lpWord = (USHORT*)lpBuf ;

// 累加
while ( dwSize > 1 )
{
dwCheckSum += *lpWord++ ;
dwSize -= 2 ;
}

// 如果长度是奇数
if ( dwSize == 1 )
dwCheckSum += *((LPBYTE)lpWord) ;

// 高16位和低16位相加
dwCheckSum = ( dwCheckSum >> 16 ) + ( dwCheckSum & 0xFFFF ) ;

// 取反
return (USHORT)(~dwCheckSum ) ;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐