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