* `topQueries(20,2)`: shows the top-20 two-level domain queries (so `topQueries(20,1)` only shows TLDs)
* `topResponses(20, 2)`: top-20 servfail responses (use ,3 for NXDOMAIN)
+
+Per domain or subnet QPS limiting
+---------------------------------
+If certain domains or source addresses are generating onerous amounts of
+traffic, you can put ceilings on the amount of traffic you are willing to
+forward:
+
+```
+> addQPSLimit("h4xorbooter.xyz.", 10)
+> addQPSLimit({"130.161.0.0/16", "145.14.0.0/16"} , 20)
+> addQPSLimit({"nl.", "be."}, 1)
+> showQPSLimits()
+# Object Lim Passed Blocked
+0 h4xorbooter.xyz. 10 0 0
+1 130.161.0.0/16, 145.14.0.0/16 20 0 0
+2 nl., be. 1 2 8
+```
+
+To delete a limit:
+```
+> deleteQPSLimit(1)
+> showQPSLimits()
+# Object Lim Passed Blocked
+0 h4xorbooter.xyz. 10 0 0
+1 nl., be. 1 16 251
+```
+
Dynamic load balancing
----------------------
NetmaskGroup g_ACL;
string g_outputBuffer;
+
/* UDP: the grand design. Per socket we listen on for incoming queries there is one thread.
Then we have a bunch of connected sockets for talking to downstream servers.
We send directly to those sockets.
return d_passthrough? 0 : d_rate;
}
+ int getPassed() const
+ {
+ return d_passed;
+ }
+ int getBlocked() const
+ {
+ return d_blocked;
+ }
+
bool check()
{
if(d_passthrough)
return true;
- d_tokens += 1.0*d_rate * (d_prev.udiffAndSet()/1000000.0);
+ auto delta = d_prev.udiffAndSet();
+
+ d_tokens += 1.0*d_rate * (delta/1000000.0);
if(d_tokens > d_burst)
d_tokens = d_burst;
if(d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise
ret=true;
--d_tokens;
+ d_passed++;
}
+ else
+ d_blocked++;
+
return ret;
}
private:
unsigned int d_burst;
double d_tokens;
StopWatch d_prev;
+ unsigned int d_passed{0};
+ unsigned int d_blocked{0};
};
+vector<pair<boost::variant<SuffixMatchNode,NetmaskGroup>, QPSLimiter> > g_limiters;
+
struct IDState
{
IDState() : origFD(-1) {}
g_rings.queryRing.push_back(qname);
+ bool blocked=false;
+ for(auto& lim : g_limiters) {
+ if(auto nmg=boost::get<NetmaskGroup>(&lim.first)) {
+ if(nmg->match(remote) && !lim.second.check()) {
+ blocked=true;
+ break;
+ }
+ }
+ else if(auto smn=boost::get<SuffixMatchNode>(&lim.first)) {
+ if(smn->check(qname) && !lim.second.check()) {
+ blocked=true;
+ break;
+ }
+ }
+ }
+ if(blocked)
+ continue;
+
if(blockFilter)
{
std::lock_guard<std::mutex> lock(g_luamutex);
return a->order < b->order;
});
return ret;
-
-
} );
+
+
g_lua.writeFunction("deleteServer",
- [](std::shared_ptr<DownstreamState> rem)
+ [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
{
- g_dstates.erase(remove(g_dstates.begin(), g_dstates.end(), rem), g_dstates.end());
+ if(auto* rem = boost::get<shared_ptr<DownstreamState>>(&var))
+ g_dstates.erase(remove(g_dstates.begin(), g_dstates.end(), *rem), g_dstates.end());
+ else
+ g_dstates.erase(g_dstates.begin() + boost::get<int>(var));
} );
});
+ g_lua.writeFunction("addQPSLimit", [](boost::variant<string,vector<pair<int, string>> > var, int lim) {
+ SuffixMatchNode smn;
+ NetmaskGroup nmg;
+
+ auto add=[&](string src) {
+ try {
+ smn.add(DNSName(src));
+ } catch(...) {
+ nmg.addMask(src);
+ }
+ };
+ if(auto src = boost::get<string>(&var))
+ add(*src);
+ else {
+ for(auto& a : boost::get<vector<pair<int, string>>>(var)) {
+ add(a.second);
+ }
+ }
+ if(nmg.empty())
+ g_limiters.push_back({smn, QPSLimiter(lim, lim)});
+ else
+ g_limiters.push_back({nmg, QPSLimiter(lim, lim)});
+ });
+
+ g_lua.writeFunction("deleteQPSLimit", [](int i) {
+ g_limiters.erase(g_limiters.begin() + i);
+ });
+
+ g_lua.writeFunction("showQPSLimits", []() {
+ boost::format fmt("%-3d %-50s %7d %8d %8d\n");
+ g_outputBuffer += (fmt % "#" % "Object" % "Lim" % "Passed" % "Blocked").str();
+ int num=0;
+ for(const auto& lim : g_limiters) {
+ string name;
+ if(auto nmg=boost::get<NetmaskGroup>(&lim.first)) {
+ name=nmg->toString();
+ }
+ else if(auto smn=boost::get<SuffixMatchNode>(&lim.first)) {
+ name=smn->toString();
+ }
+ g_outputBuffer += (fmt % num % name % lim.second.getRate() % lim.second.getPassed() % lim.second.getBlocked()).str();
+ ++num;
+ }
+ });
+
+
g_lua.writeFunction("getServers", []() {
vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
int count=1;
SuffixMatchNode(const std::string& name_="", bool endNode_=false) : name(name_), endNode(endNode_)
{}
std::string name;
+ std::string d_human;
mutable bool endNode;
mutable std::set<SuffixMatchNode> children;
bool operator<(const SuffixMatchNode& rhs) const
void add(const DNSName& name)
{
+ if(!d_human.empty())
+ d_human.append(", ");
+ d_human += name.toString();
add(name.getRawLabels());
}
return check(name.getRawLabels());
}
-
bool check(std::deque<std::string> labels) const
{
if(labels.empty()) // optimization
labels.pop_back();
return child->check(labels);
}
-
+
+ std::string toString() const
+ {
+ return d_human;
+ }
};