Files
scylladb/net/tcp.cc
2015-01-08 01:26:36 +09:00

138 lines
3.8 KiB
C++

/*
* Copyright (C) 2014 Cloudius Systems, Ltd.
*/
#include "tcp.hh"
#include "tcp-stack.hh"
#include "ip.hh"
#include "core/align.hh"
#include "native-stack-impl.hh"
namespace net {
void tcp_option::parse(uint8_t* beg, uint8_t* end) {
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 = *(beg + 1);
if (beg + len > end) {
return;
}
}
switch (kind) {
case option_kind::mss:
_mss_received = true;
_remote_mss = ntoh(reinterpret_cast<mss*>(beg)->mss);
beg += option_len::mss;
break;
case option_kind::win_scale:
_win_scale_received = true;
_remote_win_scale = reinterpret_cast<win_scale*>(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
auto len = *(beg + 1);
beg += len;
// Prevent infinite loop
if (len == 0) {
return;
}
break;
}
}
}
uint8_t tcp_option::fill(tcp_hdr* th, uint8_t options_size) {
auto hdr = reinterpret_cast<uint8_t*>(th);
auto off = hdr + sizeof(tcp_hdr);
uint8_t size = 0;
if (th->f_syn) {
if (_mss_received || !th->f_ack) {
auto mss = new (off) tcp_option::mss;
mss->mss = _local_mss;
off += mss->len;
size += mss->len;
*mss = hton(*mss);
}
if (_win_scale_received || !th->f_ack) {
auto win_scale = new (off) tcp_option::win_scale;
win_scale->shift = _local_win_scale;
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)) {
new (off) tcp_option::nop;
off += option_len::nop;
size += option_len::nop;
}
new (off) tcp_option::eol;
size += option_len::eol;
}
assert(size == options_size);
return size;
}
uint8_t tcp_option::get_size(bool foreign_syn_received) {
uint8_t size = 0;
if (_mss_received || !foreign_syn_received)
size += option_len::mss;
if (_win_scale_received || !foreign_syn_received)
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));
}
connected_socket
tcpv4_connect(tcp<ipv4_traits>& tcpv4, socket_address sa) {
return connected_socket(std::make_unique<native_connected_socket_impl<tcp<ipv4_traits>>>(
tcpv4.connect(sa)));
}
}