mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-05 04:46:52 +08:00
627 lines
19 KiB
Go
627 lines
19 KiB
Go
package header
|
||
|
||
import (
|
||
"encoding/binary"
|
||
"fmt"
|
||
"netstack/tcpip"
|
||
"netstack/tcpip/seqnum"
|
||
)
|
||
|
||
/*
|
||
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
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Source Port | Destination Port |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Sequence Number |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Acknowledgment Number |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Data | |U|A|P|R|S|F| |
|
||
| Offset| Reserved |R|C|S|S|Y|I| Window |
|
||
| | |G|K|H|T|N|N| |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Checksum | Urgent Pointer |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Options | Padding |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| data |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
*/
|
||
|
||
// TCPFields contains the fields of a TCP packet. It is used to describe the
|
||
// fields of a packet that needs to be encoded.
|
||
// tcp首部字段
|
||
type TCPFields struct {
|
||
// SrcPort is the "source port" field of a TCP packet.
|
||
SrcPort uint16
|
||
|
||
// DstPort is the "destination port" field of a TCP packet.
|
||
DstPort uint16
|
||
|
||
// SeqNum is the "sequence number" field of a TCP packet.
|
||
// TCP的初始序列号ISN是随机生成的
|
||
// 如果TCP每次连接都使用固定ISN,黑客可以很方便模拟任何IP与server建立连接
|
||
// 如果ISN是固定的,那很可能在新连接建立后,上次连接通信的报文才到达,
|
||
// 这种情况有概率发生老报文的seq号正好是server希望收到的新连接的报文seq。这就全乱了。
|
||
SeqNum uint32
|
||
|
||
// AckNum is the "acknowledgement number" field of a TCP packet.
|
||
AckNum uint32
|
||
|
||
// DataOffset is the "data offset" field of a TCP packet.
|
||
DataOffset uint8
|
||
|
||
// Flags is the "flags" field of a TCP packet.
|
||
Flags uint8
|
||
|
||
// WindowSize is the "window size" field of a TCP packet.
|
||
WindowSize uint16
|
||
|
||
// Checksum is the "checksum" field of a TCP packet.
|
||
Checksum uint16
|
||
|
||
// UrgentPointer is the "urgent pointer" field of a TCP packet.
|
||
UrgentPointer uint16
|
||
}
|
||
|
||
// TCPSynOptions is used to return the parsed TCP Options in a syn
|
||
// segment.
|
||
// syn 报文的选项
|
||
type TCPSynOptions struct {
|
||
// MSS is the maximum segment size provided by the peer in the SYN.
|
||
MSS uint16
|
||
|
||
// WS is the window scale option provided by the peer in the SYN.
|
||
//
|
||
// Set to -1 if no window scale option was provided.
|
||
WS int
|
||
|
||
// TS is true if the timestamp option was provided in the syn/syn-ack.
|
||
TS bool
|
||
|
||
// TSVal is the value of the TSVal field in the timestamp option.
|
||
TSVal uint32
|
||
|
||
// TSEcr is the value of the TSEcr field in the timestamp option.
|
||
TSEcr uint32
|
||
|
||
// SACKPermitted is true if the SACK option was provided in the SYN/SYN-ACK.
|
||
SACKPermitted bool
|
||
}
|
||
|
||
const (
|
||
srcPort = 0
|
||
dstPort = 2
|
||
seqNum = 4
|
||
ackNum = 8
|
||
dataOffset = 12
|
||
tcpFlags = 13
|
||
winSize = 14
|
||
tcpChecksum = 16
|
||
urgentPtr = 18
|
||
)
|
||
|
||
// Options that may be present in a TCP segment.
|
||
const (
|
||
// 选项表结束选项
|
||
TCPOptionEOL = 0
|
||
// 空操作(nop)选项
|
||
TCPOptionNOP = 1
|
||
// 最大报文段长度选项
|
||
TCPOptionMSS = 2
|
||
// 窗口扩大因子选项
|
||
TCPOptionWS = 3
|
||
// 时间戳选项
|
||
TCPOptionTS = 8
|
||
// 选择性确认(Selective Acknowledgment,SACK)选项
|
||
TCPOptionSACKPermitted = 4
|
||
// SACK 实际工作的选项
|
||
TCPOptionSACK = 5
|
||
)
|
||
|
||
const (
|
||
// MaxWndScale is maximum allowed window scaling, as described in
|
||
// RFC 1323, section 2.3, page 11.
|
||
MaxWndScale = 14
|
||
|
||
// TCPMaxSACKBlocks is the maximum number of SACK blocks that can
|
||
// be encoded in a TCP option field.
|
||
TCPMaxSACKBlocks = 4
|
||
)
|
||
|
||
// SACKBlock 表示 sack 块的结构体
|
||
/*
|
||
+--------+--------+
|
||
| Kind=5 | Length |
|
||
+--------+--------+--------+---------+
|
||
| Start of 1st Block |
|
||
+--------+--------+--------+---------+
|
||
| End of 1st Block |
|
||
+--------+--------+--------+---------+
|
||
| |
|
||
/ . . . . . . /
|
||
| |
|
||
+--------+--------+--------+---------+
|
||
| Start of nth Block |
|
||
+--------+--------+--------+---------+
|
||
| End of nth Block |
|
||
+--------+--------+--------+---------+
|
||
*/
|
||
type SACKBlock struct {
|
||
// Start indicates the lowest sequence number in the block.
|
||
Start seqnum.Value
|
||
|
||
// End indicates the sequence number immediately following the last
|
||
// sequence number of this block.
|
||
End seqnum.Value
|
||
}
|
||
|
||
/*
|
||
1byte 1byte nbytes
|
||
+--------+--------+------------------+
|
||
| Kind | Length | Info |
|
||
+--------+--------+------------------+
|
||
*/
|
||
|
||
// TCPOptions tcp选项结构,这个结构不表示 syn/syn-ack 报文
|
||
type TCPOptions struct {
|
||
// TS is true if the TimeStamp option is enabled.
|
||
TS bool
|
||
|
||
// TSVal is the value in the TSVal field of the segment.
|
||
TSVal uint32
|
||
|
||
// TSEcr is the value in the TSEcr field of the segment.
|
||
TSEcr uint32
|
||
|
||
// SACKBlocks are the SACK blocks specified in the segment.
|
||
SACKBlocks []SACKBlock
|
||
|
||
// 以下仅供测试之用 不对外暴露
|
||
|
||
// MSS is the maximum segment size provided by the peer in the SYN.
|
||
mss uint16
|
||
|
||
// WS is the window scale option provided by the peer in the SYN.
|
||
//
|
||
// Set to -1 if no window scale option was provided.
|
||
ws int
|
||
|
||
// SACKPermitted is true if the SACK option was provided in the SYN/SYN-ACK.
|
||
sackPermitted bool
|
||
}
|
||
|
||
// TCP represents a TCP header stored in a byte array.
|
||
type TCP []byte
|
||
|
||
const (
|
||
// TCPMinimumSize is the minimum size of a valid TCP packet.
|
||
TCPMinimumSize = 20
|
||
|
||
// TCPProtocolNumber is TCP's transport protocol number.
|
||
TCPProtocolNumber tcpip.TransportProtocolNumber = 6
|
||
)
|
||
|
||
// SourcePort returns the "source port" field of the tcp header.
|
||
func (b TCP) SourcePort() uint16 {
|
||
return binary.BigEndian.Uint16(b[srcPort:])
|
||
}
|
||
|
||
// DestinationPort returns the "destination port" field of the tcp header.
|
||
func (b TCP) DestinationPort() uint16 {
|
||
return binary.BigEndian.Uint16(b[dstPort:])
|
||
}
|
||
|
||
// SequenceNumber returns the "sequence number" field of the tcp header.
|
||
func (b TCP) SequenceNumber() uint32 {
|
||
return binary.BigEndian.Uint32(b[seqNum:])
|
||
}
|
||
|
||
// AckNumber returns the "ack number" field of the tcp header.
|
||
func (b TCP) AckNumber() uint32 {
|
||
return binary.BigEndian.Uint32(b[ackNum:])
|
||
}
|
||
|
||
// DataOffset returns the "data offset" field of the tcp header.
|
||
func (b TCP) DataOffset() uint8 {
|
||
return (b[dataOffset] >> 4) * 4 // 以32bits为单位 最小为5 20bytes
|
||
}
|
||
|
||
// Payload returns the data in the tcp packet.
|
||
func (b TCP) Payload() []byte {
|
||
return b[b.DataOffset():]
|
||
}
|
||
|
||
// TCPViewSize TCP报文概览长度
|
||
const TCPViewSize = IPViewSize - TCPMinimumSize
|
||
|
||
func (b TCP) viewPayload() []byte {
|
||
if len(b.Payload())-int(b.DataOffset()) < TCPViewSize {
|
||
return b.Payload()
|
||
}
|
||
return b[b.DataOffset():][:TCPViewSize]
|
||
}
|
||
|
||
// Flags returns the flags field of the tcp header.
|
||
func (b TCP) Flags() uint8 {
|
||
return b[tcpFlags]
|
||
}
|
||
|
||
// WindowSize returns the "window size" field of the tcp header.
|
||
func (b TCP) WindowSize() uint16 {
|
||
return binary.BigEndian.Uint16(b[winSize:])
|
||
}
|
||
|
||
// Checksum returns the "checksum" field of the tcp header.
|
||
func (b TCP) Checksum() uint16 {
|
||
return binary.BigEndian.Uint16(b[tcpChecksum:])
|
||
}
|
||
|
||
// UrgentPtr returns the "urgentptr" field of the tcp header.
|
||
func (b TCP) UrgentPtr() uint16 {
|
||
return binary.BigEndian.Uint16(b[urgentPtr:])
|
||
}
|
||
|
||
// SetSourcePort sets the "source port" field of the tcp header.
|
||
func (b TCP) SetSourcePort(port uint16) {
|
||
binary.BigEndian.PutUint16(b[srcPort:], port)
|
||
}
|
||
|
||
// SetDestinationPort sets the "destination port" field of the tcp header.
|
||
func (b TCP) SetDestinationPort(port uint16) {
|
||
binary.BigEndian.PutUint16(b[dstPort:], port)
|
||
}
|
||
|
||
// SetChecksum sets the checksum field of the tcp header.
|
||
func (b TCP) SetChecksum(checksum uint16) {
|
||
binary.BigEndian.PutUint16(b[tcpChecksum:], checksum)
|
||
}
|
||
|
||
// CalculateChecksum calculates the checksum of the tcp segment given
|
||
// the totalLen and partialChecksum(descriptions below)
|
||
// totalLen is the total length of the segment
|
||
// partialChecksum is the checksum of the network-layer pseudo-header
|
||
// (excluding the total length) and the checksum of the segment data.
|
||
func (b TCP) CalculateChecksum(partialChecksum uint16, totalLen uint16) uint16 {
|
||
// Add the length portion of the checksum to the pseudo-checksum.
|
||
tmp := make([]byte, 2)
|
||
binary.BigEndian.PutUint16(tmp, totalLen)
|
||
checksum := Checksum(tmp, partialChecksum)
|
||
|
||
// Calculate the rest of the checksum.
|
||
return Checksum(b[:b.DataOffset()], checksum)
|
||
}
|
||
|
||
// Options returns a slice that holds the unparsed TCP options in the segment.
|
||
func (b TCP) Options() []byte {
|
||
return b[TCPMinimumSize:b.DataOffset()]
|
||
}
|
||
|
||
func (b TCP) encodeSubset(seq, ack uint32, flags uint8, rcvwnd uint16) {
|
||
binary.BigEndian.PutUint32(b[seqNum:], seq)
|
||
binary.BigEndian.PutUint32(b[ackNum:], ack)
|
||
b[tcpFlags] = flags
|
||
binary.BigEndian.PutUint16(b[winSize:], rcvwnd)
|
||
}
|
||
|
||
// Encode encodes all the fields of the tcp header.
|
||
func (b TCP) Encode(t *TCPFields) {
|
||
b.encodeSubset(t.SeqNum, t.AckNum, t.Flags, t.WindowSize)
|
||
binary.BigEndian.PutUint16(b[srcPort:], t.SrcPort)
|
||
binary.BigEndian.PutUint16(b[dstPort:], t.DstPort)
|
||
b[dataOffset] = (t.DataOffset / 4) << 4
|
||
binary.BigEndian.PutUint16(b[tcpChecksum:], t.Checksum)
|
||
binary.BigEndian.PutUint16(b[urgentPtr:], t.UrgentPointer)
|
||
}
|
||
|
||
// ParseTCPOptions extracts and stores all known options in the provided byte
|
||
// slice in a TCPOptions structure.
|
||
func ParseTCPOptions(b []byte) TCPOptions {
|
||
opts := TCPOptions{}
|
||
limit := len(b)
|
||
for i := 0; i < limit; {
|
||
switch b[i] {
|
||
case TCPOptionEOL: // 末尾
|
||
i = limit
|
||
case TCPOptionNOP: // 空值
|
||
i++
|
||
case TCPOptionMSS:
|
||
if i+4 > limit || b[i+1] != 4 {
|
||
return opts
|
||
}
|
||
mss := uint16(b[i+2])<<8 | uint16(b[i+3])
|
||
if mss == 0 {
|
||
return opts
|
||
}
|
||
opts.mss = mss
|
||
i += 4
|
||
case TCPOptionWS:
|
||
if i+3 > limit || b[i+1] != 3 {
|
||
return opts
|
||
}
|
||
ws := int(b[i+2])
|
||
if ws > MaxWndScale {
|
||
ws = MaxWndScale
|
||
}
|
||
opts.ws = ws
|
||
i += 3
|
||
case TCPOptionTS: // 计时
|
||
if i+10 > limit || (b[i+1] != 10) {
|
||
return opts
|
||
}
|
||
opts.TS = true
|
||
opts.TSVal = binary.BigEndian.Uint32(b[i+2:])
|
||
opts.TSEcr = binary.BigEndian.Uint32(b[i+6:])
|
||
i += 10
|
||
case TCPOptionSACKPermitted:
|
||
if i+2 > limit || b[i+1] != 2 {
|
||
return opts
|
||
}
|
||
opts.sackPermitted = true
|
||
i += 2
|
||
case TCPOptionSACK:
|
||
if i+2 > limit {
|
||
// Malformed SACK block, just return and stop parsing.
|
||
return opts
|
||
}
|
||
sackOptionLen := int(b[i+1])
|
||
numBlocks := (sackOptionLen - 2) / 8 // 去头 每个block长为8
|
||
opts.SACKBlocks = []SACKBlock{}
|
||
for j := 0; j < numBlocks; j++ {
|
||
start := binary.BigEndian.Uint32(b[i+2+j*8:])
|
||
end := binary.BigEndian.Uint32(b[i+2+j*8+4:])
|
||
opts.SACKBlocks = append(opts.SACKBlocks, SACKBlock{
|
||
Start: seqnum.Value(start),
|
||
End: seqnum.Value(end),
|
||
})
|
||
}
|
||
|
||
i += sackOptionLen
|
||
default:
|
||
// 这里不做进一步解析 留到后面进行
|
||
if i+2 > limit {
|
||
return opts
|
||
}
|
||
l := int(b[i+1])
|
||
// If the length is incorrect or if l+i overflows the
|
||
// total options length then return false.
|
||
if l < 2 || i+l > limit {
|
||
return opts
|
||
}
|
||
i += l
|
||
}
|
||
}
|
||
|
||
return opts
|
||
}
|
||
|
||
func (opts TCPOptions) String() string {
|
||
return fmt.Sprintf("|MSS|% 29d|\n|WS |% 29d|\n|TS |% 29v|\n|TSV|% 29d|\n|TSE|% 29d|\n|SP |% 29v|\n|SBS|%v|",
|
||
opts.mss, opts.ws, opts.TS, opts.TSVal, opts.TSEcr, opts.sackPermitted, opts.SACKBlocks)
|
||
}
|
||
|
||
// ParseSynOptions parses the options received in a SYN segment and returns the
|
||
// relevant ones. opts should point to the option part of the TCP Header.
|
||
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
|
||
synOpts := TCPSynOptions{
|
||
// Per RFC 1122, page 85: "If an MSS option is not received at
|
||
// connection setup, TCP MUST assume a default send MSS of 536."
|
||
MSS: 536,
|
||
// If no window scale option is specified, WS in options is
|
||
// returned as -1; this is because the absence of the option
|
||
// indicates that the we cannot use window scaling on the
|
||
// receive end either.
|
||
WS: -1,
|
||
}
|
||
|
||
limit := len(opts)
|
||
for i := 0; i < limit; {
|
||
switch opts[i] {
|
||
case TCPOptionEOL:
|
||
i = limit
|
||
case TCPOptionNOP:
|
||
i++
|
||
case TCPOptionMSS:
|
||
if i+4 > limit || opts[i+1] != 4 {
|
||
return synOpts
|
||
}
|
||
mss := uint16(opts[i+2])<<8 | uint16(opts[i+3])
|
||
if mss == 0 {
|
||
return synOpts
|
||
}
|
||
synOpts.MSS = mss
|
||
i += 4
|
||
case TCPOptionWS:
|
||
if i+3 > limit || opts[i+1] != 3 {
|
||
return synOpts
|
||
}
|
||
ws := int(opts[i+2])
|
||
if ws > MaxWndScale {
|
||
ws = MaxWndScale
|
||
}
|
||
synOpts.WS = ws
|
||
i += 3
|
||
case TCPOptionTS:
|
||
if i+10 > limit || opts[i+1] != 10 {
|
||
return synOpts
|
||
}
|
||
synOpts.TSVal = binary.BigEndian.Uint32(opts[i+2:])
|
||
if isAck { // ACK报文需要记录时间间隔
|
||
// If the segment is a SYN-ACK then store the Timestamp Echo Reply
|
||
// in the segment.
|
||
synOpts.TSEcr = binary.BigEndian.Uint32(opts[i+6:])
|
||
}
|
||
synOpts.TS = true
|
||
i += 10
|
||
case TCPOptionSACKPermitted:
|
||
if i+2 > limit || opts[i+1] != 2 {
|
||
return synOpts
|
||
}
|
||
synOpts.SACKPermitted = true
|
||
i += 2
|
||
default:
|
||
// We don't recognize this option, just skip over it.
|
||
if i+2 > limit {
|
||
return synOpts
|
||
}
|
||
l := int(opts[i+1])
|
||
// If the length is incorrect or if l+i overflows the
|
||
// total options length then return false.
|
||
if l < 2 || i+l > limit {
|
||
return synOpts
|
||
}
|
||
i += l
|
||
}
|
||
}
|
||
return synOpts
|
||
}
|
||
|
||
func (opts TCPSynOptions) String() string {
|
||
return fmt.Sprintf("|%d|%d|%v|%d|%d|%v|", opts.MSS, opts.WS, opts.TS, opts.TSVal, opts.TSEcr, opts.SACKPermitted)
|
||
}
|
||
|
||
// EncodeMSSOption encodes the MSS TCP option with the provided MSS values in
|
||
// the supplied buffer. If the provided buffer is not large enough then it just
|
||
// returns without encoding anything. It returns the number of bytes written to
|
||
// the provided buffer.
|
||
func EncodeMSSOption(mss uint32, b []byte) int {
|
||
// mssOptionSize is the number of bytes in a valid MSS option.
|
||
const mssOptionSize = 4
|
||
|
||
if len(b) < mssOptionSize {
|
||
return 0
|
||
}
|
||
b[0], b[1], b[2], b[3] = TCPOptionMSS, mssOptionSize, byte(mss>>8), byte(mss)
|
||
return mssOptionSize
|
||
}
|
||
|
||
// EncodeWSOption encodes the WS TCP option with the WS value in the
|
||
// provided buffer. If the provided buffer is not large enough then it just
|
||
// returns without encoding anything. It returns the number of bytes written to
|
||
// the provided buffer.
|
||
func EncodeWSOption(ws int, b []byte) int {
|
||
if len(b) < 3 {
|
||
return 0
|
||
}
|
||
b[0], b[1], b[2] = TCPOptionWS, 3, uint8(ws)
|
||
return int(b[1])
|
||
}
|
||
|
||
// EncodeTSOption encodes the provided tsVal and tsEcr values as a TCP timestamp
|
||
// option into the provided buffer. If the buffer is smaller than expected it
|
||
// just returns without encoding anything. It returns the number of bytes
|
||
// written to the provided buffer.
|
||
func EncodeTSOption(tsVal, tsEcr uint32, b []byte) int {
|
||
if len(b) < 10 {
|
||
return 0
|
||
}
|
||
b[0], b[1] = TCPOptionTS, 10
|
||
binary.BigEndian.PutUint32(b[2:], tsVal)
|
||
binary.BigEndian.PutUint32(b[6:], tsEcr)
|
||
return int(b[1])
|
||
}
|
||
|
||
// EncodeSACKPermittedOption encodes a SACKPermitted option into the provided
|
||
// buffer. If the buffer is smaller than required it just returns without
|
||
// encoding anything. It returns the number of bytes written to the provided
|
||
// buffer.
|
||
func EncodeSACKPermittedOption(b []byte) int {
|
||
if len(b) < 2 {
|
||
return 0
|
||
}
|
||
|
||
b[0], b[1] = TCPOptionSACKPermitted, 2
|
||
return int(b[1])
|
||
}
|
||
|
||
// EncodeSACKBlocks encodes the provided SACK blocks as a TCP SACK option block
|
||
// in the provided slice. It tries to fit in as many blocks as possible based on
|
||
// number of bytes available in the provided buffer. It returns the number of
|
||
// bytes written to the provided buffer.
|
||
func EncodeSACKBlocks(sackBlocks []SACKBlock, b []byte) int {
|
||
if len(sackBlocks) == 0 {
|
||
return 0
|
||
}
|
||
l := len(sackBlocks)
|
||
if l > TCPMaxSACKBlocks {
|
||
l = TCPMaxSACKBlocks
|
||
}
|
||
if ll := (len(b) - 2) / 8; ll < l {
|
||
l = ll
|
||
}
|
||
if l == 0 {
|
||
// There is not enough space in the provided buffer to add
|
||
// any SACK blocks.
|
||
return 0
|
||
}
|
||
b[0] = TCPOptionSACK
|
||
b[1] = byte(l*8 + 2)
|
||
for i := 0; i < l; i++ {
|
||
binary.BigEndian.PutUint32(b[i*8+2:], uint32(sackBlocks[i].Start))
|
||
binary.BigEndian.PutUint32(b[i*8+6:], uint32(sackBlocks[i].End))
|
||
}
|
||
return int(b[1])
|
||
}
|
||
|
||
// EncodeNOP adds an explicit NOP to the option list.
|
||
func EncodeNOP(b []byte) int {
|
||
if len(b) == 0 {
|
||
return 0
|
||
}
|
||
b[0] = TCPOptionNOP
|
||
return 1
|
||
}
|
||
|
||
// AddTCPOptionPadding adds the required number of TCPOptionNOP to quad align
|
||
// the option buffer. It adds padding bytes after the offset specified and
|
||
// returns the number of padding bytes added. The passed in options slice
|
||
// must have space for the padding bytes.
|
||
func AddTCPOptionPadding(options []byte, offset int) int {
|
||
paddingToAdd := -offset & 3
|
||
// Now add any padding bytes that might be required to quad align the
|
||
// options.
|
||
for i := offset; i < offset+paddingToAdd; i++ {
|
||
options[i] = TCPOptionNOP
|
||
}
|
||
return paddingToAdd
|
||
}
|
||
|
||
/*
|
||
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
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Source Port | Destination Port |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Sequence Number |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Acknowledgment Number |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Data | |U|A|P|R|S|F| |
|
||
| Offset| Reserved |R|C|S|S|Y|I| Window |
|
||
| | |G|K|H|T|N|N| |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Checksum | Urgent Pointer |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| Options | Padding |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| data |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
*/
|
||
|
||
var tcpFmt string = `
|
||
|% 16s|% 16s|
|
||
|% 32s |
|
||
|% 32s |
|
||
|% 4s|% 4s|%06b|% 16s|
|
||
|% 16s|% 16s|
|
||
---------------------------------`
|
||
|
||
func (b TCP) String() string {
|
||
return fmt.Sprintf(tcpFmt, atoi(b.SourcePort()), atoi(b.DestinationPort()),
|
||
atoi(b.SequenceNumber()),
|
||
atoi(b.AckNumber()),
|
||
atoi(b.DataOffset()), "0", b.Flags(), atoi(b.WindowSize()),
|
||
atoi(b.Checksum()), atoi(b.UrgentPtr()))
|
||
}
|