TCP&UDP报文格式
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
TCP/UDP报文格式
TCP 协议为终端设备提供了面向连接的、可靠的网络服务,UDP 协议为终端设备提供了无连接的、不可靠的数据报服务。
从上图我们可以看出,TCP 协议为了保证数据传输的可靠性,相对于UDP 报文,TCP 报文头部有更多的字段选项。
首先让我们来看一下TCP 的报文头部主要字段:
每个TCP 报文头部都包含源端口号(source port)和目的端口号(destination port),用于标识和区分源端设备和目的端设备的应用进程。
在TCP/IP 协议栈中,源端口号和目的端口号分别与源IP 地址和目的IP 地址组成套接字(socket),唯一的确定一条TCP 连接。
序列号(Sequence number)字段用来标识TCP 源端设备向目的端设备发送的字节流,它表示在这个报文段中的第一个数据字节。
如果将字节流看作在两个应用程序间的单向流动,则TCP 用序列号对每个字节进行计数。
序列号是一个32bits 的数。
既然每个传输的字节都被计数,确认序号(Acknowledgement number,32bits)包含发送确认的一端所期望接收到的下一个序号。
因此,确认序号应该是上次已成功收到的数据字节序列号加1。
TCP 的流量控制由连接的每一端通过声明的窗口大小(windows size)来提供。
窗口大小用数据包来表示,例如Windows size=3, 表示一次可以发送三个数据包。
窗口大小起始于确认字段指明的值,是一个16bits 字段。
窗口大小可以调节。
校验和(checksum)字段用于校验TCP 报头部分和数据部分的正确性。
最常见的可选字段是MSS(Maximum Segment Size,最大报文大小)。
MSS指明本端所能够接收的最大长度的报文段。
当一个TCP 连接建立时,连接的双方都要通告各自的MSS 协商可以传输的最大报文长度。
我们常见的MSS有1024(以太网可达1460 字节)字节。
相对于TCP 报文,UDP 报文只有少量的字段:源端口号、目的端口号、长度、校验和等,各个字段功能和TCP 报文相应字段一样。
UDP 报文没有可靠性保证和顺序保证字段,流量控制字段等,可靠性较差。
当然,使用传输层UDP 服务的应用程序也有优势。
正因为UDP 协议较少的控制选项,在数据传输过程中,延迟较小,数据传输效率较高,适合于对可靠性要求并不高的应用程序,或者可以保障可靠性的应用程序像DNS、TFTP、SNMP 等;UDP 协议也可以用于传输链路可靠的网络。
UDP报文
UDP报头定长为8B。
按顺序为:
1,UInt16 源端口号
关于端口号有一些规定,服务器端通常用熟知端口号,通常在0-1023之间。
而客户端用随机的端口号,其范围在49152到65535之间。
2,UInt16 目的端口号
3,UInt16 总长度
包括报头和数据的长度之和。
显然在[8,65535]区间。
4,UInt16检验和
如果不需要检验和,就取0。
如果需要检验和,那么其算法为:
(1)构造12B的伪报头
UInt32 源IP+UInt32 目的IP+Byte 0+Byte 17+UInt16 总长度(上面所说的)(2)UDP的报头
(3)UDP的数据部分
u nsafe UInt16计算校验和(UInt16* buffer, int size)
{
Int32 cksum = 0;
int counter;
counter = 0;
while (size > 0)
{
UInt16 val = buffer[counter];
cksum += Convert.ToInt32(buffer[counter]);
counter += 1;
size -= 1;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (UInt16)(~cksum);
}
构造UDP报文(含有检验和)需要的输入参数包括:
UInt32 源IP、UInt32 目的IP、UInt16 源端口号、UInt16 目的端口号、Byte[] 数据部分、Uint16 数据部分的长度
public Byte[] 构造UDP数据报(UInt32 源IP, UInt32 目的IP, UInt16 源端口号, UInt16 目的端口号, Byte[] 数据部分, UInt16 数据部分的长度)
{
UInt16 总长度= (UInt16)(8 + 数据部分的长度);
UInt16 校验和= 0;
//网络字节顺序
源IP=(uint)IPAddress.HostToNetworkOrder((int)源IP);
目的IP = (uint)IPAddress.HostToNetworkOrder((int)目的IP);
源端口号= (ushort)IPAddress.HostToNetworkOrder((Int16)源端口号);
目的端口号= (ushort)IPAddress.HostToNetworkOrder((Int16)目的端口号);
总长度= (ushort)IPAddress.HostToNetworkOrder((Int16)总长度);
//
Byte[] udpbytes = new Byte[8 + 数据部分的长度];
BitConverter.GetBytes(源端口号).CopyTo(udpbytes, 0);//填入源端口号
BitConverter.GetBytes(目的端口号).CopyTo(udpbytes, 2);//填入目的端口号BitConverter.GetBytes(总长度).CopyTo(udpbytes, 4);//填入总长度
BitConverter.GetBytes(校验和).CopyTo(udpbytes, 6);//校验和
数据部分.CopyTo(udpbytes, 8);
//下面是计算校验和.数据部分如果不是偶数字节则补一个字节.
byte[] 伪报文= new byte[20 + (数据部分的长度+1) / 2*2];//确保偶数字节BitConverter.GetBytes(源IP).CopyTo(伪报文, 0);//填入源端口号
BitConverter.GetBytes(目的IP).CopyTo(伪报文, 4);//填入目的端口号
伪报文[8] =0;//填充0.网络字节顺序
伪报文[9] = 17;//和UDP协议号
BitConverter.GetBytes(总长度).CopyTo(伪报文, 10);//填入总长度
udpbytes.CopyTo(伪报文, 12);
//string ss = 网络字节串(伪报文);
unsafe
{
fixed (byte * pt = 伪报文)
{
ushort* pu = (ushort*)pt;
校验和= 计算校验和(pu, 伪报文.Length/2);
}
}
//不知道为什么不对校验和进行网络字节顺序的调整
BitConverter.GetBytes(校验和).CopyTo(udpbytes, 6);//校验和
return udpbytes;
}
TCP UDP报文解析
1 UDP报文:
UDP报文比较简单,由四个字段组成,每个字段2个字节:
(1) 源端口source port
(2) 目的端口destination port
(3) 长度:UDP用户数据报的长度
(4) 检验和checksum
scapy中定义的UDP类(参数一一对应)
>>> ls(UDP)
sport : ShortEnumField = (53)
dport : ShortEnumField = (53)
len : ShortField = (None)
chksum : XShortField = (None)
>>>
我们用这个发一个完整的UDP数据包。
定义:
>>> eth=Ether()
>>> ip=IP(dst='10.104.4.13')
>>> udp=UDP(sport=4321,dport=4321)
>>> udpPacket=eth/ip/udp/'This is an UDP datagram'
>>> sendp(udpPacket)
.
Sent 1 packets.
>>> udpPacket.show()
###[ Ethernet ]###
dst= 00:11:85:ae:03:3b
src= 00:22:15:27:69:16
type= 0x800
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= 0x0
src= 10.104.4.23
dst= 10.104.4.13
options= ''
###[ UDP ]###
sport= 4321
dport= 4321
len= None
chksum= 0x0
###[ Raw ]###
load= 'This is an UDP datagram'
>>>
成功发送,而且我们发现一些参数是默认填充和计算的。
>>> len(udpPacket)
65
>>>
UDP中的UDP用户数据报长度是31,正好是总长度65-20(IP头)-14(ethernet头)=31
这个checksum长度就是UDP头(8字节固定)+后面的Data长度
2 TCP报文:
TCP首部比较复杂,分为两大部分,前20个字节是大小固定的,后面的选项部分大小不固定。
首部固定部分各段意义:
(1) 源端口source port 2个字节
(2) 目的端口destination port 2个字节
(3) 序号:sequence number,TCP传送的是面向连接的连续的数据流,所传送的数据每一个字
节都编上一个序号,首部的这个序号指的是本报文段所发送的数据的第一个字节的序号,占4个字节
(4) 确认号:Ack number,是期望受到对方的下一个报文段的数据的第一个字节的序号,也就是期望收到的下一个报文段首部的序号字段的值,占4个字节
(5)数据偏移:占4位,(单位是4字节),类似于IP包头的首部长度,他是指TCP报文段首部的长度,由于存在长度不确定的选项字段,所以此值最小为20字节,最大为60字节
(6)保留:6bit,目前没用,设为0
(7)这里的6位是说明本报文段性质的,下面再详细说
(8)窗口:window,占2个字节,用来控制对方发送的数量
(9)检验和:checksum,2个字节,检验范围为首部加数据两部分
下面看看那6位的控制部分各位的含义:
SYN:该标志位用来建立连接,让连接双方同步序列号.
FIN:表示发送端已经没有数据要求传输了,希望释放连接.
RST:用来复位一个连接.RST标志置位的数据包称为复位包.一般情况下,如果TCP收到的一个分段明显不是属于该主机上的任何一个连接,则向远程发送一个复位包.
URG:为紧急数据标志.如果他为1,表示本数据包中包含紧急数据.此时紧急数据指针有效.
ACK:为确认标志位.如果为1,表示包中的确认号时有效的.否则,包中的确认号无效.
PSH:如果置位,接收端应尽快把数据传诵给应用层.
看个实际抓的一个包:
前面2位是保留,第三位是URG,第四位是ACK,第五位是PSH,第六位是RST,第七位是SYN,第八位是FIN。
可变部分就是选项和填充:长度可变。
>>> ls(TCP)
sport : ShortEnumField = (20) #2字节源端口
dport : ShortEnumField = (80) #2字节目的端口
seq : IntField = (0) #4字节sequence number
ack : IntField = (0) # 4byte ack number
dataofs : BitField = (None) #首部长度,4bit
reserved : BitField = (0)
flags : FlagsField = (2) #控制位,1字节,前两位为0,后面为6个控制window : ShortField = (8192) #2字节
chksum : XShortField = (None) #2字节
urgptr : ShortField = (0) #紧急指针
options : TCPOptionsField = ({})
其中flags的取值,可以以URG,ACK,PSH,RST,SYN,FIN的首字母来代替
下面是一个具体的TCP报文,
可以很清楚的看到TCP段每个字节的取值情况。
可以看到flags值为S,所以这是个SYN 位为1,ACK=0,表明这是个连接请求报文,紧跟这个报文的下一个数据报如下图:可见其falgs=SA,即,SYN=1,ACK=1,这说明对方同意连接,而发回的响应报文。