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