1 //--------------------------------------------------------------------------
2 // Copyright (C) 2015-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18
19 // ha_test.cc authors Ed Borgoyn <eborgoyn@cisco.com>, Michael Altizer <mialtize@cisco.com>
20 // unit test main
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "flow/ha.cc"
27
28 #include <CppUTest/CommandLineTestRunner.h>
29 #include <CppUTest/TestHarness.h>
30
31 using namespace snort;
32
33 #define MSG_SIZE 100
34 #define TEST_KEY 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47
35
36 class StreamHAClient;
37
38 static const FlowKey s_test_key =
39 {
40 { 1, 2, 3, 4 },
41 { 5, 6, 7, 8 },
42 9,
43 10,
44 11,
45 0,
46 0,
47 0,
48 12,
49 14,
50 PktType::TCP,
51 14,
52 0,
53 0
54 };
55
56 static struct __attribute__((__packed__)) TestDeleteMessage {
57 HAMessageHeader mhdr;
58 FlowKey key;
59 } s_delete_message =
60 {
61 {
62 HA_DELETE_EVENT,
63 HA_MESSAGE_VERSION,
64 0x39,
65 KEY_TYPE_IP6
66 },
67 s_test_key
68 };
69
70 static struct __attribute__((__packed__)) TestUpdateMessage {
71 HAMessageHeader mhdr;
72 FlowKey key;
73 HAClientHeader schdr;
74 uint8_t scmsg[10];
75 } s_update_stream_message =
76 {
77 {
78 HA_UPDATE_EVENT,
79 HA_MESSAGE_VERSION,
80 0x45,
81 KEY_TYPE_IP6
82 },
83 s_test_key,
84 {
85 0,
86 10
87 },
88 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
89 };
90
91
92 static struct timeval s_packet_time = { 0, 0 };
93 static uint8_t s_message[MSG_SIZE];
94 static SideChannel s_side_channel;
95 static SCMessage s_sc_message;
96 static SCMessage s_rec_sc_message;
97 static bool s_stream_consume_called = false;
98 static uint8_t s_stream_consume_size = 0;
99 static bool s_other_consume_called = false;
100 static uint8_t s_other_consume_size = 0;
101 static bool s_get_session_called = false;
102 static bool s_delete_session_called = false;
103 static bool s_transmit_message_called = false;
104 static bool s_stream_update_required = false;
105 static bool s_other_update_required = false;
106 static uint8_t* s_message_content = nullptr;
107 static uint8_t s_message_length = 0;
108 static Flow s_flow;
109 static FlowKey s_flowkey;
110 static Packet s_pkt;
111 static Active active;
112 static StreamHAClient* s_ha_client;
113 static FlowHAClient* s_other_ha_client;
114 static std::function<void (SCMessage*)> s_handler = nullptr;
115 static SCMsgHdr s_sc_header = { 0, 1, 0, 0, };
116
117 class StreamHAClient : public FlowHAClient
118 {
119 public:
StreamHAClient()120 StreamHAClient() : FlowHAClient(10, true) { }
121 ~StreamHAClient() override = default;
consume(Flow * &,const FlowKey *,HAMessage & msg,uint8_t size)122 bool consume(Flow*&, const FlowKey*, HAMessage& msg, uint8_t size) override
123 {
124 s_stream_consume_called = true;
125 s_stream_consume_size = size;
126
127 for ( uint8_t i = 0; i < 10; i++ )
128 {
129 if (*msg.cursor != i)
130 return false;
131 msg.advance_cursor(sizeof(*msg.cursor));
132 }
133
134 return true;
135 }
produce(Flow &,HAMessage & msg)136 bool produce(Flow&, HAMessage& msg) override
137 {
138 if (!msg.fits(10))
139 return false;
140
141 for ( uint8_t i = 0; i < 10; i++ )
142 {
143 *msg.cursor = i;
144 msg.advance_cursor(sizeof(*msg.cursor));
145 }
146 return true;
147 }
is_update_required(Flow *)148 bool is_update_required(Flow*) override { return s_stream_update_required; }
149 };
150
151 class OtherHAClient : public FlowHAClient
152 {
153 public:
OtherHAClient()154 OtherHAClient() : FlowHAClient(5, false) { }
155 ~OtherHAClient() override = default;
consume(Flow * &,const FlowKey *,HAMessage & msg,uint8_t size)156 bool consume(Flow*&, const FlowKey*, HAMessage& msg, uint8_t size) override
157 {
158 s_other_consume_called = true;
159 s_other_consume_size = size;
160
161 for ( uint8_t i = 0; i < 5; i++ )
162 {
163 if (*msg.cursor != i)
164 return false;
165 msg.advance_cursor(sizeof(*msg.cursor));
166 }
167
168 return true;
169 }
produce(Flow &,HAMessage & msg)170 bool produce(Flow&, HAMessage& msg) override
171 {
172 if (!msg.fits(5))
173 return false;
174
175 for ( uint8_t i = 0; i < 5; i++ )
176 {
177 *msg.cursor = i;
178 msg.advance_cursor(sizeof(*msg.cursor));
179 }
180 return true;
181 }
is_update_required(Flow *)182 bool is_update_required(Flow*) override { return s_other_update_required; }
183 };
184
185 //-------------------------------------------------------------------------
186 // stubs, spies, etc.
187 //-------------------------------------------------------------------------
188
189 THREAD_LOCAL HAStats ha_stats = { };
190
get_flow(const FlowKey * flowkey)191 Flow* Stream::get_flow(const FlowKey* flowkey)
192 {
193 s_flowkey = *flowkey;
194 s_get_session_called = true;
195 return &s_flow;
196 }
197
Packet(bool)198 Packet::Packet(bool) { }
199 Packet::~Packet() = default;
200
delete_flow(const FlowKey * flowkey)201 void Stream::delete_flow(const FlowKey* flowkey)
202 {
203 s_flowkey = *flowkey;
204 s_delete_session_called = true;
205 }
206
207 namespace snort
208 {
ErrorMessage(const char *,...)209 void ErrorMessage(const char*,...) { }
LogMessage(const char *,...)210 void LogMessage(const char*,...) { }
211
packet_gettimeofday(struct timeval * tv)212 void packet_gettimeofday(struct timeval* tv)
213 { *tv = s_packet_time; }
214 }
215
is_equal(const void *,const void *,size_t)216 bool FlowKey::is_equal(const void*, const void*, size_t) { return false; }
217
ioctl(DAQ_IoctlCmd,void *,size_t)218 int SFDAQInstance::ioctl(DAQ_IoctlCmd, void*, size_t) { return DAQ_SUCCESS; }
219
Flow()220 Flow::Flow() { ha_state = new FlowHAState; key = new FlowKey; }
~Flow()221 Flow::~Flow() { delete key; delete ha_state; }
222
223 FlowStash::~FlowStash() = default;
224
set_client_initiate(Packet *)225 void Flow::set_client_initiate(Packet*) { }
set_direction(Packet *)226 void Flow::set_direction(Packet*) { }
227
get_side_channel(SCPort)228 SideChannel* SideChannelManager::get_side_channel(SCPort)
229 { return &s_side_channel; }
230
231 SideChannel::SideChannel() = default;
232
get_direction()233 Connector::Direction SideChannel::get_direction()
234 { return Connector::CONN_DUPLEX; }
235
set_default_port(SCPort)236 void SideChannel::set_default_port(SCPort) { }
237
register_receive_handler(const std::function<void (SCMessage *)> & handler)238 void SideChannel::register_receive_handler(const std::function<void (SCMessage*)>& handler)
239 {
240 s_handler = handler;
241 }
242
unregister_receive_handler()243 void SideChannel::unregister_receive_handler() { }
244
discard_message(SCMessage *)245 bool SideChannel::discard_message(SCMessage*)
246 { return true; }
247
process(int)248 bool SideChannel::process(int)
249 {
250 if ( s_handler && s_message_content && (s_message_length != 0))
251 {
252 s_rec_sc_message.content = s_message_content;
253 s_rec_sc_message.content_length = s_message_length;
254 s_rec_sc_message.hdr = &s_sc_header;
255 s_rec_sc_message.sc = &s_side_channel;
256 s_handler(&s_rec_sc_message);
257 return true;
258 }
259 else
260 return false;
261 }
262
transmit_message(SCMessage * msg)263 bool SideChannel::transmit_message(SCMessage* msg)
264 {
265 s_transmit_message_called = true;
266 s_message_content = msg->content;
267 s_message_length = msg->content_length;
268 return true;
269 }
270
alloc_transmit_message(uint32_t len)271 SCMessage* SideChannel::alloc_transmit_message(uint32_t len)
272 {
273 if ( len > MSG_SIZE )
274 return nullptr;
275
276 s_sc_message.content = s_message;
277 s_sc_message.content_length = len;
278 return &s_sc_message;
279 }
280
281 //-------------------------------------------------------------------------
282 // tests
283 //-------------------------------------------------------------------------
284
TEST_GROUP(high_availability_manager_test)285 TEST_GROUP(high_availability_manager_test)
286 {
287 void teardown() override
288 {
289 HighAvailabilityManager::term();
290 }
291 };
292
TEST(high_availability_manager_test,init_term)293 TEST(high_availability_manager_test, init_term)
294 {
295 HighAvailabilityConfig hac = { };
296 HighAvailabilityManager::configure(&hac);
297 HighAvailabilityManager::thread_init();
298 CHECK(HighAvailabilityManager::active()==false);
299 HighAvailabilityManager::thread_term();
300 CHECK(HighAvailabilityManager::active()==false);
301 }
302
TEST(high_availability_manager_test,inst_init_term)303 TEST(high_availability_manager_test, inst_init_term)
304 {
305 HighAvailabilityConfig hac;
306 hac.enabled = true;
307 hac.daq_channel = false;
308 hac.ports = new PortBitSet();
309 hac.ports->set(1);
310 hac.min_session_lifetime = { 1, 0 };
311 hac.min_sync_interval = { 0, 500000 };
312
313 HighAvailabilityManager::configure(&hac);
314 HighAvailabilityManager::thread_init();
315 s_ha_client = new StreamHAClient;
316 CHECK(HighAvailabilityManager::active()==true);
317 delete s_ha_client;
318 HighAvailabilityManager::thread_term();
319 CHECK(HighAvailabilityManager::active()==false);
320 }
321
TEST_GROUP(flow_ha_state_test)322 TEST_GROUP(flow_ha_state_test)
323 {
324 };
325
TEST(flow_ha_state_test,timing_test)326 TEST(flow_ha_state_test, timing_test)
327 {
328 struct timeval min_age = { 10, 0 }; // 10 second min age
329
330 FlowHAState::config_timers(min_age, min_age); // one-time config
331
332 s_packet_time.tv_sec = 1;
333 FlowHAState* state = new FlowHAState;
334 state->set_next_update(); // set the time for next update
335 s_packet_time.tv_sec = 2; // advance the clock to 2 seconds
336 CHECK(state->sync_interval_elapsed() == false);
337 delete state;
338
339 s_packet_time.tv_sec = 1;
340 state = new FlowHAState;
341 CHECK(state->sync_interval_elapsed() == false);
342 s_packet_time.tv_sec = 22; // advance the clock to 22 seconds
343 state->set_next_update(); // set the time for next update
344 CHECK(state->sync_interval_elapsed() == true);
345 delete state;
346
347 }
348
TEST(flow_ha_state_test,pending_test)349 TEST(flow_ha_state_test, pending_test)
350 {
351 FlowHAState state;
352
353 state.clear_pending(ALL_CLIENTS);
354 CHECK(state.check_pending(ALL_CLIENTS) == false);
355 state.set_pending(1);
356 CHECK(state.check_pending(1) == true);
357 state.clear_pending(1);
358 CHECK(state.check_pending(1) == false);
359 state.set_pending(1);
360 CHECK(state.check_pending(1) == true);
361 state.reset();
362 CHECK(state.check_pending(1) == false);
363 }
364
TEST(flow_ha_state_test,state_test)365 TEST(flow_ha_state_test, state_test)
366 {
367 FlowHAState state;
368
369 CHECK(state.check_any(FlowHAState::MODIFIED|FlowHAState::STANDBY|
370 FlowHAState::DELETED|FlowHAState::CRITICAL|FlowHAState::MAJOR) == false);
371 CHECK(state.check_any(FlowHAState::NEW) == true);
372 CHECK(state.check_any(FlowHAState::NEW_SESSION) == true);
373 state.add(FlowHAState::MODIFIED);
374 CHECK(state.check_any(FlowHAState::MODIFIED) == true);
375 state.set(FlowHAState::MODIFIED|FlowHAState::MAJOR);
376 CHECK(state.check_any(FlowHAState::MODIFIED) == true);
377 state.reset();
378 CHECK(state.check_any(FlowHAState::MODIFIED|FlowHAState::NEW|
379 FlowHAState::STANDBY|FlowHAState::DELETED|FlowHAState::NEW_SESSION|
380 FlowHAState::CRITICAL|FlowHAState::MAJOR) == false);
381 }
382
TEST_GROUP(high_availability_test)383 TEST_GROUP(high_availability_test)
384 {
385 void setup() override
386 {
387 memset(&ha_stats, 0, sizeof(ha_stats));
388
389 HighAvailabilityConfig hac;
390 hac.enabled = true;
391 hac.daq_channel = false;
392 hac.ports = new PortBitSet();
393 hac.ports->set(1);
394 hac.min_session_lifetime = { 1, 0 };
395 hac.min_sync_interval = { 0, 500000 };
396
397 HighAvailabilityManager::configure(&hac);
398 HighAvailabilityManager::thread_init();
399 s_ha_client = new StreamHAClient;
400 s_other_ha_client = new OtherHAClient;
401 }
402
403 void teardown() override
404 {
405 delete s_other_ha_client;
406 delete s_ha_client;
407 HighAvailabilityManager::thread_term();
408 HighAvailabilityManager::term();
409 }
410 };
411
TEST(high_availability_test,receive_deletion)412 TEST(high_availability_test, receive_deletion)
413 {
414 s_delete_session_called = false;
415 s_message_content = (uint8_t*) &s_delete_message;
416 s_message_length = sizeof(s_delete_message);
417 HighAvailabilityManager::process_receive();
418 CHECK(s_delete_session_called == true);
419 CHECK(memcmp((const void*)&s_flowkey, (const void*)&s_test_key, sizeof(s_test_key)) == 0);
420 }
421
TEST(high_availability_test,receive_update_stream_only)422 TEST(high_availability_test, receive_update_stream_only)
423 {
424 s_stream_consume_called = false;
425 s_stream_consume_size = 0;
426 s_message_content = (uint8_t*) &s_update_stream_message;
427 s_message_length = sizeof(s_update_stream_message);
428 HighAvailabilityManager::process_receive();
429 CHECK(s_stream_consume_called == true);
430 CHECK(s_stream_consume_size == 10);
431 CHECK(memcmp((const void*)&s_flowkey, (const void*)&s_test_key, sizeof(s_test_key)) == 0);
432 }
433
TEST(high_availability_test,transmit_deletion)434 TEST(high_availability_test, transmit_deletion)
435 {
436 s_transmit_message_called = false;
437 HighAvailabilityManager::process_deletion(s_flow);
438 CHECK(s_transmit_message_called == true);
439 }
440
TEST(high_availability_test,transmit_update_no_update)441 TEST(high_availability_test, transmit_update_no_update)
442 {
443 s_transmit_message_called = false;
444 s_stream_update_required = false;
445 s_other_update_required = false;
446 s_pkt.active = &active;
447 HighAvailabilityManager::process_update(&s_flow, &s_pkt);
448 CHECK(s_transmit_message_called == false);
449 }
450
TEST(high_availability_test,transmit_update_stream_only)451 TEST(high_availability_test, transmit_update_stream_only)
452 {
453 s_transmit_message_called = false;
454 s_stream_update_required = true;
455 s_other_update_required = false;
456 s_pkt.active = &active;
457 HighAvailabilityManager::process_update(&s_flow, &s_pkt);
458 CHECK(s_transmit_message_called == true);
459 }
460
TEST(high_availability_test,transmit_update_both_update)461 TEST(high_availability_test, transmit_update_both_update)
462 {
463 s_transmit_message_called = false;
464 s_stream_update_required = true;
465 s_other_update_required = true;
466 s_pkt.active = &active;
467 CHECK(s_other_ha_client->handle == 1);
468 s_flow.ha_state->set_pending(s_other_ha_client->handle);
469 HighAvailabilityManager::process_update(&s_flow, &s_pkt);
470 CHECK(s_transmit_message_called == true);
471 }
472
TEST(high_availability_test,read_flow_key_error_v4)473 TEST(high_availability_test, read_flow_key_error_v4)
474 {
475 HAMessageHeader hdr = { 0, 0, 0, KEY_TYPE_IP4 };
476 HAMessage msg((uint8_t*) &s_test_key, KEY_SIZE_IP4 / 2);
477 FlowKey key;
478
479 CHECK(read_flow_key(msg, &hdr, key) == 0);
480 CHECK(ha_stats.truncated_msgs == 1);
481 }
482
TEST(high_availability_test,read_flow_key_error_v6)483 TEST(high_availability_test, read_flow_key_error_v6)
484 {
485 HAMessageHeader hdr = { 0, 0, 0, KEY_TYPE_IP6 };
486 HAMessage msg((uint8_t*) &s_test_key, KEY_SIZE_IP6 / 2);
487 FlowKey key;
488
489 CHECK(read_flow_key(msg, &hdr, key) == 0);
490 CHECK(ha_stats.truncated_msgs == 1);
491 }
492
TEST(high_availability_test,read_flow_key_error_unknown)493 TEST(high_availability_test, read_flow_key_error_unknown)
494 {
495 HAMessageHeader hdr = { 0, 0, 0, 0x42 };
496 HAMessage msg((uint8_t*) &s_test_key, sizeof(s_test_key));
497 FlowKey key;
498
499 CHECK(read_flow_key(msg, &hdr, key) == 0);
500 CHECK(ha_stats.unknown_key_type == 1);
501 }
502
TEST(high_availability_test,consume_error_truncated_client_hdr)503 TEST(high_availability_test, consume_error_truncated_client_hdr)
504 {
505 HAClientHeader chdr = { 0, 0 };
506 HAMessage msg((uint8_t*) &chdr, sizeof(chdr) / 2);
507 FlowKey key;
508
509 consume_ha_update_message(msg, key, &s_pkt);
510 CHECK(ha_stats.update_msgs_consumed == 0);
511 CHECK(ha_stats.truncated_msgs == 1);
512 }
513
TEST(high_availability_test,consume_error_invalid_client_idx)514 TEST(high_availability_test, consume_error_invalid_client_idx)
515 {
516 HAClientHeader chdr = { 0x42, 0 };
517 HAMessage msg((uint8_t*) &chdr, sizeof(chdr));
518 FlowKey key;
519
520 consume_ha_update_message(msg, key, &s_pkt);
521 CHECK(ha_stats.update_msgs_consumed == 0);
522 CHECK(ha_stats.unknown_client_idx == 1);
523 }
524
TEST(high_availability_test,consume_error_truncated_client_msg)525 TEST(high_availability_test, consume_error_truncated_client_msg)
526 {
527 struct __attribute__((__packed__))
528 {
529 HAClientHeader chdr = { 0, 0x42 };
530 uint8_t cmsg[0x42 / 2] = { };
531 } input;
532 HAMessage msg((uint8_t*) &input, sizeof(input));
533 FlowKey key;
534
535 consume_ha_update_message(msg, key, &s_pkt);
536 CHECK(ha_stats.update_msgs_consumed == 0);
537 CHECK(ha_stats.truncated_msgs == 1);
538 }
539
TEST(high_availability_test,consume_error_client_consume)540 TEST(high_availability_test, consume_error_client_consume)
541 {
542 struct __attribute__((__packed__))
543 {
544 HAClientHeader chdr = { 0, 10 };
545 uint8_t cmsg[0x42 / 2] = { };
546 } input;
547 HAMessage msg((uint8_t*) &input, sizeof(input));
548 FlowKey key;
549
550 consume_ha_update_message(msg, key, &s_pkt);
551 CHECK(ha_stats.update_msgs_consumed == 0);
552 CHECK(ha_stats.client_consume_errors == 1);
553 }
554
TEST(high_availability_test,consume_error_key_mismatch)555 TEST(high_availability_test, consume_error_key_mismatch)
556 {
557 HAMessageHeader hdr[10] = { 0, HA_MESSAGE_VERSION, 0x32, KEY_TYPE_IP4 };
558 HAMessage msg((uint8_t*) &hdr, sizeof(hdr));
559
560 FlowKey packet_key;
561 FlowKey* key = &packet_key;
562 CHECK(consume_ha_message(msg, key, &s_pkt) == nullptr);
563 CHECK(ha_stats.key_mismatch == 1);
564 }
565
TEST(high_availability_test,consume_error_truncated_msg_hdr)566 TEST(high_availability_test, consume_error_truncated_msg_hdr)
567 {
568 HAMessageHeader hdr = { };
569 HAMessage msg((uint8_t*) &hdr, sizeof(hdr) / 2);
570
571 FlowKey* key = nullptr;
572 CHECK(consume_ha_message(msg, key, &s_pkt) == nullptr);
573 CHECK(ha_stats.truncated_msgs == 1);
574 }
575
TEST(high_availability_test,consume_error_version_mismatch)576 TEST(high_availability_test, consume_error_version_mismatch)
577 {
578 HAMessageHeader hdr = { 0, HA_MESSAGE_VERSION + 1, 0, 0 };
579 HAMessage msg((uint8_t*) &hdr, sizeof(hdr));
580
581 FlowKey* key = nullptr;
582 CHECK(consume_ha_message(msg, key, &s_pkt) == nullptr);
583 CHECK(ha_stats.msg_version_mismatch == 1);
584 }
585
TEST(high_availability_test,consume_error_length_mismatch)586 TEST(high_availability_test, consume_error_length_mismatch)
587 {
588 HAMessageHeader hdr = { 0, HA_MESSAGE_VERSION, 0x42, 0 };
589 HAMessage msg((uint8_t*) &hdr, sizeof(hdr));
590
591 FlowKey* key = nullptr;
592 CHECK(consume_ha_message(msg, key, &s_pkt) == nullptr);
593 CHECK(ha_stats.msg_length_mismatch == 1);
594 }
595
TEST(high_availability_test,produce_error_client_hdr_overflow)596 TEST(high_availability_test, produce_error_client_hdr_overflow)
597 {
598 uint8_t buffer[sizeof(HAClientHeader) / 2];
599 HAMessage msg(buffer, sizeof(buffer));
600 Flow flow;
601
602 write_update_msg_client(s_ha_client, flow, msg);
603 CHECK(msg.cursor == msg.buffer);
604 }
605
TEST(high_availability_test,produce_error_client_produce)606 TEST(high_availability_test, produce_error_client_produce)
607 {
608 uint8_t buffer[sizeof(HAClientHeader)];
609 HAMessage msg(buffer, sizeof(buffer));
610 Flow flow;
611
612 write_update_msg_client(s_ha_client, flow, msg);
613 CHECK(msg.cursor == msg.buffer);
614 }
615
main(int argc,char ** argv)616 int main(int argc, char** argv)
617 {
618 return CommandLineTestRunner::RunAllTests(argc, argv);
619 }
620
621