262 lines
8.4 KiB
C++
262 lines
8.4 KiB
C++
|
|
#include <stdbool.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <assert.h>
|
||
|
|
#include <arpa/inet.h>
|
||
|
|
#include <linux/tcp.h>
|
||
|
|
|
||
|
|
#include <tfe_utils.h>
|
||
|
|
#include <tfe_tcp_restore.h>
|
||
|
|
|
||
|
|
static unsigned int fd_so_mask = 0x65;
|
||
|
|
|
||
|
|
void tfe_tcp_restore_info_dump(const struct tcp_restore_info *info)
|
||
|
|
{
|
||
|
|
char str_client_addr[64] = { 0 };
|
||
|
|
char str_server_addr[64] = { 0 };
|
||
|
|
|
||
|
|
const struct tcp_restore_endpoint *client = &info->client;
|
||
|
|
const struct tcp_restore_endpoint *server = &info->server;
|
||
|
|
|
||
|
|
assert(client->addr.ss_family == server->addr.ss_family);
|
||
|
|
|
||
|
|
if (client->addr.ss_family == AF_INET)
|
||
|
|
{
|
||
|
|
struct sockaddr_in *sk_client = (struct sockaddr_in *)&client->addr;
|
||
|
|
struct sockaddr_in *sk_server = (struct sockaddr_in *)&server->addr;
|
||
|
|
uint16_t port_client = ntohs(sk_client->sin_port);
|
||
|
|
uint16_t port_server = ntohs(sk_server->sin_port);
|
||
|
|
|
||
|
|
inet_ntop(AF_INET, &sk_client->sin_addr, str_client_addr, sizeof(str_client_addr));
|
||
|
|
inet_ntop(AF_INET, &sk_server->sin_addr, str_server_addr, sizeof(str_client_addr));
|
||
|
|
|
||
|
|
TFE_LOG_DEBUG(g_default_logger, "tcp_restore_info %p: cur_dir=%u, %s:%hu->%s:%hu, seq=%u, ack=%u, "
|
||
|
|
"client={ mss=%u, wscale_perm=%u, wscale=%u, ts=%u, sack=%u }, "
|
||
|
|
"server={ mss=%u, wscale_perm=%u, wscale=%u, ts=%u, sack=%u }",
|
||
|
|
info, info->cur_dir, str_client_addr, port_client, str_server_addr, port_server, info->client.seq, info->client.ack,
|
||
|
|
client->mss, (client->wscale_perm ? 1 : 0), client->wscale, (client->timestamp_perm ? 1 : 0), (client->sack_perm ? 1 : 0),
|
||
|
|
server->mss, (server->wscale_perm ? 1 : 0), server->wscale, (server->timestamp_perm ? 1 : 0), (server->sack_perm ? 1 : 0));
|
||
|
|
}
|
||
|
|
else if (client->addr.ss_family == AF_INET6)
|
||
|
|
{
|
||
|
|
struct sockaddr_in6 *sk_client = (struct sockaddr_in6 *)&client->addr;
|
||
|
|
struct sockaddr_in6 *sk_server = (struct sockaddr_in6 *)&server->addr;
|
||
|
|
uint16_t port_client = ntohs(sk_client->sin6_port);
|
||
|
|
uint16_t port_server = ntohs(sk_server->sin6_port);
|
||
|
|
|
||
|
|
inet_ntop(AF_INET6, &sk_client->sin6_addr, str_client_addr, sizeof(str_client_addr));
|
||
|
|
inet_ntop(AF_INET6, &sk_server->sin6_addr, str_server_addr, sizeof(str_client_addr));
|
||
|
|
|
||
|
|
TFE_LOG_DEBUG(g_default_logger, "tcp_restore_info %p: cur_dir=%u, %s:%hu->%s:%hu, seq=%u, ack=%u, "
|
||
|
|
"client={ mss=%u, wscale_perm=%u, wscale=%u, ts=%u, sack=%u }, "
|
||
|
|
"server={ mss=%u, wscale_perm=%u, wscale=%u, ts=%u, sack=%u }",
|
||
|
|
info, info->cur_dir, str_client_addr, port_client, str_server_addr, port_server, info->client.seq, info->client.ack,
|
||
|
|
client->mss, (client->wscale_perm ? 1 : 0), client->wscale, (client->timestamp_perm ? 1 : 0), (client->sack_perm ? 1 : 0),
|
||
|
|
server->mss, (server->wscale_perm ? 1 : 0), server->wscale, (server->timestamp_perm ? 1 : 0), (server->sack_perm ? 1 : 0));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int tfe_tcp_restore_fd_create(const struct tcp_restore_endpoint *endpoint, const struct tcp_restore_endpoint *peer)
|
||
|
|
{
|
||
|
|
int result = 0;
|
||
|
|
int sockopt = 0;
|
||
|
|
int sockfd = 0;
|
||
|
|
|
||
|
|
unsigned int nr_tcp_repair_opts = 0;
|
||
|
|
struct tcp_repair_opt tcp_repair_opts[8];
|
||
|
|
struct tcp_repair_window tcp_repair_window = { 0 };
|
||
|
|
|
||
|
|
if (endpoint->addr.ss_family == AF_INET)
|
||
|
|
{
|
||
|
|
sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
|
||
|
|
}
|
||
|
|
else if (endpoint->addr.ss_family == AF_INET6)
|
||
|
|
{
|
||
|
|
sockfd = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
errno = EINVAL;
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at tcp_restore_fd_create(), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sockfd < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at socket(), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Setup TCP REPAIR Status
|
||
|
|
sockopt = 1;
|
||
|
|
result = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(SO_REUSEADDR), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = 1;
|
||
|
|
result = setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(IP_TRANSPARENT), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = 1;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = fd_so_mask;
|
||
|
|
result = setsockopt(sockfd, SOL_SOCKET, SO_MARK, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(SO_MARK), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Setup SEQ/ACK and TCP options
|
||
|
|
sockopt = TCP_SEND_QUEUE;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR_QUEUE, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR_QUEUE), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = endpoint->seq;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_QUEUE_SEQ, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_QUEUE_SEQ), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = TCP_RECV_QUEUE;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR_QUEUE, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR_QUEUE), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = endpoint->ack;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_QUEUE_SEQ, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_QUEUE_SEQ), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef TCPOPT_MAXSEG
|
||
|
|
#define TCPOPT_MAXSEG 2
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef TCPOPT_WINDOW
|
||
|
|
#define TCPOPT_WINDOW 3
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef TCPOPT_SACK_PERMITTED
|
||
|
|
#define TCPOPT_SACK_PERMITTED 4
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef TCPOPT_TIMESTAMP
|
||
|
|
#define TCPOPT_TIMESTAMP 8
|
||
|
|
#endif
|
||
|
|
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_code = TCPOPT_MAXSEG;
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_val = MIN(endpoint->mss, peer->mss);
|
||
|
|
nr_tcp_repair_opts++;
|
||
|
|
|
||
|
|
if (endpoint->sack_perm && peer->sack_perm)
|
||
|
|
{
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_code = TCPOPT_SACK_PERMITTED;
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_val = 0;
|
||
|
|
nr_tcp_repair_opts++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (endpoint->wscale_perm && peer->wscale_perm)
|
||
|
|
{
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_code = TCPOPT_WINDOW;
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_val = (endpoint->wscale << 16) | peer->wscale;
|
||
|
|
nr_tcp_repair_opts++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (endpoint->timestamp_perm && peer->timestamp_perm)
|
||
|
|
{
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_code = TCPOPT_TIMESTAMP;
|
||
|
|
tcp_repair_opts[nr_tcp_repair_opts].opt_val = 0;
|
||
|
|
nr_tcp_repair_opts++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Bind address and connect to peer endpoint
|
||
|
|
result = bind(sockfd, (struct sockaddr *)&endpoint->addr, sizeof(endpoint->addr));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at bind(), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = connect(sockfd, (struct sockaddr *)&peer->addr, sizeof(peer->addr));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at connect(), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR_OPTIONS, (char *)tcp_repair_opts, nr_tcp_repair_opts * sizeof(struct tcp_repair_opt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR_OPTIONS), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (endpoint->timestamp_perm && peer->timestamp_perm)
|
||
|
|
{
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_TIMESTAMP, &(endpoint->ts_val), sizeof(endpoint->ts_val));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_TIMESTAMP), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Perpare Window Setup
|
||
|
|
tcp_repair_window.snd_wl1 = peer->seq;
|
||
|
|
tcp_repair_window.snd_wnd = peer->window;
|
||
|
|
tcp_repair_window.max_window = peer->window;
|
||
|
|
tcp_repair_window.rcv_wnd = endpoint->window;
|
||
|
|
tcp_repair_window.rcv_wup = endpoint->ack;
|
||
|
|
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR_WINDOW, (char *)&tcp_repair_window, sizeof(tcp_repair_window));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR_WINDOW), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
sockopt = 0;
|
||
|
|
result = setsockopt(sockfd, IPPROTO_TCP, TCP_REPAIR, (char *)&sockopt, sizeof(sockopt));
|
||
|
|
if (result < 0)
|
||
|
|
{
|
||
|
|
TFE_LOG_ERROR(g_default_logger, "failed at setsockopt(TCP_REPAIR), %d: %s", errno, strerror(errno));
|
||
|
|
goto errout;
|
||
|
|
}
|
||
|
|
|
||
|
|
return sockfd;
|
||
|
|
|
||
|
|
errout:
|
||
|
|
if (sockfd > 0)
|
||
|
|
{
|
||
|
|
close(sockfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|