1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2013-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation. You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //--------------------------------------------------------------------------
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "parse_rule.h"
25
26 #include "actions/actions.h"
27 #include "detection/detect.h"
28 #include "detection/fp_config.h"
29 #include "detection/fp_utils.h"
30 #include "detection/rtn_checks.h"
31 #include "detection/treenodes.h"
32 #include "framework/decode_data.h"
33 #include "hash/xhash.h"
34 #include "log/messages.h"
35 #include "main/snort_config.h"
36 #include "main/thread_config.h"
37 #include "managers/ips_manager.h"
38 #include "managers/module_manager.h"
39 #include "managers/so_manager.h"
40 #include "ports/rule_port_tables.h"
41 #include "sfip/sf_ipvar.h"
42 #include "sfip/sf_vartable.h"
43 #include "target_based/snort_protocols.h"
44 #include "utils/util.h"
45 #include "ips_options/extract.h"
46
47 #include "parser.h"
48 #include "parse_conf.h"
49 #include "parse_ports.h"
50
51 #include "parser.h"
52 #include "cmd_line.h"
53 #include "config_file.h"
54 #include "parse_conf.h"
55 #include "parse_ports.h"
56
57 using namespace snort;
58
59 #define SRC 0
60 #define DST 1
61
62 /* rule counts for port lists */
63 struct rule_count_t
64 {
65 int src;
66 int dst;
67 int any;
68 int both;
69 };
70
71 static int rule_count = 0;
72 static int prev_rule_count = 0;
73 static int skip_count = 0;
74 static int detect_rule_count = 0;
75 static int builtin_rule_count = 0;
76 static int so_rule_count = 0;
77 static int head_count = 0; // rule headers
78 static int otn_count = 0; // rule bodies
79 static int dup_count = 0; // rule bodies
80 static int prev_dup_count = 0;
81 static int rule_proto = 0;
82
83 static rule_count_t tcpCnt;
84 static rule_count_t udpCnt;
85 static rule_count_t icmpCnt;
86 static rule_count_t ipCnt;
87 static rule_count_t svcCnt; // dummy for now
88
89 static bool s_ignore = false; // for skipping drop rules when not inline, etc.
90 static bool s_capture = false;
91
92 static std::string s_type;
93 static std::string s_body;
94
95 struct SoRule
96 {
SoRuleSoRule97 SoRule(RuleTreeNode* rtn, const OptTreeNode* otn) :
98 rtn(rtn), gid(otn->sigInfo.gid), sid(otn->sigInfo.sid), rev(otn->sigInfo.rev) { }
99
100 RuleTreeNode* rtn;
101 uint32_t gid;
102 uint32_t sid;
103 uint32_t rev;
104 };
105
106 static SoRule* s_so_rule = nullptr;
107
ValidateIPList(sfip_var_t * addrset,const char * token)108 static int ValidateIPList(sfip_var_t* addrset, const char* token)
109 {
110 if (!addrset || !(addrset->head||addrset->neg_head))
111 {
112 ParseError("empty IP used either as source IP or as "
113 "destination IP in a rule. IP list: %s.", token);
114 return -1;
115 }
116 return 0;
117 }
118
ProcessIP(SnortConfig * sc,const char * addr,RuleTreeNode * rtn,int mode,int)119 static int ProcessIP(SnortConfig* sc, const char* addr, RuleTreeNode* rtn, int mode, int)
120 {
121 vartable_t* ip_vartable = get_ips_policy()->ip_vartable;
122
123 assert(rtn);
124 /* If a rule has a variable in it, we want to copy that variable's
125 * contents to the IP variable (IP list) stored with the rtn.
126 * This code tries to look up the variable, and if found, will copy it
127 * to the rtn->{sip,dip} */
128 if (mode == SRC)
129 {
130 int ret;
131
132 if ( !rtn->sip )
133 {
134 sfip_var_t* tmp = sfvt_lookup_var(ip_vartable, addr);
135 if ( tmp )
136 {
137 rtn->sip = sfvar_create_alias(tmp, tmp->name);
138 ret = rtn->sip ? SFIP_SUCCESS : SFIP_FAILURE;
139 }
140 else
141 {
142 rtn->sip = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
143 ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
144 }
145 }
146 else
147 {
148 ret = sfvt_add_to_var(ip_vartable, rtn->sip, addr);
149 }
150
151 /* The function sfvt_add_to_var adds 'addr' to the variable 'rtn->sip' */
152 if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() )
153 ret = sfvt_add_to_var(ip_vartable, rtn->sip, "any");
154
155 if (ret != SFIP_SUCCESS)
156 {
157 if (ret == SFIP_LOOKUP_FAILURE)
158 {
159 ParseError("Undefined variable in the string: %s.", addr);
160 return -1;
161 }
162 else if (ret == SFIP_CONFLICT)
163 {
164 ParseError("Negated IP ranges that are more general than "
165 "non-negated ranges are not allowed. Consider "
166 "inverting the logic: %s.", addr);
167 return -1;
168 }
169 else if (ret == SFIP_NOT_ANY)
170 {
171 ParseError("!any is not allowed: %s.", addr);
172 return -1;
173 }
174 else
175 {
176 ParseError("Unable to process the IP address: %s.", addr);
177 return -1;
178 }
179 }
180
181 if (rtn->sip->head && rtn->sip->head->flags & SFIP_ANY)
182 {
183 rtn->flags |= RuleTreeNode::ANY_SRC_IP;
184 }
185 }
186 /* mode == DST */
187 else
188 {
189 int ret;
190
191 if ( !rtn->dip )
192 {
193 sfip_var_t* tmp = sfvt_lookup_var(ip_vartable, addr);
194 if ( tmp )
195 {
196 rtn->dip = sfvar_create_alias(tmp, tmp->name);
197 ret = rtn->dip ? SFIP_SUCCESS : SFIP_FAILURE;
198 }
199 else
200 {
201 rtn->dip = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
202 ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
203 }
204 }
205 else
206 {
207 ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr);
208 }
209
210 if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() )
211 ret = sfvt_add_to_var(ip_vartable, rtn->dip, "any");
212
213 if ( ret != SFIP_SUCCESS )
214 {
215 if (ret == SFIP_LOOKUP_FAILURE)
216 {
217 ParseError("undefined variable in the string: %s.", addr);
218 return -1;
219 }
220 else if (ret == SFIP_CONFLICT)
221 {
222 ParseError("negated IP ranges that are more general than "
223 "non-negated ranges are not allowed. Consider "
224 "inverting the logic: %s.", addr);
225 return -1;
226 }
227 else if (ret == SFIP_NOT_ANY)
228 {
229 ParseError("!any is not allowed: %s.", addr);
230 return -1;
231 }
232 else
233 {
234 ParseError("unable to process the IP address: %s.", addr);
235 return -1;
236 }
237 }
238
239 if (rtn->dip->head && rtn->dip->head->flags & SFIP_ANY)
240 {
241 rtn->flags |= RuleTreeNode::ANY_DST_IP;
242 }
243 }
244
245 /* Make sure the IP lists provided by the user are valid */
246 if (mode == SRC)
247 ValidateIPList(rtn->sip, addr);
248 else
249 ValidateIPList(rtn->dip, addr);
250
251 return 0;
252 }
253
254 /*
255 * Parse a port string as a port var, and create or find a port object for it,
256 * and add it to the port var table. These are used by the rtn's
257 * as src and dst port lists for final rtn/otn processing.
258 *
259 * These should not be confused with the port objects used to merge ports and rules
260 * to build port group objects. Those are generated after the otn processing.
261 */
ParsePortListTcpUdpPort(PortVarTable * pvt,PortTable * noname,const char * port_str)262 static PortObject* ParsePortListTcpUdpPort(
263 PortVarTable* pvt, PortTable* noname, const char* port_str)
264 {
265 PortObject* portobject;
266 POParser poparser;
267
268 if ( !pvt or !noname or !port_str )
269 return nullptr;
270
271 /* 1st - check if we have an any port */
272 if ( strcasecmp(port_str,"any")== 0 )
273 {
274 portobject = PortVarTableFind(pvt, "any");
275 if ( !portobject )
276 ParseAbort("PortVarTable missing an 'any' variable.");
277
278 return portobject;
279 }
280 /* 2nd - check if we have a PortVar */
281 else if ( port_str[0]=='$' )
282 {
283 /*||isalpha(port_str[0])*/ /*TODO: interferes with protocol names for ports*/
284 const char* name = port_str + 1;
285
286 /* look it up in the port var table */
287 portobject = PortVarTableFind(pvt, name, true);
288 if ( !portobject )
289 ParseAbort("***PortVar Lookup failed on '%s'.", port_str);
290
291 }
292 /* 3rd - and finally process a raw port list */
293 else
294 {
295 /* port list = [p,p,p:p,p,...] or p or p:p , no embedded spaces due to tokenizer */
296 PortObject* pox;
297
298 portobject = PortObjectParseString(pvt, &poparser, nullptr, port_str, 0);
299
300 if ( !portobject )
301 {
302 const char* errstr = PortObjectParseError(&poparser);
303 ParseAbort("***Rule--PortVar Parse error: (pos=%d,error=%s)\n>>%s\n>>%*s",
304 poparser.pos,errstr,port_str,poparser.pos,"^");
305 }
306
307 /* check if we already have this port object in the un-named port var table ... */
308 pox = PortTableFindInputPortObjectPorts(noname, portobject);
309 if ( pox )
310 {
311 PortObjectFree(portobject);
312 portobject = pox;
313 }
314 else
315 {
316 /* Add to the un-named port var table */
317 if (PortTableAddObject(noname, portobject))
318 {
319 ParseAbort("Unable to add raw port object to unnamed "
320 "port var table, out of memory.");
321 }
322 }
323 }
324
325 return portobject;
326 }
327
328 /*
329 * Process the rule, add it to the appropriate PortObject
330 * and add the PortObject to the rtn.
331 *
332 * TCP/UDP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
333 * field as a dst port for the purposes of looking up a rule group as packets are being
334 * processed.
335 *
336 * TCP/UDP- use src/dst ports
337 * ICMP - use icmp type as dst port,src=-1
338 * IP - use protocol as dst port,src=-1
339 *
340 * rtn - proto_node
341 * port_str - port list string or port var name
342 * dst_flag - dst or src port flag, true = dst, false = src
343 *
344 */
ParsePortList(RuleTreeNode * rtn,PortVarTable * pvt,PortTable * noname,const char * port_str,int dst_flag)345 static int ParsePortList(
346 RuleTreeNode* rtn, PortVarTable* pvt, PortTable* noname,
347 const char* port_str, int dst_flag)
348 {
349 PortObject* portobject; /* src or dst */
350
351 /* Get the protocol specific port object */
352 if ( rule_proto & (PROTO_BIT__TCP | PROTO_BIT__UDP | PROTO_BIT__PDU) )
353 {
354 portobject = ParsePortListTcpUdpPort(pvt, noname, port_str);
355 }
356 else /* ICMP, IP - no real ports just Type and Protocol */
357 {
358 portobject = PortVarTableFind(pvt, "any");
359 if ( !portobject )
360 {
361 ParseError("PortVarTable missing an 'any' variable.");
362 return -1;
363 }
364 }
365
366 /* !ports - port lists can be mixed 80:90,!82,
367 * so the old NOT flag is deprecated for port lists
368 */
369
370 /* set up any any flags */
371 if ( PortObjectHasAny(portobject) )
372 {
373 if ( dst_flag )
374 rtn->flags |= RuleTreeNode::ANY_DST_PORT;
375 else
376 rtn->flags |= RuleTreeNode::ANY_SRC_PORT;
377 }
378
379 /* check for a pure not rule - fatal if we find one */
380 if ( PortObjectIsPureNot(portobject) )
381 {
382 ParseError("Pure NOT ports are not allowed.");
383 return -1;
384 }
385
386 /*
387 * set to the port object for this rules src/dst port,
388 * these are used during rtn/otn port verification of the rule.
389 */
390
391 if (dst_flag)
392 rtn->dst_portobject = portobject;
393 else
394 rtn->src_portobject = portobject;
395
396 return 0;
397 }
398
same_headers(RuleTreeNode * rule,RuleTreeNode * rtn)399 bool same_headers(RuleTreeNode* rule, RuleTreeNode* rtn)
400 {
401 if ( !rule or !rtn )
402 return false;
403
404 if (rule->action != rtn->action)
405 return false;
406
407 if (rule->snort_protocol_id != rtn->snort_protocol_id)
408 return false;
409
410 /* For custom rule type declarations */
411 if (rule->listhead != rtn->listhead)
412 return false;
413
414 if (rule->flags != rtn->flags)
415 return false;
416
417 if ( rule->sip and rtn->sip and sfvar_compare(rule->sip, rtn->sip) != SFIP_EQUAL )
418 return false;
419
420 if ( rule->dip and rtn->dip and sfvar_compare(rule->dip, rtn->dip) != SFIP_EQUAL )
421 return false;
422
423 /* compare the port group pointers - this prevents confusing src/dst port objects
424 * with the same port set, and it's quicker. It does assume that we only have
425 * one port object and pointer for each unique port set...this is handled by the
426 * parsing and initial port object storage and lookup. This must be consistent during
427 * the rule parsing phase. - man */
428 if ( (rule->src_portobject != rtn->src_portobject)
429 or (rule->dst_portobject != rtn->dst_portobject))
430 {
431 return false;
432 }
433 return true;
434 }
435
XferHeader(RuleTreeNode * from,RuleTreeNode * to)436 static void XferHeader(RuleTreeNode* from, RuleTreeNode* to)
437 {
438 to->flags = from->flags;
439 to->action = from->action;
440 to->sip = from->sip;
441 to->dip = from->dip;
442
443 to->listhead = from->listhead;
444 to->snort_protocol_id = from->snort_protocol_id;
445
446 to->src_portobject = from->src_portobject;
447 to->dst_portobject = from->dst_portobject;
448
449 to->header = from->header;
450 }
451
452 /****************************************************************************
453 * Purpose: Adds RuleTreeNode associated detection functions to the
454 * current rule's function list
455 *
456 * Arguments: *func => function pointer to the detection function
457 * rtn => pointer to the current rule
458 ***************************************************************************/
AddRuleFuncToList(int (* rfunc)(Packet *,RuleTreeNode *,struct RuleFpList *,int),RuleTreeNode * rtn)459 static void AddRuleFuncToList(
460 int (* rfunc)(Packet*, RuleTreeNode*, struct RuleFpList*, int),
461 RuleTreeNode* rtn)
462 {
463 RuleFpList* idx = rtn->rule_func;
464
465 if ( !idx )
466 {
467 rtn->rule_func = new RuleFpList;
468 rtn->rule_func->RuleHeadFunc = rfunc;
469 }
470 else
471 {
472 while ( idx->next )
473 idx = idx->next;
474
475 idx->next = new RuleFpList;
476 idx = idx->next;
477 idx->RuleHeadFunc = rfunc;
478 }
479 }
480
481 /****************************************************************************
482 * Purpose: Links the proper IP address testing function to the current RTN
483 * based on the address, netmask, and addr flags
484 *
485 * Arguments: rtn => the pointer to the current rules list entry to attach to
486 * mode => indicates whether this is a rule for the source
487 * or destination IP for the rule
488 ***************************************************************************/
AddrToFunc(RuleTreeNode * rtn,int mode)489 static void AddrToFunc(RuleTreeNode* rtn, int mode)
490 {
491 /*
492 * if IP and mask are both 0, this is a "any" IP and we don't need to
493 * check it
494 */
495 switch (mode)
496 {
497 case SRC:
498 if ((rtn->flags & RuleTreeNode::ANY_SRC_IP) == 0)
499 {
500 AddRuleFuncToList(CheckSrcIP, rtn);
501 }
502 break;
503
504 case DST:
505 if ((rtn->flags & RuleTreeNode::ANY_DST_IP) == 0)
506 {
507 AddRuleFuncToList(CheckDstIP, rtn);
508 }
509 break;
510 }
511 }
512
513 /****************************************************************************
514 * Purpose: Links in the port analysis function for the current rule
515 *
516 * Arguments: rtn => the pointer to the current rules list entry to attach to
517 * any_flag => accept any port if set
518 * except_flag => indicates negation (logical NOT) of the test
519 * mode => indicates whether this is a rule for the source
520 * or destination port for the rule
521 ***************************************************************************/
PortToFunc(RuleTreeNode * rtn,int any_flag,int except_flag,int mode)522 static void PortToFunc(RuleTreeNode* rtn, int any_flag, int except_flag, int mode)
523 {
524 /*
525 * if the any flag is set we don't need to perform any test to match on
526 * this port
527 */
528 if (any_flag)
529 return;
530
531 /* if the except_flag is up, test with the "NotEq" funcs */
532 if (except_flag)
533 {
534 switch (mode)
535 {
536 case SRC:
537 AddRuleFuncToList(CheckSrcPortNotEq, rtn);
538 break;
539
540 case DST:
541 AddRuleFuncToList(CheckDstPortNotEq, rtn);
542 break;
543 }
544
545 return;
546 }
547 /* default to setting the straight test function */
548 switch (mode)
549 {
550 case SRC:
551 AddRuleFuncToList(CheckSrcPortEqual, rtn);
552 break;
553
554 case DST:
555 AddRuleFuncToList(CheckDstPortEqual, rtn);
556 break;
557 }
558 }
559
560 // Configures the function list for the rule header detection
561 // functions (addrs and ports)
SetupRTNFuncList(RuleTreeNode * rtn)562 static void SetupRTNFuncList(RuleTreeNode* rtn)
563 {
564 if (rtn->flags & RuleTreeNode::BIDIRECTIONAL)
565 AddRuleFuncToList(CheckBidirectional, rtn);
566
567 else
568 {
569 PortToFunc(rtn, (rtn->any_dst_port() ? 1 : 0), 0, DST);
570 PortToFunc(rtn, (rtn->any_src_port() ? 1 : 0), 0, SRC);
571
572 AddrToFunc(rtn, SRC);
573 AddrToFunc(rtn, DST);
574 }
575
576 if ( rtn->snort_protocol_id < SNORT_PROTO_FILE )
577 AddRuleFuncToList(CheckProto, rtn);
578 else
579 rtn->flags |= RuleTreeNode::USER_MODE;
580
581 AddRuleFuncToList(RuleListEnd, rtn);
582 }
583
584 // make a new node and stick it at the end of the list
transfer_rtn(RuleTreeNode * tmpl)585 static RuleTreeNode* transfer_rtn(RuleTreeNode* tmpl)
586 {
587 head_count++;
588 auto rtn = new RuleTreeNode;
589 XferHeader(tmpl, rtn);
590 SetupRTNFuncList(rtn);
591 return rtn;
592 }
593
594 // Conditionally removes duplicate OTN. Keeps duplicate with
595 // higher revision. If revision is the same, keeps newest rule.
mergeDuplicateOtn(SnortConfig * sc,OptTreeNode * otn_cur,OptTreeNode * otn_new,RuleTreeNode * rtn_new)596 static int mergeDuplicateOtn(
597 SnortConfig* sc, OptTreeNode* otn_cur,
598 OptTreeNode* otn_new, RuleTreeNode* rtn_new)
599 {
600 if (otn_cur->snort_protocol_id != otn_new->snort_protocol_id)
601 {
602 ParseError("GID %u SID %u in rule duplicates previous rule, with different protocol.",
603 otn_new->sigInfo.gid, otn_new->sigInfo.sid);
604 return true;
605 }
606
607 RuleTreeNode* rtn_cur = getRtnFromOtn(otn_cur);
608
609 if ( rtn_cur and rtn_cur->action != rtn_new->action )
610 {
611 ParseError("GID %u SID %u in rule duplicates previous rule, with different type.",
612 otn_new->sigInfo.gid, otn_new->sigInfo.sid);
613 return true;
614 }
615
616 if ( otn_new->sigInfo.rev < otn_cur->sigInfo.rev )
617 {
618 // keep orig, free new
619 ParseWarning(WARN_RULES, "%u:%u duplicates previous rule. Using revision %u.",
620 otn_cur->sigInfo.gid, otn_cur->sigInfo.sid, otn_cur->sigInfo.rev);
621
622 // OTN is for new policy group, salvage RTN
623 deleteRtnFromOtn(otn_new, sc);
624
625 // Now free the OTN itself -- this function is also used
626 // by the hash-table calls out of OtnRemove, so it cannot
627 // be modified to delete data for rule options
628 delete otn_new;
629
630 // Add rtn to current otn for the first rule instance in a policy,
631 // otherwise ignore it
632 if ( !rtn_cur )
633 addRtnToOtn(sc, otn_cur, rtn_new);
634 else
635 DestroyRuleTreeNode(rtn_new);
636
637 return true;
638 }
639 // keep new, free orig
640 ParseWarning(WARN_RULES, "%u:%u duplicates previous rule. Using revision %u.",
641 otn_new->sigInfo.gid, otn_new->sigInfo.sid, otn_new->sigInfo.rev);
642
643 for ( unsigned i = 0; i < otn_cur->proto_node_num; ++i )
644 {
645 RuleTreeNode* rtnTmp2 = deleteRtnFromOtn(otn_cur, i, sc, (rtn_cur != rtn_new));
646
647 if ( rtnTmp2 and (i != get_ips_policy()->policy_id) )
648 addRtnToOtn(sc, otn_new, rtnTmp2, i);
649 }
650
651 if (rtn_cur)
652 DestroyRuleTreeNode(rtn_cur);
653
654 OtnRemove(sc->otn_map, otn_cur);
655 return false;
656 }
657
658 namespace snort
659 {
get_rule_count()660 int get_rule_count()
661 { return rule_count; }
662 }
663
get_policy_loaded_rule_count()664 int get_policy_loaded_rule_count()
665 {
666 auto policy_rule_count = rule_count - prev_rule_count;
667 prev_rule_count = rule_count;
668 return policy_rule_count;
669 }
670
get_policy_shared_rule_count()671 int get_policy_shared_rule_count()
672 {
673 auto policy_rule_count = dup_count - prev_dup_count;
674 prev_dup_count = dup_count;
675 return policy_rule_count;
676 }
677
parse_rule_init()678 void parse_rule_init()
679 {
680 rule_count = 0;
681 prev_rule_count = 0;
682 skip_count = 0;
683 detect_rule_count = 0;
684 builtin_rule_count = 0;
685 so_rule_count = 0;
686 head_count = 0;
687 otn_count = 0;
688 dup_count = 0;
689 prev_dup_count = 0;
690 rule_proto = 0;
691
692 memset(&ipCnt, 0, sizeof(ipCnt));
693 memset(&icmpCnt, 0, sizeof(icmpCnt));
694 memset(&tcpCnt, 0, sizeof(tcpCnt));
695 memset(&udpCnt, 0, sizeof(udpCnt));
696 memset(&svcCnt, 0, sizeof(svcCnt));
697 }
698
parse_rule_term()699 void parse_rule_term()
700 { }
701
parse_rule_print()702 void parse_rule_print()
703 {
704 if ( !rule_count and !skip_count )
705 return;
706
707 LogLabel("rule counts");
708 LogCount("total rules loaded", rule_count);
709 LogCount("total rules not loaded", skip_count);
710 LogCount("duplicate rules", dup_count);
711 LogCount("text rules", detect_rule_count);
712 LogCount("builtin rules", builtin_rule_count);
713 LogCount("so rules", so_rule_count);
714 LogCount("option chains", otn_count);
715 LogCount("chain headers", head_count);
716
717 unsigned ip = ipCnt.src + ipCnt.dst + ipCnt.any + ipCnt.both;
718 unsigned icmp = icmpCnt.src + icmpCnt.dst + icmpCnt.any + icmpCnt.both;
719 unsigned tcp = tcpCnt.src + tcpCnt.dst + tcpCnt.any + tcpCnt.both;
720 unsigned udp = udpCnt.src + udpCnt.dst + udpCnt.any + udpCnt.both;
721
722 if ( !ip and !icmp and !tcp and !udp )
723 return;
724
725 LogLabel("port rule counts");
726 LogMessage("%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "icmp", "ip");
727
728 if ( tcpCnt.any || udpCnt.any || icmpCnt.any || ipCnt.any )
729 LogMessage("%8s%8u%8u%8u%8u\n", "any",
730 tcpCnt.any, udpCnt.any, icmpCnt.any, ipCnt.any);
731
732 if ( tcpCnt.src || udpCnt.src || icmpCnt.src || ipCnt.src )
733 LogMessage("%8s%8u%8u%8u%8u\n", "src",
734 tcpCnt.src, udpCnt.src, icmpCnt.src, ipCnt.src);
735
736 if ( tcpCnt.dst || udpCnt.dst || icmpCnt.dst || ipCnt.dst )
737 LogMessage("%8s%8u%8u%8u%8u\n", "dst",
738 tcpCnt.dst, udpCnt.dst, icmpCnt.dst, ipCnt.dst);
739
740 if ( tcpCnt.both || udpCnt.both || icmpCnt.both || ipCnt.both )
741 LogMessage("%8s%8u%8u%8u%8u\n", "both",
742 tcpCnt.both, udpCnt.both, icmpCnt.both, ipCnt.both);
743
744 LogMessage("%8s%8u%8u%8u%8u\n", "total", tcp, udp, icmp, ip);
745 }
746
parse_rule_type(SnortConfig * sc,const char * s,RuleTreeNode & rtn)747 void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn)
748 {
749 IpsPolicy* p = get_ips_policy();
750
751 if ( !p->action_override.empty() )
752 s = p->action_override.c_str();
753
754 auto it = p->action_map.find(s);
755
756 if ( it != p->action_map.end() )
757 s = it->second.c_str();
758
759 s_type = s;
760 rtn = RuleTreeNode();
761
762 if ( s_so_rule )
763 return;
764
765 assert(s);
766
767 rtn.action = Actions::get_type(s);
768
769 if ( !Actions::is_valid_action(rtn.action) )
770 {
771 s_ignore = true;
772 ParseError("unknown rule action '%s'", s);
773 return;
774 }
775
776 if ( sc->dump_rule_meta() )
777 rtn.header = new RuleHeader(s);
778
779 rtn.listhead = get_rule_list(sc, s);
780
781 if ( !rtn.listhead )
782 {
783 CreateRuleType(sc, s, rtn.action);
784 rtn.listhead = get_rule_list(sc, s);
785 }
786
787 if ( sc->get_default_rule_state() )
788 rtn.set_enabled();
789 }
790
parse_rule_proto(SnortConfig * sc,const char * s,RuleTreeNode & rtn,bool elided)791 void parse_rule_proto(SnortConfig* sc, const char* s, RuleTreeNode& rtn, bool elided)
792 {
793 if ( s_ignore )
794 return;
795
796 if ( !s_so_rule and !elided and rtn.header )
797 rtn.header->proto = s;
798
799 if ( !strcmp(s, "tcp") )
800 rule_proto = PROTO_BIT__TCP;
801
802 else if ( !strcmp(s, "udp") )
803 rule_proto = PROTO_BIT__UDP;
804
805 else if ( !strcmp(s, "icmp") )
806 rule_proto = PROTO_BIT__ICMP;
807
808 else if ( !strcmp(s, "ip") )
809 rule_proto = PROTO_BIT__IP;
810
811 else
812 // this will allow other protocols like http to have ports
813 rule_proto = PROTO_BIT__PDU;
814
815 rtn.snort_protocol_id = sc->proto_ref->add(s);
816
817 if ( rtn.snort_protocol_id == UNKNOWN_PROTOCOL_ID )
818 {
819 ParseError("bad protocol: %s", s);
820 rule_proto = 0;
821 }
822 else if ( s_so_rule and s_so_rule->rtn->snort_protocol_id != rtn.snort_protocol_id )
823 ParseWarning(WARN_RULES, "so rule proto can not be changed");
824 }
825
parse_rule_nets(SnortConfig * sc,const char * s,bool src,RuleTreeNode & rtn,bool elided)826 void parse_rule_nets(
827 SnortConfig* sc, const char* s, bool src, RuleTreeNode& rtn, bool elided)
828 {
829 if ( s_so_rule )
830 return;
831
832 if ( s_ignore )
833 return;
834
835 if ( !elided and rtn.header )
836 {
837 if ( src )
838 rtn.header->src_nets = s;
839 else
840 rtn.header->dst_nets = s;
841 }
842 ProcessIP(sc, s, &rtn, src ? SRC : DST, 0);
843 }
844
parse_rule_ports(SnortConfig *,const char * s,bool src,RuleTreeNode & rtn,bool elided)845 void parse_rule_ports(
846 SnortConfig*, const char* s, bool src, RuleTreeNode& rtn, bool elided)
847 {
848 if ( s_so_rule )
849 return;
850
851 if ( s_ignore )
852 return;
853
854 if ( !elided and rtn.header )
855 {
856 if ( src )
857 rtn.header->src_ports = s;
858 else
859 rtn.header->dst_ports = s;
860 }
861
862 IpsPolicy* p = get_ips_policy();
863
864 if ( ParsePortList(&rtn, p->portVarTable, p->nonamePortVarTable, s, src ? SRC : DST) )
865 ParseError("bad ports: '%s'", s);
866 }
867
parse_rule_dir(SnortConfig *,const char * s,RuleTreeNode & rtn,bool elided)868 void parse_rule_dir(SnortConfig*, const char* s, RuleTreeNode& rtn, bool elided)
869 {
870 if ( s_so_rule )
871 return;
872
873 if ( s_ignore )
874 return;
875
876 if ( !elided and rtn.header )
877 rtn.header->dir = s;
878
879 if (strcmp(s, "<>") == 0)
880 rtn.flags |= RuleTreeNode::BIDIRECTIONAL;
881
882 else if ( strcmp(s, "->") )
883 ParseError("illegal direction specifier: %s", s);
884 }
885
886 // Values of the rule options "pcre", "regex" and "sd_pattern" are already escaped
887 // They are not unescaped during the rule parsing
is_already_escaped(const std::string & opt_key)888 static bool is_already_escaped(const std::string& opt_key)
889 { return opt_key == "pcre" or opt_key == "regex" or opt_key == "sd_pattern"; }
890
escape(const std::string & s)891 static std::string escape(const std::string& s)
892 {
893 std::string res;
894 int quotes_first = 0;
895 int quotes_last = std::count(s.begin(), s.end(), '"') - 1;
896 int quotes_count = quotes_first;
897
898 for ( auto it = s.begin(); it != s.end(); ++it )
899 {
900 switch ( *it )
901 {
902 case '"':
903 {
904 if ( ( quotes_count > quotes_first ) and ( quotes_count < quotes_last ) )
905 res += "\\\"";
906 else
907 res += "\"";
908
909 ++quotes_count;
910 continue;
911 }
912 case '\\': res += "\\\\"; continue;
913 case '\a': res += "\\a"; continue;
914 case '\b': res += "\\b"; continue;
915 case '\f': res += "\\f"; continue;
916 case '\n': res += "\\n"; continue;
917 case '\r': res += "\\r"; continue;
918 case '\t': res += "\\t"; continue;
919 case '\v': res += "\\v"; continue;
920 }
921
922 res += *it;
923 }
924
925 return res;
926 }
927
parse_rule_opt_begin(SnortConfig * sc,const char * key)928 void parse_rule_opt_begin(SnortConfig* sc, const char* key)
929 {
930 if ( s_ignore )
931 return;
932
933 if ( s_capture )
934 {
935 s_body += " ";
936 s_body += key;
937 s_body += ":";
938 }
939 IpsManager::option_begin(sc, key, rule_proto);
940 }
941
parse_rule_opt_set(SnortConfig * sc,const char * key,const char * opt,const char * val)942 void parse_rule_opt_set(
943 SnortConfig* sc, const char* key, const char* opt, const char* val)
944 {
945 if ( s_ignore )
946 return;
947
948 assert(opt);
949 assert(val);
950 if ( s_capture )
951 {
952 s_body += is_already_escaped(key) ? opt : escape(opt);
953 if ( *val )
954 {
955 s_body += " ";
956 s_body += val;
957 }
958 s_body += ",";
959 }
960 IpsManager::option_set(sc, key, opt, val);
961 }
962
parse_rule_opt_end(SnortConfig * sc,const char * key,OptTreeNode * otn)963 void parse_rule_opt_end(SnortConfig* sc, const char* key, OptTreeNode* otn)
964 {
965 if ( s_ignore )
966 return;
967
968 if ( s_capture )
969 {
970 s_body.erase(s_body.length()-1, 1);
971 s_body += ";";
972 }
973 RuleOptType type = OPT_TYPE_MAX;
974 IpsOption* ips = IpsManager::option_end(sc, otn, otn->snort_protocol_id, key, type);
975 CursorActionType cat = ips ? ips->get_cursor_type() : CAT_NONE;
976
977 if ( cat > CAT_ADJUST )
978 otn->sticky_buf = get_pm_type(cat);
979
980 if ( type != OPT_TYPE_META )
981 otn->num_detection_opts++;
982 }
983
parse_rule_open(SnortConfig * sc,RuleTreeNode & rtn,bool stub)984 OptTreeNode* parse_rule_open(SnortConfig* sc, RuleTreeNode& rtn, bool stub)
985 {
986 if ( s_ignore )
987 return nullptr;
988
989 if ( stub )
990 {
991 parse_rule_proto(sc, "tcp", rtn, true);
992 parse_rule_nets(sc, "any", true, rtn, true);
993 parse_rule_ports(sc, "any", true, rtn, true);
994 parse_rule_dir(sc, "->", rtn, true);
995 parse_rule_nets(sc, "any", false, rtn, true);
996 parse_rule_ports(sc, "any", false, rtn, true);
997 }
998 OptTreeNode* otn = new OptTreeNode;
999 otn->state = new OtnState[ThreadConfig::get_instance_max()];
1000
1001 if ( !stub )
1002 otn->sigInfo.gid = GID_DEFAULT;
1003
1004 otn->snort_protocol_id = rtn.snort_protocol_id;
1005
1006 if ( sc->get_default_rule_state() )
1007 rtn.set_enabled();
1008
1009 IpsManager::reset_options();
1010
1011 s_capture = sc->dump_rule_meta();
1012 s_body = "(";
1013
1014 return otn;
1015 }
1016
parse_rule_state(SnortConfig * sc,const RuleTreeNode & rtn,OptTreeNode * otn)1017 static void parse_rule_state(SnortConfig* sc, const RuleTreeNode& rtn, OptTreeNode* otn)
1018 {
1019 if ( !otn->sigInfo.gid )
1020 otn->sigInfo.gid = GID_DEFAULT;
1021
1022 if ( otn->num_detection_opts )
1023 {
1024 ParseError("%u:%u rule state stubs do not support detection options",
1025 otn->sigInfo.gid, otn->sigInfo.sid);
1026 }
1027 RuleKey key =
1028 {
1029 snort::get_ips_policy()->policy_id,
1030 otn->sigInfo.gid,
1031 otn->sigInfo.sid
1032 };
1033 RuleState state =
1034 {
1035 s_type,
1036 rtn.action,
1037 otn->enable
1038 };
1039 sc->rule_states->add(key, state);
1040
1041 if ( rtn.sip )
1042 sfvar_free(rtn.sip);
1043 if ( rtn.dip )
1044 sfvar_free(rtn.dip);
1045
1046 delete otn;
1047 }
1048
is_builtin(uint32_t gid)1049 static bool is_builtin(uint32_t gid)
1050 {
1051 if ( ModuleManager::gid_in_use(gid) )
1052 return true;
1053
1054 // the builtin range prevents unloaded sids from firing on every packet
1055 if ( gid < GID_BUILTIN_MIN or gid > GID_BUILTIN_MAX )
1056 return false;
1057
1058 // not builtin but may get used and abused by snort2lua
1059 // should be deleted at some point
1060 return gid != GID_EXCEPTION_SDF;
1061 }
1062
parse_rule_close(SnortConfig * sc,RuleTreeNode & rtn,OptTreeNode * otn)1063 void parse_rule_close(SnortConfig* sc, RuleTreeNode& rtn, OptTreeNode* otn)
1064 {
1065 if ( s_ignore )
1066 {
1067 s_ignore = false;
1068 skip_count++;
1069 return;
1070 }
1071
1072 if ( otn->is_rule_state_stub() )
1073 {
1074 parse_rule_state(sc, rtn, otn);
1075 delete rtn.header;
1076 rtn.header = nullptr;
1077 return;
1078 }
1079
1080 if ( !s_so_rule and !sc->metadata_filter.empty() and !otn->metadata_matched() )
1081 {
1082 delete otn;
1083 FreeRuleTreeNode(&rtn);
1084 ClearIpsOptionsVars();
1085 skip_count++;
1086 return;
1087 }
1088
1089 if ( s_so_rule )
1090 {
1091 otn->sigInfo.gid = s_so_rule->gid;
1092 otn->sigInfo.sid = s_so_rule->sid;
1093 otn->sigInfo.rev = s_so_rule->rev;
1094 }
1095 else if ( otn->soid )
1096 {
1097 // for so rules, delete the otn and parse the actual rule
1098 // keep the stub's rtn to allow user tuning of nets and ports
1099 // if already entered, don't recurse again
1100
1101 const char* rule = SoManager::get_so_rule(otn->soid, sc);
1102 IpsManager::reset_options();
1103
1104 if ( !rule )
1105 {
1106 if ( sc->allow_missing_so_rules )
1107 ParseWarning(WARN_RULES, "SO rule %s not loaded.", otn->soid);
1108 else
1109 ParseError("SO rule %s not loaded.", otn->soid);
1110
1111 FreeRuleTreeNode(&rtn);
1112 }
1113 else
1114 {
1115 SoRule so_rule(&rtn, otn);
1116 s_so_rule = &so_rule;
1117 parse_rules_string(sc, rule);
1118 s_so_rule = nullptr;
1119 }
1120 delete otn;
1121 return;
1122 }
1123
1124 RuleTreeNode* tmp = s_so_rule ? s_so_rule->rtn : &rtn;
1125 RuleTreeNode* new_rtn = transfer_rtn(tmp);
1126 addRtnToOtn(sc, otn, new_rtn);
1127
1128 OptTreeNode* otn_dup =
1129 OtnLookup(sc->otn_map, otn->sigInfo.gid, otn->sigInfo.sid);
1130
1131 if ( otn_dup )
1132 {
1133 dup_count++;
1134 otn->ruleIndex = otn_dup->ruleIndex;
1135
1136 if ( mergeDuplicateOtn(sc, otn_dup, otn, new_rtn) )
1137 {
1138 /* We are keeping the old/dup OTN and trashing the new one
1139 * we just created - it's freed in the remove dup function */
1140 return;
1141 }
1142 }
1143 else
1144 {
1145 otn_count++;
1146 rule_count++;
1147 }
1148
1149 if ( otn->soid )
1150 {
1151 otn->sigInfo.builtin = false;
1152 if ( !otn_dup )
1153 so_rule_count++;
1154 }
1155 else if ( is_builtin(otn->sigInfo.gid) )
1156 {
1157 if ( otn->num_detection_opts )
1158 ParseError("%u:%u builtin rules do not support detection options",
1159 otn->sigInfo.gid, otn->sigInfo.sid);
1160
1161 otn->sigInfo.builtin = true;
1162 if ( !otn_dup )
1163 builtin_rule_count++;
1164 }
1165 else
1166 {
1167 if ( !otn->num_detection_opts )
1168 ParseWarning(WARN_RULES, "%u:%u does not have any detection options",
1169 otn->sigInfo.gid, otn->sigInfo.sid);
1170
1171 otn->sigInfo.builtin = false;
1172 if ( !otn_dup )
1173 detect_rule_count++;
1174 }
1175
1176 if ( !otn_dup )
1177 otn->ruleIndex = parser_get_rule_index(otn->sigInfo.gid, otn->sigInfo.sid);
1178
1179 if ( otn->sigInfo.message.empty() )
1180 otn->sigInfo.message = "\"no msg in rule\"";
1181
1182 OptFpList* fpl = AddOptFuncToList(OptListEnd, otn);
1183 fpl->type = RULE_OPTION_TYPE_LEAF_NODE;
1184
1185 if ( is_service_protocol(otn->snort_protocol_id) )
1186 {
1187 // copy required because the call to add_service_to_otn can
1188 // invalidate the service name pointer
1189 std::string service = sc->proto_ref->get_name(otn->snort_protocol_id);
1190 add_service_to_otn(sc, otn, service.c_str());
1191 }
1192
1193 validate_services(sc, otn);
1194 OtnLookupAdd(sc->otn_map, otn);
1195
1196 if ( s_capture )
1197 {
1198 s_body += " )";
1199 otn->sigInfo.body = new std::string(s_body);
1200 }
1201
1202 ClearIpsOptionsVars();
1203 }
1204
parse_rule_process_rtn(RuleTreeNode * rtn)1205 void parse_rule_process_rtn(RuleTreeNode* rtn)
1206 {
1207 if (rtn->sip->head && rtn->sip->head->flags & SFIP_ANY)
1208 rtn->flags |= RuleTreeNode::ANY_SRC_IP;
1209 else
1210 rtn->flags &= ~RuleTreeNode::ANY_SRC_IP;
1211
1212 if (rtn->dip->head && rtn->dip->head->flags & SFIP_ANY)
1213 rtn->flags |= RuleTreeNode::ANY_DST_IP;
1214 else
1215 rtn->flags &= ~RuleTreeNode::ANY_DST_IP;
1216
1217 ValidateIPList(rtn->sip, rtn->sip->name);
1218 ValidateIPList(rtn->dip, rtn->dip->name);
1219
1220 if ( PortObjectHasAny(rtn->src_portobject) )
1221 rtn->flags |= RuleTreeNode::ANY_SRC_PORT;
1222 else
1223 rtn->flags &= ~RuleTreeNode::ANY_SRC_PORT;
1224
1225 if ( PortObjectHasAny(rtn->dst_portobject) )
1226 rtn->flags |= RuleTreeNode::ANY_DST_PORT;
1227 else
1228 rtn->flags &= ~RuleTreeNode::ANY_DST_PORT;
1229
1230 head_count++;
1231 SetupRTNFuncList(rtn);
1232 }
1233
1234 /*
1235 * Finish adding the rule to the port tables
1236 *
1237 * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
1238 * 2) find an index for the gid:sid pair
1239 * 3) add all no content rules to a single no content port object, the ports are irrelevant so
1240 * make it a any-any port object.
1241 * 4) if it's an any-any rule with content, add to an any-any port object
1242 * 5) find if we have a port object with these ports defined, if so get it, otherwise create it.
1243 * a)do this for src and dst port
1244 * b)add the rule index/id to the portobject(s)
1245 * c)if the rule is bidir add the rule and port-object to both src and dst tables
1246 */
parse_rule_finish_ports(SnortConfig * sc,RuleTreeNode * rtn,OptTreeNode * otn)1247 int parse_rule_finish_ports(SnortConfig* sc, RuleTreeNode* rtn, OptTreeNode* otn)
1248 {
1249 RulePortTables* port_tables = sc->port_tables;
1250 FastPatternConfig* fp = sc->fast_pattern_config;
1251
1252 int large_port_group = 0;
1253 PortTable* dstTable;
1254 PortTable* srcTable;
1255 PortObject* aaObject;
1256 rule_count_t* prc;
1257 uint32_t orig_flags = rtn->flags;
1258
1259 /* Select the Target PortTable for this rule, based on protocol, src/dst
1260 * dir, and if there is rule content */
1261 switch ( otn->snort_protocol_id )
1262 {
1263 case SNORT_PROTO_IP:
1264 dstTable = port_tables->ip.dst;
1265 srcTable = port_tables->ip.src;
1266 aaObject = port_tables->ip.any;
1267 prc = &ipCnt;
1268 break;
1269
1270 case SNORT_PROTO_ICMP:
1271 dstTable = port_tables->icmp.dst;
1272 srcTable = port_tables->icmp.src;
1273 aaObject = port_tables->icmp.any;
1274 prc = &icmpCnt;
1275 break;
1276
1277 case SNORT_PROTO_TCP:
1278 dstTable = port_tables->tcp.dst;
1279 srcTable = port_tables->tcp.src;
1280 aaObject = port_tables->tcp.any;
1281 prc = &tcpCnt;
1282 break;
1283
1284 case SNORT_PROTO_UDP:
1285 dstTable = port_tables->udp.dst;
1286 srcTable = port_tables->udp.src;
1287 aaObject = port_tables->udp.any;
1288 prc = &udpCnt;
1289 break;
1290
1291 default:
1292 rtn->flags |= RuleTreeNode::ANY_SRC_PORT|RuleTreeNode::ANY_DST_PORT;
1293 dstTable = srcTable = nullptr;
1294 aaObject = port_tables->svc_any;
1295 prc = &svcCnt;
1296 }
1297
1298 if ( !rtn->any_src_port() and !rtn->any_dst_port() )
1299 prc->both++;
1300
1301 int src_cnt = rtn->any_src_port() ? 65535 : PortObjectPortCount(rtn->src_portobject);
1302 int dst_cnt = rtn->any_dst_port() ? 65535 : PortObjectPortCount(rtn->dst_portobject);
1303
1304 /* If not an any-any rule test for port bleedover, if we are using a
1305 * single rule group, don't bother */
1306 if ( !fp->get_single_rule_group() and !rtn->any_any_port() )
1307 {
1308 if (src_cnt >= fp->get_bleed_over_port_limit())
1309 ++large_port_group;
1310
1311 if (dst_cnt >= fp->get_bleed_over_port_limit())
1312 ++large_port_group;
1313
1314 if (large_port_group == 2 && fp->get_bleed_over_warnings())
1315 {
1316 LogMessage("***Bleedover Port Limit(%d) Exceeded for rule %u:%u "
1317 "(%d)ports: ", fp->get_bleed_over_port_limit(),
1318 otn->sigInfo.gid, otn->sigInfo.sid,
1319 (src_cnt > dst_cnt) ? src_cnt : dst_cnt);
1320
1321 /* If logging to syslog, this will be all multiline */
1322 fflush(stdout); fflush(stderr);
1323 PortObjectPrintPortsRaw(rtn->src_portobject);
1324 LogMessage(" -> ");
1325 PortObjectPrintPortsRaw(rtn->dst_portobject);
1326 LogMessage(" adding to any-any group\n");
1327 fflush(stdout); fflush(stderr);
1328 }
1329 }
1330
1331 /* If an any-any rule add rule index to any-any port object
1332 * both content and no-content type rules go here if they are
1333 * any-any port rules...
1334 * If we have an any-any rule or a large port group or
1335 * were using a single rule group we make it an any-any rule. */
1336 if ( rtn->any_any_port() or large_port_group == 2 or fp->get_single_rule_group() )
1337 {
1338 if (otn->snort_protocol_id == SNORT_PROTO_IP)
1339 {
1340 PortObjectAddRule(port_tables->icmp.any, otn->ruleIndex);
1341 icmpCnt.any++;
1342
1343 PortObjectAddRule(port_tables->tcp.any, otn->ruleIndex);
1344 tcpCnt.any++;
1345
1346 PortObjectAddRule(port_tables->udp.any, otn->ruleIndex);
1347 udpCnt.any++;
1348 }
1349 /* For all protocols-add to the any any group */
1350 PortObjectAddRule(aaObject, otn->ruleIndex);
1351 prc->any++;
1352 rtn->flags = orig_flags;
1353 return 0; /* done */
1354 }
1355
1356 bool both_dirs = false;
1357
1358 /* add rule index to dst table if we have a specific dst port or port list */
1359 if ( dst_cnt < fp->get_bleed_over_port_limit() and dst_cnt <= src_cnt )
1360 {
1361 prc->dst++;
1362
1363 /* find the proper port object */
1364 PortObject* pox = PortTableFindInputPortObjectPorts(dstTable, rtn->dst_portobject);
1365 if ( !pox )
1366 {
1367 /* Add the port object to the table, and add the rule to the port object */
1368 pox = PortObjectDupPorts(rtn->dst_portobject);
1369 PortTableAddObject(dstTable, pox);
1370 }
1371
1372 PortObjectAddRule(pox, otn->ruleIndex);
1373
1374 /* if bidir, add this rule and port group to the src table */
1375 if ( rtn->flags & RuleTreeNode::BIDIRECTIONAL )
1376 {
1377 pox = PortTableFindInputPortObjectPorts(srcTable, rtn->dst_portobject);
1378 if ( !pox )
1379 {
1380 pox = PortObjectDupPorts(rtn->dst_portobject);
1381 PortTableAddObject(srcTable, pox);
1382 }
1383
1384 PortObjectAddRule(pox, otn->ruleIndex);
1385 both_dirs = true;
1386 }
1387 }
1388
1389 /* add rule index to src table if we have a specific src port or port list */
1390 if ( src_cnt < fp->get_bleed_over_port_limit() and src_cnt < dst_cnt )
1391 {
1392 prc->src++;
1393 PortObject* pox = PortTableFindInputPortObjectPorts(srcTable, rtn->src_portobject);
1394 if ( !pox )
1395 {
1396 pox = PortObjectDupPorts(rtn->src_portobject);
1397 PortTableAddObject(srcTable, pox);
1398 }
1399
1400 PortObjectAddRule(pox, otn->ruleIndex);
1401
1402 /* if bidir, add this rule and port group to the dst table */
1403 if ( !both_dirs and rtn->flags & RuleTreeNode::BIDIRECTIONAL )
1404 {
1405 pox = PortTableFindInputPortObjectPorts(dstTable, rtn->src_portobject);
1406 if ( !pox )
1407 {
1408 pox = PortObjectDupPorts(rtn->src_portobject);
1409 PortTableAddObject(dstTable, pox);
1410 }
1411 PortObjectAddRule(pox, otn->ruleIndex);
1412 }
1413 }
1414 return 0;
1415 }
1416
parse_rule_dec_head_count()1417 void parse_rule_dec_head_count()
1418 {
1419 head_count--;
1420 }
1421