Commit fef89a5b authored by Jan Hák's avatar Jan Hák

tls: Add TLS workers (PoC, WiP)

parent 4fcb4dc0
......@@ -623,3 +623,40 @@ ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms)
return recv_data(sock, &msg, false, &timeout_ms);
}
//TODO unused
ssize_t net_dns_tls_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms)
{
if (sock < 0 || buffer == NULL) {
return KNOT_EINVAL;
}
struct iovec iov = { 0 };
struct msghdr msg = { 0 };
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* Receive size. */
uint16_t pktsize = 0;
iov.iov_base = &pktsize;
iov.iov_len = sizeof(pktsize);
int ret = recv_data(sock, &msg, false, &timeout_ms);
if (ret != sizeof(pktsize)) {
return ret;
}
pktsize = ntohs(pktsize);
/* Check packet size */
if (size < pktsize) {
return KNOT_ESPACE;
}
/* Receive payload. */
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = buffer;
iov.iov_len = pktsize;
return recv_data(sock, &msg, false, &timeout_ms);
}
......@@ -183,3 +183,26 @@ ssize_t net_dns_tcp_send(int sock, const uint8_t *buffer, size_t size, int timeo
* \see net_base_recv
*/
ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms);
/*!
* \brief Send a DNS message on a TCP socket (cyphered by TLS).
*
* The outgoing message is prefixed with a two-byte value carrying the DNS
* message size according to the specification. These two bytes are not
* reflected in the return value.
*
* \see net_base_send
*/
ssize_t net_dns_tls_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms);
/*!
* \brief Receive a DNS message from a TCP socket (cyphered by TLS).
*
* The first two bytes of the incoming message are interpreted as a DNS message
* size according to the specification. These two bytes are not included in
* the returned size. Only a complete DNS message is retrieved.
*
* \see net_base_recv
*/
ssize_t net_dns_tls_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms);
......@@ -134,6 +134,8 @@ libknotd_la_SOURCES = \
knot/journal/serialization.h \
knot/server/server.c \
knot/server/server.h \
knot/server/tls-handler.c \
knot/server/tls-handler.h \
knot/server/tcp-handler.c \
knot/server/tcp-handler.h \
knot/server/udp-handler.c \
......
......@@ -116,7 +116,7 @@ static void init_cache(
conf_t *conf,
bool reinit_cache)
{
/* For UDP, TCP and background workers, cache the numbers of running
/* For UDP, TCP, TLS and background workers, cache the numbers of running
* workers. Cache the setting of TCP reuseport too. These values
* can't change in runtime, while config data can.
*/
......@@ -125,12 +125,14 @@ static void init_cache(
static bool running_tcp_reuseport;
static size_t running_udp_threads;
static size_t running_tcp_threads;
static size_t running_tls_threads;
static size_t running_bg_threads;
if (first_init || reinit_cache) {
running_tcp_reuseport = conf_tcp_reuseport(conf);
running_udp_threads = conf_udp_threads(conf);
running_tcp_threads = conf_tcp_threads(conf);
running_tls_threads = conf_tls_threads(conf);
running_bg_threads = conf_bg_threads(conf);
first_init = false;
......@@ -184,6 +186,8 @@ static void init_cache(
conf->cache.srv_tcp_threads = running_tcp_threads;
conf->cache.srv_tls_threads = running_tls_threads;
conf->cache.srv_bg_threads = running_bg_threads;
conf->cache.srv_tcp_max_clients = conf_tcp_max_clients(conf);
......
......@@ -114,6 +114,7 @@ typedef struct {
bool srv_tcp_reuseport;
size_t srv_udp_threads;
size_t srv_tcp_threads;
size_t srv_tls_threads;
size_t srv_bg_threads;
size_t srv_tcp_max_clients;
int ctl_timeout;
......
......@@ -35,6 +35,7 @@
#define DBG_LOG(err) CONF_LOG(LOG_DEBUG, "%s (%s)", __func__, knot_strerror((err)));
#define DFLT_TCP_WORKERS_MIN 10
#define DFLT_TLS_WORKERS_MIN 10
#define DFLT_BG_WORKERS_MAX 10
#define FALLBACK_MAX_TCP_CLIENTS 100
......@@ -1127,6 +1128,19 @@ size_t conf_tcp_threads_txn(
return workers;
}
size_t conf_tls_threads_txn(
conf_t *conf,
knot_db_txn_t *txn)
{
conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_TLS_WORKERS);
int64_t workers = conf_int(&val);
if (workers == YP_NIL) {
return MAX(dt_optimal_size(), DFLT_TLS_WORKERS_MIN);
}
return workers;
}
size_t conf_bg_threads_txn(
conf_t *conf,
knot_db_txn_t *txn)
......
......@@ -693,6 +693,25 @@ static inline size_t conf_tcp_threads(
return conf_tcp_threads_txn(conf, &conf->read_txn);
}
/*!
* Gets the configured number of TLS threads.
*
* \param[in] conf Configuration.
* \param[in] txn Configuration DB transaction.
*
* \return Number of threads.
*/
size_t conf_tls_threads_txn(
conf_t *conf,
knot_db_txn_t *txn
);
static inline size_t conf_tls_threads(
conf_t *conf)
{
return conf_tls_threads_txn(conf, &conf->read_txn);
}
/*!
* Gets the configured number of worker threads.
*
......
......@@ -154,6 +154,7 @@ static const yp_item_t desc_server[] = {
{ C_PIDFILE, YP_TSTR, YP_VSTR = { "knot.pid" } },
{ C_UDP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } },
{ C_TCP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } },
{ C_TLS_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } },
{ C_BG_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } },
{ C_ASYNC_START, YP_TBOOL, YP_VNONE },
{ C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VINT = { 1, INT32_MAX, 10, YP_STIME } },
......@@ -174,6 +175,10 @@ static const yp_item_t desc_server[] = {
{ C_ECS, YP_TBOOL, YP_VNONE },
{ C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ C_TLS_PORT, YP_TINT, YP_VINT = { 1, UINT16_MAX, 853 } },
{ C_TLS_SERVICE_KEY, YP_TSTR, YP_VSTR = { "cert.key" } },
{ C_TLS_SERVICE_PEM, YP_TSTR, YP_VSTR = { "cert.pem" } },
{ C_TLS_SERVICE_OCSP, YP_TSTR, YP_VSTR = { "cert-ocsp.key" } },
// Legacy items.
{ C_MAX_TCP_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, YP_NIL } },
{ C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5, YP_STIME } },
......
......@@ -108,6 +108,11 @@
#define C_TIMER "\x05""timer"
#define C_TIMER_DB "\x08""timer-db"
#define C_TIMER_DB_MAX_SIZE "\x11""timer-db-max-size"
#define C_TLS_WORKERS "\x0B""tls-workers"
#define C_TLS_SERVICE_KEY "\x0F""tls-service-key"
#define C_TLS_SERVICE_PEM "\x0F""tls-service-pem"
#define C_TLS_SERVICE_OCSP "\x10""tls-service-ocsp"
#define C_TLS_PORT "\x08""tls-port"
#define C_TPL "\x08""template"
#define C_UDP_MAX_PAYLOAD "\x0F""udp-max-payload"
#define C_UDP_MAX_PAYLOAD_IPV4 "\x14""udp-max-payload-ipv4"
......
......@@ -32,6 +32,7 @@
#include "knot/server/server.h"
#include "knot/server/udp-handler.h"
#include "knot/server/tcp-handler.h"
#include "knot/server/tls-handler.h"
#include "knot/zone/timers.h"
#include "knot/zone/zonedb-load.h"
#include "knot/worker/pool.h"
......@@ -72,6 +73,16 @@ static void server_deinit_iface(iface_t *iface)
free(iface->fd_tcp);
}
/* Free TLS handler. */
if (iface->fd_tls != NULL) {
for (int i = 0; i < iface->fd_tls_count; i++) {
if (iface->fd_tls[i] > -1) {
close(iface->fd_tls[i]);
}
}
free(iface->fd_tls);
}
free(iface);
}
......@@ -196,6 +207,7 @@ static int enable_fastopen(int sock, int backlog)
* \param addr Socket address.
* \param udp_thread_count Number of created UDP workers.
* \param tcp_thread_count Number of created TCP workers.
* \param tls_thread_count Number of created TLS workers.
* \param tcp_reuseport Indication if reuseport on TCP is enabled.
*
* \retval Pointer to a new initialized inteface.
......@@ -203,6 +215,7 @@ static int enable_fastopen(int sock, int backlog)
*/
static iface_t *server_init_iface(struct sockaddr_storage *addr,
int udp_thread_count, int tcp_thread_count,
int tls_thread_count,
bool tcp_reuseport)
{
iface_t *new_if = calloc(1, sizeof(*new_if));
......@@ -220,6 +233,8 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr,
int udp_bind_flags = 0;
int tcp_socket_count = 1;
int tcp_bind_flags = 0;
int tls_socket_count = 1;
int tls_bind_flags = 0;
#ifdef ENABLE_REUSEPORT
udp_socket_count = udp_thread_count;
......@@ -228,12 +243,16 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr,
if (tcp_reuseport) {
tcp_socket_count = tcp_thread_count;
tcp_bind_flags |= NET_BIND_MULTIPLE;
tls_socket_count = tls_thread_count;
tls_bind_flags |= NET_BIND_MULTIPLE;
}
#endif
new_if->fd_udp = malloc(udp_socket_count * sizeof(int));
new_if->fd_tcp = malloc(tcp_socket_count * sizeof(int));
if (new_if->fd_udp == NULL || new_if->fd_tcp == NULL) {
new_if->fd_tls = malloc(tls_socket_count * sizeof(int));
if (new_if->fd_udp == NULL || new_if->fd_tcp == NULL || new_if->fd_tls == NULL) {
log_error("failed to initialize interface");
server_deinit_iface(new_if);
return NULL;
......@@ -335,6 +354,59 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr,
}
}
/* Create bound TLS sockets. */
warn_bind = true;
warn_bufsize = true;
warn_flag_misc = true;
sockaddr_port_set(addr, 50853);
sockaddr_tostr(addr_str, sizeof(addr_str), addr);
for (int i = 0; i < tls_socket_count; i++) {
int sock = net_bound_socket(SOCK_STREAM, addr, tls_bind_flags);
if (sock == KNOT_EADDRNOTAVAIL) {
tls_bind_flags |= NET_BIND_NONLOCAL;
sock = net_bound_socket(SOCK_STREAM, addr, tls_bind_flags);
if (sock >= 0 && warn_bind) {
log_warning("address %s TLS bound, but required nonlocal bind", addr_str);
warn_bind = false;
}
}
if (sock < 0) {
log_error("cannot bind address %s TLS (%s)", addr_str,
knot_strerror(sock));
server_deinit_iface(new_if);
return NULL;
}
// TLS is same protocol as TCP on TCP/IP transport layer
if (!enlarge_net_buffers(sock, TCP_MIN_RCVSIZE, TCP_MIN_SNDSIZE) &&
warn_bufsize) {
log_warning("failed to set network buffer sizes for TLS");
warn_bufsize = false;
}
new_if->fd_tls[new_if->fd_tls_count++] = sock;
/* Listen for incoming connections. */
int ret = listen(sock, TLS_BACKLOG_SIZE); //TODO GnuTLS
if (ret < 0) {
log_error("failed to listen on TCP interface %s", addr_str);
server_deinit_iface(new_if);
return NULL;
}
/* TCP Fast Open. */
ret = enable_fastopen(sock, TLS_BACKLOG_SIZE);
if (ret < 0 && warn_flag_misc) {
log_warning("failed to enable TCP Fast Open on %s (%s)",
addr_str, knot_strerror(ret));
warn_flag_misc = false;
}
// TODO GnuTLS initialization
}
return new_if;
}
......@@ -371,8 +443,9 @@ static int configure_sockets(conf_t *conf, server_t *s)
/* Create new interface. */
unsigned size_udp = s->handlers[IO_UDP].handler.unit->size;
unsigned size_tcp = s->handlers[IO_TCP].handler.unit->size;
unsigned size_tls = s->handlers[IO_TLS].handler.unit->size;
bool tcp_reuseport = conf->cache.srv_tcp_reuseport;
iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, tcp_reuseport);
iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, size_tls, tcp_reuseport);
if (new_if != NULL) {
add_tail(newlist, &new_if->n);
}
......@@ -386,7 +459,7 @@ static int configure_sockets(conf_t *conf, server_t *s)
/* Set the ID's (thread_id) of both the TCP and UDP threads. */
unsigned thread_count = 0;
for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) {
for (unsigned proto = IO_UDP; proto < IO_HANDLERS_SIZE; ++proto) {
dt_unit_t *tu = s->handlers[proto].handler.unit;
for (unsigned i = 0; i < tu->size; ++i) {
s->handlers[proto].handler.thread_id[i] = thread_count++;
......@@ -538,7 +611,7 @@ int server_start(server_t *server, bool async)
/* Start I/O handlers. */
server->state |= ServerRunning;
for (int proto = IO_UDP; proto <= IO_TCP; ++proto) {
for (int proto = IO_UDP; proto < IO_HANDLERS_SIZE; ++proto) {
if (server->handlers[proto].size > 0) {
int ret = dt_start(server->handlers[proto].handler.unit);
if (ret != KNOT_EOK) {
......@@ -667,6 +740,7 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server)
static bool warn_tcp_reuseport = true;
static bool warn_udp = true;
static bool warn_tcp = true;
static bool warn_tls = true;
static bool warn_bg = true;
static bool warn_listen = true;
......@@ -685,6 +759,11 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server)
warn_tcp = false;
}
if (warn_tls && server->handlers[IO_TLS].size != conf_tls_threads(conf)) {
log_warning(msg, "tls-workers");
warn_tls = false;
}
if (warn_bg && conf->cache.srv_bg_threads != conf_bg_threads(conf)) {
log_warning(msg, "background-workers");
warn_bg = false;
......@@ -798,7 +877,7 @@ static int set_handler(server_t *server, int index, unsigned size, runnable_t ru
return KNOT_EOK;
}
/*! \brief Reconfigure UDP and TCP query processing threads. */
/*! \brief Reconfigure UDP, TCP and TLS query processing threads. */
static int configure_threads(conf_t *conf, server_t *server)
{
int ret = set_handler(server, IO_UDP, conf->cache.srv_udp_threads, udp_master);
......@@ -806,7 +885,12 @@ static int configure_threads(conf_t *conf, server_t *server)
return ret;
}
return set_handler(server, IO_TCP, conf->cache.srv_tcp_threads, tcp_master);
ret = set_handler(server, IO_TCP, conf->cache.srv_tcp_threads, tcp_master);
if (ret != KNOT_EOK) {
return ret;
}
return set_handler(server, IO_TLS, conf->cache.srv_tls_threads, tls_master);
}
static int reconfigure_journal_db(conf_t *conf, server_t *server)
......
......@@ -56,13 +56,17 @@ typedef struct iface {
int fd_udp_count;
int *fd_tcp;
int fd_tcp_count;
int *fd_tls; //TODO init
int fd_tls_count; // TODO setup
struct sockaddr_storage addr;
} iface_t;
/* Handler indexes. */
enum {
IO_UDP = 0,
IO_TCP = 1
IO_TCP = 1,
IO_TLS = 2,
IO_HANDLERS_SIZE
};
/*!
......@@ -85,7 +89,7 @@ typedef struct server {
struct {
unsigned size;
iohandler_t handler;
} handlers[2];
} handlers[IO_HANDLERS_SIZE];
/*! \brief Background jobs. */
worker_pool_t *workers;
......
This diff is collapsed.
/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*!
* \brief TCP sockets threading model.
*
* The master socket distributes incoming connections among
* the worker threads ("buckets"). Each threads processes it's own
* set of sockets, and eliminates mutual exclusion problem by doing so.
*/
#pragma once
#include "knot/server/tcp-handler.h"
#include "knot/server/dthreads.h"
#define TLS_BACKLOG_SIZE 10 /*!< TLS listen backlog size. */
/*!
* \brief TLS handler thread runnable.
*
* Listens to both bound TLS sockets for client connections and
* serves TLS clients. This runnable is designed to be used as coherent
* and implements cancellation point.
*
* \param thread Associated thread from DThreads unit.
*
* \retval KNOT_EOK on success.
* \retval KNOT_EINVAL invalid parameters.
*/
int tls_master(dthread_t *thread);
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