]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Implement FFI LB policies
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 11 Dec 2019 17:12:32 +0000 (18:12 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 11 Feb 2020 10:49:57 +0000 (11:49 +0100)
14 files changed:
pdns/dnsdist-carbon.cc
pdns/dnsdist-console.cc
pdns/dnsdist-lua-bindings.cc
pdns/dnsdist-lua-inspection.cc
pdns/dnsdist-lua.cc
pdns/dnsdist-snmp.cc
pdns/dnsdist-web.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/dnsdist-lua-ffi-interface.h
pdns/dnsdistdist/dnsdist-lua-ffi.cc
pdns/dnsdistdist/dnsdist-lua-ffi.hh
pdns/dnsdistdist/docs/advanced/tuning.rst
pdns/dnsdistdist/docs/guides/serverselection.rst

index 72473ca00d286dddfc8f36ca03d0d32ac090cb1d..26702dcb65db8e6320e31fc157dec8740f515920 100644 (file)
@@ -87,7 +87,7 @@ try
         }
         auto states = g_dstates.getLocal();
         for(const auto& state : *states) {
-          string serverName = state->name.empty() ? (state->remote.toString() + ":" + std::to_string(state->remote.getPort())) : state->getName();
+          string serverName = state->getName().empty() ? (state->remote.toString() + ":" + std::to_string(state->remote.getPort())) : state->getName();
           boost::replace_all(serverName, ".", "_");
           const string base = namespace_name + "." + hostname + "." + instance_name + ".servers." + serverName + ".";
           str<<base<<"queries" << ' ' << state->queries.load() << " " << now << "\r\n";
index deec2fc98d65f9d45db1fdebeca3b95ff1386433..61193e7432e842bbdfb58e62c3f8a4742f3eaad6 100644 (file)
@@ -426,6 +426,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
   { "LuaFFIAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion" },
   { "LuaFFIResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse" },
+  { "LuaFFIRule", true, "function", "Invoke a Lua FFI function that filters DNS questions" },
   { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
   { "MacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
   { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" },
@@ -532,6 +533,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" },
   { "setServerPolicy", true, "policy", "set server selection policy to that policy" },
   { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
+  { "setServerPolicyLuaFFI", true, "name, function", "set server selection policy to one named 'name' and provided by the Lua FFI 'function'" },
   { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
   { "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" },
   { "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" },
index fc25caddf3f66e434b9fcd02529b1ea3e3e479a6..a57c620254a321e0a3e2fc0a1af250b293ae3cef 100644 (file)
@@ -57,7 +57,7 @@ void setupLuaBindings(bool client)
       return string("No exception");
     });
   /* ServerPolicy */
-  g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy, true};});
+  g_lua.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared<ServerPolicy>(name, policy, true);});
   g_lua.registerMember("name", &ServerPolicy::name);
   g_lua.registerMember("policy", &ServerPolicy::policy);
   g_lua.registerMember("isLua", &ServerPolicy::isLua);
@@ -117,7 +117,7 @@ void setupLuaBindings(bool client)
     [](DownstreamState& s, int newWeight) {s.setWeight(newWeight);}
   );
   g_lua.registerMember("order", &DownstreamState::order);
-  g_lua.registerMember("name", &DownstreamState::name);
+  g_lua.registerMember<const std::string(DownstreamState::*)>("name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); });
   g_lua.registerFunction<std::string(DownstreamState::*)()>("getID", [](const DownstreamState& s) { return boost::uuids::to_string(s.id); });
 
   /* dnsheader */
index 0d37ff320c583d27ce214151de2bc9a43abfe125..d01a007b11033247ed72836144c178f36141ebdc 100644 (file)
@@ -583,7 +583,7 @@ void setupLuaInspection()
       auto states = g_dstates.getLocal();
       counter = 0;
       for(const auto& s : *states) {
-        ret << (fmt % counter % s->name % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
+        ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
         ++counter;
       }
 
index 0cb77d6064e8eeafa19d0c0c61cdf3ae6832942a..63972b50f2ac97c29361e31c69a18c5db11776b7 100644 (file)
@@ -386,7 +386,7 @@ void setupLuaConfig(bool client, bool configCheck)
       }
 
       if(vars.count("name")) {
-        ret->name=boost::get<string>(vars["name"]);
+        ret->setName(boost::get<string>(vars["name"]));
       }
 
       if (vars.count("id")) {
@@ -536,20 +536,6 @@ void setupLuaConfig(bool client, bool configCheck)
                         g_dstates.setState(states);
                       } );
 
-  g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy)  {
-      setLuaSideEffect();
-      g_policy.setState(policy);
-    });
-  g_lua.writeFunction("setServerPolicyLua", [](string name, policyfunc_t policy)  {
-      setLuaSideEffect();
-      g_policy.setState(ServerPolicy{name, policy, true});
-    });
-
-  g_lua.writeFunction("showServerPolicy", []() {
-      setLuaSideEffect();
-      g_outputBuffer=g_policy.getLocal()->name+"\n";
-    });
-
   g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
   g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
 
@@ -696,11 +682,11 @@ void setupLuaConfig(bool client, bool configCheck)
             pools+=p;
           }
           if (showUUIDs) {
-            ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
+            ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() %
                     status %
                     s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools % s->id) << endl;
           } else {
-            ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
+            ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() %
                     status %
                     s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools) << endl;
           }
@@ -1402,8 +1388,8 @@ void setupLuaConfig(bool client, bool configCheck)
             if (!servers.empty()) {
               servers += ", ";
             }
-            if (!server.second->name.empty()) {
-              servers += server.second->name;
+            if (!server.second->getName().empty()) {
+              servers += server.second->getName();
               servers += " ";
             }
             servers += server.second->remote.toStringWithPort();
@@ -1701,6 +1687,27 @@ void setupLuaConfig(bool client, bool configCheck)
 #endif /* HAVE_NET_SNMP */
     });
 
+  g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
+      setLuaSideEffect();
+      g_policy.setState(policy);
+    });
+
+  g_lua.writeFunction("setServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy) {
+      setLuaSideEffect();
+      g_policy.setState(ServerPolicy{name, policy, true});
+    });
+
+  g_lua.writeFunction("setServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy) {
+      setLuaSideEffect();
+      auto pol = ServerPolicy(name, policy);
+      g_policy.setState(std::move(pol));
+    });
+
+  g_lua.writeFunction("showServerPolicy", []() {
+      setLuaSideEffect();
+      g_outputBuffer=g_policy.getLocal()->name+"\n";
+    });
+
   g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
       setLuaSideEffect();
       auto localPools = g_pools.getCopy();
@@ -1708,7 +1715,7 @@ void setupLuaConfig(bool client, bool configCheck)
       g_pools.setState(localPools);
     });
 
-  g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
+  g_lua.writeFunction("setPoolServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy, string pool) {
       setLuaSideEffect();
       auto localPools = g_pools.getCopy();
       setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
index c0957d4e6f437673a8779f19383c50f151092e73..c2d0a86fcdd07f9d68f37a8962e09d38803d4968 100644 (file)
@@ -293,8 +293,8 @@ static int backendStatTable_handler(netsnmp_mib_handler* handler,
       case COLUMN_BACKENDNAME:
         snmp_set_var_typed_value(request->requestvb,
                                  ASN_OCTET_STR,
-                                 server->name.c_str(),
-                                 server->name.size());
+                                 server->getName().c_str(),
+                                 server->getName().size());
         break;
       case COLUMN_BACKENDLATENCY:
         DNSDistSNMPAgent::setCounter64Value(request,
@@ -388,8 +388,8 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(const std::shared_ptr<Downstr
                             backendNameOID,
                             OID_LENGTH(backendNameOID),
                             ASN_OCTET_STR,
-                            dss->name.c_str(),
-                            dss->name.size());
+                            dss->getName().c_str(),
+                            dss->getName().size());
 
   snmp_varlist_add_variable(&varList,
                             backendAddressOID,
index a089a9f6c3f17359998627d0b63a3cd5df031c28..8afe5560bdf4009330d5cd94eafa61a814ab369c 100644 (file)
@@ -558,7 +558,7 @@ static void connectionThread(int sock, ComboAddress remote)
         for (const auto& state : *states) {
           string serverName;
 
-          if (state->name.empty())
+          if (state->getName().empty())
               serverName = state->remote.toStringWithPort();
           else
               serverName = state->getName();
@@ -821,7 +821,7 @@ static void connectionThread(int sock, ComboAddress remote)
 
        Json::object server{
          {"id", num++},
-         {"name", a->name},
+         {"name", a->getName()},
           {"address", a->remote.toStringWithPort()},
           {"state", status},
           {"qps", (double)a->queryLoad},
index 75d7c56ea299d30c8a31bd31fb61128377ee9d9c..95cd7ef10de04e31e4b40dfee583241677c45428 100644 (file)
@@ -48,6 +48,7 @@
 #include "dnsdist-ecs.hh"
 #include "dnsdist-healthchecks.hh"
 #include "dnsdist-lua.hh"
+#include "dnsdist-lua-ffi.hh"
 #include "dnsdist-rings.hh"
 #include "dnsdist-secpoll.hh"
 #include "dnsdist-xpf.hh"
@@ -811,7 +812,7 @@ void DownstreamState::setWeight(int newWeight)
   }
 }
 
-DownstreamState::DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf_, const std::string& sourceItfName_, size_t numberOfSockets, bool connect=true): sourceItfName(sourceItfName_), remote(remote_), sourceAddr(sourceAddr_), sourceItf(sourceItf_)
+DownstreamState::DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf_, const std::string& sourceItfName_, size_t numberOfSockets, bool connect=true): sourceItfName(sourceItfName_), remote(remote_), sourceAddr(sourceAddr_), sourceItf(sourceItf_), name(remote_.toStringWithPort()), nameWithAddr(remote_.toStringWithPort())
 {
   pthread_rwlock_init(&d_lock, nullptr);
   id = getUniqueID();
@@ -838,7 +839,7 @@ LuaContext g_lua;
 
 GlobalStateHolder<ServerPolicy> g_policy;
 
-shared_ptr<DownstreamState> firstAvailable(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> firstAvailable(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   for(auto& d : servers) {
     if(d.second->isUp() && d.second->qps.check())
@@ -848,7 +849,7 @@ shared_ptr<DownstreamState> firstAvailable(const NumberedServerVector& servers,
 }
 
 // get server with least outstanding queries, and within those, with the lowest order, and within those: the fastest
-shared_ptr<DownstreamState> leastOutstanding(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> leastOutstanding(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   if (servers.size() == 1 && servers[0].second->isUp()) {
     return servers[0].second;
@@ -869,7 +870,7 @@ shared_ptr<DownstreamState> leastOutstanding(const NumberedServerVector& servers
   return poss.begin()->second;
 }
 
-shared_ptr<DownstreamState> valrandom(unsigned int val, const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> valrandom(unsigned int val, const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   vector<pair<int, shared_ptr<DownstreamState>>> poss;
   int sum = 0;
@@ -899,19 +900,19 @@ shared_ptr<DownstreamState> valrandom(unsigned int val, const NumberedServerVect
   return p->second;
 }
 
-shared_ptr<DownstreamState> wrandom(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> wrandom(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   return valrandom(random(), servers, dq);
 }
 
 uint32_t g_hashperturb;
 double g_consistentHashBalancingFactor = 0;
-shared_ptr<DownstreamState> whashed(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> whashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   return valrandom(dq->qname->hash(g_hashperturb), servers, dq);
 }
 
-shared_ptr<DownstreamState> chashed(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> chashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
   unsigned int qhash = dq->qname->hash(g_hashperturb);
   unsigned int sel = std::numeric_limits<unsigned int>::max();
@@ -962,9 +963,9 @@ shared_ptr<DownstreamState> chashed(const NumberedServerVector& servers, const D
   return shared_ptr<DownstreamState>();
 }
 
-shared_ptr<DownstreamState> roundrobin(const NumberedServerVector& servers, const DNSQuestion* dq)
+shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
 {
-  NumberedServerVector poss;
+  ServerPolicy::NumberedServerVector poss;
 
   for(auto& d : servers) {
     if(d.second->isUp()) {
@@ -1049,7 +1050,7 @@ std::shared_ptr<ServerPool> getPool(const pools_t& pools, const std::string& poo
   return it->second;
 }
 
-NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName)
+ServerPolicy::NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName)
 {
   std::shared_ptr<ServerPool> pool = getPool(pools, poolName);
   return pool->getServers();
@@ -1525,8 +1526,20 @@ ProcessQueryResult processQuery(DNSQuestion& dq, ClientState& cs, LocalHolders&
     }
     auto servers = serverPool->getServers();
     if (policy.isLua) {
-      std::lock_guard<std::mutex> lock(g_luamutex);
-      selectedBackend = policy.policy(servers, &dq);
+      if (!policy.isFFI) {
+        std::lock_guard<std::mutex> lock(g_luamutex);
+        selectedBackend = policy.policy(servers, &dq);
+      }
+      else {
+        dnsdist_ffi_dnsquestion_t dnsq(&dq);
+        dnsdist_ffi_servers_list_t serversList(servers);
+        unsigned int selected = 0;
+        {
+          std::lock_guard<std::mutex> lock(g_luamutex);
+          selected = policy.ffipolicy(&serversList, &dnsq);
+        }
+        selectedBackend = servers.at(selected).second;
+      }
     }
     else {
       selectedBackend = policy.policy(servers, &dq);
@@ -2030,7 +2043,7 @@ static void healthChecksThread()
           --dss->outstanding;
           ++g_stats.downstreamTimeouts; // this is an 'actively' discovered timeout
           vinfolog("Had a downstream timeout from %s (%s) for query for %s|%s from %s",
-                   dss->remote.toStringWithPort(), dss->name,
+                   dss->remote.toStringWithPort(), dss->getName(),
                    ids.qname.toLogString(), QType(ids.qtype).getName(), ids.origRemote.toStringWithPort());
 
           struct timespec ts;
index 44c992d01be2b960a87904174d3557c02d3b78ff..7b469983dcc9ed0a3925be6134320a7631d24a7b 100644 (file)
@@ -807,7 +807,6 @@ struct DownstreamState
   std::atomic<double> tcpAvgQueriesPerConnection{0.0};
   /* in ms */
   std::atomic<double> tcpAvgConnectionDuration{0.0};
-  string name;
   size_t socketsOffset{0};
   double queryLoad{0.0};
   double dropRate{0.0};
@@ -852,17 +851,19 @@ struct DownstreamState
   void setDown() { availability = Availability::Down; }
   void setAuto() { availability = Availability::Auto; }
   string getName() const {
-    if (name.empty()) {
-      return remote.toStringWithPort();
-    }
     return name;
   }
   string getNameWithAddr() const {
-    if (name.empty()) {
-      return remote.toStringWithPort();
+    return nameWithAddr;
+  }
+  void setName(const std::string& newName)
+  {
+    name = newName;
+    if (newName.empty()) {
+      nameWithAddr = newName.empty() ? remote.toStringWithPort() : (name + " (" + remote.toStringWithPort()+ ")");
     }
-    return name + " (" + remote.toStringWithPort()+ ")";
   }
+
   string getStatus() const
   {
     string status;
@@ -884,11 +885,12 @@ struct DownstreamState
     tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0);
     tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0);
   }
+private:
+  std::string name;
+  std::string nameWithAddr;
 };
 using servers_t =vector<std::shared_ptr<DownstreamState>>;
 
-template <class T> using NumberedVector = std::vector<std::pair<unsigned int, T> >;
-
 void responderThread(std::shared_ptr<DownstreamState> state);
 extern std::mutex g_luamutex;
 extern LuaContext g_lua;
@@ -905,14 +907,33 @@ public:
   mutable std::atomic<uint64_t> d_matches{0};
 };
 
-using NumberedServerVector = NumberedVector<shared_ptr<DownstreamState>>;
-typedef std::function<shared_ptr<DownstreamState>(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t;
+struct dnsdist_ffi_servers_list_t;
+struct dnsdist_ffi_server_t;
+struct dnsdist_ffi_dnsquestion_t;
 
 struct ServerPolicy
 {
+  template <class T> using NumberedVector = std::vector<std::pair<unsigned int, T> >;
+  using NumberedServerVector = NumberedVector<shared_ptr<DownstreamState>>;
+  typedef std::function<shared_ptr<DownstreamState>(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t;
+  typedef std::function<unsigned int(dnsdist_ffi_servers_list_t* servers, dnsdist_ffi_dnsquestion_t* dq)> ffipolicyfunc_t;
+
+  ServerPolicy(const std::string& name_, policyfunc_t policy_, bool isLua_): name(name_), policy(policy_), isLua(isLua_)
+  {
+  }
+  ServerPolicy(const std::string& name_, ffipolicyfunc_t policy_): name(name_), ffipolicy(policy_), isLua(true), isFFI(true)
+  {
+  }
+  ServerPolicy()
+  {
+  }
+
   string name;
   policyfunc_t policy;
-  bool isLua;
+  ffipolicyfunc_t ffipolicy;
+  bool isLua{false};
+  bool isFFI{false};
+
   std::string toString() const {
     return string("ServerPolicy") + (isLua ? " (Lua)" : "") + " \"" + name + "\"";
   }
@@ -956,9 +977,9 @@ struct ServerPool
     return count;
   }
 
-  NumberedVector<shared_ptr<DownstreamState>> getServers()
+  ServerPolicy::NumberedServerVector getServers()
   {
-    NumberedVector<shared_ptr<DownstreamState>> result;
+    ServerPolicy::NumberedServerVector result;
     {
       ReadLock rl(&d_lock);
       result = d_servers;
@@ -1005,7 +1026,7 @@ struct ServerPool
   }
 
 private:
-  NumberedVector<shared_ptr<DownstreamState>> d_servers;
+  ServerPolicy::NumberedServerVector d_servers;
   pthread_rwlock_t d_lock;
   bool d_useECS{false};
 };
@@ -1117,15 +1138,15 @@ struct dnsheader;
 void controlThread(int fd, ComboAddress local);
 std::shared_ptr<ServerPool> getPool(const pools_t& pools, const std::string& poolName);
 std::shared_ptr<ServerPool> createPoolIfNotExists(pools_t& pools, const string& poolName);
-NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName);
+ServerPolicy::NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName);
 
-std::shared_ptr<DownstreamState> firstAvailable(const NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> firstAvailable(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
 
-std::shared_ptr<DownstreamState> leastOutstanding(const NumberedServerVector& servers, const DNSQuestion* dq);
-std::shared_ptr<DownstreamState> wrandom(const NumberedServerVector& servers, const DNSQuestion* dq);
-std::shared_ptr<DownstreamState> whashed(const NumberedServerVector& servers, const DNSQuestion* dq);
-std::shared_ptr<DownstreamState> chashed(const NumberedServerVector& servers, const DNSQuestion* dq);
-std::shared_ptr<DownstreamState> roundrobin(const NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> leastOutstanding(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> wrandom(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> whashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> chashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
+std::shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
 
 struct WebserverConfig
 {
index fa8841d0b8c33c462b3eb0cd55f328e7dfffb0f0..d51340751167c60b2ff05c6218d27f1f8eb82d88 100644 (file)
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 typedef struct dnsdist_ffi_dnsquestion_t dnsdist_ffi_dnsquestion_t;
+typedef struct dnsdist_ffi_servers_list_t dnsdist_ffi_servers_list_t;
+typedef struct dnsdist_ffi_server_t dnsdist_ffi_server_t;
 
 typedef struct dnsdist_ednsoption {
   uint16_t    optionCode;
@@ -88,3 +90,16 @@ size_t dnsdist_ffi_dnsquestion_get_trailing_data(dnsdist_ffi_dnsquestion_t* dq,
 bool dnsdist_ffi_dnsquestion_set_trailing_data(dnsdist_ffi_dnsquestion_t* dq, const char* data, size_t dataLen) __attribute__ ((visibility ("default")));
 
 void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char* reason, size_t reasonLen) __attribute__ ((visibility ("default")));
+
+typedef struct dnsdist_ffi_servers_list_t dnsdist_ffi_servers_list_t;
+typedef struct dnsdist_ffi_server_t dnsdist_ffi_server_t;
+
+size_t dnsdist_ffi_servers_list_get_count(const dnsdist_ffi_servers_list_t* list) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_servers_list_get_server(const dnsdist_ffi_servers_list_t* list, size_t idx, const dnsdist_ffi_server_t** out) __attribute__ ((visibility ("default")));
+
+uint64_t dnsdist_ffi_server_get_outstanding(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+bool dnsdist_ffi_server_is_up(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+const char* dnsdist_ffi_server_get_name(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+const char* dnsdist_ffi_server_get_name_with_addr(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+int dnsdist_ffi_server_get_weight(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+int dnsdist_ffi_server_get_order(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
index cfffc2c1e34c4706ee44e27786497b0049404f2a..67466e2631114ddc8b8928217e749a3a6ea3922b 100644 (file)
@@ -410,6 +410,46 @@ void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char
   }
 }
 
+size_t dnsdist_ffi_servers_list_get_count(const dnsdist_ffi_servers_list_t* list)
+{
+  return list->ffiServers.size();
+}
+
+void dnsdist_ffi_servers_list_get_server(const dnsdist_ffi_servers_list_t* list, size_t idx, const dnsdist_ffi_server_t** out)
+{
+  *out = &list->ffiServers.at(idx);
+}
+
+uint64_t dnsdist_ffi_server_get_outstanding(const dnsdist_ffi_server_t* server)
+{
+  return server->server->outstanding;
+}
+
+int dnsdist_ffi_server_get_weight(const dnsdist_ffi_server_t* server)
+{
+  return server->server->weight;
+}
+
+int dnsdist_ffi_server_get_order(const dnsdist_ffi_server_t* server)
+{
+  return server->server->order;
+}
+
+bool dnsdist_ffi_server_is_up(const dnsdist_ffi_server_t* server)
+{
+  return server->server->isUp();
+}
+
+const char* dnsdist_ffi_server_get_name(const dnsdist_ffi_server_t* server)
+{
+  return server->server->getName().c_str();
+}
+
+const char* dnsdist_ffi_server_get_name_with_addr(const dnsdist_ffi_server_t* server)
+{
+  return server->server->getNameWithAddr().c_str();
+}
+
 const std::string& getLuaFFIWrappers()
 {
   static const std::string interface =
index 913c759474056ab7fd59aebfde132bf4e4882361..811a7f777e205f2a457ee54cc731acc4a257ed9f 100644 (file)
@@ -58,4 +58,50 @@ struct dnsdist_ffi_dnsquestion_t
   boost::optional<std::string> httpScheme{boost::none};
 };
 
+// dnsdist_ffi_server_t is a lightuserdata
+template<>
+struct LuaContext::Pusher<dnsdist_ffi_server_t*> {
+    static const int minSize = 1;
+    static const int maxSize = 1;
+
+    static PushedObject push(lua_State* state, dnsdist_ffi_server_t* ptr) noexcept {
+        lua_pushlightuserdata(state, ptr);
+        return PushedObject{state, 1};
+    }
+};
+
+struct dnsdist_ffi_server_t
+{
+  dnsdist_ffi_server_t(const std::shared_ptr<DownstreamState>& server_): server(server_)
+  {
+  }
+
+  const std::shared_ptr<DownstreamState>& server;
+};
+
+// dnsdist_ffi_servers_list_t is a lightuserdata
+template<>
+struct LuaContext::Pusher<dnsdist_ffi_servers_list_t*> {
+    static const int minSize = 1;
+    static const int maxSize = 1;
+
+    static PushedObject push(lua_State* state, dnsdist_ffi_servers_list_t* ptr) noexcept {
+        lua_pushlightuserdata(state, ptr);
+        return PushedObject{state, 1};
+    }
+};
+
+struct dnsdist_ffi_servers_list_t
+{
+  dnsdist_ffi_servers_list_t(const ServerPolicy::NumberedServerVector& servers)
+  {
+    ffiServers.reserve(servers.size());
+    for (const auto& server: servers) {
+      ffiServers.push_back(dnsdist_ffi_server_t(server.second));
+    }
+  }
+
+  std::vector<dnsdist_ffi_server_t> ffiServers;
+};
+
 const std::string& getLuaFFIWrappers();
index 4fbbd171b703537924583e27d89ec3ad66e9e8d6..46f846c68ca31f044b9d0df1e6470314d98a64b4 100644 (file)
@@ -31,7 +31,7 @@ Large installations are advised to increase the default value at the cost of a s
 Most of the query processing is done in C++ for maximum performance, but some operations are executed in Lua for maximum flexibility:
 
  * Rules added by :func:`addLuaAction`
- * Server selection policies defined via :func:`setServerPolicyLua` or :func:`newServerPolicy`
+ * Server selection policies defined via :func:`setServerPolicyLua`, :func:`setServerPolicyLuaFFI` or :func:`newServerPolicy`
 
 While Lua is fast, its use should be restricted to the strict necessary in order to achieve maximum performance, it might be worth considering using LuaJIT instead of Lua.
 When Lua inspection is needed, the best course of action is to restrict the queries sent to Lua inspection by using :func:`addLuaAction` with a selector.
index f86dc6d8523fb2373f5bfd34434950dd6f74acbe..53286c346e09f3452218889f39c7ff65ee5cb492 100644 (file)
@@ -146,6 +146,15 @@ Functions
   :param string name: name for this policy
   :param string function: name of the function
 
+.. function:: setServerPolicyLuaFFI(name, function)
+
+  .. versionadded:: 1.5.0
+
+  Set server selection policy to one named `name`` and provided by the FFI function ``function``.
+
+  :param string name: name for this policy
+  :param string function: name of the FFI function
+
 .. function:: setServFailWhenNoServer(value)
 
   If set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query.