summaryrefslogtreecommitdiffstats
path: root/tcp.go
blob: 8be1617f3935f88453db68f9aeffcbaa5288ed4c (plain)
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)
}