forked from scylladb/seastar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp.cc
169 lines (152 loc) · 4.82 KB
/
tcp.cc
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
169
/*
* This file is open source software, licensed to you under the terms
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright (C) 2014 Cloudius Systems, Ltd.
*/
#include "tcp.hh"
#include "tcp-stack.hh"
#include "ip.hh"
#include "core/align.hh"
#include "core/future.hh"
#include "native-stack-impl.hh"
namespace seastar {
namespace net {
void tcp_option::parse(uint8_t* beg1, uint8_t* end1) {
const char* beg = reinterpret_cast<const char*>(beg1);
const char* end = reinterpret_cast<const char*>(end1);
while (beg < end) {
auto kind = option_kind(*beg);
if (kind != option_kind::nop && kind != option_kind::eol) {
// Make sure there is enough room for this option
auto len = uint8_t(beg[1]);
if (beg + len > end) {
return;
}
}
switch (kind) {
case option_kind::mss:
_mss_received = true;
_remote_mss = mss::read(beg).mss;
beg += option_len::mss;
break;
case option_kind::win_scale:
_win_scale_received = true;
_remote_win_scale = win_scale::read(beg).shift;
// We can turn on win_scale option, 7 is Linux's default win scale size
_local_win_scale = 7;
beg += option_len::win_scale;
break;
case option_kind::sack:
_sack_received = true;
beg += option_len::sack;
break;
case option_kind::nop:
beg += option_len::nop;
break;
case option_kind::eol:
return;
default:
// Ignore options we do not understand
uint8_t len = *(beg + 1);
beg += len;
// Prevent infinite loop
if (len == 0) {
return;
}
break;
}
}
}
uint8_t tcp_option::fill(void* h, const tcp_hdr* th, uint8_t options_size) {
auto hdr = reinterpret_cast<char*>(h);
auto off = hdr + tcp_hdr::len;
uint8_t size = 0;
bool syn_on = th->f_syn;
bool ack_on = th->f_ack;
if (syn_on) {
if (_mss_received || !ack_on) {
auto mss = tcp_option::mss();
mss.mss = _local_mss;
mss.write(off);
off += mss.len;
size += mss.len;
}
if (_win_scale_received || !ack_on) {
auto win_scale = tcp_option::win_scale();
win_scale.shift = _local_win_scale;
win_scale.write(off);
off += win_scale.len;
size += win_scale.len;
}
}
if (size > 0) {
// Insert NOP option
auto size_max = align_up(uint8_t(size + 1), tcp_option::align);
while (size < size_max - uint8_t(option_len::eol)) {
auto nop = tcp_option::nop();
nop.write(off);
off += option_len::nop;
size += option_len::nop;
}
auto eol = tcp_option::eol();
eol.write(off);
size += option_len::eol;
}
assert(size == options_size);
return size;
}
uint8_t tcp_option::get_size(bool syn_on, bool ack_on) {
uint8_t size = 0;
if (syn_on) {
if (_mss_received || !ack_on) {
size += option_len::mss;
}
if (_win_scale_received || !ack_on) {
size += option_len::win_scale;
}
}
if (size > 0) {
size += option_len::eol;
// Insert NOP option to align on 32-bit
size = align_up(size, tcp_option::align);
}
return size;
}
ipv4_tcp::ipv4_tcp(ipv4& inet)
: _inet_l4(inet), _tcp(std::make_unique<tcp<ipv4_traits>>(_inet_l4)) {
}
ipv4_tcp::~ipv4_tcp() {
}
void ipv4_tcp::received(packet p, ipv4_address from, ipv4_address to) {
_tcp->received(std::move(p), from, to);
}
bool ipv4_tcp::forward(forward_hash& out_hash_data, packet& p, size_t off) {
return _tcp->forward(out_hash_data, p, off);
}
server_socket
tcpv4_listen(tcp<ipv4_traits>& tcpv4, uint16_t port, listen_options opts) {
return server_socket(std::make_unique<native_server_socket_impl<tcp<ipv4_traits>>>(
tcpv4, port, opts));
}
::seastar::socket
tcpv4_socket(tcp<ipv4_traits>& tcpv4) {
return ::seastar::socket(std::make_unique<native_socket_impl<tcp<ipv4_traits>>>(
tcpv4));
}
}
}