00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 // Copyright (C) 2007-2013 Sourcefire, Inc.
00004 //
00005 // This program is free software; you can redistribute it and/or modify it
00006 // under the terms of the GNU General Public License Version 2 as published
00007 // by the Free Software Foundation. You may not use, modify or distribute
00008 // this program under any other version of the GNU General Public License.
00009 //
00010 // This program is distributed in the hope that it will be useful, but
00011 // WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program; if not, write to the Free Software Foundation, Inc.,
00017 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 //--------------------------------------------------------------------------
00019
00020 /*
00021 ** @file detection_options.c
00022 ** @author Steven Sturges
00023 ** @brief Support functions for rule option tree
00024 **
00025 ** This implements tree processing for rule options, evaluating common
00026 ** detection options only once per pattern match.
00027 */
00028
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032
00033 #include "detection_options.h"
00034
00035 #include <string>
00036
00037 #include "filters/detection_filter.h"
00038 #include "framework/cursor.h"
00039 #include "hash/sfhashfcn.h"
00040 #include "hash/sfxhash.h"
00041 #include "ips_options/extract.h"
00042 #include "ips_options/ips_flowbits.h"
00043 #include "latency/packet_latency.h"
00044 #include "latency/rule_latency_state.h"
00045 #include "log/messages.h"
00046 #include "main/modules.h"
00047 #include "main/snort_config.h"
00048 #include "main/snort_debug.h"
00049 #include "main/thread_config.h"
00050 #include "managers/ips_manager.h"
00051 #include "parser/parser.h"
00052 #include "profiler/rule_profiler_defs.h"
00053 #include "protocols/packet_manager.h"
00054 #include "utils/util.h"
00055
00056 #include "detection_defines.h"
00057 #include "detection_engine.h"
00058 #include "detection_util.h"
00059 #include "detect_trace.h"
00060 #include "fp_create.h"
00061 #include "fp_detect.h"
00062 #include "ips_context.h"
00063 #include "pattern_match_data.h"
00064 #include "rules.h"
00065 #include "treenodes.h"
00066
00067 #define HASH_RULE_OPTIONS 16384
00068 #define HASH_RULE_TREE 8192
00069
00070 #define DETECTION_OPTION_EQUAL 0
00071 #define DETECTION_OPTION_NOT_EQUAL 1
00072
00073 struct detection_option_key_t
00074 {
00075 option_type_t option_type;
00076 void* option_data;
00077 };
00078
00079 // FIXIT-L find a better place for this
00080 static inline bool operator==(const struct timeval& a, const struct timeval& b)
00081 { return a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec; }
00082
00083 static uint32_t detection_option_hash_func(SFHASHFCN*, const unsigned char* k, int)
00084 {
00085 const detection_option_key_t* key = (const detection_option_key_t*)k;
00086
00087 if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
00088 {
00089 IpsOption* opt = (IpsOption*)key->option_data;
00090 return opt->hash();
00091 }
00092 return 0;
00093 }
00094
00095 static int detection_option_key_compare_func(const void* k1, const void* k2, size_t)
00096 {
00097 const detection_option_key_t* key1 = (const detection_option_key_t*)k1;
00098 const detection_option_key_t* key2 = (const detection_option_key_t*)k2;
00099
00100 if ( !key1 || !key2 )
00101 return DETECTION_OPTION_NOT_EQUAL;
00102
00103 if ( key1->option_type != key2->option_type )
00104 return DETECTION_OPTION_NOT_EQUAL;
00105
00106 if ( key1->option_type != RULE_OPTION_TYPE_LEAF_NODE )
00107 {
00108 IpsOption* opt1 = (IpsOption*)key1->option_data;
00109 IpsOption* opt2 = (IpsOption*)key2->option_data;
00110
00111 if ( *opt1 == *opt2 )
00112 return DETECTION_OPTION_EQUAL;
00113 }
00114 return DETECTION_OPTION_NOT_EQUAL;
00115 }
00116
00117 static int detection_hash_free_func(void* option_key, void*)
00118 {
00119 detection_option_key_t* key = (detection_option_key_t*)option_key;
00120
00121 if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
00122 {
00123 IpsOption* opt = (IpsOption*)key->option_data;
00124 IpsManager::delete_option(opt);
00125 }
00126 return 0;
00127 }
00128
00129 static SFXHASH* DetectionHashTableNew()
00130 {
00131 SFXHASH* doht = sfxhash_new(HASH_RULE_OPTIONS,
00132 sizeof(detection_option_key_t),
00133 0, /* Data size == 0, just store the ptr */
00134 0, /* Memcap */
00135 0, /* Auto node recovery */
00136 nullptr, /* Auto free function */
00137 detection_hash_free_func, /* User free function */
00138 1); /* Recycle nodes */
00139
00140 if (doht == nullptr)
00141 FatalError("Failed to create rule detection option hash table");
00142
00143 sfxhash_set_keyops(doht, detection_option_hash_func, detection_option_key_compare_func);
00144
00145 return doht;
00146 }
00147
00148 void DetectionHashTableFree(SFXHASH* doht)
00149 {
00150 if (doht != nullptr)
00151 sfxhash_delete(doht);
00152 }
00153
00154 void* add_detection_option(SnortConfig* sc, option_type_t type, void* option_data)
00155 {
00156 if ( !sc->detection_option_hash_table )
00157 sc->detection_option_hash_table = DetectionHashTableNew();
00158
00159 detection_option_key_t key;
00160 key.option_type = type;
00161 key.option_data = option_data;
00162
00163 if ( void* p = sfxhash_find(sc->detection_option_hash_table, &key) )
00164 return p;
00165
00166 sfxhash_add(sc->detection_option_hash_table, &key, option_data);
00167 return nullptr;
00168 }
00169
00170 static uint32_t detection_option_tree_hash(detection_option_tree_node_t* node)
00171 {
00172 uint32_t a,b,c;
00173 int i;
00174
00175 if (!node)
00176 return 0;
00177
00178 a = b = c = 0;
00179
00180 for (i=0; i<node->num_children; i++)
00181 {
00182 #if (defined(__ia64) || defined(__amd64) || defined(_LP64))
00183 {
00184 /* Cleanup warning because of cast from 64bit ptr to 32bit int
00185 * warning on 64bit OSs */
00186 uint64_t ptr; /* Addresses are 64bits */
00187 ptr = (uint64_t)node->children[i]->option_data;
00188 a += (ptr >> 32);
00189 b += (ptr & 0xFFFFFFFF);
00190 }
00191 #else
00192 a += (uint32_t)node->children[i]->option_data;
00193 b += 0;
00194 #endif
00195 c += detection_option_tree_hash(node->children[i]);
00196 mix(a,b,c);
00197 a += node->children[i]->num_children;
00198 mix(a,b,c);
00199 #if 0
00200 a += (uint32_t)node->children[i]->option_data;
00201 /* Recurse & hash up this guy's children */
00202 b += detection_option_tree_hash(node->children[i]);
00203 c += node->children[i]->num_children;
00204 mix(a,b,c);
00205 #endif
00206 }
00207
00208 finalize(a,b,c);
00209
00210 return c;
00211 }
00212
00213 static uint32_t detection_option_tree_hash_func(SFHASHFCN*, const unsigned char* k, int)
00214 {
00215 const detection_option_key_t* key = (const detection_option_key_t*)k;
00216 detection_option_tree_node_t* node;
00217
00218 if (!key || !key->option_data)
00219 return 0;
00220
00221 node = (detection_option_tree_node_t*)key->option_data;
00222
00223 return detection_option_tree_hash(node);
00224 }
00225
00226 static bool detection_option_tree_compare(
00227 const detection_option_tree_node_t* r, const detection_option_tree_node_t* l)
00228 {
00229 if ( !r and !l )
00230 return DETECTION_OPTION_EQUAL;
00231
00232 if ( !r or !l )
00233 return DETECTION_OPTION_NOT_EQUAL;
00234
00235 if ( r->option_data != l->option_data )
00236 return DETECTION_OPTION_NOT_EQUAL;
00237
00238 if ( r->num_children != l->num_children )
00239 return DETECTION_OPTION_NOT_EQUAL;
00240
00241 for ( int i=0; i<r->num_children; i++ )
00242 {
00243 /* Recurse & check the children for equality */
00244 int ret = detection_option_tree_compare(r->children[i], l->children[i]);
00245
00246 if ( ret != DETECTION_OPTION_EQUAL )
00247 return ret;
00248 }
00249
00250 return DETECTION_OPTION_EQUAL;
00251 }
00252
00253 static int detection_option_tree_compare_func(const void* k1, const void* k2, size_t)
00254 {
00255 const detection_option_key_t* key_r = (const detection_option_key_t*)k1;
00256 const detection_option_key_t* key_l = (const detection_option_key_t*)k2;
00257
00258 if ( !key_r or !key_l )
00259 return DETECTION_OPTION_NOT_EQUAL;
00260
00261 const detection_option_tree_node_t* r = (const detection_option_tree_node_t*)key_r->option_data;
00262 const detection_option_tree_node_t* l = (const detection_option_tree_node_t*)key_l->option_data;
00263
00264 return detection_option_tree_compare(r, l);
00265 }
00266
00267 static int detection_option_tree_free_func(void*, void* data)
00268 {
00269 detection_option_tree_node_t* node = (detection_option_tree_node_t*)data;
00270 free_detection_option_tree(node);
00271 return 0;
00272 }
00273
00274 void DetectionTreeHashTableFree(SFXHASH* dtht)
00275 {
00276 if (dtht != nullptr)
00277 sfxhash_delete(dtht);
00278 }
00279
00280 static SFXHASH* DetectionTreeHashTableNew()
00281 {
00282 SFXHASH* dtht = sfxhash_new(
00283 HASH_RULE_TREE,
00284 sizeof(detection_option_key_t),
00285 0, /* Data size == 0, just store the ptr */
00286 0, /* Memcap */
00287 0, /* Auto node recovery */
00288 nullptr, /* Auto free function */
00289 detection_option_tree_free_func, /* User free function */
00290 1); /* Recycle nodes */
00291
00292 if (dtht == nullptr)
00293 FatalError("Failed to create rule detection option hash table");
00294
00295 sfxhash_set_keyops(dtht, detection_option_tree_hash_func, detection_option_tree_compare_func);
00296
00297 return dtht;
00298 }
00299
00300 void print_option_tree(detection_option_tree_node_t* node, int level)
00301 {
00302 #ifdef DEBUG_OPTION_TREE
00303 char buf[32];
00304 const char* opt;
00305
00306 if ( node->option_type != RULE_OPTION_TYPE_LEAF_NODE )
00307 opt = ((IpsOption*)node->option_data)->get_name();
00308 else
00309 {
00310 const OptTreeNode* otn = (OptTreeNode*)node->option_data;
00311 const SigInfo& si = otn->sigInfo;
00312 snprintf(buf, sizeof(buf), "%u:%u:%u", si.gid, si.sid, si.rev);
00313 opt = buf;
00314 }
00315
00316 unsigned int indent = level + strlen(opt);
00317
00318 DebugFormatNoFileLine(DEBUG_DETECT, "%3d %3d %p %*s\n",
00319 level, node->num_children, node->option_data, indent, opt);
00320
00321 for ( int i=0; i<node->num_children; i++ )
00322 print_option_tree(node->children[i], level+1);
00323 #else
00324 UNUSED(node);
00325 UNUSED(level);
00326 #endif
00327 }
00328
00329 void* add_detection_option_tree(SnortConfig* sc, detection_option_tree_node_t* option_tree)
00330 {
00331 if ( !sc->detection_option_tree_hash_table )
00332 sc->detection_option_tree_hash_table = DetectionTreeHashTableNew();
00333
00334 detection_option_key_t key;
00335 key.option_data = (void*)option_tree;
00336 key.option_type = RULE_OPTION_TYPE_LEAF_NODE;
00337
00338 if ( void* p = sfxhash_find(sc->detection_option_tree_hash_table, &key) )
00339 return p;
00340
00341 sfxhash_add(sc->detection_option_tree_hash_table, &key, option_tree);
00342 return nullptr;
00343 }
00344
00345 int detection_option_node_evaluate(
00346 detection_option_tree_node_t* node, detection_option_eval_data_t* eval_data,
00347 Cursor& orig_cursor)
00348 {
00349 // need node->state to do perf profiling
00350 if ( !node )
00351 return 0;
00352
00353 auto& state = node->state[get_instance_id()];
00354 RuleContext profile(state);
00355
00356 int result = 0;
00357 int rval = DETECTION_OPTION_NO_MATCH;
00358 char tmp_noalert_flag = 0;
00359 Cursor cursor = orig_cursor;
00360 bool continue_loop = true;
00361 char flowbits_setoperation = 0;
00362 int loop_count = 0;
00363 uint32_t tmp_byte_extract_vars[NUM_IPS_OPTIONS_VARS];
00364 uint64_t cur_eval_context_num = DetectionEngine::get_context()->context_num;
00365
00366 if ( !eval_data || !eval_data->p || !eval_data->pomd )
00367 return 0;
00368
00369 node_eval_trace(node, cursor);
00370
00371 auto p = eval_data->p;
00372 auto pomd = eval_data->pomd;
00373
00374 // see if evaluated it before ...
00375 if ( !node->is_relative )
00376 {
00377 auto last_check = state.last_check;
00378
00379 if ( last_check.ts == p->pkth->ts &&
00380 last_check.run_num == get_run_num() &&
00381 last_check.context_num == cur_eval_context_num &&
00382 last_check.rebuild_flag == (p->packet_flags & PKT_REBUILT_STREAM) &&
00383 !(p->packet_flags & PKT_ALLOW_MULTIPLE_DETECT) )
00384 {
00385 if ( !last_check.flowbit_failed &&
00386 !(p->packet_flags & PKT_IP_RULE_2ND) &&
00387 !(p->proto_bits & (PROTO_BIT__TEREDO|PROTO_BIT__GTP)) )
00388 {
00389 trace_log(detection, TRACE_RULE_EVAL,
00390 "Was evaluated before, returning last check result\n");
00391 return last_check.result;
00392 }
00393 }
00394 }
00395
00396 state.last_check.ts = eval_data->p->pkth->ts;
00397 state.last_check.run_num = get_run_num();
00398 state.last_check.context_num = cur_eval_context_num;
00399 state.last_check.flowbit_failed = 0;
00400 state.last_check.rebuild_flag = p->packet_flags & PKT_REBUILT_STREAM;
00401
00402 // Save some stuff off for repeated pattern tests
00403 bool try_again = false;
00404 PmdLastCheck* content_last = nullptr;
00405
00406 if ( node->option_type != RULE_OPTION_TYPE_LEAF_NODE )
00407 {
00408 IpsOption* opt = (IpsOption*)node->option_data;
00409 try_again = opt->retry();
00410
00411 PatternMatchData* pmd = opt->get_pattern(0, RULE_WO_DIR);
00412
00413 if ( pmd and pmd->last_check )
00414 content_last = pmd->last_check + get_instance_id();
00415 }
00416
00417 // No, haven't evaluated this one before... Check it.
00418 do
00419 {
00420 switch ( node->option_type )
00421 {
00422 case RULE_OPTION_TYPE_LEAF_NODE:
00423 // Add the match for this otn to the queue.
00424 {
00425 OptTreeNode* otn = (OptTreeNode*)node->option_data;
00426 int16_t app_proto = p->get_application_protocol();
00427 int check_ports = 1;
00428
00429 if ( app_proto and ((OtnxMatchData*)(pomd))->check_ports != 2 )
00430 {
00431 auto sig_info = otn->sigInfo;
00432
00433 for ( unsigned svc_idx = 0; svc_idx < sig_info.num_services; ++svc_idx )
00434 {
00435 if ( app_proto == sig_info.services[svc_idx].service_ordinal )
00436 {
00437 check_ports = 0;
00438 break; // out of for
00439 }
00440 }
00441
00442 if (sig_info.num_services && check_ports)
00443 {
00444 // none of the services match
00445 DebugFormat(DEBUG_DETECT,
00446 "[**] SID %u not matched because of service mismatch (%d!=%d [**]\n",
00447 sig_info.sid, app_proto, sig_info.services[0].service_ordinal);
00448 trace_logf(detection, TRACE_RULE_EVAL,
00449 "SID %u not matched because of service mismatch %d!=%d \n",
00450 sig_info.sid, app_proto, sig_info.services[0].service_ordinal);
00451 break; // out of case
00452 }
00453 }
00454
00455 int eval_rtn_result = 0;
00456
00457 // Don't include RTN time
00458 {
00459 RulePause pause(profile);
00460 eval_rtn_result = fpEvalRTN(getRuntimeRtnFromOtn(otn), p, check_ports);
00461 }
00462
00463 if ( eval_rtn_result )
00464 {
00465 bool f_result = true;
00466
00467 if ( otn->detection_filter )
00468 {
00469 trace_log(detection, TRACE_RULE_EVAL,
00470 "Evaluating detection filter\n");
00471 f_result = !detection_filter_test(otn->detection_filter,
00472 p->ptrs.ip_api.get_src(), p->ptrs.ip_api.get_dst(),
00473 p->pkth->ts.tv_sec);
00474 }
00475
00476 if ( f_result )
00477 {
00478 otn->state[get_instance_id()].matches++;
00479
00480 if ( !eval_data->flowbit_noalert )
00481 {
00482 PatternMatchData* pmd = (PatternMatchData*)eval_data->pmd;
00483 int pattern_size = pmd ? pmd->pattern_size : 0;
00484 #ifdef DEBUG_MSGS
00485 const SigInfo& si = otn->sigInfo;
00486 trace_logf(detection, TRACE_RULE_EVAL,
00487 "Matched rule gid:sid:rev %u:%u:%u\n", si.gid, si.sid, si.rev);
00488 #endif
00489
00490 fpAddMatch((OtnxMatchData*)pomd, pattern_size, otn);
00491 }
00492 result = rval = DETECTION_OPTION_MATCH;
00493 }
00494 }
00495 #ifdef DEBUG_MSGS
00496 else
00497 trace_log(detection, TRACE_RULE_EVAL, "Header check failed\n");
00498 #endif
00499
00500 break;
00501 }
00502
00503 case RULE_OPTION_TYPE_CONTENT:
00504 if ( node->evaluate )
00505 {
00506 // This will be set in the fast pattern matcher if we found
00507 // a content and the rule option specifies not that
00508 // content. Essentially we've already evaluated this rule
00509 // option via the content option processing since only not
00510 // contents that are not relative in any way will have this
00511 // flag set
00512 if ( content_last )
00513 {
00514 if ( content_last->ts == p->pkth->ts &&
00515 content_last->run_num == get_run_num() &&
00516 content_last->context_num == cur_eval_context_num &&
00517 content_last->rebuild_flag == (p->packet_flags & PKT_REBUILT_STREAM) )
00518 {
00519 rval = DETECTION_OPTION_NO_MATCH;
00520 break;
00521 }
00522 }
00523
00524 rval = node->evaluate(node->option_data, cursor, p);
00525 }
00526
00527 break;
00528
00529 case RULE_OPTION_TYPE_FLOWBIT:
00530 if ( node->evaluate )
00531 {
00532 flowbits_setoperation =
00533 FlowBits_SetOperation(node->option_data);
00534
00535 if ( flowbits_setoperation )
00536 // set to match so we don't bail early
00537 rval = DETECTION_OPTION_MATCH;
00538
00539 else
00540 rval = node->evaluate(node->option_data, cursor, eval_data->p);
00541 }
00542
00543 break;
00544
00545 default:
00546 if ( node->evaluate )
00547 rval = node->evaluate(node->option_data, cursor, p);
00548
00549 break;
00550 }
00551
00552 if ( rval == DETECTION_OPTION_NO_MATCH )
00553 {
00554 trace_log(detection, TRACE_RULE_EVAL, "no match\n");
00555 state.last_check.result = result;
00556 return result;
00557 }
00558 else if ( rval == DETECTION_OPTION_FAILED_BIT )
00559 {
00560 trace_log(detection, TRACE_RULE_EVAL, "failed bit\n");
00561 eval_data->flowbit_failed = 1;
00562 // clear the timestamp so failed flowbit gets eval'd again
00563 state.last_check.flowbit_failed = 1;
00564 state.last_check.result = result;
00565 return 0;
00566 }
00567 else if ( rval == DETECTION_OPTION_NO_ALERT )
00568 {
00569 // Cache the current flowbit_noalert flag, and set it
00570 // so nodes below this don't alert.
00571 tmp_noalert_flag = eval_data->flowbit_noalert;
00572 eval_data->flowbit_noalert = 1;
00573 trace_log(detection, TRACE_RULE_EVAL, "flowbit no alert\n");
00574 }
00575
00576 // Back up byte_extract vars so they don't get overwritten between rules
00577 trace_log(detection, TRACE_RULE_VARS, "Rule options variables: \n");
00578 for ( int i = 0; i < NUM_IPS_OPTIONS_VARS; ++i )
00579 {
00580 GetVarValueByIndex(&(tmp_byte_extract_vars[i]), (int8_t)i);
00581 trace_logf_wo_name(detection, TRACE_RULE_VARS, "var[%d]=%d ", i,
00582 tmp_byte_extract_vars[i]);
00583 }
00584 trace_log_wo_name(detection, TRACE_RULE_VARS, "\n");
00585
00586 if ( PacketLatency::fastpath() )
00587 {
00588 profile.stop(result != DETECTION_OPTION_NO_MATCH);
00589 state.last_check.result = result;
00590 return result;
00591 }
00592
00593 {
00594 RulePause pause(profile);
00595 // Passed, check the children.
00596 if ( node->num_children )
00597 {
00598 for ( int i = 0; i < node->num_children; ++i )
00599 {
00600 detection_option_tree_node_t* child_node =
00601 node->children[i];
00602
00603 dot_node_state_t* child_state =
00604 child_node->state + get_instance_id();
00605
00606 for ( int j = 0; j < NUM_IPS_OPTIONS_VARS; ++j )
00607 SetVarValueByIndex(tmp_byte_extract_vars[j], (int8_t)j);
00608
00609 if ( loop_count > 0 )
00610 {
00611 if ( child_state->result == DETECTION_OPTION_NO_MATCH )
00612 {
00613 if ( !child_node->is_relative )
00614 {
00615 if ( child_node->option_type == RULE_OPTION_TYPE_CONTENT )
00616 {
00617 // If it's a non-relative content or pcre, no reason
00618 // to check again. Only increment result once.
00619 // Should hit this condition on first loop iteration.
00620 if ( loop_count == 1 )
00621 ++result;
00622
00623 continue;
00624 }
00625 }
00626 else if ( child_node->option_type == RULE_OPTION_TYPE_CONTENT )
00627 {
00628 // Check for an unbounded relative search. If this
00629 // failed before, it's going to fail again so don't
00630 // go down this path again
00631 IpsOption* opt = (IpsOption*)node->option_data;
00632 PatternMatchData* pmd = opt->get_pattern(0, RULE_WO_DIR);
00633
00634 if ( pmd->is_unbounded() )
00635 {
00636 // Only increment result once. Should hit this
00637 // condition on first loop iteration
00638 if (loop_count == 1)
00639 ++result;
00640
00641 continue;
00642 }
00643 }
00644 }
00645 else if ( child_node->option_type == RULE_OPTION_TYPE_LEAF_NODE )
00646 // Leaf node matched, don't eval again
00647 continue;
00648
00649 else if ( child_state->result == child_node->num_children )
00650 // This branch of the tree matched or has options that
00651 // don't need to be evaluated again, so don't need to
00652 // evaluate this option again
00653 continue;
00654 }
00655
00656 child_state->result = detection_option_node_evaluate(
00657 node->children[i], eval_data, cursor);
00658
00659 if ( child_node->option_type == RULE_OPTION_TYPE_LEAF_NODE )
00660 // Leaf node won't have any children but will return success
00661 // or failure
00662 result += child_state->result;
00663
00664 else if (child_state->result == child_node->num_children)
00665 // Indicate that the child's tree branches are done
00666 ++result;
00667
00668 if ( PacketLatency::fastpath() )
00669 {
00670 state.last_check.result = result;
00671 return result;
00672 }
00673 }
00674
00675 // If all children branches matched, we don't need to reeval any of
00676 // the children so don't need to reeval this content/pcre rule
00677 // option at a new offset.
00678 // Else, reset the DOE ptr to last eval for offset/depth,
00679 // distance/within adjustments for this same content/pcre
00680 // rule option
00681 if ( result == node->num_children )
00682 continue_loop = false;
00683
00684 // Don't need to reset since it's only checked after we've gone
00685 // through the loop at least once and the result will have
00686 // been set again already
00687 //for (i = 0; i < node->num_children; i++)
00688 // node->children[i]->result;
00689 }
00690 }
00691
00692 if ( rval == DETECTION_OPTION_NO_ALERT )
00693 {
00694 // Reset the flowbit_noalert flag in eval data
00695 eval_data->flowbit_noalert = tmp_noalert_flag;
00696 }
00697
00698 if ( continue_loop &&
00699 rval == DETECTION_OPTION_MATCH &&
00700 node->relative_children )
00701 {
00702 continue_loop = try_again;
00703 }
00704 else
00705 continue_loop = false;
00706
00707 // We're essentially checking this node again and it potentially
00708 // might match again
00709 if ( continue_loop )
00710 state.checks++;
00711
00712 loop_count++;
00713 }
00714 while ( continue_loop );
00715
00716 if ( flowbits_setoperation && result == DETECTION_OPTION_MATCH )
00717 {
00718 // Do any setting/clearing/resetting/toggling of flowbits here
00719 // given that other rule options matched
00720 rval = node->evaluate(node->option_data, cursor, p);
00721 if ( rval != DETECTION_OPTION_MATCH )
00722 result = rval;
00723 }
00724
00725 if ( eval_data->flowbit_failed )
00726 {
00727 // something deeper in the tree failed a flowbit test, we may need to
00728 // reeval this node
00729 state.last_check.flowbit_failed = 1;
00730 }
00731
00732 state.last_check.result = result;
00733
00734 profile.stop(result != DETECTION_OPTION_NO_MATCH);
00735
00736 return result;
00737 }
00738
00739 struct node_profile_stats
00740 {
00741 // FIXIT-L duplicated from dot_node_state_t and OtnState
00742 hr_duration elapsed;
00743 hr_duration elapsed_match;
00744 hr_duration elapsed_no_match;
00745 uint64_t checks;
00746 uint64_t latency_timeouts;
00747 uint64_t latency_suspends;
00748 };
00749
00750 static void detection_option_node_update_otn_stats(detection_option_tree_node_t* node,
00751 node_profile_stats* stats, uint64_t checks, uint64_t timeouts, uint64_t suspends)
00752 {
00753 node_profile_stats local_stats; /* cumulative stats for this node */
00754 node_profile_stats node_stats; /* sum of all instances */
00755
00756 memset(&node_stats, 0, sizeof(node_stats));
00757
00758 for ( unsigned i = 0; i < ThreadConfig::get_instance_max(); ++i )
00759 {
00760 node_stats.elapsed += node->state[i].elapsed;
00761 node_stats.elapsed_match += node->state[i].elapsed_match;
00762 node_stats.elapsed_no_match += node->state[i].elapsed_no_match;
00763 node_stats.checks += node->state[i].checks;
00764 }
00765
00766 if ( stats )
00767 {
00768 local_stats.elapsed = stats->elapsed + node_stats.elapsed;
00769 local_stats.elapsed_match = stats->elapsed_match + node_stats.elapsed_match;
00770 local_stats.elapsed_no_match = stats->elapsed_no_match + node_stats.elapsed_no_match;
00771
00772 if (node_stats.checks > stats->checks)
00773 local_stats.checks = node_stats.checks;
00774 else
00775 local_stats.checks = stats->checks;
00776
00777 local_stats.latency_timeouts = timeouts;
00778 local_stats.latency_suspends = suspends;
00779 }
00780 else
00781 {
00782 local_stats.elapsed = node_stats.elapsed;
00783 local_stats.elapsed_match = node_stats.elapsed_match;
00784 local_stats.elapsed_no_match = node_stats.elapsed_no_match;
00785 local_stats.checks = node_stats.checks;
00786 local_stats.latency_timeouts = timeouts;
00787 local_stats.latency_suspends = suspends;
00788 }
00789
00790 if ( node->option_type == RULE_OPTION_TYPE_LEAF_NODE )
00791 {
00792 // Update stats for this otn
00793 // FIXIT-L call from packet threads at exit or total *all* states by main thread
00794 // Right now, it looks like we're missing out on some stats although it's possible
00795 // that this is "corrected" in the profiler code
00796 auto* otn = (OptTreeNode*)node->option_data;
00797 auto& state = otn->state[get_instance_id()];
00798
00799 state.elapsed += local_stats.elapsed;
00800 state.elapsed_match += local_stats.elapsed_match;
00801 state.elapsed_no_match += local_stats.elapsed_no_match;
00802
00803 if (local_stats.checks > state.checks)
00804 state.checks = local_stats.checks;
00805
00806 state.latency_timeouts += local_stats.latency_timeouts;
00807 state.latency_suspends += local_stats.latency_suspends;
00808 }
00809
00810 if ( node->num_children )
00811 {
00812 for ( int i=0; i < node->num_children; ++i )
00813 detection_option_node_update_otn_stats(node->children[i], &local_stats, checks,
00814 timeouts, suspends);
00815 }
00816 }
00817
00818 void detection_option_tree_update_otn_stats(SFXHASH* doth)
00819 {
00820 if ( !doth )
00821 return;
00822
00823 for ( auto hnode = sfxhash_findfirst(doth); hnode; hnode = sfxhash_findnext(doth) )
00824 {
00825 auto* node = (detection_option_tree_node_t*)hnode->data;
00826 assert(node);
00827
00828 uint64_t checks = 0;
00829 uint64_t timeouts = 0;
00830 uint64_t suspends = 0;
00831
00832 for ( unsigned i = 0; i < ThreadConfig::get_instance_max(); ++i )
00833 {
00834 checks += node->state[i].checks;
00835 timeouts += node->state[i].latency_timeouts;
00836 suspends += node->state[i].latency_suspends;
00837 }
00838
00839 if ( checks )
00840 detection_option_node_update_otn_stats(node, nullptr, checks, timeouts, suspends);
00841 }
00842 }
00843
00844 detection_option_tree_root_t* new_root(OptTreeNode* otn)
00845 {
00846 detection_option_tree_root_t* p = (detection_option_tree_root_t*)
00847 snort_calloc(sizeof(detection_option_tree_root_t));
00848
00849 p->latency_state = new RuleLatencyState[ThreadConfig::get_instance_max()]();
00850 p->otn = otn;
00851
00852 return p;
00853 }
00854
00855 void free_detection_option_root(void** existing_tree)
00856 {
00857 detection_option_tree_root_t* root;
00858
00859 if (!existing_tree || !*existing_tree)
00860 return;
00861
00862 root = (detection_option_tree_root_t*)*existing_tree;
00863 snort_free(root->children);
00864
00865 delete[] root->latency_state;
00866 snort_free(root);
00867 *existing_tree = nullptr;
00868 }
00869
00870 detection_option_tree_node_t* new_node(option_type_t type, void* data)
00871 {
00872 detection_option_tree_node_t* p =
00873 (detection_option_tree_node_t*)snort_calloc(sizeof(*p));
00874
00875 p->option_type = type;
00876 p->option_data = data;
00877
00878 p->state = (dot_node_state_t*)
00879 snort_calloc(ThreadConfig::get_instance_max(), sizeof(*p->state));
00880
00881 return p;
00882 }
00883
00884 void free_detection_option_tree(detection_option_tree_node_t* node)
00885 {
00886 int i;
00887 for (i=0; i<node->num_children; i++)
00888 {
00889 free_detection_option_tree(node->children[i]);
00890 }
00891 snort_free(node->children);
00892 snort_free(node->state);
00893 snort_free(node);
00894 }
00895
END OF CODE