(int)cfg->incoming_num_tcp:0));
size_t ifs = (size_t)(cfg->num_ifs==0?1:cfg->num_ifs);
size_t listen_num = list*ifs;
- size_t out_ifs = (size_t)(cfg->num_out_ifs==0?1:cfg->num_out_ifs);
- size_t outnum = cfg->outgoing_num_ports*out_ifs + cfg->outgoing_num_tcp;
+ size_t out_ifs = (size_t)(cfg->num_out_ifs==0?
+ ( (cfg->do_ip4?1:0) + (cfg->do_ip6?1:0) ) :cfg->num_out_ifs);
+ size_t outudpnum = cfg->outgoing_num_ports*out_ifs;
+ size_t outtcpnum = cfg->outgoing_num_tcp;
size_t misc = 4; /* logfile, pidfile, stdout... */
- size_t perthread = listen_num + outnum + 2/*cmdpipe*/ + 2/*libevent*/
- + misc;
+ size_t perthread_noudp = listen_num + outtcpnum +
+ 2/*cmdpipe*/ + 2/*libevent*/ + misc;
+ size_t perthread = perthread_noudp + outudpnum;
+
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
int numthread = 1; /* it forks */
#else
int numthread = cfg->num_threads;
#endif
size_t total = numthread * perthread + misc;
+ size_t avail;
struct rlimit rlim;
if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
log_warn("getrlimit: %s", strerror(errno));
if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
return;
if((size_t)rlim.rlim_cur < total) {
- log_err("Not enough sockets available. Increase "
- "ulimit(open files).");
- log_err("or decrease number of threads, outgoing num ports, "
- "outgoing num tcp or number of interfaces");
- log_err("estimate %u fds high mark, %u available",
- (unsigned)total, (unsigned)rlim.rlim_cur);
- fatal_exit("Not enough file descriptors available");
+ avail = (size_t)rlim.rlim_cur;
+ rlim.rlim_cur = (rlim_t)(total + 10);
+ rlim.rlim_max = (rlim_t)(total + 10);
+ if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ log_warn("setrlimit: %s", strerror(errno));
+ log_warn("cannot increase max open fds from %u to %u",
+ (unsigned)avail, (unsigned)total+10);
+ cfg->outgoing_num_ports = (int)((avail
+ - numthread*perthread_noudp
+ - 10 /* safety margin */)
+ /(numthread*out_ifs));
+ log_warn("continuing with less udp ports: %u",
+ cfg->outgoing_num_ports);
+ log_warn("increase ulimit or decrease threads, ports in config to remove this warning");
+ return;
+ }
+ log_warn("increased limit(open files) from %u to %u",
+ (unsigned)avail, (unsigned)total+10);
}
}
+8 April 2008: Wouter
+ - unbound tries to set the ulimit fds when started as server.
+ if that does not work, it will scale back its requirements.
+
27 March 2008: Wouter
- documented /dev/random symlink from chrootdir as FAQ entry.
# port range. A larger port range gives more resistance to certain
# spoof attacks, as it gets harder to guess which port is used.
# But also takes more system resources (for open sockets).
- # outgoing-range: 16
+ # outgoing-range: 256
# number of outgoing simultaneous tcp buffers to hold per thread.
# outgoing-num-tcp: 10
.TP
.B outgoing\-range: \fI<number>
Number of ports to open. This number is opened per thread for every outgoing
-query interface. Must be at least 1. Default is 16.
+query interface. Must be at least 1. Default is 256.
Larger numbers give more protection against spoofing attempts, but need
extra resources from the operating system.
.TP
}
int
-create_udp_sock(struct addrinfo *addr, int v6only)
+create_udp_sock(struct addrinfo *addr, int v6only, int* inuse)
{
int s;
# if defined(IPV6_USE_MIN_MTU)
verbose_print_addr(addr);
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
log_err("can't create socket: %s", strerror(errno));
+ *inuse = 0;
return -1;
}
if(addr->ai_family == AF_INET6) {
&val, (socklen_t)sizeof(val)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s", strerror(errno));
+ *inuse = 0;
return -1;
}
}
&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
"...) failed: %s", strerror(errno));
+ *inuse = 0;
return -1;
}
# endif
}
if(bind(s, (struct sockaddr*)addr->ai_addr, addr->ai_addrlen) != 0) {
- log_err("can't bind socket: %s", strerror(errno));
+#ifdef EADDRINUSE
+ *inuse = (errno == EADDRINUSE);
+ if(errno != EADDRINUSE)
+#endif
+ log_err("can't bind socket: %s", strerror(errno));
return -1;
}
- if(!fd_set_nonblock(s))
+ if(!fd_set_nonblock(s)) {
+ *inuse = 0;
return -1;
+ }
return s;
}
struct addrinfo *hints, int v6only)
{
struct addrinfo *res = NULL;
- int r, s;
+ int r, s, inuse;
hints->ai_socktype = stype;
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
log_err("node %s:%s getaddrinfo: %s %s",
r==EAI_SYSTEM?(char*)strerror(errno):"");
return -1;
}
- if(stype == SOCK_DGRAM)
- s = create_udp_sock(res, v6only);
- else s = create_tcp_accept_sock(res, v6only);
+ if(stype == SOCK_DGRAM) {
+ s = create_udp_sock(res, v6only, &inuse);
+ if(s == -1 && inuse) {
+ log_err("bind: address already in use");
+ }
+ } else s = create_tcp_accept_sock(res, v6only);
freeaddrinfo(res);
return s;
}
* @param addr: address info ready to make socket.
* @param v6only: if enabled, IP6 sockets get IP6ONLY option set.
* if enabled with value 2 IP6ONLY option is disabled.
+ * @param inuse: on error, this is set true if the port was in use.
* @return: the socket. -1 on error.
*/
-int create_udp_sock(struct addrinfo* addr, int v6only);
+int create_udp_sock(struct addrinfo* addr, int v6only, int* inuse);
#endif /* LISTEN_DNSPORT_H */
* @param ifname: on which interface to open the port.
* @param hints: hints on family and passiveness preset.
* @param porthint: if not -1, it gives the port to base range on.
+ * @param inuse: on error, true if the port was in use.
* @return: file descriptor
*/
static int
-open_udp_port_range(const char* ifname, struct addrinfo* hints, int porthint)
+open_udp_port_range(const char* ifname, struct addrinfo* hints, int porthint,
+ int* inuse)
{
struct addrinfo *res = NULL;
int r, s;
r==EAI_SYSTEM?(char*)strerror(errno):"");
return -1;
}
- s = create_udp_sock(res, 1);
+ s = create_udp_sock(res, 1, inuse);
freeaddrinfo(res);
return s;
}
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
for(i=0; i<num_ports; i++) {
- int fd = open_udp_port_range(ifname, &hints, porthint);
- if(porthint != -1)
- porthint++;
- if(fd == -1)
- continue;
+ int fd = -1;
+ int inuse = 1;
+ while(fd == -1 && inuse) {
+ inuse = 0;
+ fd = open_udp_port_range(ifname, &hints,
+ porthint, &inuse);
+ if(fd == -1 && porthint != -1 && inuse)
+ verbose(VERB_DETAIL, "%sport %d already in use, skipped",
+ (do_ip6?"IP6 ":""), porthint);
+ if(porthint != -1) {
+ porthint++;
+ if(porthint > 65535) {
+ log_err("ports maxed. cannot open ports");
+ return done;
+ }
+ }
+ }
coms[done] = comm_point_create_udp(outnet->base, fd,
outnet->udp_buff, outnet_udp_cb, outnet);
if(coms[done])
cfg->do_tcp = 1;
cfg->use_syslog = 1;
cfg->outgoing_base_port = cfg->port + 2000;
- cfg->outgoing_num_ports = 16;
+ cfg->outgoing_num_ports = 256;
cfg->outgoing_num_tcp = 10;
cfg->incoming_num_tcp = 10;
cfg->msg_buffer_size = 65552; /* 64 k + a small margin */
free(cfg->chrootdir);
cfg->chrootdir = NULL;
cfg->verbosity = 0;
+ cfg->outgoing_num_ports = 16; /* in library use, this is 'reasonable'
+ and probably within the ulimit(maxfds) of the user */
cfg->outgoing_num_tcp = 2;
cfg->msg_cache_size = 1024*1024;
cfg->msg_cache_slabs = 1;