/src/ntopng/src/InterfaceStatsHash.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * |
3 | | * (C) 2013-24 - ntop.org |
4 | | * |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, write to the Free Software Foundation, |
18 | | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | | * |
20 | | */ |
21 | | |
22 | | #include "ntop_includes.h" |
23 | | |
24 | | /* ************************************ */ |
25 | | |
26 | 0 | InterfaceStatsHash::InterfaceStatsHash(u_int _max_hash_size) { |
27 | 0 | if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__); |
28 | | |
29 | 0 | max_hash_size = _max_hash_size; |
30 | 0 | buckets = (sFlowInterfaceStats **)calloc(sizeof(sFlowInterfaceStats *), |
31 | 0 | max_hash_size); |
32 | |
|
33 | 0 | if (buckets == NULL) throw "Not enough memory"; |
34 | 0 | } |
35 | | |
36 | | /* ************************************ */ |
37 | | |
38 | 0 | InterfaceStatsHash::~InterfaceStatsHash() { |
39 | 0 | for (u_int i = 0; i < max_hash_size; i++) { |
40 | 0 | if (buckets[i] != NULL) { |
41 | 0 | if (buckets[i]->ifName) free(buckets[i]->ifName); |
42 | |
|
43 | 0 | if (buckets[i]->container_info_set) { |
44 | 0 | if (buckets[i]->container_info.id) free(buckets[i]->container_info.id); |
45 | 0 | if (buckets[i]->container_info.name) |
46 | 0 | free(buckets[i]->container_info.name); |
47 | |
|
48 | 0 | if (buckets[i]->container_info.data_type == |
49 | 0 | container_info_data_type_k8s) { |
50 | 0 | if (buckets[i]->container_info.data.k8s.pod) |
51 | 0 | free(buckets[i]->container_info.data.k8s.pod); |
52 | 0 | if (buckets[i]->container_info.data.k8s.ns) |
53 | 0 | free(buckets[i]->container_info.data.k8s.ns); |
54 | 0 | } else if (buckets[i]->container_info.data_type == |
55 | 0 | container_info_data_type_docker) |
56 | 0 | ; |
57 | 0 | } |
58 | |
|
59 | 0 | free(buckets[i]); |
60 | 0 | } |
61 | 0 | } |
62 | |
|
63 | 0 | free(buckets); |
64 | 0 | } |
65 | | /* ************************************ */ |
66 | | |
67 | 0 | bool InterfaceStatsHash::set(const sFlowInterfaceStats *const stats) { |
68 | 0 | sFlowInterfaceStats *head; |
69 | 0 | u_int32_t ifIndex = stats->ifIndex, deviceIP = stats->deviceIP; |
70 | 0 | const char *ifName = stats->ifName; |
71 | 0 | u_int32_t hash = (deviceIP + ifIndex + Utils::hashString(ifName)) % |
72 | 0 | max_hash_size, |
73 | 0 | num_runs = 0; |
74 | 0 | bool ret = true; |
75 | |
|
76 | 0 | m.lock(__FILE__, __LINE__); |
77 | |
|
78 | 0 | if (!buckets[hash]) goto new_bucket; |
79 | | |
80 | 0 | head = (sFlowInterfaceStats *)buckets[hash]; |
81 | |
|
82 | 0 | while (head != NULL) { |
83 | 0 | if (head->deviceIP == deviceIP && head->ifIndex == ifIndex && |
84 | 0 | ((!head->ifName && !ifName) || |
85 | 0 | (head->ifName && ifName && strcmp(head->ifName, ifName) == 0))) { |
86 | 0 | break; |
87 | 0 | } else { |
88 | | /* Inplace hash */ |
89 | 0 | hash = (hash + 1) % max_hash_size, num_runs++; |
90 | |
|
91 | 0 | if (num_runs >= max_hash_size) { |
92 | 0 | ntop->getTrace()->traceEvent( |
93 | 0 | TRACE_WARNING, "Internal error: too many loops=%u", max_hash_size); |
94 | 0 | ret = false; |
95 | 0 | goto unlock; |
96 | 0 | } |
97 | | |
98 | 0 | head = buckets[hash]; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | 0 | if (head) { |
103 | | /* Update values */ |
104 | 0 | head->ifType = stats->ifType, head->ifSpeed = stats->ifSpeed, |
105 | 0 | head->ifFullDuplex = stats->ifFullDuplex, |
106 | 0 | head->ifAdminStatus = stats->ifAdminStatus, |
107 | 0 | head->ifOperStatus = stats->ifOperStatus, |
108 | 0 | head->ifPromiscuousMode = stats->ifPromiscuousMode; |
109 | |
|
110 | 0 | if (stats->samplesGenerated > 0 && |
111 | 0 | stats->samplesGenerated == head->samplesGenerated) { |
112 | | /* |
113 | | This is an update as `samplesGenerated`. |
114 | | Ubiquiti routers when generating sFlow send two counters samples in two |
115 | | different packets with the same value for `samplesGenerated`. The first |
116 | | counter sample has data for the IN direction and the second counter |
117 | | sample has data for the OUT direction. In this case, we need to check |
118 | | and update values rather than overwriting them. |
119 | | */ |
120 | 0 | head->ifInOctets += stats->ifInOctets, |
121 | 0 | head->ifInPackets += stats->ifInPackets, |
122 | 0 | head->ifInErrors += stats->ifInErrors, |
123 | 0 | head->ifOutOctets += stats->ifOutOctets, |
124 | 0 | head->ifOutPackets += stats->ifOutPackets, |
125 | 0 | head->ifOutErrors += stats->ifOutErrors; |
126 | 0 | } else { |
127 | 0 | head->ifInOctets = stats->ifInOctets, |
128 | 0 | head->ifInPackets = stats->ifInPackets, |
129 | 0 | head->ifInErrors = stats->ifInErrors, |
130 | 0 | head->ifOutOctets = stats->ifOutOctets, |
131 | 0 | head->ifOutPackets = stats->ifOutPackets, |
132 | 0 | head->ifOutErrors = stats->ifOutErrors; |
133 | 0 | } |
134 | |
|
135 | 0 | head->samplesGenerated = stats->samplesGenerated; |
136 | 0 | } else { |
137 | 0 | new_bucket: |
138 | 0 | buckets[hash] = (sFlowInterfaceStats *)malloc(sizeof(sFlowInterfaceStats)); |
139 | |
|
140 | 0 | if (!buckets[hash]) { |
141 | 0 | ret = false; |
142 | 0 | goto unlock; |
143 | 0 | } |
144 | | |
145 | 0 | memcpy(buckets[hash], stats, sizeof(sFlowInterfaceStats)); |
146 | |
|
147 | 0 | if (buckets[hash]->ifName) |
148 | 0 | buckets[hash]->ifName = strdup(buckets[hash]->ifName); |
149 | |
|
150 | 0 | if (buckets[hash]->container_info_set) { |
151 | 0 | if (buckets[hash]->container_info.id) |
152 | 0 | buckets[hash]->container_info.id = |
153 | 0 | strdup(buckets[hash]->container_info.id); |
154 | 0 | if (buckets[hash]->container_info.name) |
155 | 0 | buckets[hash]->container_info.name = |
156 | 0 | strdup(buckets[hash]->container_info.name); |
157 | |
|
158 | 0 | if (buckets[hash]->container_info.data_type == |
159 | 0 | container_info_data_type_k8s) { |
160 | 0 | if (buckets[hash]->container_info.data.k8s.pod) |
161 | 0 | buckets[hash]->container_info.data.k8s.pod = |
162 | 0 | strdup(buckets[hash]->container_info.data.k8s.pod); |
163 | 0 | if (buckets[hash]->container_info.data.k8s.ns) |
164 | 0 | buckets[hash]->container_info.data.k8s.ns = |
165 | 0 | strdup(buckets[hash]->container_info.data.k8s.ns); |
166 | 0 | } else if (buckets[hash]->container_info.data_type == |
167 | 0 | container_info_data_type_docker) { |
168 | 0 | } |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | unlock: |
173 | 0 | m.unlock(__FILE__, __LINE__); |
174 | |
|
175 | 0 | return (ret); |
176 | 0 | } |
177 | | |
178 | | /* ************************************ */ |
179 | | |
180 | 0 | void InterfaceStatsHash::luaDeviceList(lua_State *vm) { |
181 | 0 | std::set<u_int32_t> |
182 | 0 | flowDevices; /* Set size automatically limited by max_hash_size */ |
183 | 0 | std::set<u_int32_t>::const_iterator it; |
184 | |
|
185 | 0 | lua_newtable(vm); |
186 | |
|
187 | 0 | m.lock(__FILE__, __LINE__); |
188 | |
|
189 | 0 | for (u_int i = 0; i < max_hash_size; i++) { |
190 | 0 | sFlowInterfaceStats *head = (sFlowInterfaceStats *)buckets[i]; |
191 | |
|
192 | 0 | if (head) { |
193 | 0 | bool found = false; |
194 | |
|
195 | 0 | if (flowDevices.find(head->deviceIP) != flowDevices.end()) found = true; |
196 | |
|
197 | 0 | if (!found) { |
198 | 0 | char a[64]; |
199 | |
|
200 | 0 | flowDevices.insert(head->deviceIP); |
201 | |
|
202 | 0 | lua_push_uint64_table_entry( |
203 | 0 | vm, Utils::intoaV4(head->deviceIP, a, sizeof(a)), head->deviceIP); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | |
|
208 | 0 | m.unlock(__FILE__, __LINE__); |
209 | 0 | } |
210 | | |
211 | | /* ************************************ */ |
212 | | |
213 | 0 | void InterfaceStatsHash::luaDeviceInfo(lua_State *vm, u_int32_t deviceIP) { |
214 | 0 | lua_newtable(vm); |
215 | |
|
216 | 0 | m.lock(__FILE__, __LINE__); |
217 | |
|
218 | 0 | for (u_int i = 0; i < max_hash_size; i++) { |
219 | 0 | sFlowInterfaceStats *head = (sFlowInterfaceStats *)buckets[i]; |
220 | |
|
221 | 0 | if (head && (head->deviceIP == deviceIP)) { |
222 | 0 | lua_newtable(vm); |
223 | |
|
224 | 0 | lua_push_uint64_table_entry(vm, "ifType", head->ifType); |
225 | 0 | if (head->ifName) lua_push_str_table_entry(vm, "ifName", head->ifName); |
226 | 0 | if (head->container_info_set) { |
227 | 0 | Utils::containerInfoLua(vm, &head->container_info); |
228 | |
|
229 | 0 | lua_pushstring(vm, "container"); |
230 | 0 | lua_insert(vm, -2); |
231 | 0 | lua_settable(vm, -3); |
232 | 0 | } |
233 | 0 | lua_push_uint64_table_entry(vm, "ifSpeed", head->ifSpeed); |
234 | 0 | lua_push_bool_table_entry(vm, "ifFullDuplex", head->ifFullDuplex); |
235 | 0 | lua_push_bool_table_entry(vm, "ifAdminStatus", head->ifAdminStatus); |
236 | 0 | lua_push_bool_table_entry(vm, "ifOperStatus", head->ifOperStatus); |
237 | 0 | lua_push_bool_table_entry(vm, "ifPromiscuousMode", |
238 | 0 | head->ifPromiscuousMode); |
239 | 0 | lua_push_uint64_table_entry(vm, "ifInOctets", head->ifInOctets); |
240 | 0 | lua_push_uint64_table_entry(vm, "ifInPackets", head->ifInPackets); |
241 | 0 | lua_push_uint64_table_entry(vm, "ifInErrors", head->ifInErrors); |
242 | 0 | lua_push_uint64_table_entry(vm, "ifOutOctets", head->ifOutOctets); |
243 | 0 | lua_push_uint64_table_entry(vm, "ifOutPackets", head->ifOutPackets); |
244 | 0 | lua_push_uint64_table_entry(vm, "ifOutErrors", head->ifOutErrors); |
245 | |
|
246 | 0 | lua_pushinteger(vm, head->ifIndex); |
247 | 0 | lua_insert(vm, -2); |
248 | 0 | lua_settable(vm, -3); |
249 | 0 | } |
250 | 0 | } |
251 | |
|
252 | 0 | m.unlock(__FILE__, __LINE__); |
253 | 0 | } |