so-rcvbuf option.

git-svn-id: file:///svn/unbound/trunk@1851 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2009-09-25 08:54:50 +00:00
parent c467aabbf1
commit d59a8baec2
14 changed files with 1472 additions and 1338 deletions

View File

@ -1,3 +1,8 @@
24 September 2009: Wouter
- so-rcvbuf: 4m option added. Set this on large busy servers to not
drop the occasional packet in spikes due to full socket buffers.
netstat -su keeps a counter of UDP dropped due to full buffers.
23 September 2009: Wouter
- 5011 query failed counts verification failures, not lookup failures.
- 5011 probe failure handling fixup.

View File

@ -73,6 +73,10 @@ server:
# number of incoming simultaneous tcp buffers to hold per thread.
# incoming-num-tcp: 10
# buffer size for UDP port 53 incoming (SO_RCVBUF socket option).
# 0 is system default. Use 4m to catch query spikes for busy servers.
# so-rcvbuf: 0
# buffer size for handling DNS data. No messages larger than this
# size can be sent or received, by UDP or TCP. In bytes.
# msg-buffer-size: 65552

View File

@ -197,6 +197,17 @@ the other 50% are replaced with the new incoming query if they have already
spent more than their allowed time. This protects against denial of
service by slow queries or high query rates. Default 200 milliseconds.
.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
space on UDP port 53 incoming queries. So that short spikes on busy
servers do not drop packets (see counter in netstat \-su). Default is
0 (use system value). Otherwise, the number of bytes to ask for, try
"4m" on a busy server. The OS caps it at a maximum, on linux unbound
needs root permission to bypass the limit, or the admin can use sysctl
net.core.rmem_max. On BSD change kern.ipc.maxsockbuf in /etc/sysctl.conf.
On OpenBSD change header and recompile kernel. On Solaris ndd \-set
/dev/udp udp_max_buf 8388608.
.TP
.B rrset\-cache\-size: \fI<number>
Number of bytes size of the RRset cache. Default is 4 megabytes.
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes

View File

@ -89,7 +89,7 @@ verbose_print_addr(struct addrinfo *addr)
int
create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto)
socklen_t addrlen, int v6only, int* inuse, int* noproto, int rcv)
{
int s;
#if defined(IPV6_USE_MIN_MTU)
@ -98,6 +98,9 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
#ifdef IPV6_MTU
int mtu = IPV6_MIN_MTU;
#endif
#if !defined(SO_RCVBUFFORCE) && !defined(SO_RCVBUF)
(void)rcv;
#endif
#ifndef IPV6_V6ONLY
(void)v6only;
#endif
@ -121,6 +124,64 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
*noproto = 0;
return -1;
}
if(rcv) {
#ifdef SO_RCVBUF
int got;
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_RCVBUFFORCE
/* Linux specific: try to use root permission to override
* system limits on rcvbuf. The limit is stored in
* /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
if(errno != EPERM) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_RCVBUFFORCE, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_RCVBUFFORCE, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* SO_RCVBUFFORCE */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
# ifndef USE_WINSOCK
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s", strerror(errno));
close(s);
# else
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s",
wsa_strerror(WSAGetLastError()));
closesocket(s);
# endif
*noproto = 0;
*inuse = 0;
return -1;
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
&slen) >= 0 && got < rcv/2) {
log_warn("so-rcvbuf %u was not granted. "
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.rmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
(unsigned)rcv, (unsigned)got);
}
# ifdef SO_RCVBUFFORCE
}
# endif
#endif /* SO_RCVBUF */
}
if(family == AF_INET6) {
# if defined(IPV6_V6ONLY)
if(v6only) {
@ -313,7 +374,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto)
*/
static int
make_sock(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6)
struct addrinfo *hints, int v6only, int* noip6, size_t rcv)
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
@ -339,8 +400,8 @@ make_sock(int stype, const char* ifname, const char* port,
if(stype == SOCK_DGRAM) {
verbose_print_addr(res);
s = create_udp_sock(res->ai_family, res->ai_socktype,
(struct sockaddr*)res->ai_addr,
res->ai_addrlen, v6only, &inuse, &noproto);
(struct sockaddr*)res->ai_addr,
res->ai_addrlen, v6only, &inuse, &noproto, (int)rcv);
if(s == -1 && inuse) {
log_err("bind: address already in use");
} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
@ -442,18 +503,20 @@ set_recvpktinfo(int s, int family)
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param list: list of open ports, appended to, changed to point to list head.
* @param rcv: receive buffer size for UDP
* @return: returns false on error.
*/
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list)
struct addrinfo *hints, const char* port, struct listen_port** list,
size_t rcv)
{
int s, noip6=0;
if(!do_udp && !do_tcp)
return 0;
if(do_auto) {
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1,
&noip6)) == -1) {
&noip6, rcv)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
@ -474,7 +537,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
} else if(do_udp) {
/* regular udp socket */
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1,
&noip6)) == -1) {
&noip6, rcv)) == -1) {
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
@ -492,7 +555,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
if(do_tcp) {
if((s = make_sock(SOCK_STREAM, ifname, port, hints, 1,
&noip6)) == -1) {
&noip6, 0)) == -1) {
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
return 1;
@ -668,7 +731,7 @@ listening_ports_open(struct config_file* cfg)
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list)) {
&hints, portbuf, &list, cfg->socket_rcvbuf)) {
listening_ports_free(list);
return NULL;
}
@ -677,7 +740,7 @@ listening_ports_open(struct config_file* cfg)
hints.ai_family = AF_INET;
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list)) {
&hints, portbuf, &list, cfg->socket_rcvbuf)) {
listening_ports_free(list);
return NULL;
}
@ -688,7 +751,8 @@ listening_ports_open(struct config_file* cfg)
continue;
hints.ai_family = AF_INET6;
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list)) {
do_tcp, &hints, portbuf, &list,
cfg->socket_rcvbuf)) {
listening_ports_free(list);
return NULL;
}
@ -697,7 +761,8 @@ listening_ports_open(struct config_file* cfg)
continue;
hints.ai_family = AF_INET;
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list)) {
do_tcp, &hints, portbuf, &list,
cfg->socket_rcvbuf)) {
listening_ports_free(list);
return NULL;
}

View File

@ -180,10 +180,11 @@ size_t listen_get_mem(struct listen_dnsport* listen);
* @param inuse: on error, this is set true if the port was in use.
* @param noproto: on error, this is set true if cause is that the
IPv6 proto (family) is not available.
* @param rcv: set size on rcvbuf with socket option, if 0 it is not set.
* @return: the socket. -1 on error.
*/
int create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto);
socklen_t addrlen, int v6only, int* inuse, int* noproto, int rcv);
/**
* Create and bind TCP listening socket

View File

@ -754,12 +754,12 @@ udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int port,
struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
sa->sin6_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto);
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto, 0);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto);
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto, 0);
}
return fd;
}

View File

@ -140,6 +140,7 @@ print_option(struct config_file* cfg, const char* opt)
else O_DEC(opt, "msg-cache-slabs", msg_cache_slabs)
else O_DEC(opt, "num-queries-per-thread", num_queries_per_thread)
else O_UNS(opt, "jostle-timeout", jostle_time)
else O_MEM(opt, "so-rcvbuf", socket_rcvbuf)
else O_MEM(opt, "rrset-cache-size", rrset_cache_size)
else O_DEC(opt, "rrset-cache-slabs", rrset_cache_slabs)
else O_DEC(opt, "cache-max-ttl", max_ttl)

View File

@ -123,6 +123,7 @@ config_create()
cfg->root_hints = NULL;
cfg->do_daemonize = 1;
cfg->if_automatic = 0;
cfg->socket_rcvbuf = 0;
cfg->num_ifs = 0;
cfg->ifs = NULL;
cfg->num_out_ifs = 0;
@ -284,6 +285,8 @@ int config_set_option(struct config_file* cfg, const char* opt,
} else if(strcmp(opt, "jostle-timeout:") == 0) {
IS_NUMBER_OR_ZERO;
cfg->jostle_time = (size_t)atoi(val);
} else if(strcmp(opt, "so-rcvbuf:") == 0) {
return cfg_parse_memsize(val, &cfg->socket_rcvbuf);
} else if(strcmp(opt, "rrset-cache-size:") == 0) {
return cfg_parse_memsize(val, &cfg->rrset_cache_size);
} else if(strcmp(opt, "rrset-cache-slabs:") == 0) {

View File

@ -114,6 +114,8 @@ struct config_file {
/** automatic interface for incoming messages. Uses ipv6 remapping,
* and recvmsg/sendmsg ancillary data to detect interfaces, boolean */
int if_automatic;
/** SO_RCVBUF size to set on port 53 UDP socket */
size_t socket_rcvbuf;
/** number of interfaces to open. If 0 default all interfaces. */
int num_ifs;

File diff suppressed because it is too large Load Diff

View File

@ -142,6 +142,7 @@ do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) }
interface{COLON} { YDVAR(1, VAR_INTERFACE) }
outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) }
interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) }
so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) }
chroot{COLON} { YDVAR(1, VAR_CHROOT) }
username{COLON} { YDVAR(1, VAR_USERNAME) }
directory{COLON} { YDVAR(1, VAR_DIRECTORY) }

File diff suppressed because it is too large Load Diff

View File

@ -148,7 +148,8 @@
VAR_AUTO_TRUST_ANCHOR_FILE = 364,
VAR_KEEP_MISSING = 365,
VAR_ADD_HOLDDOWN = 366,
VAR_DEL_HOLDDOWN = 367
VAR_DEL_HOLDDOWN = 367,
VAR_SO_RCVBUF = 368
};
#endif
/* Tokens. */
@ -262,6 +263,7 @@
#define VAR_KEEP_MISSING 365
#define VAR_ADD_HOLDDOWN 366
#define VAR_DEL_HOLDDOWN 367
#define VAR_SO_RCVBUF 368
@ -278,7 +280,7 @@ typedef union YYSTYPE
/* Line 1676 of yacc.c */
#line 282 "util/configparser.h"
#line 284 "util/configparser.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */

View File

@ -100,7 +100,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_DOMAIN_INSECURE VAR_PYTHON VAR_PYTHON_SCRIPT VAR_VAL_SIG_SKEW_MIN
%token VAR_VAL_SIG_SKEW_MAX VAR_CACHE_MIN_TTL VAR_VAL_LOG_LEVEL
%token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN
%token VAR_DEL_HOLDDOWN
%token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -152,7 +152,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_domain_insecure | server_val_sig_skew_min |
server_val_sig_skew_max | server_cache_min_ttl | server_val_log_level |
server_auto_trust_anchor_file | server_add_holddown |
server_del_holddown | server_keep_missing
server_del_holddown | server_keep_missing | server_so_rcvbuf
;
stubstart: VAR_STUB_ZONE
{
@ -519,6 +519,14 @@ server_version: VAR_VERSION STRING_ARG
cfg_parser->cfg->version = $2;
}
;
server_so_rcvbuf: VAR_SO_RCVBUF STRING_ARG
{
OUTYY(("P(server_so_rcvbuf:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->socket_rcvbuf))
yyerror("buffer size expected");
free($2);
}
;
server_msg_buffer_size: VAR_MSG_BUFFER_SIZE STRING_ARG
{
OUTYY(("P(server_msg_buffer_size:%s)\n", $2));