github.com/20yyq/packet@v0.1.4-0.20231013092308-386a004a3baa/tcp.go (about) 1 // @@ 2 // @ Author : Eacher 3 // @ Date : 2023-07-14 08:11:29 4 // @ LastEditTime : 2023-09-04 09:40:14 5 // @ LastEditors : Eacher 6 // @ --------------------------------------------------------------------------------< 7 // @ Description : 8 // @ --------------------------------------------------------------------------------< 9 // @ FilePath : /20yyq/packet/tcp.go 10 // @@ 11 package packet 12 13 import ( 14 "encoding/binary" 15 ) 16 17 const ( 18 SizeofTCPPacket = 0x14 19 ) 20 21 /* 22 23 // 来源: https://support.huawei.com/enterprise/zh/doc/EDOC1100174722?section=j006 24 25 0 1 2 3 26 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 27 +-------------------------------+-------------------------------+ 28 | Source Port | Destination Port | 29 +-------------------------------+-------------------------------+ 30 | Sequence Number | 31 +---------------------------------------------------------------+ 32 | Acknowledgment Number | 33 +-------+-----------+-+-+-+-+-+-+-------------------------------+ 34 | Data | |U|A|P|R|S|F| | 35 | Offset| Reserved |R|C|S|S|Y|I| Window | 36 | | |G|K|H|T|N|N| | 37 +-------+-----------+-+-+-+-+-+-+-------------------------------+ 38 | Checksum | Urgent Pointer | 39 +-------------------------------+---------------+---------------+ 40 | Options | Padding | 41 +-----------------------------------------------+---------------+ 42 | data | 43 +---------------------------------------------------------------+ 44 45 字段 长度 含义 46 Source Port 16比特 源端口,标识哪个应用程序发送。 47 Destination Port 16比特 目的端口,标识哪个应用程序接收。 48 Sequence Number 32比特 序号字段。TCP链接中传输的数据流中每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号。 49 Acknowledgment Number 32比特 确认号,是期望收到对方的下一个报文段的数据的第1个字节的序号,即上次已成功接收到的数据字节序号加1。只有ACK标识为1,此字段有效。 50 Data Offset 4比特 数据偏移,即首部长度,指出TCP报文段的数据起始处距离TCP报文段的起始处有多远,以32比特(4字节)为计算单位。最多有60字节的首部,若无选项字段,正常为20字节。 51 Reserved 6比特 保留,必须填0。 52 URG 1比特 紧急指针有效标识。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。 53 ACK 1比特 确认序号有效标识。只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。 54 PSH 1比特 标识接收方应该尽快将这个报文段交给应用层。接收到PSH = 1的TCP报文段,应尽快的交付接收应用进程,而不再等待整个缓存都填满了后再向上交付。 55 RST 1比特 重建连接标识。当RST=1时,表明TCP连接中出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接。 56 SYN 1比特 同步序号标识,用来发起一个连接。SYN=1表示这是一个连接请求或连接接受请求。 57 FIN 1比特 发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。 58 Window 16比特 窗口:TCP的流量控制,窗口起始于确认序号字段指明的值,这个值是接收端期望接收的字节数。窗口最大为65535字节。 59 Checksum 16比特 校验字段,包括TCP首部和TCP数据,是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。 60 Urgent Pointer 16比特 紧急指针,只有当URG标志置1时紧急指针才有效。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。紧急指针指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。 61 Options 可变 选项字段。TCP协议最初只规定了一种选项,即最长报文段长度(只包含数据字段,不包括TCP首部),又称为MSS。MSS告诉对方TCP“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节”。 62 新的RFC规定有以下几种选型:选项表结束,空操作,最大报文段长度,窗口扩大因子,时间戳。 63 选项表结束。 64 空操作:没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。 65 最大报文段长度:又称为MSS,只包含数据字段,不包括TCP首部。 66 窗口扩大因子:3字节,其中一个字节表示偏移值S。新的窗口值等于TCP首部中的窗口位数增大到(16+S),相当于把窗口值向左移动S位后获得实际的窗口大小。 67 时间戳:10字节,其中最主要的字段是时间戳值(4字节)和时间戳回送应答字段(4字节)。 68 Padding 可变 填充字段,用来补位,使整个首部长度是4字节的整数倍。 69 */ 70 type TCPPacket struct { 71 SrcPort uint16 72 DstPort uint16 73 Sequence uint32 74 AckNum uint32 75 orgBites uint16 76 Window uint16 77 CheckSum uint16 78 UrgentPtr uint16 79 80 DataOffset uint8 81 Reserved uint8 82 URG bool 83 ACK bool 84 PSH bool 85 RST bool 86 SYN bool 87 FIN bool 88 Options []byte 89 } 90 91 // 14.byte EthernetPacket 92 // 20.byte IPv4Packet 或者 IPv6Packet 93 // 返回负载下标起始位 94 func NewTCPPacket(b []byte) (tcp TCPPacket, next uint8) { 95 if len(b) < SizeofTCPPacket { 96 return 97 } 98 tcp.SrcPort = binary.BigEndian.Uint16(b[:2]) 99 tcp.DstPort = binary.BigEndian.Uint16(b[2:4]) 100 tcp.Sequence = binary.BigEndian.Uint32(b[4:8]) 101 tcp.AckNum = binary.BigEndian.Uint32(b[8:12]) 102 tcp.orgBites = binary.BigEndian.Uint16(b[12:14]) 103 tcp.Window = binary.BigEndian.Uint16(b[14:16]) 104 tcp.CheckSum = binary.BigEndian.Uint16(b[16:18]) 105 tcp.UrgentPtr = binary.BigEndian.Uint16(b[18:20]) 106 tcp.DataOffset, next = uint8(tcp.orgBites & 0b1111000000000000 >> 10), SizeofTCPPacket 107 if tcp.DataOffset < SizeofTCPPacket || len(b) < int(tcp.DataOffset) { 108 tcp, next = TCPPacket{}, 0 109 return 110 } 111 if tcp.DataOffset > SizeofTCPPacket { 112 tcp.Options, next = make([]byte, tcp.DataOffset - SizeofTCPPacket), tcp.DataOffset 113 copy(tcp.Options, b[SizeofTCPPacket:tcp.DataOffset]) 114 } 115 tcp.URG = (tcp.orgBites & 0b0000000000100000) != 0 116 tcp.ACK = (tcp.orgBites & 0b0000000000010000) != 0 117 tcp.PSH = (tcp.orgBites & 0b0000000000001000) != 0 118 tcp.RST = (tcp.orgBites & 0b0000000000000100) != 0 119 tcp.SYN = (tcp.orgBites & 0b0000000000000010) != 0 120 tcp.FIN = (tcp.orgBites & 0b0000000000000001) != 0 121 return 122 } 123 124 func (tcp TCPPacket) WireFormat() []byte { 125 opLen := len(tcp.Options) 126 if opLen > 40 { 127 return nil 128 } 129 b := make([]byte, SizeofTCPPacket + opLen) 130 if opLen > 0 { 131 copy(b[SizeofTCPPacket:], tcp.Options) 132 b = append(b, make([]byte, opLen % 4)...) 133 } 134 binary.BigEndian.PutUint16(b[:2], tcp.SrcPort) 135 binary.BigEndian.PutUint16(b[2:4], tcp.DstPort) 136 binary.BigEndian.PutUint32(b[4:8], tcp.Sequence) 137 binary.BigEndian.PutUint32(b[8:12], tcp.AckNum) 138 var tmp uint16 139 tmp = uint16(tcp.DataOffset >> 2) 140 if tcp.URG { 141 tmp |= 0b0000000000100000 142 } 143 if tcp.ACK { 144 tmp |= 0b0000000000010000 145 } 146 if tcp.PSH { 147 tmp |= 0b0000000000001000 148 } 149 if tcp.RST { 150 tmp |= 0b0000000000000100 151 } 152 if tcp.SYN { 153 tmp |= 0b0000000000000010 154 } 155 if tcp.FIN { 156 tmp |= 0b0000000000000001 157 } 158 binary.BigEndian.PutUint16(b[12:14], tmp) 159 binary.BigEndian.PutUint16(b[14:16], tcp.Window) 160 binary.BigEndian.PutUint16(b[16:18], tcp.CheckSum) 161 binary.BigEndian.PutUint16(b[18:20], tcp.UrgentPtr) 162 return b 163 }