1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
/**
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package main
import (
"bytes"
"encoding/binary"
"net"
)
//
// TCP flags
//
const (
FIN = 1 << 0
SYN = 1 << 1
RST = 1 << 2
PSH = 1 << 3
ACK = 1 << 4
URG = 1 << 5
)
// TCPHeader defines the TCP header struct
type TCPHeader struct {
Source uint16
Destination uint16
SeqNum uint32
AckNum uint32
DataOffset uint8 // 4 bits
Reserved uint8 // 6 bits
Flags uint8 // 6 bits
Window uint16
Checksum uint16
Urgent uint16
}
//
// create & serialize a TCP header, compute and fill in the checksum (v4/v6)
//
func makeTCPHeader(af string, srcAddr, dstAddr net.IP, srcPort, dstPort int, ts uint32) []byte {
tcpHeader := TCPHeader{
Source: uint16(srcPort), // Random ephemeral port
Destination: uint16(dstPort),
SeqNum: ts,
AckNum: 0,
DataOffset: 5, // 4 bits
Reserved: 0, // 6 bits
Flags: SYN, // 6 bits (000010, SYN bit set)
Window: 0xffff, // max window
Checksum: 0,
Urgent: 0,
}
// temporary bytes for checksum
bytes := tcpHeader.Serialize()
tcpHeader.Checksum = tcpChecksum(af, bytes, srcAddr, dstAddr)
return tcpHeader.Serialize()
}
// Parse packet into TCPHeader structure
func parseTCPHeader(data []byte) *TCPHeader {
var tcp TCPHeader
r := bytes.NewReader(data)
binary.Read(r, binary.BigEndian, &tcp.Source)
binary.Read(r, binary.BigEndian, &tcp.Destination)
binary.Read(r, binary.BigEndian, &tcp.SeqNum)
binary.Read(r, binary.BigEndian, &tcp.AckNum)
// read the flags from a 16-bit field
var field uint16
binary.Read(r, binary.BigEndian, &field)
// most significant 4 bits
tcp.DataOffset = byte(field >> 12)
// reserved part - 6 bits
tcp.Reserved = byte(field >> 6 & 0x3f)
// flags - 6 bits
tcp.Flags = byte(field & 0x3f)
binary.Read(r, binary.BigEndian, &tcp.Window)
binary.Read(r, binary.BigEndian, &tcp.Checksum)
binary.Read(r, binary.BigEndian, &tcp.Urgent)
return &tcp
}
// Serialize emits raw bytes for the header
func (tcp *TCPHeader) Serialize() []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, tcp.Source)
binary.Write(buf, binary.BigEndian, tcp.Destination)
binary.Write(buf, binary.BigEndian, tcp.SeqNum)
binary.Write(buf, binary.BigEndian, tcp.AckNum)
var mix uint16
mix = uint16(tcp.DataOffset)<<12 |
uint16(tcp.Reserved&0x3f)<<9 |
uint16(tcp.Flags&0x3f)
binary.Write(buf, binary.BigEndian, mix)
binary.Write(buf, binary.BigEndian, tcp.Window)
binary.Write(buf, binary.BigEndian, tcp.Checksum)
binary.Write(buf, binary.BigEndian, tcp.Urgent)
out := buf.Bytes()
return out
}
//
// TCP Checksum, works for both v4 and v6 IP addresses
//
func tcpChecksum(af string, data []byte, srcip, dstip net.IP) uint16 {
// the pseudo header used for TCP c-sum computation
var pseudoHeader []byte
pseudoHeader = append(pseudoHeader, srcip...)
pseudoHeader = append(pseudoHeader, dstip...)
switch {
case af == "ip4":
pseudoHeader = append(pseudoHeader, []byte{
0,
6, // protocol number for TCP
0, byte(len(data)), // TCP length (16 bits), w/o pseudoheader
}...)
case af == "ip6":
pseudoHeader = append(pseudoHeader, []byte{
0, 0, 0, byte(len(data)), // TCP length (32 bits), w/0 pseudoheader
0, 0, 0,
6, // protocol number for TCP
}...)
}
body := make([]byte, 0, len(pseudoHeader)+len(data))
body = append(body, pseudoHeader...)
body = append(body, data...)
bodyLen := len(body)
var word uint16
var csum uint32
for i := 0; i+1 < bodyLen; i += 2 {
word = uint16(body[i])<<8 | uint16(body[i+1])
csum += uint32(word)
}
if bodyLen%2 != 0 {
csum += uint32(body[len(body)-1])
}
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
return uint16(^csum)
}
|