Commit 9e40bb79 authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'tcp-fastopen' into 'master'

TCP Fast Open

See merge request !764
parents 9271b7ee a5c8d653
......@@ -205,6 +205,9 @@ Options
**+**\ [\ **no**\ ]\ **tcp**
Use the TCP protocol (default is UDP for standard query and TCP for AXFR/IXFR).
**+**\ [\ **no**\ ]\ **fastopen**
Use TCP Fast Open (default with TCP).
**+**\ [\ **no**\ ]\ **ignore**
Don't use TCP automatically if a truncated reply is received.
......
......@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <assert.h>
#include <urcu.h>
#include <netinet/tcp.h>
#include "libknot/errcode.h"
#include "libknot/yparser/ypschema.h"
......@@ -133,6 +134,24 @@ static bool enable_pktinfo(int sock, int family)
return setsockopt(sock, level, option, &on, sizeof(on)) == 0;
}
/**
* \brief Enable TCP Fast Open.
*/
static int enable_fastopen(int sock, int backlog)
{
#if __APPLE__
if (backlog > 0) {
backlog = 1; // just on-off switch on macOS
}
#endif
if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) != 0) {
return knot_map_errno();
}
return KNOT_EOK;
}
/*!
* \brief Initialize new interface from config value.
*
......@@ -242,6 +261,13 @@ static int server_init_iface(iface_t *new_if, struct sockaddr_storage *addr, int
return KNOT_ERROR;
}
/* TCP Fast Open. */
ret = enable_fastopen(sock, TCP_BACKLOG_SIZE);
if (ret < 0) {
log_warning("failed to enable TCP Fast Open on '%s' (%s)",
addr_str, knot_strerror(ret));
}
return KNOT_EOK;
}
......
......@@ -163,6 +163,7 @@ int net_init(const srv_info_t *local,
const int iptype,
const int socktype,
const int wait,
const net_flags_t flags,
const tls_params_t *tls_params,
net_t *net)
{
......@@ -195,6 +196,7 @@ int net_init(const srv_info_t *local,
net->wait = wait;
net->local = local;
net->remote = remote;
net->flags = flags;
// Prepare for TLS.
if (tls_params != NULL && tls_params->enable) {
......@@ -207,6 +209,57 @@ int net_init(const srv_info_t *local,
return KNOT_EOK;
}
/**
* Connect with TCP Fast Open.
*/
static int fastopen_connect(int sockfd, const struct addrinfo *srv)
{
#if __APPLE__
// connection is performed lazily when first data are sent
struct sa_endpoints ep = {0};
ep.sae_dstaddr = srv->ai_addr;
ep.sae_dstaddrlen = srv->ai_addrlen;
int flags = CONNECT_DATA_IDEMPOTENT|CONNECT_RESUME_ON_READ_WRITE;
return connectx(sockfd, &ep, SAE_ASSOCID_ANY, flags, NULL, 0, NULL, NULL);
#elif defined(MSG_FASTOPEN) // Linux with RFC 7413
// connect() will be called implicitly with sendto(), sendmsg()
return 0;
#else
errno = ENOTSUP;
return -1;
#endif
}
/**
* Sends data with TCP Fast Open.
*/
static int fastopen_send(int sockfd, const struct msghdr *msg, int timeout)
{
#if __APPLE__
return sendmsg(sockfd, msg, 0);
#elif defined(MSG_FASTOPEN)
int ret = sendmsg(sockfd, msg, MSG_FASTOPEN);
if (ret == -1 && EINPROGRESS) {
struct pollfd pfd = {
.fd = sockfd,
.events = POLLOUT,
.revents = 0,
};
if (poll(&pfd, 1, 1000 * timeout) != 1) {
errno = ETIMEDOUT;
return -1;
}
ret = sendmsg(sockfd, msg, 0);
}
return ret;
#else
errno = ENOTSUP;
return -1;
#endif
}
int net_connect(net_t *net)
{
if (net == NULL || net->srv == NULL) {
......@@ -248,19 +301,24 @@ int net_connect(net_t *net)
}
if (net->socktype == SOCK_STREAM) {
int cs, err = 0;
int cs, err, ret = 0;
socklen_t err_len = sizeof(err);
bool fastopen = net->flags & NET_FLAGS_FASTOPEN;
// Connect using socket.
if (connect(sockfd, net->srv->ai_addr, net->srv->ai_addrlen)
== -1 && errno != EINPROGRESS) {
if (fastopen) {
ret = fastopen_connect(sockfd, net->srv);
} else {
ret = connect(sockfd, net->srv->ai_addr, net->srv->ai_addrlen);
}
if (ret != 0 && errno != EINPROGRESS) {
WARN("can't connect to %s\n", net->remote_str);
close(sockfd);
return KNOT_NET_ECONNECT;
}
// Check for connection timeout.
if (poll(&pfd, 1, 1000 * net->wait) != 1) {
if (!fastopen && poll(&pfd, 1, 1000 * net->wait) != 1) {
WARN("connection timeout for %s\n", net->remote_str);
close(sockfd);
return KNOT_NET_ECONNECT;
......@@ -347,11 +405,12 @@ int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len)
}
// Send data over TCP.
} else {
struct iovec iov[2];
bool fastopen = net->flags & NET_FLAGS_FASTOPEN;
// Leading packet length bytes.
uint16_t pktsize = htons(buf_len);
struct iovec iov[2];
iov[0].iov_base = &pktsize;
iov[0].iov_len = sizeof(pktsize);
iov[1].iov_base = (uint8_t *)buf;
......@@ -360,8 +419,19 @@ int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len)
// Compute packet total length.
ssize_t total = iov[0].iov_len + iov[1].iov_len;
// Send data.
if (writev(net->sockfd, iov, 2) != total) {
struct msghdr msg = {0};
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
msg.msg_name = net->srv->ai_addr;
msg.msg_namelen = net->srv->ai_addrlen;
int ret = 0;
if (fastopen) {
ret = fastopen_send(net->sockfd, &msg, net->wait);
} else {
ret = sendmsg(net->sockfd, &msg, 0);
}
if (ret != total) {
WARN("can't send query to %s\n", net->remote_str);
return KNOT_NET_ESEND;
}
......
......@@ -41,16 +41,23 @@ typedef struct {
char *service;
} srv_info_t;
typedef enum {
NET_FLAGS_NONE = 0,
NET_FLAGS_FASTOPEN = 1 << 0,
} net_flags_t;
typedef struct {
/*! Socket descriptor. */
int sockfd;
int sockfd;
/*! IP protocol type. */
int iptype;
int iptype;
/*! Socket type. */
int socktype;
int socktype;
/*! Timeout for all network operations. */
int wait;
int wait;
/*! Connection flags. */
net_flags_t flags;
/*! Local interface parameters. */
const srv_info_t *local;
......@@ -145,6 +152,7 @@ void get_addr_str(const struct sockaddr_storage *ss,
* \param socktype Socket type.
* \param wait Network timeout interval.
* \param tls_params TLS parameters.
* \param flags Connection flags.
* \param net Network structure to initialize.
*
* \retval KNOT_EOK if success.
......@@ -155,6 +163,7 @@ int net_init(const srv_info_t *local,
const int iptype,
const int socktype,
const int wait,
const net_flags_t flags,
const tls_params_t *tls_params,
net_t *net);
......
......@@ -707,6 +707,7 @@ static void process_query(const query_t *query)
// Get connection parameters.
int iptype = get_iptype(query->ip);
int socktype = get_socktype(query->protocol, query->type_num);
int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE;
// Loop over server list to process query.
WALK_LIST(server, query->servers) {
......@@ -721,7 +722,7 @@ static void process_query(const query_t *query)
for (size_t i = 0; i <= query->retries; i++) {
// Initialize network structure for current server.
ret = net_init(query->local, remote, iptype, socktype,
query->wait, &query->tls, &net);
query->wait, flags, &query->tls, &net);
if (ret != KNOT_EOK) {
continue;
}
......@@ -990,6 +991,7 @@ static void process_xfr(const query_t *query)
// Get connection parameters.
int iptype = get_iptype(query->ip);
int socktype = get_socktype(query->protocol, query->type_num);
int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE;
// Use the first nameserver from the list.
srv_info_t *remote = HEAD(query->servers);
......@@ -1001,7 +1003,7 @@ static void process_xfr(const query_t *query)
// Initialize network structure.
ret = net_init(query->local, remote, iptype, socktype, query->wait,
&query->tls, &net);
flags, &query->tls, &net);
if (ret != KNOT_EOK) {
sign_context_deinit(&sign_ctx);
knot_pkt_free(&out_packet);
......
......@@ -565,6 +565,24 @@ static int opt_notcp(const char *arg, void *query)
return opt_ignore(arg, query);
}
static int opt_fastopen(const char *arg, void *query)
{
query_t *q = query;
q->fastopen = true;
return KNOT_EOK;
}
static int opt_nofastopen(const char *arg, void *query)
{
query_t *q = query;
q->fastopen = false;
return opt_ignore(arg, query);
}
static int opt_tls(const char *arg, void *query)
{
query_t *q = query;
......@@ -1019,6 +1037,9 @@ static const param_t kdig_opts2[] = {
{ "tcp", ARG_NONE, opt_tcp },
{ "notcp", ARG_NONE, opt_notcp },
{ "fastopen", ARG_NONE, opt_fastopen },
{ "nofastopen", ARG_NONE, opt_nofastopen },
{ "ignore", ARG_NONE, opt_ignore },
{ "noignore", ARG_NONE, opt_noignore },
......@@ -1096,6 +1117,7 @@ query_t *query_create(const char *owner, const query_t *conf)
query->operation = OPERATION_QUERY;
query->ip = IP_ALL;
query->protocol = PROTO_ALL;
query->fastopen = true;
query->port = strdup("");
query->udp_size = -1;
query->retries = DEFAULT_RETRIES_DIG;
......@@ -1134,6 +1156,7 @@ query_t *query_create(const char *owner, const query_t *conf)
query->operation = conf->operation;
query->ip = conf->ip;
query->protocol = conf->protocol;
query->fastopen = conf->fastopen;
query->port = strdup(conf->port);
query->udp_size = conf->udp_size;
query->retries = conf->retries;
......@@ -1633,6 +1656,7 @@ static void print_help(void)
" +[no]ttl Show TTL value.\n"
" +[no]crypto Show binary parts of RRSIGs and DNSKEYs.\n"
" +[no]tcp Use TCP protocol.\n"
" +[no]fastopen Use TCP Fast Open.\n"
" +[no]ignore Don't use TCP automatically if truncated.\n"
" +[no]tls Use TLS with Opportunistic privacy profile.\n"
" +[no]tls-ca[=FILE] Use TLS with Out-Of-Band privacy profile.\n"
......
......@@ -88,6 +88,8 @@ struct query {
ip_t ip;
/*!< Protocol type (TCP, UDP) to use. */
protocol_t protocol;
/*!< Use TCP Fast Open. */
bool fastopen;
/*!< Port/service to connect to. */
char *port;
/*!< UDP buffer size (16unsigned + -1 uninitialized). */
......
......@@ -434,6 +434,7 @@ static int pkt_sendrecv(knsupdate_params_t *params)
get_iptype(params->ip),
get_socktype(params->protocol, KNOT_RRTYPE_SOA),
params->wait,
NET_FLAGS_NONE,
NULL,
&net);
if (ret != KNOT_EOK) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment