::arg().set("smtpredirector","Our smtpredir MX host")="a.misconfigured.powerdns.smtp.server";
::arg().set("local-address","Local IP addresses to which we bind")="0.0.0.0";
::arg().set("local-ipv6","Local IP address to which we bind")="";
+ ::arg().setSwitch("reuseport","Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket")="";
::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
::arg().set("overload-queue-length","Maximum queuelength moving to packetcache only")="0";
// If we have SO_REUSEPORT then create a new port for all receiver threads
// other than the first one.
- if( number > 0 && NS->canReusePort() )
- NS = new UDPNameserver();
+ if( number > 0 && NS->canReusePort() ) {
+ L<<Logger::Notice<<"Starting new listen thread on the same IPs/ports using SO_REUSEPORT"<<endl;
+ NS = new UDPNameserver( true );
+ }
for(;;) {
if (skipfirst)
<listitem><para>
If this many packets are waiting for database attention, answer any new questions strictly from the packet cache.
</para></listitem></varlistentry>
+ <varlistentry><term>reuseport=[yes|no]</term>
+ <listitem><para>
+ On Linux 3.9 and some BSD kernels the SO_REUSEPORT option allows each
+ receiver-thread to open a new socket on the same port which allows
+ for much higher performance on multi-core boxes. Setting this option
+ will enable use of SO_REUSEPORT when available and seamlessly fall
+ back to a single socket when it is not available. A side-effect is
+ that you can start multiple servers on the same IP/port combination
+ which may or may not be a good idea. You could use this to enable
+ transparent restarts, but it may also mask configuration issues and
+ for this reason it is disabled by default.
+ </para></listitem></varlistentry>
<varlistentry>
<term>server-id</term>
<listitem>
#endif
vector<ComboAddress> g_localaddresses; // not static, our unit tests need to poke this
-pthread_mutex_t localaddresses_lock=PTHREAD_MUTEX_INITIALIZER;
void UDPNameserver::bindIPv4()
{
setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one));
#ifdef SO_REUSEPORT
- if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
- d_can_reuseport = false;
+ if( d_can_reuseport )
+ if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
+ d_can_reuseport = false;
#endif
locala=ComboAddress(localname, ::arg().asNum("local-port"));
if(locala.sin4.sin_family != AF_INET)
throw PDNSException("Attempting to bind IPv4 socket to IPv6 address");
- pthread_mutex_lock(&localaddresses_lock);
- g_localaddresses.push_back(locala);
- pthread_mutex_unlock(&localaddresses_lock);
+ if( !d_additional_socket )
+ g_localaddresses.push_back(locala);
if(::bind(s, (sockaddr*)&locala, locala.getSocklen()) < 0) {
L<<Logger::Error<<"binding UDP socket to '"+locala.toStringWithPort()+": "<<strerror(errno)<<endl;
}
#ifdef SO_REUSEPORT
- if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
- d_can_reuseport = false;
+ if( d_can_reuseport )
+ if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
+ d_can_reuseport = false;
#endif
- g_localaddresses.push_back(locala);
+ if( !d_additional_socket )
+ g_localaddresses.push_back(locala);
if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) {
L<<Logger::Error<<"binding to UDP ipv6 socket: "<<strerror(errno)<<endl;
throw PDNSException("Unable to bind to UDP ipv6 socket");
#endif
}
-UDPNameserver::UDPNameserver()
+UDPNameserver::UDPNameserver( bool additional_socket )
{
#ifdef SO_REUSEPORT
- d_can_reuseport = true;
+ d_can_reuseport = ::arg().mustDo("reuseport");
#endif
+ // Are we the main socket (false) or a rebinding using SO_REUSEPORT ?
+ d_additional_socket = additional_socket;
if(!::arg()["local-address"].empty())
bindIPv4();