/src/ntopng/src/IEC104Stats.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * |
3 | | * (C) 2013-25 - 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 | | #include "flow_alerts_includes.h" |
24 | | |
25 | | /* |
26 | | #define DEBUG_IEC60870 |
27 | | #define IEC60870_TRACE |
28 | | */ |
29 | | |
30 | 36 | #define CLIENT_ALERT_SCORE 90 |
31 | 36 | #define SERVER_ALERT_SCORE 10 |
32 | | |
33 | | /* *************************************** */ |
34 | | |
35 | 1.50k | IEC104Stats::IEC104Stats() { |
36 | 1.50k | if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__); |
37 | | |
38 | 1.50k | memset(&pkt_lost, 0, sizeof(pkt_lost)); |
39 | 1.50k | last_type_i = 0; |
40 | 1.50k | memset(&last_i_apdu, 0, sizeof(last_i_apdu)); |
41 | 1.50k | memset(&stats, 0, sizeof(stats)); |
42 | 1.50k | memset(&transitions, 0, sizeof(transitions)); |
43 | | |
44 | 1.50k | i_s_apdu = ndpi_alloc_data_analysis(32 /* sliding window side */); |
45 | 1.50k | tx_seq_num = rx_seq_num = 0, infobuf[0] = '\0'; |
46 | 1.50k | invalid_command_transition_detected = false; |
47 | 1.50k | } |
48 | | |
49 | | /* *************************************** */ |
50 | | |
51 | 1.50k | IEC104Stats::~IEC104Stats() { |
52 | 1.50k | if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__); |
53 | 1.50k | ndpi_free_data_analysis(i_s_apdu, 1); |
54 | 1.50k | } |
55 | | |
56 | | /* *************************************** */ |
57 | | |
58 | | void IEC104Stats::processPacket(Flow *f, bool tx_direction, |
59 | | const u_char *payload, u_int16_t payload_len, |
60 | 3.98k | const struct pcap_pkthdr *h) { |
61 | 3.98k | if ((payload_len >= 6) && (payload[0] == 0x68 /* IEC magic byte */)) { |
62 | 3.92k | u_int offset = 1 /* Skip magic byte */; |
63 | 3.92k | u_int64_t *allowedTypeIDs = ntop->getPrefs()->getIEC104AllowedTypeIDs(); |
64 | 3.92k | std::unordered_map<u_int16_t, u_int32_t>::iterator it; |
65 | | |
66 | 3.92k | lock.wrlock(__FILE__, __LINE__); |
67 | | |
68 | 3.92k | if (tx_direction) |
69 | 78 | stats.forward_msgs++; |
70 | 3.84k | else |
71 | 3.84k | stats.reverse_msgs++; |
72 | | |
73 | 10.9k | while ((offset+1) /* Skip START byte */ < payload_len) { |
74 | | /* https://infosys.beckhoff.com/english.php?content=../content/1033/tcplclibiec870_5_104/html/tcplclibiec870_5_104_objref_overview.htm&id |
75 | | */ |
76 | 10.6k | u_int8_t len = payload[offset]; |
77 | 10.6k | u_int8_t pdu_type = ((payload[offset + 1] & 0x01) == 0) ? 0 : (payload[offset + 1] & 0x03); |
78 | | |
79 | | #ifdef DEBUG_IEC60870 |
80 | | ntop->getTrace()->traceEvent(TRACE_WARNING, "[%s] %02X %02X %02X %02X", |
81 | | __FUNCTION__, payload[offset - 1], |
82 | | payload[offset], payload[offset + 1], |
83 | | payload[offset + 2]); |
84 | | #endif |
85 | | |
86 | | #ifdef DEBUG_IEC60870 |
87 | | ntop->getTrace()->traceEvent( |
88 | | TRACE_WARNING, "[%s] A-PDU Len %u/%u [pdu_type: %u][magic: %02X]", |
89 | | __FUNCTION__, len, payload_len, pdu_type, payload[offset - 1]); |
90 | | #endif |
91 | | |
92 | 10.6k | if (len == 0) break; /* Something went wrong */ |
93 | | |
94 | 10.6k | switch (pdu_type) { |
95 | 987 | case 0x03: /* U */ |
96 | 987 | { |
97 | 987 | u_int8_t u_type = (payload[offset + 1] & 0xFC) >> 2; |
98 | 987 | const char *u_type_str; |
99 | | |
100 | | #ifdef IEC60870_TRACE |
101 | | ntop->getTrace()->traceEvent(TRACE_NORMAL, "A-PDU U-%u", |
102 | | (payload[offset + 1] & 0xFC) >> 2); |
103 | | #endif |
104 | | /* No rx and tx to be updated */ |
105 | 987 | stats.type_u++; |
106 | | |
107 | 987 | switch (u_type) { |
108 | 35 | case 0x01: |
109 | 35 | u_type_str = "STARTDT act"; |
110 | 35 | break; |
111 | | |
112 | 74 | case 0x02: |
113 | 74 | u_type_str = "STARTDT con"; |
114 | 74 | break; |
115 | | |
116 | 18 | case 0x04: |
117 | 18 | u_type_str = "STOPDT act"; |
118 | 18 | break; |
119 | | |
120 | 77 | case 0x08: |
121 | 77 | u_type_str = "STOPDT con"; |
122 | 77 | break; |
123 | | |
124 | 51 | case 0x10: |
125 | 51 | u_type_str = "TESTFR act"; |
126 | 51 | break; |
127 | | |
128 | 20 | case 0x20: |
129 | 20 | u_type_str = "TESTFR con"; |
130 | 20 | break; |
131 | | |
132 | 712 | default: |
133 | 712 | u_type_str = "???"; |
134 | 712 | break; |
135 | 987 | } |
136 | | |
137 | 987 | snprintf(infobuf, sizeof(infobuf) - 1, "%s U (%s)", |
138 | 987 | tx_direction ? "->" : "<-", u_type_str); |
139 | 987 | } |
140 | 0 | break; |
141 | | |
142 | 953 | case 0x01: /* S */ |
143 | 953 | if((offset+4) < payload_len) { |
144 | 945 | if (len >= 4) { |
145 | 712 | u_int16_t rx = ((((u_int16_t)payload[offset + 4]) << 8) + payload[offset + 3]) >> 1; |
146 | | |
147 | 712 | if (last_i_apdu.tv_sec != 0) { |
148 | 337 | float ms = Utils::msTimevalDiff(&h->ts, &last_i_apdu); |
149 | | |
150 | | #ifdef IEC60870_TRACE |
151 | | ntop->getTrace()->traceEvent( |
152 | | TRACE_NORMAL, |
153 | | "A-PDU S [last I-TX: %u][S RX ack: %u][tdiff: %.2f msec]", |
154 | | tx_seq_num, rx, ms); |
155 | | #endif |
156 | | /* |
157 | | In theory if all is in good shape |
158 | | (rx + 1) == tx_seq_num |
159 | | */ |
160 | | |
161 | 337 | ndpi_data_add_value(i_s_apdu, ms); |
162 | 337 | } |
163 | | |
164 | | /* No rx and tx to be updated */ |
165 | 712 | snprintf(infobuf, sizeof(infobuf) - 1, "%s S, RX %u", |
166 | 712 | tx_direction ? "->" : "<-", rx); |
167 | 712 | } |
168 | | |
169 | 945 | stats.type_s++; |
170 | 945 | } |
171 | 953 | break; |
172 | 10.6k | } |
173 | | |
174 | 10.6k | if (pdu_type != 0x0 /* Type I */) { |
175 | 1.94k | offset += len + 2; |
176 | 1.94k | stats.type_other++; |
177 | 1.94k | continue; |
178 | 1.94k | } |
179 | | |
180 | | /* From now on, only Type I packets are processed */ |
181 | 8.71k | memcpy(&last_i_apdu, &h->ts, sizeof(struct timeval)); |
182 | 8.71k | stats.type_i++; |
183 | | |
184 | 8.71k | if(((offset + 6) < payload_len) && (len >= 6 /* Ignore 4 bytes APDUs */)) { |
185 | 8.66k | u_int16_t rx_value, tx_value; |
186 | 8.66k | bool initial_run = ((rx_seq_num == 0) && (tx_seq_num == 0)) ? true : false; |
187 | | |
188 | 8.66k | tx_value = ((((u_int16_t)payload[offset + 2]) << 8) + payload[offset + 1]) >> 1; |
189 | 8.66k | rx_value = ((((u_int16_t)payload[offset + 4]) << 8) + payload[offset + 3]) >> 1; |
190 | | |
191 | 8.66k | if (!tx_direction) { |
192 | | /* Counters are swapped */ |
193 | 8.55k | u_int16_t v = rx_value; |
194 | | |
195 | 8.55k | rx_value = tx_value; |
196 | 8.55k | tx_value = v; |
197 | 8.55k | } |
198 | | |
199 | 8.66k | if ((tx_value == tx_seq_num) && (rx_value == rx_seq_num)) { |
200 | 42 | stats.retransmitted_msgs++; |
201 | 42 | lock.unlock(__FILE__, __LINE__); |
202 | 42 | return; |
203 | 42 | } |
204 | | |
205 | 8.62k | if (!initial_run) { |
206 | 7.27k | u_int32_t diff = abs(tx_value - (tx_seq_num + 1)); |
207 | | |
208 | | /* Check for id reset (16 bit only) */ |
209 | 7.27k | if (diff != 32768) pkt_lost.tx += diff; |
210 | 7.27k | } |
211 | 8.62k | tx_seq_num = tx_value; |
212 | | |
213 | 8.62k | if (!tx_direction) { |
214 | 8.53k | if (!initial_run) { |
215 | 7.20k | u_int32_t diff = abs(rx_value - rx_seq_num); |
216 | | |
217 | | /* Check for id reset (16 bit only) */ |
218 | 7.20k | if (diff != 32768) pkt_lost.rx += diff; |
219 | 7.20k | } |
220 | | |
221 | 8.53k | rx_value++; /* The next RX will be increased by 1 */ |
222 | 8.53k | } else { |
223 | 90 | if (!initial_run) { |
224 | 63 | u_int32_t diff = abs(rx_value - rx_seq_num); |
225 | | |
226 | | /* Check for id reset (16 bit only) */ |
227 | 63 | if (diff != 32768) pkt_lost.rx += diff; |
228 | 63 | } |
229 | 90 | } |
230 | 8.62k | rx_seq_num = rx_value; |
231 | | |
232 | | /* Skip magic(1), len(1), type/TX(2), RX(2) = 6 */ |
233 | 8.62k | len -= 6 /* Skip magic and len */, |
234 | 8.62k | offset += 5 /* magic already skept */; |
235 | | |
236 | 8.62k | if (payload_len >= (offset + len)) { |
237 | 8.53k | u_int8_t type_id = payload[offset]; |
238 | 8.53k | u_int8_t cause_tx = payload[offset + 1] & 0x3F; |
239 | 8.53k | u_int8_t negative = |
240 | 8.53k | ((payload[offset + 1] & 0x40) == 0x40) ? true : false; |
241 | 8.53k | u_int16_t asdu; |
242 | 8.53k | u_int64_t bit; |
243 | 8.53k | bool unexpected_typeid_alerted = false; |
244 | | |
245 | 8.53k | offset += len + 2 /* magic and len */; |
246 | | |
247 | 8.53k | if((len >= 6) && ((offset + 6) <= payload_len)) |
248 | 3.49k | asdu = /* ntohs */ (*((u_int16_t *)&payload[4 + offset])); |
249 | 5.03k | else |
250 | 5.03k | asdu = 0; |
251 | | |
252 | | #ifdef DEBUG_IEC60870 |
253 | | ntop->getTrace()->traceEvent( |
254 | | TRACE_WARNING, "[%s] TypeId %u [offset %u/%u]", __FUNCTION__, |
255 | | type_id, offset, payload_len); |
256 | | #endif |
257 | | |
258 | | #ifdef IEC60870_TRACE |
259 | | ntop->getTrace()->traceEvent( |
260 | | TRACE_NORMAL, |
261 | | "[%s] A-PDU I-%-3u [rx: %u][tx: %u][lost rx/tx: %u/%u]", |
262 | | tx_direction ? "->" : "<-", type_id, rx_seq_num, tx_seq_num, |
263 | | pkt_lost.rx, pkt_lost.tx); |
264 | | #endif |
265 | | |
266 | 8.53k | snprintf(infobuf, sizeof(infobuf) - 1, "%s I, RX %u, TX %u", |
267 | 8.53k | tx_direction ? "->" : "<-", rx_seq_num, tx_seq_num); |
268 | | |
269 | 8.53k | if (!initial_run) { |
270 | 7.21k | u_int32_t transition = (last_type_i << 8) + type_id; |
271 | | |
272 | 7.21k | it = type_i_transitions.find(transition); |
273 | | |
274 | 7.21k | if (it == type_i_transitions.end()) { |
275 | 3.86k | if (f->get_duration() > ntop->getPrefs()->getIEC60870LearingPeriod()) { |
276 | 377 | FlowAlert *alert = NULL; |
277 | 377 | u_int16_t c_score = 50, s_score = 10; |
278 | | |
279 | | #ifdef IEC60870_TRACE |
280 | | ntop->getTrace()->traceEvent(TRACE_NORMAL, |
281 | | "Found new transition %u -> %u", |
282 | | last_type_i, type_id); |
283 | | #endif |
284 | 377 | char key[128], rsp[64]; |
285 | 377 | snprintf(key, sizeof(key), CHECKS_IEC_INVALID_TRANSITION); |
286 | | |
287 | 377 | if ((!ntop->getRedis()->get(key, rsp, sizeof(rsp))) && |
288 | 377 | ((rsp[0] != '\0') && (!strcmp(rsp, "1")))) |
289 | 0 | alert = new IECInvalidTransitionAlert(NULL, f, |
290 | 0 | (struct timeval*)&h->ts, |
291 | 0 | last_type_i, type_id); |
292 | | |
293 | 377 | if (alert) { |
294 | 0 | alert->setCliSrvScores(c_score, s_score); |
295 | 0 | f->triggerAlert(alert, true); |
296 | 0 | } |
297 | | |
298 | 377 | type_i_transitions[transition] = 2; /* Post Learning */ |
299 | 377 | } else |
300 | 3.49k | type_i_transitions[transition] = 1; /* During Learning */ |
301 | 3.86k | } else |
302 | 3.34k | type_i_transitions[transition] = it->second + 1; |
303 | 7.21k | } |
304 | | |
305 | 8.53k | if (!initial_run) { |
306 | 7.21k | if (isMonitoringTypeId(last_type_i) && isMonitoringTypeId(type_id)) |
307 | 2.32k | transitions.m_to_m++; |
308 | 4.88k | else if (isMonitoringTypeId(last_type_i) && |
309 | 4.88k | isCommandTypeId(type_id)) |
310 | 462 | transitions.m_to_c++; |
311 | 4.42k | else if (isCommandTypeId(last_type_i) && |
312 | 4.42k | isMonitoringTypeId(type_id)) |
313 | 469 | transitions.c_to_m++; |
314 | 3.95k | else if (isCommandTypeId(last_type_i) && isCommandTypeId(type_id)) |
315 | 922 | transitions.c_to_c++; |
316 | | |
317 | 7.21k | if ((invalid_command_transition_detected == false) && |
318 | 7.21k | ((transitions.m_to_c > 20) || (transitions.c_to_m > 20) || |
319 | 6.93k | (transitions.c_to_c > 5))) { |
320 | | /* https://github.com/ntop/ntopng/issues/6598 */ |
321 | 36 | FlowAlert *alert = NULL; |
322 | 36 | u_int16_t c_score = CLIENT_ALERT_SCORE, |
323 | 36 | s_score = SERVER_ALERT_SCORE; |
324 | | |
325 | 36 | char key[128], rsp[64]; |
326 | 36 | snprintf(key, sizeof(key), CHECKS_IEC_INVALID_COMMAND_TRANSITION); |
327 | | |
328 | 36 | if ((!ntop->getRedis()->get(key, rsp, sizeof(rsp))) && |
329 | 36 | ((rsp[0] != '\0') && (!strcmp(rsp, "1")))) |
330 | 0 | alert = new IECInvalidCommandTransitionAlert(NULL, f, |
331 | 0 | (struct timeval*)&h->ts, transitions.m_to_c, |
332 | 0 | transitions.c_to_m, transitions.c_to_c); |
333 | | |
334 | 36 | if (alert) { |
335 | 0 | alert->setCliSrvScores(c_score, s_score); |
336 | 0 | f->triggerAlert(alert, true); |
337 | 0 | } |
338 | | |
339 | | // ntop->getTrace()->traceEvent(TRACE_WARNING, "*** INVALID |
340 | | // TRANSITION %u -> %u", last_type_i, type_id); |
341 | | |
342 | 36 | invalid_command_transition_detected = true; |
343 | 36 | } |
344 | 7.21k | } |
345 | | |
346 | 8.53k | last_type_i = type_id; |
347 | | |
348 | 8.53k | it = typeid_uses.find(type_id); |
349 | | |
350 | 8.53k | if (it == typeid_uses.end()) |
351 | 3.59k | typeid_uses[type_id] = 1; |
352 | 4.93k | else |
353 | 4.93k | typeid_uses[type_id] = it->second + 1; |
354 | | |
355 | 8.53k | if (type_id < 64) { |
356 | 5.26k | bit = ((u_int64_t)1) << type_id; |
357 | 5.26k | if ((allowedTypeIDs[0] & bit) == 0) |
358 | 0 | unexpected_typeid_alerted = true; |
359 | 5.26k | } else if (type_id < 128) { |
360 | 2.16k | bit = ((u_int64_t)1) << (type_id - 64); |
361 | | |
362 | 2.16k | if ((allowedTypeIDs[1] & bit) == 0) |
363 | 0 | unexpected_typeid_alerted = true; |
364 | 2.16k | } |
365 | | |
366 | 8.53k | if (unexpected_typeid_alerted) { |
367 | 0 | FlowAlert *alert = NULL; |
368 | 0 | u_int16_t c_score = CLIENT_ALERT_SCORE, |
369 | 0 | s_score = SERVER_ALERT_SCORE; |
370 | |
|
371 | 0 | char key[128], rsp[64]; |
372 | 0 | snprintf(key, sizeof(key), CHECKS_IEC_UNEXPECTED_TYPE_ID); |
373 | |
|
374 | 0 | if ((!ntop->getRedis()->get(key, rsp, sizeof(rsp))) && |
375 | 0 | ((rsp[0] != '\0') && (!strcmp(rsp, "1")))) |
376 | 0 | alert = new IECUnexpectedTypeIdAlert(NULL, f, type_id, asdu, |
377 | 0 | cause_tx, negative); |
378 | |
|
379 | 0 | if (alert) { |
380 | 0 | alert->setCliSrvScores(c_score, s_score); |
381 | 0 | f->triggerAlert(alert, true); |
382 | 0 | } |
383 | 0 | } /* unexpected_typeid_alerted */ |
384 | | /* Discard typeIds 127..255 */ |
385 | 8.53k | } else /* payload_len < len */ |
386 | 94 | break; |
387 | 8.62k | } else { |
388 | | // ntop->getTrace()->traceEvent(TRACE_WARNING, "*** short APDUs"); |
389 | 49 | break; |
390 | 49 | } |
391 | | |
392 | 8.53k | if((offset < payload_len) && (payload[offset] == 0x68 /* IEC magic byte */)) |
393 | 5.12k | offset += 1; /* We skip the initial magic byte */ |
394 | 3.40k | else { |
395 | | #ifdef DEBUG_IEC60870 |
396 | | ntop->getTrace()->traceEvent( |
397 | | TRACE_WARNING, "Skipping IEC entry: no magic byte @ offset %u", |
398 | | offset); |
399 | | #endif |
400 | 3.40k | break; |
401 | 3.40k | } |
402 | 8.53k | } /* while */ |
403 | | |
404 | 3.88k | lock.unlock(__FILE__, __LINE__); |
405 | 3.88k | } |
406 | 3.98k | } |
407 | | |
408 | | /* *************************************** */ |
409 | | |
410 | 0 | void IEC104Stats::lua(lua_State *vm) { |
411 | 0 | lua_newtable(vm); |
412 | |
|
413 | 0 | lock.rdlock(__FILE__, __LINE__); |
414 | | |
415 | | /* *************************** */ |
416 | |
|
417 | 0 | lua_newtable(vm); |
418 | |
|
419 | 0 | for (std::unordered_map<u_int16_t, u_int32_t>::iterator it = typeid_uses.begin(); |
420 | 0 | it != typeid_uses.end(); ++it) { |
421 | 0 | char buf[8]; |
422 | |
|
423 | 0 | snprintf(buf, sizeof(buf), "%u", it->first); |
424 | 0 | lua_push_int32_table_entry(vm, buf, it->second); |
425 | 0 | } |
426 | |
|
427 | 0 | lua_pushstring(vm, "typeid"); |
428 | 0 | lua_insert(vm, -2); |
429 | 0 | lua_settable(vm, -3); |
430 | | |
431 | | /* *************************** */ |
432 | |
|
433 | 0 | lua_newtable(vm); |
434 | |
|
435 | 0 | for (std::unordered_map<u_int16_t, u_int32_t>::iterator it = |
436 | 0 | type_i_transitions.begin(); |
437 | 0 | it != type_i_transitions.end(); ++it) { |
438 | 0 | char buf[8]; |
439 | |
|
440 | 0 | snprintf(buf, sizeof(buf), "%u,%u", (it->first >> 8), it->first & 0xFF); |
441 | 0 | lua_push_int32_table_entry(vm, buf, it->second); |
442 | 0 | } |
443 | |
|
444 | 0 | lua_pushstring(vm, "typeid_transitions"); |
445 | 0 | lua_insert(vm, -2); |
446 | 0 | lua_settable(vm, -3); |
447 | |
|
448 | 0 | lock.unlock(__FILE__, __LINE__); |
449 | | |
450 | | /* *************************** */ |
451 | |
|
452 | 0 | lua_newtable(vm); |
453 | 0 | lua_push_int32_table_entry(vm, "type_i", stats.type_i); |
454 | 0 | lua_push_int32_table_entry(vm, "type_s", stats.type_s); |
455 | 0 | lua_push_int32_table_entry(vm, "type_u", stats.type_u); |
456 | 0 | lua_push_int32_table_entry(vm, "type_other", stats.type_other); |
457 | 0 | lua_push_int32_table_entry(vm, "forward_msgs", stats.forward_msgs); |
458 | 0 | lua_push_int32_table_entry(vm, "reverse_msgs", stats.reverse_msgs); |
459 | 0 | lua_push_int32_table_entry(vm, "retransmitted_msgs", |
460 | 0 | stats.retransmitted_msgs); |
461 | 0 | lua_pushstring(vm, "stats"); |
462 | 0 | lua_insert(vm, -2); |
463 | 0 | lua_settable(vm, -3); |
464 | | |
465 | | /* *************************** */ |
466 | |
|
467 | 0 | lua_newtable(vm); |
468 | 0 | lua_push_int32_table_entry(vm, "rx", pkt_lost.rx); |
469 | 0 | lua_push_int32_table_entry(vm, "tx", pkt_lost.tx); |
470 | 0 | lua_pushstring(vm, "pkt_lost"); |
471 | 0 | lua_insert(vm, -2); |
472 | 0 | lua_settable(vm, -3); |
473 | | |
474 | | /* *************************** */ |
475 | |
|
476 | 0 | lua_newtable(vm); |
477 | 0 | lua_push_float_table_entry(vm, "average", ndpi_data_average(i_s_apdu)); |
478 | 0 | lua_push_float_table_entry(vm, "stddev", ndpi_data_stddev(i_s_apdu)); |
479 | 0 | lua_pushstring(vm, "ack_time"); |
480 | 0 | lua_insert(vm, -2); |
481 | 0 | lua_settable(vm, -3); |
482 | | |
483 | | /* *************************** */ |
484 | |
|
485 | 0 | lua_pushstring(vm, "iec104"); |
486 | 0 | lua_insert(vm, -2); |
487 | 0 | lua_settable(vm, -3); |
488 | 0 | } |
489 | | |
490 | | /* *************************************** */ |
491 | | |
492 | 0 | std::string IEC104Stats::getFlowInfo() { |
493 | 0 | lock.rdlock(__FILE__, __LINE__); |
494 | 0 | std::string info_field = std::string(infobuf); |
495 | 0 | lock.unlock(__FILE__, __LINE__); |
496 | |
|
497 | 0 | return info_field; |
498 | 0 | } |
499 | | |
500 | | /* *************************************** */ |
501 | | |
502 | 17.5k | bool IEC104Stats::isMonitoringTypeId(u_int16_t tid) { |
503 | 17.5k | return (((tid <= 40) || (tid == 70)) ? true : false); |
504 | 17.5k | } |
505 | | |
506 | | /* *************************************** */ |
507 | | |
508 | 10.9k | bool IEC104Stats::isCommandTypeId(u_int16_t tid) { |
509 | 10.9k | if (((tid >= 45) && (tid <= 64)) || ((tid >= 100) && (tid <= 107))) |
510 | 5.17k | return (true); |
511 | 5.80k | else |
512 | 5.80k | return (false); |
513 | 10.9k | } |
514 | | |
515 | | /* *************************************** */ |