cutest_udbrad.o: $(srcdir)/tpkg/cutest/cutest_udbrad.c config.h \
$(srcdir)/tpkg/cutest/cutest.h $(srcdir)/udbradtree.h $(srcdir)/udb.h
cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c config.h $(srcdir)/tpkg/cutest/cutest.h \
- $(srcdir)/region-allocator.h $(srcdir)/util.h
+ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/xfrd-tcp.h
qtest.o: $(srcdir)/tpkg/cutest/qtest.c config.h $(srcdir)/tpkg/cutest/qtest.h $(srcdir)/buffer.h \
$(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h \
$(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/namedb.h $(srcdir)/util.h $(srcdir)/nsec3.h \
answer-cookie{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ANSWER_COOKIE;}
cookie-secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET;}
cookie-secret-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET_FILE;}
+xfrd-tcp-max{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_MAX;}
+xfrd-tcp-pipeline{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_PIPELINE;}
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
servers={UNQUOTEDLETTER}* {
%token VAR_XFRD_CPU_AFFINITY
%token <llng> VAR_SERVER_CPU_AFFINITY
%token VAR_DROP_UPDATES
+%token VAR_XFRD_TCP_MAX
+%token VAR_XFRD_TCP_PIPELINE
/* dnstap */
%token VAR_DNSTAP
{ cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2); }
| VAR_COOKIE_SECRET_FILE STRING
{ cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_XFRD_TCP_MAX number
+ { cfg_parser->opt->xfrd_tcp_max = (int)$2; }
+ | VAR_XFRD_TCP_PIPELINE number
+ { cfg_parser->opt->xfrd_tcp_pipeline = (int)$2; }
| VAR_CPU_AFFINITY cpus
{
cfg_parser->opt->cpu_affinity = $2;
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for NSD 4.3.9.
+# Generated by GNU Autoconf 2.69 for NSD 4.4.0.
#
# Report bugs to <nsd-bugs@nlnetlabs.nl>.
#
# Identity of this package.
PACKAGE_NAME='NSD'
PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.3.9'
-PACKAGE_STRING='NSD 4.3.9'
+PACKAGE_VERSION='4.4.0'
+PACKAGE_STRING='NSD 4.4.0'
PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl'
PACKAGE_URL=''
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures NSD 4.3.9 to adapt to many kinds of systems.
+\`configure' configures NSD 4.4.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of NSD 4.3.9:";;
+ short | recursive ) echo "Configuration of NSD 4.4.0:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-NSD configure 4.3.9
+NSD configure 4.4.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by NSD $as_me 4.3.9, which was
+It was created by NSD $as_me 4.4.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by NSD $as_me 4.3.9, which was
+This file was extended by NSD $as_me 4.4.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-NSD config.status 4.3.9
+NSD config.status 4.4.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
sinclude(acx_nlnetlabs.m4)
sinclude(dnstap/dnstap.m4)
-AC_INIT([NSD],[4.3.9],[nsd-bugs@nlnetlabs.nl])
+AC_INIT([NSD],[4.4.0],[nsd-bugs@nlnetlabs.nl])
AC_CONFIG_HEADERS([config.h])
#
#endif /* HAVE_MMAP */
}
-/** the the file mtime stat (or nonexist or error) */
+/** get the file mtime stat (or nonexist or error) */
int
file_get_mtime(const char* file, struct timespec* mtime, int* nonexist)
{
+10 February 2022: Wouter
+ - Tag for 4.4.0rc1 release.
+
+9 February 2022: Wouter
+ - Fix unit tests for nds-control-setup exit code and the
+ xfrd-tcp-max default.
+
+7 February 2022: Wouter
+ - Merge #207 Sync nsd-control-setup with unbound-control-setup to
+ generate certificates with SANs.
+
+28 January 2022: Wouter
+ - Fix #206: build with --without-ssl fails.
+
+27 January 2022: Wouter
+ - current code branch continues as version 4.4.0, because of added
+ feature.
+
+26 January 2022: Wouter
+ - Merge #193: Lower memory usage of the XFRD process by default.
+ Instead of preallocating all elements, they are allocated when used.
+ There are options for managing the memory usage, defaults are the
+ same as before. xfrd-tcp-max sets the number of sockets for tcp
+ connections that xfrd can make to download zone contents. And
+ xfrd-tcp-pipeline the number of simultaneous transfers over the
+ same connection.
+
+12 January 2022: Wouter
+ - Fix to document nsd-checkzone -p in the man page for nsd-checkzone.
+
+7 January 2022: Wouter
+ - Fix to change file mode before changing file owner for the
+ nsd-control unix socket file.
+
+3 January 2022: Wouter
+ - Merge #204 from jonathangray: correct some spelling mistakes.
+
+15 December 2021: Wouter
+ - Fix #200: nsd-checkzone succeeds even with incorrect serial in SOA
+ record.
+
2 December 2021: Wouter
- Fix socket_partitioning unit test for FreeBSD.
- Fix SVCB test to work around older dig with drill.
+ - Fix unit test to not syslog setlogin failures.
1 December 2021: Wouter
- Set up for branch for 4.3.9 release.
+ This became release 4.3.9 on 9 Dec 2021 and included the changes
+ until the SVCB fix on 2 dec 2021, but not the setlogin fix.
+ The main branch continues as 4.3.10.
- Fix unit tests for new answer-cookie default.
30 November 2021: Wouter
NSD RELEASE NOTES
+4.4.0
+================
+FEATURES:
+ - Merge #193: Lower memory usage of the XFRD process by default.
+ Instead of preallocating all elements, they are allocated when used.
+ There are options for managing the memory usage, defaults are the
+ same as before. xfrd-tcp-max sets the number of sockets for tcp
+ connections that xfrd can make to download zone contents. And
+ xfrd-tcp-pipeline the number of simultaneous transfers over the
+ same connection.
+BUG FIXES:
+ - Fix #200: nsd-checkzone succeeds even with incorrect serial in SOA
+ record.
+ - Merge #204 from jonathangray: correct some spelling mistakes.
+ - Fix to change file mode before changing file owner for the
+ nsd-control unix socket file.
+ - Fix to document nsd-checkzone -p in the man page for nsd-checkzone.
+ - Fix #206: build with --without-ssl fails.
+ - Merge #207 Sync nsd-control-setup with unbound-control-setup to
+ generate certificates with SANs.
+ - Fix unit tests for nds-control-setup exit code and the
+ xfrd-tcp-max default.
+
4.3.9
================
BUG FIXES:
#include "nsd.h"
#include "query.h"
+#if !defined(HAVE_SSL) || !defined(HAVE_CRYPTO_MEMCMP)
+/* we need fixed time compare, pull it in from tsig.c */
+#define CRYPTO_memcmp memcmp_fixedtime
+int memcmp_fixedtime(const void *s1, const void *s2, size_t n);
+#endif
+
void
edns_init_data(edns_data_type *data, uint16_t max_length)
{
{
for (j=0; j<8; ++j) /*------------------------ for each input bit, */
{
- for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */
+ for (m=1; m<8; ++m) /*------------ for several possible initvals, */
{
for (l=0; l<HASHSTATE; ++l)
e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
-.TH "nsd\-checkconf" "8" "Dec 9, 2021" "NLnet Labs" "nsd 4.3.9"
+.TH "nsd\-checkconf" "8" "Feb 17, 2022" "NLnet Labs" "nsd 4.4.0"
.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
SERV_GET_INT(tcp_timeout, o);
SERV_GET_INT(tcp_mss, o);
SERV_GET_INT(outgoing_tcp_mss, o);
+ SERV_GET_INT(xfrd_tcp_max, o);
+ SERV_GET_INT(xfrd_tcp_pipeline, o);
SERV_GET_INT(ipv4_edns_size, o);
SERV_GET_INT(ipv6_edns_size, o);
SERV_GET_INT(statistics, o);
printf("\ttcp-timeout: %d\n", opt->tcp_timeout);
printf("\ttcp-mss: %d\n", opt->tcp_mss);
printf("\toutgoing-tcp-mss: %d\n", opt->outgoing_tcp_mss);
+ printf("\txfrd-tcp-max: %d\n", opt->xfrd_tcp_max);
+ printf("\txfrd-tcp-pipeline: %d\n", opt->xfrd_tcp_pipeline);
printf("\tipv4-edns-size: %d\n", (int) opt->ipv4_edns_size);
printf("\tipv6-edns-size: %d\n", (int) opt->ipv6_edns_size);
print_string_var("pidfile:", opt->pidfile);
-.TH "nsd\-checkzone" "8" "Dec 9, 2021" "NLnet Labs" "nsd 4.3.9"
+.TH "nsd\-checkzone" "8" "Feb 17, 2022" "NLnet Labs" "nsd 4.4.0"
.\" Copyright (c) 2014, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
.TP
.I zonefile
The file to read, eg. "zones/example.com.zone.signed".
+.TP
+.B \-p
+Print the zone contents to stdout if the zone is ok. This prints the
+contents as it has been parsed, not literally a copy of the input, but
+as printed by the formatting routines in NSD, much like the nsd-control
+command write does.
.SH "SEE ALSO"
\fInsd\fR(8), \fInsd-checkconf\fR(8)
.SH "AUTHORS"
CLIENTNAME=nsd-control
# validity period for certificates
-DAYS=3650
+DAYS=7200
# size of keys in bits
BITS=3072
usage() {
cat <<EOF
usage: $0 OPTIONS
-
OPTIONS
-
-d <dir> used directory to store keys and certificates (default: $DESTDIR)
-h show help notice
-r recreate certificates
while getopts 'd:hr' arg; do
case "$arg" in
d) DESTDIR="$OPTARG" ;;
- h) usage; exit 0 ;;
+ h) usage; exit 1 ;;
r) RECREATE=1 ;;
?) fatal "'$arg' unknown option" ;;
esac
fi
cat >server.cnf <<EOF
+[req]
default_bits=$BITS
default_md=$HASH
prompt=no
distinguished_name=req_distinguished_name
-
+x509_extensions=v3_ca
[req_distinguished_name]
commonName=$SERVERNAME
+[v3_ca]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints=critical,CA:TRUE,pathlen:0
+subjectAltName=DNS:$SERVERNAME
EOF
[ -f server.cnf ] || fatal "cannot create openssl configuration"
default_md=$HASH
prompt=no
distinguished_name=req_distinguished_name
-
+req_extensions=v3_req
[req_distinguished_name]
commonName=$CLIENTNAME
+[v3_req]
+basicConstraints=critical,CA:FALSE
+subjectAltName=DNS:$CLIENTNAME
EOF
[ -f client.cnf ] || fatal "cannot create openssl configuration"
-CAkey "$SVR_BASE.key" \
-CAcreateserial \
-$HASH \
+ -extfile client.cnf \
+ -extensions v3_req \
-out "$CTL_BASE.pem"
[ ! -f "CTL_BASE.pem" ] || fatal "cannot create signed client certificate"
echo "Setup success. Certificates created. Enable in nsd.conf file to use"
-
# create trusted usage pem
# openssl x509 -in $CTL_BASE.pem -addtrust clientAuth -out $CTL_BASE"_trust.pem"
-.TH "nsd\-control" "8" "Dec 9, 2021" "NLnet Labs" "nsd 4.3.9"
+.TH "nsd\-control" "8" "Feb 17, 2022" "NLnet Labs" "nsd 4.4.0"
.\" Copyright (c) 2011, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
-.TH "NSD" "8" "Dec 9, 2021" "NLnet Labs" "NSD 4.3.9"
+.TH "NSD" "8" "Feb 17, 2022" "NLnet Labs" "NSD 4.4.0"
.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
.B nsd
-\- Name Server Daemon (NSD) version 4.3.9.
+\- Name Server Daemon (NSD) version 4.4.0.
.SH "SYNOPSIS"
.B nsd
.RB [ \-4 ]
-.TH "nsd.conf" "5" "Dec 9, 2021" "NLnet Labs" "nsd 4.3.9"
+.TH "nsd.conf" "5" "Feb 17, 2022" "NLnet Labs" "nsd 4.4.0"
.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
.TP
.B outgoing\-tcp\-mss:\fR <number>
Maximum segment size (MSS) of TCP socket for outgoing XFR request
-to other namesevers. Value lower than
+to other nameservers. Value lower than
common MSS on Ethernet (1220 for example) will
address path MTU problem.
Note that not all platform supports socket option to set MSS (TCP_MAXSEG).
Default is system default MSS determined by interface MTU and
negotiation between NSD and other servers.
.TP
+.B xfrd\-tcp\-max:\fR <number>
+Number of sockets for xfrd to use for outgoing zone transfers. Default 128.
+Increase it to allow more zone transfer sockets, like to 256.
+To save memory, this can be lowered, set it lower together with some other
+settings to have reduced memory footprint for NSD. xfrd\-tcp\-max: 32
+and xfrd\-tcp\-pipeline: 128 and rrl\-size: 1000
+.IP
+This reduces memory footprint, other memory usage is caused mainly by
+the server\-count setting, the number of server processes, and the
+tcp\-count setting, which keeps buffers per server process, and by the
+size of the zone data.
+.TP
+.B xfrd\-tcp\-pipeline:\fR <number>
+Number of simultaneous outgoing zone transfers that are possible on the
+tcp sockets of xfrd. Max is 65536, default is 128.
+.TP
.B ipv4\-edns\-size:\fR <number>
Preferred EDNS buffer size for IPv4. Default 1232.
.TP
.\" rrlend
.TP
.B answer\-cookie:\fR <yes or no>
-Enable to answer to requests containig DNS Cookies as specified in RFC7873.
+Enable to answer to requests containing DNS Cookies as specified in RFC7873.
Default is yes.
.TP
.B cookie\-secret:\fR <128 bit hex string>
# Default is 0, system default MSS.
# outgoing-tcp-mss: 0
+ # reduce these settings to save memory for NSD, to about
+ # xfrd-tcp-max: 32 and xfrd-tcp-pipeline: 128, also rrl-size: 1000
+ # other memory is determined by server-count, tcp-count and zone data
+ # max number of sockets used for outgoing zone transfers.
+ # Increase this to allow more sockets for zone transfers.
+ # xfrd-tcp-max: 128
+ # max number of simultaneous outgoing zone transfers over one socket.
+ # xfrd-tcp-pipeline: 128
+
# Preferred EDNS buffer size for IPv4.
# ipv4-edns-size: 1232
#request-xfr: 192.0.2.2 the_tsig_key_name
#request-xfr: 192.0.2.2 the_tsig_key_name the_tls_auth_name
# Attention: You cannot use UDP and AXFR together. AXFR is always over
- # TCP. If you use UDP, we higly recommend you to deploy TSIG.
+ # TCP. If you use UDP, we highly recommend you to deploy TSIG.
# Allow AXFR fallback if the master does not support IXFR. Default
# is yes.
#allow-axfr-fallback: yes
opt->port = UDP_PORT;
/* deprecated? opt->port = TCP_PORT; */
opt->reuseport = 0;
+ opt->xfrd_tcp_max = 128;
+ opt->xfrd_tcp_pipeline = 128;
opt->statistics = 0;
opt->chroot = 0;
opt->username = USER;
int minimal_responses;
int refuse_any;
int reuseport;
+ /* max number of xfrd tcp sockets */
+ int xfrd_tcp_max;
+ /* max number of simultaneous requests on xfrd tcp socket */
+ int xfrd_tcp_pipeline;
/* private key file for TLS */
char* tls_service_key;
*/
if(fd != -1) {
#ifdef HAVE_CHOWN
+ if(chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
+ VERBOSITY(3, (LOG_INFO, "cannot chmod control socket %s: %s", ip, strerror(errno)));
+ }
if (cfg->username && cfg->username[0] &&
nsd.uid != (uid_t)-1) {
if(chown(ip, nsd.uid, nsd.gid) == -1)
(unsigned)nsd.uid, (unsigned)nsd.gid,
ip, strerror(errno)));
}
- if(chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
- VERBOSITY(3, (LOG_INFO, "cannot chmod control socket %s: %s", ip, strerror(errno)));
- }
#else
(void)cfg;
#endif
*/
void rrl_init(size_t ch);
-/** deinit (for this child server processs) */
+/** deinit (for this child server process) */
void rrl_deinit(size_t ch);
/** deinit mmaps for n children */
}
if (!(tcp_fastopen_value & TCP_FASTOPEN_SERVER_BIT_MASK)) {
log_msg(LOG_WARNING, "Error: TCP Fast Open support is available and configured in NSD by default.\n");
- log_msg(LOG_WARNING, "However the kernel paramenters are not configured to support TCP_FASTOPEN in server mode.\n");
+ log_msg(LOG_WARNING, "However the kernel parameters are not configured to support TCP_FASTOPEN in server mode.\n");
log_msg(LOG_WARNING, "To enable TFO use the command:");
log_msg(LOG_WARNING, " 'sudo sysctl -w net.ipv4.tcp_fastopen=2' for pure server mode or\n");
log_msg(LOG_WARNING, " 'sudo sysctl -w net.ipv4.tcp_fastopen=3' for both client and server mode\n");
return ge;
}
-/** see if free space is enogh to warrant shrink (while file is open) */
+/** see if free space is enough to warrant shrink (while file is open) */
static int
enough_free(udb_alloc* alloc)
{
udb_ptr** ram_hash;
/** size of the current udb_ptr hashtable array */
size_t ram_size;
- /** mask for the curren udb_ptr hashtable lookups */
+ /** mask for the current udb_ptr hashtable lookups */
int ram_mask;
/** number of ptrs in ram, used to decide when to grow */
size_t ram_num;
i += (**endptr - '0');
break;
default:
- break;
+ return 0;
}
}
serial += i;
return (uintptr_t)x < (uintptr_t)y ? -1 : 1;
}
-struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle)
+struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle, int tcp_max, int tcp_pipeline)
{
int i;
struct xfrd_tcp_set* tcp_set = region_alloc(region,
sizeof(struct xfrd_tcp_set));
memset(tcp_set, 0, sizeof(struct xfrd_tcp_set));
+ tcp_set->tcp_state = NULL;
+ tcp_set->tcp_max = tcp_max;
+ tcp_set->tcp_pipeline = tcp_pipeline;
tcp_set->tcp_count = 0;
tcp_set->tcp_waiting_first = 0;
tcp_set->tcp_waiting_last = 0;
(void)tls_cert_bundle;
log_msg(LOG_INFO, "xfrd: No TLS 1.3 support - XFR-over-TLS not available");
#endif
- for(i=0; i<XFRD_MAX_TCP; i++)
- tcp_set->tcp_state[i] = xfrd_tcp_pipeline_create(region);
+ tcp_set->tcp_state = region_alloc(region,
+ sizeof(*tcp_set->tcp_state)*tcp_set->tcp_max);
+ for(i=0; i<tcp_set->tcp_max; i++)
+ tcp_set->tcp_state[i] = xfrd_tcp_pipeline_create(region,
+ tcp_pipeline);
tcp_set->pipetree = rbtree_create(region, &xfrd_pipe_cmp);
return tcp_set;
}
+static int pipeline_id_compare(const void* x, const void* y)
+{
+ struct xfrd_tcp_pipeline_id* a = (struct xfrd_tcp_pipeline_id*)x;
+ struct xfrd_tcp_pipeline_id* b = (struct xfrd_tcp_pipeline_id*)y;
+ if(a->id < b->id)
+ return -1;
+ if(a->id > b->id)
+ return 1;
+ return 0;
+}
+
+void pick_id_values(uint16_t* array, int num, int max)
+{
+ uint8_t inserted[65536];
+ int j, done;
+ if(num == 65536) {
+ /* all of them, loop and insert */
+ int i;
+ for(i=0; i<num; i++)
+ array[i] = (uint16_t)i;
+ return;
+ }
+ assert(max <= 65536);
+ /* This uses the Robert Floyd sampling algorithm */
+ /* keep track if values are already inserted, using the bitmap
+ * in insert array */
+ memset(inserted, 0, sizeof(inserted[0])*max);
+ done=0;
+ for(j = max-num; j<max; j++) {
+ /* random generate creates from 0..arg-1 */
+ int t;
+ if(j+1 <= 1)
+ t = 0;
+ else t = random_generate(j+1);
+ if(!inserted[t]) {
+ array[done++]=t;
+ inserted[t] = 1;
+ } else {
+ array[done++]=j;
+ inserted[j] = 1;
+ }
+ }
+}
+
+static void
+clear_pipeline_entry(struct xfrd_tcp_pipeline* tp, rbnode_type* node)
+{
+ struct xfrd_tcp_pipeline_id *n;
+ if(node == NULL || node == RBTREE_NULL)
+ return;
+ clear_pipeline_entry(tp, node->left);
+ node->left = NULL;
+ clear_pipeline_entry(tp, node->right);
+ node->right = NULL;
+ /* move the node into the free list */
+ n = (struct xfrd_tcp_pipeline_id*)node;
+ n->next_free = tp->pipe_id_free_list;
+ tp->pipe_id_free_list = n;
+}
+
+static void
+xfrd_tcp_pipeline_cleanup(struct xfrd_tcp_pipeline* tp)
+{
+ /* move entries into free list */
+ clear_pipeline_entry(tp, tp->zone_per_id->root);
+ /* clear the tree */
+ tp->zone_per_id->count = 0;
+ tp->zone_per_id->root = RBTREE_NULL;
+}
+
+static void
+xfrd_tcp_pipeline_init(struct xfrd_tcp_pipeline* tp)
+{
+ tp->key.node.key = tp;
+ tp->key.num_unused = tp->pipe_num;
+ tp->key.num_skip = 0;
+ tp->tcp_send_first = NULL;
+ tp->tcp_send_last = NULL;
+ xfrd_tcp_pipeline_cleanup(tp);
+ pick_id_values(tp->unused, tp->pipe_num, 65536);
+}
+
struct xfrd_tcp_pipeline*
-xfrd_tcp_pipeline_create(region_type* region)
+xfrd_tcp_pipeline_create(region_type* region, int tcp_pipeline)
{
int i;
struct xfrd_tcp_pipeline* tp = (struct xfrd_tcp_pipeline*)
region_alloc_zero(region, sizeof(*tp));
- tp->key.num_unused = ID_PIPE_NUM;
- assert(sizeof(tp->unused)/sizeof(tp->unused[0]) == ID_PIPE_NUM);
- for(i=0; i<ID_PIPE_NUM; i++)
- tp->unused[i] = (uint16_t)i;
+ if(tcp_pipeline < 0)
+ tcp_pipeline = 0;
+ if(tcp_pipeline > 65536)
+ tcp_pipeline = 65536; /* max 16 bit ID numbers */
+ tp->pipe_num = tcp_pipeline;
+ tp->key.num_unused = tp->pipe_num;
+ tp->zone_per_id = rbtree_create(region, &pipeline_id_compare);
+ tp->pipe_id_free_list = NULL;
+ for(i=0; i<tp->pipe_num; i++) {
+ struct xfrd_tcp_pipeline_id* n = (struct xfrd_tcp_pipeline_id*)
+ region_alloc_zero(region, sizeof(*n));
+ n->next_free = tp->pipe_id_free_list;
+ tp->pipe_id_free_list = n;
+ }
+ tp->unused = (uint16_t*)region_alloc_zero(region,
+ sizeof(tp->unused[0])*tp->pipe_num);
tp->tcp_r = xfrd_tcp_create(region, QIOBUFSZ);
tp->tcp_w = xfrd_tcp_create(region, 512);
+ xfrd_tcp_pipeline_init(tp);
return tp;
}
+static struct xfrd_zone*
+xfrd_tcp_pipeline_lookup_id(struct xfrd_tcp_pipeline* tp, uint16_t id)
+{
+ struct xfrd_tcp_pipeline_id key;
+ rbnode_type* n;
+ memset(&key, 0, sizeof(key));
+ key.node.key = &key;
+ key.id = id;
+ n = rbtree_search(tp->zone_per_id, &key);
+ if(n && n != RBTREE_NULL) {
+ return ((struct xfrd_tcp_pipeline_id*)n)->zone;
+ }
+ return NULL;
+}
+
+static void
+xfrd_tcp_pipeline_insert_id(struct xfrd_tcp_pipeline* tp, uint16_t id,
+ struct xfrd_zone* zone)
+{
+ struct xfrd_tcp_pipeline_id* n;
+ /* because there are tp->pipe_num preallocated entries, and we have
+ * only tp->pipe_num id values, the list cannot be empty now. */
+ assert(tp->pipe_id_free_list != NULL);
+ /* pick up next free xfrd_tcp_pipeline_id node */
+ n = tp->pipe_id_free_list;
+ tp->pipe_id_free_list = n->next_free;
+ n->next_free = NULL;
+ memset(&n->node, 0, sizeof(n->node));
+ n->node.key = n;
+ n->id = id;
+ n->zone = zone;
+ rbtree_insert(tp->zone_per_id, &n->node);
+}
+
+static void
+xfrd_tcp_pipeline_remove_id(struct xfrd_tcp_pipeline* tp, uint16_t id)
+{
+ struct xfrd_tcp_pipeline_id key;
+ rbnode_type* node;
+ memset(&key, 0, sizeof(key));
+ key.node.key = &key;
+ key.id = id;
+ node = rbtree_delete(tp->zone_per_id, &key);
+ if(node && node != RBTREE_NULL) {
+ struct xfrd_tcp_pipeline_id* n =
+ (struct xfrd_tcp_pipeline_id*)node;
+ n->next_free = tp->pipe_id_free_list;
+ tp->pipe_id_free_list = n;
+ }
+}
+
+static void
+xfrd_tcp_pipeline_skip_id(struct xfrd_tcp_pipeline* tp, uint16_t id)
+{
+ struct xfrd_tcp_pipeline_id key;
+ rbnode_type* n;
+ memset(&key, 0, sizeof(key));
+ key.node.key = &key;
+ key.id = id;
+ n = rbtree_search(tp->zone_per_id, &key);
+ if(n && n != RBTREE_NULL) {
+ struct xfrd_tcp_pipeline_id* zid = (struct xfrd_tcp_pipeline_id*)n;
+ zid->zone = TCP_NULL_SKIP;
+ }
+}
+
void
xfrd_setup_packet(buffer_type* packet,
uint16_t type, uint16_t klass, const dname_type* dname, uint16_t qid)
struct xfrd_tcp_pipeline_key k, *key=&k;
key->node.key = key;
key->ip_len = xfrd_acl_sockaddr_to(zone->master, &key->ip);
- key->num_unused = ID_PIPE_NUM;
+ key->num_unused = set->tcp_pipeline;
/* lookup existing tcp transfer to the master with highest unused */
if(rbtree_find_less_equal(set->pipetree, key, &sme)) {
/* exact match, strange, fully unused tcp cannot be open */
/* remove zone from tcp pipe ID map */
static void
-tcp_pipe_id_remove(struct xfrd_tcp_pipeline* tp, xfrd_zone_type* zone)
+tcp_pipe_id_remove(struct xfrd_tcp_pipeline* tp, xfrd_zone_type* zone,
+ int alsotree)
{
- assert(tp->key.num_unused < ID_PIPE_NUM && tp->key.num_unused >= 0);
- assert(tp->id[zone->query_id] == zone);
- tp->id[zone->query_id] = NULL;
+ assert(tp->key.num_unused < tp->pipe_num && tp->key.num_unused >= 0);
+ if(alsotree)
+ xfrd_tcp_pipeline_remove_id(tp, zone->query_id);
tp->unused[tp->key.num_unused] = zone->query_id;
/* must remove and re-add for sort order in tree */
(void)rbtree_delete(xfrd->tcp_set->pipetree, &tp->key.node);
static void
xfrd_tcp_pipe_stop(struct xfrd_tcp_pipeline* tp)
{
- int i, conn = -1;
- assert(tp->key.num_unused < ID_PIPE_NUM); /* at least one 'in-use' */
- assert(ID_PIPE_NUM - tp->key.num_unused > tp->key.num_skip); /* at least one 'nonskip' */
+ struct xfrd_tcp_pipeline_id* zid;
+ int conn = -1;
+ assert(tp->key.num_unused < tp->pipe_num); /* at least one 'in-use' */
+ assert(tp->pipe_num - tp->key.num_unused > tp->key.num_skip); /* at least one 'nonskip' */
/* need to retry for all the zones connected to it */
/* these could use different lists and go to a different nextmaster*/
- for(i=0; i<ID_PIPE_NUM; i++) {
- if(tp->id[i] && tp->id[i] != TCP_NULL_SKIP) {
- xfrd_zone_type* zone = tp->id[i];
+ RBTREE_FOR(zid, struct xfrd_tcp_pipeline_id*, tp->zone_per_id) {
+ xfrd_zone_type* zone = zid->zone;
+ if(zone && zone != TCP_NULL_SKIP) {
+ assert(zone->query_id == zid->id);
conn = zone->tcp_conn;
zone->tcp_conn = -1;
zone->tcp_waiting = 0;
tcp_pipe_sendlist_remove(tp, zone);
- tcp_pipe_id_remove(tp, zone);
+ tcp_pipe_id_remove(tp, zone, 0);
xfrd_set_refresh_now(zone);
}
}
+ xfrd_tcp_pipeline_cleanup(tp);
assert(conn != -1);
/* now release the entire tcp pipe */
xfrd_tcp_pipe_release(xfrd->tcp_set, tp, conn);
idx = random_generate(tp->key.num_unused);
zone->query_id = tp->unused[idx];
tp->unused[idx] = tp->unused[tp->key.num_unused-1];
- tp->id[zone->query_id] = zone;
+ xfrd_tcp_pipeline_insert_id(tp, zone->query_id, zone);
/* decrement unused counter, and fixup tree */
(void)rbtree_delete(set->pipetree, &tp->key.node);
tp->key.num_unused--;
assert(zone->tcp_conn == -1);
assert(zone->tcp_waiting == 0);
- if(set->tcp_count < XFRD_MAX_TCP) {
+ if(set->tcp_count < set->tcp_max) {
int i;
assert(!set->tcp_waiting_first);
set->tcp_count ++;
/* find a free tcp_buffer */
- for(i=0; i<XFRD_MAX_TCP; i++) {
+ for(i=0; i<set->tcp_max; i++) {
if(set->tcp_state[i]->tcp_r->fd == -1) {
zone->tcp_conn = i;
break;
return;
}
/* ip and ip_len set by tcp_open */
- tp->key.node.key = tp;
- tp->key.num_unused = ID_PIPE_NUM;
- tp->key.num_skip = 0;
- tp->tcp_send_first = NULL;
- tp->tcp_send_last = NULL;
- memset(tp->id, 0, sizeof(tp->id));
- for(i=0; i<ID_PIPE_NUM; i++) {
- tp->unused[i] = i;
- }
+ xfrd_tcp_pipeline_init(tp);
/* insert into tree */
(void)rbtree_insert(set->pipetree, &tp->key.node);
int i;
if(zone->zone_handler.ev_fd != -1)
xfrd_udp_release(zone);
- for(i=0; i<XFRD_MAX_TCP; i++) {
+ for(i=0; i<set->tcp_max; i++) {
if(set->tcp_state[i] == tp)
zone->tcp_conn = i;
}
/* wait, at end of line */
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: max number of tcp "
- "connections (%d) reached.", XFRD_MAX_TCP));
+ "connections (%d) reached.", set->tcp_max));
zone->tcp_waiting_next = 0;
zone->tcp_waiting_prev = set->tcp_waiting_last;
zone->tcp_waiting = 1;
tcp_conn_ready_for_reading(tcp);
return;
}
- zone = tp->id[ID(tcp->packet)];
+ zone = xfrd_tcp_pipeline_lookup_id(tp, ID(tcp->packet));
if(!zone || zone == TCP_NULL_SKIP) {
/* no zone for this id? skip it */
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
break;
case xfrd_packet_newlease:
/* set to skip if more packets with this ID */
- tp->id[zone->query_id] = TCP_NULL_SKIP;
+ xfrd_tcp_pipeline_skip_id(tp, zone->query_id);
tp->key.num_skip++;
/* fall through to remove zone from tp */
/* fallthrough */
case xfrd_packet_tcp:
default:
/* set to skip if more packets with this ID */
- tp->id[zone->query_id] = TCP_NULL_SKIP;
+ xfrd_tcp_pipeline_skip_id(tp, zone->query_id);
tp->key.num_skip++;
xfrd_tcp_release(xfrd->tcp_set, zone);
/* query next server */
/* remove from tcp_send list */
tcp_pipe_sendlist_remove(tp, zone);
/* remove it from the ID list */
- if(tp->id[zone->query_id] != TCP_NULL_SKIP)
- tcp_pipe_id_remove(tp, zone);
+ if(xfrd_tcp_pipeline_lookup_id(tp, zone->query_id) != TCP_NULL_SKIP)
+ tcp_pipe_id_remove(tp, zone, 1);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: released tcp pipe now %d unused",
tp->key.num_unused));
/* if pipe was full, but no more, then see if waiting element is
}
/* if all unused, or only skipped leftover, close the pipeline */
- if(tp->key.num_unused >= ID_PIPE_NUM || tp->key.num_skip >= ID_PIPE_NUM - tp->key.num_unused)
+ if(tp->key.num_unused >= tp->pipe_num || tp->key.num_skip >= tp->pipe_num - tp->key.num_unused)
xfrd_tcp_pipe_release(set, tp, conn);
}
/* a waiting zone can use the free tcp slot (to another server) */
/* if that zone fails to set-up or connect, we try to start the next
* waiting zone in the list */
- while(set->tcp_count == XFRD_MAX_TCP && set->tcp_waiting_first) {
- int i;
-
+ while(set->tcp_count == set->tcp_max && set->tcp_waiting_first) {
/* pop first waiting process */
xfrd_zone_type* zone = set->tcp_waiting_first;
/* start it */
}
/* re-init this tcppipe */
/* ip and ip_len set by tcp_open */
- tp->key.node.key = tp;
- tp->key.num_unused = ID_PIPE_NUM;
- tp->key.num_skip = 0;
- tp->tcp_send_first = NULL;
- tp->tcp_send_last = NULL;
- memset(tp->id, 0, sizeof(tp->id));
- for(i=0; i<ID_PIPE_NUM; i++) {
- tp->unused[i] = i;
- }
+ xfrd_tcp_pipeline_init(tp);
/* insert into tree */
(void)rbtree_insert(set->pipetree, &tp->key.node);
* A set of xfrd tcp connections.
*/
struct xfrd_tcp_set {
- /* tcp connections, each has packet and read/wr state */
- struct xfrd_tcp_pipeline *tcp_state[XFRD_MAX_TCP];
+ /* tcp connections, array, each has packet and read/wr state */
+ struct xfrd_tcp_pipeline **tcp_state;
+ /* max number of tcp connections, size of tcp_state array */
+ int tcp_max;
+ /* max number of simultaneous connections on a tcp_pipeline */
+ int tcp_pipeline;
/* number of TCP connections in use. */
int tcp_count;
/* TCP timeout. */
/* use illegal pointer value to denote skipped ID number.
* if this does not work, we can allocate with malloc */
#define TCP_NULL_SKIP ((struct xfrd_zone*)-1)
-/* the number of ID values (16 bits) for a pipeline */
-#define ID_PIPE_NUM 65536
+
+/**
+ * The per-id zone pointers, with TCP_NULL_SKIP or a zone pointer for the
+ * ID value.
+ */
+struct xfrd_tcp_pipeline_id {
+ /** rbtree node as first member, this is the key. */
+ rbnode_type node;
+ /** the ID of this member */
+ uint16_t id;
+ /** zone pointer or TCP_NULL_SKIP */
+ struct xfrd_zone* zone;
+ /** next free in free list */
+ struct xfrd_tcp_pipeline_id* next_free;
+};
/**
* The tcp pipeline key structure. By ip_len, ip, num_unused and unique by
/* list of queries that want to send, first to get write event,
* if NULL, no write event interest */
struct xfrd_zone* tcp_send_first, *tcp_send_last;
- /* the unused and id arrays must be last in the structure */
- /* per-ID number the queries that have this ID number, every
+
+ /* size of the id and unused arrays. */
+ int pipe_num;
+ /* list of free xfrd_tcp_pipeline_id nodes, these are not in the
+ * zone_per_id tree. preallocated at pipe_num amount. */
+ struct xfrd_tcp_pipeline_id* pipe_id_free_list;
+ /* The xfrd_zone pointers, per id number.
+ * The key is struct xfrd_tcp_pipeline_id.
+ * per-ID number the queries that have this ID number, every
* query owns one ID numbers (until it is done). NULL: unused
* When a query is done but not all answer-packets have been
* consumed for that ID number, the rest is skipped, this
* is denoted with the pointer-value TCP_NULL_SKIP, the ids that
* are skipped are not on the unused list. They may be
* removed once the last answer packet is skipped.
- * ID_PIPE_NUM-num_unused values in the id array are nonNULL (either
+ * pipe_num-num_unused values are in the tree (either
* a zone pointer or SKIP) */
- struct xfrd_zone* id[ID_PIPE_NUM];
- /* unused ID numbers; the first part of the array contains the IDs */
- uint16_t unused[ID_PIPE_NUM];
+ rbtree_type* zone_per_id;
+ /* Array of uint16_t, with ID values.
+ * unused ID numbers; the first part of the array contains the IDs */
+ uint16_t* unused;
};
/* create set of tcp connections */
-struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle);
+struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle, int tcp_max, int tcp_pipeline);
/* init tcp state */
struct xfrd_tcp* xfrd_tcp_create(struct region* region, size_t bufsize);
#endif /* INET6 */
/* create pipeline tcp structure */
-struct xfrd_tcp_pipeline* xfrd_tcp_pipeline_create(region_type* region);
+struct xfrd_tcp_pipeline* xfrd_tcp_pipeline_create(region_type* region,
+ int tcp_pipeline);
+/* pick num uint16_t values, from 0..max-1, store in array */
+void pick_id_values(uint16_t* array, int num, int max);
#endif /* XFRD_TCP_H */
daemon_remote_attach(xfrd->nsd->rc, xfrd);
#endif
- xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region, nsd->options->tls_cert_bundle);
+ xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region, nsd->options->tls_cert_bundle, nsd->options->xfrd_tcp_max, nsd->options->xfrd_tcp_pipeline);
xfrd->tcp_set->tcp_timeout = nsd->tcp_timeout;
#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
srandom((unsigned long) getpid() * (unsigned long) time(NULL));
And it should be below FD_SETSIZE, to be able to select() on replies.
Note that also some sockets are used for writing the ixfr.db, xfrd.state
files and for the pipes to the main parent process.
+
+ For xfrd_tcp_max, 128 is the default number of TCP AXFR/IXFR concurrent
+ connections. Each entry has 64Kb buffer preallocated.
*/
-#define XFRD_MAX_TCP 128 /* max number of TCP AXFR/IXFR concurrent connections.*/
- /* Each entry has 64Kb buffer preallocated.*/
#define XFRD_MAX_UDP 128 /* max number of UDP sockets at a time for IXFR */
#define XFRD_MAX_UDP_NOTIFY 128 /* max concurrent UDP sockets for NOTIFY */
/*
* Make a new request to next master server.
* uses next_master if set (and a fresh set of rounds).
- * otherwised, starts new round of requests if none started already.
+ * otherwise, starts new round of requests if none started already.
* starts next round of requests if at last master.
* if too many rounds of requests, sets timer for next retry.
*/