1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "fp_utils.h"
24
25 #include <cassert>
26 #include <cstring>
27 #include <fstream>
28 #include <iomanip>
29 #include <list>
30 #include <mutex>
31 #include <sstream>
32 #include <thread>
33
34 #include "framework/mpse.h"
35 #include "framework/mpse_batch.h"
36 #include "hash/ghash.h"
37 #include "log/messages.h"
38 #include "main/snort_config.h"
39 #include "parser/parse_conf.h"
40 #include "pattern_match_data.h"
41 #include "ports/port_group.h"
42 #include "ports/port_table.h"
43 #include "ports/rule_port_tables.h"
44 #include "target_based/snort_protocols.h"
45 #include "treenodes.h"
46 #include "utils/util.h"
47
48 #include "service_map.h"
49
50 #ifdef UNIT_TEST
51 #include "catch/snort_catch.h"
52 #endif
53
54 using namespace snort;
55
56 //--------------------------------------------------------------------------
57 // private utilities
58 //--------------------------------------------------------------------------
59
pmd_can_be_fp(PatternMatchData * pmd,CursorActionType cat,bool only_literals)60 static bool pmd_can_be_fp(
61 PatternMatchData* pmd, CursorActionType cat, bool only_literals)
62 {
63 switch ( cat )
64 {
65 case CAT_NONE:
66 case CAT_ADJUST:
67 case CAT_SET_OTHER:
68 return false;
69 default:
70 break;
71 }
72
73 if ( only_literals and !pmd->is_literal() )
74 return false;
75
76 return pmd->can_be_fp();
77 }
78
get_pm_type(CursorActionType cat)79 PmType get_pm_type(CursorActionType cat)
80 {
81 switch ( cat )
82 {
83 case CAT_SET_RAW:
84 case CAT_SET_OTHER:
85 return PM_TYPE_PKT;
86
87 case CAT_SET_COOKIE:
88 return PM_TYPE_COOKIE;
89
90 case CAT_SET_JS_DATA:
91 return PM_TYPE_JS_DATA;
92
93 case CAT_SET_STAT_MSG:
94 return PM_TYPE_STAT_MSG;
95
96 case CAT_SET_STAT_CODE:
97 return PM_TYPE_STAT_CODE;
98
99 case CAT_SET_METHOD:
100 return PM_TYPE_METHOD;
101
102 case CAT_SET_RAW_HEADER:
103 return PM_TYPE_RAW_HEADER;
104
105 case CAT_SET_RAW_KEY:
106 return PM_TYPE_RAW_KEY;
107
108 case CAT_SET_FILE:
109 return PM_TYPE_FILE;
110
111 case CAT_SET_BODY:
112 return PM_TYPE_BODY;
113
114 case CAT_SET_HEADER:
115 return PM_TYPE_HEADER;
116
117 case CAT_SET_KEY:
118 return PM_TYPE_KEY;
119
120 case CAT_SET_VBA:
121 return PM_TYPE_VBA;
122
123 default:
124 break;
125 }
126 assert(false);
127 return PM_TYPE_MAX;
128 }
129
get_dir(OptTreeNode * otn)130 static RuleDirection get_dir(OptTreeNode* otn)
131 {
132 if ( otn->to_client() )
133 return RULE_FROM_SERVER;
134
135 if ( otn->to_server() )
136 return RULE_FROM_CLIENT;
137
138 return RULE_WO_DIR;
139 }
140
141 // this will be made extensible when fast patterns are extensible
get_service(const char * opt)142 static const char* get_service(const char* opt)
143 {
144 if ( !strncmp(opt, "http_", 5) )
145 return "http";
146
147 if ( !strncmp(opt, "js_data", 7) )
148 return "http";
149
150 if ( !strncmp(opt, "cip_", 4) ) // NO FP BUF
151 return "cip";
152
153 if ( !strncmp(opt, "dce_", 4) )
154 return "netbios-ssn";
155
156 if ( !strncmp(opt, "dnp3_", 5) )
157 return "dnp3";
158
159 if ( !strncmp(opt, "gtp_", 4) ) // NO FP BUF
160 return "gtp";
161
162 if ( !strncmp(opt, "modbus_", 7) )
163 return "modbus";
164
165 if ( !strncmp(opt, "s7commplus_", 11) )
166 return "s7commplus";
167
168 if ( !strncmp(opt, "sip_", 4) )
169 return "sip";
170
171 if ( !strncmp(opt, "vba_data", 8) )
172 return "file";
173
174 return nullptr;
175 }
176
177 //--------------------------------------------------------------------------
178 // class to help determine which of two candidate patterns is better for
179 // a rule that does not have a valid fast_pattern flag.
180 //--------------------------------------------------------------------------
181
182 struct FpSelector
183 {
184 CursorActionType cat = CAT_NONE;
185 IpsOption* opt = nullptr;
186 PatternMatchData* pmd = nullptr;
187 unsigned size = 0;
188
189 FpSelector() = default;
190 FpSelector(CursorActionType, IpsOption*, PatternMatchData*);
191
192 bool is_better_than(FpSelector&, bool srvc, RuleDirection, bool only_literals = false);
193 };
194
FpSelector(CursorActionType c,IpsOption * o,PatternMatchData * p)195 FpSelector::FpSelector(CursorActionType c, IpsOption* o, PatternMatchData* p)
196 {
197 cat = c;
198 opt = o;
199 pmd = p;
200 size = p->pattern_size;
201 }
202
is_better_than(FpSelector & rhs,bool,RuleDirection,bool only_literals)203 bool FpSelector::is_better_than(
204 FpSelector& rhs, bool /*srvc*/, RuleDirection /*dir*/, bool only_literals)
205 {
206 if ( !pmd_can_be_fp(pmd, cat, only_literals) )
207 {
208 if ( pmd->is_fast_pattern() )
209 ParseWarning(WARN_RULES, "content ineligible for fast_pattern matcher - ignored");
210
211 return false;
212 }
213
214 if ( !rhs.pmd )
215 return true;
216
217 if ( pmd->is_fast_pattern() )
218 {
219 if ( rhs.pmd->is_fast_pattern() )
220 {
221 ParseWarning(WARN_RULES,
222 "only one fast_pattern content per rule allowed - using first");
223
224 return false;
225 }
226 return true;
227 }
228 if ( rhs.pmd->is_fast_pattern() )
229 return false;
230
231 if ( !pmd->is_negated() && rhs.pmd->is_negated() )
232 return true;
233
234 if ( pmd->is_negated() && !rhs.pmd->is_negated() )
235 return false;
236
237 if ( size > rhs.size )
238 return true;
239
240 return false;
241 }
242
243 //--------------------------------------------------------------------------
244 // mpse database serialization
245 //--------------------------------------------------------------------------
246
247 static unsigned mpse_loaded, mpse_dumped;
248
store(const std::string & s,const uint8_t * data,size_t len)249 static bool store(const std::string& s, const uint8_t* data, size_t len)
250 {
251 std::ofstream out(s.c_str(), std::ofstream::binary);
252 out.write((const char*)data, len);
253 return true;
254 }
255
fetch(const std::string & s,uint8_t * & data,size_t & len)256 static bool fetch(const std::string& s, uint8_t*& data, size_t& len)
257 {
258 std::ifstream in(s.c_str(), std::ifstream::binary);
259
260 if ( !in.is_open() )
261 return false;
262
263 in.seekg (0, in.end);
264 len = in.tellg();
265 in.seekg (0);
266
267 data = new uint8_t[len];
268 in.read((char*)data, len);
269
270 return true;
271 }
272
make_db_name(const std::string & path,const char * proto,const char * dir,const char * buf,const std::string & id)273 static std::string make_db_name(
274 const std::string& path, const char* proto, const char* dir, const char* buf, const std::string& id)
275 {
276 std::stringstream ss;
277
278 ss << path << "/";
279 ss << proto << "_";
280 ss << dir << "_";
281 ss << buf << "_";
282
283 ss << std::hex << std::setfill('0') << std::setw(2);
284
285 for ( auto c : id )
286 ss << (unsigned)(uint8_t)c;
287
288 ss << ".hsdb";
289
290 return ss.str();
291 }
292
db_dump(const std::string & path,const char * proto,const char * dir,RuleGroup * g)293 static bool db_dump(const std::string& path, const char* proto, const char* dir, RuleGroup* g)
294 {
295 for ( auto i = 0; i < PM_TYPE_MAX; ++i )
296 {
297 if ( !g->mpsegrp[i] )
298 continue;
299
300 std::string id;
301 g->mpsegrp[i]->normal_mpse->get_hash(id);
302
303 std::string file = make_db_name(path, proto, dir, pm_type_strings[i], id);
304
305 uint8_t* db = nullptr;
306 size_t len = 0;
307
308 if ( g->mpsegrp[i]->normal_mpse->serialize(db, len) and db and len > 0 )
309 {
310 store(file, db, len);
311 free(db);
312 ++mpse_dumped;
313 }
314 else
315 {
316 ParseWarning(WARN_RULES, "Failed to serialize %s", file.c_str());
317 return false;
318 }
319 }
320 return true;
321 }
322
db_load(const std::string & path,const char * proto,const char * dir,RuleGroup * g)323 static bool db_load(const std::string& path, const char* proto, const char* dir, RuleGroup* g)
324 {
325 for ( auto i = 0; i < PM_TYPE_MAX; ++i )
326 {
327 if ( !g->mpsegrp[i] )
328 continue;
329
330 std::string id;
331 g->mpsegrp[i]->normal_mpse->get_hash(id);
332
333 std::string file = make_db_name(path, proto, dir, pm_type_strings[i], id);
334
335 uint8_t* db = nullptr;
336 size_t len = 0;
337
338 if ( !fetch(file, db, len) )
339 {
340 ParseWarning(WARN_RULES, "Failed to read %s", file.c_str());
341 return false;
342 }
343 else if ( !g->mpsegrp[i]->normal_mpse->deserialize(db, len) )
344 {
345 ParseWarning(WARN_RULES, "Failed to deserialize %s", file.c_str());
346 return false;
347 }
348 delete[] db;
349 ++mpse_loaded;
350 }
351 return true;
352 }
353
354 typedef bool (*db_io)(const std::string&, const char*, const char*, RuleGroup*);
355
port_io(const std::string & path,const char * proto,const char * end,PortTable * pt,db_io func)356 static void port_io(
357 const std::string& path, const char* proto, const char* end, PortTable* pt, db_io func)
358 {
359 for (GHashNode* node = pt->pt_mpo_hash->find_first();
360 node;
361 node = pt->pt_mpo_hash->find_next())
362 {
363 PortObject2* po = (PortObject2*)node->data;
364
365 if ( !po or !po->group )
366 continue;
367
368 func(path, proto, end, po->group);
369 }
370 }
371
port_io(const std::string & path,const char * proto,const char * end,PortObject * po,db_io func)372 static void port_io(
373 const std::string& path, const char* proto, const char* end, PortObject* po, db_io func)
374 {
375 if ( po->group )
376 func(path, proto, end, po->group);
377 }
378
svc_io(const std::string & path,const char * dir,GHash * h,db_io func)379 static void svc_io(const std::string& path, const char* dir, GHash* h, db_io func)
380 {
381 for ( GHashNode* n = h->find_first(); n; n = h->find_next())
382 {
383 func(path, (const char*)n->key, dir, (RuleGroup*)n->data);
384 }
385 }
386
fp_io(const SnortConfig * sc,const std::string & path,db_io func)387 static void fp_io(const SnortConfig* sc, const std::string& path, db_io func)
388 {
389 auto* pt = sc->port_tables;
390
391 port_io(path, "ip", "src", pt->ip.src, func);
392 port_io(path, "ip", "dst", pt->ip.dst, func);
393 port_io(path, "ip", "any", pt->ip.any, func);
394
395 port_io(path, "icmp", "src", pt->icmp.src, func);
396 port_io(path, "icmp", "dst", pt->icmp.dst, func);
397 port_io(path, "icmp", "any", pt->icmp.any, func);
398
399 port_io(path, "tcp", "src", pt->tcp.src, func);
400 port_io(path, "tcp", "dst", pt->tcp.dst, func);
401 port_io(path, "tcp", "any", pt->tcp.any, func);
402
403 port_io(path, "udp", "src", pt->udp.src, func);
404 port_io(path, "udp", "dst", pt->udp.dst, func);
405 port_io(path, "udp", "any", pt->udp.any, func);
406
407 auto* sp = sc->spgmmTable;
408
409 svc_io(path, "s2c", sp->to_cli, func);
410 svc_io(path, "c2s", sp->to_srv, func);
411 }
412
413 //--------------------------------------------------------------------------
414 // public methods
415 //--------------------------------------------------------------------------
416
fp_serialize(const SnortConfig * sc,const std::string & dir)417 unsigned fp_serialize(const SnortConfig* sc, const std::string& dir)
418 {
419 mpse_dumped = 0;
420 fp_io(sc, dir, db_dump);
421 return mpse_dumped;
422 }
423
fp_deserialize(const SnortConfig * sc,const std::string & dir)424 unsigned fp_deserialize(const SnortConfig* sc, const std::string& dir)
425 {
426 mpse_loaded = 0;
427 fp_io(sc, dir, db_load);
428 return mpse_loaded;
429 }
430
validate_services(SnortConfig * sc,OptTreeNode * otn)431 void validate_services(SnortConfig* sc, OptTreeNode* otn)
432 {
433 std::string svc;
434 bool file = false;
435
436 for (OptFpList* ofl = otn->opt_func; ofl; ofl = ofl->next)
437 {
438 if ( !ofl->ips_opt )
439 continue;
440
441 CursorActionType cat = ofl->ips_opt->get_cursor_type();
442
443 if ( cat <= CAT_ADJUST )
444 continue;
445
446 const char* s = ofl->ips_opt->get_name();
447
448 // special case file_data because it could be any subset of file carving services
449 if ( !strcmp(s, "file_data") )
450 {
451 file = true;
452 continue;
453 }
454
455 s = get_service(s);
456
457 if ( !s )
458 continue;
459
460 if ( !svc.empty() and svc != s )
461 {
462 ParseWarning(WARN_RULES, "%u:%u:%u has mixed service buffers (%s and %s)",
463 otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev, svc.c_str(), s);
464 }
465 svc = s;
466 }
467 if ( otn->sigInfo.services.size() == 1 and !svc.empty() and otn->sigInfo.services[0].service != svc )
468 {
469 ParseWarning(WARN_RULES, "%u:%u:%u has service:%s with %s buffer",
470 otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev,
471 otn->sigInfo.services[0].service.c_str(), svc.c_str());
472 }
473 if ( otn->sigInfo.services.empty() and !svc.empty() )
474 {
475 ParseWarning(WARN_RULES, "%u:%u:%u has no service with %s buffer",
476 otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev, svc.c_str());
477
478 add_service_to_otn(sc, otn, svc.c_str());
479 }
480 if ( otn->sigInfo.services.empty() and file )
481 {
482 ParseWarning(WARN_RULES, "%u:%u:%u has no service with file_data",
483 otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev);
484 add_service_to_otn(sc, otn, "file");
485 }
486 }
487
get_fp_content(OptTreeNode * otn,OptFpList * & node,bool srvc,bool only_literals,bool & exclude)488 PatternMatchVector get_fp_content(
489 OptTreeNode* otn, OptFpList*& node, bool srvc, bool only_literals, bool& exclude)
490 {
491 CursorActionType curr_cat = CAT_SET_RAW;
492 FpSelector best;
493 bool content = false;
494 PatternMatchVector pmds;
495
496 for (OptFpList* ofl = otn->opt_func; ofl; ofl = ofl->next)
497 {
498 if ( !ofl->ips_opt )
499 continue;
500
501 CursorActionType cat = ofl->ips_opt->get_cursor_type();
502
503 if ( cat > CAT_ADJUST )
504 curr_cat = cat;
505
506 RuleDirection dir = get_dir(otn);
507 PatternMatchData* tmp = ofl->ips_opt->get_pattern(otn->snort_protocol_id, dir);
508
509 if ( !tmp )
510 continue;
511
512 content = true;
513
514 FpSelector curr(curr_cat, ofl->ips_opt, tmp);
515
516 if ( curr.is_better_than(best, srvc, dir, only_literals) )
517 {
518 best = curr;
519 node = ofl;
520 }
521 }
522
523 exclude = best.pmd and (best.cat != CAT_SET_RAW) and !srvc and !otn->sigInfo.services.empty();
524
525 if ( content && !best.pmd)
526 ParseWarning(WARN_RULES, "content based rule %u:%u has no eligible fast pattern",
527 otn->sigInfo.gid, otn->sigInfo.sid);
528
529 if ( !exclude and best.pmd )
530 {
531 PatternMatchData* alt_pmd = best.opt->get_alternate_pattern();
532 if (alt_pmd)
533 pmds.emplace_back(alt_pmd);
534 pmds.emplace_back(best.pmd); // add primary pattern last
535 }
536 return pmds;
537 }
538
make_fast_pattern_only(const OptFpList * ofp,const PatternMatchData * pmd)539 bool make_fast_pattern_only(const OptFpList* ofp, const PatternMatchData* pmd)
540 {
541 // FIXIT-L no_case consideration is mpse specific, delegate
542 if ( !pmd->is_relative() and !pmd->is_negated() and
543 !pmd->offset and !pmd->depth and pmd->is_no_case() )
544 {
545 ofp = ofp->next;
546 if ( !ofp || !ofp->ips_opt || !ofp->ips_opt->is_relative() )
547 return true;
548 }
549 return false;
550 }
551
is_fast_pattern_only(const OptTreeNode * otn,const OptFpList * ofp,Mpse::MpseType mpse_type)552 bool is_fast_pattern_only(const OptTreeNode* otn, const OptFpList* ofp, Mpse::MpseType mpse_type)
553 {
554 if ( mpse_type == Mpse::MPSE_TYPE_NORMAL and otn->normal_fp_only == ofp )
555 return true;
556
557 if ( mpse_type == Mpse::MPSE_TYPE_OFFLOAD and otn->offload_fp_only == ofp )
558 return true;
559
560 return false;
561 }
562
563 //--------------------------------------------------------------------------
564 // mpse compile threads
565 //--------------------------------------------------------------------------
566
567 static std::list<Mpse*> s_tbd;
568 static std::mutex s_mutex;
569
get_mpse()570 static Mpse* get_mpse()
571 {
572 std::lock_guard<std::mutex> lock(s_mutex);
573
574 if ( s_tbd.empty() )
575 return nullptr;
576
577 Mpse* m = s_tbd.front();
578 s_tbd.pop_front();
579
580 return m;
581 }
582
compile_mpse(SnortConfig * sc,unsigned id,unsigned * count)583 static void compile_mpse(SnortConfig* sc, unsigned id, unsigned* count)
584 {
585 set_instance_id(id);
586 unsigned c = 0;
587
588 while ( Mpse* m = get_mpse() )
589 {
590 if ( !m->prep_patterns(sc) )
591 c++;
592 }
593 std::lock_guard<std::mutex> lock(s_mutex);
594 *count += c;
595 }
596
queue_mpse(Mpse * m)597 void queue_mpse(Mpse* m)
598 {
599 s_tbd.push_back(m);
600 }
601
compile_mpses(struct SnortConfig * sc,bool parallel)602 unsigned compile_mpses(struct SnortConfig* sc, bool parallel)
603 {
604 std::list<std::thread*> workers;
605 unsigned max = parallel ? sc->num_slots : 1;
606 unsigned count = 0;
607
608 if ( max == 1 )
609 {
610 compile_mpse(sc, get_instance_id(), &count);
611 return count;
612 }
613
614 for ( unsigned i = 0; i < max; ++i )
615 workers.push_back(new std::thread(compile_mpse, sc, i, &count));
616
617 for ( auto* w : workers )
618 {
619 w->join();
620 delete w;
621 }
622 return count;
623 }
624
625 //--------------------------------------------------------------------------
626 // unit tests
627 //--------------------------------------------------------------------------
628
629 #ifdef UNIT_TEST
set_pmd(PatternMatchData & pmd,unsigned flags,const char * s)630 static void set_pmd(PatternMatchData& pmd, unsigned flags, const char* s)
631 {
632 memset(&pmd, 0, sizeof(pmd));
633
634 if ( flags & 0x01 )
635 pmd.set_negated();
636 if ( flags & 0x02 )
637 pmd.set_no_case();
638 if ( flags & 0x04 )
639 pmd.set_relative();
640 if ( flags & 0x08 )
641 pmd.set_literal();
642 if ( flags & 0x10 )
643 pmd.set_fast_pattern();
644
645 pmd.pattern_buf = s;
646 pmd.pattern_size = strlen(s);
647 }
648
649 TEST_CASE("pmd_no_options", "[PatternMatchData]")
650 {
651 PatternMatchData pmd;
652 set_pmd(pmd, 0x0, "foo");
653 CHECK(pmd.can_be_fp());
654 }
655
656 TEST_CASE("pmd_negated", "[PatternMatchData]")
657 {
658 PatternMatchData pmd;
659 set_pmd(pmd, 0x1, "foo");
660 CHECK(!pmd.can_be_fp());
661 }
662
663 TEST_CASE("pmd_no_case", "[PatternMatchData]")
664 {
665 PatternMatchData pmd;
666 set_pmd(pmd, 0x2, "foo");
667 CHECK(pmd.can_be_fp());
668 }
669
670 TEST_CASE("pmd_relative", "[PatternMatchData]")
671 {
672 PatternMatchData pmd;
673 set_pmd(pmd, 0x4, "foo");
674 CHECK(pmd.can_be_fp());
675 }
676
677 TEST_CASE("pmd_negated_no_case", "[PatternMatchData]")
678 {
679 PatternMatchData pmd;
680 set_pmd(pmd, 0x3, "foo");
681 CHECK(pmd.can_be_fp());
682 }
683
684 TEST_CASE("pmd_negated_relative", "[PatternMatchData]")
685 {
686 PatternMatchData pmd;
687 set_pmd(pmd, 0x5, "foo");
688 CHECK(!pmd.can_be_fp());
689 }
690
691 TEST_CASE("pmd_negated_no_case_offset", "[PatternMatchData]")
692 {
693 PatternMatchData pmd;
694 set_pmd(pmd, 0x1, "foo");
695 pmd.offset = 3;
696 CHECK(!pmd.can_be_fp());
697 }
698
699 TEST_CASE("pmd_negated_no_case_depth", "[PatternMatchData]")
700 {
701 PatternMatchData pmd;
702 set_pmd(pmd, 0x3, "foo");
703 pmd.depth = 1;
704 CHECK(!pmd.can_be_fp());
705 }
706
707 TEST_CASE("fp_simple", "[FastPatternSelect]")
708 {
709 FpSelector test;
710 PatternMatchData pmd;
711 set_pmd(pmd, 0x0, "foo");
712 FpSelector left(CAT_SET_RAW, nullptr, &pmd);
713 CHECK(left.is_better_than(test, false, RULE_WO_DIR));
714
715 test.size = 1;
716 CHECK(left.is_better_than(test, false, RULE_WO_DIR));
717 }
718
719 TEST_CASE("fp_negated", "[FastPatternSelect]")
720 {
721 PatternMatchData p0;
722 set_pmd(p0, 0x0, "foo");
723 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
724
725 PatternMatchData p1;
726 set_pmd(p1, 0x1, "foo");
727 FpSelector s1(CAT_SET_RAW, nullptr, &p1);
728
729 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
730 CHECK(!s1.is_better_than(s0, false, RULE_WO_DIR));
731 }
732
733 TEST_CASE("fp_cat1", "[FastPatternSelect]")
734 {
735 PatternMatchData p0;
736 set_pmd(p0, 0x0, "longer");
737 FpSelector s0(CAT_SET_FILE, nullptr, &p0);
738
739 PatternMatchData p1;
740 set_pmd(p1, 0x0, "short");
741 FpSelector s1(CAT_SET_BODY, nullptr, &p1);
742
743 CHECK(s0.is_better_than(s1, true, RULE_WO_DIR));
744 }
745
746 TEST_CASE("fp_cat2", "[FastPatternSelect]")
747 {
748 PatternMatchData p0;
749 set_pmd(p0, 0x0, "foo");
750 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
751
752 PatternMatchData p1;
753 set_pmd(p1, 0x0, "foo");
754 FpSelector s1(CAT_SET_FILE, nullptr, &p1);
755
756 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
757 CHECK(!s1.is_better_than(s0, false, RULE_WO_DIR));
758 }
759
760 TEST_CASE("fp_cat3", "[FastPatternSelect]")
761 {
762 PatternMatchData p0;
763 set_pmd(p0, 0x0, "foo");
764 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
765
766 PatternMatchData p1;
767 set_pmd(p1, 0x0, "foo");
768 FpSelector s1(CAT_SET_FILE, nullptr, &p1);
769
770 CHECK(!s0.is_better_than(s1, true, RULE_WO_DIR));
771 }
772
773 TEST_CASE("fp_size", "[FastPatternSelect]")
774 {
775 PatternMatchData p0;
776 set_pmd(p0, 0x0, "longer");
777 FpSelector s0(CAT_SET_HEADER, nullptr, &p0);
778
779 PatternMatchData p1;
780 set_pmd(p1, 0x0, "short");
781 FpSelector s1(CAT_SET_HEADER, nullptr, &p1);
782
783 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
784 }
785
786 TEST_CASE("fp_pkt_key_port", "[FastPatternSelect]")
787 {
788 PatternMatchData p0;
789 set_pmd(p0, 0x0, "short");
790 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
791
792 PatternMatchData p1;
793 set_pmd(p1, 0x0, "longer");
794 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
795
796 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
797 }
798
799 TEST_CASE("fp_pkt_key_port_user", "[FastPatternSelect]")
800 {
801 PatternMatchData p0;
802 set_pmd(p0, 0x10, "short");
803 FpSelector s0(CAT_SET_KEY, nullptr, &p0);
804
805 PatternMatchData p1;
806 set_pmd(p1, 0x0, "longer");
807 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
808
809 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
810 }
811
812 TEST_CASE("fp_pkt_key_port_user_user", "[FastPatternSelect]")
813 {
814 PatternMatchData p0;
815 set_pmd(p0, 0x10, "longer");
816 FpSelector s0(CAT_SET_KEY, nullptr, &p0);
817
818 PatternMatchData p1;
819 set_pmd(p1, 0x10, "short");
820 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
821
822 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
823 }
824
825 TEST_CASE("fp_pkt_key_port_user_user2", "[FastPatternSelect]")
826 {
827 PatternMatchData p0;
828 set_pmd(p0, 0x0, "longer");
829 FpSelector s0(CAT_SET_KEY, nullptr, &p0);
830
831 PatternMatchData p1;
832 set_pmd(p1, 0x10, "short");
833 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
834
835 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
836 }
837
838 TEST_CASE("fp_pkt_key_srvc_1", "[FastPatternSelect]")
839 {
840 PatternMatchData p0;
841 set_pmd(p0, 0x0, "short");
842 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
843
844 PatternMatchData p1;
845 set_pmd(p1, 0x0, "longer");
846 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
847
848 CHECK(s1.is_better_than(s0, true, RULE_WO_DIR));
849 }
850
851 TEST_CASE("fp_pkt_key_srvc_2", "[FastPatternSelect]")
852 {
853 PatternMatchData p0;
854 set_pmd(p0, 0x0, "longer");
855 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
856
857 PatternMatchData p1;
858 set_pmd(p1, 0x0, "short");
859 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
860
861 CHECK(s0.is_better_than(s1, true, RULE_WO_DIR));
862 }
863
864 TEST_CASE("fp_pkt_key_srvc_rsp", "[FastPatternSelect]")
865 {
866 PatternMatchData p0;
867 set_pmd(p0, 0x0, "short");
868 FpSelector s0(CAT_SET_RAW, nullptr, &p0);
869
870 PatternMatchData p1;
871 set_pmd(p1, 0x0, "longer");
872 FpSelector s1(CAT_SET_KEY, nullptr, &p1);
873
874 CHECK(!s0.is_better_than(s1, true, RULE_FROM_SERVER));
875 CHECK(s1.is_better_than(s0, true, RULE_FROM_SERVER));
876 }
877 #endif
878
879