1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include <fstream>
24
25 #include "dnsdist-rings.hh"
26
numDistinctRequestors()27 size_t Rings::numDistinctRequestors()
28 {
29 std::set<ComboAddress, ComboAddress::addressOnlyLessThan> s;
30 for (const auto& shard : d_shards) {
31 std::lock_guard<std::mutex> rl(shard->queryLock);
32 for(const auto& q : shard->queryRing) {
33 s.insert(q.requestor);
34 }
35 }
36 return s.size();
37 }
38
getTopBandwidth(unsigned int numentries)39 std::unordered_map<int, vector<boost::variant<string,double>>> Rings::getTopBandwidth(unsigned int numentries)
40 {
41 map<ComboAddress, unsigned int, ComboAddress::addressOnlyLessThan> counts;
42 uint64_t total=0;
43 for (const auto& shard : d_shards) {
44 {
45 std::lock_guard<std::mutex> rl(shard->queryLock);
46 for(const auto& q : shard->queryRing) {
47 counts[q.requestor]+=q.size;
48 total+=q.size;
49 }
50 }
51 {
52 std::lock_guard<std::mutex> rl(shard->respLock);
53 for(const auto& r : shard->respRing) {
54 counts[r.requestor]+=r.size;
55 total+=r.size;
56 }
57 }
58 }
59
60 typedef vector<pair<unsigned int, ComboAddress>> ret_t;
61 ret_t rcounts;
62 rcounts.reserve(counts.size());
63 for(const auto& p : counts)
64 rcounts.push_back({p.second, p.first});
65 numentries = rcounts.size() < numentries ? rcounts.size() : numentries;
66 partial_sort(rcounts.begin(), rcounts.begin()+numentries, rcounts.end(), [](const ret_t::value_type&a, const ret_t::value_type&b)
67 {
68 return(b.first < a.first);
69 });
70 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
71 uint64_t rest = 0;
72 unsigned int count = 1;
73 for(const auto& rc : rcounts) {
74 if(count==numentries+1) {
75 rest+=rc.first;
76 }
77 else {
78 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
79 }
80 }
81
82 if (total > 0) {
83 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
84 }
85 else {
86 ret.insert({count, {"Rest", rest, 100.0 }});
87 }
88
89 return ret;
90 }
91
loadFromFile(const std::string & filepath,const struct timespec & now)92 size_t Rings::loadFromFile(const std::string& filepath, const struct timespec& now)
93 {
94 ifstream ifs(filepath);
95 if (!ifs) {
96 throw std::runtime_error("unable to open the file at " + filepath);
97 }
98
99 size_t inserted = 0;
100 string line;
101 dnsheader dh;
102 memset(&dh, 0, sizeof(dh));
103
104 while (std::getline(ifs, line)) {
105 boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
106 boost::trim_left(line);
107 bool isResponse = false;
108 vector<string> parts;
109 stringtok(parts, line, " \t,");
110
111 if (parts.size() == 7) {
112 }
113 else if (parts.size() >= 10 && parts.size() <= 12) {
114 isResponse = true;
115 }
116 else {
117 cerr<<"skipping line with "<<parts.size()<<"parts: "<<line<<endl;
118 continue;
119 }
120
121 size_t idx = 0;
122 vector<string> timeStr;
123 stringtok(timeStr, parts.at(idx++), ".");
124 if (timeStr.size() != 2) {
125 cerr<<"skipping invalid time "<<parts.at(0)<<endl;
126 continue;
127 }
128
129 struct timespec when;
130 try {
131 when.tv_sec = now.tv_sec + std::stoi(timeStr.at(0));
132 when.tv_nsec = now.tv_nsec + std::stoi(timeStr.at(1)) * 100 * 1000 * 1000;
133 }
134 catch (const std::exception& e) {
135 cerr<<"error parsing time "<<parts.at(idx-1)<<" from line "<<line<<endl;
136 continue;
137 }
138
139 ComboAddress from(parts.at(idx++));
140 ComboAddress to;
141
142 if (isResponse) {
143 to = ComboAddress(parts.at(idx++));
144 }
145 /* skip ID */
146 idx++;
147 DNSName qname(parts.at(idx++));
148 QType qtype(QType::chartocode(parts.at(idx++).c_str()));
149
150 if (isResponse) {
151 insertResponse(when, from, qname, qtype.getCode(), 0, 0, dh, to);
152 }
153 else {
154 insertQuery(when, from, qname, qtype.getCode(), 0, dh);
155 }
156 ++inserted;
157 }
158
159 return inserted;
160 }
161