diff options
Diffstat (limited to 'usr.sbin/nsd/nsd.c')
-rw-r--r-- | usr.sbin/nsd/nsd.c | 365 |
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); |