]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add a 'source' option to select the source addr/interface
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 7 Jan 2016 11:55:14 +0000 (12:55 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 7 Jan 2016 12:00:47 +0000 (13:00 +0100)
It only supports one source address/interface per downstream server.
The more I tried to support more than one, the more I realized I was
in fact having grouping several DS into one, without the benefits
of separate stats and status checking. In particular, having several
sources adresses mean that we would get "random" failures if some
addresses are allowed on the backend and some others are not.
Simply adding the same backend several times with different source
addresses means that only the ones with faulty addresses will be
disabled.
Closes #3138.

12 files changed:
pdns/README-dnsdist.md
pdns/dnsdist-lua.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsproxy.cc
pdns/iputils.cc
pdns/iputils.hh
pdns/misc.cc
pdns/misc.hh
pdns/nameserver.cc
pdns/pdns_recursor.cc

index f34cfad3f87d312056d7f23ac3a5d50c448aa75f..e45ddc5098a29e3aed211760c155092f1975717a 100644 (file)
@@ -174,6 +174,26 @@ parameters to `newServer`:
 newServer({address="192.0.2.1", tcpRecvTimeout=10, tcpSendTimeout=10})
 ```
 
+Source address
+--------------
+
+In multi-homed setups, it can be useful to be able to select the source address or the outgoing
+interface used by `dnsdist` to contact a downstream server.
+This can be done by using the `source` parameter:
+```
+newServer({address="192.0.2.1", source="192.0.2.127"})
+newServer({address="192.0.2.1", source="eth1"})
+newServer({address="192.0.2.1", source="192.0.2.127@eth1"})
+```
+
+The supported values for `source` are:
+ * an IPv4 or IPv6 address, which must exist on the system
+ * an interface name
+ * an IPv4 or IPv6 address followed by '@' then an interface name
+
+Specifying the interface name is only supported on system having IP_PKTINFO.
+
+
 Configuration management
 ------------------------
 At startup, configuration is read from the command line and the
@@ -739,7 +759,7 @@ Here are all functions:
    * `errlog(string)`: log at level error
  * Server related:
    * `newServer("ip:port")`: instantiate a new downstream server with default settings
-   * `newServer({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", mustResolve=false, useClientSubnet=true})`:
+   * `newServer({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface"})`:
 instantiate a server with additional parameters
    * `showServers()`: output all servers
    * `getServer(n)`: returns server with index n 
index 4dff75d95e06f8e2d5dd7da1b31db700a707ddf5..30938867e05b35ad6d8437e369716f623179aa9e 100644 (file)
@@ -7,6 +7,7 @@
 #include <fstream>
 #include "dnswriter.hh"
 #include "lock.hh"
+#include <net/if.h>
 
 using std::thread;
 
@@ -139,6 +140,8 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
                        if(client) {
                          return std::make_shared<DownstreamState>(ComboAddress());
                        }
+                       ComboAddress sourceAddr;
+                       unsigned int sourceItf = 0;
                        if(auto address = boost::get<string>(&pvars)) {
                          std::shared_ptr<DownstreamState> ret;
                          try {
@@ -173,16 +176,63 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
                          return ret;
                        }
                        auto vars=boost::get<newserver_t>(pvars);
+
+                       if(vars.count("source")) {
+                         /* handle source in the following forms:
+                            - v4 address ("192.0.2.1")
+                            - v6 address ("2001:DB8::1")
+                            - interface name ("eth0")
+                            - v4 address and interface name ("192.0.2.1@eth0")
+                            - v6 address and interface name ("2001:DB8::1@eth0")
+                         */
+                         const string source = boost::get<string>(vars["source"]);
+                         bool parsed = false;
+                         std::string::size_type pos = source.find("@");
+                         if (pos == std::string::npos) {
+                           /* no '@', try to parse that as a valid v4/v6 address */
+                           try {
+                             sourceAddr = ComboAddress(source);
+                             parsed = true;
+                           }
+                           catch(...)
+                           {
+                           }
+                         }
+
+                         if (parsed == false)
+                         {
+                           /* try to parse as interface name, or v4/v6@itf */
+                           string itfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
+                           unsigned int itfIdx = if_nametoindex(itfName.c_str());
+
+                           if (itfIdx != 0) {
+                             if (pos == 0 || pos == std::string::npos) {
+                               /* "eth0" or "@eth0" */
+                               sourceItf = itfIdx;
+                             }
+                             else {
+                               /* "192.0.2.1@eth0" */
+                               sourceAddr = ComboAddress(source.substr(0, pos));
+                               sourceItf = itfIdx;
+                             }
+                           }
+                           else
+                           {
+                             warnlog("Dismissing source %s because '%s' is not a valid interface name", source, itfName);
+                           }
+                         }
+                       }
+
                        std::shared_ptr<DownstreamState> ret;
                        try {
-                         ret=std::make_shared<DownstreamState>(ComboAddress(boost::get<string>(vars["address"]), 53));
+                         ret=std::make_shared<DownstreamState>(ComboAddress(boost::get<string>(vars["address"]), 53), sourceAddr, sourceItf);
                        }
                        catch(std::exception& e) {
                          g_outputBuffer="Error creating new server: "+string(e.what());
                          errlog("Error creating new server with address %s: %s", boost::get<string>(vars["address"]), e.what());
                          return ret;
                        }
-                       
+
                        if(vars.count("qps")) {
                          int qps=std::stoi(boost::get<string>(vars["qps"]));
                          ret->qps=QPSLimiter(qps, qps);
index 3f14be24e924b74a0aaa7ea69390af8e1c61c899..e70599d6553deae81114361c82b5b05067fd0c09 100644 (file)
@@ -45,11 +45,15 @@ using std::atomic;
    Let's start naively.
 */
 
-static int setupTCPDownstream(const ComboAddress& remote)
+static int setupTCPDownstream(shared_ptr<DownstreamState> ds)
 {  
-  vinfolog("TCP connecting to downstream %s", remote.toStringWithPort());
-  int sock = SSocket(remote.sin4.sin_family, SOCK_STREAM, 0);
-  SConnect(sock, remote);
+  vinfolog("TCP connecting to downstream %s", ds->remote.toStringWithPort());
+  int sock = SSocket(ds->remote.sin4.sin_family, SOCK_STREAM, 0);
+  if (!IsAnyAddress(ds->sourceAddr)) {
+    SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+    SBind(sock, ds->sourceAddr);
+  }
+  SConnect(sock, ds->remote);
   setNonBlocking(sock);
   return sock;
 }
@@ -106,6 +110,20 @@ catch(...) {
   return false;
 }
 
+static bool sendNonBlockingMsgLen(int fd, uint16_t len, int timeout, ComboAddress& dest, ComboAddress& local, unsigned int localItf)
+try
+{
+  if (localItf == 0)
+    return putNonBlockingMsgLen(fd, len, timeout);
+
+  uint16_t raw = htons(len);
+  ssize_t ret = sendMsgWithTimeout(fd, (char*) &raw, sizeof raw, timeout, dest, local, localItf);
+  return ret == sizeof raw;
+}
+catch(...) {
+  return false;
+}
+
 TCPClientCollection g_tcpclientthreads;
 
 void* tcpClientThread(int pipefd)
@@ -297,7 +315,7 @@ void* tcpClientThread(int pipefd)
         }
 
        if(sockets.count(ds->remote) == 0) {
-         dsock=sockets[ds->remote]=setupTCPDownstream(ds->remote);
+         dsock=sockets[ds->remote]=setupTCPDownstream(ds);
        }
        else
          dsock=sockets[ds->remote];
@@ -322,21 +340,26 @@ void* tcpClientThread(int pipefd)
           break;
         }
 
-        if(!putNonBlockingMsgLen(dsock, queryLen, ds->tcpSendTimeout)) {
+        if(!sendNonBlockingMsgLen(dsock, queryLen, ds->tcpSendTimeout, ds->remote, ds->sourceAddr, ds->sourceItf)) {
          vinfolog("Downstream connection to %s died on us, getting a new one!", ds->getName());
           close(dsock);
-          sockets[ds->remote]=dsock=setupTCPDownstream(ds->remote);
+          sockets[ds->remote]=dsock=setupTCPDownstream(ds);
           downstream_failures++;
           goto retry;
         }
 
         try {
-          writen2WithTimeout(dsock, query, queryLen, ds->tcpSendTimeout);
+          if (ds->sourceItf == 0) {
+            writen2WithTimeout(dsock, query, queryLen, ds->tcpSendTimeout);
+          }
+          else {
+            sendMsgWithTimeout(dsock, query, queryLen, ds->tcpSendTimeout, ds->remote, ds->sourceAddr, ds->sourceItf);
+          }
         }
         catch(const runtime_error& e) {
           vinfolog("Downstream connection to %s died on us, getting a new one!", ds->getName());
           close(dsock);
-          sockets[ds->remote]=dsock=setupTCPDownstream(ds->remote);
+          sockets[ds->remote]=dsock=setupTCPDownstream(ds);
           downstream_failures++;
           goto retry;
         }
@@ -344,7 +367,7 @@ void* tcpClientThread(int pipefd)
         if(!getNonBlockingMsgLen(dsock, &rlen, ds->tcpRecvTimeout)) {
          vinfolog("Downstream connection to %s died on us phase 2, getting a new one!", ds->getName());
           close(dsock);
-          sockets[ds->remote]=dsock=setupTCPDownstream(ds->remote);
+          sockets[ds->remote]=dsock=setupTCPDownstream(ds);
           downstream_failures++;
           goto retry;
         }
index c14538beebd60210c0dca87b5e6aef54c3ee75a7..5a064ffd6ac6007eab7e4b21c1ebe4991f1ecc36 100644 (file)
@@ -305,11 +305,13 @@ void* responderThread(std::shared_ptr<DownstreamState> state)
   return 0;
 }
 
-DownstreamState::DownstreamState(const ComboAddress& remote_): checkName("a.root-servers.net."), checkType(QType::A), mustResolve(false)
+DownstreamState::DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf_): remote(remote_), sourceAddr(sourceAddr_), sourceItf(sourceItf_)
 {
-  remote = remote_;
-  
   fd = SSocket(remote.sin4.sin_family, SOCK_DGRAM, 0);
+  if (!IsAnyAddress(sourceAddr)) {
+    SSetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 1);
+    SBind(fd, sourceAddr);
+  }
   SConnect(fd, remote);
   idStates.resize(g_maxOutstanding);
   sw.start();
@@ -481,6 +483,20 @@ int getEDNSZ(const char* packet, unsigned int len)
   return 0x100 * (*z) + *(z+1);
 }
 
+static ssize_t udpClientSendRequestToBackend(DownstreamState* ss, const int sd, const char* request, const size_t requestLen)
+{
+  if (ss->sourceItf == 0) {
+    return send(sd, request, requestLen, 0);
+  }
+
+  struct msghdr msgh;
+  struct iovec iov;
+  char cbuf[256];
+  fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), const_cast<char*>(request), requestLen, &ss->remote);
+  addCMsgSrcAddr(&msgh, cbuf, &ss->sourceAddr, ss->sourceItf);
+  return sendmsg(sd, &msgh, 0);
+}
+
 // listens to incoming queries, sends out to downstream servers, noting the intended return path 
 static void* udpClientThread(ClientState* cs)
 try
@@ -722,10 +738,10 @@ try
       }
 
       if (largerQuery.empty()) {
-        ret = send(ss->fd, query, len, 0);
+        ret = udpClientSendRequestToBackend(ss, ss->fd, query, len);
       }
       else {
-        ret = send(ss->fd, largerQuery.c_str(), largerQuery.size(), 0);
+        ret = udpClientSendRequestToBackend(ss, ss->fd, largerQuery.c_str(), largerQuery.size());
         largerQuery.clear();
       }
 
@@ -759,24 +775,30 @@ catch(...)
 }
 
 
-bool upCheck(const ComboAddress& remote, const DNSName& checkName, const QType& checkType, bool mustResolve)
+static bool upCheck(DownstreamState& ds)
 try
 {
   vector<uint8_t> packet;
-  DNSPacketWriter dpw(packet, checkName, checkType.getCode());
+  DNSPacketWriter dpw(packet, ds.checkName, ds.checkType.getCode());
   dnsheader * requestHeader = dpw.getHeader();
   requestHeader->rd=true;
 
-  Socket sock(remote.sin4.sin_family, SOCK_DGRAM);
+  Socket sock(ds.remote.sin4.sin_family, SOCK_DGRAM);
   sock.setNonBlocking();
-  sock.connect(remote);
-  sock.write((char*)&packet[0], packet.size());  
+  if (!IsAnyAddress(ds.sourceAddr)) {
+    sock.setReuseAddr();
+    sock.bind(ds.sourceAddr);
+  }
+  sock.connect(ds.remote);
+  ssize_t sent = udpClientSendRequestToBackend(&ds, sock.getHandle(), (char*)&packet[0], packet.size());
+  if (sent < 0)
+    return false;
+
   int ret=waitForRWData(sock.getHandle(), true, 1, 0);
   if(ret < 0 || !ret) // error, timeout, both are down!
     return false;
   string reply;
-  ComboAddress dest=remote;
-  sock.recvFrom(reply, dest);
+  sock.recvFrom(reply, ds.remote);
 
   const dnsheader * responseHeader = (const dnsheader *) reply.c_str();
 
@@ -789,7 +811,7 @@ try
     return false;
   if (responseHeader->rcode == RCode::ServFail)
     return false;
-  if (mustResolve && (responseHeader->rcode == RCode::NXDomain || responseHeader->rcode == RCode::Refused))
+  if (ds.mustResolve && (responseHeader->rcode == RCode::NXDomain || responseHeader->rcode == RCode::Refused))
     return false;
 
   // XXX fixme do bunch of checking here etc 
@@ -814,7 +836,7 @@ void* maintThread()
 
     for(auto& dss : g_dstates.getCopy()) { // this points to the actual shared_ptrs!
       if(dss->availability==DownstreamState::Availability::Auto) {
-       bool newState=upCheck(dss->remote, dss->checkName, dss->checkType, dss->mustResolve);
+       bool newState=upCheck(*dss);
        if(newState != dss->upStatus) {
          warnlog("Marking downstream %s as '%s'", dss->getNameWithAddr(), newState ? "up" : "down");
        }
@@ -1240,7 +1262,7 @@ try
 
   for(auto& dss : g_dstates.getCopy()) { // it is a copy, but the internal shared_ptrs are the real deal
     if(dss->availability==DownstreamState::Availability::Auto) {
-      bool newState=upCheck(dss->remote, dss->checkName, dss->checkType, dss->mustResolve);
+      bool newState=upCheck(*dss);
       warnlog("Marking downstream %s as '%s'", dss->getNameWithAddr(), newState ? "up" : "down");
       dss->upStatus = newState;
     }
index e6d13156d2736c44c25dacac1154166102c46a4e..59dd339c911b18eb69a91a1e25f111716dd93eed 100644 (file)
@@ -278,15 +278,17 @@ extern TCPClientCollection g_tcpclientthreads;
 
 struct DownstreamState
 {
-  DownstreamState(const ComboAddress& remote_);
+  DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf);
+  DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0) {}
 
   int fd;            
   std::thread tid;
   ComboAddress remote;
   QPSLimiter qps;
   vector<IDState> idStates;
-  DNSName checkName;
-  QType checkType;
+  ComboAddress sourceAddr;
+  DNSName checkName{"a.root-servers.net."};
+  QType checkType{QType::A};
   std::atomic<uint64_t> idOffset{0};
   std::atomic<uint64_t> sendErrors{0};
   std::atomic<uint64_t> outstanding{0};
@@ -305,11 +307,12 @@ struct DownstreamState
   int weight{1};
   int tcpRecvTimeout{30};
   int tcpSendTimeout{30};
+  unsigned int sourceItf{0};
   uint16_t retries{5};
   StopWatch sw;
   set<string> pools;
   enum class Availability { Up, Down, Auto} availability{Availability::Auto};
-  bool mustResolve;
+  bool mustResolve{false};
   bool upStatus{false};
   bool useECS{false};
   bool isUp() const
index 80710a3e4fee318d07d5d267de0c9df23c17e96a..d6464bb693466405c5b0b245cd0fa5f217a5b150 100644 (file)
@@ -278,7 +278,7 @@ void DNSProxy::mainloop(void)
         msgh.msg_namelen = i->second.remote.getSocklen();
 
         if(i->second.anyLocal) {
-          addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr());
+          addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr(), 0);
         }
         if(sendmsg(i->second.outsock, &msgh, 0) < 0)
           L<<Logger::Warning<<"dnsproxy.cc: Error sending reply with sendmsg (socket="<<i->second.outsock<<"): "<<strerror(errno)<<endl;
index 8a255e70d302cc00b287151d0ab7c6a8d37fae49..545a50d83afc4c24f7700f2b1ed5f7d8750968f5 100644 (file)
@@ -134,7 +134,7 @@ int sendfromto(int sock, const char* data, int len, int flags, const ComboAddres
   msgh.msg_namelen = to.getSocklen();
 
   if(from.sin4.sin_family) {
-    addCMsgSrcAddr(&msgh, cbuf, &from);
+    addCMsgSrcAddr(&msgh, cbuf, &from, 0);
   }
   else {
     msgh.msg_control=NULL;
@@ -190,4 +190,46 @@ void ComboAddress::truncate(unsigned int bits)
   *place &= (~((1<<bitsleft)-1));
 }
 
+ssize_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int timeout, ComboAddress& dest, const ComboAddress& local, unsigned int localItf)
+{
+  struct msghdr msgh;
+  struct iovec iov;
+  char cbuf[256];
+  bool firstTry = true;
+  fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), const_cast<char*>(buffer), len, &dest);
+  addCMsgSrcAddr(&msgh, cbuf, &local, localItf);
+
+  do {
+    ssize_t written = sendmsg(fd, &msgh, 0);
+
+    if (written > 0)
+      return written;
+
+    if (errno == EAGAIN) {
+      if (firstTry) {
+        int res = waitForRWData(fd, false, timeout, 0);
+        if (res > 0) {
+          /* there is room available */
+          firstTry = false;
+        }
+        else if (res == 0) {
+          throw runtime_error("Timeout while waiting to write data");
+        } else {
+          throw runtime_error("Error while waiting for room to write data");
+        }
+      }
+      else {
+        throw runtime_error("Timeout while waiting to write data");
+      }
+    }
+    else {
+      unixDie("failed in write2WithTimeout");
+    }
+  }
+  while (firstTry);
+
+  return 0;
+}
+
 template class NetmaskTree<bool>;
+
index e2b9741f69fd4adfb0ea2c10c4739f2b02a7392c..7c133e2939067aeee006b943aeb29aa301fc2fe9 100644 (file)
@@ -811,6 +811,8 @@ bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination);
 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv);
 void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr);
 int sendfromto(int sock, const char* data, int len, int flags, const ComboAddress& from, const ComboAddress& to);
+ssize_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int timeout, ComboAddress& dest, const ComboAddress& local, unsigned int localItf);
+
 #endif
 
 extern template class NetmaskTree<bool>;
index 5ef1138b44dc390153e4e9a6e2c8ce119fff9c68..b60eb6dc2d4a8cbe10765b2aaf3b988f490858c6 100644 (file)
@@ -868,7 +868,7 @@ Regex::Regex(const string &expr)
     throw PDNSException("Regular expression did not compile");
 }
 
-void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source)
+void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source, int itfIndex)
 {
   struct cmsghdr *cmsg = NULL;
 
@@ -886,6 +886,7 @@ void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* sour
     pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
     memset(pkt, 0, sizeof(*pkt));
     pkt->ipi6_addr = source->sin6.sin6_addr;
+    pkt->ipi6_ifindex = itfIndex;
     msgh->msg_controllen = cmsg->cmsg_len; // makes valgrind happy and is slightly better style
   }
   else {
@@ -903,6 +904,7 @@ void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* sour
     pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
     memset(pkt, 0, sizeof(*pkt));
     pkt->ipi_spec_dst = source->sin4.sin_addr;
+    pkt->ipi_ifindex = itfIndex;
     msgh->msg_controllen = cmsg->cmsg_len;
 #endif
 #ifdef IP_SENDSRCADDR
index 4ba73f92a9f9359dcf8485d4d1e840290696e342..51cae92f9f416cd25bc8447e9cdb11af5069947c 100644 (file)
@@ -587,7 +587,8 @@ private:
 };
 
 union ComboAddress;
-void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source);
+/* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */
+void addCMsgSrcAddr(struct msghdr* msgh, void* cmsgbuf, const ComboAddress* source, int itfIndex);
 
 unsigned int getFilenumLimit(bool hardOrSoft=0);
 void setFilenumLimit(unsigned int lim);
index c17406942e5df68ab8555872bfcb719c19b20e18..57c0056250b8aed33af2205bdbfdb4b4f2c36259 100644 (file)
@@ -292,7 +292,7 @@ void UDPNameserver::send(DNSPacket *p)
   fillMSGHdr(&msgh, &iov, cbuf, 0, (char*)buffer.c_str(), buffer.length(), &p->d_remote);
 
   if(p->d_anyLocal) {
-    addCMsgSrcAddr(&msgh, cbuf, p->d_anyLocal.get_ptr());
+    addCMsgSrcAddr(&msgh, cbuf, p->d_anyLocal.get_ptr(), 0);
   }
   else {
     msgh.msg_control=NULL;
index 42f2e239cad410bbf32b3f47c88e223971fc99e6..c6074517f64f5a47c61c942a450ea2dd6e422cad 100644 (file)
@@ -873,7 +873,7 @@ void startDoResolve(void *p)
       char cbuf[256];
       fillMSGHdr(&msgh, &iov, cbuf, 0, (char*)&*packet.begin(), packet.size(), &dc->d_remote);
       if(dc->d_local.sin4.sin_family)
-       addCMsgSrcAddr(&msgh, cbuf, &dc->d_local);
+       addCMsgSrcAddr(&msgh, cbuf, &dc->d_local, 0);
       else
         msgh.msg_control=NULL;
       sendmsg(dc->d_socket, &msgh, 0);
@@ -1174,7 +1174,7 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
       char cbuf[256];
       fillMSGHdr(&msgh, &iov, cbuf, 0, (char*)response.c_str(), response.length(), const_cast<ComboAddress*>(&fromaddr));
       if(destaddr.sin4.sin_family) {
-       addCMsgSrcAddr(&msgh, cbuf, &destaddr);
+       addCMsgSrcAddr(&msgh, cbuf, &destaddr, 0);
       }
       else {
         msgh.msg_control=NULL;