diff options
-rw-r--r-- | Makefile.in | 7 | ||||
-rw-r--r-- | RELNOTES | 12 | ||||
-rw-r--r-- | aclocal.m4 | 1 | ||||
-rwxr-xr-x | configure | 159 | ||||
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | doc/Makefile.in | 7 | ||||
-rw-r--r-- | doc/reference.texi | 14 | ||||
-rw-r--r-- | doc/running.texi | 74 | ||||
-rw-r--r-- | m4/ax_recvmmsg.m4 | 96 | ||||
-rw-r--r-- | samples/Makefile.in | 7 | ||||
-rw-r--r-- | samples/knot.full.conf | 8 | ||||
-rw-r--r-- | src/Makefile.in | 7 | ||||
-rw-r--r-- | src/common/log.c | 11 | ||||
-rw-r--r-- | src/common/log.h | 9 | ||||
-rw-r--r-- | src/knot.conf.5 | 2 | ||||
-rw-r--r-- | src/knot/conf/conf.c | 4 | ||||
-rw-r--r-- | src/knot/conf/conf.h | 2 | ||||
-rw-r--r-- | src/knot/ctl/knotc_main.c | 106 | ||||
-rw-r--r-- | src/knot/ctl/remote.c | 324 | ||||
-rw-r--r-- | src/knot/ctl/remote.h | 12 | ||||
-rw-r--r-- | src/knot/main.c | 24 | ||||
-rw-r--r-- | src/knot/server/rrl.c | 269 | ||||
-rw-r--r-- | src/knot/server/rrl.h | 54 | ||||
-rw-r--r-- | src/knot/server/server.c | 2 | ||||
-rw-r--r-- | src/knotc.8 | 5 | ||||
-rw-r--r-- | src/knotd.8 | 2 | ||||
-rw-r--r-- | src/libknot/packet/packet.c | 3 | ||||
-rw-r--r-- | src/tests/knot/rrl_tests.c | 111 |
28 files changed, 1049 insertions, 314 deletions
diff --git a/Makefile.in b/Makefile.in index 21d7df3..4135b77 100644 --- a/Makefile.in +++ b/Makefile.in @@ -58,9 +58,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ $(top_srcdir)/m4/ax_ext.m4 \ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ @@ -1,3 +1,15 @@ +v1.2.0-rc4 - Mar 22, 2013 +------------------------- + +Features: + * knotc 'zonestatus' command + +Bugfixes: + * Changing logfile ownership before dropping privileges + * knotc respects 'control' section from configuration + * RRL: resolved bucket collisions + * RRL: updated bucket mapping to conform RRL technical memo + v1.2.0-rc3 - Mar 1, 2013 ------------------------ @@ -1022,6 +1022,7 @@ AC_SUBST([am__untar]) m4_include([m4/ax_check_compiler_flags.m4]) m4_include([m4/ax_ext.m4]) m4_include([m4/ax_gcc_x86_cpuid.m4]) +m4_include([m4/ax_recvmmsg.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for knot 1.2.0-rc3. +# Generated by GNU Autoconf 2.69 for knot 1.2.0-rc4. # # Report bugs to <knot-dns@labs.nic.cz>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='knot' PACKAGE_TARNAME='knot' -PACKAGE_VERSION='1.2.0-rc3' -PACKAGE_STRING='knot 1.2.0-rc3' +PACKAGE_VERSION='1.2.0-rc4' +PACKAGE_STRING='knot 1.2.0-rc4' PACKAGE_BUGREPORT='knot-dns@labs.nic.cz' PACKAGE_URL='' @@ -1319,7 +1319,7 @@ if test "$ac_init_help" = "long"; then # 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 knot 1.2.0-rc3 to adapt to many kinds of systems. +\`configure' configures knot 1.2.0-rc4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1389,7 +1389,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of knot 1.2.0-rc3:";; + short | recursive ) echo "Configuration of knot 1.2.0-rc4:";; esac cat <<\_ACEOF @@ -1509,7 +1509,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -knot configure 1.2.0-rc3 +knot configure 1.2.0-rc4 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2062,7 +2062,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by knot $as_me 1.2.0-rc3, which was +It was created by knot $as_me 1.2.0-rc4, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2889,7 +2889,7 @@ fi # Define the identity of the package. PACKAGE='knot' - VERSION='1.2.0-rc3' + VERSION='1.2.0-rc4' cat >>confdefs.h <<_ACEOF @@ -13425,28 +13425,155 @@ $as_echo "#define DEBUG_ENABLE_BRIEF 1" >>confdefs.h fi + + + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for recv_mmsg support" >&5 +$as_echo_n "checking for recv_mmsg support... " >&6; } +if ${ax_cv_have_msg_waitforone+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + ax_cv_have_msg_waitforone=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +volatile int _intr = 0; +void sighandle(int s) { + _intr = 1; +} + +int +main () +{ + +#ifndef MSG_WAITFORONE + return 3; /* Not supported. */ +#else + int port = 35353; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) return 1; + struct mmsghdr msgs[2]; + struct iovec iovecs[2]; + char bufs[2][64]; + unsigned i = 0; + memset(msgs, 0, sizeof(msgs)); + for (i = 0; i < 2; i++) { + iovecs[i].iov_base = bufs[i]; + iovecs[i].iov_len = 64; + msgs[i].msg_hdr.msg_iov = &iovecs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + } + + struct sockaddr_in sa; /* Bind to socket. */ + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa.sin_port = htons(port); /* Find free port. */ + while (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + if (errno == EADDRINUSE) sa.sin_port = ++port; + else break; + } + + int cfd = socket(AF_INET, SOCK_DGRAM, 0); /* Send datagram. */ + char pkt[32] = { '\xdf' }; + sendto(cfd, pkt, sizeof(pkt), 0, (struct sockaddr*)&sa, sizeof(sa)); + + /* Broken implementation doesn't respect recvmmsg timeout. */ + struct sigaction aset; + memset(&aset, 0, sizeof(struct sigaction)); + aset.sa_handler = sighandle; + sigaction(SIGALRM, &aset, NULL); + alarm(1); + + int ret = recvmmsg(fd, msgs, 2, MSG_WAITFORONE, NULL); + close(cfd); + close(fd); + if (ret < 0) { /* Completely failed. */ + return 2; + } + + return _intr; /* OK if not interrupted. */ +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_have_msg_waitforone=yes +else + ax_cv_have_msg_waitforone=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_msg_waitforone" >&5 +$as_echo "$ax_cv_have_msg_waitforone" >&6; } + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + # recvmmsg() (valgrind doesn't support it, so disable for debugging) # Check whether --enable-recvmmsg was given. if test "${enable_recvmmsg+set}" = set; then : enableval=$enable_recvmmsg; case "${enableval}" in yes) + if test "$ax_cv_have_msg_waitforone" = "yes"; then $as_echo "#define ENABLE_RECVMMSG 1" >>confdefs.h - ;; + recvmmsg=true + else + recvmmsg=false + fi + ;; no) - recvmmsg=false - ;; + recvmmsg=false + ;; *) - as_fn_error $? "bad value ${enableval} for --enable-recvmmsg" "$LINENO" 5 - ;; + as_fn_error $? "bad value ${enableval} for --enable-recvmmsg" "$LINENO" 5 + ;; esac else + if test "$ax_cv_have_msg_waitforone" = "yes"; then $as_echo "#define ENABLE_RECVMMSG 1" >>confdefs.h - recvmmsg=true + recvmmsg=true + else + recvmmsg=false + fi fi @@ -15342,7 +15469,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by knot $as_me 1.2.0-rc3, which was +This file was extended by knot $as_me 1.2.0-rc4, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15408,7 +15535,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -knot config.status 1.2.0-rc3 +knot config.status 1.2.0-rc4 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index f593068..23ff7c0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # -*- Autoconf -*- AC_PREREQ([2.60]) -AC_INIT([knot], [1.2.0-rc3], [knot-dns@labs.nic.cz]) +AC_INIT([knot], [1.2.0-rc4], [knot-dns@labs.nic.cz]) AM_INIT_AUTOMAKE([gnu -Wall -Werror]) AC_CONFIG_SRCDIR([src/knot/main.c]) AC_CONFIG_HEADERS([src/config.h]) @@ -103,22 +103,32 @@ AC_ARG_ENABLE([debuglevel], ;; esac], []) +AX_MSG_WAITFORONE # recvmmsg() (valgrind doesn't support it, so disable for debugging) AC_ARG_ENABLE([recvmmsg], AS_HELP_STRING([--enable-recvmmsg=yes|no], [enable recvmmsg() network API under Linux (kernel support required) (set to 'no' if you have trouble running server under valgrind) [default=yes]]), [case "${enableval}" in yes) - AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled]) - ;; + if test "$ax_cv_have_msg_waitforone" = "yes"; then + AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled]) + recvmmsg=true + else + recvmmsg=false + fi + ;; no) - recvmmsg=false - ;; + recvmmsg=false + ;; *) - AC_MSG_ERROR([bad value ${enableval} for --enable-recvmmsg]) - ;; + AC_MSG_ERROR([bad value ${enableval} for --enable-recvmmsg]) + ;; esac], [ - AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled]) - recvmmsg=true + if test "$ax_cv_have_msg_waitforone" = "yes"; then + AC_DEFINE([ENABLE_RECVMMSG], [1], [recvmmsg enabled]) + recvmmsg=true + else + recvmmsg=false + fi ]) # Check for link time optimizations support and predictive commoning @@ -128,8 +138,7 @@ AC_ARG_ENABLE([lto], yes) AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], []) ;; no) ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-lto]) ;; - esac], [ - ]) + esac]) AX_CHECK_COMPILER_FLAGS("-fpredictive-commoning", [CFLAGS="$CFLAGS -fpredictive-commoning"], []) diff --git a/doc/Makefile.in b/doc/Makefile.in index 779e241..3d391d2 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -57,9 +57,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ $(top_srcdir)/m4/ax_ext.m4 \ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d diff --git a/doc/reference.texi b/doc/reference.texi index 2d9ac7e..bb2bf39 100644 --- a/doc/reference.texi +++ b/doc/reference.texi @@ -201,10 +201,12 @@ Default value: @kbd{0 (disabled)} @vindex rate-limit-size Option controls the size of a hashtable of buckets. The larger the hashtable, the lesser probability of a hash collision, but -at the expense of additional memory costs. Each bucket is estimated roughly to 16B. +at the expense of additional memory costs. Each bucket is estimated roughly to 32B. Size should be selected as a reasonably large prime due to the better hash function distribution properties. +Hash table is internally chained and works well up to a fill rate of 90%, general rule of thumb is to select a prime near +@kbd{1.2 * maximum_qps}. -Default value: @kbd{1572869} +Default value: @kbd{393241} @node rate-limit-slip @subsubsection rate-limit-slip @@ -664,7 +666,9 @@ Remotes are defined in @code{remotes} section of configuration file (@pxref{remo @vindex semantic-checks @code{semantic-checks} statement turns on optional semantic checks for this particular zone. -See @ref{zones List of zone semantic checks} for more information. Possible values are @code{on} and @code{off}. +See @ref{zones List of zone semantic checks} for more information. + +Possible values are @code{on} and @code{off}. Most checks are disabled by default. @node ixfr-from-differences @@ -673,7 +677,9 @@ Most checks are disabled by default. EXPERIMENTAL: option @code{ixfr-from-differences} is only relevant if you are running Knot DNS as a master for this zone. By turning the feature on you tell Knot to create differences from changes you made to a zone file upon server reload. -See @ref{Controlling running daemon} for more information. Possible values are @code{on} and @code{off}. Disabled by default. +See @ref{Controlling running daemon} for more information. + +Possible values are @code{on} and @code{off}. Disabled by default. @node disable-any @subsubsection disable-any diff --git a/doc/running.texi b/doc/running.texi index 628cd7d..bee48f3 100644 --- a/doc/running.texi +++ b/doc/running.texi @@ -13,49 +13,45 @@ effort, so each time the zone file changes you need to compile it. @example $ knotc -c knot.conf compile @end example -Or alternatively, you can compile automatically using the @code{-a} flag. +Knot DNS automatically compiles zones on some actions (@code{start|restart}). @example -$ knotc -a -c knot.conf start|reload|restart +$ knotc -c knot.conf start|reload|restart @end example The tool @code{knotc} is designed as a front-end for user, making it easier to do everything from zone compilation to controlling the server daemon. -To communicate with the binary, it reads the PID from the @emph{PID file} specified in the configuration @code{pidfile} and sends POSIX signals to it (@pxref{pidfile}). -If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration. Signal @code{SIGUSR2} is currently used to refresh slave zones. +If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration. @example -Usage: knotc [parameters] start|stop|restart|reload|running|compile +Usage: knotc [parameters] <action> [action_args] + Parameters: - -c [file], --config=[file] - Select configuration file. - -j [num], --jobs=[num] - Number of parallel tasks to run when compiling. - -f, --force - Force operation - override some checks. - -v, --verbose - Verbose mode - additional runtime information. - -V, --version - Print knot server version. - -w, --wait - Wait for the server to finish start/stop operations. - -i, --interactive - Interactive mode (do not daemonize). - -a, --auto - Enable automatic recompilation (start or reload). - -h, --help - Print help and usage. + -c [file], --config=[file] Select configuration file. + -j [num], --jobs=[num] Number of parallel tasks to run when compiling. + -s [server] Remote server address (default 127.0.0.1) + -p [port] Remote server port (default 5553) + -y [hmac:]name:key] Use key_id specified on the command line. + -k [file] Use key file (as in config section 'keys'). + f.e. echo "knotc-key hmac-md5 Wg==" > knotc.key + -f, --force Force operation - override some checks. + -v, --verbose Verbose mode - additional runtime information. + -V, --version Print knot server version. + -w, --wait Wait for the server to finish start/stop operations. + -i, --interactive Interactive mode (do not daemonize). + -h, --help Print help and usage. Actions: - start Start knot server zone (no-op if running). - stop Stop knot server (no-op if not running). - restart Stops and then starts knot server. - reload Reload knot configuration and compiled zones. - refresh Refresh all slave zones. - running Check if server is running. - checkconf Check server configuration. - checkzone Check zones (accepts specific zones, - e.g. 'knotc checkzone example1.com example2.com'). - compile Compile zones (accepts specific zones, see above). + start Start server (no-op if running). + stop Stop server (no-op if running). + restart Restarts server (no-op if running). + reload Reloads configuration and changed zones. + refresh Refresh slave zones (all if not specified). + flush Flush journal and update zone files. + status Check if server is running. + zonestatus Show status of configured zones. + checkconf Check server configuration. + checkzone Check specified zone files. + compile Compile zone files (all if not specified). @end example If you want to run Knot DNS daemon directly, you can use @code{knotd} binary @@ -129,7 +125,8 @@ When the server is running, you can control the daemon, see @ref{Controlling run @section Running a master server Knot DNS first needs to compile the zones before it can load them, therefore you need to -compile them with the @code{knotc compile} action or use flag @code{-a} to compile the zones automatically. +compile them with the @code{knotc compile} action. +Some actions like @code{start} or @code{restart} compile zones automatically. If you want to just check the zone files first before starting, you can use @code{knotc checkzone} action. @@ -146,7 +143,7 @@ $ knotc -w -c master.conf start Or you can compile it automatically: @example $ knotc -c master.conf checkconf # check configuration -$ knotc -a -w -c master.conf start +$ knotc -w -c master.conf start @end example @node Controlling running daemon @@ -162,12 +159,7 @@ $ knotc -c master.conf compile # compile updated zones $ knotc -c master.conf reload # reconfigure and load updated zones @end example -Or use the @code{-a} again. -@example -$ knotc -a -c master.conf reload # compile zones and reconfigure -@end example - -If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @code{ixfr-from-differences} +If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @ref{ixfr-from-differences} in @code{zones} statement, then compile the zone and reload your server as seen above. If @emph{SOA}'s @emph{serial} is not changed no differences will be created. Please note that this feature is in @emph{experimental} stage and should be used with care. diff --git a/m4/ax_recvmmsg.m4 b/m4/ax_recvmmsg.m4 new file mode 100644 index 0000000..eb5c4d6 --- /dev/null +++ b/m4/ax_recvmmsg.m4 @@ -0,0 +1,96 @@ +dnl @synopsis AX_MSG_WAITFORONE +dnl @summary Test if the libc/kernel have working recvmmsg MSG_WAITFORONE implementation +dnl @category Misc +dnl +dnl We need recvmmsg to support MSG_WAITFORONE. RHEL 6 (and derivates) +dnl are know for broken implementation +dnl +dnl @version 2013-03-12 +dnl @license GPL +dnl @author Ondřej Surý <ondrej@sury.org> and Marek Vavruša <marek@vavrusa.com> + +AC_DEFUN([AX_MSG_WAITFORONE], +[ + AC_REQUIRE([AC_PROG_CC]) + + AC_LANG_PUSH([C]) + + AC_CACHE_CHECK([for recv_mmsg support], [ax_cv_have_msg_waitforone], + [ + AC_RUN_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +volatile int _intr = 0; +void sighandle(int s) { + _intr = 1; +} + ]],[[ +#ifndef MSG_WAITFORONE + return 3; /* Not supported. */ +#else + int port = 35353; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) return 1; + struct mmsghdr msgs[2]; + struct iovec iovecs[2]; + char bufs[2][64]; + unsigned i = 0; + memset(msgs, 0, sizeof(msgs)); + for (i = 0; i < 2; i++) { + iovecs[i].iov_base = bufs[i]; + iovecs[i].iov_len = 64; + msgs[i].msg_hdr.msg_iov = &iovecs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + } + + struct sockaddr_in sa; /* Bind to socket. */ + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa.sin_port = htons(port); /* Find free port. */ + while (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + if (errno == EADDRINUSE) sa.sin_port = ++port; + else break; + } + + int cfd = socket(AF_INET, SOCK_DGRAM, 0); /* Send datagram. */ + char pkt[32] = { '\xdf' }; + sendto(cfd, pkt, sizeof(pkt), 0, (struct sockaddr*)&sa, sizeof(sa)); + + /* Broken implementation doesn't respect recvmmsg timeout. */ + struct sigaction aset; + memset(&aset, 0, sizeof(struct sigaction)); + aset.sa_handler = sighandle; + sigaction(SIGALRM, &aset, NULL); + alarm(1); + + int ret = recvmmsg(fd, msgs, 2, MSG_WAITFORONE, NULL); + close(cfd); + close(fd); + if (ret < 0) { /* Completely failed. */ + return 2; + } + + return _intr; /* OK if not interrupted. */ +#endif + ]])], + [ax_cv_have_msg_waitforone=yes], + [ax_cv_have_msg_waitforone=no], + [ax_cv_have_msg_waitforone=no])]) + + AC_LANG_POP([C]) +]) diff --git a/samples/Makefile.in b/samples/Makefile.in index 44b12ae..b307eda 100644 --- a/samples/Makefile.in +++ b/samples/Makefile.in @@ -55,9 +55,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ $(top_srcdir)/m4/ax_ext.m4 \ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d diff --git a/samples/knot.full.conf b/samples/knot.full.conf index f6a9d89..8c02d3c 100644 --- a/samples/knot.full.conf +++ b/samples/knot.full.conf @@ -70,10 +70,10 @@ system { # Number of hashtable buckets, set to reasonable value as default. # We chose a reasonably large prime number as it's used for hashtable size, # it is recommended to do so as well due to better distribution. - # Tweak if you experience a lot of hash collisions, estimated memory overhead - # is approx. 16B per bucket - # Default: 1572869 - rate-limit-size 1572869; + # Rule of thumb is to set it to about 1.2 * (maximum_qps) + # Memory cost is approx. 32B per bucket + # Default: 393241 + rate-limit-size 393241; # Rate limit SLIP # Each Nth blocked response will be sent as truncated, this is a way to allow diff --git a/src/Makefile.in b/src/Makefile.in index 4991a28..61a7298 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -66,9 +66,10 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ $(top_srcdir)/m4/ax_ext.m4 \ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ax_recvmmsg.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d diff --git a/src/common/log.c b/src/common/log.c index 5267d2d..ff6475e 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -331,3 +331,14 @@ void hex_log(int source, const char *data, int length) log_msg(source, LOG_DEBUG, "%s\n", lbuf); } } + +int log_update_privileges(int uid, int gid) +{ + for (unsigned i = 0; i < LOG_FDS_OPEN; ++i) { + if (fchown(fileno(LOG_FDS[i]), uid, gid) < 0) { + return KNOT_ERROR; + } + + } + return KNOT_EOK; +} diff --git a/src/common/log.h b/src/common/log.h index 8b9e311..639957c 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -203,6 +203,15 @@ void hex_log(int source, const char *data, int length); #define log_zone_info(msg...) log_msg(LOG_ZONE, LOG_INFO, msg) #define log_zone_debug(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg) +/*! + * \brief Update open files ownership. + * \param uid New owner id. + * \param gid New group id. + * \retval KNOT_EOK if success + * \retval KNOT_ERROR on error + */ +int log_update_privileges(int uid, int gid); + #endif /* _KNOTD_LOG_H_ */ /*! @} */ diff --git a/src/knot.conf.5 b/src/knot.conf.5 index 734c812..ccd2960 100644 --- a/src/knot.conf.5 +++ b/src/knot.conf.5 @@ -1,4 +1,4 @@ -.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3" +.TH "knot.conf" "5" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4" .SH "NAME" .LP .B knot.conf diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index f93eefe..28d33e0 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -357,8 +357,8 @@ void __attribute__ ((constructor)) conf_init() /* Create default interface. */ conf_iface_t * iface = malloc(sizeof(conf_iface_t)); memset(iface, 0, sizeof(conf_iface_t)); - iface->name = strdup("any"); - iface->address = strdup("0.0.0.0"); + iface->name = strdup("localhost"); + iface->address = strdup("127.0.0.1"); iface->port = CONFIG_DEFAULT_PORT; add_tail(&s_config->ifaces, &iface->n); ++s_config->ifaces_count; diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index f56d92d..380d9df 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -51,7 +51,7 @@ #define CONFIG_HANDSHAKE_WD 10 /*!< [secs] for connection to make a request.*/ #define CONFIG_IDLE_WD 60 /*!< [secs] of allowed inactivity between requests */ #define CONFIG_RRL_SLIP 2 /*!< Default slip value. */ -#define CONFIG_RRL_SIZE 1572869 /*!< Htable default size. */ +#define CONFIG_RRL_SIZE 393241 /*!< Htable default size. */ /*! * \brief Configuration for the interface diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index 3881ab9..3354cae 100644 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -86,6 +86,7 @@ static int cmd_reload(int argc, char *argv[], unsigned flags, int jobs); static int cmd_refresh(int argc, char *argv[], unsigned flags, int jobs); static int cmd_flush(int argc, char *argv[], unsigned flags, int jobs); static int cmd_status(int argc, char *argv[], unsigned flags, int jobs); +static int cmd_zonestatus(int argc, char *argv[], unsigned flags, int jobs); static int cmd_checkconf(int argc, char *argv[], unsigned flags, int jobs); static int cmd_checkzone(int argc, char *argv[], unsigned flags, int jobs); static int cmd_compile(int argc, char *argv[], unsigned flags, int jobs); @@ -99,6 +100,7 @@ knot_cmd_t knot_cmd_tbl[] = { {"refresh", &cmd_refresh,"Refresh slave zones (all if not specified).",0}, {"flush", &cmd_flush, "\tFlush journal and update zone files.",0}, {"status", &cmd_status, "\tCheck if server is running.",0}, + {"zonestatus",&cmd_zonestatus, "Show status of configured zones.",0}, {"checkconf", &cmd_checkconf, "Check server configuration.",1}, {"checkzone", &cmd_checkzone, "Check specified zone files.",1}, {"compile", &cmd_compile, "Compile zone files (all if not specified).",1}, @@ -281,12 +283,7 @@ static int cmd_remote_print_reply(const knot_rrset_t *rr) } /* Parse TXT. */ - char* txt = remote_parse_txt(rd); - if (txt) { - log_server_info("Server reply: %s\n", txt); - } - free(txt); - + remote_print_txt(rd); rd = knot_rrset_rdata_next(rr, rd); } @@ -362,7 +359,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) break; case KNOT_RRTYPE_TXT: default: - rd = remote_create_txt(argv[i]); + rd = remote_create_txt(argv[i], strlen(argv[i])); break; } knot_rrset_add_rdata(rr, rd); @@ -390,8 +387,11 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) /* Wait for reply. */ if (rc == 0) { - int ret = cmd_remote_reply(s); - if (ret != KNOT_EOK) { + int ret = KNOT_EOK; + while (ret != KNOT_ECONN) { + ret = cmd_remote_reply(s); + } + if (ret != KNOT_EOK && ret != KNOT_ECONN) { log_server_warning("Remote command reply: %s\n", knot_strerror(ret)); rc = 1; @@ -399,6 +399,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) } /* Cleanup. */ + printf("\n"); knot_rrset_deep_free(&rr, 1, 1, 1); /* Close connection. */ @@ -556,8 +557,10 @@ static int tsig_parse_file(knot_key_t *k, const char *f) static void tsig_key_cleanup(knot_key_t *k) { - knot_dname_free(&k->name); - free(k->secret); + if (k) { + knot_dname_free(&k->name); + free(k->secret); + } } int main(int argc, char **argv) @@ -567,16 +570,18 @@ int main(int argc, char **argv) unsigned jobs = 1; unsigned flags = F_NULL; char *config_fn = NULL; + char *default_config = conf_find_default(); /* Remote server descriptor. */ int ret = KNOT_EOK; - const char *r_addr = "127.0.0.1"; - int r_port = REMOTE_DPORT; + const char *r_addr = NULL; + int r_port = -1; knot_key_t r_key; memset(&r_key, 0, sizeof(knot_key_t)); /* Initialize. */ log_init(); + log_levels_set(LOG_SYSLOG, LOG_ANY, 0); /* Long options. */ struct option opts[] = { @@ -647,6 +652,8 @@ int main(int argc, char **argv) optarg); help(argc, argv); log_close(); + free(config_fn); + free(default_config); return 1; } @@ -654,12 +661,16 @@ int main(int argc, char **argv) case 'V': printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION); log_close(); + free(config_fn); + free(default_config); return 0; case 'h': case '?': default: help(argc, argv); log_close(); + free(config_fn); + free(default_config); return 1; } } @@ -669,6 +680,8 @@ int main(int argc, char **argv) help(argc, argv); tsig_key_cleanup(&r_key); log_close(); + free(config_fn); + free(default_config); return 1; } @@ -684,38 +697,33 @@ int main(int argc, char **argv) /* Command not found. */ if (!cmd->name) { log_server_error("Invalid command: '%s'\n", argv[optind]); - free(config_fn); tsig_key_cleanup(&r_key); log_close(); + free(config_fn); + free(default_config); return 1; } /* Open config, allow if not exists. */ - if (cmd->need_conf) { - if (!config_fn) { - config_fn = conf_find_default(); - } - if (conf_open(config_fn) != KNOT_EOK) { + if (conf_open(config_fn) != KNOT_EOK) { + if(conf_open(default_config) != KNOT_EOK) { flags |= F_NOCONF; } - free(config_fn); - config_fn = NULL; } - /* Discard remote interface. */ + /* Create remote iface if not present in config. */ conf_iface_t *ctl_if = conf()->ctl.iface; - conf_free_iface(ctl_if); - - /* Update remote interface. */ - ctl_if = malloc(sizeof(conf_iface_t)); - if (ctl_if) { + if (!ctl_if) { + ctl_if = malloc(sizeof(conf_iface_t)); + assert(ctl_if); + conf()->ctl.iface = ctl_if; memset(ctl_if, 0, sizeof(conf_iface_t)); - ctl_if->address = strdup(r_addr); - ctl_if->port = r_port; - ctl_if->family = AF_INET; - if (strchr(r_addr, ':')) { /* Dumb way to check for v6 addr. */ - ctl_if->family = AF_INET6; - } + + /* Fill defaults. */ + if (!r_addr) r_addr = "127.0.0.1"; + if (r_port < 0) r_port = REMOTE_DPORT; + + /* Create empty key. */ if (r_key.name) { ctl_if->key = malloc(sizeof(knot_key_t)); if (ctl_if->key) { @@ -723,7 +731,22 @@ int main(int argc, char **argv) } } } - conf()->ctl.iface = ctl_if; + + /* Override from command line. */ + if (r_addr) { + free(ctl_if->address); + ctl_if->address = strdup(r_addr); + ctl_if->family = AF_INET; + if (strchr(r_addr, ':')) { /* Dumb way to check for v6 addr. */ + ctl_if->family = AF_INET6; + } + } + if (r_port > -1) ctl_if->port = r_port; + if (r_key.name != NULL) { + tsig_key_cleanup(ctl_if->key); + ctl_if->key = &r_key; + } + /* Verbose mode. */ if (has_flag(flags, F_VERBOSE)) { @@ -737,6 +760,8 @@ int main(int argc, char **argv) /* Finish */ tsig_key_cleanup(&r_key); /* Not cleaned by config deinit. */ log_close(); + free(config_fn); + free(default_config); return rc; } @@ -749,6 +774,10 @@ static int cmd_start(int argc, char *argv[], unsigned flags, int jobs) return 1; } + /* Alter privileges. */ + log_update_privileges(conf()->uid, conf()->gid); + proc_update_privileges(conf()->uid, conf()->gid); + /* Fetch PID. */ char *pidfile = pid_filename(); pid_t pid = pid_read(pidfile); @@ -847,6 +876,10 @@ static int cmd_stop(int argc, char *argv[], unsigned flags, int jobs) return 1; } + /* Alter privileges. */ + log_update_privileges(conf()->uid, conf()->gid); + proc_update_privileges(conf()->uid, conf()->gid); + /* Fetch PID. */ char *pidfile = pid_filename(); pid_t pid = pid_read(pidfile); @@ -930,6 +963,11 @@ static int cmd_status(int argc, char *argv[], unsigned flags, int jobs) return cmd_remote("status", KNOT_RRTYPE_TXT, 0, NULL); } +static int cmd_zonestatus(int argc, char *argv[], unsigned flags, int jobs) +{ + return cmd_remote("zonestatus", KNOT_RRTYPE_TXT, 0, NULL); +} + static int cmd_checkconf(int argc, char *argv[], unsigned flags, int jobs) { /* Check config. */ diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index ac1e527..ac8bc25 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -32,19 +32,22 @@ #define KNOT_CTL_REALM "knot." #define KNOT_CTL_REALM_EXT ("." KNOT_CTL_REALM) #define KNOT_CTL_REALM_LEN 5 +#define CMDARGS_BUFLEN (1024*1024) /* 1M */ /*! \brief Remote command structure. */ typedef struct remote_cmdargs_t { const knot_rrset_t **arg; unsigned argc; knot_rcode_t rc; + char resp[CMDARGS_BUFLEN]; + size_t rlen; } remote_cmdargs_t; /*! \brief Callback prototype for remote commands. */ typedef int (*remote_cmdf_t)(server_t*, remote_cmdargs_t*); /*! \brief Callback prototype for per-zone operations. */ -typedef int (remote_zonef_t)(server_t*, knot_zone_t *); +typedef int (remote_zonef_t)(server_t*, const knot_zone_t *); /*! \brief Remote command table item. */ typedef struct remote_cmd_t { @@ -56,6 +59,7 @@ typedef struct remote_cmd_t { static int remote_c_reload(server_t *s, remote_cmdargs_t* a); static int remote_c_refresh(server_t *s, remote_cmdargs_t* a); static int remote_c_status(server_t *s, remote_cmdargs_t* a); +static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a); static int remote_c_flush(server_t *s, remote_cmdargs_t* a); /*! \brief Table of remote commands. */ @@ -63,6 +67,7 @@ struct remote_cmd_t remote_cmd_tbl[] = { { "reload", &remote_c_reload }, { "refresh", &remote_c_refresh }, { "status", &remote_c_status }, + { "zonestatus",&remote_c_zonestatus }, { "flush", &remote_c_flush }, { NULL, NULL } }; @@ -112,7 +117,7 @@ static int remote_rdata_apply(server_t *s, remote_cmdargs_t* a, remote_zonef_t * } /*! \brief Zone refresh callback. */ -static int remote_zone_refresh(server_t *s, knot_zone_t *z) +static int remote_zone_refresh(server_t *s, const knot_zone_t *z) { if (!s || !z) { return KNOT_EINVAL; @@ -136,7 +141,7 @@ static int remote_zone_refresh(server_t *s, knot_zone_t *z) } /*! \brief Zone flush callback. */ -static int remote_zone_flush(server_t *s, knot_zone_t *z) +static int remote_zone_flush(server_t *s, const knot_zone_t *z) { if (!s || !z) { return KNOT_EINVAL; @@ -160,7 +165,7 @@ static int remote_zone_flush(server_t *s, knot_zone_t *z) } /*! \brief Helper to build RDATA from RDATA item. */ -knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i) +knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i, unsigned c) { /* Create RDATA. */ knot_rdata_t *rd = knot_rdata_new(); @@ -169,7 +174,7 @@ knot_rdata_t* remote_build_rdata(knot_rdata_item_t *i) } /* Set RDATA items. */ - int ret = knot_rdata_set_items(rd, i, 1); + int ret = knot_rdata_set_items(rd, i, c); if (ret != KNOT_EOK) { knot_rdata_free(&rd); return NULL; @@ -192,7 +197,7 @@ static int remote_c_reload(server_t *s, remote_cmdargs_t* a) /*! * \brief Remote command 'status' handler. * - * QNAME: refresh + * QNAME: status * DATA: NONE */ static int remote_c_status(server_t *s, remote_cmdargs_t* a) @@ -203,6 +208,108 @@ static int remote_c_status(server_t *s, remote_cmdargs_t* a) } /*! + * \brief Remote command 'zonestatus' handler. + * + * QNAME: zonestatus + * DATA: NONE + */ +static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a) +{ + dbg_server("remote: %s\n", __func__); + char *dst = a->resp; + size_t rb = sizeof(a->resp) - 1; + + int ret = KNOT_EOK; + rcu_read_lock(); + knot_nameserver_t *ns = s->nameserver; + const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); + for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { + zonedata_t *zd = (zonedata_t *)zones[i]->data; + + /* Fetch latest serial. */ + const knot_rrset_t *soa_rrs = 0; + const knot_rdata_t *soa_rr = 0; + uint32_t serial = 0; + knot_zone_contents_t *contents = knot_zone_get_contents(zones[i]); + if (contents) { + soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents), + KNOT_RRTYPE_SOA); + assert(soa_rrs != NULL); + + soa_rr = knot_rrset_rdata(soa_rrs); + + int64_t serial_ret = knot_rdata_soa_serial(soa_rr); + if (serial_ret > 0) { + serial = (uint32_t)serial_ret; + } + } + + /* Evalute zone type. */ + const char *state = NULL; + if (serial == 0) { + state = "bootstrap"; + } else if (zd->xfr_in.has_master) { + state = "xfer"; + } + + /* Evaluate zone state. */ + char *when = NULL; + int locked = pthread_mutex_trylock(&zd->xfr_in.lock); + if (locked == 0) pthread_mutex_unlock(&zd->xfr_in.lock); + if (locked != 0) { + when = strdup("pending"); + } else if (zd->xfr_in.scheduled) { + when = strdup("scheduled"); + } else if (zd->xfr_in.timer) { + struct timeval now, dif; + gettimeofday(&now, 0); + timersub(&zd->xfr_in.timer->tv, &now, &dif); + when = malloc(64); + if (when == NULL) { + ret = KNOT_ENOMEM; + break; + } + if (snprintf(when, 64, "in %luh%lum%lus", + dif.tv_sec/3600, + (dif.tv_sec % 3600)/60, + dif.tv_sec % 60) < 0) { + free(when); + ret = KNOT_ESPACE; + break; + } + } else { + when = strdup("idle"); + } + + /* Workaround, some platforms ignore 'size' with snprintf() */ + char buf[256]; + int n = snprintf(buf, sizeof(buf), "%s\ttype=%s | serial=%u | %s %s\n", + zd->conf->name, + zd->xfr_in.has_master ? "slave" : "master", + serial, + state ? state : "", + when ? when : ""); + free(when); + if (n > rb) { + *dst = '\0'; + ret = KNOT_ESPACE; + break; + } + + memcpy(dst, buf, n); + rb -= n; + dst += n; + + + } + rcu_read_unlock(); + free(zones); + + a->rlen = sizeof(a->resp) - 1 - rb; + return ret; +} + +/*! * \brief Remote command 'refresh' handler. * * QNAME: refresh @@ -234,8 +341,17 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a) /* Flush all. */ dbg_server("remote: %s\n", __func__); if (a->argc == 0) { + int ret = 0; dbg_server_verb("remote: flushing all zones\n"); - return KNOT_ENOTSUP; + rcu_read_lock(); + knot_nameserver_t *ns = s->nameserver; + const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); + for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { + ret = remote_zone_flush(s, zones[i]); + } + rcu_read_unlock(); + free(zones); + return ret; } /* Flush specific zones. */ @@ -334,10 +450,62 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen) return ret; } -int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen) +static int tmp_send(int c, knot_packet_t *pkt, const char* d, uint16_t dlen, uint8_t* rwire, size_t rlen) { - if (!s || !pkt || !rwire) { - *rlen = 0; + int ret = KNOT_ERROR; + knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE); + if (!resp) { + return ret; + } + uint8_t *wire = NULL; + size_t len = 0; + ret = knot_packet_set_max_size(resp, SOCKET_MTU_SZ); + if (ret != KNOT_EOK) { + knot_packet_free(&resp); + return ret; + } + ret = knot_response_init_from_query(resp, pkt, 1); + if (ret != KNOT_EOK) { + knot_packet_free(&resp); + return ret; + } + ret = knot_packet_to_wire(resp, &wire, &len); + if (ret != KNOT_EOK) { + knot_packet_free(&resp); + return ret; + } + if (len > 0) { + memcpy(rwire, wire, len); + rlen -= len; + } + knot_packet_free(&resp); + if (len == 0) { + return KNOT_ERROR; + } + + /* Evaluate output. */ + int rr_count = 0; + knot_rrset_t *rr = remote_build_rr("result.", KNOT_RRTYPE_TXT); + knot_rdata_t *rd = remote_create_txt(d, dlen); + knot_rrset_add_rdata(rr, rd); + + size_t rrlen = rlen; + knot_rrset_to_wire(rr, rwire + len, &rrlen, &rr_count); + knot_wire_set_nscount(rwire, rr_count); + len += rrlen; + rlen -= rrlen; + knot_rrset_deep_free(&rr, 1, 1, 1); + + if (len > 0) { + return tcp_send(c, rwire, len); + } + + return len; +} + +int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t rlen) +{ + if (fd < 0 || !s || !pkt || !rwire) { return KNOT_EINVAL; } @@ -348,7 +516,6 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen) const knot_dname_t *qname = knot_packet_qname(pkt); if (knot_packet_qclass(pkt) != KNOT_CLASS_CH) { dbg_server("remote: qclass != CH\n"); - *rlen = 0; return KNOT_EMALF; } @@ -357,7 +524,6 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen) if (!knot_dname_is_subdomain(qname, realm) != 0) { dbg_server("remote: qname != *%s\n", KNOT_CTL_REALM_EXT); knot_dname_free(&realm); - *rlen = 0; return KNOT_EMALF; } knot_dname_free(&realm); @@ -373,64 +539,43 @@ int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen) * AR: data */ int ret = KNOT_EOK; + remote_cmdargs_t* args = malloc(sizeof(remote_cmdargs_t)); + if (!args) { + free(cmd); + return KNOT_ENOMEM; + } + memset(args, 0, sizeof(remote_cmdargs_t)); + args->arg = pkt->authority; + args->argc = knot_packet_authority_rrset_count(pkt); + args->rc = KNOT_RCODE_NOERROR; + remote_cmd_t *c = remote_cmd_tbl; - remote_cmdargs_t args; - args.arg = pkt->authority; - args.argc = knot_packet_authority_rrset_count(pkt); - args.rc = KNOT_RCODE_NOERROR; while(c->name != NULL) { if (strcmp(cmd, c->name) == 0) { - ret = c->f(s, &args); + ret = c->f(s, args); break; } ++c; } /* Prepare response. */ - size_t remaining = *rlen; - knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE); - if (!resp) { - free(cmd); - return ret; - } - uint8_t *wire = NULL; - size_t len = 0; - ret = knot_packet_set_max_size(resp, SOCKET_MTU_SZ); - if (ret != KNOT_EOK) { - free(cmd); - knot_packet_free(&resp); - return ret; + if (ret != KNOT_EOK || args->rlen == 0) { + args->rlen = strlen(knot_strerror(ret)); + strncpy(args->resp, knot_strerror(ret), args->rlen); } - ret = knot_response_init_from_query(resp, pkt, 1); - if (ret != KNOT_EOK) { - free(cmd); - knot_packet_free(&resp); - return ret; + + unsigned p = 0; + size_t chunk = 16384; + for (; p + chunk < args->rlen; p += chunk) { + tmp_send(fd, pkt, args->resp + p, chunk, rwire, rlen); } - ret = knot_packet_to_wire(resp, &wire, &len); - if (ret != KNOT_EOK) { - free(cmd); - knot_packet_free(&resp); - return ret; - } - if (len > 0) { - memcpy(rwire, wire, len); - *rlen = len; + unsigned r = args->rlen - p; + if (r > 0) { + tmp_send(fd, pkt, args->resp + p, r, rwire, rlen); } - knot_packet_free(&resp); - /* Evaluate output. */ - int rr_count = 0; - knot_rrset_t *rr = remote_build_rr("result.", KNOT_RRTYPE_TXT); - knot_rdata_t *rd = remote_create_txt(knot_strerror(ret)); - knot_rrset_add_rdata(rr, rd); - - remaining -= *rlen; - knot_rrset_to_wire(rr, rwire + *rlen, &remaining, &rr_count); - knot_wire_set_nscount(rwire, 1); - *rlen += remaining; - knot_rrset_deep_free(&rr, 1, 1, 1); + free(args); free(cmd); return ret; } @@ -504,11 +649,7 @@ int remote_process(server_t *s, int r, uint8_t* buf, size_t buflen) } /* Answer packet. */ - wire_len = buflen; - remote_answer(s, pkt, buf, &wire_len); - if (wire_len > 0) { - tcp_send(c, buf, wire_len); - } + remote_answer(c, s, pkt, buf, buflen); } knot_packet_free(&pkt); @@ -623,29 +764,44 @@ knot_rrset_t* remote_build_rr(const char *k, uint16_t t) return rr; } -knot_rdata_t* remote_create_txt(const char *v) +knot_rdata_t* remote_create_txt(const char *v, size_t v_len) { if (!v) { return NULL; } + + /* Number of chunks. */ + const size_t K = 255; + unsigned chunks = v_len / K + 1; /* Create raw_data item. */ - size_t v_len = strlen(v); - knot_rdata_item_t i; - i.raw_data = malloc(v_len + 3); - if (!i.raw_data) { + knot_rdata_item_t rditem; + rditem.raw_data = malloc(sizeof(uint16_t) + chunks + v_len); + if (!rditem.raw_data) { return NULL; } - *i.raw_data = v_len + 1; + rditem.raw_data[0] = v_len + chunks; + uint8_t *raw = (uint8_t*)(rditem.raw_data + 1); /* Write TXT item. */ - uint8_t *raw_item = (uint8_t*)(i.raw_data + 1); - *(raw_item++) = v_len; - memcpy(raw_item, v, v_len); - - knot_rdata_t *rd = remote_build_rdata(&i); + unsigned p = 0; + if (v_len > K) { + for (; p + K < v_len; p += K) { + *(raw++) = (uint8_t)K; + memcpy(raw, v+p, K); + raw += K; + } + } + unsigned r = v_len - p; + if (r > 0) { + *(raw++) = (uint8_t)r; + memcpy(raw, v+p, r); + raw += K; + } + + knot_rdata_t *rd = remote_build_rdata(&rditem, 1); if (!rd) { - free(i.raw_data); + free(rditem.raw_data); } return rd; @@ -663,7 +819,7 @@ knot_rdata_t* remote_create_cname(const char *d) i.dname = dn; /* Build RDATA. */ - knot_rdata_t *rd = remote_build_rdata(&i); + knot_rdata_t *rd = remote_build_rdata(&i, 1); if (!rd) { knot_dname_release(dn); } @@ -671,20 +827,30 @@ knot_rdata_t* remote_create_cname(const char *d) return rd; } -char* remote_parse_txt(const knot_rdata_t *rd) +int remote_print_txt(const knot_rdata_t *rd) { if (!rd || knot_rdata_count(rd) < 1) { - return NULL; + return KNOT_EINVAL; } const knot_rdata_item_t *ri = knot_rdata_item(rd, 0); if (!ri) { - return NULL; + return KNOT_EINVAL; } /* Packet parser should have already checked the packet validity. */ - uint8_t item_len = ri->raw_data[1] & 0x00ff; - return strndup(((const char*)ri->raw_data) + 3, item_len); + char buf[256]; + uint16_t parsed = 0; + uint16_t rlen = ri->raw_data[0]; + uint8_t *p = (uint8_t*)(ri->raw_data + 1); + while (parsed < rlen) { + memcpy(buf, (const char*)(p+1), *p); + buf[*p] = '\0'; + printf("%s", buf); + parsed += *p + 1; + p += *p + 1; + } + return KNOT_EOK; } knot_dname_t* remote_dname_fqdn(const char *k) diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h index eb3eb81..55c6d5a 100644 --- a/src/knot/ctl/remote.h +++ b/src/knot/ctl/remote.h @@ -94,6 +94,7 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen); /*! * \brief Execute command and prepare answer for client. * + * \param fd Remote client * \param s Server instance. * \param pkt Parsed RC command. * \param rwire Buffer for response. @@ -102,7 +103,7 @@ int remote_parse(knot_packet_t* pkt, const uint8_t* buf, size_t buflen); * \retval KNOT_EOK on success. * \retval knot_error else. */ -int remote_answer(server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t *rlen); +int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_t rlen); /*! * \brief Accept new client, receive command, process it and send response. @@ -174,9 +175,10 @@ knot_rrset_t* remote_build_rr(const char *k, uint16_t t); /*! * \brief Create a TXT rdata. * \param v Text as a string. + * \param v_len Text length. * \return Created rdata or NULL. */ -knot_rdata_t* remote_create_txt(const char *v); +knot_rdata_t* remote_create_txt(const char *v, size_t v_len); /*! * \brief Create a CNAME rdata. @@ -186,11 +188,11 @@ knot_rdata_t* remote_create_txt(const char *v); knot_rdata_t* remote_create_cname(const char *d); /*! - * \brief Parse TXT rdata to string. + * \brief Print TXT rdata to stdout. * \param rd TXT rdata. - * \return Parsed string or NULL. + * \return KNOT_EOK */ -char* remote_parse_txt(const knot_rdata_t *rd); +int remote_print_txt(const knot_rdata_t *rd); /*! * \brief Create dname from str and make sure the name is FQDN. diff --git a/src/knot/main.c b/src/knot/main.c index 4359194..c433007 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -88,7 +88,7 @@ int main(int argc, char **argv) int c = 0, li = 0; int verbose = 0; int daemonize = 0; - char* config_fn = 0; + char* config_fn = NULL; /* Long options. */ struct option opts[] = { @@ -118,6 +118,7 @@ int main(int argc, char **argv) case 'h': case '?': default: + free(config_fn); help(argc, argv); return 1; } @@ -126,6 +127,7 @@ int main(int argc, char **argv) // Now check if we want to daemonize if (daemonize) { if (daemon(1, 0) != 0) { + free(config_fn); fprintf(stderr, "Daemonization failed, " "shutting down...\n"); return 1; @@ -157,15 +159,6 @@ int main(int argc, char **argv) // Initialize pseudorandom number generator srand(time(0)); - // Create server - server_t *server = server_create(); - - // Initialize configuration - rcu_read_lock(); - conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0); - conf_add_hook(conf(), CONF_ALL, server_conf_hook, server); - rcu_read_unlock(); - // Find implicit configuration file if (!config_fn) { config_fn = conf_find_default(); @@ -179,6 +172,7 @@ int main(int argc, char **argv) if (rpath == NULL) { log_server_error("Couldn't get absolute path for configuration file '%s' - " "%s.\n", config_fn, strerror(errno)); + free(config_fn); return 1; } else { free(config_fn); @@ -186,6 +180,15 @@ int main(int argc, char **argv) } } + // Create server + server_t *server = server_create(); + + // Initialize configuration + rcu_read_lock(); + conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0); + conf_add_hook(conf(), CONF_ALL, server_conf_hook, server); + rcu_read_unlock(); + /* POSIX 1003.1e capabilities. */ #ifdef HAVE_CAP_NG_H @@ -245,6 +248,7 @@ int main(int argc, char **argv) log_server_info("\n"); /* Alter privileges. */ + log_update_privileges(conf()->uid, conf()->gid); proc_update_privileges(conf()->uid, conf()->gid); /* Load zones and add hook. */ diff --git a/src/knot/server/rrl.c b/src/knot/server/rrl.c index 70101c9..84b8c90 100644 --- a/src/knot/server/rrl.c +++ b/src/knot/server/rrl.c @@ -28,6 +28,8 @@ #include "common/prng.h" #include "common/errors.h" +/* Hopscotch defines. */ +#define HOP_LEN (sizeof(unsigned)*8) /* Limits */ #define RRL_CLSBLK_MAXLEN (4 + 8 + 1 + 256) /* CIDR block prefix lengths for v4/v6 */ @@ -41,21 +43,6 @@ /* Enable RRL logging. */ #define RRL_ENABLE_LOG -/* RRL granular locking. */ -static int rrl_lock_mx(rrl_table_t *t, int lk_id) -{ - assert(lk_id > -1); - dbg_rrl_verb("%s: locking id '%d'\n", __func__, lk_id); - return pthread_mutex_lock(&t->lk[lk_id].mx); -} - -static int rrl_unlock_mx(rrl_table_t *t, int lk_id) -{ - assert(lk_id > -1); - dbg_rrl_verb("%s: unlocking id '%d'\n", __func__, lk_id); - return pthread_mutex_unlock(&t->lk[lk_id].mx); -} - /* Classification */ enum { CLS_NULL = 0 << 0, /* Empty bucket. */ @@ -199,8 +186,11 @@ static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a, blklen += sizeof(nb); /* Name */ + uint16_t *nlen = (uint16_t*)(dst + blklen); + blklen += sizeof(uint16_t); int len = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z); if (len < 0) return len; + *nlen = len; blklen += len; /* Seed. */ @@ -212,51 +202,81 @@ static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a, return blklen; } -static rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p, - const knot_zone_t *zone, uint32_t stamp, int *lk) +static int bucket_free(rrl_item_t *b, uint32_t now) { + return b->cls == CLS_NULL || (b->time + 1 < now); +} + +static int bucket_match(rrl_item_t *b, rrl_item_t *m) { - char buf[RRL_CLSBLK_MAXLEN]; - int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed); - if (len < 0) { - return NULL; + return b->cls == m->cls && + b->netblk == m->netblk && + b->qname == m->qname; +} + +static int find_free(rrl_table_t *t, unsigned i, uint32_t now) +{ + rrl_item_t *np = t->arr + t->size; + rrl_item_t *b = NULL; + for (b = t->arr + i; b != np; ++b) { + if (bucket_free(b, now)) { + return b - (t->arr + i); + } } - - uint32_t id = hash(buf, len) % t->size; - - /* Check locking. */ - *lk = -1; - if (t->lk_count > 0) { - *lk = id % t->lk_count; - rrl_lock_mx(t, *lk); + np = t->arr + i; + for (b = t->arr; b != np; ++b) { + if (bucket_free(b, now)) { + return (b - t->arr) + (t->size - i); + } } - rrl_item_t *b = t->arr + id; - dbg_rrl("%s: classified pkt as '0x%x' bucket=%p\n", __func__, id, b); + /* this happens if table is full... force vacate current elm */ + dbg_rrl("%s: out of free buckets, freeing bucket %u\n", __func__, i); + return i; +} - /* Inspect bucket state. */ - uint64_t nprefix = *((uint64_t*)(buf + sizeof(uint8_t))); - if (b->cls == CLS_NULL) { - b->cls = *buf; /* Stored as a first byte in clsblock. */ - b->flags = RRL_BF_NULL; - b->ntok = t->rate; - b->time = stamp; - b->pref = nprefix; /* Invalidate */ +static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m) +{ + unsigned f = 0; + unsigned d = 0; + unsigned match = t->arr[id].hop; + while (match != 0) { + d = __builtin_ctz(match); + f = (id + d) % t->size; + if (bucket_match(t->arr + f, m)) { + return d; + } else { + match &= ~(1<<d); /* clear potential match */ + } } - /* Check for collisions. */ - if (b->pref != nprefix) { - dbg_rrl("%s: collision in bucket '0x%4x'\n", __func__, id); - if (!(b->flags & RRL_BF_SSTART)) { - b->pref = nprefix; - b->cls = *buf; - b->flags = RRL_BF_NULL; /* Reset flags. */ - b->time = stamp; /* Reset time */ - b->ntok = t->rate / RRL_SSTART; - b->flags |= RRL_BF_SSTART; - dbg_rrl("%s: bucket '0x%4x' slow-start\n", __func__, id); + + return HOP_LEN + 1; +} + +static inline unsigned reduce_dist(rrl_table_t *t, unsigned id, unsigned d, unsigned *f) +{ + unsigned rd = HOP_LEN - 1; + while (rd > 0) { + unsigned s = (t->size + *f - rd) % t->size; /* bucket to be vacated */ + unsigned o = __builtin_ctz(t->arr[s].hop); /* offset of first valid bucket */ + if (t->arr[s].hop != 0 && o < rd) { /* only offsets in <s, f> are interesting */ + unsigned e = (s + o) % t->size; /* this item will be displaced to [f] */ + unsigned keep_hop = t->arr[*f].hop; /* unpredictable padding */ + memcpy(t->arr + *f, t->arr + e, sizeof(rrl_item_t)); + t->arr[*f].hop = keep_hop; + t->arr[e].cls = CLS_NULL; + t->arr[s].hop &= ~(1<<o); + t->arr[s].hop |= 1<<rd; + *f = e; + return d - (rd - o); } + --rd; } - return b; + assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */ + *f = id; + d = 0; /* force vacate initial element */ + dbg_rrl("%s: no potential relocation, freeing bucket %u\n", __func__, id); + return d; } static void rrl_log_state(const sockaddr_t *a, uint16_t flags, uint8_t cls) @@ -277,14 +297,16 @@ static void rrl_log_state(const sockaddr_t *a, uint16_t flags, uint8_t cls) rrl_table_t *rrl_create(size_t size) { + if (size == 0) { + return NULL; + } + const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t); rrl_table_t *t = malloc(tbl_len); if (!t) return NULL; - - memset(t, 0, tbl_len); - t->rate = 0; - t->seed = (uint32_t)(tls_rand() * (double)UINT32_MAX); + memset(t, 0, sizeof(rrl_table_t)); t->size = size; + rrl_reseed(t); dbg_rrl("%s: created table size '%zu'\n", __func__, t->size); return t; } @@ -303,25 +325,31 @@ uint32_t rrl_rate(rrl_table_t *rrl) return rrl->rate; } -int rrl_setlocks(rrl_table_t *rrl, size_t granularity) +int rrl_setlocks(rrl_table_t *rrl, unsigned granularity) { if (!rrl) return KNOT_EINVAL; assert(!rrl->lk); /* Cannot change while locks are used. */ + assert(granularity <= rrl->size / 10); /* Due to int. division err. */ + + if (pthread_mutex_init(&rrl->ll, NULL) < 0) { + return KNOT_ENOMEM; + } /* Alloc new locks. */ - rrl->lk = malloc(granularity * sizeof(rrl_lock_t)); + rrl->lk = malloc(granularity * sizeof(pthread_mutex_t)); if (!rrl->lk) return KNOT_ENOMEM; - memset(rrl->lk, 0, granularity * sizeof(rrl_lock_t)); + memset(rrl->lk, 0, granularity * sizeof(pthread_mutex_t)); /* Initialize. */ for (size_t i = 0; i < granularity; ++i) { - if (pthread_mutex_init(&rrl->lk[i].mx, NULL) < 0) break; + if (pthread_mutex_init(rrl->lk + i, NULL) < 0) break; ++rrl->lk_count; } + /* Incomplete initialization */ if (rrl->lk_count != granularity) { for (size_t i = 0; i < rrl->lk_count; ++i) { - pthread_mutex_destroy(&rrl->lk[i].mx); + pthread_mutex_destroy(rrl->lk + i); } free(rrl->lk); rrl->lk_count = 0; @@ -333,6 +361,71 @@ int rrl_setlocks(rrl_table_t *rrl, size_t granularity) return KNOT_EOK; } +rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p, + const knot_zone_t *zone, uint32_t stamp, int *lock) +{ + char buf[RRL_CLSBLK_MAXLEN]; + int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed); + if (len < 0) { + return NULL; + } + + uint32_t id = hash(buf, len) % t->size; + + /* Lock for lookup. */ + pthread_mutex_lock(&t->ll); + + /* Find an exact match in <id, id + HOP_LEN). */ + uint16_t *qname = (uint16_t*)(buf + sizeof(uint8_t) + sizeof(uint64_t)); + rrl_item_t match = { + 0, *((uint64_t*)(buf + 1)), t->rate, /* hop, netblk, ntok */ + buf[0], RRL_BF_NULL, /* cls, flags */ + hash((char*)(qname + 1), *qname), stamp /* qname, time*/ + }; + + unsigned d = find_match(t, id, &match); + if (d > HOP_LEN) { /* not an exact match, find free element [f] */ + d = find_free(t, id, stamp); + } + + /* Reduce distance to fit <id, id + HOP_LEN) */ + unsigned f = (id + d) % t->size; + while (d >= HOP_LEN) { + d = reduce_dist(t, id, d, &f); + } + + /* Assign granular lock and unlock lookup. */ + *lock = f % t->lk_count; + rrl_lock(t, *lock); + pthread_mutex_unlock(&t->ll); + + /* found free elm 'k' which is in <id, id + HOP_LEN) */ + t->arr[id].hop |= (1 << d); + rrl_item_t* b = t->arr + f; + assert(f == (id+d) % t->size); + dbg_rrl("%s: classified pkt as %4x '%u+%u' bucket=%p \n", __func__, f, id, d, b); + + /* Inspect bucket state. */ + unsigned hop = b->hop; + if (b->cls == CLS_NULL) { + memcpy(b, &match, sizeof(rrl_item_t)); + b->hop = hop; + } + /* Check for collisions. */ + if (!bucket_match(b, &match)) { + dbg_rrl("%s: collision in bucket '%4x'\n", __func__, id); + if (!(b->flags & RRL_BF_SSTART)) { + memcpy(b, &match, sizeof(rrl_item_t)); + b->hop = hop; + b->ntok = t->rate + t->rate / RRL_SSTART; + b->flags |= RRL_BF_SSTART; + dbg_rrl("%s: bucket '%4x' slow-start\n", __func__, id); + } + } + + return b; +} + int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req, const knot_zone_t *zone) { @@ -344,8 +437,8 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req, uint32_t now = time(NULL); rrl_item_t *b = rrl_hash(rrl, a, req, zone, now, &lock); if (!b) { - assert(lock < 0); dbg_rrl("%s: failed to compute bucket from packet\n", __func__); + if (lock > -1) rrl_unlock(rrl, lock); return KNOT_ERROR; } @@ -369,7 +462,6 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req, /* Add new tokens. */ uint32_t dn = rrl->rate * dt; if (b->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */ - dn /= RRL_SSTART; b->flags &= ~RRL_BF_SSTART; dbg_rrl("%s: bucket '0x%x' slow-start finished\n", __func__, (unsigned)(b - rrl->arr)); @@ -392,12 +484,8 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req, } else if (b->ntok == 0) { ret = KNOT_ELIMIT; } - - /* Unlock bucket. */ - if (lock > -1) { - rrl_unlock_mx(rrl, lock); - } + if (lock > -1) rrl_unlock(rrl, lock); return ret; } @@ -405,8 +493,9 @@ int rrl_destroy(rrl_table_t *rrl) { if (rrl) { dbg_rrl("%s: freeing table %p\n", __func__, rrl); + if (rrl->lk_count > 0) pthread_mutex_destroy(&rrl->ll); for (size_t i = 0; i < rrl->lk_count; ++i) { - pthread_mutex_destroy(&rrl->lk[i].mx); + pthread_mutex_destroy(rrl->lk + i); } free(rrl->lk); } @@ -414,3 +503,47 @@ int rrl_destroy(rrl_table_t *rrl) free(rrl); return KNOT_EOK; } + +int rrl_reseed(rrl_table_t *rrl) +{ + /* Lock entire table. */ + if (rrl->lk_count > 0) { + pthread_mutex_lock(&rrl->ll); + for (unsigned i = 0; i < rrl->lk_count; ++i) { + rrl_lock(rrl, i); + } + } + + memset(rrl->arr, 0, rrl->size * sizeof(rrl_item_t)); + rrl->seed = (uint32_t)(tls_rand() * (double)UINT32_MAX); + dbg_rrl("%s: reseed to '%u'\n", __func__, rrl->seed); + + if (rrl->lk_count > 0) { + for (unsigned i = 0; i < rrl->lk_count; ++i) { + rrl_unlock(rrl, i); + } + pthread_mutex_unlock(&rrl->ll); + } + + return KNOT_EOK; +} + +int rrl_lock(rrl_table_t *t, int lk_id) +{ + assert(lk_id > -1); + dbg_rrl_verb("%s: locking id '%d'\n", __func__, lk_id); + if (pthread_mutex_lock(t->lk + lk_id) != 0) { + return KNOT_ERROR; + } + return KNOT_EOK; +} + +int rrl_unlock(rrl_table_t *t, int lk_id) +{ + assert(lk_id > -1); + dbg_rrl_verb("%s: unlocking id '%d'\n", __func__, lk_id); + if (pthread_mutex_unlock(t->lk + lk_id)!= 0) { + return KNOT_ERROR; + } + return KNOT_EOK; +} diff --git a/src/knot/server/rrl.h b/src/knot/server/rrl.h index e0f650d..6ced2f8 100644 --- a/src/knot/server/rrl.h +++ b/src/knot/server/rrl.h @@ -34,23 +34,21 @@ #include "libknot/zone/zone.h" /* Defaults */ -#define RRL_LOCK_GRANULARITY 10 /* Last digit granularity */ +#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */ /*! * \brief RRL hash bucket. */ typedef struct rrl_item { - uint64_t pref; /* Prefix associated. */ + unsigned hop; /* Hop bitmap. */ + uint64_t netblk; /* Prefix associated. */ uint16_t ntok; /* Tokens available */ uint8_t cls; /* Bucket class */ uint8_t flags; /* Flags */ + uint32_t qname; /* imputed(QNAME) hash */ uint32_t time; /* Timestamp */ } rrl_item_t; -typedef struct rrl_lock { /* Wrapper around lock struct. */ - pthread_mutex_t mx; -} rrl_lock_t; - /*! * \brief RRL hash bucket table. * @@ -66,8 +64,9 @@ typedef struct rrl_lock { /* Wrapper around lock struct. */ typedef struct rrl_table { uint32_t rate; /* Configured RRL limit */ uint32_t seed; /* Pseudorandom seed for hashing. */ - rrl_lock_t *lk; /* Table locks. */ - size_t lk_count; /* Table lock count (granularity). */ + pthread_mutex_t ll; + pthread_mutex_t *lk; /* Table locks. */ + unsigned lk_count; /* Table lock count (granularity). */ size_t size; /* Number of buckets */ rrl_item_t arr[]; /* Buckets */ } rrl_table_t; @@ -115,7 +114,20 @@ uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate); * \retval KNOT_EOK * \retval KNOT_EINVAL */ -int rrl_setlocks(rrl_table_t *rrl, size_t granularity); +int rrl_setlocks(rrl_table_t *rrl, unsigned granularity); + +/*! + * \brief Get bucket for current combination of parameters. + * \param t RRL table. + * \param a Source address. + * \param p RRL request. + * \param zone Relate zone. + * \param stamp Timestamp (current time). + * \param lock Held lock. + * \return assigned bucket + */ +rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p, + const knot_zone_t *zone, uint32_t stamp, int* lock); /*! * \brief Query the RRL table for accept or deny, when the rate limit is reached. @@ -137,6 +149,30 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req, */ int rrl_destroy(rrl_table_t *rrl); +/*! + * \brief Reseed RRL table secret. + * \param rrl RRL table. + * \return KNOT_EOK + */ +int rrl_reseed(rrl_table_t *rrl); + +/*! + * \brief Lock specified element lock. + * \param rrl RRL table. + * \param lk_id Specified lock. + * \retval KNOT_EOK + * \retval KNOT_ERROR + */ +int rrl_lock(rrl_table_t *rrl, int lk_id); + +/*! + * \brief Unlock specified element lock. + * \param rrl RRL table. + * \param lk_id Specified lock. + * \retval KNOT_EOK + * \retval KNOT_ERROR + */ +int rrl_unlock(rrl_table_t *rrl, int lk_id); #endif /* _KNOTD_RRL_H_ */ diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 08abe11..4ac919d 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -652,7 +652,7 @@ int server_refresh(server_t *server) if (zd->xfr_in.timer) { evsched_cancel(sch, zd->xfr_in.timer); evsched_schedule(sch, zd->xfr_in.timer, - tls_rand() * 500 + i*200); + tls_rand() * 500 + i/2); /* Cumulative delay. */ } } diff --git a/src/knotc.8 b/src/knotc.8 index 039a994..bf4561f 100644 --- a/src/knotc.8 +++ b/src/knotc.8 @@ -1,4 +1,4 @@ -.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3" +.TH knotc "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4" .SH NAME .B knotc \- Knot DNS control utility @@ -63,6 +63,9 @@ Flush journal and update zone files. status Check if server is running. .TP +zonestatus +Show status of configured zones. +.TP compile Compile zone file. .TP diff --git a/src/knotd.8 b/src/knotd.8 index 22d0dac..3681343 100644 --- a/src/knotd.8 +++ b/src/knotd.8 @@ -1,4 +1,4 @@ -.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc3" +.TH "knotd" "8" "September 2012" "CZ.NIC Labs" "Knot DNS, version 1.2.0-rc4" .SH NAME .B knotd \- Knot DNS daemon diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c index b6381c4..2e6e922 100644 --- a/src/libknot/packet/packet.c +++ b/src/libknot/packet/packet.c @@ -307,9 +307,6 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos, return KNOT_EMALF; } } - if (*pos != i + 1) { - dbg_packet("Parsed dname expected len=%zu, parsed=%zu.\n", i+1-bp, *pos-bp); - } question->qtype = knot_wire_read_u16(wire + i + 1); question->qclass = knot_wire_read_u16(wire + i + 3); *pos += 4; diff --git a/src/tests/knot/rrl_tests.c b/src/tests/knot/rrl_tests.c index 35b481e..ed16530 100644 --- a/src/tests/knot/rrl_tests.c +++ b/src/tests/knot/rrl_tests.c @@ -18,13 +18,78 @@ #include <sys/socket.h> #include "tests/knot/rrl_tests.h" #include "knot/server/rrl.h" +#include "knot/server/dthreads.h" #include "knot/common.h" #include "libknot/packet/response.h" #include "libknot/packet/query.h" #include "libknot/nameserver/name-server.h" +#include "common/prng.h" /* Enable time-dependent tests. */ //#define ENABLE_TIMED_TESTS +#define RRL_SIZE 196613 +#define RRL_THREADS 8 +#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */ +#define RRL_LOCKS 64 + +/* Disabled as default as it depends on random input. + * Table may be consistent even if some collision occur (and they may occur). + */ +#ifdef ENABLE_TIMED_TESTS +struct bucketmap_t { + unsigned i; + uint64_t x; +}; + +/*! \brief Unit runnable. */ +struct runnable_data { + int passed; + rrl_table_t *rrl; + sockaddr_t *addr; + rrl_req_t *rq; + knot_zone_t *zone; +}; + +static void* rrl_runnable(void *arg) +{ + struct runnable_data* d = (struct runnable_data*)arg; + sockaddr_t addr; + memcpy(&addr, d->addr, sizeof(sockaddr_t)); + sockaddr_update(&addr); + int lock = -1; + uint32_t now = time(NULL); + struct bucketmap_t *m = malloc(RRL_INSERTS * sizeof(struct bucketmap_t)); + for (unsigned i = 0; i < RRL_INSERTS; ++i) { + m[i].i = tls_rand() * UINT32_MAX; + addr.addr4.sin_addr.s_addr = m[i].i; + rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock); + rrl_unlock(d->rrl, lock); + m[i].x = b->netblk; + } + for (unsigned i = 0; i < RRL_INSERTS; ++i) { + addr.addr4.sin_addr.s_addr = m[i].i; + rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock); + rrl_unlock(d->rrl, lock); + if (b->netblk != m[i].x) { + d->passed = 0; + } + } + free(m); + return NULL; +} + +static void rrl_hopscotch(struct runnable_data* rd) +{ + rd->passed = 1; + pthread_t thr[RRL_THREADS]; + for (unsigned i = 0; i < RRL_THREADS; ++i) { + pthread_create(thr + i, NULL, &rrl_runnable, rd); + } + for (unsigned i = 0; i < RRL_THREADS; ++i) { + pthread_join(thr[i], NULL); + } +} +#endif static int rrl_tests_count(int argc, char *argv[]); static int rrl_tests_run(int argc, char *argv[]); @@ -44,9 +109,9 @@ unit_api rrl_tests_api = { static int rrl_tests_count(int argc, char *argv[]) { - int c = 6; -#ifndef ENABLE_TIMED_TESTS - c -= 2; + int c = 5; +#ifdef ENABLE_TIMED_TESTS + c += 5; #endif return c; } @@ -60,7 +125,11 @@ static int rrl_tests_run(int argc, char *argv[]) qst.qname = knot_dname_new_from_str("beef.", 5, NULL); knot_packet_t *query = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY); knot_query_init(query); - knot_packet_set_max_size(query, 512); + if (knot_packet_set_max_size(query, 512) < 0) { + knot_dname_free(&qst.qname); + knot_packet_free(&query); + return KNOT_ERROR; /* Fatal */ + } knot_query_set_question(query, &qst); /* Prepare response */ @@ -77,22 +146,26 @@ static int rrl_tests_run(int argc, char *argv[]) rq.flags = 0; /* 1. create rrl table */ - rrl_table_t *rrl = rrl_create(101); + rrl_table_t *rrl = rrl_create(RRL_SIZE); ok(rrl != NULL, "rrl: create"); /* 2. set rate limit */ uint32_t rate = 10; rrl_setrate(rrl, rate); ok(rate == rrl_rate(rrl), "rrl: setrate"); + + /* 3. setlocks */ + int ret = rrl_setlocks(rrl, RRL_LOCKS); + ok(ret == KNOT_EOK, "rrl: setlocks"); - /* 3. N unlimited requests. */ + /* 4. N unlimited requests. */ knot_dname_t *apex = knot_dname_new_from_str("rrl.", 4, NULL); knot_zone_t *zone = knot_zone_new(knot_node_new(apex, NULL, 0), 0, 0); sockaddr_t addr; sockaddr_t addr6; sockaddr_set(&addr, AF_INET, "1.2.3.4", 0); sockaddr_set(&addr6, AF_INET6, "1122:3344:5566:7788::aabb", 0); - int ret = 0; + ret = 0; for (unsigned i = 0; i < rate; ++i) { if (rrl_query(rrl, &addr, &rq, zone) != KNOT_EOK || rrl_query(rrl, &addr6, &rq, zone) != KNOT_EOK) { @@ -103,16 +176,16 @@ static int rrl_tests_run(int argc, char *argv[]) ok(ret == 0, "rrl: unlimited IPv4/v6 requests"); #ifdef ENABLE_TIMED_TESTS - /* 4. limited request */ + /* 5. limited request */ ret = rrl_query(rrl, &addr, &rq, zone); ok(ret != 0, "rrl: throttled IPv4 request"); - /* 5. limited IPv6 request */ + /* 6. limited IPv6 request */ ret = rrl_query(rrl, &addr6, &rq, zone); ok(ret != 0, "rrl: throttled IPv6 request"); #endif - /* 6. invalid values. */ + /* 7. invalid values. */ ret = 0; lives_ok( { rrl_create(0); // NULL @@ -123,7 +196,23 @@ static int rrl_tests_run(int argc, char *argv[]) ret += rrl_query(rrl, 0, 0, 0); // -1 ret += rrl_query(rrl, (void*)0x1, 0, 0); // -1 ret += rrl_destroy(0); // -1 - }, "dthreads: not crashed while executing functions on NULL context"); + }, "rrl: not crashed while executing functions on NULL context"); + +#ifdef ENABLE_TIMED_TESTS + /* 8. hopscotch test */ + struct runnable_data rd = { + 1, rrl, &addr, &rq, zone + }; + rrl_hopscotch(&rd); + ok(rd.passed, "rrl: hashtable is ~ consistent"); + + /* 9. reseed */ + ok(rrl_reseed(rrl) == 0, "rrl: reseed"); + + /* 10. hopscotch after reseed. */ + rrl_hopscotch(&rd); + ok(rd.passed, "rrl: hashtable is ~ consistent"); +#endif knot_dname_release(qst.qname); knot_dname_release(apex); |