summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/nsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/nsd/nsd.c')
-rw-r--r--usr.sbin/nsd/nsd.c365
1 files changed, 362 insertions, 3 deletions
diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c
index e65c8f9dc54..46f5fb18c48 100644
--- a/usr.sbin/nsd/nsd.c
+++ b/usr.sbin/nsd/nsd.c
@@ -41,6 +41,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <ifaddrs.h>
#include "nsd.h"
#include "options.h"
@@ -130,7 +131,9 @@ copyaddrinfo(struct nsd_addrinfo *dest, struct addrinfo *src)
}
static void
-setup_socket(struct nsd_socket *sock, const char *node, const char *port, struct addrinfo *hints)
+setup_socket(
+ struct nsd_socket *sock, const char *node, const char *port,
+ struct addrinfo *hints)
{
int ret;
char *sep = NULL;
@@ -139,6 +142,7 @@ setup_socket(struct nsd_socket *sock, const char *node, const char *port, struct
char service_buf[6 + 1 /* '\0' */]; /* 65535 */
struct addrinfo *addr = NULL;
+ sock->fib = -1;
if(node) {
host = host_buf;
sep = strchr(node, '@');
@@ -171,6 +175,59 @@ setup_socket(struct nsd_socket *sock, const char *node, const char *port, struct
}
static void
+figure_socket_servers(
+ struct nsd_socket *sock, struct ip_address_option *ip)
+{
+ int i;
+ struct range_option *server;
+
+ sock->servers = xalloc_zero(nsd_bitset_size(nsd.child_count));
+ region_add_cleanup(nsd.region, free, sock->servers);
+ nsd_bitset_init(sock->servers, nsd.child_count);
+
+ if(!ip || !ip->servers) {
+ /* every server must listen on this socket */
+ for(i = 0; i < (int)nsd.child_count; i++) {
+ nsd_bitset_set(sock->servers, i);
+ }
+ return;
+ }
+
+ /* only specific servers must listen on this socket */
+ for(server = ip->servers; server; server = server->next) {
+ if(server->first == server->last) {
+ if(server->first <= 0) {
+ error("server %d specified for ip-address %s "
+ "is invalid; server ranges are 1-based",
+ server->first, ip->address);
+ } else if(server->last > (int)nsd.child_count) {
+ error("server %d specified for ip-address %s "
+ "exceeds number of servers configured "
+ "in server-count",
+ server->first, ip->address);
+ }
+ } else {
+ /* parse_range must ensure range itself is valid */
+ assert(server->first < server->last);
+ if(server->first <= 0) {
+ error("server range %d-%d specified for "
+ "ip-address %s is invalid; server "
+ "ranges are 1-based",
+ server->first, server->last, ip->address);
+ } else if(server->last > (int)nsd.child_count) {
+ error("server range %d-%d specified for "
+ "ip-address %s exceeds number of servers "
+ "configured in server-count",
+ server->first, server->last, ip->address);
+ }
+ }
+ for(i = server->first - 1; i < server->last; i++) {
+ nsd_bitset_set(sock->servers, i);
+ }
+ }
+}
+
+static void
figure_default_sockets(
struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs,
const char *udp_port, const char *tcp_port,
@@ -223,9 +280,13 @@ figure_default_sockets(
(r = getaddrinfo(NULL, tcp_port, &ai[1], &addrs[1])) == 0)
{
(*udp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
+ (*udp)[i].fib = -1;
copyaddrinfo(&(*udp)[i].addr, addrs[0]);
+ figure_socket_servers(&(*udp)[i], NULL);
(*tcp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
+ (*tcp)[i].fib = -1;
copyaddrinfo(&(*tcp)[i].addr, addrs[1]);
+ figure_socket_servers(&(*tcp)[i], NULL);
i++;
} else {
log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s",
@@ -245,7 +306,63 @@ figure_default_sockets(
*ifs = i + 1;
setup_socket(&(*udp)[i], NULL, udp_port, &ai[0]);
+ figure_socket_servers(&(*udp)[i], NULL);
setup_socket(&(*tcp)[i], NULL, tcp_port, &ai[1]);
+ figure_socket_servers(&(*tcp)[i], NULL);
+}
+
+static int
+find_device(
+ struct nsd_socket *sock,
+ const struct ifaddrs *ifa)
+{
+ for(; ifa != NULL; ifa = ifa->ifa_next) {
+ if((ifa->ifa_addr == NULL) ||
+ (ifa->ifa_addr->sa_family != sock->addr.ai_family) ||
+ ((ifa->ifa_flags & IFF_UP) == 0 ||
+ (ifa->ifa_flags & IFF_LOOPBACK) != 0 ||
+ (ifa->ifa_flags & IFF_RUNNING) == 0))
+ {
+ continue;
+ }
+
+#ifdef INET6
+ if(ifa->ifa_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa1, *sa2;
+ size_t sz = sizeof(struct in6_addr);
+ sa1 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ sa2 = (struct sockaddr_in6 *)&sock->addr.ai_addr;
+ if(memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sz) == 0) {
+ break;
+ }
+ } else
+#endif
+ if(ifa->ifa_addr->sa_family == AF_INET) {
+ struct sockaddr_in *sa1, *sa2;
+ sa1 = (struct sockaddr_in *)ifa->ifa_addr;
+ sa2 = (struct sockaddr_in *)&sock->addr.ai_addr;
+ if(sa1->sin_addr.s_addr == sa2->sin_addr.s_addr) {
+ break;
+ }
+ }
+ }
+
+ if(ifa != NULL) {
+ char *colon;
+ size_t len;
+
+ if((colon = strchr(ifa->ifa_name, ':')) != NULL) {
+ len = (size_t)((uintptr_t)colon - (uintptr_t)ifa->ifa_name);
+ } else {
+ len = strlen(ifa->ifa_name);
+ }
+ if (len < sizeof(sock->device)) {
+ strlcpy(sock->device, ifa->ifa_name, len);
+ return 1;
+ }
+ }
+
+ return 0;
}
static void
@@ -258,15 +375,23 @@ figure_sockets(
size_t i = 0;
struct addrinfo ai = *hints;
struct ip_address_option *ip;
+ struct ifaddrs *ifa = NULL;
+ int bind_device = 0;
if(!ips) {
- figure_default_sockets(udp, tcp, ifs, udp_port, tcp_port, hints);
+ figure_default_sockets(
+ udp, tcp, ifs, udp_port, tcp_port, hints);
return;
}
*ifs = 0;
for(ip = ips; ip; ip = ip->next) {
(*ifs)++;
+ bind_device |= (ip->dev != 0);
+ }
+
+ if(bind_device && getifaddrs(&ifa) == -1) {
+ error("getifaddrs failed: %s", strerror(errno));
}
*udp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket));
@@ -278,12 +403,152 @@ figure_sockets(
for(ip = ips, i = 0; ip; ip = ip->next, i++) {
ai.ai_socktype = SOCK_DGRAM;
setup_socket(&(*udp)[i], ip->address, udp_port, &ai);
+ figure_socket_servers(&(*udp)[i], ip);
ai.ai_socktype = SOCK_STREAM;
setup_socket(&(*tcp)[i], ip->address, tcp_port, &ai);
+ figure_socket_servers(&(*tcp)[i], ip);
+ if(ip->fib != -1) {
+ (*udp)[i].fib = ip->fib;
+ (*tcp)[i].fib = ip->fib;
+ }
+ if(ip->dev != 0) {
+ (*udp)[i].flags |= NSD_BIND_DEVICE;
+ (*tcp)[i].flags |= NSD_BIND_DEVICE;
+ if(ifa != NULL && (find_device(&(*udp)[i], ifa) == 0 ||
+ find_device(&(*tcp)[i], ifa) == 0))
+ {
+ error("cannot find device for ip-address %s",
+ ip->address);
+ }
+ }
}
assert(i == *ifs);
+
+ if(ifa != NULL) {
+ freeifaddrs(ifa);
+ }
+}
+
+/* print server affinity for given socket. "*" if socket has no affinity with
+ any specific server, "x-y" if socket has affinity with more than two
+ consecutively numbered servers, "x" if socket has affinity with a specific
+ server number, which is not necessarily just one server. e.g. "1 3" is
+ printed if socket has affinity with servers number one and three, but not
+ server number two. */
+static ssize_t
+print_socket_servers(struct nsd_socket *sock, char *buf, size_t bufsz)
+{
+ int i, x, y, z, n = (int)(sock->servers->size);
+ char *sep = "";
+ size_t off, tot;
+ ssize_t cnt = 0;
+
+ assert(bufsz != 0);
+
+ off = tot = 0;
+ x = y = z = -1;
+ for (i = 0; i <= n; i++) {
+ if (i == n || !nsd_bitset_isset(sock->servers, i)) {
+ cnt = 0;
+ if (i == n && x == -1) {
+ assert(y == -1);
+ assert(z == -1);
+ cnt = snprintf(buf, bufsz, "-");
+ } else if (y > z) {
+ assert(x > z);
+ if (x == 0 && y == (n - 1)) {
+ assert(z == -1);
+ cnt = snprintf(buf+off, bufsz-off,
+ "*");
+ } else if (x == y) {
+ cnt = snprintf(buf+off, bufsz-off,
+ "%s%d", sep, x+1);
+ } else if (x == (y - 1)) {
+ cnt = snprintf(buf+off, bufsz-off,
+ "%s%d %d", sep, x+1, y+1);
+ } else {
+ assert(y > (x + 1));
+ cnt = snprintf(buf+off, bufsz-off,
+ "%s%d-%d", sep, x+1, y+1);
+ }
+ }
+ z = i;
+ if (cnt > 0) {
+ tot += (size_t)cnt;
+ off = (tot < bufsz) ? tot : bufsz - 1;
+ sep = " ";
+ } else if (cnt < 0) {
+ return -1;
+ }
+ } else if (x <= z) {
+ x = y = i;
+ } else {
+ assert(x > z);
+ y = i;
+ }
+ }
+
+ return tot;
+}
+
+static void
+print_sockets(
+ struct nsd_socket *udp, struct nsd_socket *tcp, size_t ifs)
+{
+ char sockbuf[INET6_ADDRSTRLEN + 6 + 1];
+ char *serverbuf;
+ size_t i, serverbufsz, servercnt;
+ const char *fmt = "listen on ip-address %s (%s) with server(s): %s";
+ struct nsd_bitset *servers;
+
+ if(ifs == 0) {
+ return;
+ }
+
+ assert(udp != NULL);
+ assert(tcp != NULL);
+
+ servercnt = udp[0].servers->size;
+ serverbufsz = (((servercnt / 10) * servercnt) + servercnt) + 1;
+ serverbuf = xalloc(serverbufsz);
+
+ /* warn user of unused servers */
+ servers = xalloc(nsd_bitset_size(servercnt));
+ nsd_bitset_init(servers, (size_t)servercnt);
+
+ for(i = 0; i < ifs; i++) {
+ assert(udp[i].servers->size == servercnt);
+ addrport2str(&udp[i].addr.ai_addr, sockbuf, sizeof(sockbuf));
+ print_socket_servers(&udp[i], serverbuf, serverbufsz);
+ nsd_bitset_or(servers, servers, udp[i].servers);
+ log_msg(LOG_NOTICE, fmt, sockbuf, "udp", serverbuf);
+ assert(tcp[i].servers->size == servercnt);
+ addrport2str(&tcp[i].addr.ai_addr, sockbuf, sizeof(sockbuf));
+ print_socket_servers(&tcp[i], serverbuf, serverbufsz);
+ nsd_bitset_or(servers, servers, tcp[i].servers);
+ log_msg(LOG_NOTICE, fmt, sockbuf, "tcp", serverbuf);
+ }
+
+
+ /* warn user of unused servers */
+ for(i = 0; i < servercnt; i++) {
+ if(!nsd_bitset_isset(servers, i)) {
+ log_msg(LOG_WARNING, "server %zu will not listen on "
+ "any specified ip-address", i+1);
+ }
+ }
+ free(serverbuf);
+ free(servers);
+}
+
+#ifdef HAVE_CPUSET_T
+static void free_cpuset(void *ptr)
+{
+ cpuset_t *set = (cpuset_t *)ptr;
+ cpuset_destroy(set);
}
+#endif
/*
* Fetch the nsd parent process id from the nsd pidfile
@@ -764,6 +1029,7 @@ main(int argc, char *argv[])
if(nsd.child_count == 0) {
nsd.child_count = nsd.options->server_count;
}
+
#ifdef SO_REUSEPORT
if(nsd.options->reuseport && nsd.child_count > 1) {
nsd.reuseport = nsd.child_count;
@@ -845,6 +1111,54 @@ main(int argc, char *argv[])
edns_init_nsid(&nsd.edns_ipv6, nsd.nsid_len);
#endif /* defined(INET6) */
+#ifdef HAVE_CPUSET_T
+ nsd.use_cpu_affinity = (nsd.options->cpu_affinity != NULL);
+ if(nsd.use_cpu_affinity) {
+ int ncpus;
+ struct cpu_option* opt = nsd.options->cpu_affinity;
+
+ if((ncpus = number_of_cpus()) == -1) {
+ error("cannot retrieve number of cpus: %s",
+ strerror(errno));
+ }
+ nsd.cpuset = cpuset_create();
+ region_add_cleanup(nsd.region, free_cpuset, nsd.cpuset);
+ for(; opt; opt = opt->next) {
+ assert(opt->cpu >= 0);
+ if(opt->cpu >= ncpus) {
+ error("invalid cpu %d specified in "
+ "cpu-affinity", opt->cpu);
+ }
+ cpuset_set((cpuid_t)opt->cpu, nsd.cpuset);
+ }
+ }
+ if(nsd.use_cpu_affinity) {
+ int cpu;
+ struct cpu_map_option *opt
+ = nsd.options->service_cpu_affinity;
+
+ cpu = -1;
+ for(; opt && cpu == -1; opt = opt->next) {
+ if(opt->service == -1) {
+ cpu = opt->cpu;
+ assert(cpu >= 0);
+ }
+ }
+ nsd.xfrd_cpuset = cpuset_create();
+ region_add_cleanup(nsd.region, free_cpuset, nsd.xfrd_cpuset);
+ if(cpu == -1) {
+ cpuset_or(nsd.xfrd_cpuset,
+ nsd.cpuset);
+ } else {
+ if(!cpuset_isset(cpu, nsd.cpuset)) {
+ error("cpu %d specified in xfrd-cpu-affinity "
+ "is not specified in cpu-affinity", cpu);
+ }
+ cpuset_set((cpuid_t)cpu, nsd.xfrd_cpuset);
+ }
+ }
+#endif /* HAVE_CPUSET_T */
+
/* Number of child servers to fork. */
nsd.children = (struct nsd_child *) region_alloc_array(
nsd.region, nsd.child_count, sizeof(struct nsd_child));
@@ -858,9 +1172,43 @@ main(int argc, char *argv[])
nsd.children[i].need_to_send_QUIT = 0;
nsd.children[i].need_to_exit = 0;
nsd.children[i].has_exited = 0;
-#ifdef BIND8_STATS
+#ifdef BIND8_STATS
nsd.children[i].query_count = 0;
#endif
+
+#ifdef HAVE_CPUSET_T
+ if(nsd.use_cpu_affinity) {
+ int cpu, server;
+ struct cpu_map_option *opt
+ = nsd.options->service_cpu_affinity;
+
+ cpu = -1;
+ server = i+1;
+ for(; opt && cpu == -1; opt = opt->next) {
+ if(opt->service == server) {
+ cpu = opt->cpu;
+ assert(cpu >= 0);
+ }
+ }
+ nsd.children[i].cpuset = cpuset_create();
+ region_add_cleanup(nsd.region,
+ free_cpuset,
+ nsd.children[i].cpuset);
+ if(cpu == -1) {
+ cpuset_or(nsd.children[i].cpuset,
+ nsd.cpuset);
+ } else {
+ if(!cpuset_isset((cpuid_t)cpu, nsd.cpuset)) {
+ error("cpu %d specified in "
+ "server-%d-cpu-affinity is not "
+ "specified in cpu-affinity",
+ cpu, server);
+ }
+ cpuset_set(
+ (cpuid_t)cpu, nsd.children[i].cpuset);
+ }
+ }
+#endif /* HAVE_CPUSET_T */
}
nsd.this_child = NULL;
@@ -968,6 +1316,17 @@ main(int argc, char *argv[])
}
}
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("main");
+#endif
+#ifdef HAVE_CPUSET_T
+ if(nsd.use_cpu_affinity) {
+ set_cpu_affinity(nsd.cpuset);
+ }
+#endif
+
+ print_sockets(nsd.udp, nsd.tcp, nsd.ifs);
+
/* Setup the signal handling... */
action.sa_handler = sig_handler;
sigfillset(&action.sa_mask);